Pārlūkot izejas kodu

2005-03-23 Atsushi Enomoto <[email protected]>

	* XslStylesheet.cs :
	  Variables are stored per stylesheet and added to compiler after
	  all of the contents are processed (handle import precedence).
	  Imports are now processed in prior to other contents (incorrect
	  imports are now rejected). After imports, process includes to 
	  process nested imports. In-process included stylesheets are 
	  temporarily stored in including stylesheet.
	  Handle keys like variables are handled.
	  Added Compile() that is splitted from .ctor().
	* Compiler.cs :
	  Store keys in compiler and compiled stylesheet.
	  Duplicate variables in imports are now overriden as expected.
	* XsltCompiledContext.cs : use CompiledStylesheet.ResolveKey().



svn path=/trunk/mcs/; revision=42144
Atsushi Eno 21 gadi atpakaļ
vecāks
revīzija
9e00749d20

+ 16 - 0
mcs/class/System.XML/Mono.Xml.Xsl/ChangeLog

@@ -1,3 +1,19 @@
+2005-03-23  Atsushi Enomoto  <[email protected]>
+
+	* XslStylesheet.cs :
+	  Variables are stored per stylesheet and added to compiler after
+	  all of the contents are processed (handle import precedence).
+	  Imports are now processed in prior to other contents (incorrect
+	  imports are now rejected). After imports, process includes to 
+	  process nested imports. In-process included stylesheets are 
+	  temporarily stored in including stylesheet.
+	  Handle keys like variables are handled.
+	  Added Compile() that is splitted from .ctor().
+	* Compiler.cs :
+	  Store keys in compiler and compiled stylesheet.
+	  Duplicate variables in imports are now overriden as expected.
+	* XsltCompiledContext.cs : use CompiledStylesheet.ResolveKey().
+
 2005-03-22  Atsushi Enomoto  <[email protected]>
 
 	* XslTransformProcessor.cs : now it looks safe to remove SetContext()

+ 24 - 6
mcs/class/System.XML/Mono.Xml.Xsl/Compiler.cs

@@ -97,6 +97,11 @@ namespace Mono.Xml.Xsl
 			return (XslGeneralVariable)globalVariables [name];
 		}
 		
+		public XslKey ResolveKey (QName name)
+		{
+			return (XslKey) keys [name];
+		}
+		
 		public XslAttributeSet ResolveAttributeSet (QName name)
 		{
 			return (XslAttributeSet)attrSets [name];
@@ -112,6 +117,7 @@ namespace Mono.Xml.Xsl
 		Stack styleStack = new Stack ();
 		XslStylesheet currentStyle;
 		
+		Hashtable keys = new Hashtable ();
 		Hashtable globalVariables = new Hashtable ();
 		Hashtable attrSets = new Hashtable ();
 	
@@ -150,14 +156,15 @@ namespace Mono.Xml.Xsl
 				nav.MoveToParent ();
 			}
 			try {
-				this.rootStyle = new XslStylesheet (this);
+				rootStyle = new XslStylesheet ();
+				rootStyle.Compile (this);
 			} catch (XsltCompileException) {
 				throw;
 			} catch (Exception x) {
 				throw new XsltCompileException ("XSLT compile error. " + x.Message, x,  Input);
 			}
 			
-			return new CompiledStylesheet (rootStyle, globalVariables, attrSets, exprStore, nsMgr, rootStyle.Keys, outputs, decimalFormats, msScripts);
+			return new CompiledStylesheet (rootStyle, globalVariables, attrSets, exprStore, nsMgr, keys, outputs, decimalFormats, msScripts);
 		}
 		
 		MSXslScriptManager msScripts = new MSXslScriptManager ();
@@ -220,7 +227,7 @@ namespace Mono.Xml.Xsl
 			}
 		}
 		
-		private void PushInputDocument (XPathNavigator nav)
+		public void PushInputDocument (XPathNavigator nav)
 		{
 			// Inclusion nest check
 			IXmlLineInfo li = currentInput as IXmlLineInfo;
@@ -392,7 +399,12 @@ namespace Mono.Xml.Xsl
 		{
 			globalVariables [var.Name] = var;
 		}
-		
+
+		public void AddKey (XslKey key)
+		{
+			keys [key.Name] = key;
+		}
+
 		public void AddAttributeSet (XslAttributeSet set)
 		{
 			XslAttributeSet existing = attrSets [set.Name] as XslAttributeSet;
@@ -598,6 +610,7 @@ namespace Mono.Xml.Xsl
 	}
 	
 	internal class VariableScope {
+		ArrayList variableNames;
 		Hashtable variables;
 		VariableScope parent;
 		int nextSlot = 0;
@@ -622,10 +635,15 @@ namespace Mono.Xml.Xsl
 		
 		public int AddVariable (XslLocalVariable v)
 		{
-			if (variables == null)
+			if (variables == null) {
+				variableNames = new ArrayList ();
 				variables = new Hashtable ();
-			
+			}
 			variables [v.Name] = v;
+			int idx = variableNames.IndexOf (v.Name);
+			if (idx >= 0)
+				return idx;
+			variableNames.Add (v.Name);
 			return nextSlot++;
 		}
 		

+ 126 - 45
mcs/class/System.XML/Mono.Xml.Xsl/XslStylesheet.cs

@@ -59,6 +59,8 @@ namespace Mono.Xml.Xsl {
 		Hashtable parameters = new Hashtable ();
 		// [QName]=>XslKey
 		Hashtable keys = new Hashtable();
+		// [QName]=>XslVariable
+		Hashtable variables = new Hashtable ();
 
 		XslTemplateTable templates;
 
@@ -68,6 +70,11 @@ namespace Mono.Xml.Xsl {
 		XmlQualifiedName [] excludeResultPrefixes;
 		ArrayList stylesheetNamespaces = new ArrayList ();
 
+		// in-process includes. They must be first parsed as
+		// XPathNavigator, collected imports, and then processed
+		// other content.
+		Hashtable inProcessIncludes = new Hashtable ();
+
 		public XmlQualifiedName [] ExtensionElementPrefixes {
 			get { return extensionElementPrefixes; }
 		}
@@ -100,15 +107,15 @@ namespace Mono.Xml.Xsl {
 			get { return templates; }
 		}
 
-		public Hashtable Keys {
-			get { return keys; }
-		}
-
 		public string Version {
 			get { return version; }
 		}
 
-		public XslStylesheet (Compiler c)
+		public XslStylesheet ()
+		{
+		}
+
+		internal void Compile (Compiler c)
 		{
 			c.PushStylesheet (this);
 			
@@ -123,7 +130,7 @@ namespace Mono.Xml.Xsl {
 				if (c.Input.GetAttribute ("version", XsltNamespace) == String.Empty)
 					throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
 				// then it is simplified stylesheet.
-				Templates.Add (new XslTemplate (c));
+				templates.Add (new XslTemplate (c));
 			} else {
 				if (c.Input.LocalName != "stylesheet" &&
 					c.Input.LocalName != "transform")
@@ -145,8 +152,14 @@ namespace Mono.Xml.Xsl {
 				}
 				ProcessTopLevelElements (c);
 			}
-			
+
+			foreach (XslGlobalVariable v in variables.Values)
+				c.AddGlobalVariable (v);
+			foreach (XslKey key in keys.Values)
+				c.AddKey (key);
+
 			c.PopStylesheet ();
+			inProcessIncludes = null;
 		}
 
 		private QName [] ParseMappedPrefixes (string list, XPathNavigator nav)
@@ -168,19 +181,6 @@ namespace Mono.Xml.Xsl {
 			return (QName []) al.ToArray (typeof (QName));
 		}
 
-		public XslKey FindKey (QName name)
-		{
-			XslKey key = Keys [name] as XslKey;
-			if (key != null)
-				return key;
-			for (int i = Imports.Count - 1; i >= 0; i--) {
-				key = ((XslStylesheet) Imports [i]).FindKey (name);
-				if (key != null)
-					return key;
-			}
-			return null;
-		}
-
 		bool countedSpaceControlExistence;
 		bool cachedHasSpaceControls;
 		static readonly QName allMatchName = new QName ("*");
@@ -319,36 +319,81 @@ namespace Mono.Xml.Xsl {
 			return result != null ? result : prefix;
 		}
 
-		private XslStylesheet (Compiler c, XslStylesheet importer) : this (c)
+		private void StoreInclude (Compiler c)
 		{
-//			this.importer = importer;
+			XPathNavigator including = c.Input.Clone ();
+			c.PushInputDocument (c.Input.GetAttribute ("href", String.Empty));
+			inProcessIncludes [including] = c.Input;
+
+			HandleImportsInInclude (c);
+			c.PopInputDocument ();
 		}
-		
-		private void HandleInclude (Compiler c, string href)
+
+		private void HandleImportsInInclude (Compiler c)
 		{
-			c.PushInputDocument (href);
+			if (c.Input.NamespaceURI != XsltNamespace) {
+				if (c.Input.GetAttribute ("version",
+					XsltNamespace) == String.Empty)
+					throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
+				// simplified style == never imports.
+				// Keep this position
+				return;
+			}
+
+			if (!c.Input.MoveToFirstChild ()) {
+				c.Input.MoveToRoot ();
+				return;
+			}
+
+			HandleIncludesImports (c);
+		}
+
+		private void HandleInclude (Compiler c)
+		{
+			XPathNavigator included = null;
+			foreach (XPathNavigator inc in inProcessIncludes.Keys) {
+				if (inc.IsSamePosition (c.Input)) {
+					included = (XPathNavigator) inProcessIncludes [inc];
+					break;
+				}
+			}
+			if (included == null)
+				throw new Exception ("Should not happen. Current input is " + c.Input.BaseURI + " / " + c.Input.Name + ", " + inProcessIncludes.Count);
+
+			if (included.NodeType == XPathNodeType.Root)
+				return; // Already done.
+
+			c.PushInputDocument (included);
 
-			// move to root element
 			while (c.Input.NodeType != XPathNodeType.Element)
 				if (!c.Input.MoveToNext ())
-					throw new XsltCompileException ("Stylesheet root element must be either \"stylesheet\" or \"transform\" or any literal element.", null, c.Input);
+					break;
 
-			if (c.Input.NamespaceURI != XsltNamespace) {
-				if (c.Input.GetAttribute ("version", XsltNamespace) == String.Empty)
-					throw new XsltCompileException ("Mandatory global attribute version is missing.", null, c.Input);
+			if (c.Input.NamespaceURI != XsltNamespace &&
+				c.Input.NodeType == XPathNodeType.Element) {
 				// then it is simplified stylesheet.
-				Templates.Add (new XslTemplate (c));
+				templates.Add (new XslTemplate (c));
+			}
+			else {
+				do {
+					if (c.Input.NodeType != XPathNodeType.Element)
+						continue;
+					Debug.EnterNavigator (c);
+					HandleTopLevelElement (c);
+					Debug.ExitNavigator (c);
+				} while (c.Input.MoveToNext ());
 			}
-			else
-				ProcessTopLevelElements (c);
 
+			c.Input.MoveToParent ();
 			c.PopInputDocument ();
 		}
 		
 		private void HandleImport (Compiler c, string href)
 		{
 			c.PushInputDocument (href);
-			imports.Add (new XslStylesheet (c, this));
+			XslStylesheet imported = new XslStylesheet ();
+			imported.Compile (c);
+			imports.Add (imported);
 			c.PopInputDocument ();
 		}
 		
@@ -358,14 +403,10 @@ namespace Mono.Xml.Xsl {
 			switch (n.NamespaceURI)
 			{
 			case XsltNamespace:
-				
 				switch (n.LocalName)
 				{
 				case "include":
-					HandleInclude (c, c.GetAttribute ("href"));
-					break;
-				case "import":
-					HandleImport (c, c.GetAttribute ("href"));
+					HandleInclude (c);
 					break;
 				case "preserve-space":
 					AddSpaceControls (c.ParseQNameListAttribute ("elements"), XmlSpace.Preserve, n);
@@ -383,7 +424,8 @@ namespace Mono.Xml.Xsl {
 					break;
 
 				case "key":
-					keys [c.ParseQNameAttribute ("name")] = new XslKey (c);
+					XslKey key = new XslKey (c);
+					keys [key.Name] = key;
 					break;
 					
 				case "output":
@@ -398,14 +440,16 @@ namespace Mono.Xml.Xsl {
 					templates.Add (new XslTemplate (c));	
 					break;
 				case "variable":
-					c.AddGlobalVariable (new XslGlobalVariable (c));
+					XslGlobalVariable gvar = new XslGlobalVariable (c);
+					variables [gvar.Name] = gvar;
 					break;
 				case "param":
-					c.AddGlobalVariable (new XslGlobalParam (c));
+					XslGlobalParam gpar = new XslGlobalParam (c);
+					variables [gpar.Name] = gpar;
 					break;
 				default:
 					if (version == "1.0")
-						throw new XsltCompileException ("Unrecognized top level element.", null, c.Input);
+						throw new XsltCompileException ("Unrecognized top level element after imports.", null, c.Input);
 					break;
 				}
 				break;
@@ -419,11 +463,48 @@ namespace Mono.Xml.Xsl {
 				break;
 			}
 		}
-		
+
+		private XPathNavigator HandleIncludesImports (Compiler c)
+		{
+			// process imports. They must precede to other
+			// top level elements by schema.
+			do {
+				if (c.Input.NodeType != XPathNodeType.Element)
+					continue;
+				if (c.Input.LocalName != "import" ||
+					c.Input.NamespaceURI != XsltNamespace)
+					break;
+				Debug.EnterNavigator (c);
+				HandleImport (c, c.GetAttribute ("href"));
+				Debug.ExitNavigator (c);
+			} while (c.Input.MoveToNext ());
+
+			XPathNavigator saved = c.Input.Clone ();
+
+			// process includes to handle nested imports. They must precede to other
+			// top level elements by schema.
+			do {
+				if (c.Input.NodeType != XPathNodeType.Element ||
+					c.Input.LocalName != "include" ||
+					c.Input.NamespaceURI != XsltNamespace)
+					continue;
+				Debug.EnterNavigator (c);
+				StoreInclude (c);
+				Debug.ExitNavigator (c);
+			} while (c.Input.MoveToNext ());
+
+			c.Input.MoveTo (saved);
+
+			return saved;
+		}
+
 		private void ProcessTopLevelElements (Compiler c)
 		{
 			if (!c.Input.MoveToFirstChild ())
 				return;
+
+			XPathNavigator saved = HandleIncludesImports (c);
+
 			do {
 				// Collect namespace aliases first.
 				if (c.Input.NodeType != XPathNodeType.Element ||
@@ -439,7 +520,7 @@ namespace Mono.Xml.Xsl {
 				namespaceAliases.Set (sprefix, rprefix);
 			} while (c.Input.MoveToNext ());
 
-			c.Input.MoveToFirst ();
+			c.Input.MoveTo (saved);
 			do {
 				if (c.Input.NodeType != XPathNodeType.Element)
 					continue;

+ 1 - 1
mcs/class/System.XML/Mono.Xml.Xsl/XsltCompiledContext.cs

@@ -128,7 +128,7 @@ namespace Mono.Xml.Xsl
 			KeyIndexTable table =
 				keyIndexTables [name] as KeyIndexTable;
 			if (table == null) {
-				table = new KeyIndexTable (this, p.CompiledStyle.Style.FindKey (name));
+				table = new KeyIndexTable (this, p.CompiledStyle.ResolveKey (name));
 				keyIndexTables [name] = table;
 			}
 			return table;