// Copyright (c) Microsoft Corporation. All rights reserved. // using System; using System.Collections.Generic; using System.Xml.XPath; using System.Configuration; using System.IO; namespace Microsoft.Ddue.Tools { public class XamlUsageSyntaxGenerator : SyntaxGeneratorTemplate { public XamlUsageSyntaxGenerator(XPathNavigator configuration) : base(configuration) { LoadConfigNode(configuration); if (String.IsNullOrEmpty(language)) language = "XAML"; } public override void WriteSyntax(XPathNavigator reflection, SyntaxWriter writer) { writer.WriteStartBlock(Language); // Check the list of assemblies for which to generate XAML syntax string assemblyName = (string)reflection.Evaluate(apiContainingAssemblyExpression); string namespaceName = (string)reflection.Evaluate(apiContainingNamespaceNameExpression); if (!xamlAssemblies.ContainsKey(assemblyName.ToLower())) { WriteXamlBoilerplate(XamlBoilerplateID.nonXamlAssemblyBoilerplate, writer); } else { string group = (string)reflection.Evaluate(apiGroupExpression); switch (group) { case "namespace": WriteNamespaceSyntax(reflection, writer); break; case "type": WriteTypeSyntax(reflection, writer); break; case "member": WriteMemberSyntax(reflection, writer); break; } WriteXamlXmlnsUri(assemblyName, namespaceName, writer); } writer.WriteEndBlock(); } private void WriteXamlXmlnsUri(string assemblyName, string namespaceName, SyntaxWriter writer) { Dictionary> clrNamespaces; if (xamlAssemblies.TryGetValue(assemblyName.ToLower(), out clrNamespaces)) { List xmlnsUriList; if (clrNamespaces.TryGetValue(namespaceName, out xmlnsUriList)) { foreach (string xmlnsUri in xmlnsUriList) { // start the syntax block writer.WriteStartSubBlock("xamlXmlnsUri"); writer.WriteString(xmlnsUri); writer.WriteEndSubBlock(); } } } } // list of classes whose subclasses do NOT get XAML syntax protected List excludedAncestorList = new List(); // list of assemblies whose members get XAML syntax // the nested Dictionary is a list of assembly's namespaces that have one or more xmlns uris for xaml private Dictionary>> xamlAssemblies = new Dictionary>>(); private void LoadConfigNode(XPathNavigator configuration) { // get the filter files XPathNodeIterator filterNodes = configuration.Select("filter"); if (filterNodes.Count == 0) { LoadConfiguration(configuration); return; } foreach (XPathNavigator filterNode in filterNodes) { string filterFiles = filterNode.GetAttribute("files", String.Empty); if ((filterFiles == null) || (filterFiles.Length == 0)) throw new ConfigurationErrorsException("The XamlUsageSyntaxGenerator filter/@files attribute must specify a path."); ParseDocuments(filterFiles); } } private void LoadConfiguration(XPathNavigator configuration) { // get the list of excluded ancestor classes foreach (XPathNavigator excludedClass in configuration.Select("xamlExcludedAncestors/class")) { string apiId = excludedClass.GetAttribute("api", string.Empty); if (apiId.Length > 0 && !excludedAncestorList.Contains(apiId)) excludedAncestorList.Add(apiId); } // get the list of XAML assemblies; members in other assemblies get no xaml syntax, just 'not applicable' boilerplate foreach (XPathNavigator xamlAssembly in configuration.Select("xamlAssemblies/assembly")) { string assemblyName = xamlAssembly.GetAttribute("name", string.Empty); if (string.IsNullOrEmpty(assemblyName)) continue; // should emit warning message Dictionary> clrNamespaces; if (!xamlAssemblies.TryGetValue(assemblyName.ToLower(), out clrNamespaces)) { clrNamespaces = new Dictionary>(); xamlAssemblies.Add(assemblyName.ToLower(), clrNamespaces); } foreach (XPathNavigator xmlnsNode in xamlAssembly.Select("xmlns[@uri][clrNamespace]")) { string xmlnsUri = xmlnsNode.GetAttribute("uri", string.Empty); if (string.IsNullOrEmpty(xmlnsUri)) continue; // should emit warning message foreach (XPathNavigator clrNamespaceNode in xmlnsNode.Select("clrNamespace[@name]")) { string namespaceName = clrNamespaceNode.GetAttribute("name", string.Empty); if (string.IsNullOrEmpty(namespaceName)) continue; // should emit warning message List xmlnsUriList; if (!clrNamespaces.TryGetValue(namespaceName, out xmlnsUriList)) { xmlnsUriList = new List(); clrNamespaces.Add(namespaceName, xmlnsUriList); } if (!xmlnsUriList.Contains(xmlnsUri)) xmlnsUriList.Add(xmlnsUri); } } } } public void ParseDocuments(string wildcardPath) { string filterFiles = Environment.ExpandEnvironmentVariables(wildcardPath); if ((filterFiles == null) || (filterFiles.Length == 0)) throw new ConfigurationErrorsException("The XamlUsageSyntaxGenerator filter path is an empty string."); //WriteMessage(MessageLevel.Info, String.Format("XamlUsageSyntaxGenerator: Searching for files that match '{0}'.", filterFiles)); string directoryPart = Path.GetDirectoryName(filterFiles); if (String.IsNullOrEmpty(directoryPart)) directoryPart = Environment.CurrentDirectory; directoryPart = Path.GetFullPath(directoryPart); string filePart = Path.GetFileName(filterFiles); string[] files = Directory.GetFiles(directoryPart, filePart); foreach (string file in files) ParseDocument(file); //WriteMessage(MessageLevel.Info, String.Format("Found {0} files in {1}.", files.Length, filterFiles)); } private void ParseDocument(string file) { try { XPathDocument document = new XPathDocument(file); XPathNavigator xamlSyntaxNode = document.CreateNavigator().SelectSingleNode("/*"); LoadConfiguration(xamlSyntaxNode); } catch (Exception e) { throw new ConfigurationErrorsException(string.Format("Exception parsing XamlUsageSyntaxGenerator filter file: {0}. Exception message: {1}", file, e.Message)); } } public override void WriteNamespaceSyntax(XPathNavigator reflection, SyntaxWriter writer) { // empty xaml syntax for namespace topics } private void WriteXamlBoilerplate(XamlBoilerplateID bpID, SyntaxWriter writer) { WriteXamlBoilerplate(bpID, null, writer); } private void WriteXamlBoilerplate(XamlBoilerplateID bpID, XPathNavigator typeReflection, SyntaxWriter writer) { string xamlBlockId = System.Enum.GetName(typeof(XamlBoilerplateID), bpID); if (xamlBlockId != null) { writer.WriteStartSubBlock(xamlBlockId); if (typeReflection != null) WriteTypeReference(typeReflection, writer); writer.WriteEndSubBlock(); } } public override void WriteClassSyntax(XPathNavigator reflection, SyntaxWriter writer) { string name = reflection.Evaluate(apiNameExpression).ToString(); bool isAbstract = (bool)reflection.Evaluate(apiIsAbstractTypeExpression); bool isSealed = (bool)reflection.Evaluate(apiIsSealedTypeExpression); bool isSerializable = (bool)reflection.Evaluate(apiIsSerializableTypeExpression); // if (isAbstract && !isSealed) { // Output boilerplate for abstract class WriteXamlBoilerplate(XamlBoilerplateID.classXamlSyntax_abstract, writer); } else if (!HasDefaultConstructor(reflection)) { if (HasTypeConverterAttribute(reflection)) { WriteXamlBoilerplate(XamlBoilerplateID.classXamlSyntax_noDefaultCtorWithTypeConverter, writer); } else { WriteXamlBoilerplate(XamlBoilerplateID.classXamlSyntax_noDefaultCtor, writer); } } else if (IsExcludedSubClass(reflection)) { WriteXamlBoilerplate(XamlBoilerplateID.classXamlSyntax_excludedSubClass, writer); } else { // show the default XAML syntax for classes // Note: skipped the test for TypeConverterAttribute shown in the flowchart because same syntax either way ObjectElementUsageForClassStruct(reflection, writer); } } private void ObjectElementUsageForClassStruct(XPathNavigator reflection, SyntaxWriter writer) { string typeName = (string)reflection.Evaluate(apiNameExpression); bool isGeneric = (bool)reflection.Evaluate(apiIsGenericExpression); string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlObjectElementUsageHeading); string contentPropertyId = (string)reflection.Evaluate(contentPropertyIdExpression); if (contentPropertyId == "") contentPropertyId = (string)reflection.Evaluate(ancestorContentPropertyIdExpression); // start the syntax block writer.WriteStartSubBlock(xamlBlockId); writer.WriteString("<"); if (isGeneric) { writer.WriteIdentifier(typeName); // for generic types show the type arguments XPathNodeIterator templates = (XPathNodeIterator)reflection.Evaluate(apiTemplatesExpression); if (templates.Count > 0) { writer.WriteString(" x:TypeArguments=\""); while (templates.MoveNext()) { XPathNavigator template = templates.Current; string name = template.GetAttribute("name", String.Empty); writer.WriteString(name); if (templates.CurrentPosition < templates.Count) writer.WriteString(","); } writer.WriteString("\""); } } else { // for non-generic types just show the name writer.WriteIdentifier(typeName); } if (contentPropertyId == string.Empty) { writer.WriteString(" .../>"); } else { // close the start tag writer.WriteString(">"); // the inner xml of the Object Element syntax for a type with a content property // is a link to the content property writer.WriteLine(); writer.WriteString(" "); writer.WriteReferenceLink(contentPropertyId); writer.WriteLine(); // write the end tag writer.WriteString(""); } // end the sub block writer.WriteEndSubBlock(); } public override void WriteStructureSyntax(XPathNavigator reflection, SyntaxWriter writer) { string name = (string)reflection.Evaluate(apiNameExpression); bool notWriteable = (bool)reflection.Evaluate(noSettablePropertiesExpression); if (notWriteable) { // Output boilerplate for struct with no writeable properties WriteXamlBoilerplate(XamlBoilerplateID.structXamlSyntax_nonXaml, writer); } else { // All writeable structs in XAML assemblies are usable in XAML // always show the Object Element Usage syntax ObjectElementUsageForClassStruct(reflection, writer); // For structs with TypeConverterAttribute, // if we can show multiple syntax blocks, also output AttributeUsage boilerplate if (HasTypeConverterAttribute(reflection)) WriteXamlBoilerplate(XamlBoilerplateID.structXamlSyntax_attributeUsage, writer); } } public override void WriteInterfaceSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.interfaceOverviewXamlSyntax, writer); } public override void WriteDelegateSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.delegateOverviewXamlSyntax, writer); } public override void WriteEnumerationSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.enumerationOverviewXamlSyntax, writer); } public override void WriteConstructorSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.constructorOverviewXamlSyntax, writer); } public override void WriteMethodSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.methodOverviewXamlSyntax, writer); } public override void WriteAttachedPropertySyntax(XPathNavigator reflection, SyntaxWriter writer) { string propertyName = (string)reflection.Evaluate(apiNameExpression); string containingTypeName = (string)reflection.Evaluate(apiContainingTypeNameExpression); bool isSettable = (bool)reflection.Evaluate(apiIsWritePropertyExpression); XPathNavigator returnType = reflection.SelectSingleNode(apiReturnTypeExpression); if (!isSettable) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_readOnly, writer); } else { // xaml syntax block for attached property string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlAttributeUsageHeading); writer.WriteStartSubBlock(xamlBlockId); writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(" "); writer.WriteIdentifier(containingTypeName + "." + propertyName); writer.WriteString("=\""); WriteTypeReference(returnType, writer); writer.WriteString("\" .../>"); writer.WriteEndSubBlock(); } } public override void WritePropertySyntax(XPathNavigator reflection, SyntaxWriter writer) { //containingTypeSubgroupExpression string propertyName = (string)reflection.Evaluate(apiNameExpression); bool isSettable = (bool)reflection.Evaluate(apiIsWritePropertyExpression); bool isSetterPublic = (bool)reflection.Evaluate(apiIsSetterPublicExpression); bool isAbstract = (bool)reflection.Evaluate(apiIsAbstractProcedureExpression); string propertyVisibility = (string)reflection.Evaluate(apiVisibilityExpression); XPathNodeIterator parameters = reflection.Select(apiParametersExpression); XPathNavigator returnType = reflection.SelectSingleNode(apiReturnTypeExpression); bool notWriteableReturnType = (bool)returnType.Evaluate(noSettablePropertiesExpression); string returnTypeId = returnType.GetAttribute("api", string.Empty); string returnTypeSubgroup = (string)returnType.Evaluate(apiSubgroupExpression); bool returnTypeIsAbstract = (bool)returnType.Evaluate(apiIsAbstractTypeExpression); bool returnTypeIsReadonlyStruct = (returnTypeSubgroup == "structure" && notWriteableReturnType && !IsPrimitiveType(returnTypeId)); XPathNavigator containingType = reflection.SelectSingleNode(apiContainingTypeExpression); string containingTypeSubgroup = (string)containingType.Evaluate(apiSubgroupExpression); // an ordinary property, not an attached prop if (containingTypeSubgroup == "interface") { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_noXamlSyntaxForInterfaceMembers, writer); } else if ((bool)containingType.Evaluate(apiIsAbstractTypeExpression) && (bool)containingType.Evaluate(apiIsSealedTypeExpression)) { // the property's containing type is static if it's abstract and sealed // members of a static class cannot be used in XAML. WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_nonXamlParent, writer); } else if (IsExcludedSubClass(containingType)) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_parentIsExcludedSubClass, writer); } else if (!DoesParentSupportXaml(reflection)) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_nonXamlParent, writer); } else if (propertyVisibility != "public") { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_notPublic, writer); } else if (isAbstract) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_abstract, writer); } else if (parameters.Count > 0) { // per DDUERELTools bug 1373: indexer properties cannot be used in XAML WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_nonXaml, writer); } else if (IsContentProperty(reflection) && !returnTypeIsReadonlyStruct) { PropertyContentElementUsageSimple(reflection, writer); } else if (!isSettable || !isSetterPublic) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_readOnly, writer); } else if (returnTypeIsAbstract) { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_abstractType, returnType, writer); } else if (IsPrimitiveType(returnTypeId)) { PropertyAttributeUsage(reflection, writer); } else if (returnTypeSubgroup == "enumeration") { PropertyAttributeUsage(reflection, writer); } else { bool hasDefaultConstructor = HasDefaultConstructor(returnType); if (HasTypeConverterAttribute(returnType)) { if (hasDefaultConstructor && !returnTypeIsReadonlyStruct) { PropertyElementUsageGrande(reflection, writer); } PropertyAttributeUsage(reflection, writer); } else if (hasDefaultConstructor && !returnTypeIsReadonlyStruct) { PropertyElementUsageGrande(reflection, writer); } else { WriteXamlBoilerplate(XamlBoilerplateID.propertyXamlSyntax_nonXaml, writer); } } } // A simple Property Element Usage block for a content property // syntax looks like: // // // private void PropertyContentElementUsageSimple(XPathNavigator propertyReflection, SyntaxWriter writer) { string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlContentElementUsageHeading); XPathNavigator returnType = propertyReflection.SelectSingleNode(apiReturnTypeExpression); // start the syntax block writer.WriteStartSubBlock(xamlBlockId); // writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(">"); writer.WriteLine(); // writer.WriteString(" <"); WriteTypeReference(returnType, writer); writer.WriteString(" .../>"); writer.WriteLine(); // writer.WriteString(""); writer.WriteEndSubBlock(); } // A grandiose Property Element Usage block // syntax looks like: // // // // // private void PropertyElementUsageGrande(XPathNavigator propertyReflection, SyntaxWriter writer) { string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlPropertyElementUsageHeading); string propertyName = (string)propertyReflection.Evaluate(apiNameExpression); XPathNavigator returnType = propertyReflection.SelectSingleNode(apiReturnTypeExpression); // start the syntax block writer.WriteStartSubBlock(xamlBlockId); // writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(">"); writer.WriteLine(); // writer.WriteString(" <"); writer.WriteParameter("object"); writer.WriteString("."); writer.WriteIdentifier(propertyName); writer.WriteString(">"); writer.WriteLine(); // writer.WriteString(" <"); WriteTypeReference(returnType, writer); writer.WriteString(" .../>"); writer.WriteLine(); // writer.WriteString(" "); writer.WriteLine(); // writer.WriteString(""); writer.WriteEndSubBlock(); } // An Attribute Usage block private void PropertyAttributeUsage(XPathNavigator propertyReflection, SyntaxWriter writer) { string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlAttributeUsageHeading); string propertyName = (string)propertyReflection.Evaluate(apiNameExpression); XPathNavigator returnType = propertyReflection.SelectSingleNode(apiReturnTypeExpression); // start the syntax block writer.WriteStartSubBlock(xamlBlockId); // syntax looks like: // writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(" "); writer.WriteIdentifier(propertyName); writer.WriteString("=\""); WriteTypeReference(returnType, writer); writer.WriteString("\" .../>"); writer.WriteEndSubBlock(); } public override void WriteEventSyntax(XPathNavigator reflection, SyntaxWriter writer) { string eventName = (string)reflection.Evaluate(apiNameExpression); string eventVisibility = (string)reflection.Evaluate(apiVisibilityExpression); bool isAbstract = (bool)reflection.Evaluate(apiIsAbstractProcedureExpression); XPathNavigator eventHandler = reflection.SelectSingleNode(apiHandlerOfEventExpression); XPathNavigator containingType = reflection.SelectSingleNode(apiContainingTypeExpression); string containingTypeSubgroup = (string)containingType.Evaluate(apiSubgroupExpression); bool containingTypeIsAbstract = (bool)containingType.Evaluate(apiIsAbstractTypeExpression); bool containingTypeIsSealed = (bool)containingType.Evaluate(apiIsSealedTypeExpression); if (containingTypeSubgroup == "interface") { WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_noXamlSyntaxForInterfaceMembers, writer); } else if (containingTypeIsAbstract && containingTypeIsSealed) { // the event's containing type is static if it's abstract and sealed // members of a static class cannot be used in XAML. WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_nonXamlParent, writer); } else if (IsExcludedSubClass(containingType)) { WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_parentIsExcludedSubClass, writer); } else if (!DoesParentSupportXaml(reflection)) { WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_nonXamlParent, writer); } else if (eventVisibility != "public") { WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_notPublic, writer); } else if (isAbstract) { WriteXamlBoilerplate(XamlBoilerplateID.eventXamlSyntax_abstract, writer); } else { // start the syntax block string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlAttributeUsageHeading); writer.WriteStartSubBlock(xamlBlockId); // syntax looks like: // writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(" "); writer.WriteIdentifier(eventName); writer.WriteString("=\""); WriteTypeReference(eventHandler, writer); writer.WriteString("\" .../>"); writer.WriteEndSubBlock(); } } public override void WriteAttachedEventSyntax(XPathNavigator reflection, SyntaxWriter writer) { string eventName = (string)reflection.Evaluate(apiNameExpression); string containingTypeName = (string)reflection.Evaluate(apiContainingTypeNameExpression); XPathNavigator eventHandler = reflection.SelectSingleNode(apiHandlerOfEventExpression); // xaml syntax block for attached event string xamlBlockId = System.Enum.GetName(typeof(XamlHeadingID), XamlHeadingID.xamlAttributeUsageHeading); writer.WriteStartSubBlock(xamlBlockId); writer.WriteString("<"); writer.WriteParameter("object"); writer.WriteString(" "); writer.WriteIdentifier(containingTypeName + "." + eventName); writer.WriteString("=\""); WriteTypeReference(eventHandler, writer); writer.WriteString(string.Format("\" .../>")); writer.WriteEndSubBlock(); } public override void WriteFieldSyntax(XPathNavigator reflection, SyntaxWriter writer) { WriteXamlBoilerplate(XamlBoilerplateID.fieldOverviewXamlSyntax, writer); } // References private void WriteTypeReference(XPathNavigator reference, SyntaxWriter writer) { switch (reference.LocalName) { case "arrayOf": int rank = Convert.ToInt32(reference.GetAttribute("rank", String.Empty)); XPathNavigator element = reference.SelectSingleNode(typeExpression); WriteTypeReference(element, writer); writer.WriteString("["); for (int i = 1; i < rank; i++) { writer.WriteString(","); } writer.WriteString("]"); break; case "pointerTo": XPathNavigator pointee = reference.SelectSingleNode(typeExpression); WriteTypeReference(pointee, writer); writer.WriteString("*"); break; case "referenceTo": XPathNavigator referee = reference.SelectSingleNode(typeExpression); WriteTypeReference(referee, writer); break; case "type": string id = reference.GetAttribute("api", String.Empty); XPathNavigator outerTypeReference = reference.SelectSingleNode(typeOuterTypeExpression); if (outerTypeReference != null) { WriteTypeReference(outerTypeReference, writer); writer.WriteString("."); } WriteNormalTypeReference(id, writer); XPathNodeIterator typeModifiers = reference.Select(typeModifiersExpression); while (typeModifiers.MoveNext()) { WriteTypeReference(typeModifiers.Current, writer); } break; case "template": string name = reference.GetAttribute("name", String.Empty); writer.WriteString(name); XPathNodeIterator modifiers = reference.Select(typeModifiersExpression); while (modifiers.MoveNext()) { WriteTypeReference(modifiers.Current, writer); } break; case "specialization": writer.WriteString("<"); XPathNodeIterator arguments = reference.Select(specializationArgumentsExpression); while (arguments.MoveNext()) { if (arguments.CurrentPosition > 1) writer.WriteString(", "); WriteTypeReference(arguments.Current, writer); } writer.WriteString(">"); break; } } private void WriteNormalTypeReference(string reference, SyntaxWriter writer) { switch (reference) { case "T:System.Void": writer.WriteReferenceLink(reference, "void"); break; case "T:System.String": writer.WriteReferenceLink(reference, "string"); break; case "T:System.Boolean": writer.WriteReferenceLink(reference, "bool"); break; case "T:System.Byte": writer.WriteReferenceLink(reference, "byte"); break; case "T:System.SByte": writer.WriteReferenceLink(reference, "sbyte"); break; case "T:System.Char": writer.WriteReferenceLink(reference, "char"); break; case "T:System.Int16": writer.WriteReferenceLink(reference, "short"); break; case "T:System.Int32": writer.WriteReferenceLink(reference, "int"); break; case "T:System.Int64": writer.WriteReferenceLink(reference, "long"); break; case "T:System.UInt16": writer.WriteReferenceLink(reference, "ushort"); break; case "T:System.UInt32": writer.WriteReferenceLink(reference, "uint"); break; case "T:System.UInt64": writer.WriteReferenceLink(reference, "ulong"); break; case "T:System.Single": writer.WriteReferenceLink(reference, "float"); break; case "T:System.Double": writer.WriteReferenceLink(reference, "double"); break; case "T:System.Decimal": writer.WriteReferenceLink(reference, "decimal"); break; default: writer.WriteReferenceLink(reference); break; } } // utility routines // A default constructor is a a parameterless, public constructor method // This is called for: // a class // the declaring type of a member // the type of a property private bool HasDefaultConstructor(XPathNavigator typeReflection) { // all structs have implicit default constructors string subgroup = (string)typeReflection.Evaluate(apiSubgroupExpression); if (subgroup == "structure") return true; return (bool)typeReflection.Evaluate(hasDefaultConstructorExpression); } // This is called to check for a "TypeConverterAttribute" on: // a class or structure topic // the declaring type of a property or event member // the type of a property private bool HasTypeConverterAttribute(XPathNavigator typeReflection) { return (bool)typeReflection.Evaluate(hasTypeConverterAttributeExpression); } // Get the id of the content property, if any, for the property's containing type // return true if the content property id matches the current property's id private bool IsContentProperty(XPathNavigator propertyReflection) { string propertyName = (string)propertyReflection.Evaluate(apiNameExpression); XPathNavigator containingType = propertyReflection.SelectSingleNode(apiContainingTypeExpression); string containingTypeName = (string)containingType.Evaluate(apiNameExpression); string namespaceId = (string)propertyReflection.Evaluate(apiContainingNamespaceIdExpression); string propertyId = string.Concat("P:", namespaceId.Substring(2), ".", string.Concat(containingTypeName, ".", propertyName)); string contentPropertyId = (string)containingType.Evaluate(contentPropertyIdExpression); if (propertyId == contentPropertyId) return true; else return false; } // Check the list of subclasses to exclude // This is called to check the class ancestors of // a class // the declaring type of a property or event member private bool IsExcludedSubClass(XPathNavigator typeReflection) { XPathNodeIterator ancestors = (XPathNodeIterator)typeReflection.Evaluate(apiAncestorsExpression); foreach (XPathNavigator ancestor in ancestors) { string ancestorId = ancestor.GetAttribute("api", string.Empty); if (excludedAncestorList.Contains(ancestorId)) return true; } return false; } // Check the parent type of a property or event. // Does it have the necessary characteristics so the property or event can be used in XAML? // Is PARENT CLASS abstract OR does it have a default ctor OR a class-level TypeConverter attribute? private bool DoesParentSupportXaml(XPathNavigator memberReflection) { XPathNavigator containingType = memberReflection.SelectSingleNode(apiContainingTypeExpression); if ((bool)containingType.Evaluate(apiIsAbstractTypeExpression)) return true; if (HasDefaultConstructor(containingType)) return true; if (HasTypeConverterAttribute(containingType)) return true; // A property that returns a String doesn't need a TypeConverterAttribute, so return true here XPathNavigator returnType = memberReflection.SelectSingleNode(apiReturnTypeExpression); if (returnType != null) { string returnTypeId = returnType.GetAttribute("api", string.Empty); if (returnTypeId == "T:System.String") return true; } return false; } private bool IsPrimitiveType(string typeId) { // The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, Char, Double, and Single. switch (typeId) { case "T:System.Boolean": case "T:System.Byte": case "T:System.SByte": case "T:System.Int16": case "T:System.UInt16": case "T:System.Int32": case "T:System.UInt32": case "T:System.Int64": case "T:System.UInt64": case "T:System.IntPtr": case "T:System.Char": case "T:System.Double": case "T:System.Single": case "T:System.String": // String is not a primitive but is treated as one for this XAML purpose return true; default: return false; } } private XPathExpression hasTypeConverterAttributeExpression = XPathExpression.Compile("boolean(attributes/attribute/type[@api='T:System.ComponentModel.TypeConverterAttribute'])"); private XPathExpression hasDefaultConstructorExpression = XPathExpression.Compile("boolean(typedata/@defaultConstructor)"); private XPathExpression contentPropertyNameExpression = XPathExpression.Compile("string(attributes/attribute[type[contains(@api,'.ContentPropertyAttribute')]]/argument/value/.)"); private XPathExpression contentPropertyIdExpression = XPathExpression.Compile("string(typedata/@contentProperty)"); private XPathExpression ancestorContentPropertyIdExpression = XPathExpression.Compile("string(family/ancestors/type/@contentProperty)"); private XPathExpression noSettablePropertiesExpression = XPathExpression.Compile("boolean(typedata/@noSettableProperties)"); private XPathExpression apiIsSetterPublicExpression = XPathExpression.Compile("boolean((memberdata[@visibility='public'] and not(propertydata[@set-visibility!='public'])) or propertydata[@set-visibility='public'])"); } public enum XamlBoilerplateID { // boilerplate for classes in xaml assemblies classXamlSyntax_abstract, classXamlSyntax_excludedSubClass, classXamlSyntax_noDefaultCtor, classXamlSyntax_noDefaultCtorWithTypeConverter, // boilerplate for structs in xaml assemblies structXamlSyntax_nonXaml, structXamlSyntax_attributeUsage, // boilerplate for events in xaml assemblies eventXamlSyntax_parentIsExcludedSubClass, eventXamlSyntax_noXamlSyntaxForInterfaceMembers, eventXamlSyntax_nonXamlParent, eventXamlSyntax_notPublic, eventXamlSyntax_abstract, eventXamlSyntax_nonXaml, // boilerplate for properties in xaml assemblies propertyXamlSyntax_parentIsExcludedSubClass, propertyXamlSyntax_noXamlSyntaxForInterfaceMembers, propertyXamlSyntax_nonXamlParent, propertyXamlSyntax_notPublic, propertyXamlSyntax_abstract, propertyXamlSyntax_readOnly, propertyXamlSyntax_abstractType, propertyXamlSyntax_nonXaml, // syntax used with all enums in xaml assemblies enumerationOverviewXamlSyntax, // boilerplate used with all method, field, etc. in xaml assemblies delegateOverviewXamlSyntax, interfaceOverviewXamlSyntax, constructorOverviewXamlSyntax, fieldOverviewXamlSyntax, methodOverviewXamlSyntax, // boilerplate used with all types and members in all non-xaml assemblies nonXamlAssemblyBoilerplate } // XAML headings public enum XamlHeadingID { xamlAttributeUsageHeading, xamlObjectElementUsageHeading, xamlPropertyElementUsageHeading, xamlContentElementUsageHeading, xamlSyntaxBoilerplateHeading } }