File: OutputScopeManager.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (139 lines) | stat: -rw-r--r-- 6,264 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//------------------------------------------------------------------------------
// <copyright file="OutputScopeManager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Xml;
using System.Collections;

namespace System.Xml.Xsl.Xslt {
    internal class OutputScopeManager {
        public struct ScopeReord {
            public int        scopeCount;
            public string     prefix;
            public string     nsUri;
        }
        ScopeReord[] records = new ScopeReord[32];
        int lastRecord = 0;
        int lastScopes = 0;  // This is cash of records[lastRecord].scopeCount field;
                             // most often we will have PushScope()/PopScope pare over the same record.
                             // It has sence to avoid adresing this field through array access.

        public OutputScopeManager() {
            Reset();
        }

        public void Reset() {
//          AddNamespace(null, null);                  --  lookup barier
            records[0].prefix = null;
            records[0].nsUri  = null;
            PushScope();
        }

        public void PushScope() {
            lastScopes ++;
        }

        public void PopScope() {
            if (0 < lastScopes) {
                lastScopes --;
            }
            else {
                while(records[-- lastRecord].scopeCount == 0) ;
                lastScopes = records[lastRecord].scopeCount;
                lastScopes --;
            }
        }

        // This can be ns declaration or ns exclussion. Las one when prefix == null;
        public void AddNamespace(string prefix, string uri) {
            Debug.Assert(prefix != null);
            Debug.Assert(uri    != null);
//            uri = nameTable.Add(uri);
            AddRecord(prefix, uri);
        }

        private void AddRecord(string prefix, string uri) {
            records[lastRecord].scopeCount = lastScopes;
            lastRecord ++;
            if (lastRecord == records.Length) {
                ScopeReord[] newRecords = new ScopeReord[lastRecord * 2];
                Array.Copy(records, 0, newRecords, 0, lastRecord);
                records = newRecords;
            }
            lastScopes = 0;
            records[lastRecord].prefix = prefix;
            records[lastRecord].nsUri  = uri;
        }
        
        // There are some cases where we can't predict namespace content. To garantee correct results we should output all 
        // literal namespaces once again. 
        // <xsl:element name="{}" namespace="{}"> all prefixes should be invalidated
        // <xsl:element name="{}" namespace="FOO"> all prefixes should be invalidated
        // <xsl:element name="foo:A" namespace="{}"> prefixe "foo" should be invalidated
        // <xsl:element name="foo:{}" namespace="{}"> prefixe "foo" should be invalidated
        // <xsl:element name="foo:A" namespace="FOO"> no invalidations reqired
        // <xsl:attribute name="{}" namespace="FOO"> all prefixes should be invalidated but not default ""
        // <xsl:attribute name="foo:A" namespace="{}"> all prefixes should be invalidated but not default ""
        // <xsl:element name="foo:A" namespace="FOO"> We can try to invalidate only foo prefix, but there to many thinks to consider here.
        //                                            So for now if attribute has non-null namespace it invalidates all prefixes in the
        //                                            scope of its element.
        //
        // <xsl:copy-of select="@*|namespace::*"> all prefixes are invalidated for the current element scope
        // <xsl:copy-of select="/|*|text()|etc."> no invalidations needed
        // <xsl:copy> if the node is either attribute or namespace, all prefixes are invalidated for the current element scope
        //            if the node is element, new scope is created, and all prefixes are invalidated
        //            otherwise, no invalidations needed

        //// We need following methods:
        //public void InvalidatePrefix(string prefix) {
        //    Debug.Assert(prefix != null);
        //    if (LookupNamespace(prefix) == null) { // This is optimisation. May be better just add this record?
        //        return;                            
        //    }
        //    AddRecord(prefix, null);
        //}
        
        public void InvalidateAllPrefixes() {
            if (records[lastRecord].prefix == null) {
                return;                            // Averything was invalidated already. Nothing to do.
            }
            AddRecord(null, null);            
        }
        
        public void InvalidateNonDefaultPrefixes() {
            string defaultNs = LookupNamespace(string.Empty);
            if (defaultNs == null) {             // We don't know default NS anyway.
                InvalidateAllPrefixes();
            }
            else {
                if (
                    records[lastRecord    ].prefix.Length == 0 &&
                    records[lastRecord - 1].prefix == null
                ) {
                    return;                       // Averything was already done
                }
                AddRecord(null, null);
                AddRecord(string.Empty, defaultNs);
            }
        }

        public string LookupNamespace(string prefix) {
            Debug.Assert(prefix != null);
            for (
                int record = lastRecord;              // from last record 
                 records[record].prefix != null;      // till lookup barrier
                -- record                             // in reverce direction
            ) {
                Debug.Assert(0 < record, "first record is lookup bariaer, so we don't need to check this condition runtime");
                if (records[record].prefix == prefix) {
                    return records[record].nsUri;
                }
            }
            return null;
        }
    }
}