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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="CompilerScopeManager.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.Collections;
  16. using System.Diagnostics;
  17. using System.Xml.Xsl.Qil;
  18. namespace System.Xml.Xsl.Xslt
  19. {
  20.    
  21.     // Compiler scope manager keeps track of
  22.     // Variable declarations
  23.     // Namespace declarations
  24.     // Extension and excluded namespaces
  25.     internal sealed class CompilerScopeManager<V> : IEnumerable where V : class
  26.     {
  27.         public struct ScopeRecord
  28.         {
  29.             public int scopeCount;
  30.             public string ncName;
  31.             // local-name for variable, prefix for namespace, null for extension or excluded namespace
  32.             public string nsUri;
  33.             // namespace uri
  34.             public V value;
  35.             // value for variable, null for namespace
  36.             // Exactly one of these three properties is true for every given record
  37.             public bool IsVariable {
  38.                 get { return value != default(V); }
  39.             }
  40.             public bool IsNamespace {
  41.                 get { return value == default(V) && ncName != null; }
  42.             }
  43.             public bool IsExNamespace {
  44.                 get { return value == default(V) && ncName == null; }
  45.             }
  46.         }
  47.        
  48.         // Number of predefined records minus one
  49.         private const int LastPredefRecord = 0;
  50.        
  51.         private ScopeRecord[] records = new ScopeRecord[32];
  52.         private int lastRecord = 0;
  53.        
  54.         // This is cash of records[lastRecord].scopeCount field;
  55.         // most often we will have PushScope()/PopScope pare over the same record.
  56.         // It has sence to avoid adresing this field through array access.
  57.         private int lastScopes = 0;
  58.        
  59.         public CompilerScopeManager()
  60.         {
  61.             Reset();
  62.         }
  63.        
  64.         private void Reset()
  65.         {
  66.             // The prefix 'xml' is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace
  67.             records[0].ncName = "xml";
  68.             records[0].nsUri = XmlReservedNs.NsXml;
  69.             lastRecord = LastPredefRecord;
  70.         }
  71.        
  72.         public void PushScope()
  73.         {
  74.             lastScopes++;
  75.         }
  76.        
  77.         public void PopScope()
  78.         {
  79.             if (0 < lastScopes) {
  80.                 lastScopes--;
  81.             }
  82.             else {
  83.                 while (records[--lastRecord].scopeCount == 0) {
  84.                 }
  85.                 lastScopes = records[lastRecord].scopeCount;
  86.                 lastScopes--;
  87.             }
  88.         }
  89.        
  90.         private void AddRecord(string ncName, string uri, V value)
  91.         {
  92.             Debug.Assert(uri != null);
  93.            
  94.             records[lastRecord].scopeCount = lastScopes;
  95.             if (++lastRecord == records.Length) {
  96.                 ScopeRecord[] newRecords = new ScopeRecord[lastRecord * 2];
  97.                 Array.Copy(records, 0, newRecords, 0, lastRecord);
  98.                 records = newRecords;
  99.             }
  100.             lastScopes = 0;
  101.            
  102.             records[lastRecord].ncName = ncName;
  103.             records[lastRecord].nsUri = uri;
  104.             records[lastRecord].value = value;
  105.         }
  106.        
  107.         // Add namespace declaration (prefix != null) or namespace extension or exclusion (prefix == null) to the current scope.
  108.         public void AddNamespace(string prefix, string uri)
  109.         {
  110.             AddRecord(prefix, uri, default(V));
  111.         }
  112.        
  113.         // Add variable to the current scope. Returns false in case of duplicates.
  114.         public void AddVariable(QilName varName, V value)
  115.         {
  116.             Debug.Assert(varName.LocalName != null && varName.NamespaceUri != null && value != default(V));
  117.             AddRecord(varName.LocalName, varName.NamespaceUri, value);
  118.         }
  119.        
  120.         // Since the prefix might be redefined in an inner scope, we search in descending order in [to, from]
  121.         // If interval is empty (from < to), the function returns null.
  122.         private string LookupNamespace(string prefix, int from, int to)
  123.         {
  124.             Debug.Assert(prefix != null);
  125.             for (int record = from; to <= record; --record) {
  126.                 if (records[record].IsNamespace && records[record].ncName == prefix) {
  127.                     return records[record].nsUri;
  128.                 }
  129.             }
  130.             return null;
  131.         }
  132.        
  133.         public string LookupNamespace(string prefix)
  134.         {
  135.             return LookupNamespace(prefix, lastRecord, 0);
  136.         }
  137.        
  138.         public bool IsExNamespace(string nsUri)
  139.         {
  140.             Debug.Assert(nsUri != null);
  141.             for (int record = lastRecord; 0 <= record; record--) {
  142.                 if (records[record].IsExNamespace && records[record].nsUri == nsUri) {
  143.                     return true;
  144.                 }
  145.             }
  146.             return false;
  147.         }
  148.        
  149.         private int SearchVariable(string localName, string uri)
  150.         {
  151.             Debug.Assert(localName != null);
  152.             for (int record = lastRecord; 0 <= record; --record) {
  153.                 if (records[record].IsVariable && records[record].ncName == localName && records[record].nsUri == uri) {
  154.                     return record;
  155.                 }
  156.             }
  157.             return -1;
  158.         }
  159.        
  160.         public V LookupVariable(string localName, string uri)
  161.         {
  162.             int record = SearchVariable(localName, uri);
  163.             return (record < 0) ? default(V) : records[record].value;
  164.         }
  165.        
  166.         public bool IsLocalVariable(string localName, string uri)
  167.         {
  168.             int record = SearchVariable(localName, uri);
  169.             while (0 <= --record) {
  170.                 if (records[record].scopeCount != 0) {
  171.                     return true;
  172.                 }
  173.             }
  174.             return false;
  175.         }
  176.        
  177.         IEnumerator IEnumerable.GetEnumerator()
  178.         {
  179.             return new NamespaceEnumerator(this);
  180.         }
  181.        
  182.         // Class for enumerating namespaces that must be output with literal result element.
  183.         private sealed class NamespaceEnumerator : IEnumerator
  184.         {
  185.             CompilerScopeManager<V> scope;
  186.             int lastRecord;
  187.             int currentRecord;
  188.            
  189.             public NamespaceEnumerator(CompilerScopeManager<V> scope)
  190.             {
  191.                 this.scope = scope;
  192.                 this.lastRecord = scope.lastRecord;
  193.                 Reset();
  194.             }
  195.            
  196.             public void Reset()
  197.             {
  198.                 currentRecord = lastRecord + 1;
  199.             }
  200.            
  201.             public bool MoveNext()
  202.             {
  203.                 while (LastPredefRecord < --currentRecord) {
  204.                     if (scope.records[currentRecord].IsNamespace) {
  205.                         // This is a namespace declaration
  206.                         if (scope.LookupNamespace(scope.records[currentRecord].ncName, lastRecord, currentRecord + 1) == null) {
  207.                             // Its prefix has not been redefined later in [currentRecord + 1, lastRecord]
  208.                             return true;
  209.                         }
  210.                     }
  211.                 }
  212.                 return false;
  213.             }
  214.            
  215.             public object Current {
  216.                 get {
  217.                     Debug.Assert(LastPredefRecord <= currentRecord && currentRecord <= scope.lastRecord, "MoveNext() either was not called or returned false");
  218.                     Debug.Assert(scope.records[currentRecord].IsNamespace);
  219.                     return scope.records[currentRecord];
  220.                 }
  221.             }
  222.         }
  223.     }
  224. }

Developer Fusion