The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Xslt \ ScopeFlags

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsltInput.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System.Diagnostics;
  16. using System.Text;
  17. using System.Xml.XPath;
  18. namespace System.Xml.Xsl.Xslt
  19. {
  20.     using Res = System.Xml.Utils.Res;
  21.    
  22.     // This internal class is designed to help XSLT parser with reading stylesheet. We should be able to load SS from
  23.     // XmlReader, URL. The simples way to load from URL and XmlReader is to load xml to XPathDocument and
  24.     // use navigator over XPathDocument. To avoid intermediate storage XsltInput can work directly from reader and limits
  25.     // navigation over SS nodes to XmlReader like MoveTo* functions.
  26.     // document("") function in the case of XslInput(XmlReader) will read SS again. To be able to "reread" SS
  27.     // we should be garanteed that Xml.Reader.BaseUri != "".
  28.    
  29.     // a) Forward only, one pass.
  30.     // b) You should call MoveToFirstChildren on nonempty element node. (or may be skip)
  31.     // c) You should call MoveToParent when no more children is available.
  32.    
  33.     internal class XsltInput : IErrorHelper
  34.     {
  35.         private XmlReader reader;
  36.         private IXmlLineInfo readerLineInfo;
  37.         private bool topLevelReader;
  38.         private InputScopeManager scopeManager;
  39.         private KeywordsTable atoms;
  40.         private Compiler compiler;
  41.        
  42.         private string text;
  43.         // When text != null reader stays after all merged text nodes
  44.         private bool textIsWhite;
  45.         // true if all parts were WS or SWS
  46.         // Cached properties. MoveTo* functions set them.
  47.         private XPathNodeType nodeType;
  48.         private string localName;
  49.         private string namespaceName;
  50.         private string value;
  51.        
  52.         public XsltInput(XmlReader reader, Compiler compiler)
  53.         {
  54.             Debug.Assert(reader != null);
  55.             EnsureExpandEntities(reader);
  56.             IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo;
  57.            
  58.             this.reader = reader;
  59.             this.readerLineInfo = (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) ? xmlLineInfo : null;
  60.             this.topLevelReader = reader.ReadState == ReadState.Initial;
  61.             this.scopeManager = new InputScopeManager(reader.NameTable);
  62.             this.atoms = new KeywordsTable(reader.NameTable);
  63.             this.compiler = compiler;
  64.             this.textIsWhite = true;
  65.             this.nodeType = XPathNodeType.Root;
  66.         }
  67.        
  68.         private static void EnsureExpandEntities(XmlReader reader)
  69.         {
  70.             XmlTextReader tr = reader as XmlTextReader;
  71.             if (tr != null && tr.EntityHandling != EntityHandling.ExpandEntities) {
  72.                 Debug.Assert(tr.Settings == null, "XmlReader created with XmlReader.Create should always expand entities.");
  73.                 tr.EntityHandling = EntityHandling.ExpandEntities;
  74.             }
  75.         }
  76.        
  77.         public bool Start()
  78.         {
  79.             if (topLevelReader) {
  80.                 return MoveToNextSibling();
  81.             }
  82.             else {
  83.                 if (reader.ReadState != ReadState.Interactive) {
  84.                     return false;
  85.                 }
  86.                 StepOnNodeRdr();
  87.                 if (nodeType == XPathNodeType.Comment || nodeType == XPathNodeType.ProcessingInstruction) {
  88.                     return MoveToNextSibling();
  89.                 }
  90.                 return nodeType == XPathNodeType.Element;
  91.             }
  92.         }
  93.        
  94.         public void Finish()
  95.         {
  96.             scopeManager.CheckEmpty();
  97.            
  98.             // We may need to fix line info for the last end-tag
  99.             reader.Read();
  100.             FixLastLineInfo();
  101.            
  102.             if (topLevelReader) {
  103.                 while (reader.ReadState == ReadState.Interactive) {
  104.                     reader.Skip();
  105.                 }
  106.             }
  107.         }
  108.        
  109.         private void SetCachedProperties()
  110.         {
  111.             nodeType = ConvertNodeType(reader.NodeType);
  112.             localName = reader.LocalName;
  113.             namespaceName = reader.NamespaceURI;
  114.             value = reader.Value;
  115.            
  116.             if (nodeType == XPathNodeType.Attribute && IsNamespace(reader)) {
  117.                 nodeType = XPathNodeType.Namespace;
  118.                 namespaceName = string.Empty;
  119.                 if (localName == "xmlns") {
  120.                     localName = string.Empty;
  121.                 }
  122.             }
  123.         }
  124.        
  125.         public KeywordsTable Atoms {
  126.             get { return atoms; }
  127.         }
  128.        
  129.         // Cached properties
  130.         public XPathNodeType NodeType {
  131.             get { return nodeType; }
  132.         }
  133.         public string Value {
  134.             get { return value; }
  135.         }
  136.         public string LocalName {
  137.             get { return localName; }
  138.         }
  139.         public string NamespaceUri {
  140.             get { return namespaceName; }
  141.         }
  142.        
  143.         // Non-cached properties
  144.         public string Prefix {
  145.             get { return reader.Prefix; }
  146.         }
  147.         public string BaseUri {
  148.             get { return reader.BaseURI; }
  149.         }
  150.         public string QualifiedName {
  151.             get { return reader.Name; }
  152.         }
  153.         public bool IsEmptyElement {
  154.             get { return reader.IsEmptyElement; }
  155.         }
  156.        
  157.         // Resolve prefix, return null and report an error if not found
  158.         public string LookupXmlNamespace(string prefix)
  159.         {
  160.             Debug.Assert(prefix != null);
  161.             string nsUri = reader.LookupNamespace(prefix);
  162.             if (nsUri != null) {
  163.                 return nsUri;
  164.             }
  165.             if (prefix.Length == 0) {
  166.                 return string.Empty;
  167.             }
  168.                 /*[XT0280]*/            ReportError(Res.Xslt_InvalidPrefix, prefix);
  169.             return null;
  170.         }
  171.        
  172.         public bool MoveToNextAttOrNs()
  173.         {
  174.             Debug.Assert(NodeType == XPathNodeType.Element || NodeType == XPathNodeType.Attribute || NodeType == XPathNodeType.Namespace);
  175.            
  176.             if (NodeType == XPathNodeType.Element) {
  177.                 if (!reader.MoveToFirstAttribute()) {
  178.                     reader.MoveToElement();
  179.                     return false;
  180.                 }
  181.             }
  182.             else {
  183.                 if (!reader.MoveToNextAttribute()) {
  184.                     reader.MoveToElement();
  185.                     nodeType = XPathNodeType.Element;
  186.                     return false;
  187.                 }
  188.             }
  189.             SetCachedProperties();
  190.             return true;
  191.         }
  192.        
  193.         // Debug subsystem
  194.         private enum Moves
  195.         {
  196.             Next,
  197.             Child,
  198.             Parent
  199.         }
  200.        
  201.         private Moves lastMove = Moves.Child;
  202.         private bool lastResult = true;
  203.        
  204.         [Conditional("DEBUG")]
  205.         private void SetLastMove(Moves lastMove, bool lastResult)
  206.         {
  207.             this.lastMove = lastMove;
  208.             this.lastResult = lastResult;
  209.         }
  210.         // --------------------
  211.        
  212.         private void StepOnNodeRdr()
  213.         {
  214.             if (text == null) {
  215.                 SetCachedProperties();
  216.             }
  217.             else {
  218.                 value = text;
  219.                 localName = string.Empty;
  220.                 namespaceName = string.Empty;
  221.                     /*default:    */                nodeType = (!textIsWhite ? XPathNodeType.Text : textPreserveWS ? XPathNodeType.SignificantWhitespace : XPathNodeType.Whitespace);
  222.             }
  223.             if (NodeType == XPathNodeType.Element) {
  224.                 scopeManager.PushScope();
  225.             }
  226.         }
  227.        
  228.         private void StepOffNode()
  229.         {
  230.             if (NodeType == XPathNodeType.Element) {
  231.                 scopeManager.PopScope();
  232.             }
  233.         }
  234.        
  235.         private bool MoveToFirstChildAny()
  236.         {
  237.             if (!reader.IsEmptyElement) {
  238.                 return ReadNextSibling();
  239.             }
  240.             nodeType = XPathNodeType.Element;
  241.             return false;
  242.         }
  243.        
  244.         public bool MoveToFirstChild()
  245.         {
  246.             Debug.Assert(lastResult, "The previous move must be successful");
  247.             Debug.Assert(lastMove != Moves.Parent, "MoveToNextSibling() must be called after MoveToParent()");
  248.             bool result = MoveToFirstChildAny();
  249.             if (result) {
  250.                 // Pass comments and PIs
  251.                 if (NodeType == XPathNodeType.Comment || NodeType == XPathNodeType.ProcessingInstruction) {
  252.                     result = MoveToNextSibling();
  253.                     if (!result) {
  254.                         MoveToParent();
  255.                     }
  256.                 }
  257.             }
  258.             SetLastMove(Moves.Child, result);
  259.             return result;
  260.         }
  261.        
  262.         public bool MoveToNextSibling()
  263.         {
  264.             #if DEBUG
  265.             if (lastMove == Moves.Next) {
  266.                 Debug.Assert(lastResult, "MoveToParent() must be called after MoveToNextSibling() failed");
  267.                 Debug.Assert(NodeType != XPathNodeType.Element, "MoveToFirstChild() was not called on the element. Did you miss SkipNode()?");
  268.             }
  269.             #endif
  270.             bool result;
  271.             do {
  272.                 StepOffNode();
  273.                 result = ReadNextSibling();
  274.             }
  275.             while (result && (NodeType == XPathNodeType.Comment || NodeType == XPathNodeType.ProcessingInstruction));
  276.             SetLastMove(Moves.Next, result);
  277.             return result;
  278.         }
  279.        
  280.         public bool MoveToParent()
  281.         {
  282.             Debug.Assert(lastMove == Moves.Next && !lastResult, "All siblings must be visited before moving to parent");
  283.             // We shouldn't call StepOffNode() here because we already left last node.
  284.             bool result = true;
  285.             // Frankly, we should return false for the root
  286.             SetLastMove(Moves.Parent, result);
  287.             return result;
  288.         }
  289.        
  290.         public void SkipNode()
  291.         {
  292.             if (NodeType == XPathNodeType.Element && MoveToFirstChild()) {
  293.                 do {
  294.                     SkipNode();
  295.                 }
  296.                 while (MoveToNextSibling());
  297.                 MoveToParent();
  298.             }
  299.         }
  300.        
  301.         private bool ReadNextSiblingHelper()
  302.         {
  303.             if (text != null) {
  304.                 text = null;
  305.                 textIsWhite = true;
  306.                 return reader.NodeType != XmlNodeType.EndElement;
  307.             }
  308.             else {
  309.                 while (reader.Read()) {
  310.                     switch (reader.NodeType) {
  311.                         case XmlNodeType.Text:
  312.                             textIsWhite = false;
  313.                             goto case XmlNodeType.SignificantWhitespace;
  314.                             break;
  315.                         case XmlNodeType.CDATA:
  316.                             if (textIsWhite && !IsWhitespace(reader.Value)) {
  317.                                 textIsWhite = false;
  318.                             }
  319.                             goto case XmlNodeType.SignificantWhitespace;
  320.                             break;
  321.                         case XmlNodeType.Whitespace:
  322.                         case XmlNodeType.SignificantWhitespace:
  323.                             if (reader.Depth == 0 && text == null && textIsWhite) {
  324.                                 break;
  325.                                 // Ignore whitespace nodes on the root level
  326.                             }
  327.                             if (text == null) {
  328.                                 // First chunk of a text node
  329.                                 SaveTextInfo();
  330.                             }
  331.                             text += reader.Value;
  332.                             break;
  333.                         case XmlNodeType.EntityReference:
  334.                             Debug.Assert(false, "Entity references should be resolved for us");
  335.                             break;
  336.                         case XmlNodeType.DocumentType:
  337.                         case XmlNodeType.XmlDeclaration:
  338.                             break;
  339.                         default:
  340.                             if (text != null) {
  341.                                 return true;
  342.                             }
  343.                             return reader.NodeType != XmlNodeType.EndElement;
  344.                     }
  345.                 }
  346.                 return text != null;
  347.             }
  348.         }
  349.        
  350.         private bool ReadNextSibling()
  351.         {
  352.             bool result = ReadNextSiblingHelper();
  353.             FixLastLineInfo();
  354.             if (result) {
  355.                 StepOnNodeRdr();
  356.                 return true;
  357.             }
  358.             nodeType = XPathNodeType.Element;
  359.             return false;
  360.         }
  361.        
  362.         private static bool IsNamespace(XmlReader reader)
  363.         {
  364.             Debug.Assert(reader.NodeType == XmlNodeType.Attribute);
  365.             return reader.Prefix == "xmlns" || reader.Prefix.Length == 0 && reader.LocalName == "xmlns";
  366.         }
  367.        
  368.         private static bool IsWhitespace(string text)
  369.         {
  370.             return XmlCharType.Instance.IsOnlyWhitespace(text);
  371.         }
  372.        
  373.         private static XPathNodeType[] XmlNodeType2XPathNodeType = new XPathNodeType[] {(XPathNodeType)(-1), XPathNodeType.Element, XPathNodeType.Attribute, XPathNodeType.Text, XPathNodeType.Text, (XPathNodeType)(-1), (XPathNodeType)(-1), XPathNodeType.ProcessingInstruction, XPathNodeType.Comment, (XPathNodeType)(-1),
  374.             // 0 = None,
  375.             // 1 = Element,s
  376.             // 2 = Attribute,
  377.             // 3 = Text,
  378.             // 4 = CDATA,
  379.             // 5 = EntityReference,
  380.             // 6 = Entity,
  381.             // 7 = ProcessingInstruction,
  382.             // 8 = Comment,
  383.             // 9 = Document,
  384.             // 10 = DocumentType,
  385.             // 11 = DocumentFragment,
  386.             // 12 = Notation,
  387.             // 13 = Whitespace,
  388.             // 14 = SignificantWhitespace,
  389.             // 15 = EndElement,
  390.             // 16 = EndEntity,
  391.             // 17 = XmlDeclaration
  392.         (XPathNodeType)(-1), (XPathNodeType)(-1), (XPathNodeType)(-1), XPathNodeType.Whitespace, XPathNodeType.SignificantWhitespace, XPathNodeType.Element, (XPathNodeType)(-1), (XPathNodeType)(-1)};
  393.        
  394.         private static XPathNodeType ConvertNodeType(XmlNodeType xmlNodeType)
  395.         {
  396.             XPathNodeType xpathNodeType = XmlNodeType2XPathNodeType[(int)xmlNodeType];
  397.            
  398.             if (xpathNodeType == (XPathNodeType)(-1)) {
  399.                 Debug.Fail("We must not step on this node");
  400.                 return XPathNodeType.All;
  401.             }
  402.             return xpathNodeType;
  403.         }
  404.        
  405.         // -------------------- Keywords testing --------------------
  406.        
  407.         public bool IsNs(string ns)
  408.         {
  409.             return Ref.Equal(ns, NamespaceUri);
  410.         }
  411.         public bool IsKeyword(string kwd)
  412.         {
  413.             return Ref.Equal(kwd, LocalName);
  414.         }
  415.         public bool IsXsltNamespace()
  416.         {
  417.             return IsNs(atoms.UriXsl);
  418.         }
  419.         public bool IsNullNamespace()
  420.         {
  421.             return IsNs(string.Empty);
  422.         }
  423.         public bool IsXsltAttribute(string kwd)
  424.         {
  425.             return IsKeyword(kwd) && IsNullNamespace();
  426.         }
  427.        
  428.         // -------------------- Scope Management --------------------
  429.         // See private class InputScopeManager bellow.
  430.         // InputScopeManager handles some flags and values with respect of scope level where they as defined.
  431.         // To parse XSLT style sheet we need the folloing values:
  432.         // ForwardCompatibility -- this flag is set when xsl:version!='1.0'.
  433.         // CanHaveApplyImports -- we allow xsl:apply-templates instruction to apear in any template with match!=null, but not inside xsl:for-each
  434.         // so it can't be inside global variable and has initial value = false
  435.         // ExtentionNamespace -- is defined by extension-element-prefixes attribute on LRE or xsl:stylesheet
  436.        
  437.         public bool CanHaveApplyImports {
  438.             get { return scopeManager.CanHaveApplyImports; }
  439.             set { scopeManager.CanHaveApplyImports = value; }
  440.         }
  441.        
  442.         public void AddExtensionNamespace(string uri)
  443.         {
  444.             scopeManager.AddExtensionNamespace(uri);
  445.         }
  446.         public bool IsExtensionNamespace(string uri)
  447.         {
  448.             return scopeManager.IsExtensionNamespace(uri);
  449.         }
  450.        
  451.         public bool ForwardCompatibility {
  452.             get { return scopeManager.ForwardCompatibility; }
  453.         }
  454.        
  455.         public XslVersion XslVersion {
  456.             get { return scopeManager.ForwardCompatibility ? XslVersion.ForwardsCompatible : XslVersion.Current; }
  457.         }
  458.        
  459.         public void SetVersion(string version, string attName)
  460.         {
  461.             double versionNum = XPathConvert.StringToDouble(version);
  462.             if (double.IsNaN(versionNum)) {
  463.                     /*[XT0110]*/                ReportError(Res.Xslt_InvalidAttrValue, attName, version);
  464.                 versionNum = 1.0;
  465.             }
  466.             scopeManager.ForwardCompatibility = (versionNum != 1.0);
  467.         }
  468.        
  469.         // --------------- GetAtributes(...) -------------------------
  470.         // All Xslt Instructions allows fixed set of attributes in null-ns, no in XSLT-ns and any in other ns.
  471.         // In ForwardCompatibility mode we should ignore any of this problems.
  472.         // We not use these functions for parseing LiteralResultElement and xsl:stylesheet
  473.        
  474.         private string[] names = new string[10];
  475.         private string[] values = new string[10];
  476.        
  477.         public ContextInfo GetAttributes()
  478.         {
  479.             return GetAttributes(0, 0, names, values);
  480.         }
  481.        
  482.         public ContextInfo GetAttributes(int required, string name, out string value)
  483.         {
  484.             names[0] = name;
  485.             ContextInfo ctxInfo = GetAttributes(required, 1, names, values);
  486.             value = values[0];
  487.             return ctxInfo;
  488.         }
  489.        
  490.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1)
  491.         {
  492.             names[0] = name0;
  493.             names[1] = name1;
  494.             ContextInfo ctxInfo = GetAttributes(required, 2, names, values);
  495.             value0 = values[0];
  496.             value1 = values[1];
  497.             return ctxInfo;
  498.         }
  499.        
  500.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1, string name2, out string value2)
  501.         {
  502.             names[0] = name0;
  503.             names[1] = name1;
  504.             names[2] = name2;
  505.             ContextInfo ctxInfo = GetAttributes(required, 3, names, values);
  506.             value0 = values[0];
  507.             value1 = values[1];
  508.             value2 = values[2];
  509.             return ctxInfo;
  510.         }
  511.        
  512.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1, string name2, out string value2, string name3, out string value3)
  513.         {
  514.             names[0] = name0;
  515.             names[1] = name1;
  516.             names[2] = name2;
  517.             names[3] = name3;
  518.             ContextInfo ctxInfo = GetAttributes(required, 4, names, values);
  519.             value0 = values[0];
  520.             value1 = values[1];
  521.             value2 = values[2];
  522.             value3 = values[3];
  523.             return ctxInfo;
  524.         }
  525.        
  526.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1, string name2, out string value2, string name3, out string value3, string name4,
  527.         out string value4)
  528.         {
  529.             names[0] = name0;
  530.             names[1] = name1;
  531.             names[2] = name2;
  532.             names[3] = name3;
  533.             names[4] = name4;
  534.             ContextInfo ctxInfo = GetAttributes(required, 5, names, values);
  535.             value0 = values[0];
  536.             value1 = values[1];
  537.             value2 = values[2];
  538.             value3 = values[3];
  539.             value4 = values[4];
  540.             return ctxInfo;
  541.         }
  542.        
  543.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1, string name2, out string value2, string name3, out string value3, string name4,
  544.         out string value4, string name5, out string value5, string name6, out string value6, string name7, out string value7, string name8, out string value8)
  545.         {
  546.             names[0] = name0;
  547.             names[1] = name1;
  548.             names[2] = name2;
  549.             names[3] = name3;
  550.             names[4] = name4;
  551.             names[5] = name5;
  552.             names[6] = name6;
  553.             names[7] = name7;
  554.             names[8] = name8;
  555.             ContextInfo ctxInfo = GetAttributes(required, 9, names, values);
  556.             value0 = values[0];
  557.             value1 = values[1];
  558.             value2 = values[2];
  559.             value3 = values[3];
  560.             value4 = values[4];
  561.             value5 = values[5];
  562.             value6 = values[6];
  563.             value7 = values[7];
  564.             value8 = values[8];
  565.             return ctxInfo;
  566.         }
  567.        
  568.         public ContextInfo GetAttributes(int required, string name0, out string value0, string name1, out string value1, string name2, out string value2, string name3, out string value3, string name4,
  569.         out string value4, string name5, out string value5, string name6, out string value6, string name7, out string value7, string name8, out string value8, string name9,
  570.         out string value9)
  571.         {
  572.             names[0] = name0;
  573.             names[1] = name1;
  574.             names[2] = name2;
  575.             names[3] = name3;
  576.             names[4] = name4;
  577.             names[5] = name5;
  578.             names[6] = name6;
  579.             names[7] = name7;
  580.             names[8] = name8;
  581.             names[9] = name9;
  582.             ContextInfo ctxInfo = GetAttributes(required, 10, names, values);
  583.             value0 = values[0];
  584.             value1 = values[1];
  585.             value2 = values[2];
  586.             value3 = values[3];
  587.             value4 = values[4];
  588.             value5 = values[5];
  589.             value6 = values[6];
  590.             value7 = values[7];
  591.             value8 = values[8];
  592.             value9 = values[9];
  593.             return ctxInfo;
  594.         }
  595.        
  596.         public ContextInfo GetAttributes(int required, int number, string[] names, string[] values)
  597.         {
  598.             Debug.Assert(reader.NodeType == XmlNodeType.Element);
  599.             for (int i = 0; i < number; i++) {
  600.                 values[i] = null;
  601.             }
  602.             string elementName = QualifiedName;
  603.             ContextInfo ctxInfo = new ContextInfo(this);
  604.             compiler.EnterForwardsCompatible();
  605.            
  606.             while (MoveToNextAttOrNs()) {
  607.                 if (nodeType == XPathNodeType.Namespace) {
  608.                     ctxInfo.AddNamespace(this);
  609.                     continue;
  610.                 }
  611.                 Debug.Assert(nodeType == XPathNodeType.Attribute);
  612.                 ctxInfo.AddAttribute(this);
  613.                 bool found = false;
  614.                 for (int i = 0; i < number; i++) {
  615.                     if (IsXsltAttribute(names[i])) {
  616.                         found = true;
  617.                         values[i] = Value;
  618.                        
  619.                         // There are only two XSL elements, xsl:stylesheet/xsl:transform and xsl:output, capable
  620.                         // of having 'version' attribute. And only for the first one this attribute is required.
  621.                         if (Ref.Equal(names[i], Atoms.Version) && i < required) {
  622.                             SetVersion(Value, Atoms.Version);
  623.                         }
  624.                         break;
  625.                     }
  626.                 }
  627.                 if (!found) {
  628.                     // An element from the XSLT namespace may have any attribute not from the XSLT namespace,
  629.                     // provided that the expanded-name of the attribute has a non-null namespace URI.
  630.                     // For example, it may be 'xml:space'.
  631.                     if (IsNullNamespace() || IsXsltNamespace()) {
  632.                             /*[XT0090]*/                        ReportError(Res.Xslt_InvalidAttribute, QualifiedName, elementName);
  633.                     }
  634.                 }
  635.             }
  636.            
  637.             // Ignore invalid attributes if forwards-compatible behavior is enabled. Note that invalid
  638.             // attributes may encounter before ForwardCompatibility flag is set to true. For example,
  639.             // <xsl:stylesheet unknown="foo" version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"/>
  640.             compiler.ExitForwardsCompatible(ForwardCompatibility);
  641.            
  642.             // Report missing mandatory attributes
  643.             for (int i = 0; i < required; i++) {
  644.                 if (values[i] == null) {
  645.                         /*[XT_001]*/                    ReportError(Res.Xslt_MissingAttribute, names[i]);
  646.                 }
  647.             }
  648.             ctxInfo.Finish(this);
  649.             return ctxInfo;
  650.         }
  651.        
  652.         // ----------------------- ISourceLineInfo -----------------------
  653.        
  654.         private SourceLineInfo lastLineInfo = null;
  655.        
  656.         public string Uri {
  657.             get { return reader.BaseURI; }
  658.         }
  659.        
  660.         public int StartLine {
  661.             get {
  662.                 if (readerLineInfo == null) {
  663.                     return 0;
  664.                 }
  665.                 if (OnTextNode) {
  666.                     return textStartLine;
  667.                 }
  668.                 else {
  669.                     return readerLineInfo.LineNumber;
  670.                 }
  671.             }
  672.         }
  673.        
  674.         public int StartPos {
  675.             get {
  676.                 if (readerLineInfo == null) {
  677.                     return 0;
  678.                 }
  679.                 if (OnTextNode) {
  680.                     return textStartPos;
  681.                 }
  682.                 else {
  683.                     return readerLineInfo.LinePosition - PositionAdjustment(reader.NodeType);
  684.                 }
  685.             }
  686.         }
  687.        
  688.         public int EndLine {
  689.             get {
  690.                 if (readerLineInfo == null) {
  691.                     return 0;
  692.                 }
  693.                 return readerLineInfo.LineNumber;
  694.             }
  695.         }
  696.        
  697.         public int EndPos {
  698.             get {
  699.                 if (readerLineInfo == null) {
  700.                     return 0;
  701.                 }
  702.                 int pos = readerLineInfo.LinePosition;
  703.                 if (OnTextNode) {
  704.                     return pos - PositionAdjustment(reader.NodeType);
  705.                 }
  706.                 else {
  707.                     // This value will be fixed later
  708.                     return pos + 1;
  709.                 }
  710.             }
  711.         }
  712.        
  713.         private bool OnTextNode {
  714.             get { return text != null; }
  715.         }
  716.        
  717.         private static int PositionAdjustment(XmlNodeType nt)
  718.         {
  719.             switch (nt) {
  720.                 case XmlNodeType.Element:
  721.                     return 1;
  722.                 case XmlNodeType.CDATA:
  723.                     // "<"
  724.                     return 9;
  725.                 case XmlNodeType.ProcessingInstruction:
  726.                     // "<![CDATA["
  727.                     return 2;
  728.                 case XmlNodeType.Comment:
  729.                     // "<?"
  730.                     return 4;
  731.                 case XmlNodeType.EndElement:
  732.                     // "<!--"
  733.                     return 2;
  734.                 default:
  735.                     // "</"
  736.                     return 0;
  737.             }
  738.         }
  739.        
  740.         private void SaveTextInfo()
  741.         {
  742.             Debug.Assert(!OnTextNode);
  743.             textPreserveWS = reader.XmlSpace == XmlSpace.Preserve;
  744.             textStartLine = StartLine;
  745.             textStartPos = StartPos;
  746.         }
  747.        
  748.         private bool textPreserveWS;
  749.         private int textStartLine;
  750.         private int textStartPos;
  751.        
  752.         public ISourceLineInfo BuildLineInfo()
  753.         {
  754.             bool onAttribute = (nodeType == XPathNodeType.Attribute);
  755.             if (lastLineInfo != null && !onAttribute) {
  756.                 Debug.Assert(Uri == lastLineInfo.Uri && StartLine == lastLineInfo.StartLine && StartPos == lastLineInfo.StartPos && EndLine == lastLineInfo.EndLine && EndPos == lastLineInfo.EndPos);
  757.                 return lastLineInfo;
  758.             }
  759.             SourceLineInfo lineInfo = new SourceLineInfo(Uri, StartLine, StartPos, EndLine, EndPos);
  760.             if (!OnTextNode && !onAttribute) {
  761.                 lastLineInfo = lineInfo;
  762.             }
  763.             return lineInfo;
  764.         }
  765.        
  766.         public void FixLastLineInfo()
  767.         {
  768.             if (lastLineInfo != null) {
  769.                 lastLineInfo.SetEndLinePos(StartLine, StartPos);
  770.                 lastLineInfo = null;
  771.             }
  772.         }
  773.        
  774.         // ---------------------- Error Handling ----------------------
  775.        
  776.         public void ReportError(string res, params string[] args)
  777.         {
  778.             compiler.ReportError(BuildLineInfo(), res, args);
  779.         }
  780.        
  781.         public void ReportWarning(string res, params string[] args)
  782.         {
  783.             compiler.ReportWarning(BuildLineInfo(), res, args);
  784.         }
  785.        
  786.         // ---------------------- InputScopeManager ----------------------
  787.        
  788.         private class InputScopeManager
  789.         {
  790.             private enum ScopeFlags
  791.             {
  792.                 ForwardCompatibility = 1,
  793.                 CanHaveApplyImports = 2,
  794.                 NsExtension = 4,
  795.                
  796.                 InheritedFlags = ForwardCompatibility | CanHaveApplyImports
  797.             }
  798.            
  799.             private struct ScopeRecord
  800.             {
  801.                 public int scopeCount;
  802.                 public ScopeFlags scopeFlags;
  803.                 public string nsUri;
  804.             }
  805.            
  806.             XmlNameTable nameTable;
  807.             ScopeRecord[] records = new ScopeRecord[32];
  808.             int lastRecord = 0;
  809.             int lastScopes = 0;
  810.             // This is cash of records[lastRecord].scopeCount field;
  811.             // most often we will have PushScope()/PopScope pare over the same record.
  812.             // It has sence to avoid adresing this field through array access.
  813.             public InputScopeManager(XmlNameTable nameTable)
  814.             {
  815.                 this.nameTable = nameTable;
  816.                 records[0].scopeFlags = 0;
  817.             }
  818.            
  819.             public void PushScope()
  820.             {
  821.                 lastScopes++;
  822.             }
  823.            
  824.             public void PopScope()
  825.             {
  826.                 if (0 < lastScopes) {
  827.                     lastScopes--;
  828.                 }
  829.                 else {
  830.                     do {
  831.                         lastRecord--;
  832.                     }
  833.                     while (records[lastRecord].scopeCount == 0);
  834.                     lastScopes = records[lastRecord].scopeCount;
  835.                     lastScopes--;
  836.                 }
  837.             }
  838.            
  839.             private void AddRecord()
  840.             {
  841.                 records[lastRecord].scopeCount = lastScopes;
  842.                 lastRecord++;
  843.                 if (lastRecord == records.Length) {
  844.                     ScopeRecord[] newRecords = new ScopeRecord[lastRecord * 2];
  845.                     Array.Copy(records, 0, newRecords, 0, lastRecord);
  846.                     records = newRecords;
  847.                 }
  848.                 lastScopes = 0;
  849.             }
  850.            
  851.             private void SetFlag(bool value, ScopeFlags flag)
  852.             {
  853.                 Debug.Assert(flag == (flag & ScopeFlags.InheritedFlags) && (flag & (flag - 1)) == 0 && flag != 0);
  854.                 ScopeFlags lastFlags = records[lastRecord].scopeFlags;
  855.                 if (((lastFlags & flag) != 0) != value) {
  856.                     // lastScopes == records[lastRecord].scopeCount; // we know this because we are cashing it.
  857.                     bool canReuseLastRecord = lastScopes == 0;
  858.                     // last record is from last scope
  859.                     if (!canReuseLastRecord) {
  860.                         AddRecord();
  861.                         lastFlags = lastFlags & ScopeFlags.InheritedFlags;
  862.                     }
  863.                     records[lastRecord].scopeFlags = lastFlags ^ flag;
  864.                 }
  865.             }
  866.            
  867.             public bool ForwardCompatibility {
  868.                 get { return (records[lastRecord].scopeFlags & ScopeFlags.ForwardCompatibility) != 0; }
  869.                 set { SetFlag(value, ScopeFlags.ForwardCompatibility); }
  870.             }
  871.            
  872.             public bool CanHaveApplyImports {
  873.                 get { return (records[lastRecord].scopeFlags & ScopeFlags.CanHaveApplyImports) != 0; }
  874.                 set { SetFlag(value, ScopeFlags.CanHaveApplyImports); }
  875.             }
  876.            
  877.             public void AddExtensionNamespace(string uri)
  878.             {
  879.                 Debug.Assert(uri != null);
  880.                
  881.                 uri = nameTable.Add(uri);
  882.                
  883.                 ScopeFlags lastFlags = records[lastRecord].scopeFlags;
  884.                 // lastScopes == records[lastRecord].scopeCount; // we know this because we are cashing it.
  885.                     // last record is from last scope
  886.                     // only flag fields are used in this record
  887.                 bool canReuseLastRecord = (lastScopes == 0 && (lastFlags & ScopeFlags.NsExtension) == 0);
  888.                 if (!canReuseLastRecord) {
  889.                     AddRecord();
  890.                     lastFlags = lastFlags & ScopeFlags.InheritedFlags;
  891.                 }
  892.                 records[lastRecord].nsUri = uri;
  893.                 records[lastRecord].scopeFlags = lastFlags | ScopeFlags.NsExtension;
  894.             }
  895.            
  896.             public bool IsExtensionNamespace(string nsUri)
  897.             {
  898.                 for (int record = lastRecord; 0 <= record; record--) {
  899.                     if ((records[record].scopeFlags & ScopeFlags.NsExtension) != 0 && (records[record].nsUri == nsUri)) {
  900.                         return true;
  901.                     }
  902.                 }
  903.                 return false;
  904.             }
  905.            
  906.             [Conditional("DEBUG")]
  907.             public void CheckEmpty()
  908.             {
  909.                 PopScope();
  910.                 Debug.Assert(lastRecord == 0 && lastScopes == 0, "StepOnNode() and StepOffNode() calls have been unbalanced");
  911.             }
  912.         }
  913.        
  914.         // -------------------------------- ContextInfo ------------------------------------
  915.        
  916.         internal class ContextInfo
  917.         {
  918.             public NsDecl nsList;
  919.             public ISourceLineInfo lineInfo;
  920.             // Line info for whole start tag
  921.             public ISourceLineInfo elemNameLi;
  922.             // Line info for element name
  923.             public ISourceLineInfo endTagLi;
  924.             // Line info for end tag or '/>'
  925.             private int elemNameLength;
  926.            
  927.             public ContextInfo(XsltInput input)
  928.             {
  929.                 lineInfo = input.BuildLineInfo();
  930.                 elemNameLength = input.QualifiedName.Length;
  931.             }
  932.            
  933.             public void AddAttribute(XsltInput input)
  934.             {
  935.             }
  936.            
  937.             public void AddNamespace(XsltInput input)
  938.             {
  939.                 if (Ref.Equal(input.LocalName, input.Atoms.Xml)) {
  940.                     Debug.Assert(input.Value == XmlReservedNs.NsXml, "XmlReader must check binding for 'xml' prefix");
  941.                 }
  942.                 else {
  943.                     nsList = new NsDecl(nsList, input.LocalName, input.Value);
  944.                 }
  945.             }
  946.            
  947.             public void Finish(XsltInput input)
  948.             {
  949.             }
  950.            
  951.             public void SaveExtendedLineInfo(XsltInput input)
  952.             {
  953.                     // "<"
  954.                 elemNameLi = new SourceLineInfo(lineInfo.Uri, lineInfo.StartLine, lineInfo.StartPos + 1, lineInfo.StartLine, lineInfo.StartPos + 1 + elemNameLength);
  955.                
  956.                 if (!input.IsEmptyElement) {
  957.                     Debug.Assert(input.reader.NodeType == XmlNodeType.EndElement);
  958.                     endTagLi = input.BuildLineInfo();
  959.                 }
  960.                 else {
  961.                     Debug.Assert(input.reader.NodeType == XmlNodeType.Element);
  962.                     endTagLi = new EmptyElementEndTag(lineInfo);
  963.                 }
  964.             }
  965.            
  966.             // We need this wrapper class because elementTagLi is not yet calculated
  967.             internal class EmptyElementEndTag : ISourceLineInfo
  968.             {
  969.                 private ISourceLineInfo elementTagLi;
  970.                
  971.                 public EmptyElementEndTag(ISourceLineInfo elementTagLi)
  972.                 {
  973.                     this.elementTagLi = elementTagLi;
  974.                 }
  975.                
  976.                 public string Uri {
  977.                     get { return elementTagLi.Uri; }
  978.                 }
  979.                 public int StartLine {
  980.                     get { return elementTagLi.EndLine; }
  981.                 }
  982.                 public int StartPos {
  983.                     get { return elementTagLi.EndPos - 2; }
  984.                 }
  985.                 public int EndLine {
  986.                     get { return elementTagLi.EndLine; }
  987.                 }
  988.                 public int EndPos {
  989.                     get { return elementTagLi.EndPos; }
  990.                 }
  991.                 public bool IsNoSource {
  992.                     get { return elementTagLi.IsNoSource; }
  993.                 }
  994.             }
  995.         }
  996.     }
  997. }

Developer Fusion