+2004-02-16 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XslKey.cs : Now it collects key and matching nodes at the first
+ evaluation time. After that, we can just get them from the table.
+ * Compiler.cs : Added KeyCompilationMode. It is used to prevent
+ prohibited key() usage in "match" and "use" attributes in xsl:key.
+ Modified accessibility of some classes.
+ * GenericOutputter.cs,
+ HtmlEmitter.cs,
+ TextEmitter.cs,
+ TextOutputter.cs,
+ XmlWriterEmitter.cs : made classes internal.
+ * XslOutput.cs : support line info for exception.
+ * XsltCompiledContext.cs : implemented CompareDocument() - so easily.
+
2004-02-13 Atsushi Enomoto <atsushi@ximian.com>
* XsltCompiledContext.cs : fixed the length of context info array.
XslStylesheet rootStyle;
Hashtable outputs = new Hashtable ();
-
+ bool keyCompilationMode;
+
public CompiledStylesheet Compile (XPathNavigator nav, XmlResolver res, Evidence evidence)
{
this.parser = new XPathParser (this);
get { return msScripts; }
}
- public Evidence Evidence {
+ public bool KeyCompilationMode {
+ get { return keyCompilationMode; }
+ set { keyCompilationMode = value; }
+ }
+
+ internal Evidence Evidence {
get { return evidence; }
}
}
internal XPathParser parser;
- public XPathExpression CompileExpression (string expression)
+ internal CompiledExpression CompileExpression (string expression)
{
if (expression == null || expression == "") return null;
- XPathExpression e = new CompiledExpression (parser.Compile (expression));
+ CompiledExpression e = new CompiledExpression (parser.Compile (expression), true);
exprStore.AddExpression (e, this);
case "function-available": return new XsltFunctionAvailable (args, this);
case "generate-id": return new XsltGenerateId (args);
case "format-number": return new XsltFormatNumber (args, this);
- case "key": return new XsltKey (args, this);
+ case "key":
+ if (KeyCompilationMode)
+ throw new XsltCompileException ("Cannot use key() function inside key definition.", null, this.Input);
+ return new XsltKey (args, this);
case "document": return new XsltDocument (args, this);
}
}
}
- public class XslNameUtil
+ internal class XslNameUtil
{
public static QName [] FromListString (string names, XPathNavigator current)
{
}
}
- public class XPathNavigatorNsm : XmlNamespaceManager {
+ internal class XPathNavigatorNsm : XmlNamespaceManager {
XPathNavigator nsScope;
public XPathNavigatorNsm (XPathNavigator n) : base () {
/// Implements attributes dublicate checking, nemaspace stuff and
/// choosing of right Emitter implementation.
/// </summary>
- public class GenericOutputter : Outputter {
+ internal class GenericOutputter : Outputter {
private Hashtable _outputs;
//Current xsl:output
private XslOutput _currentOutput;
{
XslOutput xslOutput = (XslOutput)_outputs [String.Empty];
switch (xslOutput.Method) {
- case OutputMethod.Unknown: //TODO: handle xml vs html
+ case OutputMethod.Unknown:
if (localName != null && localName.ToLower () == "html" && ns == String.Empty)
goto case OutputMethod.HTML;
goto case OutputMethod.XML;
_emitter = new TextEmitter (pendingTextWriter);
break;
case OutputMethod.Custom:
- throw new NotImplementedException ("Custom output method is not implemented yet.");
+ throw new NotSupportedException ("Custom output method is not supported in this version.");
}
pendingTextWriter = null;
}
namespace Mono.Xml.Xsl
{
- public class HtmlEmitter : Emitter
+ internal class HtmlEmitter : Emitter
{
TextWriter writer;
Stack elementNameStack;
/// <summary>
/// Emitetr, which emits result tree according to "text" output method.
/// </summary>
- public class TextEmitter : Emitter
+ internal class TextEmitter : Emitter
{
TextWriter writer;
/// <summary>
/// Outputter implementation for text output method.
/// </summary>
- public class TextOutputter : Outputter {
+ internal class TextOutputter : Outputter {
private TextWriter _writer;
//Current output depth
private int _depth;
/// <summary>
/// Emitter, which emits result tree to a XmlWriter.
/// </summary>
- public class XmlWriterEmitter : Emitter
+ internal class XmlWriterEmitter : Emitter
{
XmlWriter writer;
using QName = System.Xml.XmlQualifiedName;
-namespace Mono.Xml.Xsl {
- public class XslKey {
+namespace Mono.Xml.Xsl
+{
+ public class XslKey
+ {
QName name;
- XPathExpression usePattern;
- XPathExpression matchPattern;
+ CompiledExpression usePattern;
+ CompiledExpression matchPattern;
+ Hashtable map;
+ Hashtable mappedDocuments;
public XslKey (Compiler c)
{
this.name = c.ParseQNameAttribute ("name");
-
+
+ c.KeyCompilationMode = true;
usePattern = c.CompileExpression (c.GetAttribute ("use"));
if (usePattern == null)
usePattern = c.CompileExpression (".");
c.AssertAttribute ("match");
- this.matchPattern = c.CompileExpression (c.GetAttribute ("match"));
+ string matchString = c.GetAttribute ("match");
+ this.matchPattern = c.CompileExpression (matchString);
+ c.KeyCompilationMode = false;
}
public QName Name { get { return name; }}
- public XPathExpression UsePattern { get { return usePattern; }}
- public XPathExpression MatchPattern { get { return matchPattern; }}
-
- public bool Matches (XPathNavigator nav, XmlNamespaceManager nsmgr, string value)
+ internal CompiledExpression UsePattern { get { return usePattern; }}
+ internal CompiledExpression MatchPattern { get { return matchPattern; }}
+
+ internal void ClearKeyTable ()
{
- MatchPattern.SetContext (nsmgr);
- UsePattern.SetContext (nsmgr);
- if (!nav.Matches (MatchPattern))
- return false;
-// Debug.WriteLine ("? " + nav.Name);
- switch (UsePattern.ReturnType)
- {
+ if (map != null) {
+ map.Clear ();
+ map = null;
+ }
+ if (mappedDocuments != null) {
+ mappedDocuments.Clear ();
+ mappedDocuments = null;
+ }
+ }
+
+ internal void CollectTable (XPathNavigator doc)
+ {
+ XPathNavigator nav = doc.Clone ();
+ nav.MoveToRoot ();
+// if (MatchPattern.ExpressionNode.IsAbsolutePath)
+// CollectAbsoluteMatchNodes (nav);
+// else
+ CollectRelativeMatchNodes (nav);
+ }
+
+ private void CollectAbsoluteMatchNodes (XPathNavigator nav)
+ {
+ XPathNodeIterator iter = nav.Select (MatchPattern);
+ while (iter.MoveNext ())
+ CollectIndex (iter.Current);
+ }
+
+ private void CollectRelativeMatchNodes (XPathNavigator nav)
+ {
+ do {
+ if (nav.NodeType != XPathNodeType.Root)
+ while (!nav.MoveToNext ())
+ if (!nav.MoveToParent ())
+ // finished
+ return;
+ do {
+ do {
+ if (nav.Matches (MatchPattern))
+ CollectIndex (nav);
+ } while (nav.MoveToFirstChild ());
+ } while (nav.MoveToNext ());
+ } while (nav.MoveToParent ());
+ }
+
+ private void CollectIndex (XPathNavigator nav)
+ {
+ XPathNavigator target = nav.Clone ();
+ XPathNodeIterator iter;
+ switch (UsePattern.ReturnType) {
case XPathResultType.NodeSet:
- XPathNodeIterator matches = nav.Select (UsePattern);
- while (matches.MoveNext ()) {
- if (matches.Current.Value == value)
- return true;
- }
-
- return false;
+ iter = nav.Select (UsePattern);
+ while (iter.MoveNext ())
+ AddIndex (iter.Current.Value, target);
+ break;
case XPathResultType.Any:
-
object o = nav.Evaluate (UsePattern);
- if (o is XPathNodeIterator) {
- XPathNodeIterator it = (XPathNodeIterator)o;
- while (it.MoveNext ())
- if (it.Current.Value == value)
- return true;
- return false;
- } else {
- return value == XPathFunctions.ToString (o);
+ iter = o as XPathNodeIterator;
+ if (iter != null) {
+ while (iter.MoveNext ())
+ AddIndex (iter.Current.Value, target);
}
+ else
+ AddIndex (nav.EvaluateString (UsePattern, null, null), target);
+ break;
default:
- return value == nav.EvaluateString (UsePattern, null, null);
+ string key = nav.EvaluateString (UsePattern, null, null);
+ AddIndex (key, target);
+ break;
+ }
+ }
+
+ private void AddIndex (string key, XPathNavigator target)
+ {
+ ArrayList al = map [key] as ArrayList;
+ if (al == null) {
+ al = new ArrayList ();
+ map [key] = al;
}
+ al.Add (target);
+ }
+
+ public bool Matches (XPathNavigator nav, XmlNamespaceManager nsmgr, string value)
+ {
+ if (map == null) {
+ mappedDocuments = new Hashtable ();
+ map = new Hashtable ();
+ }
+ if (mappedDocuments [nav.BaseURI] == null) {
+ mappedDocuments.Add (nav.BaseURI, nav.BaseURI);
+ MatchPattern.SetContext (nsmgr);
+ UsePattern.SetContext (nsmgr);
+ CollectTable (nav);
+ MatchPattern.SetContext (null);
+ UsePattern.SetContext (null);
+ }
+
+ ArrayList al = map [value] as ArrayList;
+ if (al == null)
+ return false;
+ for (int i = 0; i < al.Count; i++)
+ if (((XPathNavigator) al [i]).IsSamePosition (nav))
+ return true;
+ return false;
}
}
}
\ No newline at end of file
if (att != String.Empty) {
switch (att) {
- case "xml":
- method = OutputMethod.XML;
- break;
- case "html":
- method = OutputMethod.HTML;
- break;
- case "text":
- method = OutputMethod.Text;
- break;
- default:
- method = OutputMethod.Custom;
- customMethod = XslNameUtil.FromString (att, nav);
- if (customMethod.Namespace == String.Empty)
- //TODO: how to get current line number and position?
- throw new XsltCompileException(new ArgumentException("Invalid output method value: '" + att +
- "'. It must be either 'xml' or 'html' or 'text' or QName."), nav.BaseURI, 1, 1);
- break;
+ case "xml":
+ method = OutputMethod.XML;
+ break;
+ case "html":
+ method = OutputMethod.HTML;
+ break;
+ case "text":
+ method = OutputMethod.Text;
+ break;
+ default:
+ method = OutputMethod.Custom;
+ customMethod = XslNameUtil.FromString (att, nav);
+ if (customMethod.Namespace == String.Empty) {
+ IXmlLineInfo li = nav as IXmlLineInfo;
+ throw new XsltCompileException (new ArgumentException ("Invalid output method value: '" + att +
+ "'. It must be either 'xml' or 'html' or 'text' or QName."),
+ nav.BaseURI,
+ li != null ? li.LineNumber : 0,
+ li != null ? li.LinePosition : 0);
+ }
+ break;
}
}
return p.CompiledStyle.ResolveVariable (q);
}
- public override int CompareDocument (string baseUri, string nextBaseUri) { throw new NotImplementedException (); }
+ public override int CompareDocument (string baseUri, string nextBaseUri)
+ {
+ // it is implementation specific
+ return baseUri.GetHashCode ().CompareTo (nextBaseUri.GetHashCode ());
+ }
public override bool PreserveWhitespace (XPathNavigator nav)
{