From 6677f7c490d1476d1e7d00a72fc03f2b8f1ac324 Mon Sep 17 00:00:00 2001 From: Kirill Osenkov Date: Wed, 4 Aug 2021 13:05:10 -0700 Subject: [PATCH] Restore XmlSchemaCompletionProvider.cs as this is an API we can't remove It's used by VSMac XmlEditorContext --- .../Completion/XmlSchemaCompletionProvider.cs | 956 +++++++++++++++++- 1 file changed, 922 insertions(+), 34 deletions(-) diff --git a/Editor/Completion/XmlSchemaCompletionProvider.cs b/Editor/Completion/XmlSchemaCompletionProvider.cs index b9993ab..3bcdfd6 100644 --- a/Editor/Completion/XmlSchemaCompletionProvider.cs +++ b/Editor/Completion/XmlSchemaCompletionProvider.cs @@ -23,16 +23,21 @@ using System; using System.IO; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Schema; +using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; +using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; +using MonoDevelop.Xml.Editor.Completion; namespace MonoDevelop.Xml.Editor.Completion { /// /// Holds the completion (intellisense) data for an xml schema. /// - class XmlSchemaCompletionProvider + class XmlSchemaCompletionProvider : IXmlCompletionProvider { string namespaceUri = String.Empty; XmlSchema schema = null; @@ -41,21 +46,31 @@ namespace MonoDevelop.Xml.Editor.Completion bool readOnly = false; bool loaded = false; + /// + /// Stores attributes that have been prohibited whilst the code + /// generates the attribute completion data. + /// + XmlSchemaObjectCollection prohibitedAttributes = new XmlSchemaObjectCollection (); + #region Constructors + public XmlSchemaCompletionProvider () + { + } + /// /// Creates completion data from the schema passed in /// via the reader object. /// - public XmlSchemaCompletionProvider (TextReader reader, string filePath = null) + public XmlSchemaCompletionProvider (TextReader reader) { - this.schema = ReadSchema (GetUri(filePath), reader); + ReadSchema (String.Empty, reader); } /// /// Creates the completion data from the specified schema file. /// - public XmlSchemaCompletionProvider (string fileName) : this (GetUri(fileName), fileName) + public XmlSchemaCompletionProvider (string fileName) : this (String.Empty, fileName) { } @@ -63,7 +78,7 @@ namespace MonoDevelop.Xml.Editor.Completion /// Creates the completion data from the specified schema file and uses /// the specified baseUri to resolve any referenced schemas. /// - public XmlSchemaCompletionProvider (string baseUri, string fileName) : this (baseUri, fileName, lazyLoadFile: false) + public XmlSchemaCompletionProvider (string baseUri, string fileName) : this (baseUri, fileName, false) { } @@ -74,7 +89,8 @@ namespace MonoDevelop.Xml.Editor.Completion this.baseUri = baseUri; if (!lazyLoadFile) - this.schema = ReadSchema (fileName, baseUri); + using (var reader = new StreamReader (fileName, true)) + ReadSchema (baseUri, reader); } #endregion @@ -130,7 +146,8 @@ namespace MonoDevelop.Xml.Editor.Completion return Task.Run (() => { if (schema == null) - this.schema = ReadSchema (fileName, baseUri); + using (var reader = new StreamReader (fileName, true)) + ReadSchema (baseUri, reader); //TODO: should we evaluate unresolved imports against other registered schemas? //will be messy because we'll have to re-evaluate if any schema is added, removed or changes @@ -146,6 +163,231 @@ namespace MonoDevelop.Xml.Editor.Completion } #endregion + #region Simplified API, useful for e.g. HTML + + public async Task GetChildElementCompletionDataAsync (IAsyncCompletionSource source, string tagName, CancellationToken token) + { + await EnsureLoadedAsync (); + + var list = new XmlSchemaCompletionBuilder (source); + var element = FindElement (tagName); + if (element != null) + GetChildElementCompletionData (list, element, ""); + return new CompletionContext (list.GetItems ()); + } + + public async Task GetAttributeCompletionDataAsync (IAsyncCompletionSource source, string tagName, CancellationToken token) + { + await EnsureLoadedAsync (); + + var list = new XmlSchemaCompletionBuilder (source); + var element = FindElement (tagName); + if (element != null) { + prohibitedAttributes.Clear (); + GetAttributeCompletionData (list, element); + } + return new CompletionContext (list.GetItems ()); + } + + public async Task GetAttributeValueCompletionDataAsync (IAsyncCompletionSource source, string tagName, string name, CancellationToken token) + { + await EnsureLoadedAsync (); + + var list = new XmlSchemaCompletionBuilder (source); + var element = FindElement (tagName); + if (element != null) + GetAttributeValueCompletionData (list, element, name); + return new CompletionContext (list.GetItems ()); + } + + #endregion + + /// + /// Gets the possible root elements for an xml document using this schema. + /// + public Task GetElementCompletionDataAsync (IAsyncCompletionSource source, CancellationToken token) + { + return GetElementCompletionDataAsync (source, "", token); + } + + /// + /// Gets the possible root elements for an xml document using this schema. + /// + public async Task GetElementCompletionDataAsync (IAsyncCompletionSource source, string namespacePrefix, CancellationToken token) + { + await EnsureLoadedAsync (); + + var builder = new XmlSchemaCompletionBuilder (source); + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (element.Name != null) { + builder.AddElement (element.Name, namespacePrefix, element.Annotation); + } else { + // Do not add reference element. + } + } + return new CompletionContext (builder.GetItems ()); + } + + /// + /// Gets the attribute completion data for the xml element that exists + /// at the end of the specified path. + /// + public async Task GetAttributeCompletionDataAsync (IAsyncCompletionSource source, XmlElementPath path, CancellationToken token) + { + await EnsureLoadedAsync (); + + var builder = new XmlSchemaCompletionBuilder (source, path.Namespaces); + var element = FindElement (path); + if (element != null) { + prohibitedAttributes.Clear (); + GetAttributeCompletionData (builder, element); + } + return new CompletionContext (builder.GetItems ()); + } + + /// + /// Gets the child element completion data for the xml element that exists + /// at the end of the specified path. + /// + public async Task GetChildElementCompletionDataAsync (IAsyncCompletionSource source, XmlElementPath path, CancellationToken token) + { + await EnsureLoadedAsync (); + + var builder = new XmlSchemaCompletionBuilder (source, path.Namespaces); + var element = FindElement (path); + if (element != null) { + var last = path.Elements.LastOrDefault (); + GetChildElementCompletionData (builder, element, last != null ? last.Prefix : ""); + } + return new CompletionContext (builder.GetItems ()); + } + + /// + /// Gets the autocomplete data for the specified attribute value. + /// + public async Task GetAttributeValueCompletionDataAsync (IAsyncCompletionSource source, XmlElementPath path, string name, CancellationToken token) + { + await EnsureLoadedAsync (); + + var builder = new XmlSchemaCompletionBuilder (source, path.Namespaces); + var element = FindElement (path); + if (element != null) + GetAttributeValueCompletionData (builder, element, name); + return new CompletionContext (builder.GetItems ()); + } + + /// + /// Finds the element that exists at the specified path. + /// + /// This method is not used when generating completion data, + /// but is a useful method when locating an element so we can jump + /// to its schema definition. + /// if no element can be found. + public XmlSchemaElement FindElement (XmlElementPath path) + { + EnsureLoaded (); + + XmlSchemaElement element = null; + for (int i = 0; i < path.Elements.Count; ++i) { + QualifiedName name = path.Elements[i]; + if (i == 0) { + // Look for root element. + element = FindElement (name); + if (element == null) { + break; + } + } else { + element = FindChildElement (element, name); + if (element == null) { + break; + } + } + } + return element; + } + + /// + /// Finds an element in the schema. + /// + /// + /// Only looks at the elements that are defined in the + /// root of the schema so it will not find any elements + /// that are defined inside any complex types. + /// + public XmlSchemaElement FindElement (QualifiedName name) + { + EnsureLoaded (); + + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (name.Equals (element.QualifiedName)) { + return element; + } + } + LoggingService.LogDebug ("XmlSchemaDataObject did not find element '{0}' in the schema", name.Name); + return null; + } + + public XmlSchemaElement FindElement (string name) + { + EnsureLoaded (); + + foreach (XmlSchemaElement element in schema.Elements.Values) + if (element.QualifiedName.Name == name) + return element; + + LoggingService.LogDebug ("XmlSchemaDataObject did not find element '{0}' in the schema", name); + return null; + } + + /// + /// Finds the complex type with the specified name. + /// + public XmlSchemaComplexType FindComplexType (QualifiedName name) + { + EnsureLoaded (); + + var qualifiedName = new XmlQualifiedName (name.Name, name.Namespace); + return FindNamedType (schema, qualifiedName); + } + + /// + /// Finds the specified attribute name given the element. + /// + /// This method is not used when generating completion data, + /// but is a useful method when locating an attribute so we can jump + /// to its schema definition. + /// if no attribute can be found. + public XmlSchemaAttribute FindAttribute (XmlSchemaElement element, string name) + { + EnsureLoaded (); + + XmlSchemaAttribute attribute = null; + var complexType = GetElementAsComplexType (element); + if (complexType != null) { + attribute = FindAttribute (complexType, name); + } + return attribute; + } + + /// + /// Finds the attribute group with the specified name. + /// + public XmlSchemaAttributeGroup FindAttributeGroup (string name) + { + EnsureLoaded (); + return FindAttributeGroup (schema, name); + } + + /// + /// Finds the simple type with the specified name. + /// + public XmlSchemaSimpleType FindSimpleType (string name) + { + EnsureLoaded (); + var qualifiedName = new XmlQualifiedName (name, namespaceUri); + return FindSimpleType (qualifiedName); + } + /// /// Finds the specified attribute in the schema. This method only checks /// the attributes defined in the root of the schema. @@ -212,36 +454,17 @@ namespace MonoDevelop.Xml.Editor.Completion /// /// Loads the schema. /// - XmlSchema ReadSchema (XmlReader reader, string schemaFilePath = null) + void ReadSchema (XmlReader reader) { try { - var schema = XmlSchema.Read (reader, SchemaValidation); + schema = XmlSchema.Read (reader, SchemaValidation); namespaceUri = schema.TargetNamespace; - - // is probably bad when there's nested includes due to recursive stack calls... - foreach (XmlSchemaObject include in schema.Includes) { - var includeSchema = include as XmlSchemaInclude; - if (includeSchema != null && schemaFilePath != null) { - var schemaDirectory = Path.GetDirectoryName(schemaFilePath); - var includedSchema = Path.Combine(schemaDirectory, includeSchema.SchemaLocation); - includedSchema = Path.GetFullPath(includedSchema); - if (File.Exists(includedSchema)) - { - includeSchema.Schema = ReadSchema(includedSchema, GetUri(includedSchema)); - } - } - } - return schema; - } - catch { - return null; - } - finally { + } finally { reader.Close (); } } - XmlSchema ReadSchema (string baseUri, TextReader reader) + void ReadSchema (string baseUri, TextReader reader) { // The default resolve can cause exceptions loading // xhtml1-strict.xsd because of the referenced dtds. It also has the @@ -257,13 +480,678 @@ namespace MonoDevelop.Xml.Editor.Completion }, baseUri ); - return ReadSchema (xmlReader, baseUri); + ReadSchema (xmlReader); } - XmlSchema ReadSchema (string fileName, string baseUri) + + /// + /// Finds an element in the schema. + /// + /// + /// Only looks at the elements that are defined in the + /// root of the schema so it will not find any elements + /// that are defined inside any complex types. + /// + XmlSchemaElement FindElement (XmlQualifiedName name) { - using (var reader = new StreamReader (fileName, detectEncodingFromByteOrderMarks: true)) - return ReadSchema (baseUri, reader); + XmlSchemaElement matchedElement = null; + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (name.Equals (element.QualifiedName)) { + matchedElement = element; + break; + } + } + + return matchedElement; + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaElement element, string prefix) + { + var complexType = GetElementAsComplexType (element); + if (complexType != null) + GetChildElementCompletionData (data, complexType, prefix); + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexType complexType, string prefix) + { + if (complexType.Particle is XmlSchemaSequence sequence) { + GetChildElementCompletionData (data, sequence.Items, prefix); + return; + } + if (complexType.Particle is XmlSchemaChoice choice) { + GetChildElementCompletionData (data, choice.Items, prefix); + return; + } + var complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) { + GetChildElementCompletionData (data, complexContent, prefix); + return; + } + var groupRef = complexType.Particle as XmlSchemaGroupRef; + if (groupRef != null) { + GetChildElementCompletionData (data, groupRef, prefix); + return; + } + var all = complexType.Particle as XmlSchemaAll; + if (all != null) { + GetChildElementCompletionData (data, all.Items, prefix); + return; + } + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaObjectCollection items, string prefix) + { + foreach (XmlSchemaObject schemaObject in items) { + var childElement = schemaObject as XmlSchemaElement; + if (childElement != null) { + string name = childElement.Name; + if (name == null) { + name = childElement.RefName.Name; + var element = FindElement (childElement.RefName); + if (element != null) { + if (element.IsAbstract) { + AddSubstitionGroupElements (data, element.QualifiedName, prefix); + } else { + data.AddElement (name, prefix, element.Annotation); + } + } else { + data.AddElement (name, prefix, childElement.Annotation); + } + } else { + data.AddElement (name, prefix, childElement.Annotation); + } + continue; + } + var childSequence = schemaObject as XmlSchemaSequence; + if (childSequence != null) { + GetChildElementCompletionData (data, childSequence.Items, prefix); + continue; + } + var childChoice = schemaObject as XmlSchemaChoice; + if (childChoice != null) { + GetChildElementCompletionData (data, childChoice.Items, prefix); + continue; + } + var groupRef = schemaObject as XmlSchemaGroupRef; + if (groupRef != null) { + GetChildElementCompletionData (data, groupRef, prefix); + continue; + } + } + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexContent complexContent, string prefix) + { + var extension = complexContent.Content as XmlSchemaComplexContentExtension; + if (extension != null) { + GetChildElementCompletionData (data, extension, prefix); + return; + } + var restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (restriction != null) { + GetChildElementCompletionData (data, restriction, prefix); + return; + } + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexContentExtension extension, string prefix) + { + var complexType = FindNamedType (schema, extension.BaseTypeName); + if (complexType != null) + GetChildElementCompletionData (data, complexType, prefix); + + if (extension.Particle == null) + return; + + var sequence = extension.Particle as XmlSchemaSequence; + if (sequence != null) { + GetChildElementCompletionData (data, sequence.Items, prefix); + return; + } + var choice = extension.Particle as XmlSchemaChoice; + if (choice != null) { + GetChildElementCompletionData (data, choice.Items, prefix); + return; + } + var groupRef = extension.Particle as XmlSchemaGroupRef; + if (groupRef != null) { + GetChildElementCompletionData (data, groupRef, prefix); + return; + } + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaGroupRef groupRef, string prefix) + { + var group = FindGroup (groupRef.RefName.Name); + if (group == null) + return; + var sequence = group.Particle as XmlSchemaSequence; + if (sequence != null) { + GetChildElementCompletionData (data, sequence.Items, prefix); + return; + } + var choice = group.Particle as XmlSchemaChoice; + if (choice != null) { + GetChildElementCompletionData (data, choice.Items, prefix); + return; + } + } + + void GetChildElementCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexContentRestriction restriction, string prefix) + { + if (restriction.Particle == null) + return; + var sequence = restriction.Particle as XmlSchemaSequence; + if (sequence != null) { + GetChildElementCompletionData (data, sequence.Items, prefix); + return; + } + var choice = restriction.Particle as XmlSchemaChoice; + if (choice != null) { + GetChildElementCompletionData (data, choice.Items, prefix); + return; + } + var groupRef = restriction.Particle as XmlSchemaGroupRef; + if (groupRef != null) { + GetChildElementCompletionData (data, groupRef, prefix); + return; + } + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaElement element) + { + var complexType = GetElementAsComplexType (element); + if (complexType != null) + GetAttributeCompletionData (data, complexType); + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexContentRestriction restriction) + { + GetAttributeCompletionData (data, restriction.Attributes); + + var baseComplexType = FindNamedType (schema, restriction.BaseTypeName); + if (baseComplexType != null) { + GetAttributeCompletionData (data, baseComplexType); + } + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexType complexType) + { + GetAttributeCompletionData (data, complexType.Attributes); + + // Add any complex content attributes. + var complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) { + var extension = complexContent.Content as XmlSchemaComplexContentExtension; + var restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (extension != null) + GetAttributeCompletionData (data, extension); + else if (restriction != null) + GetAttributeCompletionData (data, restriction); + } else { + var simpleContent = complexType.ContentModel as XmlSchemaSimpleContent; + if (simpleContent != null) + GetAttributeCompletionData (data, simpleContent); + } + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaComplexContentExtension extension) + { + GetAttributeCompletionData (data, extension.Attributes); + var baseComplexType = FindNamedType (schema, extension.BaseTypeName); + if (baseComplexType != null) + GetAttributeCompletionData (data, baseComplexType); + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleContent simpleContent) + { + var extension = simpleContent.Content as XmlSchemaSimpleContentExtension; + if (extension != null) + GetAttributeCompletionData (data, extension); + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleContentExtension extension) + { + GetAttributeCompletionData (data, extension.Attributes); + } + + /// + /// Converts the element to a complex type if possible. + /// + XmlSchemaComplexType GetElementAsComplexType (XmlSchemaElement element) + { + return (element.SchemaType as XmlSchemaComplexType) + ?? FindNamedType (schema, element.SchemaTypeName); + } + + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaObjectCollection attributes) + { + foreach (XmlSchemaObject schemaObject in attributes) { + var attribute = schemaObject as XmlSchemaAttribute; + if (attribute != null) { + if (!IsProhibitedAttribute (attribute)) { + data.AddAttribute (attribute); + } else { + prohibitedAttributes.Add (attribute); + } + } else { + var attributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef; + if (attributeGroupRef != null) + GetAttributeCompletionData (data, attributeGroupRef); + } + } + } + + /// + /// Checks that the attribute is prohibited or has been flagged + /// as prohibited previously. + /// + bool IsProhibitedAttribute (XmlSchemaAttribute attribute) + { + bool prohibited = false; + if (attribute.Use == XmlSchemaUse.Prohibited) { + prohibited = true; + } else { + foreach (XmlSchemaAttribute prohibitedAttribute in prohibitedAttributes) { + if (prohibitedAttribute.QualifiedName == attribute.QualifiedName) { + prohibited = true; + break; + } + } + } + + return prohibited; + } + + /// + /// Gets attribute completion data from a group ref. + /// + void GetAttributeCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaAttributeGroupRef groupRef) + { + var group = FindAttributeGroup (schema, groupRef.RefName.Name); + if (group != null) + GetAttributeCompletionData (data, group.Attributes); + } + + static XmlSchemaComplexType FindNamedType (XmlSchema schema, XmlQualifiedName name) + { + if (name == null) + return null; + + foreach (XmlSchemaObject schemaObject in schema.Items) { + var complexType = schemaObject as XmlSchemaComplexType; + if (complexType != null && complexType.QualifiedName == name) + return complexType; + } + + // Try included schemas. + foreach (XmlSchemaExternal external in schema.Includes) { + var include = external as XmlSchemaInclude; + if (include != null && include.Schema != null) { + var matchedComplexType = FindNamedType (include.Schema, name); + if (matchedComplexType != null) + return matchedComplexType; + } + } + + return null; + } + + /// + /// Finds an element that matches the specified + /// from the children of the given . + /// + XmlSchemaElement FindChildElement (XmlSchemaElement element, QualifiedName name) + { + var complexType = GetElementAsComplexType (element); + if (complexType != null) + return FindChildElement (complexType, name); + return null; + } + + XmlSchemaElement FindChildElement (XmlSchemaComplexType complexType, QualifiedName name) + { + var sequence = complexType.Particle as XmlSchemaSequence; + if (sequence != null) + return FindElement (sequence.Items, name); + + var choice = complexType.Particle as XmlSchemaChoice; + if (choice != null) + return FindElement (choice.Items, name); + + var complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) { + var extension = complexContent.Content as XmlSchemaComplexContentExtension; + if (extension != null) + return FindChildElement (extension, name); + var restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (restriction != null) + return FindChildElement (restriction, name); + } + + var groupRef = complexType.Particle as XmlSchemaGroupRef; + if (groupRef != null) + return FindElement (groupRef, name); + + var all = complexType.Particle as XmlSchemaAll; + if (all != null) + return FindElement (all.Items, name); + + return null; + } + + /// + /// Finds the named child element contained in the extension element. + /// + XmlSchemaElement FindChildElement (XmlSchemaComplexContentExtension extension, QualifiedName name) + { + var complexType = FindNamedType (schema, extension.BaseTypeName); + if (complexType == null) + return null; + + var matchedElement = FindChildElement (complexType, name); + if (matchedElement != null) + return matchedElement; + + var sequence = extension.Particle as XmlSchemaSequence; + if (sequence != null) + return FindElement (sequence.Items, name); + + var choice = extension.Particle as XmlSchemaChoice; + if (choice != null) + return FindElement (choice.Items, name); + + var groupRef = extension.Particle as XmlSchemaGroupRef; + if (groupRef != null) + return FindElement (groupRef, name); + + return null; + } + + /// + /// Finds the named child element contained in the restriction element. + /// + XmlSchemaElement FindChildElement (XmlSchemaComplexContentRestriction restriction, QualifiedName name) + { + var sequence = restriction.Particle as XmlSchemaSequence; + if (sequence != null) + return FindElement (sequence.Items, name); + + var groupRef = restriction.Particle as XmlSchemaGroupRef; + if (groupRef != null) + return FindElement (groupRef, name); + + return null; + } + + /// + /// Finds the element in the collection of schema objects. + /// + XmlSchemaElement FindElement (XmlSchemaObjectCollection items, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + foreach (XmlSchemaObject schemaObject in items) { + var element = schemaObject as XmlSchemaElement; + var sequence = schemaObject as XmlSchemaSequence; + var choice = schemaObject as XmlSchemaChoice; + var groupRef = schemaObject as XmlSchemaGroupRef; + + if (element != null) { + if (element.Name != null) { + if (name.Name == element.Name) { + return element; + } + } else if (element.RefName != null) { + if (name.Name == element.RefName.Name) { + matchedElement = FindElement (element.RefName); + } else { + var abstractElement = FindElement (element.RefName); + if (abstractElement != null && abstractElement.IsAbstract) { + matchedElement = FindSubstitutionGroupElement (abstractElement.QualifiedName, name); + } + } + } + } else if (sequence != null) { + matchedElement = FindElement (sequence.Items, name); + } else if (choice != null) { + matchedElement = FindElement (choice.Items, name); + } else if (groupRef != null) { + matchedElement = FindElement (groupRef, name); + } + + if (matchedElement != null) + return matchedElement; + } + + return null; + } + + XmlSchemaElement FindElement (XmlSchemaGroupRef groupRef, QualifiedName name) + { + var group = FindGroup (groupRef.RefName.Name); + if (group == null) + return null; + + var sequence = group.Particle as XmlSchemaSequence; + if (sequence != null) + return FindElement (sequence.Items, name); + var choice = group.Particle as XmlSchemaChoice; + if (choice != null) + return FindElement (choice.Items, name); + + return null; + } + + static XmlSchemaAttributeGroup FindAttributeGroup (XmlSchema schema, string name) + { + if (name == null) + return null; + + foreach (XmlSchemaObject schemaObject in schema.Items) { + var group = schemaObject as XmlSchemaAttributeGroup; + if (group != null && group.Name == name) + return group; + } + + // Try included schemas. + foreach (XmlSchemaExternal external in schema.Includes) { + var include = external as XmlSchemaInclude; + if (include != null && include.Schema != null) { + var found = FindAttributeGroup (include.Schema, name); + if (found != null) + return found; + } + } + return null; + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaElement element, string name) + { + var complexType = GetElementAsComplexType (element); + if (complexType != null) { + var attribute = FindAttribute (complexType, name); + if (attribute != null) + GetAttributeValueCompletionData (data, attribute); + } + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaAttribute attribute) + { + if (attribute.SchemaType != null) { + var simpleTypeRestriction = attribute.SchemaType.Content as XmlSchemaSimpleTypeRestriction; + if (simpleTypeRestriction != null) { + GetAttributeValueCompletionData (data, simpleTypeRestriction); + } + } else if (attribute.AttributeSchemaType != null) { + if (attribute.AttributeSchemaType.TypeCode == XmlTypeCode.Boolean) + GetBooleanAttributeValueCompletionData (data); + else + GetAttributeValueCompletionData (data, attribute.AttributeSchemaType); + } + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleTypeRestriction simpleTypeRestriction) + { + foreach (XmlSchemaObject schemaObject in simpleTypeRestriction.Facets) { + var enumFacet = schemaObject as XmlSchemaEnumerationFacet; + if (enumFacet != null) + data.AddAttributeValue (enumFacet.Value, enumFacet.Annotation); + } + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleTypeUnion union) + { + foreach (XmlSchemaObject schemaObject in union.BaseTypes) { + var simpleType = schemaObject as XmlSchemaSimpleType; + if (simpleType != null) + GetAttributeValueCompletionData (data, simpleType); + } + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleType simpleType) + { + var xsstr = simpleType.Content as XmlSchemaSimpleTypeRestriction; + if (xsstr != null) { + GetAttributeValueCompletionData (data, xsstr); + return; + } + var xsstu = simpleType.Content as XmlSchemaSimpleTypeUnion; + if (xsstu != null) { + GetAttributeValueCompletionData (data, xsstu); + return; + } + var xsstl = simpleType.Content as XmlSchemaSimpleTypeList; + if (xsstl != null) { + GetAttributeValueCompletionData (data, xsstl); + return; + } + } + + void GetAttributeValueCompletionData (XmlSchemaCompletionBuilder data, XmlSchemaSimpleTypeList list) + { + if (list.ItemType != null) { + GetAttributeValueCompletionData (data, list.ItemType); + } else if (list.ItemTypeName != null) { + var simpleType = FindSimpleType (list.ItemTypeName); + if (simpleType != null) + GetAttributeValueCompletionData (data, simpleType); + } + } + + /// + /// Gets the set of attribute values for an xs:boolean type. + /// + void GetBooleanAttributeValueCompletionData (XmlSchemaCompletionBuilder data) + { + data.AddAttributeValue ("0"); + data.AddAttributeValue ("1"); + data.AddAttributeValue ("true"); + data.AddAttributeValue ("false"); + } + + XmlSchemaAttribute FindAttribute (XmlSchemaComplexType complexType, string name) + { + var matchedAttribute = FindAttribute (complexType.Attributes, name); + if (matchedAttribute != null) + return matchedAttribute; + + var complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) + return FindAttribute (complexContent, name); + + return null; + } + + XmlSchemaAttribute FindAttribute (XmlSchemaObjectCollection schemaObjects, string name) + { + foreach (XmlSchemaObject schemaObject in schemaObjects) { + var attribute = schemaObject as XmlSchemaAttribute; + if (attribute != null && attribute.Name == name) + return attribute; + + var groupRef = schemaObject as XmlSchemaAttributeGroupRef; + if (groupRef != null) { + var matchedAttribute = FindAttribute (groupRef, name); + if (matchedAttribute != null) + return matchedAttribute; + } + } + return null; + } + + XmlSchemaAttribute FindAttribute (XmlSchemaAttributeGroupRef groupRef, string name) + { + if (groupRef.RefName != null) { + var group = FindAttributeGroup (schema, groupRef.RefName.Name); + if (group != null) { + return FindAttribute (group.Attributes, name); + } + } + return null; + } + + XmlSchemaAttribute FindAttribute (XmlSchemaComplexContent complexContent, string name) + { + var extension = complexContent.Content as XmlSchemaComplexContentExtension; + if (extension != null) + return FindAttribute (extension, name); + + var restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (restriction != null) + return FindAttribute (restriction, name); + + return null; + } + + XmlSchemaAttribute FindAttribute (XmlSchemaComplexContentExtension extension, string name) + { + return FindAttribute (extension.Attributes, name); + } + + XmlSchemaAttribute FindAttribute (XmlSchemaComplexContentRestriction restriction, string name) + { + var matchedAttribute = FindAttribute (restriction.Attributes, name); + if (matchedAttribute != null) + return matchedAttribute; + + var complexType = FindNamedType (schema, restriction.BaseTypeName); + if (complexType != null) + return FindAttribute (complexType, name); + + return null; + } + + XmlSchemaSimpleType FindSimpleType (XmlQualifiedName name) + { + foreach (XmlSchemaObject schemaObject in schema.SchemaTypes.Values) { + var simpleType = schemaObject as XmlSchemaSimpleType; + if (simpleType != null && simpleType.QualifiedName == name) + return simpleType; + } + return null; + } + + /// + /// Adds any elements that have the specified substitution group. + /// + void AddSubstitionGroupElements (XmlSchemaCompletionBuilder data, XmlQualifiedName group, string prefix) + { + foreach (XmlSchemaElement element in schema.Elements.Values) + if (element.SubstitutionGroup == group) + data.AddElement (element.Name, prefix, element.Annotation); + } + + /// + /// Looks for the substitution group element of the specified name. + /// + XmlSchemaElement FindSubstitutionGroupElement (XmlQualifiedName group, QualifiedName name) + { + foreach (XmlSchemaElement element in schema.Elements.Values) + if (element.SubstitutionGroup == group && element.Name != null && element.Name == name.Name) + return element; + + return null; } } } \ No newline at end of file