//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] //------------------------------------------------------------------------------ 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. // all prefixes should be invalidated // all prefixes should be invalidated // prefixe "foo" should be invalidated // prefixe "foo" should be invalidated // no invalidations reqired // all prefixes should be invalidated but not default "" // all prefixes should be invalidated but not default "" // 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. // // all prefixes are invalidated for the current element scope // no invalidations needed // 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; } } }