Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Base / System / Windows / Markup / XmlCompatibilityReader.cs / 1 / XmlCompatibilityReader.cs
/****************************************************************************\ * * File: XmlCompatibilityReaderr.cs * * Purpose: * * History: * 5/11/05: fmunoz Created * 9/16/05: oliverfo Modified * 9/16/05: tjhsiang Modified * * Copyright (C) 2005 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Xml; using System.Collections.Generic; using System.Globalization; using System.Diagnostics; #if SYSTEM_XAML using System.Xaml; #endif #if PBTCOMPILER using MS.Utility; namespace MS.Internal.Markup #else using MS.Internal.WindowsBase; using System.Windows; namespace System.Windows.Markup #endif { #if !PBTCOMPILER [FriendAccessAllowed] #endif //// true if xmlNamespace is recognized // // // the namespace to be checked // // // if the passed in namespace is subsumed, then newXmlNamespace returns the subsuming namespace. // internal delegate bool IsXmlNamespaceSupportedCallback(string xmlNamespace, out string newXmlNamespace); delegate void HandleElementCallback(int elementDepth, ref bool more); delegate void HandleAttributeCallback(int elementDepth); #if !PBTCOMPILER [FriendAccessAllowed] #endif internal sealed class XmlCompatibilityReader: XmlWrappingReader { #region Construction public XmlCompatibilityReader(XmlReader baseReader):base(baseReader) { _compatibilityScope = new CompatibilityScope(null, -1, this); foreach (string xmlNamespace in _predefinedNamespaces) { AddKnownNamespace(xmlNamespace); _namespaceMap[xmlNamespace] = xmlNamespace; Reader.NameTable.Add(xmlNamespace); } _elementHandler.Add(AlternateContent, new HandleElementCallback(HandleAlternateContent)); _elementHandler.Add(Choice, new HandleElementCallback(HandleChoice)); _elementHandler.Add(Fallback, new HandleElementCallback(HandleFallback)); _attributeHandler.Add(Ignorable, new HandleAttributeCallback(HandleIgnorable)); _attributeHandler.Add(MustUnderstand, new HandleAttributeCallback(HandleMustUnderstand)); _attributeHandler.Add(ProcessContent, new HandleAttributeCallback(HandleProcessContent)); _attributeHandler.Add(PreserveElements, new HandleAttributeCallback(HandlePreserveElements)); _attributeHandler.Add(PreserveAttributes, new HandleAttributeCallback(HandlePreserveAttributes)); } public XmlCompatibilityReader(XmlReader baseReader, IsXmlNamespaceSupportedCallback isXmlNamespaceSupported): this(baseReader) { _namespaceCallback = isXmlNamespaceSupported; } public XmlCompatibilityReader(XmlReader baseReader, IsXmlNamespaceSupportedCallback isXmlNamespaceSupported, IEnumerablesupportedNamespaces):this(baseReader, isXmlNamespaceSupported) { foreach (string xmlNamespace in supportedNamespaces) { AddKnownNamespace(xmlNamespace); _namespaceMap[xmlNamespace] = xmlNamespace; } } #if !PBTCOMPILER public XmlCompatibilityReader(XmlReader baseReader, IEnumerable supportedNamespaces):this(baseReader, null, supportedNamespaces) { } #endif #endregion Construction #region Public Methods /// /// replaces all future references of namespace URI 'oldNamespace' with 'newNamespace' /// /// /// the namespace to subsume with /// /// /// the namespace to be subsumed /// public void DeclareNamespaceCompatibility(string newNamespace, string oldNamespace) { if(newNamespace != oldNamespace) { // indicate that newNamespace subsumes another namespace AddSubsumingNamespace(newNamespace); // If newNamespace is mapped to a namespace, string tempNamespace; if (_namespaceMap.TryGetValue(newNamespace, out tempNamespace)) { // If we have mapped newNamespace already get the newest name. // We don't have to do this recursively because of the code below // ensures the map always refers to the newest namespace. newNamespace = tempNamespace; } if(IsSubsumingNamespace(oldNamespace)) { // if we are mapping what was used as a new namespace to a newer name, // scan the _newNamespaces dictionary and update the entries. We collect // a list to avoid updating the dictonary during enumeration. ListkeysToUpdate = new List (); foreach (KeyValuePair pair in _namespaceMap) { if (pair.Value == oldNamespace) { keysToUpdate.Add(pair.Key); } } foreach (string key in keysToUpdate) { _namespaceMap[key] = newNamespace; } } } _namespaceMap[oldNamespace] = newNamespace; } /// /// Reads the next node from the stream. /// ////// true if the next node was read successfully; false if there are no more nodes to read. /// public override bool Read() { bool more = Reader.Read(); //passed as ref arg to ReadStartElement and ReadEndElement bool result = false; while (more) { switch (Reader.NodeType) { case XmlNodeType.Element: { // if the element read should be ignored, read the next element if(!ReadStartElement(ref more)) { continue; } break; } case XmlNodeType.EndElement: { // if the element read should be ignored, read the next element if(!ReadEndElement(ref more)) { continue; } break; } } // if the element was read successfully and was not ignored, break and return true result = true; break; } return result; } ////// Used to handle 'start element' tags. These are actually /// just called 'element' tags, the 'start' is just for clarity /// /// /// is set to true if there is the document contains more elements, false if the end of the /// document has been reached. /// ////// true if an element was read that should not be ignored /// false if the element read should be ignored or the end of document has been reached /// private bool ReadStartElement(ref bool more) { // when processing elements, the Reader may advance to another element or attribute, // so we save the values of the current element here int elementDepth = Reader.Depth; int depthOffset = _depthOffset; bool isEmpty = Reader.IsEmptyElement; string namespaceName = NamespaceURI; bool result = false; if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the element is a markup-compatibility element, we get the appropriate handler for // the element type, and call the appropriate delegate. If the element is not recognized // we throw an exception. string elementName = Reader.LocalName; HandleElementCallback elementCB; if(!_elementHandler.TryGetValue(elementName, out elementCB)) { Error(SR.Get(SRID.XCRUnknownCompatElement), elementName); } elementCB(elementDepth, ref more); } // handle non-markup-compatibility elements else { // check for markup-compatibility attributes and namespaces that should be ignored ScanForCompatibility(elementDepth); if (ShouldIgnoreNamespace(namespaceName)) { if(Scope.ShouldProcessContent(namespaceName, Reader.LocalName)) { // if the current element is unknown and has been marked Ignorable and ProcessContent, // then read the next element, and increase depth offset if(Scope.Depth == elementDepth) { // if the current element pushed a scope, mark the scope as InProcessContent to // note that for certain logic this scope's parent should be checked Scope.InProcessContent = true; } _depthOffset++; more = Reader.Read(); } else { // if element should be ignored but not processed, check to see if scope must be popped, // then skip to the next element after the end tag of the current element ScanForEndCompatibility(elementDepth); Reader.Skip(); } } else { if (Scope.InAlternateContent) { // if this element is the child of an AlternateContent element, then throw an exception. Error(SR.Get(SRID.XCRInvalidACChild), Reader.Name); } result = true; } } // if the element is empty (e.g. "" and we pushed a scope then get // rid of the scope if(isEmpty) { ScanForEndCompatibility(elementDepth); _depthOffset = depthOffset; } return result; } ////// Used to handle any end element tag /// /// /// is set to true if there is the document contains more elements, false if the end of the /// document has been reached. /// ////// true if an element was read that should not be ignored /// false if the element read should be ignored or the end of document has been reached /// private bool ReadEndElement(ref bool more) { // when reading attributes, the reader's depth increases, so for consistency // we store the depth before reading any attributes int elementDepth = Reader.Depth; string namespaceName = NamespaceURI; bool result = false; // return value if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the element is a markup-compatibility element, pop a scope, decrement the // depth offset and read the next element. string elementName = Reader.LocalName; if (object.ReferenceEquals(elementName, AlternateContent)) { if (!Scope.ChoiceSeen) { // if the current element was a , without any Choice // element children, throw an exception Error(SR.Get(SRID.XCRChoiceNotFound)); } } _depthOffset--; PopScope(); //we know we can pop, so no need to scan more = Reader.Read(); } else { if (ShouldIgnoreNamespace(namespaceName)) { // if current element is Ignorable, then to be on it, it must have been marked // ProcessContent. Pop a scope if the corresponding start element pushed a scope a // scope, decrement the depth offset and read the next element. Debug.Assert(Scope.ShouldProcessContent(namespaceName, Reader.LocalName)); ScanForEndCompatibility(elementDepth); _depthOffset--; more = Reader.Read(); } else { ScanForEndCompatibility(elementDepth); result = true; } } return result; } ////// Gets the value of the attribute with the specified index. /// /// /// The index of the attribute. The index is zero-based. (The first attribute has index 0.) /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(int i) { string result = null; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any of its attributes, skip extra logic result = Reader.GetAttribute(i); } else { SaveReaderPosition(); // move to 'i'th attribute, get its value MoveToAttribute(i); result = Reader.Value; RestoreReaderPosition(); } return result; } ////// Gets the value of the attribute with the specified name. /// /// /// The qualified name of the attribute. /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(string name) { string result = null; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.GetAttribute(name); } else { SaveReaderPosition(); // move to "name" attribute if (MoveToAttribute(name)) { result = Reader.Value; RestoreReaderPosition(); } } return result; } ////// Gets the value of the attribute with the specified local name and namespace URI. /// /// /// The local name of the attribute. /// /// /// The namespace URI of the attribute. /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(string localName, string namespaceURI) { string result = null; if (_ignoredAttributeCount == 0 || !ShouldIgnoreNamespace(namespaceURI) ) { // if the current element does not have any attributes that should be ignored or // the namespace provided is not ignorable, call Reader method result = Reader.GetAttribute(localName, namespaceURI); } return result; } ////// Gets the value of the attribute with the specified index. /// /// /// The index of the attribute. The index is zero-based. (The first attribute has index 0.) /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override void MoveToAttribute(int i) { if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method Reader.MoveToAttribute(i); } else if (i < 0 || i >= AttributeCount) { throw new ArgumentOutOfRangeException("i"); } else { // move Reader to first attribute and iterate until 'i'th element found Reader.MoveToFirstAttribute(); while (true) { if (!ShouldIgnoreNamespace(NamespaceURI)) { // if attribute should not be ignored, decrement 'i', if i == 0 we've found element if (i-- == 0) { break; } } Reader.MoveToNextAttribute(); } } } ////// Moves to the attribute with the specified name. /// /// /// The qualified name of the attribute. /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override bool MoveToAttribute(string name) { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToAttribute(name); } else { SaveReaderPosition(); result = Reader.MoveToAttribute(name); if (result && ShouldIgnoreNamespace(NamespaceURI)) { // if attribute should be ignored, return false and restore state result = false; RestoreReaderPosition(); } } return result; } ////// Moves to the attribute with the specified local name and namespace URI. /// /// /// The local name of the attribute. /// /// /// The namespace URI of the attribute. /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override bool MoveToAttribute(string localName, string namespaceURI) { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToAttribute(localName, namespaceURI); } else { SaveReaderPosition(); result = Reader.MoveToAttribute(localName, namespaceURI); if (result && ShouldIgnoreNamespace(namespaceURI)) { result = false; RestoreReaderPosition(); } } return result; } ////// Moves to the first attribute. /// ////// true if an attribute exists (the reader moves to the first attribute); /// otherwise, false (the position of the reader does not change). /// public override bool MoveToFirstAttribute() { bool result = HasAttributes; if(result) { MoveToAttribute(0); } return result; } ////// Moves to the next attribute. /// ////// true if there is a next attribute; false if there are no more attributes. /// public override bool MoveToNextAttribute() { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToNextAttribute(); } else { SaveReaderPosition(); result = Reader.MoveToNextAttribute(); if (result) { result = SkipToKnownAttribute(); if (!result) { // if no more attributes exist that should not be ignored, return false and restore state RestoreReaderPosition(); } } } return result; } ////// Resolves a namespace prefix in the current element's scope. /// /// /// The prefix whose namespace URI you want to resolve. To match the default namespace, /// pass an empty string. This string does not have to be atomized. /// ////// The namespace URI to which the prefix maps or a null reference if no matching prefix is found. /// public override string LookupNamespace (string prefix) { string namespaceName = Reader.LookupNamespace(prefix); if (namespaceName != null) { namespaceName = GetMappedNamespace(namespaceName); } return namespaceName; } #endregion Public Methods #region Public Properties ////// This override is to ensure that the value /// for the xmlns attribute reflects all the /// compatibility (subsuming) rules. /// public override string Value { get { // Look for xmlns if (String.Equals(XmlnsDeclaration, Reader.LocalName, StringComparison.Ordinal)) { return LookupNamespace(String.Empty); } // Look for xmlns: ... else if (String.Equals(XmlnsDeclaration, Reader.Prefix, StringComparison.Ordinal)) { return LookupNamespace(Reader.LocalName); } return Reader.Value; } } ////// Gets the namespace URI (as defined in the W3C Namespace specification) of the node /// on which the reader is positioned. /// public override string NamespaceURI { get { return GetMappedNamespace(Reader.NamespaceURI); } } ////// Gets the depth of the current node in the XML document. /// public override int Depth { get { return Reader.Depth - _depthOffset; } } ////// Gets a value indicating whether the current node has any attributes /// public override bool HasAttributes { get { return AttributeCount != 0; } } ////// Gets the number of attributes on the current node. /// public override int AttributeCount { get { return Reader.AttributeCount - _ignoredAttributeCount; } } ////// Sets a value indicating whether to normalize white space and attribute values. /// public bool Normalization { set { XmlTextReader xmlTextReader = Reader as XmlTextReader; // review, what if not the XmlTextReader. if (null != xmlTextReader) { xmlTextReader.Normalization = value; } } } #if !PBTCOMPILER ////// Answer the encoding of the underlying xaml stream /// internal System.Text.Encoding Encoding { get { XmlTextReader textReader = Reader as XmlTextReader; if (textReader == null) { return new System.Text.UTF8Encoding(true,true); } else { return textReader.Encoding; } } } #endif #endregion Public Properties #region Private Methods private void SaveReaderPosition() { // Save current state so we can go back to the same spot if this fails _inAttribute = (Reader.NodeType == XmlNodeType.Attribute); _currentName = Reader.Name; } private void RestoreReaderPosition() { // Restore reader state from SaveReaderPosition if (_inAttribute) { Reader.MoveToAttribute(_currentName); } else { Reader.MoveToElement(); } } ////// Retrieves the correctly mapped namespace from the namespace provided /// /// /// The name of the namespace to retrieve the mapping of /// ////// The name of the mapped namespace. /// private string GetMappedNamespace(string namespaceName) { string mappedNamespace; // if the namespace is not null, get the mapped namespace (which may be itself) if (!_namespaceMap.TryGetValue(namespaceName, out mappedNamespace)) { // if the namespace has not yet been mapped, map it mappedNamespace = MapNewNamespace(namespaceName); } else if (mappedNamespace == null) { // if the mapped namespace is null, then the namespace was not supported, just return // the given namespace mappedNamespace = namespaceName; } return mappedNamespace; } ////// Adds the namespace to the namespace map. The default is to map the namespace to itself. /// The namespace is mapped to the value returned by the callback, if a callback exists and the /// callback returns a subsuming namespace. /// /// /// The name of the namespace to be mapped. /// ////// The name of the mapped namespace. /// private string MapNewNamespace(string namespaceName) { if (_namespaceCallback != null) { string mappedNamespace; // the callback returns whether the namespace is supported, and mappedNamespace is the // namespace subsuming the namespace passed in. bool isSupported = _namespaceCallback(namespaceName, out mappedNamespace); if (isSupported) { AddKnownNamespace(namespaceName); if(String.IsNullOrEmpty(mappedNamespace) || namespaceName == mappedNamespace) { _namespaceMap[namespaceName] = namespaceName; } else { // subsume namespace with mappedNamespace. string tempNamespace; if (!_namespaceMap.TryGetValue(mappedNamespace, out tempNamespace)) { // mappedNamespace has not been mapped, so map it tempNamespace = MapNewNamespace(mappedNamespace); } DeclareNamespaceCompatibility(tempNamespace, namespaceName); namespaceName = tempNamespace; } } else { // if the namespace is not supported, we enter null into the namespaceMap as a placeholder // so that we do not call the callback again on this namespace. _namespaceMap[namespaceName] = null; } } return namespaceName; } ////// Used to determine whether a given namespace subsumes another namespace /// /// /// The name of the namespace to be checked. /// ////// true if the namespace subsumes another namespace; false otherwise /// private bool IsSubsumingNamespace(string namespaceName) { return _subsumingNamespaces.ContainsKey(namespaceName); } ////// Used to specify that a namespace subsumes another namespace /// /// /// The name of the namespace to be added. /// private void AddSubsumingNamespace(string namespaceName) { _subsumingNamespaces[namespaceName] = null; } ////// Used to determine whether a given namespace is known/supported /// /// /// The name of the namespace to be checked. /// ////// true if the namespace is known/supported; false otherwise /// private bool IsNamespaceKnown(string namespaceName) { return _knownNamespaces.ContainsKey(namespaceName); } ////// Used to specify that a namespace is known or supported /// /// /// The name of the namespace to be added. /// private void AddKnownNamespace(string namespaceName) { _knownNamespaces[namespaceName] = null; } ////// Used to determine whether a given namespace should be ignored. A namespace should be ignored if: /// EITHER /// a) the namespace is not known/supported and has been marked Ignorable /// OR /// b) the namespace is the markup-compatibility namespace /// /// /// The name of the prefix to be checked. /// ////// true if the namespace should be ignored; false otherwise /// private bool ShouldIgnoreNamespace(string namespaceName) { bool result; if(IsNamespaceKnown(namespaceName)) { result = object.ReferenceEquals(namespaceName, CompatibilityUri); } else { result = Scope.CanIgnore(namespaceName); } return result; } ////// breaks up a space-delineated string into namespace/element pairs /// /// /// the string to be parsed /// /// /// The calling element, used in case of an error /// ////// the list of namespace/element pairs /// private IEnumerableParseContentToNamespaceElementPair(string content, string callerContext) { foreach (string pair in content.Trim().Split(' ')) { // check each non-null, non-empty space-delineated namespace/element pair if(!String.IsNullOrEmpty(pair)) { int colonIndex = pair.IndexOf(':'); int length = pair.Length; if(colonIndex <= 0 || colonIndex >= length - 1 || colonIndex != pair.LastIndexOf(':')) { // if string does not have a ':', if the last character in the string is a ':' // or if the string contains more than one ':', throw an exception Error(SR.Get(SRID.XCRInvalidFormat), callerContext); } string prefix = pair.Substring(0, colonIndex); string elementName = pair.Substring(colonIndex + 1, length - 1 - colonIndex); string namespaceName = LookupNamespace(prefix); if (namespaceName == null) { // if a prefix does not map to a namespace, throw an exception Error(SR.Get(SRID.XCRUndefinedPrefix), prefix); } else if (elementName != "*" && !IsName(elementName)) { // if the element's name is not valid XML, throw an exception Error(SR.Get(SRID.XCRInvalidXMLName), pair); } else { yield return new NamespaceElementPair(namespaceName, elementName); } } } } /// /// converts a string of space-delineated prefixes into a list of namespaces /// /// /// the string to be parsed /// ////// the list of namespace/element pairs /// private IEnumerablePrefixesToNamespaces(string prefixes) { foreach (string prefix in prefixes.Trim().Split(' ')) { // check each non-null, non-empty space-delineated prefix if(!String.IsNullOrEmpty(prefix)) { string namespaceUri = LookupNamespace(prefix); if (namespaceUri == null) { // if a prefix does not map to a namespace, throw an exception Error(SR.Get(SRID.XCRUndefinedPrefix), prefix); } else { yield return namespaceUri; } } } } /// /// advances the reader to the next known namespace/attribute pair /// ////// true if a known namespace/attribute pair was found /// private bool SkipToKnownAttribute() { bool result = true; while (result && ShouldIgnoreNamespace(NamespaceURI)) { result = Reader.MoveToNextAttribute(); } return result; } ////// Scans the current element for compatibility attributes. Pushes a new /// scope onto the stack under the following conditions: /// 1) Ignorable or MustUnderstand attribute read /// 2) current element has not previously declared an Ignorable or /// MustUnderstand attribute /// /// However, if a last condition is not fulfilled, then the scope is popped off /// before the function returns /// 3) current element is not empty /// /// stores in _ignoredAttributeCount the number of attributes on the current element /// that should be ignored, for the sake of improving perf in attribute-related /// methods/properties /// /// /// the depth of the Reader at the element currently being processed /// private void ScanForCompatibility(int elementDepth) { bool onAttribute = Reader.MoveToFirstAttribute(); _ignoredAttributeCount = 0; if (onAttribute) { _attributePosition = 0; // we count the attribute index in case we see Ignorable do { string namespaceName = NamespaceURI; if(ShouldIgnoreNamespace(namespaceName)) { // check each attribute's namespace to see if it should be ignored if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the attribute is in the markup-compatibility namespace // find and call the appropriate attribute handler callback. string attributeName = Reader.LocalName; HandleAttributeCallback attributeCB; if(!_attributeHandler.TryGetValue(attributeName, out attributeCB)) { Error(SR.Get(SRID.XCRUnknownCompatAttrib), attributeName); } attributeCB(elementDepth); } _ignoredAttributeCount++; } onAttribute = Reader.MoveToNextAttribute(); _attributePosition++; // we count the attribute index in case we see Ignorable } while (onAttribute); if (Scope.Depth == elementDepth) { // if this element pushed a scope, then we need to do a sanity check Scope.Verify(); } // move the reader back to the element for the client Reader.MoveToElement(); } } ////// pops a scope if the end of a compatibility region. /// /// /// the depth of the Reader at the element currently being processed /// private void ScanForEndCompatibility(int elementDepth) { if (elementDepth == Scope.Depth) { // if the current element's depth equals the depth of the top-level scope, then pop PopScope(); } } ////// pushes a new scope onto the stack with a depth passed as an arg. /// PushScope does not push a scope if the top scope on the stack is not a lower depth. /// /// /// the depth of the Reader at the element currently being processed /// private void PushScope(int elementDepth) { if(_compatibilityScope.Depth < elementDepth) { // if the current element has already pushed a scope, then don't push another one _compatibilityScope = new CompatibilityScope(_compatibilityScope, elementDepth, this); } } ////// pops a scope off the top of the stack. /// PopScope *always* pops, it does not check the depth before doing so /// private void PopScope() { _compatibilityScope = _compatibilityScope.Previous; } ////// handles mc:AlternateContent element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleAlternateContent(int elementDepth, ref bool more) { if (Scope.InAlternateContent) { // the only valid tags within... > are // Choice and Fallback Error(SR.Get(SRID.XCRInvalidACChild, Reader.Name)); } if(Reader.IsEmptyElement) { // AlternateContent blocks must have a Choice, so they can't be empty Error(SR.Get(SRID.XCRChoiceNotFound)); } // check for markup-compatibility attributes, then push an AlternateContent scope ScanForCompatibility(elementDepth); PushScope(elementDepth); Scope.InAlternateContent = true; _depthOffset++; more = Reader.Read(); } /// /// handles mc:Choice element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleChoice(int elementDepth, ref bool more) { if (!Scope.InAlternateContent) { // Choice must be the child of AlternateContent Error(SR.Get(SRID.XCRChoiceOnlyInAC)); } if (Scope.FallbackSeen) { // Choice cannot occur after Fallback Error(SR.Get(SRID.XCRChoiceAfterFallback)); } string requiresValue = Reader.GetAttribute(Requires); if (requiresValue == null) { // Choice must have a requires attribute Error(SR.Get(SRID.XCRRequiresAttribNotFound)); } if (String.IsNullOrEmpty(requiresValue)) { // Requires attribute may not be empty Error(SR.Get(SRID.XCRInvalidRequiresAttribute)); } CompatibilityScope scope = Scope; // check for markup-compatibility attributes ScanForCompatibility(elementDepth); if (AttributeCount != 1) { // Choice may not have any attribute that should not be ignored other than Requires // get first non-markup-compatibility, non-Requires attribute MoveToFirstAttribute(); if(Reader.LocalName == Requires) { MoveToNextAttribute(); } string attributeName = Reader.LocalName; MoveToElement(); Error(SR.Get(SRID.XCRInvalidAttribInElement), attributeName, Choice); } if (scope.ChoiceTaken) { // a previous choice was valid, so pop any scope pushed and // skip to next attribute after ScanForEndCompatibility(elementDepth); Reader.Skip(); } else { // mark AlternateContent as having seen a choice scope.ChoiceSeen = true; bool allKnown = true; bool somethingSeen = false; foreach (string namespaceUri in PrefixesToNamespaces(requiresValue)) { somethingSeen = true; if (!IsNamespaceKnown(namespaceUri)) { // if any attribute in the Requires value is unknown, then do not take this choice allKnown = false; break; } } if (!somethingSeen) { // if the Requires value does not contain a valid prefix/namespace, throw an exception Error(SR.Get(SRID.XCRInvalidRequiresAttribute)); } if (allKnown) { // if all namespace in the Requires value are known, then this is the Choice taken. // Mark AlternateContent scope as having taken a choice scope.ChoiceTaken = true; // we push a scope here as a place holder, because AlternateContent // scopes do not allow child elements other than Choice and Fallback PushScope(elementDepth); _depthOffset++; more = Reader.Read(); } else { // this is not the choice taken, so pop any scope pushed and // skip to next attribute after ScanForEndCompatibility(elementDepth); Reader.Skip(); } } } ////// handles mc:Fallback element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleFallback(int elementDepth, ref bool more) { if (!Scope.InAlternateContent) { // Fallback must be the child of AlternateContent Error(SR.Get(SRID.XCRFallbackOnlyInAC)); } if (!Scope.ChoiceSeen) { // AlternateContent block must contain a Choice element Error(SR.Get(SRID.XCRChoiceNotFound)); } if (Scope.FallbackSeen) { // AlternateContent block may only contain one Fallback child Error(SR.Get(SRID.XCRMultipleFallbackFound)); } // mark scope as having a fallback Scope.FallbackSeen = true; bool choiceTaken = Scope.ChoiceTaken; // check for markup-compatibility attributes ScanForCompatibility(elementDepth); if (AttributeCount != 0) { // Fallback may not have any attribute that should not be ignored // get first non-markup-compatibility attribute MoveToFirstAttribute(); string attributeName = Reader.LocalName; MoveToElement(); Error(SR.Get(SRID.XCRInvalidAttribInElement), attributeName, Fallback); } if (choiceTaken) { // a choice was valid, so ignore contents ScanForEndCompatibility(elementDepth); Reader.Skip(); } else { // this is the content that will be used, so push a scope if(!Reader.IsEmptyElement) { // we push a scope here as a place holder, because AlternateContent // scopes do not allow child elements other than Choice and Fallback PushScope(elementDepth); _depthOffset++; } more = Reader.Read(); } } ////// handles mc:Ignorable="foo" attribute /// /// Ignorable is used to indicate that the namespace the prefix is mapped to can /// be ignored, i.e. when the namespace/element or namespace/attribute occurs it /// is not returned by the reader. /// private void HandleIgnorable(int elementDepth) { PushScope(elementDepth); foreach (string namespaceUri in PrefixesToNamespaces(Reader.Value)) { Scope.Ignorable(namespaceUri); } // Just in case one of the namespaces that preceded the Ignorable declaration // was an ignorable namespace, we have to recompute _ignoredAttributeCount :Þ . // No need to check if we haven't yet had any non-ignored attributes. if (_ignoredAttributeCount < _attributePosition) { _ignoredAttributeCount = 0; Reader.MoveToFirstAttribute(); for (int i = 0; i < _attributePosition; i++) { if (ShouldIgnoreNamespace(Reader.NamespaceURI)) { _ignoredAttributeCount++; } Reader.MoveToNextAttribute(); } } } ////// handles mc:MustUnderstand="foo" attribute /// /// MustUnderstand is used to indicate that the namespace the prefix is mapped to /// cannot be handled, and if it is not understood an exception is thrown /// private void HandleMustUnderstand(int elementDepth) { foreach (string namespaceUri in PrefixesToNamespaces(Reader.Value)) { if (!IsNamespaceKnown(namespaceUri)) { Error(SR.Get(SRID.XCRMustUnderstandFailed), namespaceUri); } } } ////// handles mc:ProcessContent="foo:bar" attribute /// /// ProcessContent is used to indicate that an ignorable namespace has some /// elements that should be skipped, but contain child elements that should be processed. /// /// The wildcard token ("foo:*") indicates that the children of any element in that /// namespace should be processed. /// private void HandleProcessContent(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _processContent)) { Scope.ProcessContent(pair.namespaceName, pair.itemName); } } ////// handles mc:PreserveElements="foo:bar" attribute /// /// functionality is supported, but not implemented /// private void HandlePreserveElements(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _preserveElements)) { Scope.PreserveElement(pair.namespaceName, pair.itemName); } } ////// handles mc:PreserveAttributes="foo:bar" attribute /// /// functionality is supported, but not implemented /// private void HandlePreserveAttributes(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _preserveAttributes)) { Scope.PreserveAttribute(pair.namespaceName, pair.itemName); } } ////// helper method to generate an exception /// private void Error(string message, params object[] args) { IXmlLineInfo info = Reader as IXmlLineInfo; throw new XmlException(string.Format(CultureInfo.InvariantCulture, message, args), null, info == null ? 1 : info.LineNumber, info == null ? 1 : info.LinePosition); } #endregion Private Methods #region Private Properties private CompatibilityScope Scope { get { return _compatibilityScope; } } private string AlternateContent { get { if (_alternateContent == null) { _alternateContent = Reader.NameTable.Add("AlternateContent"); } return _alternateContent; } } private string Choice { get { if (_choice == null) { _choice = Reader.NameTable.Add("Choice"); } return _choice; } } private string Fallback { get { if (_fallback == null) { _fallback = Reader.NameTable.Add("Fallback"); } return _fallback; } } private string Requires { get { if (_requires == null) { _requires = Reader.NameTable.Add("Requires"); } return _requires; } } private string Ignorable { get { if(_ignorable == null) { _ignorable = Reader.NameTable.Add("Ignorable"); } return _ignorable; } } private string MustUnderstand { get { if(_mustUnderstand == null) { _mustUnderstand = Reader.NameTable.Add("MustUnderstand"); } return _mustUnderstand; } } private string ProcessContent { get { if(_processContent == null) { _processContent = Reader.NameTable.Add("ProcessContent"); } return _processContent; } } private string PreserveElements { get { if(_preserveElements == null) { _preserveElements = Reader.NameTable.Add("PreserveElements"); } return _preserveElements; } } private string PreserveAttributes { get { if(_preserveAttributes == null) { _preserveAttributes = Reader.NameTable.Add("PreserveAttributes"); } return _preserveAttributes; } } private string CompatibilityUri { get { if (_compatibilityUri == null) { _compatibilityUri = Reader.NameTable.Add(MarkupCompatibilityURI); } return _compatibilityUri; } } #endregion Private Properties #region Nested Classes struct NamespaceElementPair { public string namespaceName; public string itemName; public NamespaceElementPair(string namespaceName, string itemName) { this.namespaceName = namespaceName; this.itemName = itemName; } } ////// CompatibilityScopes are used to handle markup-compatibility elements and attributes. /// Each scope stores the "previous" or parent scope, its depth, and an associated XmlCompatibilityReader. /// At a particular Reader depth, only one scope should be pushed. /// private class CompatibilityScope { CompatibilityScope _previous; int _depth; bool _fallbackSeen; bool _inAlternateContent; bool _inProcessContent; bool _choiceTaken; bool _choiceSeen; XmlCompatibilityReader _reader; Dictionary_ignorables; Dictionary _processContents; Dictionary _preserveElements; Dictionary _preserveAttributes; public CompatibilityScope(CompatibilityScope previous, int depth, XmlCompatibilityReader reader) { _previous = previous; _depth = depth; _reader = reader; } public CompatibilityScope Previous { get { return _previous; } } public int Depth { get { return _depth; } } public bool FallbackSeen { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.FallbackSeen; } else { result = _fallbackSeen; } return result; } set { if(_inProcessContent && _previous != null) { _previous.FallbackSeen = value; } else { _fallbackSeen = value; } } } public bool InAlternateContent { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.InAlternateContent; } else { result = _inAlternateContent; } return result; } set { _inAlternateContent = value; } } public bool InProcessContent { set { _inProcessContent = value; } } public bool ChoiceTaken { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.ChoiceTaken; } else { result = _choiceTaken; } return result; } set { if(_inProcessContent && _previous != null) { _previous.ChoiceTaken = value; } else { _choiceTaken = value; } } } public bool ChoiceSeen { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.ChoiceSeen; } else { result = _choiceSeen; } return result; } set { if(_inProcessContent && _previous != null) { _previous.ChoiceSeen = value; } else { _choiceSeen = value; } } } public bool CanIgnore(string namespaceName) { bool result = IsIgnorableAtCurrentScope(namespaceName); if (!result && _previous != null) { result = _previous.CanIgnore(namespaceName); } return result; } public bool IsIgnorableAtCurrentScope(string namespaceName) { return _ignorables != null && _ignorables.ContainsKey(namespaceName); } public bool ShouldProcessContent(string namespaceName, string elementName) { bool result = false; ProcessContentSet set; if (_processContents != null && _processContents.TryGetValue(namespaceName, out set)) { result = set.ShouldProcessContent(elementName); } else if (_previous != null) { result = _previous.ShouldProcessContent(namespaceName, elementName); } return result; } public void Ignorable(string namespaceName) { if (_ignorables == null) { _ignorables = new Dictionary (); } _ignorables[namespaceName] = null; // we don't care about value, just key } public void ProcessContent(string namespaceName, string elementName) { if (_processContents == null) { _processContents = new Dictionary (); } ProcessContentSet processContentSet; if (!_processContents.TryGetValue(namespaceName, out processContentSet)) { processContentSet = new ProcessContentSet(namespaceName, _reader); _processContents.Add(namespaceName, processContentSet); } processContentSet.Add(elementName); } public void PreserveElement(string namespaceName, string elementName) { if (_preserveElements == null) { _preserveElements = new Dictionary (); } PreserveItemSet preserveElementSet; if (!_preserveElements.TryGetValue(namespaceName, out preserveElementSet)) { preserveElementSet = new PreserveItemSet(namespaceName, _reader); _preserveElements.Add(namespaceName, preserveElementSet); } preserveElementSet.Add(elementName); } public void PreserveAttribute(string namespaceName, string attributeName) { if (_preserveAttributes == null) { _preserveAttributes = new Dictionary (); } PreserveItemSet preserveAttributeSet; if (!_preserveAttributes.TryGetValue(namespaceName, out preserveAttributeSet)) { preserveAttributeSet = new PreserveItemSet(namespaceName, _reader); _preserveAttributes.Add(namespaceName, preserveAttributeSet); } preserveAttributeSet.Add(attributeName); } /*public bool ShouldPreserveElement(string namespaceName, string elementName) { bool result = _preserveItems != null && _preserveItems.ShouldPreserveElement(namespaceName, elementName); if (!result && _previous != null) result = _previous.ShouldPreserveElement(namespaceName, elementName); return result; } public bool ShouldPreserveAttribute(string namespaceName, string attributeName) { bool result = _preserveItems != null && _preserveItems.ShouldPreserveAttribute(namespaceName, attributeName); if (!result && _previous != null) result = _previous.ShouldPreserveAttribute(namespaceName, attributeName); return result; }*/ public void Verify() { // Check process content if (_processContents != null) { foreach (string key in _processContents.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSProcessContentNotIgnorable), key); } } } // Check preserve elements if (_preserveElements != null) { foreach (string key in _preserveElements.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSPreserveNotIgnorable), key); } } } // Check preserve attributes if (_preserveAttributes != null) { foreach (string key in _preserveAttributes.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSPreserveNotIgnorable), key); } } } } } class ProcessContentSet { bool _all; string _namespaceName; XmlCompatibilityReader _reader; Dictionary _names; public ProcessContentSet(string namespaceName, XmlCompatibilityReader reader) { _namespaceName = namespaceName; _reader = reader; } public bool ShouldProcessContent(string elementName) { return _all || (_names != null && _names.ContainsKey(elementName)); } public void Add(string elementName) { if (ShouldProcessContent(elementName)) { if (elementName == "*") { _reader.Error(SR.Get(SRID.XCRDuplicateWildcardProcessContent), _namespaceName); } else { _reader.Error(SR.Get(SRID.XCRDuplicateProcessContent), _namespaceName, elementName); } } if (elementName == "*") { if (_names != null) { _reader.Error(SR.Get(SRID.XCRInvalidProcessContent), _namespaceName); } else { _all = true; } } else { if (_names == null) { _names = new Dictionary (); } _names[elementName] = null; // we don't care about value, just key } } } class PreserveItemSet { bool _all; string _namespaceName; XmlCompatibilityReader _reader; Dictionary _names; public PreserveItemSet(string namespaceName, XmlCompatibilityReader reader) { _namespaceName = namespaceName; _reader = reader; } public bool ShouldPreserveItem(string itemName) { return _all || (_names != null && _names.ContainsKey(itemName)); } public void Add(string itemName) { if (ShouldPreserveItem(itemName)) { if (itemName == "*") { _reader.Error(SR.Get(SRID.XCRDuplicateWildcardPreserve), _namespaceName); } else { _reader.Error(SR.Get(SRID.XCRDuplicatePreserve), itemName, _namespaceName); } } if (itemName == "*") { if (_names != null) { _reader.Error(SR.Get(SRID.XCRInvalidPreserve), _namespaceName); } else { _all = true; } } else { if (_names == null) { _names = new Dictionary (); } _names.Add(itemName, itemName); } } } #endregion Nested Classes #region Private Fields private bool _inAttribute; // for Save/Restore ReaderPosition private string _currentName; // for Save/Restore ReaderPosition private IsXmlNamespaceSupportedCallback _namespaceCallback; private Dictionary _knownNamespaces = new Dictionary (); private Dictionary _namespaceMap = new Dictionary (); private Dictionary _subsumingNamespaces = new Dictionary (); private Dictionary _elementHandler = new Dictionary (); private Dictionary _attributeHandler = new Dictionary (); private int _depthOffset; // offset for Depth method, to account for elements that should be ignored by client private int _ignoredAttributeCount; private int _attributePosition; // used for ScanForCompatibility / HandleIgnorable private string _compatibilityUri; private string _alternateContent; private string _choice; private string _fallback; private string _requires; private string _ignorable; private string _mustUnderstand; private string _processContent; private string _preserveElements; private string _preserveAttributes; private CompatibilityScope _compatibilityScope; private const string XmlnsDeclaration = "xmlns"; private const string MarkupCompatibilityURI = "http://schemas.openxmlformats.org/markup-compatibility/2006"; static private string [] _predefinedNamespaces = new string [4] { "http://www.w3.org/2000/xmlns/", "http://www.w3.org/XML/1998/namespace", "http://www.w3.org/2001/XMLSchema-instance", MarkupCompatibilityURI }; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. /****************************************************************************\ * * File: XmlCompatibilityReaderr.cs * * Purpose: * * History: * 5/11/05: fmunoz Created * 9/16/05: oliverfo Modified * 9/16/05: tjhsiang Modified * * Copyright (C) 2005 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Xml; using System.Collections.Generic; using System.Globalization; using System.Diagnostics; #if SYSTEM_XAML using System.Xaml; #endif #if PBTCOMPILER using MS.Utility; namespace MS.Internal.Markup #else using MS.Internal.WindowsBase; using System.Windows; namespace System.Windows.Markup #endif { #if !PBTCOMPILER [FriendAccessAllowed] #endif // // true if xmlNamespace is recognized // // // the namespace to be checked // // // if the passed in namespace is subsumed, then newXmlNamespace returns the subsuming namespace. // internal delegate bool IsXmlNamespaceSupportedCallback(string xmlNamespace, out string newXmlNamespace); delegate void HandleElementCallback(int elementDepth, ref bool more); delegate void HandleAttributeCallback(int elementDepth); #if !PBTCOMPILER [FriendAccessAllowed] #endif internal sealed class XmlCompatibilityReader: XmlWrappingReader { #region Construction public XmlCompatibilityReader(XmlReader baseReader):base(baseReader) { _compatibilityScope = new CompatibilityScope(null, -1, this); foreach (string xmlNamespace in _predefinedNamespaces) { AddKnownNamespace(xmlNamespace); _namespaceMap[xmlNamespace] = xmlNamespace; Reader.NameTable.Add(xmlNamespace); } _elementHandler.Add(AlternateContent, new HandleElementCallback(HandleAlternateContent)); _elementHandler.Add(Choice, new HandleElementCallback(HandleChoice)); _elementHandler.Add(Fallback, new HandleElementCallback(HandleFallback)); _attributeHandler.Add(Ignorable, new HandleAttributeCallback(HandleIgnorable)); _attributeHandler.Add(MustUnderstand, new HandleAttributeCallback(HandleMustUnderstand)); _attributeHandler.Add(ProcessContent, new HandleAttributeCallback(HandleProcessContent)); _attributeHandler.Add(PreserveElements, new HandleAttributeCallback(HandlePreserveElements)); _attributeHandler.Add(PreserveAttributes, new HandleAttributeCallback(HandlePreserveAttributes)); } public XmlCompatibilityReader(XmlReader baseReader, IsXmlNamespaceSupportedCallback isXmlNamespaceSupported): this(baseReader) { _namespaceCallback = isXmlNamespaceSupported; } public XmlCompatibilityReader(XmlReader baseReader, IsXmlNamespaceSupportedCallback isXmlNamespaceSupported, IEnumerablesupportedNamespaces):this(baseReader, isXmlNamespaceSupported) { foreach (string xmlNamespace in supportedNamespaces) { AddKnownNamespace(xmlNamespace); _namespaceMap[xmlNamespace] = xmlNamespace; } } #if !PBTCOMPILER public XmlCompatibilityReader(XmlReader baseReader, IEnumerable supportedNamespaces):this(baseReader, null, supportedNamespaces) { } #endif #endregion Construction #region Public Methods /// /// replaces all future references of namespace URI 'oldNamespace' with 'newNamespace' /// /// /// the namespace to subsume with /// /// /// the namespace to be subsumed /// public void DeclareNamespaceCompatibility(string newNamespace, string oldNamespace) { if(newNamespace != oldNamespace) { // indicate that newNamespace subsumes another namespace AddSubsumingNamespace(newNamespace); // If newNamespace is mapped to a namespace, string tempNamespace; if (_namespaceMap.TryGetValue(newNamespace, out tempNamespace)) { // If we have mapped newNamespace already get the newest name. // We don't have to do this recursively because of the code below // ensures the map always refers to the newest namespace. newNamespace = tempNamespace; } if(IsSubsumingNamespace(oldNamespace)) { // if we are mapping what was used as a new namespace to a newer name, // scan the _newNamespaces dictionary and update the entries. We collect // a list to avoid updating the dictonary during enumeration. ListkeysToUpdate = new List (); foreach (KeyValuePair pair in _namespaceMap) { if (pair.Value == oldNamespace) { keysToUpdate.Add(pair.Key); } } foreach (string key in keysToUpdate) { _namespaceMap[key] = newNamespace; } } } _namespaceMap[oldNamespace] = newNamespace; } /// /// Reads the next node from the stream. /// ////// true if the next node was read successfully; false if there are no more nodes to read. /// public override bool Read() { bool more = Reader.Read(); //passed as ref arg to ReadStartElement and ReadEndElement bool result = false; while (more) { switch (Reader.NodeType) { case XmlNodeType.Element: { // if the element read should be ignored, read the next element if(!ReadStartElement(ref more)) { continue; } break; } case XmlNodeType.EndElement: { // if the element read should be ignored, read the next element if(!ReadEndElement(ref more)) { continue; } break; } } // if the element was read successfully and was not ignored, break and return true result = true; break; } return result; } ////// Used to handle 'start element' tags. These are actually /// just called 'element' tags, the 'start' is just for clarity /// /// /// is set to true if there is the document contains more elements, false if the end of the /// document has been reached. /// ////// true if an element was read that should not be ignored /// false if the element read should be ignored or the end of document has been reached /// private bool ReadStartElement(ref bool more) { // when processing elements, the Reader may advance to another element or attribute, // so we save the values of the current element here int elementDepth = Reader.Depth; int depthOffset = _depthOffset; bool isEmpty = Reader.IsEmptyElement; string namespaceName = NamespaceURI; bool result = false; if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the element is a markup-compatibility element, we get the appropriate handler for // the element type, and call the appropriate delegate. If the element is not recognized // we throw an exception. string elementName = Reader.LocalName; HandleElementCallback elementCB; if(!_elementHandler.TryGetValue(elementName, out elementCB)) { Error(SR.Get(SRID.XCRUnknownCompatElement), elementName); } elementCB(elementDepth, ref more); } // handle non-markup-compatibility elements else { // check for markup-compatibility attributes and namespaces that should be ignored ScanForCompatibility(elementDepth); if (ShouldIgnoreNamespace(namespaceName)) { if(Scope.ShouldProcessContent(namespaceName, Reader.LocalName)) { // if the current element is unknown and has been marked Ignorable and ProcessContent, // then read the next element, and increase depth offset if(Scope.Depth == elementDepth) { // if the current element pushed a scope, mark the scope as InProcessContent to // note that for certain logic this scope's parent should be checked Scope.InProcessContent = true; } _depthOffset++; more = Reader.Read(); } else { // if element should be ignored but not processed, check to see if scope must be popped, // then skip to the next element after the end tag of the current element ScanForEndCompatibility(elementDepth); Reader.Skip(); } } else { if (Scope.InAlternateContent) { // if this element is the child of an AlternateContent element, then throw an exception. Error(SR.Get(SRID.XCRInvalidACChild), Reader.Name); } result = true; } } // if the element is empty (e.g. "" and we pushed a scope then get // rid of the scope if(isEmpty) { ScanForEndCompatibility(elementDepth); _depthOffset = depthOffset; } return result; } ////// Used to handle any end element tag /// /// /// is set to true if there is the document contains more elements, false if the end of the /// document has been reached. /// ////// true if an element was read that should not be ignored /// false if the element read should be ignored or the end of document has been reached /// private bool ReadEndElement(ref bool more) { // when reading attributes, the reader's depth increases, so for consistency // we store the depth before reading any attributes int elementDepth = Reader.Depth; string namespaceName = NamespaceURI; bool result = false; // return value if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the element is a markup-compatibility element, pop a scope, decrement the // depth offset and read the next element. string elementName = Reader.LocalName; if (object.ReferenceEquals(elementName, AlternateContent)) { if (!Scope.ChoiceSeen) { // if the current element was a , without any Choice // element children, throw an exception Error(SR.Get(SRID.XCRChoiceNotFound)); } } _depthOffset--; PopScope(); //we know we can pop, so no need to scan more = Reader.Read(); } else { if (ShouldIgnoreNamespace(namespaceName)) { // if current element is Ignorable, then to be on it, it must have been marked // ProcessContent. Pop a scope if the corresponding start element pushed a scope a // scope, decrement the depth offset and read the next element. Debug.Assert(Scope.ShouldProcessContent(namespaceName, Reader.LocalName)); ScanForEndCompatibility(elementDepth); _depthOffset--; more = Reader.Read(); } else { ScanForEndCompatibility(elementDepth); result = true; } } return result; } ////// Gets the value of the attribute with the specified index. /// /// /// The index of the attribute. The index is zero-based. (The first attribute has index 0.) /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(int i) { string result = null; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any of its attributes, skip extra logic result = Reader.GetAttribute(i); } else { SaveReaderPosition(); // move to 'i'th attribute, get its value MoveToAttribute(i); result = Reader.Value; RestoreReaderPosition(); } return result; } ////// Gets the value of the attribute with the specified name. /// /// /// The qualified name of the attribute. /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(string name) { string result = null; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.GetAttribute(name); } else { SaveReaderPosition(); // move to "name" attribute if (MoveToAttribute(name)) { result = Reader.Value; RestoreReaderPosition(); } } return result; } ////// Gets the value of the attribute with the specified local name and namespace URI. /// /// /// The local name of the attribute. /// /// /// The namespace URI of the attribute. /// ////// The value of the specified attribute. If the attribute is not found, a null reference is returned. /// public override string GetAttribute(string localName, string namespaceURI) { string result = null; if (_ignoredAttributeCount == 0 || !ShouldIgnoreNamespace(namespaceURI) ) { // if the current element does not have any attributes that should be ignored or // the namespace provided is not ignorable, call Reader method result = Reader.GetAttribute(localName, namespaceURI); } return result; } ////// Gets the value of the attribute with the specified index. /// /// /// The index of the attribute. The index is zero-based. (The first attribute has index 0.) /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override void MoveToAttribute(int i) { if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method Reader.MoveToAttribute(i); } else if (i < 0 || i >= AttributeCount) { throw new ArgumentOutOfRangeException("i"); } else { // move Reader to first attribute and iterate until 'i'th element found Reader.MoveToFirstAttribute(); while (true) { if (!ShouldIgnoreNamespace(NamespaceURI)) { // if attribute should not be ignored, decrement 'i', if i == 0 we've found element if (i-- == 0) { break; } } Reader.MoveToNextAttribute(); } } } ////// Moves to the attribute with the specified name. /// /// /// The qualified name of the attribute. /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override bool MoveToAttribute(string name) { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToAttribute(name); } else { SaveReaderPosition(); result = Reader.MoveToAttribute(name); if (result && ShouldIgnoreNamespace(NamespaceURI)) { // if attribute should be ignored, return false and restore state result = false; RestoreReaderPosition(); } } return result; } ////// Moves to the attribute with the specified local name and namespace URI. /// /// /// The local name of the attribute. /// /// /// The namespace URI of the attribute. /// ////// true if the attribute is found; otherwise, false. If false, the reader's position does not change. /// public override bool MoveToAttribute(string localName, string namespaceURI) { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToAttribute(localName, namespaceURI); } else { SaveReaderPosition(); result = Reader.MoveToAttribute(localName, namespaceURI); if (result && ShouldIgnoreNamespace(namespaceURI)) { result = false; RestoreReaderPosition(); } } return result; } ////// Moves to the first attribute. /// ////// true if an attribute exists (the reader moves to the first attribute); /// otherwise, false (the position of the reader does not change). /// public override bool MoveToFirstAttribute() { bool result = HasAttributes; if(result) { MoveToAttribute(0); } return result; } ////// Moves to the next attribute. /// ////// true if there is a next attribute; false if there are no more attributes. /// public override bool MoveToNextAttribute() { bool result; if (_ignoredAttributeCount == 0) { // if the current element should not ignored any attributes, call Reader method result = Reader.MoveToNextAttribute(); } else { SaveReaderPosition(); result = Reader.MoveToNextAttribute(); if (result) { result = SkipToKnownAttribute(); if (!result) { // if no more attributes exist that should not be ignored, return false and restore state RestoreReaderPosition(); } } } return result; } ////// Resolves a namespace prefix in the current element's scope. /// /// /// The prefix whose namespace URI you want to resolve. To match the default namespace, /// pass an empty string. This string does not have to be atomized. /// ////// The namespace URI to which the prefix maps or a null reference if no matching prefix is found. /// public override string LookupNamespace (string prefix) { string namespaceName = Reader.LookupNamespace(prefix); if (namespaceName != null) { namespaceName = GetMappedNamespace(namespaceName); } return namespaceName; } #endregion Public Methods #region Public Properties ////// This override is to ensure that the value /// for the xmlns attribute reflects all the /// compatibility (subsuming) rules. /// public override string Value { get { // Look for xmlns if (String.Equals(XmlnsDeclaration, Reader.LocalName, StringComparison.Ordinal)) { return LookupNamespace(String.Empty); } // Look for xmlns: ... else if (String.Equals(XmlnsDeclaration, Reader.Prefix, StringComparison.Ordinal)) { return LookupNamespace(Reader.LocalName); } return Reader.Value; } } ////// Gets the namespace URI (as defined in the W3C Namespace specification) of the node /// on which the reader is positioned. /// public override string NamespaceURI { get { return GetMappedNamespace(Reader.NamespaceURI); } } ////// Gets the depth of the current node in the XML document. /// public override int Depth { get { return Reader.Depth - _depthOffset; } } ////// Gets a value indicating whether the current node has any attributes /// public override bool HasAttributes { get { return AttributeCount != 0; } } ////// Gets the number of attributes on the current node. /// public override int AttributeCount { get { return Reader.AttributeCount - _ignoredAttributeCount; } } ////// Sets a value indicating whether to normalize white space and attribute values. /// public bool Normalization { set { XmlTextReader xmlTextReader = Reader as XmlTextReader; // review, what if not the XmlTextReader. if (null != xmlTextReader) { xmlTextReader.Normalization = value; } } } #if !PBTCOMPILER ////// Answer the encoding of the underlying xaml stream /// internal System.Text.Encoding Encoding { get { XmlTextReader textReader = Reader as XmlTextReader; if (textReader == null) { return new System.Text.UTF8Encoding(true,true); } else { return textReader.Encoding; } } } #endif #endregion Public Properties #region Private Methods private void SaveReaderPosition() { // Save current state so we can go back to the same spot if this fails _inAttribute = (Reader.NodeType == XmlNodeType.Attribute); _currentName = Reader.Name; } private void RestoreReaderPosition() { // Restore reader state from SaveReaderPosition if (_inAttribute) { Reader.MoveToAttribute(_currentName); } else { Reader.MoveToElement(); } } ////// Retrieves the correctly mapped namespace from the namespace provided /// /// /// The name of the namespace to retrieve the mapping of /// ////// The name of the mapped namespace. /// private string GetMappedNamespace(string namespaceName) { string mappedNamespace; // if the namespace is not null, get the mapped namespace (which may be itself) if (!_namespaceMap.TryGetValue(namespaceName, out mappedNamespace)) { // if the namespace has not yet been mapped, map it mappedNamespace = MapNewNamespace(namespaceName); } else if (mappedNamespace == null) { // if the mapped namespace is null, then the namespace was not supported, just return // the given namespace mappedNamespace = namespaceName; } return mappedNamespace; } ////// Adds the namespace to the namespace map. The default is to map the namespace to itself. /// The namespace is mapped to the value returned by the callback, if a callback exists and the /// callback returns a subsuming namespace. /// /// /// The name of the namespace to be mapped. /// ////// The name of the mapped namespace. /// private string MapNewNamespace(string namespaceName) { if (_namespaceCallback != null) { string mappedNamespace; // the callback returns whether the namespace is supported, and mappedNamespace is the // namespace subsuming the namespace passed in. bool isSupported = _namespaceCallback(namespaceName, out mappedNamespace); if (isSupported) { AddKnownNamespace(namespaceName); if(String.IsNullOrEmpty(mappedNamespace) || namespaceName == mappedNamespace) { _namespaceMap[namespaceName] = namespaceName; } else { // subsume namespace with mappedNamespace. string tempNamespace; if (!_namespaceMap.TryGetValue(mappedNamespace, out tempNamespace)) { // mappedNamespace has not been mapped, so map it tempNamespace = MapNewNamespace(mappedNamespace); } DeclareNamespaceCompatibility(tempNamespace, namespaceName); namespaceName = tempNamespace; } } else { // if the namespace is not supported, we enter null into the namespaceMap as a placeholder // so that we do not call the callback again on this namespace. _namespaceMap[namespaceName] = null; } } return namespaceName; } ////// Used to determine whether a given namespace subsumes another namespace /// /// /// The name of the namespace to be checked. /// ////// true if the namespace subsumes another namespace; false otherwise /// private bool IsSubsumingNamespace(string namespaceName) { return _subsumingNamespaces.ContainsKey(namespaceName); } ////// Used to specify that a namespace subsumes another namespace /// /// /// The name of the namespace to be added. /// private void AddSubsumingNamespace(string namespaceName) { _subsumingNamespaces[namespaceName] = null; } ////// Used to determine whether a given namespace is known/supported /// /// /// The name of the namespace to be checked. /// ////// true if the namespace is known/supported; false otherwise /// private bool IsNamespaceKnown(string namespaceName) { return _knownNamespaces.ContainsKey(namespaceName); } ////// Used to specify that a namespace is known or supported /// /// /// The name of the namespace to be added. /// private void AddKnownNamespace(string namespaceName) { _knownNamespaces[namespaceName] = null; } ////// Used to determine whether a given namespace should be ignored. A namespace should be ignored if: /// EITHER /// a) the namespace is not known/supported and has been marked Ignorable /// OR /// b) the namespace is the markup-compatibility namespace /// /// /// The name of the prefix to be checked. /// ////// true if the namespace should be ignored; false otherwise /// private bool ShouldIgnoreNamespace(string namespaceName) { bool result; if(IsNamespaceKnown(namespaceName)) { result = object.ReferenceEquals(namespaceName, CompatibilityUri); } else { result = Scope.CanIgnore(namespaceName); } return result; } ////// breaks up a space-delineated string into namespace/element pairs /// /// /// the string to be parsed /// /// /// The calling element, used in case of an error /// ////// the list of namespace/element pairs /// private IEnumerableParseContentToNamespaceElementPair(string content, string callerContext) { foreach (string pair in content.Trim().Split(' ')) { // check each non-null, non-empty space-delineated namespace/element pair if(!String.IsNullOrEmpty(pair)) { int colonIndex = pair.IndexOf(':'); int length = pair.Length; if(colonIndex <= 0 || colonIndex >= length - 1 || colonIndex != pair.LastIndexOf(':')) { // if string does not have a ':', if the last character in the string is a ':' // or if the string contains more than one ':', throw an exception Error(SR.Get(SRID.XCRInvalidFormat), callerContext); } string prefix = pair.Substring(0, colonIndex); string elementName = pair.Substring(colonIndex + 1, length - 1 - colonIndex); string namespaceName = LookupNamespace(prefix); if (namespaceName == null) { // if a prefix does not map to a namespace, throw an exception Error(SR.Get(SRID.XCRUndefinedPrefix), prefix); } else if (elementName != "*" && !IsName(elementName)) { // if the element's name is not valid XML, throw an exception Error(SR.Get(SRID.XCRInvalidXMLName), pair); } else { yield return new NamespaceElementPair(namespaceName, elementName); } } } } /// /// converts a string of space-delineated prefixes into a list of namespaces /// /// /// the string to be parsed /// ////// the list of namespace/element pairs /// private IEnumerablePrefixesToNamespaces(string prefixes) { foreach (string prefix in prefixes.Trim().Split(' ')) { // check each non-null, non-empty space-delineated prefix if(!String.IsNullOrEmpty(prefix)) { string namespaceUri = LookupNamespace(prefix); if (namespaceUri == null) { // if a prefix does not map to a namespace, throw an exception Error(SR.Get(SRID.XCRUndefinedPrefix), prefix); } else { yield return namespaceUri; } } } } /// /// advances the reader to the next known namespace/attribute pair /// ////// true if a known namespace/attribute pair was found /// private bool SkipToKnownAttribute() { bool result = true; while (result && ShouldIgnoreNamespace(NamespaceURI)) { result = Reader.MoveToNextAttribute(); } return result; } ////// Scans the current element for compatibility attributes. Pushes a new /// scope onto the stack under the following conditions: /// 1) Ignorable or MustUnderstand attribute read /// 2) current element has not previously declared an Ignorable or /// MustUnderstand attribute /// /// However, if a last condition is not fulfilled, then the scope is popped off /// before the function returns /// 3) current element is not empty /// /// stores in _ignoredAttributeCount the number of attributes on the current element /// that should be ignored, for the sake of improving perf in attribute-related /// methods/properties /// /// /// the depth of the Reader at the element currently being processed /// private void ScanForCompatibility(int elementDepth) { bool onAttribute = Reader.MoveToFirstAttribute(); _ignoredAttributeCount = 0; if (onAttribute) { _attributePosition = 0; // we count the attribute index in case we see Ignorable do { string namespaceName = NamespaceURI; if(ShouldIgnoreNamespace(namespaceName)) { // check each attribute's namespace to see if it should be ignored if (object.ReferenceEquals(namespaceName, CompatibilityUri)) { // if the attribute is in the markup-compatibility namespace // find and call the appropriate attribute handler callback. string attributeName = Reader.LocalName; HandleAttributeCallback attributeCB; if(!_attributeHandler.TryGetValue(attributeName, out attributeCB)) { Error(SR.Get(SRID.XCRUnknownCompatAttrib), attributeName); } attributeCB(elementDepth); } _ignoredAttributeCount++; } onAttribute = Reader.MoveToNextAttribute(); _attributePosition++; // we count the attribute index in case we see Ignorable } while (onAttribute); if (Scope.Depth == elementDepth) { // if this element pushed a scope, then we need to do a sanity check Scope.Verify(); } // move the reader back to the element for the client Reader.MoveToElement(); } } ////// pops a scope if the end of a compatibility region. /// /// /// the depth of the Reader at the element currently being processed /// private void ScanForEndCompatibility(int elementDepth) { if (elementDepth == Scope.Depth) { // if the current element's depth equals the depth of the top-level scope, then pop PopScope(); } } ////// pushes a new scope onto the stack with a depth passed as an arg. /// PushScope does not push a scope if the top scope on the stack is not a lower depth. /// /// /// the depth of the Reader at the element currently being processed /// private void PushScope(int elementDepth) { if(_compatibilityScope.Depth < elementDepth) { // if the current element has already pushed a scope, then don't push another one _compatibilityScope = new CompatibilityScope(_compatibilityScope, elementDepth, this); } } ////// pops a scope off the top of the stack. /// PopScope *always* pops, it does not check the depth before doing so /// private void PopScope() { _compatibilityScope = _compatibilityScope.Previous; } ////// handles mc:AlternateContent element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleAlternateContent(int elementDepth, ref bool more) { if (Scope.InAlternateContent) { // the only valid tags within... > are // Choice and Fallback Error(SR.Get(SRID.XCRInvalidACChild, Reader.Name)); } if(Reader.IsEmptyElement) { // AlternateContent blocks must have a Choice, so they can't be empty Error(SR.Get(SRID.XCRChoiceNotFound)); } // check for markup-compatibility attributes, then push an AlternateContent scope ScanForCompatibility(elementDepth); PushScope(elementDepth); Scope.InAlternateContent = true; _depthOffset++; more = Reader.Read(); } /// /// handles mc:Choice element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleChoice(int elementDepth, ref bool more) { if (!Scope.InAlternateContent) { // Choice must be the child of AlternateContent Error(SR.Get(SRID.XCRChoiceOnlyInAC)); } if (Scope.FallbackSeen) { // Choice cannot occur after Fallback Error(SR.Get(SRID.XCRChoiceAfterFallback)); } string requiresValue = Reader.GetAttribute(Requires); if (requiresValue == null) { // Choice must have a requires attribute Error(SR.Get(SRID.XCRRequiresAttribNotFound)); } if (String.IsNullOrEmpty(requiresValue)) { // Requires attribute may not be empty Error(SR.Get(SRID.XCRInvalidRequiresAttribute)); } CompatibilityScope scope = Scope; // check for markup-compatibility attributes ScanForCompatibility(elementDepth); if (AttributeCount != 1) { // Choice may not have any attribute that should not be ignored other than Requires // get first non-markup-compatibility, non-Requires attribute MoveToFirstAttribute(); if(Reader.LocalName == Requires) { MoveToNextAttribute(); } string attributeName = Reader.LocalName; MoveToElement(); Error(SR.Get(SRID.XCRInvalidAttribInElement), attributeName, Choice); } if (scope.ChoiceTaken) { // a previous choice was valid, so pop any scope pushed and // skip to next attribute after ScanForEndCompatibility(elementDepth); Reader.Skip(); } else { // mark AlternateContent as having seen a choice scope.ChoiceSeen = true; bool allKnown = true; bool somethingSeen = false; foreach (string namespaceUri in PrefixesToNamespaces(requiresValue)) { somethingSeen = true; if (!IsNamespaceKnown(namespaceUri)) { // if any attribute in the Requires value is unknown, then do not take this choice allKnown = false; break; } } if (!somethingSeen) { // if the Requires value does not contain a valid prefix/namespace, throw an exception Error(SR.Get(SRID.XCRInvalidRequiresAttribute)); } if (allKnown) { // if all namespace in the Requires value are known, then this is the Choice taken. // Mark AlternateContent scope as having taken a choice scope.ChoiceTaken = true; // we push a scope here as a place holder, because AlternateContent // scopes do not allow child elements other than Choice and Fallback PushScope(elementDepth); _depthOffset++; more = Reader.Read(); } else { // this is not the choice taken, so pop any scope pushed and // skip to next attribute after ScanForEndCompatibility(elementDepth); Reader.Skip(); } } } ////// handles mc:Fallback element /// /// a good way to think of AlternateContent blocks is as a switch/case /// statement. The AlternateContent tag is like switch, Choice is like /// case, and Fallback is like default. /// /// /// the depth of the Reader at the element currently being processed /// /// /// returns whether the Reader has more to be read /// private void HandleFallback(int elementDepth, ref bool more) { if (!Scope.InAlternateContent) { // Fallback must be the child of AlternateContent Error(SR.Get(SRID.XCRFallbackOnlyInAC)); } if (!Scope.ChoiceSeen) { // AlternateContent block must contain a Choice element Error(SR.Get(SRID.XCRChoiceNotFound)); } if (Scope.FallbackSeen) { // AlternateContent block may only contain one Fallback child Error(SR.Get(SRID.XCRMultipleFallbackFound)); } // mark scope as having a fallback Scope.FallbackSeen = true; bool choiceTaken = Scope.ChoiceTaken; // check for markup-compatibility attributes ScanForCompatibility(elementDepth); if (AttributeCount != 0) { // Fallback may not have any attribute that should not be ignored // get first non-markup-compatibility attribute MoveToFirstAttribute(); string attributeName = Reader.LocalName; MoveToElement(); Error(SR.Get(SRID.XCRInvalidAttribInElement), attributeName, Fallback); } if (choiceTaken) { // a choice was valid, so ignore contents ScanForEndCompatibility(elementDepth); Reader.Skip(); } else { // this is the content that will be used, so push a scope if(!Reader.IsEmptyElement) { // we push a scope here as a place holder, because AlternateContent // scopes do not allow child elements other than Choice and Fallback PushScope(elementDepth); _depthOffset++; } more = Reader.Read(); } } ////// handles mc:Ignorable="foo" attribute /// /// Ignorable is used to indicate that the namespace the prefix is mapped to can /// be ignored, i.e. when the namespace/element or namespace/attribute occurs it /// is not returned by the reader. /// private void HandleIgnorable(int elementDepth) { PushScope(elementDepth); foreach (string namespaceUri in PrefixesToNamespaces(Reader.Value)) { Scope.Ignorable(namespaceUri); } // Just in case one of the namespaces that preceded the Ignorable declaration // was an ignorable namespace, we have to recompute _ignoredAttributeCount :Þ . // No need to check if we haven't yet had any non-ignored attributes. if (_ignoredAttributeCount < _attributePosition) { _ignoredAttributeCount = 0; Reader.MoveToFirstAttribute(); for (int i = 0; i < _attributePosition; i++) { if (ShouldIgnoreNamespace(Reader.NamespaceURI)) { _ignoredAttributeCount++; } Reader.MoveToNextAttribute(); } } } ////// handles mc:MustUnderstand="foo" attribute /// /// MustUnderstand is used to indicate that the namespace the prefix is mapped to /// cannot be handled, and if it is not understood an exception is thrown /// private void HandleMustUnderstand(int elementDepth) { foreach (string namespaceUri in PrefixesToNamespaces(Reader.Value)) { if (!IsNamespaceKnown(namespaceUri)) { Error(SR.Get(SRID.XCRMustUnderstandFailed), namespaceUri); } } } ////// handles mc:ProcessContent="foo:bar" attribute /// /// ProcessContent is used to indicate that an ignorable namespace has some /// elements that should be skipped, but contain child elements that should be processed. /// /// The wildcard token ("foo:*") indicates that the children of any element in that /// namespace should be processed. /// private void HandleProcessContent(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _processContent)) { Scope.ProcessContent(pair.namespaceName, pair.itemName); } } ////// handles mc:PreserveElements="foo:bar" attribute /// /// functionality is supported, but not implemented /// private void HandlePreserveElements(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _preserveElements)) { Scope.PreserveElement(pair.namespaceName, pair.itemName); } } ////// handles mc:PreserveAttributes="foo:bar" attribute /// /// functionality is supported, but not implemented /// private void HandlePreserveAttributes(int elementDepth) { PushScope(elementDepth); foreach (NamespaceElementPair pair in ParseContentToNamespaceElementPair(Reader.Value, _preserveAttributes)) { Scope.PreserveAttribute(pair.namespaceName, pair.itemName); } } ////// helper method to generate an exception /// private void Error(string message, params object[] args) { IXmlLineInfo info = Reader as IXmlLineInfo; throw new XmlException(string.Format(CultureInfo.InvariantCulture, message, args), null, info == null ? 1 : info.LineNumber, info == null ? 1 : info.LinePosition); } #endregion Private Methods #region Private Properties private CompatibilityScope Scope { get { return _compatibilityScope; } } private string AlternateContent { get { if (_alternateContent == null) { _alternateContent = Reader.NameTable.Add("AlternateContent"); } return _alternateContent; } } private string Choice { get { if (_choice == null) { _choice = Reader.NameTable.Add("Choice"); } return _choice; } } private string Fallback { get { if (_fallback == null) { _fallback = Reader.NameTable.Add("Fallback"); } return _fallback; } } private string Requires { get { if (_requires == null) { _requires = Reader.NameTable.Add("Requires"); } return _requires; } } private string Ignorable { get { if(_ignorable == null) { _ignorable = Reader.NameTable.Add("Ignorable"); } return _ignorable; } } private string MustUnderstand { get { if(_mustUnderstand == null) { _mustUnderstand = Reader.NameTable.Add("MustUnderstand"); } return _mustUnderstand; } } private string ProcessContent { get { if(_processContent == null) { _processContent = Reader.NameTable.Add("ProcessContent"); } return _processContent; } } private string PreserveElements { get { if(_preserveElements == null) { _preserveElements = Reader.NameTable.Add("PreserveElements"); } return _preserveElements; } } private string PreserveAttributes { get { if(_preserveAttributes == null) { _preserveAttributes = Reader.NameTable.Add("PreserveAttributes"); } return _preserveAttributes; } } private string CompatibilityUri { get { if (_compatibilityUri == null) { _compatibilityUri = Reader.NameTable.Add(MarkupCompatibilityURI); } return _compatibilityUri; } } #endregion Private Properties #region Nested Classes struct NamespaceElementPair { public string namespaceName; public string itemName; public NamespaceElementPair(string namespaceName, string itemName) { this.namespaceName = namespaceName; this.itemName = itemName; } } ////// CompatibilityScopes are used to handle markup-compatibility elements and attributes. /// Each scope stores the "previous" or parent scope, its depth, and an associated XmlCompatibilityReader. /// At a particular Reader depth, only one scope should be pushed. /// private class CompatibilityScope { CompatibilityScope _previous; int _depth; bool _fallbackSeen; bool _inAlternateContent; bool _inProcessContent; bool _choiceTaken; bool _choiceSeen; XmlCompatibilityReader _reader; Dictionary_ignorables; Dictionary _processContents; Dictionary _preserveElements; Dictionary _preserveAttributes; public CompatibilityScope(CompatibilityScope previous, int depth, XmlCompatibilityReader reader) { _previous = previous; _depth = depth; _reader = reader; } public CompatibilityScope Previous { get { return _previous; } } public int Depth { get { return _depth; } } public bool FallbackSeen { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.FallbackSeen; } else { result = _fallbackSeen; } return result; } set { if(_inProcessContent && _previous != null) { _previous.FallbackSeen = value; } else { _fallbackSeen = value; } } } public bool InAlternateContent { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.InAlternateContent; } else { result = _inAlternateContent; } return result; } set { _inAlternateContent = value; } } public bool InProcessContent { set { _inProcessContent = value; } } public bool ChoiceTaken { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.ChoiceTaken; } else { result = _choiceTaken; } return result; } set { if(_inProcessContent && _previous != null) { _previous.ChoiceTaken = value; } else { _choiceTaken = value; } } } public bool ChoiceSeen { get { bool result; if(_inProcessContent && _previous != null) { result = _previous.ChoiceSeen; } else { result = _choiceSeen; } return result; } set { if(_inProcessContent && _previous != null) { _previous.ChoiceSeen = value; } else { _choiceSeen = value; } } } public bool CanIgnore(string namespaceName) { bool result = IsIgnorableAtCurrentScope(namespaceName); if (!result && _previous != null) { result = _previous.CanIgnore(namespaceName); } return result; } public bool IsIgnorableAtCurrentScope(string namespaceName) { return _ignorables != null && _ignorables.ContainsKey(namespaceName); } public bool ShouldProcessContent(string namespaceName, string elementName) { bool result = false; ProcessContentSet set; if (_processContents != null && _processContents.TryGetValue(namespaceName, out set)) { result = set.ShouldProcessContent(elementName); } else if (_previous != null) { result = _previous.ShouldProcessContent(namespaceName, elementName); } return result; } public void Ignorable(string namespaceName) { if (_ignorables == null) { _ignorables = new Dictionary (); } _ignorables[namespaceName] = null; // we don't care about value, just key } public void ProcessContent(string namespaceName, string elementName) { if (_processContents == null) { _processContents = new Dictionary (); } ProcessContentSet processContentSet; if (!_processContents.TryGetValue(namespaceName, out processContentSet)) { processContentSet = new ProcessContentSet(namespaceName, _reader); _processContents.Add(namespaceName, processContentSet); } processContentSet.Add(elementName); } public void PreserveElement(string namespaceName, string elementName) { if (_preserveElements == null) { _preserveElements = new Dictionary (); } PreserveItemSet preserveElementSet; if (!_preserveElements.TryGetValue(namespaceName, out preserveElementSet)) { preserveElementSet = new PreserveItemSet(namespaceName, _reader); _preserveElements.Add(namespaceName, preserveElementSet); } preserveElementSet.Add(elementName); } public void PreserveAttribute(string namespaceName, string attributeName) { if (_preserveAttributes == null) { _preserveAttributes = new Dictionary (); } PreserveItemSet preserveAttributeSet; if (!_preserveAttributes.TryGetValue(namespaceName, out preserveAttributeSet)) { preserveAttributeSet = new PreserveItemSet(namespaceName, _reader); _preserveAttributes.Add(namespaceName, preserveAttributeSet); } preserveAttributeSet.Add(attributeName); } /*public bool ShouldPreserveElement(string namespaceName, string elementName) { bool result = _preserveItems != null && _preserveItems.ShouldPreserveElement(namespaceName, elementName); if (!result && _previous != null) result = _previous.ShouldPreserveElement(namespaceName, elementName); return result; } public bool ShouldPreserveAttribute(string namespaceName, string attributeName) { bool result = _preserveItems != null && _preserveItems.ShouldPreserveAttribute(namespaceName, attributeName); if (!result && _previous != null) result = _previous.ShouldPreserveAttribute(namespaceName, attributeName); return result; }*/ public void Verify() { // Check process content if (_processContents != null) { foreach (string key in _processContents.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSProcessContentNotIgnorable), key); } } } // Check preserve elements if (_preserveElements != null) { foreach (string key in _preserveElements.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSPreserveNotIgnorable), key); } } } // Check preserve attributes if (_preserveAttributes != null) { foreach (string key in _preserveAttributes.Keys) { if (!IsIgnorableAtCurrentScope(key)) { _reader.Error(SR.Get(SRID.XCRNSPreserveNotIgnorable), key); } } } } } class ProcessContentSet { bool _all; string _namespaceName; XmlCompatibilityReader _reader; Dictionary _names; public ProcessContentSet(string namespaceName, XmlCompatibilityReader reader) { _namespaceName = namespaceName; _reader = reader; } public bool ShouldProcessContent(string elementName) { return _all || (_names != null && _names.ContainsKey(elementName)); } public void Add(string elementName) { if (ShouldProcessContent(elementName)) { if (elementName == "*") { _reader.Error(SR.Get(SRID.XCRDuplicateWildcardProcessContent), _namespaceName); } else { _reader.Error(SR.Get(SRID.XCRDuplicateProcessContent), _namespaceName, elementName); } } if (elementName == "*") { if (_names != null) { _reader.Error(SR.Get(SRID.XCRInvalidProcessContent), _namespaceName); } else { _all = true; } } else { if (_names == null) { _names = new Dictionary (); } _names[elementName] = null; // we don't care about value, just key } } } class PreserveItemSet { bool _all; string _namespaceName; XmlCompatibilityReader _reader; Dictionary _names; public PreserveItemSet(string namespaceName, XmlCompatibilityReader reader) { _namespaceName = namespaceName; _reader = reader; } public bool ShouldPreserveItem(string itemName) { return _all || (_names != null && _names.ContainsKey(itemName)); } public void Add(string itemName) { if (ShouldPreserveItem(itemName)) { if (itemName == "*") { _reader.Error(SR.Get(SRID.XCRDuplicateWildcardPreserve), _namespaceName); } else { _reader.Error(SR.Get(SRID.XCRDuplicatePreserve), itemName, _namespaceName); } } if (itemName == "*") { if (_names != null) { _reader.Error(SR.Get(SRID.XCRInvalidPreserve), _namespaceName); } else { _all = true; } } else { if (_names == null) { _names = new Dictionary (); } _names.Add(itemName, itemName); } } } #endregion Nested Classes #region Private Fields private bool _inAttribute; // for Save/Restore ReaderPosition private string _currentName; // for Save/Restore ReaderPosition private IsXmlNamespaceSupportedCallback _namespaceCallback; private Dictionary _knownNamespaces = new Dictionary (); private Dictionary _namespaceMap = new Dictionary (); private Dictionary _subsumingNamespaces = new Dictionary (); private Dictionary _elementHandler = new Dictionary (); private Dictionary _attributeHandler = new Dictionary (); private int _depthOffset; // offset for Depth method, to account for elements that should be ignored by client private int _ignoredAttributeCount; private int _attributePosition; // used for ScanForCompatibility / HandleIgnorable private string _compatibilityUri; private string _alternateContent; private string _choice; private string _fallback; private string _requires; private string _ignorable; private string _mustUnderstand; private string _processContent; private string _preserveElements; private string _preserveAttributes; private CompatibilityScope _compatibilityScope; private const string XmlnsDeclaration = "xmlns"; private const string MarkupCompatibilityURI = "http://schemas.openxmlformats.org/markup-compatibility/2006"; static private string [] _predefinedNamespaces = new string [4] { "http://www.w3.org/2000/xmlns/", "http://www.w3.org/XML/1998/namespace", "http://www.w3.org/2001/XMLSchema-instance", MarkupCompatibilityURI }; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ScriptReference.cs
- GenericUriParser.cs
- _FtpControlStream.cs
- Frame.cs
- SynchronizedInputProviderWrapper.cs
- UrlPath.cs
- ClientSession.cs
- MembershipPasswordException.cs
- PieceDirectory.cs
- EdmSchemaError.cs
- AttachedAnnotationChangedEventArgs.cs
- QueryContinueDragEventArgs.cs
- NotifyInputEventArgs.cs
- XmlIterators.cs
- DataGridViewIntLinkedList.cs
- CasesDictionary.cs
- ChtmlTextWriter.cs
- ForceCopyBuildProvider.cs
- FormViewUpdateEventArgs.cs
- XmlSchemaAny.cs
- RectValueSerializer.cs
- EntityDataReader.cs
- DateTimePicker.cs
- DataTemplateSelector.cs
- BuilderInfo.cs
- DesignerHost.cs
- GenericAuthenticationEventArgs.cs
- XslCompiledTransform.cs
- updateconfighost.cs
- ArrangedElementCollection.cs
- ActiveXMessageFormatter.cs
- ModelItemCollection.cs
- DPCustomTypeDescriptor.cs
- SettingsSavedEventArgs.cs
- Decimal.cs
- RelatedCurrencyManager.cs
- SelectionChangedEventArgs.cs
- HuffmanTree.cs
- CorruptingExceptionCommon.cs
- AsyncPostBackErrorEventArgs.cs
- DataTableClearEvent.cs
- WebPartExportVerb.cs
- LogicalTreeHelper.cs
- ConfigUtil.cs
- AutomationTextAttribute.cs
- DropDownButton.cs
- BlurBitmapEffect.cs
- NativeMethods.cs
- DriveInfo.cs
- ThreadSafeList.cs
- DataSvcMapFileSerializer.cs
- WindowsFormsHostAutomationPeer.cs
- ContainerVisual.cs
- QueryOpeningEnumerator.cs
- Stackframe.cs
- DSACryptoServiceProvider.cs
- MimeFormImporter.cs
- ExitEventArgs.cs
- AppSettingsExpressionBuilder.cs
- ChineseLunisolarCalendar.cs
- FragmentNavigationEventArgs.cs
- Symbol.cs
- PointLightBase.cs
- GenericArgumentsUpdater.cs
- SerializationInfoEnumerator.cs
- LinqDataSourceContextEventArgs.cs
- WsdlImporterElement.cs
- EntityDataSourceMemberPath.cs
- Block.cs
- WmlLabelAdapter.cs
- GridViewEditEventArgs.cs
- RijndaelManaged.cs
- GiveFeedbackEvent.cs
- EDesignUtil.cs
- AutoResizedEvent.cs
- FontInfo.cs
- GotoExpression.cs
- XPathEmptyIterator.cs
- HostingEnvironmentException.cs
- Stopwatch.cs
- CqlIdentifiers.cs
- TransactionContext.cs
- Automation.cs
- IntSecurity.cs
- PageThemeBuildProvider.cs
- DataGridViewAccessibleObject.cs
- TransformPatternIdentifiers.cs
- shaperfactoryquerycachekey.cs
- WhereQueryOperator.cs
- CreateUserErrorEventArgs.cs
- BlockCollection.cs
- ObjectDataSourceDisposingEventArgs.cs
- FixedSOMImage.cs
- DragEvent.cs
- SQLInt64Storage.cs
- OutputCacheProfileCollection.cs
- AccessDataSourceView.cs
- XLinq.cs
- ModifierKeysConverter.cs
- Query.cs