| File: System\Xml\Xsl\Xslt\OutputScopeManager.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Diagnostics; using System.Xml; namespace System.Xml.Xsl.Xslt { internal sealed class OutputScopeManager { public struct ScopeReord { public int scopeCount; public string? prefix; public string? nsUri; } private ScopeReord[] _records = new ScopeReord[32]; private int _lastRecord; private int _lastScopes; // Cache of records[lastRecord].scopeCount field; // most often we will have PushScope()/PopScope over the same record. // It makes sense to avoid accessing this field through the array. 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, newRecords, _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 barrier, so we don't need to check this condition runtime"); if (_records[record].prefix == prefix) { return _records[record].nsUri; } } return null; } } }