| File: System\Xml\Xsl\XsltOld\RootAction.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.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Security; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl.Runtime; using MS.Internal.Xml.XPath; namespace System.Xml.Xsl.XsltOld { internal sealed class Key { private readonly XmlQualifiedName _name; private readonly int _matchKey; private readonly int _useKey; private ArrayList? _keyNodes; public Key(XmlQualifiedName name, int matchkey, int usekey) { _name = name; _matchKey = matchkey; _useKey = usekey; _keyNodes = null; } public XmlQualifiedName Name { get { return _name; } } public int MatchKey { get { return _matchKey; } } public int UseKey { get { return _useKey; } } public void AddKey(XPathNavigator root, Hashtable table) { _keyNodes ??= new ArrayList(); _keyNodes.Add(new DocumentKeyList(root, table)); } public Hashtable? GetKeys(XPathNavigator root) { if (_keyNodes != null) { for (int i = 0; i < _keyNodes.Count; i++) { if (((DocumentKeyList)_keyNodes[i]!).RootNav.IsSamePosition(root)) { return ((DocumentKeyList)_keyNodes[i]!).KeyTable; } } } return null; } public Key Clone() { return new Key(_name, _matchKey, _useKey); } } internal readonly struct DocumentKeyList { private readonly XPathNavigator _rootNav; private readonly Hashtable _keyTable; public DocumentKeyList(XPathNavigator rootNav, Hashtable keyTable) { _rootNav = rootNav; _keyTable = keyTable; } public XPathNavigator RootNav { get { return _rootNav; } } public Hashtable KeyTable { get { return _keyTable; } } } internal sealed class RootAction : TemplateBaseAction { private const int QueryInitialized = 2; private const int RootProcessed = 3; private readonly Hashtable _attributeSetTable = new Hashtable(); private readonly Hashtable _decimalFormatTable = new Hashtable(); private List<Key>? _keyList; internal XsltOutput Output => field ??= new XsltOutput(); /* * Compile */ internal override void Compile(Compiler compiler) { CompileDocument(compiler, /*inInclude*/ false); } internal void InsertKey(XmlQualifiedName name, int MatchKey, int UseKey) { _keyList ??= new List<Key>(); _keyList.Add(new Key(name, MatchKey, UseKey)); } internal AttributeSetAction GetAttributeSet(XmlQualifiedName name) { AttributeSetAction? action = (AttributeSetAction?)_attributeSetTable[name]; if (action == null) { throw XsltException.Create(SR.Xslt_NoAttributeSet, name.ToString()); } return action; } public void PorcessAttributeSets(Stylesheet rootStylesheet) { MirgeAttributeSets(rootStylesheet); // As we mentioned we need to invert all lists. foreach (AttributeSetAction attSet in _attributeSetTable.Values) { attSet.containedActions?.Reverse(); } // ensures there are no cycles in the attribute-sets use dfs marking method CheckAttributeSets_RecurceInList(new Hashtable(), _attributeSetTable.Keys); } private void MirgeAttributeSets(Stylesheet stylesheet) { // mirge stylesheet.AttributeSetTable to this.AttributeSetTable if (stylesheet.AttributeSetTable != null) { foreach (AttributeSetAction srcAttSet in stylesheet.AttributeSetTable.Values) { ArrayList? srcAttList = srcAttSet.containedActions; AttributeSetAction? dstAttSet = (AttributeSetAction?)_attributeSetTable[srcAttSet.Name!]; if (dstAttSet == null) { dstAttSet = new AttributeSetAction(); { dstAttSet.name = srcAttSet.Name; dstAttSet.containedActions = new ArrayList(); } _attributeSetTable[srcAttSet.Name!] = dstAttSet; } ArrayList? dstAttList = dstAttSet.containedActions; // We adding attributes in reverse order for purpuse. In the mirged list most important attset should go last one // so we'll need to invert dstAttList finaly. if (srcAttList != null) { for (int src = srcAttList.Count - 1; 0 <= src; src--) { // We can ignore duplicate attributes here. dstAttList!.Add(srcAttList[src]); } } } } foreach (Stylesheet importedStylesheet in stylesheet.Imports) { MirgeAttributeSets(importedStylesheet); } } private void CheckAttributeSets_RecurceInList(Hashtable markTable, ICollection setQNames) { const string PROCESSING = "P"; const string DONE = "D"; foreach (XmlQualifiedName qname in setQNames) { object? mark = markTable[qname]; if (mark == (object)PROCESSING) { throw XsltException.Create(SR.Xslt_CircularAttributeSet, qname.ToString()); } else if (mark == (object)DONE) { continue; // optimization: we already investigated this attribute-set. } else { Debug.Assert(mark == null); markTable[qname] = (object)PROCESSING; CheckAttributeSets_RecurceInContainer(markTable, GetAttributeSet(qname)); markTable[qname] = (object)DONE; } } } private void CheckAttributeSets_RecurceInContainer(Hashtable markTable, ContainerAction container) { if (container.containedActions == null) { return; } foreach (Action action in container.containedActions) { if (action is UseAttributeSetsAction) { CheckAttributeSets_RecurceInList(markTable, ((UseAttributeSetsAction)action).UsedSets!); } else if (action is ContainerAction) { CheckAttributeSets_RecurceInContainer(markTable, (ContainerAction)action); } } } internal void AddDecimalFormat(XmlQualifiedName name, DecimalFormat formatinfo) { DecimalFormat? exist = (DecimalFormat?)_decimalFormatTable[name]; if (exist != null) { NumberFormatInfo info = exist.info; NumberFormatInfo newinfo = formatinfo.info; if (info.NumberDecimalSeparator != newinfo.NumberDecimalSeparator || info.NumberGroupSeparator != newinfo.NumberGroupSeparator || info.PositiveInfinitySymbol != newinfo.PositiveInfinitySymbol || info.NegativeSign != newinfo.NegativeSign || info.NaNSymbol != newinfo.NaNSymbol || info.PercentSymbol != newinfo.PercentSymbol || info.PerMilleSymbol != newinfo.PerMilleSymbol || exist.zeroDigit != formatinfo.zeroDigit || exist.digit != formatinfo.digit || exist.patternSeparator != formatinfo.patternSeparator ) { throw XsltException.Create(SR.Xslt_DupDecimalFormat, name.ToString()); } } _decimalFormatTable[name] = formatinfo; } internal DecimalFormat? GetDecimalFormat(XmlQualifiedName name) { return _decimalFormatTable[name] as DecimalFormat; } internal List<Key>? KeyList { get { return _keyList; } } internal override void Execute(Processor processor, ActionFrame frame) { Debug.Assert(processor != null && frame != null); switch (frame.State) { case Initialized: frame.AllocateVariables(variableCount); XPathNavigator root = processor.Document.Clone(); root.MoveToRoot(); frame.InitNodeSet(new XPathSingletonIterator(root)); if (this.containedActions != null && this.containedActions.Count > 0) { processor.PushActionFrame(frame); } frame.State = QueryInitialized; break; case QueryInitialized: Debug.Assert(frame.State == QueryInitialized); frame.NextNode(processor); Debug.Assert(Processor.IsRoot(frame.Node!)); if (processor.Debugger != null) { // this is like apply-templates, but we don't have it on stack. // Pop the stack, otherwise last instruction will be on it. processor.PopDebuggerStack(); } processor.PushTemplateLookup(frame.NodeSet, /*mode:*/null, /*importsOf:*/null); frame.State = RootProcessed; break; case RootProcessed: Debug.Assert(frame.State == RootProcessed); frame.Finished(); break; default: Debug.Fail("Invalid RootAction execution state"); break; } } } }