1291 lines
53 KiB
C#
1291 lines
53 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
using JD.Kernel.Classes;
|
|
using JD.Kernel.StaticHelpers;
|
|
using JD.LogService;
|
|
using JD.Serializers.Xml;
|
|
using JD.TeaBoxAddinSystem.Addins.Attributes;
|
|
using JD.TeaBoxAddinSystem.Addins.Description;
|
|
using JD.TeaBoxAddinSystem.Addins.Description.Conditions;
|
|
|
|
namespace JD.TeaBoxAddinSystem.Addins.Management
|
|
{
|
|
/// <summary>
|
|
/// Generates a schema from an addin repository.
|
|
/// </summary>
|
|
public class AddinXmlSchemaGenerator
|
|
{
|
|
#region Constants
|
|
|
|
private const string SchemaNamespace = "http://Addin";
|
|
|
|
private const string AbstractNodeType = "AbstractNode";
|
|
private const string AbstractExtensionNodeType = "AbstractExtensionNode";
|
|
private const string ExtensionNodeType = "ExtensionNode";
|
|
private const string AbstractExtensionTypeType = "AbstractExtensionType";
|
|
private const string DescriptionType = "Description";
|
|
private const string ImportResourceType = "ImportResource";
|
|
private const string ComplexConditionType = "ComplexCondition";
|
|
private const string ConditionalOperatorGroup = "ConditionalOperatorGroup";
|
|
private const string SimpleConditionGroup = "SimpleConditionGroup";
|
|
private const string ConditionGroup = "ConditionGroup";
|
|
private const string NodeOrConditionGroup = "NodeOrConditionGroup";
|
|
private const string TargetAttributeGroup = "TargetAttributeGroup";
|
|
private const string ExtensionTypePrefix = "ExtensionType.";
|
|
private const string ConditionTypePrefix = "ConditionType.";
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly XmlDocument m_factoryDocument = new XmlDocument();
|
|
private readonly AddinRepository m_repository;
|
|
private readonly ILogger m_logger;
|
|
|
|
private readonly Dictionary<string, Addin> m_addins = new Dictionary<string, Addin>();
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="repository"></param>
|
|
/// <param name="logger"></param>
|
|
public AddinXmlSchemaGenerator(AddinRepository repository, ILogger logger)
|
|
{
|
|
Throw.IfNull(repository, "repository");
|
|
m_repository = repository;
|
|
m_logger = logger;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
#region Generation
|
|
|
|
/// <summary>
|
|
/// Generates a new schema.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public XmlSchema Generate()
|
|
{
|
|
m_addins.Clear();
|
|
foreach (Addin addin in m_repository.Addins)
|
|
{
|
|
m_addins.Add(addin.FullId, addin);
|
|
}
|
|
|
|
CollectTypes();
|
|
|
|
XmlSchema addinSchema = new XmlSchema();
|
|
addinSchema.TargetNamespace = SchemaNamespace;
|
|
addinSchema.ElementFormDefault = XmlSchemaForm.Qualified;
|
|
addinSchema.Namespaces.Add("mstns", SchemaNamespace);
|
|
addinSchema.Namespaces.Add(string.Empty, SchemaNamespace);
|
|
|
|
addinSchema.Items.Add(CreateRootElement());
|
|
|
|
CreateComplexTypes(addinSchema);
|
|
CreateDynamicTypes(addinSchema);
|
|
|
|
return addinSchema;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a new schema and saves it to the given <paramref name="filename"/>.
|
|
/// </summary>
|
|
/// <param name="filename"></param>
|
|
public void GenerateAndSaveTo(string filename)
|
|
{
|
|
Throw.IfNull(filename, "filename");
|
|
|
|
XmlSchema schema = Generate();
|
|
|
|
using (MemoryStream memoryStream = new MemoryStream())
|
|
{
|
|
schema.Write(memoryStream);
|
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
// Add a comment telling people to not temper directly with the file.
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.Load(memoryStream);
|
|
|
|
XmlComment comment = doc.CreateComment("THIS SCHEMA IS AUTOGENERATED FROM THE CURRENT ADDIN REPOSITORY. DO NOT MODIFY THIS FILE DIRECTLY.");
|
|
doc.InsertBefore(comment, doc.DocumentElement);
|
|
|
|
using (var stream = System.IO.File.OpenWrite(filename))
|
|
{
|
|
XmlWriterSettings settings = new XmlWriterSettings();
|
|
settings.Indent = true;
|
|
using (XmlWriter writer = XmlWriter.Create(stream, settings))
|
|
{
|
|
doc.WriteTo(writer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateComplexTypes(XmlSchema schema)
|
|
{
|
|
schema.Items.Add(CreateImportResourceType());
|
|
schema.Items.Add(CreateDescriptionNodeType());
|
|
schema.Items.Add(CreateAbstractNodeType());
|
|
schema.Items.Add(CreateAbstractExtensionNodeType());
|
|
schema.Items.Add(CreateExtensionNodeType());
|
|
schema.Items.Add(CreateAbstractExtensionTypeType());
|
|
schema.Items.Add(CreateSimpleConditionGroup());
|
|
schema.Items.Add(CreateConditionGroup());
|
|
schema.Items.Add(CreateNodeOrConditionGroup());
|
|
CreateConditionalOperatorTypes(schema);
|
|
schema.Items.Add(CreateComplexConditionType());
|
|
|
|
if (m_conditionalTargets.Count > 0)
|
|
{
|
|
schema.Items.Add(CreateTargetAttributeGroup());
|
|
}
|
|
}
|
|
|
|
private XmlSchemaAttributeGroup CreateTargetAttributeGroup()
|
|
{
|
|
XmlSchemaAttributeGroup group = new XmlSchemaAttributeGroup();
|
|
group.Name = TargetAttributeGroup;
|
|
|
|
XmlSchemaAttribute attribute = CreateAttribute(AddinXmlSerializer.TargetAttribute, "Target on which to apply the condition", true, null);
|
|
|
|
XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType();
|
|
XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
|
|
|
|
restriction.BaseTypeName = GetSimpleTypeQualifiedName(typeof(string));
|
|
|
|
foreach (KeyValuePair<string, string> conditionalTarget in m_conditionalTargets)
|
|
{
|
|
XmlSchemaEnumerationFacet enumFacet = new XmlSchemaEnumerationFacet();
|
|
enumFacet.Value = conditionalTarget.Key;
|
|
if (!string.IsNullOrEmpty(conditionalTarget.Value))
|
|
{
|
|
enumFacet.Annotation = CreateAnnotation(conditionalTarget.Value);
|
|
}
|
|
restriction.Facets.Add(enumFacet);
|
|
}
|
|
|
|
simpleType.Content = restriction;
|
|
attribute.SchemaType = simpleType;
|
|
|
|
group.Attributes.Add(attribute);
|
|
return group;
|
|
}
|
|
|
|
private XmlSchemaGroup CreateNodeOrConditionGroup()
|
|
{
|
|
XmlSchemaGroup group = new XmlSchemaGroup();
|
|
group.Name = NodeOrConditionGroup;
|
|
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
|
|
XmlSchemaGroupRef conditionRef = new XmlSchemaGroupRef();
|
|
conditionRef.RefName = new XmlQualifiedName(ConditionGroup);
|
|
conditionRef.MinOccurs = 0;
|
|
conditionRef.MaxOccurs = decimal.MaxValue;
|
|
choice.Items.Add(conditionRef);
|
|
|
|
foreach (ExtensionType extensionType in m_extensionTypeToType.Keys)
|
|
{
|
|
XmlSchemaElement element = CreateComplexElementReference(extensionType.Name, ExtensionTypePrefix + extensionType.Name, extensionType.Description);
|
|
element.MinOccurs = 0;
|
|
element.MaxOccurs = decimal.MaxValue;
|
|
|
|
choice.Items.Add(element);
|
|
}
|
|
|
|
group.Particle = choice;
|
|
|
|
return group;
|
|
}
|
|
|
|
private XmlSchemaGroup CreateConditionGroup()
|
|
{
|
|
XmlSchemaGroup group = new XmlSchemaGroup();
|
|
group.Name = ConditionGroup;
|
|
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
|
|
// Add all condition types
|
|
foreach (ConditionType type in m_conditionTypeToType.Keys)
|
|
{
|
|
XmlSchemaElement conditionElement = CreateComplexElement(AddinXmlSerializer.ConditionPrefix + "." + type.Name, type.Description);
|
|
XmlSchemaComplexType conditionComplexType = CreateComplexType(null);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(ConditionTypePrefix + type.Name);
|
|
contentModel.Content = extension;
|
|
conditionComplexType.ContentModel = contentModel;
|
|
|
|
// Add target attribute
|
|
if (m_conditionalTargets.Count > 0)
|
|
{
|
|
extension.Attributes.Add(CreateTargetAttribute());
|
|
}
|
|
|
|
// Add children
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(NodeOrConditionGroup);
|
|
XmlSchemaChoice conditionChoice = new XmlSchemaChoice();
|
|
conditionChoice.MinOccurs = 0;
|
|
conditionChoice.MaxOccurs = decimal.MaxValue;
|
|
conditionChoice.Items.Add(groupRef);
|
|
extension.Particle = conditionChoice;
|
|
|
|
conditionElement.SchemaType = conditionComplexType;
|
|
choice.Items.Add(conditionElement);
|
|
}
|
|
|
|
// Add complex condition
|
|
XmlSchemaElement complexConditionElement = CreateComplexElementReference(AddinXmlSerializer.ConditionPrefix, ComplexConditionType, "Implements a complex condition.");
|
|
complexConditionElement.MinOccurs = 0;
|
|
complexConditionElement.MaxOccurs = decimal.MaxValue;
|
|
choice.Items.Add(complexConditionElement);
|
|
|
|
group.Particle = choice;
|
|
|
|
return group;
|
|
}
|
|
|
|
private XmlSchemaGroup CreateSimpleConditionGroup()
|
|
{
|
|
XmlSchemaGroup group = new XmlSchemaGroup();
|
|
group.Name = SimpleConditionGroup;
|
|
|
|
// Add all condition types
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
foreach (ConditionType type in m_conditionTypeToType.Keys)
|
|
{
|
|
XmlSchemaElement conditionElement = CreateComplexElementReference(AddinXmlSerializer.ConditionPrefix + "." + type.Name, ConditionTypePrefix + type.Name, type.Description);
|
|
choice.Items.Add(conditionElement);
|
|
}
|
|
|
|
group.Particle = choice;
|
|
|
|
return group;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateComplexConditionType()
|
|
{
|
|
XmlSchemaComplexType complexConditionType = CreateComplexType(ComplexConditionType);
|
|
|
|
if (m_conditionalTargets.Count > 0)
|
|
{
|
|
complexConditionType.Attributes.Add(CreateTargetAttribute());
|
|
}
|
|
|
|
XmlSchemaSequence sequence = new XmlSchemaSequence();
|
|
|
|
// First child must be conditional operator
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.MinOccurs = 1;
|
|
groupRef.MaxOccurs = 1;
|
|
groupRef.RefName = new XmlQualifiedName(ConditionalOperatorGroup);
|
|
sequence.Items.Add(groupRef);
|
|
|
|
// Other children can be anything
|
|
groupRef = new XmlSchemaGroupRef();
|
|
groupRef.MinOccurs = 1;
|
|
groupRef.MaxOccurs = decimal.MaxValue;
|
|
groupRef.RefName = new XmlQualifiedName(NodeOrConditionGroup);
|
|
sequence.Items.Add(groupRef);
|
|
|
|
complexConditionType.Particle = sequence;
|
|
return complexConditionType;
|
|
}
|
|
|
|
private void CreateConditionalOperatorTypes(XmlSchema schema)
|
|
{
|
|
System.Diagnostics.Debug.Assert(schema != null);
|
|
|
|
List<XmlSchemaComplexType> allConditionalOperators = new List<XmlSchemaComplexType>();
|
|
|
|
// Not
|
|
XmlSchemaComplexType notComplexType = CreateConditionalOperatorComplexType(AddinXmlSerializer.NotNode, true, "Implements a NOT operator.");
|
|
schema.Items.Add(notComplexType);
|
|
allConditionalOperators.Add(notComplexType);
|
|
|
|
// Or
|
|
XmlSchemaComplexType orComplexType = CreateConditionalOperatorComplexType(AddinXmlSerializer.OrNode, false, "Implements an OR operator.");
|
|
schema.Items.Add(orComplexType);
|
|
allConditionalOperators.Add(orComplexType);
|
|
|
|
// And
|
|
XmlSchemaComplexType andComplexType = CreateConditionalOperatorComplexType(AddinXmlSerializer.AndNode, false, "Implements an AND operator.");
|
|
schema.Items.Add(andComplexType);
|
|
allConditionalOperators.Add(andComplexType);
|
|
|
|
// Xor
|
|
XmlSchemaComplexType xorComplexType = CreateConditionalOperatorComplexType(AddinXmlSerializer.XorNode, false, "Implements a XOR operator.");
|
|
schema.Items.Add(xorComplexType);
|
|
allConditionalOperators.Add(xorComplexType);
|
|
|
|
// True
|
|
XmlSchemaComplexType trueComplexType = CreateComplexType(AddinXmlSerializer.TrueNode);
|
|
trueComplexType.Annotation = CreateAnnotation("A condition that always evaluate to true.");
|
|
schema.Items.Add(trueComplexType);
|
|
allConditionalOperators.Add(trueComplexType);
|
|
|
|
// False
|
|
XmlSchemaComplexType falseComplexType = CreateComplexType(AddinXmlSerializer.FalseNode);
|
|
falseComplexType.Annotation = CreateAnnotation("A condition that always evaluate to false.");
|
|
schema.Items.Add(falseComplexType);
|
|
allConditionalOperators.Add(falseComplexType);
|
|
|
|
XmlSchemaGroup group = new XmlSchemaGroup();
|
|
group.Name = ConditionalOperatorGroup;
|
|
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
foreach (XmlSchemaComplexType complexType in allConditionalOperators)
|
|
{
|
|
choice.Items.Add(CreateComplexElementReference(complexType.Name, complexType.Name, null));
|
|
}
|
|
|
|
group.Particle = choice;
|
|
schema.Items.Add(group);
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateConditionalOperatorComplexType(string name, bool restricted, string description)
|
|
{
|
|
XmlSchemaComplexType complexType = CreateComplexType(name);
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
choice.Items.Add(CreateConditionalOperatorGroupRef());
|
|
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(SimpleConditionGroup);
|
|
choice.Items.Add(groupRef);
|
|
|
|
complexType.Particle = choice;
|
|
complexType.Annotation = CreateAnnotation(description);
|
|
|
|
if (restricted)
|
|
{
|
|
complexType.Particle.MinOccurs = 1;
|
|
complexType.Particle.MaxOccurs = 1;
|
|
}
|
|
else
|
|
{
|
|
complexType.Particle.MinOccurs = 1;
|
|
complexType.Particle.MaxOccurs = decimal.MaxValue;
|
|
}
|
|
|
|
return complexType;
|
|
}
|
|
|
|
private XmlSchemaGroupRef CreateConditionalOperatorGroupRef()
|
|
{
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(ConditionalOperatorGroup);
|
|
return groupRef;
|
|
}
|
|
|
|
private XmlSchemaAttributeGroupRef CreateTargetAttribute()
|
|
{
|
|
XmlSchemaAttributeGroupRef groupRef = new XmlSchemaAttributeGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(TargetAttributeGroup);
|
|
return groupRef;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateImportResourceType()
|
|
{
|
|
XmlSchemaComplexType importResourceType = CreateComplexType(ImportResourceType);
|
|
|
|
importResourceType.Attributes.Add(CreateAttribute(AddinXmlSerializer.ResourceAttribute, "External resource file used by the current addin/module/assembly to get localized resources", true));
|
|
|
|
return importResourceType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateDescriptionNodeType()
|
|
{
|
|
XmlSchemaComplexType descriptionNodeType = CreateComplexType(DescriptionType);
|
|
|
|
XmlSchemaSimpleContent contentModel = new XmlSchemaSimpleContent();
|
|
XmlSchemaSimpleContentExtension extension = new XmlSchemaSimpleContentExtension();
|
|
extension.BaseTypeName = XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.String).QualifiedName;
|
|
contentModel.Content = extension;
|
|
descriptionNodeType.ContentModel = contentModel;
|
|
|
|
return descriptionNodeType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateAbstractNodeType()
|
|
{
|
|
// <xs:complex-type name="AbstractNode">
|
|
XmlSchemaComplexType abstractNodeType = CreateComplexType(AbstractNodeType);
|
|
|
|
// Attributes
|
|
abstractNodeType.Attributes.Add(CreateAttribute(AddinXmlSerializer.DescriptionAttribute, "Description of the node.", true));
|
|
|
|
// Child elements
|
|
XmlSchemaElement descriptionElement = CreateComplexElementReference(AddinXmlSerializer.DescriptionNode, DescriptionType, "Description of the node.");
|
|
descriptionElement.MinOccurs = 0;
|
|
descriptionElement.MaxOccurs = 1;
|
|
|
|
abstractNodeType.Particle = CreateParticle(descriptionElement);
|
|
abstractNodeType.Particle.MinOccurs = 0;
|
|
abstractNodeType.Particle.MaxOccurs = 1;
|
|
|
|
return abstractNodeType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateAbstractExtensionTypeType()
|
|
{
|
|
XmlSchemaComplexType abstractExtensionType = CreateComplexType(AbstractExtensionTypeType);
|
|
|
|
// Attributes
|
|
abstractExtensionType.Attributes.Add(CreateAttribute(ExtensionNodeDescription.IdAttributeName, "Id of the node.", true));
|
|
abstractExtensionType.Attributes.Add(CreateAttribute(ExtensionNodeDescription.InsertBeforeAttributeName, "Id of nodes that need to be placed after this node, separated by commas", true));
|
|
abstractExtensionType.Attributes.Add(CreateAttribute(ExtensionNodeDescription.InsertAfterAttributeName, "Id of nodes that need to be placed before this node, separated by commas", true));
|
|
|
|
return abstractExtensionType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateAbstractExtensionNodeType()
|
|
{
|
|
XmlSchemaComplexType abstractExtensionNodeType = CreateComplexType(AbstractExtensionNodeType);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractNodeType);
|
|
contentModel.Content = extension;
|
|
abstractExtensionNodeType.ContentModel = contentModel;
|
|
|
|
// Child elements
|
|
XmlSchemaElement nodeElement = CreateComplexElementReference(AddinXmlSerializer.ExtensionNodeNode, ExtensionNodeType, "Defines a type of extension node valid in this context.");
|
|
nodeElement.MinOccurs = 0;
|
|
nodeElement.MaxOccurs = decimal.MaxValue;
|
|
|
|
XmlSchemaElement nodeSetElement = CreateComplexElement(AddinXmlSerializer.ExtensionNodeSetNode, "References an existing extension node set.");
|
|
XmlSchemaComplexType nodeSetElementType = new XmlSchemaComplexType();
|
|
nodeSetElementType.Attributes.Add(CreateAttribute(AddinXmlSerializer.NameAttribute, "Name of the extension node set.", false));
|
|
nodeSetElement.SchemaType = nodeSetElementType;
|
|
|
|
extension.Particle = CreateParticle(nodeElement, nodeSetElement);
|
|
|
|
return abstractExtensionNodeType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateExtensionNodeType()
|
|
{
|
|
// <xs:complex-type name="ExtensionNode">
|
|
XmlSchemaComplexType extensionNodeType = CreateComplexType(ExtensionNodeType);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractExtensionNodeType);
|
|
contentModel.Content = extension;
|
|
extensionNodeType.ContentModel = contentModel;
|
|
|
|
// Attributes
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.NameAttribute, "Name of the extension node. This name is used to identify the extension node in the extensions where it is used.", false));
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.TypeAttribute, "CLR type corresponding to the extension node. It must derive from " + typeof(ExtensionNode).FullName + " and it must be fully qualified.", true));
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.ObjectTypeAttribute, "Base class of objects that this type of node will produce. This is only used when type is not specified.", true));
|
|
|
|
return extensionNodeType;
|
|
}
|
|
|
|
private XmlSchemaElement CreateModuleElement()
|
|
{
|
|
XmlSchemaElement moduleElement = CreateComplexElement(AddinXmlSerializer.ModuleNode, "Describes an optional module in the addin.");
|
|
XmlSchemaComplexType moduleType = CreateComplexType(null);
|
|
|
|
// Child elements
|
|
moduleType.Particle = CreateParticle(CreateBaseModuleElements().ToArray());
|
|
|
|
moduleElement.SchemaType = moduleType;
|
|
return moduleElement;
|
|
}
|
|
|
|
private List<XmlSchemaElement> CreateBaseModuleElements()
|
|
{
|
|
List<XmlSchemaElement> elements = new List<XmlSchemaElement>();
|
|
|
|
// Files
|
|
XmlSchemaElement filesElement = CreateFilesElement();
|
|
filesElement.MinOccurs = 0;
|
|
filesElement.MaxOccurs = decimal.MaxValue;
|
|
elements.Add(filesElement);
|
|
|
|
// Dependencies
|
|
XmlSchemaElement dependenciesElement = CreateDependenciesElement();
|
|
dependenciesElement.MinOccurs = 0;
|
|
dependenciesElement.MaxOccurs = decimal.MaxValue;
|
|
elements.Add(dependenciesElement);
|
|
|
|
// Extension
|
|
XmlSchemaElement extensionElement = CreateExtensionElement();
|
|
extensionElement.MinOccurs = 0;
|
|
extensionElement.MaxOccurs = decimal.MaxValue;
|
|
elements.Add(extensionElement);
|
|
|
|
return elements;
|
|
}
|
|
|
|
private XmlSchemaElement CreateExtensionElement()
|
|
{
|
|
XmlSchemaElement extensionElement = CreateComplexElement(AddinXmlSerializer.ExtensionNode, "Defines an extension to an existing extension point.");
|
|
XmlSchemaComplexType extensionElementType = CreateComplexType(null);
|
|
|
|
extensionElementType.Attributes.Add(CreateAttribute(AddinXmlSerializer.PathAttribute, "Path of the extension point to extend.", false));
|
|
|
|
// Child elements
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(NodeOrConditionGroup);
|
|
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
choice.MinOccurs = 0;
|
|
choice.MaxOccurs = decimal.MaxValue;
|
|
choice.Items.Add(groupRef);
|
|
extensionElementType.Particle = choice;
|
|
|
|
extensionElement.SchemaType = extensionElementType;
|
|
return extensionElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateFilesElement()
|
|
{
|
|
XmlSchemaElement filesElement = CreateComplexElement(AddinXmlSerializer.FilesNode, "Describes the files used by the current addin/module.");
|
|
XmlSchemaComplexType filesType = CreateComplexType(null);
|
|
|
|
XmlSchemaElement importElement = CreateComplexElement(AddinXmlSerializer.ImportNode, "Describes a file used by the current addin/module.");
|
|
XmlSchemaComplexType importType = CreateComplexType(null);
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(ImportResourceType);
|
|
contentModel.Content = extension;
|
|
importType.ContentModel = contentModel;
|
|
|
|
// Import attributes (resource attribute is already in base type)
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.AssemblyAttribute, "Assembly used by the current addin/module to instantiate types", true));
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.FileAttribute, "File used by the current addin/module", true));
|
|
|
|
XmlSchemaElement childImportNode = CreateComplexElementReference(AddinXmlSerializer.ImportNode, ImportResourceType, "Describes a file used by the current addin/module/assembly");
|
|
childImportNode.MinOccurs = 0;
|
|
childImportNode.MaxOccurs = decimal.MaxValue;
|
|
extension.Particle = CreateParticle(childImportNode);
|
|
|
|
importElement.SchemaType = importType;
|
|
importElement.MinOccurs = 0;
|
|
importElement.MaxOccurs = decimal.MaxValue;
|
|
|
|
filesType.Particle = CreateParticle(importElement);
|
|
|
|
filesElement.SchemaType = filesType;
|
|
return filesElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateDependenciesElement()
|
|
{
|
|
XmlSchemaElement dependenciesElement = CreateComplexElement(AddinXmlSerializer.DependenciesNode, "Describes the dependencies of the current addin/module. An addin/module can only be loaded if all its dependencies are also loaded.");
|
|
XmlSchemaComplexType dependenciesType = CreateComplexType(null);
|
|
|
|
XmlSchemaElement addinDependencyElement = CreateComplexElement(AddinXmlSerializer.AddinNode, "A dependency on another addin.");
|
|
XmlSchemaComplexType addinDependencyType = CreateComplexType(null);
|
|
|
|
addinDependencyType.Attributes.Add(CreateAttribute(AddinXmlSerializer.IdAttribute, "Fully qualified id of the addin.", false));
|
|
addinDependencyType.Attributes.Add(CreateAttribute(AddinXmlSerializer.VersionAttribute, "Version or version range of the addin. When specified, the dependency will refer to a specific version range of the addin.", true));
|
|
addinDependencyElement.SchemaType = addinDependencyType;
|
|
|
|
dependenciesType.Particle = CreateParticle(addinDependencyElement);
|
|
dependenciesElement.SchemaType = dependenciesType;
|
|
return dependenciesElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateRootElement()
|
|
{
|
|
XmlSchemaElement rootElement = CreateComplexElement(XmlSerializerWriter.RootElement, "Root element");
|
|
XmlSchemaComplexType rootType = CreateComplexType(null);
|
|
|
|
XmlSchemaElement addinElement = CreateAddinElement();
|
|
|
|
rootType.Particle = CreateParticle(addinElement);
|
|
|
|
rootElement.SchemaType = rootType;
|
|
return rootElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateAddinElement()
|
|
{
|
|
// Addin element
|
|
XmlSchemaElement addinElement = CreateComplexElement(AddinXmlSerializer.AddinNode, "Defines an addin.");
|
|
|
|
XmlSchemaComplexType addinType = CreateComplexType(null);
|
|
|
|
// Attributes
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.IdAttribute, "Unique id of the addin.", false));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.NameAttribute, "Friendly name of the addin. If not specified, the name of the addin will be its id.", true));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.VersionAttribute, "Version of the addin.", true));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.AuthorAttribute, "Author of the addin.", true));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.UrlAttribute, "Url related to the addin.", true));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.DescriptionAttribute, "Description of the addin.", true));
|
|
addinType.Attributes.Add(CreateAttribute(AddinXmlSerializer.CategoryAttribute, "Category of the addin.", true));
|
|
|
|
// Child elements
|
|
List<XmlSchemaElement> childElements = CreateBaseModuleElements();
|
|
|
|
// ConditionNode
|
|
XmlSchemaElement conditionNodeElement = CreateConditionNodeElement();
|
|
childElements.Add(conditionNodeElement);
|
|
|
|
// Extension point
|
|
XmlSchemaElement extensionPointElement = CreateExtensionPointElement();
|
|
childElements.Add(extensionPointElement);
|
|
|
|
// Extension node set
|
|
XmlSchemaElement extensionNodeSetElement = CreateExtensionNodeSetElement();
|
|
childElements.Add(extensionNodeSetElement);
|
|
|
|
// Include
|
|
XmlSchemaElement includeElement = CreateIncludeElement();
|
|
childElements.Add(includeElement);
|
|
|
|
// Module
|
|
XmlSchemaElement moduleElement = CreateModuleElement();
|
|
childElements.Add(moduleElement);
|
|
|
|
addinType.Particle = CreateParticle(childElements.ToArray());
|
|
|
|
addinElement.SchemaType = addinType;
|
|
return addinElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateConditionNodeElement()
|
|
{
|
|
XmlSchemaElement conditionNodeElement = CreateComplexElement(AddinXmlSerializer.ConditionNodeNode, "Defines a new type of condition.");
|
|
XmlSchemaComplexType conditionNodeType = CreateComplexType(null);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractNodeType);
|
|
contentModel.Content = extension;
|
|
conditionNodeType.ContentModel = contentModel;
|
|
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.NameAttribute, "Name of the condition node. This name is used to identify the node in the extensions where it is used.", false));
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.TypeAttribute, "CLR type corresponding to the condition node. It must derive from either " + typeof(ConditionNode).FullName + " or " + typeof(PolledConditionNode).FullName + " and it must be fully qualified.", false));
|
|
|
|
conditionNodeElement.SchemaType = conditionNodeType;
|
|
return conditionNodeElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateExtensionPointElement()
|
|
{
|
|
XmlSchemaElement extensionPointElement = CreateComplexElement(AddinXmlSerializer.ExtensionPointNode, "Defines a new extension point.");
|
|
XmlSchemaComplexType extensionPointType = CreateComplexType(null);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractExtensionNodeType);
|
|
contentModel.Content = extension;
|
|
extensionPointType.ContentModel = contentModel;
|
|
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.PathAttribute, "Path of the extension point.", false));
|
|
|
|
extensionPointElement.SchemaType = extensionPointType;
|
|
return extensionPointElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateExtensionNodeSetElement()
|
|
{
|
|
XmlSchemaElement extensionNodeSetElement = CreateComplexElement(AddinXmlSerializer.ExtensionNodeSetNode, "Defines a new extension node set.");
|
|
XmlSchemaComplexType extensionNodeSetType = CreateComplexType(null);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractExtensionNodeType);
|
|
contentModel.Content = extension;
|
|
extensionNodeSetType.ContentModel = contentModel;
|
|
|
|
extension.Attributes.Add(CreateAttribute(AddinXmlSerializer.NameAttribute, "Name of the extension node set.", false));
|
|
|
|
extensionNodeSetElement.SchemaType = extensionNodeSetType;
|
|
return extensionNodeSetElement;
|
|
}
|
|
|
|
private XmlSchemaElement CreateIncludeElement()
|
|
{
|
|
XmlSchemaElement includeElement = CreateComplexElement(AddinXmlSerializer.IncludeNode, "Includes a partial addin file.");
|
|
XmlSchemaComplexType includeType = CreateComplexType(null);
|
|
|
|
includeType.Attributes.Add(CreateAttribute(AddinXmlSerializer.FileAttribute, "Filename of the partial addin to include.", false));
|
|
|
|
includeElement.SchemaType = includeType;
|
|
return includeElement;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Data Collecting
|
|
|
|
private readonly Dictionary<string, ExtensionTypeSet> m_extensionTypeSets = new Dictionary<string, ExtensionTypeSet>();
|
|
private readonly Dictionary<ExtensionType, Type> m_extensionTypeToType = new Dictionary<ExtensionType, Type>();
|
|
private readonly Dictionary<ConditionType, Type> m_conditionTypeToType = new Dictionary<ConditionType, Type>();
|
|
private readonly Dictionary<string, string> m_conditionalTargets = new Dictionary<string, string>();
|
|
|
|
private void CollectTypes()
|
|
{
|
|
m_extensionTypeToType.Clear();
|
|
m_conditionTypeToType.Clear();
|
|
m_conditionalTargets.Clear();
|
|
|
|
CollectExtensionTypeSets();
|
|
|
|
ProcessExtensionTypeSets();
|
|
|
|
ProcessConditionalTargets();
|
|
}
|
|
|
|
private void ProcessExtensionTypeSets()
|
|
{
|
|
List<string> typeSetNames = CollectTypesInAddins();
|
|
|
|
foreach (ExtensionTypeSet extensionTypeSet in m_extensionTypeSets.Values)
|
|
{
|
|
ProcessExtensionTypeSet(extensionTypeSet, typeSetNames);
|
|
}
|
|
}
|
|
|
|
private void ProcessConditionalTargets()
|
|
{
|
|
foreach (Type clrType in m_extensionTypeToType.Values)
|
|
{
|
|
if (clrType != null)
|
|
{
|
|
// Check for conditional target attributes
|
|
foreach (ValueMemberInfo memberInfo in ValueMemberInfo.GetValueMembers(clrType, true))
|
|
{
|
|
ConditionalTargetAttribute attribute = (ConditionalTargetAttribute)Attribute.GetCustomAttribute(memberInfo.InnerMemberInfo, typeof(ConditionalTargetAttribute), true);
|
|
if (attribute != null)
|
|
{
|
|
AddConditionalTarget(memberInfo, attribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<string> CollectTypesInAddins()
|
|
{
|
|
List<string> typeSetNames = new List<string>();
|
|
foreach (Addin addin in m_repository.Addins)
|
|
{
|
|
foreach (ExtensionPoint extensionPoint in addin.ExtensionPoints)
|
|
{
|
|
ProcessExtensionTypeSet(extensionPoint.ExtensionTypeSet, typeSetNames);
|
|
}
|
|
|
|
foreach (ConditionType type in addin.ConditionTypes)
|
|
{
|
|
ProcessConditionType(type);
|
|
}
|
|
}
|
|
return typeSetNames;
|
|
}
|
|
|
|
private void CollectExtensionTypeSets()
|
|
{
|
|
m_extensionTypeSets.Clear();
|
|
foreach (Addin addin in m_repository.Addins)
|
|
{
|
|
foreach (ExtensionTypeSet typeSet in addin.ExtensionTypeSets)
|
|
{
|
|
// Ignore duplicates, we assume that the addin data is is relatively ok already.
|
|
if (!m_extensionTypeSets.ContainsKey(typeSet.Name))
|
|
{
|
|
m_extensionTypeSets.Add(typeSet.Name, typeSet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddConditionalTarget(ValueMemberInfo memberInfo, ConditionalTargetAttribute attribute)
|
|
{
|
|
System.Diagnostics.Debug.Assert(memberInfo != null);
|
|
System.Diagnostics.Debug.Assert(attribute != null);
|
|
|
|
string name = string.IsNullOrEmpty(attribute.Name) ? memberInfo.Name : attribute.Name;
|
|
|
|
if (!m_conditionalTargets.ContainsKey(name))
|
|
{
|
|
m_conditionalTargets.Add(name, attribute.Description);
|
|
}
|
|
}
|
|
|
|
private void ProcessExtensionTypeSet(ExtensionTypeSet typeSet, IList<string> processedSets)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
System.Diagnostics.Debug.Assert(processedSets != null);
|
|
System.Diagnostics.Debug.Assert(!processedSets.IsReadOnly);
|
|
|
|
foreach (ExtensionType type in typeSet.ExtensionTypes)
|
|
{
|
|
ProcessExtensionType(type);
|
|
ProcessExtensionTypeSet(type.ExtensionTypeSet, processedSets);
|
|
}
|
|
|
|
foreach (string typeSetName in typeSet.ExtensionTypeSets)
|
|
{
|
|
if (!processedSets.Contains(typeSetName))
|
|
{
|
|
processedSets.Add(typeSetName);
|
|
ExtensionTypeSet inlineTypeSet;
|
|
if (m_extensionTypeSets.TryGetValue(typeSetName, out inlineTypeSet))
|
|
{
|
|
ProcessExtensionTypeSet(inlineTypeSet, processedSets);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessExtensionType(ExtensionType type)
|
|
{
|
|
System.Diagnostics.Debug.Assert(type != null);
|
|
|
|
if (m_extensionTypeToType.ContainsKey(type))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Cannot have two elements with the same name in the xml schema, so we have to ignore types that have the same name :(
|
|
foreach (ExtensionType existingType in m_extensionTypeToType.Keys)
|
|
{
|
|
if (type.Name == existingType.Name)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_extensionTypeToType.Add(type, GetCLRType(type));
|
|
}
|
|
|
|
private void ProcessConditionType(ConditionType type)
|
|
{
|
|
System.Diagnostics.Debug.Assert(type != null);
|
|
|
|
if (!m_conditionTypeToType.ContainsKey(type))
|
|
{
|
|
m_conditionTypeToType.Add(type, GetType(type.Module, type.TypeName));
|
|
}
|
|
}
|
|
|
|
private Type GetCLRType(ExtensionType type)
|
|
{
|
|
if (string.IsNullOrEmpty(type.TypeName))
|
|
{
|
|
return typeof(TypeExtensionNode);
|
|
}
|
|
else
|
|
{
|
|
return GetType(type.Module, type.TypeName);
|
|
}
|
|
}
|
|
|
|
private Type GetType(Module module, string typeName)
|
|
{
|
|
// Look in module
|
|
Type type = module.GetType(typeName);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
|
|
// Look in dependencies
|
|
foreach (AddinReference reference in module.AllDependencies)
|
|
{
|
|
Addin addin;
|
|
if (m_addins.TryGetValue(reference.Name, out addin))
|
|
{
|
|
type = GetType(addin.MainModule, typeName);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void CreateDynamicTypes(XmlSchema schema)
|
|
{
|
|
System.Diagnostics.Debug.Assert(schema != null);
|
|
|
|
foreach (Addin addin in m_repository.Addins)
|
|
{
|
|
// Extension types
|
|
foreach (ExtensionPoint point in addin.ExtensionPoints)
|
|
{
|
|
foreach (XmlSchemaComplexType type in CreateDynamicTypesForExtensionTypeSet(point.ExtensionTypeSet))
|
|
{
|
|
schema.Items.Add(type);
|
|
}
|
|
}
|
|
|
|
foreach (ExtensionTypeSet typeSet in addin.ExtensionTypeSets)
|
|
{
|
|
foreach (XmlSchemaComplexType type in CreateDynamicTypesForExtensionTypeSet(typeSet))
|
|
{
|
|
schema.Items.Add(type);
|
|
}
|
|
}
|
|
|
|
// Condition types
|
|
foreach (ConditionType conditionType in addin.ConditionTypes)
|
|
{
|
|
XmlSchemaComplexType type = CreateDynamicTypeForConditionType(conditionType);
|
|
if (type != null)
|
|
{
|
|
schema.Items.Add(type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private IEnumerable<XmlSchemaComplexType> CreateDynamicTypesForExtensionTypeSet(ExtensionTypeSet typeSet)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
|
|
foreach (ExtensionType type in typeSet.ExtensionTypes)
|
|
{
|
|
if (m_extensionTypeToType.ContainsKey(type))
|
|
{
|
|
XmlSchemaComplexType schemaType = CreateExtensionTypeElement(type);
|
|
if (schemaType != null)
|
|
{
|
|
yield return schemaType;
|
|
}
|
|
}
|
|
|
|
foreach (XmlSchemaComplexType childSchemaType in CreateDynamicTypesForExtensionTypeSet(type.ExtensionTypeSet))
|
|
{
|
|
yield return childSchemaType;
|
|
}
|
|
}
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateExtensionTypeElement(ExtensionType type)
|
|
{
|
|
System.Diagnostics.Debug.Assert(type != null);
|
|
|
|
XmlSchemaComplexType complexType = CreateComplexType(ExtensionTypePrefix + type.Name);
|
|
|
|
XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
|
|
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
|
|
extension.BaseTypeName = new XmlQualifiedName(AbstractExtensionTypeType);
|
|
contentModel.Content = extension;
|
|
complexType.ContentModel = contentModel;
|
|
|
|
// Attributes
|
|
System.Diagnostics.Debug.Assert(m_extensionTypeToType.ContainsKey(type));
|
|
Type clrType = m_extensionTypeToType[type];
|
|
if (clrType != null)
|
|
{
|
|
foreach (XmlSchemaAttribute attribute in CreateDynamicAttributes(clrType))
|
|
{
|
|
extension.Attributes.Add(attribute);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_logger.LogWarning("Could not find type {0}, ignoring...", type.TypeName);
|
|
}
|
|
|
|
// Child elements
|
|
List<XmlSchemaObject> childElements = new List<XmlSchemaObject>();
|
|
foreach(ExtensionType childType in CollectChildTypes(type.ExtensionTypeSet))
|
|
{
|
|
XmlSchemaElement childElement = CreateComplexElementReference(childType.Name, ExtensionTypePrefix + childType.Name, childType.Description);
|
|
childElement.MinOccurs = 0;
|
|
childElement.MaxOccurs = decimal.MaxValue;
|
|
childElements.Add(childElement);
|
|
}
|
|
|
|
XmlSchemaGroupRef groupRef = new XmlSchemaGroupRef();
|
|
groupRef.RefName = new XmlQualifiedName(ConditionGroup);
|
|
childElements.Add(groupRef);
|
|
|
|
extension.Particle = CreateParticle(childElements.ToArray());
|
|
|
|
return complexType;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateDynamicTypeForConditionType(ConditionType type)
|
|
{
|
|
System.Diagnostics.Debug.Assert(type != null);
|
|
|
|
XmlSchemaComplexType complexType = CreateComplexType(ConditionTypePrefix + type.Name);
|
|
|
|
// Attributes
|
|
System.Diagnostics.Debug.Assert(m_conditionTypeToType.ContainsKey(type));
|
|
Type clrType = m_conditionTypeToType[type];
|
|
if (clrType != null)
|
|
{
|
|
foreach (XmlSchemaAttribute attribute in CreateDynamicAttributes(clrType))
|
|
{
|
|
complexType.Attributes.Add(attribute);
|
|
}
|
|
}
|
|
|
|
return complexType;
|
|
}
|
|
|
|
private IEnumerable<ExtensionType> CollectChildTypes(ExtensionTypeSet typeSet)
|
|
{
|
|
System.Diagnostics.Debug.Assert(typeSet != null);
|
|
|
|
foreach (ExtensionType type in typeSet.ExtensionTypes)
|
|
{
|
|
yield return type;
|
|
}
|
|
|
|
foreach (string setReference in typeSet.ExtensionTypeSets)
|
|
{
|
|
ExtensionTypeSet inlineSet;
|
|
if (m_extensionTypeSets.TryGetValue(setReference, out inlineSet))
|
|
{
|
|
foreach(ExtensionType type in CollectChildTypes(inlineSet))
|
|
{
|
|
yield return type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private IEnumerable<XmlSchemaAttribute> CreateDynamicAttributes(Type clrType)
|
|
{
|
|
System.Diagnostics.Debug.Assert(clrType != null);
|
|
|
|
foreach (ValueMemberInfo memberInfo in ValueMemberInfo.GetValueMembers(clrType, true))
|
|
{
|
|
NodeAttributeAttribute attribute = (NodeAttributeAttribute)Attribute.GetCustomAttribute(memberInfo.InnerMemberInfo, typeof(NodeAttributeAttribute), true);
|
|
if (attribute != null)
|
|
{
|
|
yield return CreateDynamicAttribute(memberInfo, attribute);
|
|
}
|
|
}
|
|
}
|
|
|
|
private XmlSchemaAttribute CreateDynamicAttribute(ValueMemberInfo memberInfo, NodeAttributeAttribute attribute)
|
|
{
|
|
System.Diagnostics.Debug.Assert(memberInfo != null);
|
|
System.Diagnostics.Debug.Assert(attribute != null);
|
|
|
|
string name = string.IsNullOrEmpty(attribute.Name) ? memberInfo.Name : attribute.Name;
|
|
|
|
return CreateAttribute(name, attribute.Description, !attribute.Required, memberInfo.ValueType);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper methods
|
|
|
|
private XmlSchemaChoice CreateParticle(params XmlSchemaObject[] elements)
|
|
{
|
|
XmlSchemaChoice choice = new XmlSchemaChoice();
|
|
|
|
foreach (XmlSchemaObject element in elements)
|
|
{
|
|
choice.Items.Add(element);
|
|
}
|
|
|
|
choice.MinOccurs = 0;
|
|
choice.MaxOccurs = decimal.MaxValue;
|
|
return choice;
|
|
}
|
|
|
|
private XmlSchemaElement CreateComplexElement(string elementName, string description)
|
|
{
|
|
XmlSchemaElement element = new XmlSchemaElement();
|
|
|
|
element.Name = elementName;
|
|
element.Annotation = CreateAnnotation(description);
|
|
|
|
return element;
|
|
}
|
|
|
|
private XmlSchemaElement CreateComplexElementReference(string elementName, string complexTypeReference, string description)
|
|
{
|
|
XmlSchemaElement element = new XmlSchemaElement();
|
|
|
|
element.Name = elementName;
|
|
if (!string.IsNullOrEmpty(description))
|
|
{
|
|
element.Annotation = CreateAnnotation(description);
|
|
}
|
|
element.SchemaTypeName = new XmlQualifiedName(complexTypeReference);
|
|
|
|
return element;
|
|
}
|
|
|
|
private XmlSchemaComplexType CreateComplexType(string name)
|
|
{
|
|
XmlSchemaComplexType complexType = new XmlSchemaComplexType();
|
|
complexType.Name = name;
|
|
return complexType;
|
|
}
|
|
|
|
private XmlSchemaAttribute CreateAttribute(string attributeName, string description, bool optional)
|
|
{
|
|
return CreateAttribute(attributeName, description, optional, typeof(string));
|
|
}
|
|
|
|
private XmlSchemaAttribute CreateAttribute(string attributeName, string description, bool optional, Type type)
|
|
{
|
|
if (optional && !string.IsNullOrEmpty(description))
|
|
{
|
|
description += " [Optional]";
|
|
}
|
|
|
|
XmlSchemaAttribute attribute = new XmlSchemaAttribute();
|
|
attribute.Name = attributeName;
|
|
attribute.Annotation = CreateAnnotation(description);
|
|
|
|
if (type != null)
|
|
{
|
|
if (type.IsEnum)
|
|
{
|
|
FillEnumAttribute(attribute, type);
|
|
}
|
|
else if (type == typeof(bool)) // Special case for booleans
|
|
{
|
|
FillBoolAttribute(attribute);
|
|
}
|
|
else
|
|
{
|
|
FillGenericAttribute(attribute, type);
|
|
}
|
|
}
|
|
|
|
attribute.Use = optional ? XmlSchemaUse.Optional : XmlSchemaUse.Required;
|
|
|
|
return attribute;
|
|
}
|
|
|
|
private static void FillGenericAttribute(XmlSchemaAttribute attribute, Type type)
|
|
{
|
|
XmlQualifiedName typeName = GetSimpleTypeQualifiedName(type);
|
|
|
|
if (typeName == null)
|
|
{
|
|
typeName = GetSimpleTypeQualifiedName(typeof(string));
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(typeName != null);
|
|
attribute.SchemaTypeName = typeName;
|
|
}
|
|
|
|
private static void FillBoolAttribute(XmlSchemaAttribute attribute)
|
|
{
|
|
XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType();
|
|
XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
|
|
|
|
restriction.BaseTypeName = GetSimpleTypeQualifiedName(typeof(string));
|
|
|
|
XmlSchemaEnumerationFacet enumFacet = new XmlSchemaEnumerationFacet();
|
|
enumFacet.Value = bool.TrueString;
|
|
restriction.Facets.Add(enumFacet);
|
|
|
|
enumFacet = new XmlSchemaEnumerationFacet();
|
|
enumFacet.Value = bool.FalseString;
|
|
restriction.Facets.Add(enumFacet);
|
|
|
|
simpleType.Content = restriction;
|
|
attribute.SchemaType = simpleType;
|
|
}
|
|
|
|
private static void FillEnumAttribute(XmlSchemaAttribute attribute, Type type)
|
|
{
|
|
XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType();
|
|
XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction();
|
|
|
|
restriction.BaseTypeName = GetSimpleTypeQualifiedName(typeof(string));
|
|
|
|
foreach (string enumValue in Enum.GetNames(type))
|
|
{
|
|
XmlSchemaEnumerationFacet enumFacet = new XmlSchemaEnumerationFacet();
|
|
enumFacet.Value = enumValue;
|
|
restriction.Facets.Add(enumFacet);
|
|
}
|
|
|
|
simpleType.Content = restriction;
|
|
attribute.SchemaType = simpleType;
|
|
}
|
|
|
|
private static XmlQualifiedName GetSimpleTypeQualifiedName(Type type)
|
|
{
|
|
if (type == typeof(string))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.String).QualifiedName;
|
|
}
|
|
else if (type == typeof(byte))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.UnsignedByte).QualifiedName;
|
|
}
|
|
else if (type == typeof(sbyte))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Byte).QualifiedName;
|
|
}
|
|
else if (type == typeof(short))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Short).QualifiedName;
|
|
}
|
|
else if (type == typeof(ushort))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.UnsignedShort).QualifiedName;
|
|
}
|
|
else if (type == typeof(int))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Int).QualifiedName;
|
|
}
|
|
else if (type == typeof(uint))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.UnsignedInt).QualifiedName;
|
|
}
|
|
else if (type == typeof(long))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Long).QualifiedName;
|
|
}
|
|
else if (type == typeof(ulong))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.UnsignedLong).QualifiedName;
|
|
}
|
|
else if (type == typeof(float))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Float).QualifiedName;
|
|
}
|
|
else if (type == typeof(double))
|
|
{
|
|
return XmlSchemaSimpleType.GetBuiltInSimpleType(XmlTypeCode.Double).QualifiedName;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private XmlSchemaAnnotation CreateAnnotation(string description)
|
|
{
|
|
XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
|
|
XmlSchemaDocumentation doc = new XmlSchemaDocumentation();
|
|
doc.Markup = TextToNodeArray(description);
|
|
annotation.Items.Add(doc);
|
|
return annotation;
|
|
}
|
|
|
|
private XmlNode[] TextToNodeArray(string text)
|
|
{
|
|
return new XmlNode[1] { m_factoryDocument.CreateTextNode(text) };
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|