2005-11-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / XslFunctions.cs
old mode 100755 (executable)
new mode 100644 (file)
index 102b9ac..cf061cc
@@ -193,6 +193,15 @@ namespace Mono.Xml.Xsl
                {
                        return new SelfIterator ((iter.NamespaceManager as XsltCompiledContext).Processor.CurrentNode, null);
                }
+
+               internal override bool Peer {
+                       get { return false; }
+               }
+
+               public override string ToString ()
+               {
+                       return "current()";
+               }
        }
        
        class XsltDocument : XPathFunction 
@@ -211,7 +220,11 @@ namespace Mono.Xml.Xsl
                        doc = c.Input.Clone ();
                }
                public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
-               
+
+               internal override bool Peer {
+                       get { return arg0.Peer && (arg1 != null ? arg1.Peer : true); }
+               }
+
                public override object Evaluate (BaseIterator iter)
                {
                        string baseUri = null;
@@ -237,7 +250,8 @@ namespace Mono.Xml.Xsl
 //                     Debug.WriteLine ("THIS: " + thisUri);
 //                     Debug.WriteLine ("BASE: " + baseUri);
                        XmlResolver r = p.Resolver;
-                       
+                       if (r == null)
+                               return null;
                        Uri uriBase = null;
                        if (! object.ReferenceEquals (baseUri, VoidBaseUriFlag) && baseUri != String.Empty)
                                uriBase = r.ResolveUri (null, baseUri);
@@ -248,42 +262,61 @@ namespace Mono.Xml.Xsl
                XPathNodeIterator GetDocument (XsltCompiledContext xsltContext, XPathNodeIterator itr, string baseUri)
                {
                        ArrayList list = new ArrayList ();
-                       Hashtable got = new Hashtable ();
+                       try {
+                               Hashtable got = new Hashtable ();
                        
-                       while (itr.MoveNext()) {
-                               Uri uri = Resolve (itr.Current.Value, baseUri != null ? baseUri : /*itr.Current.BaseURI*/doc.BaseURI, xsltContext.Processor);
-                               if (!got.ContainsKey (uri)) {
-                                       got.Add (uri, null);
-                                       if (uri.ToString () == "") {
-                                               XPathNavigator n = doc.Clone ();
-                                               n.MoveToRoot ();
-                                               list.Add (n);
-                                       } else
-                                               list.Add (xsltContext.Processor.GetDocument (uri));
+                               while (itr.MoveNext()) {
+                                       Uri uri = Resolve (itr.Current.Value, baseUri != null ? baseUri : /*itr.Current.BaseURI*/doc.BaseURI, xsltContext.Processor);
+                                       if (!got.ContainsKey (uri)) {
+                                               got.Add (uri, null);
+                                               if (uri != null && uri.ToString () == "") {
+                                                       XPathNavigator n = doc.Clone ();
+                                                       n.MoveToRoot ();
+                                                       list.Add (n);
+                                               } else
+                                                       list.Add (xsltContext.Processor.GetDocument (uri));
+                                       }
                                }
+                       } catch (Exception) {
+                               // Error recovery.
+                               // See http://www.w3.org/TR/xslt#document and
+                               // bug #75663.
+                               list.Clear ();
                        }
-                       
-                       return new ListIterator (list, xsltContext, false);
+                       return new ListIterator (list, xsltContext);
                }
        
                XPathNodeIterator GetDocument (XsltCompiledContext xsltContext, string arg0, string baseUri)
                {
-                       Uri uri = Resolve (arg0, baseUri != null ? baseUri : doc.BaseURI, xsltContext.Processor);
-                       XPathNavigator n;
-                       if (uri.ToString () == "") {
-                               n = doc.Clone ();
-                               n.MoveToRoot ();
-                       } else
-                               n = xsltContext.Processor.GetDocument (uri);
+                       try {
+                               Uri uri = Resolve (arg0, baseUri != null ? baseUri : doc.BaseURI, xsltContext.Processor);
+                               XPathNavigator n;
+                               if (uri != null && uri.ToString () == "") {
+                                       n = doc.Clone ();
+                                       n.MoveToRoot ();
+                               } else
+                                       n = xsltContext.Processor.GetDocument (uri);
                        
-                       return new SelfIterator (n, xsltContext);
+                               return new SelfIterator (n, xsltContext);
+                       } catch (Exception) {
+                               return new ListIterator (new ArrayList (), xsltContext);
+                       }
+               }
+
+               public override string ToString ()
+               {
+                       return String.Concat ("document(",
+                               arg0.ToString (),
+                               arg1 != null ? "," : String.Empty,
+                               arg1 != null ? arg1.ToString () : String.Empty,
+                               ")");
                }
        }
        
        class XsltElementAvailable : XPathFunction 
        {
                Expression arg0;
-               XmlNamespaceManager nsm;
+               IStaticXsltContext ctx;
                
                public XsltElementAvailable (FunctionArguments args, IStaticXsltContext ctx) : base (args)
                {
@@ -291,14 +324,18 @@ namespace Mono.Xml.Xsl
                                throw new XPathException ("element-available takes 1 arg");
                        
                        arg0 = args.Arg;
-                       nsm = ctx.GetNsm ();
+                       this.ctx = ctx;
                }
                
                public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
 
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
+
                public override object Evaluate (BaseIterator iter)
                {
-                       QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), nsm);
+                       QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), ctx);
 
                        return (
                                (name.Namespace == Compiler.XsltNamespace) &&
@@ -330,7 +367,7 @@ namespace Mono.Xml.Xsl
        class XsltFormatNumber : XPathFunction 
        {
                Expression arg0, arg1, arg2;
-               XmlNamespaceManager nsm;
+               IStaticXsltContext ctx;
                
                public XsltFormatNumber (FunctionArguments args, IStaticXsltContext ctx) : base (args)
                {
@@ -341,10 +378,14 @@ namespace Mono.Xml.Xsl
                        arg1 = args.Tail.Arg;
                        if (args.Tail.Tail != null) {
                                arg2= args.Tail.Tail.Arg;
-                               nsm = ctx.GetNsm ();
+                               this.ctx = ctx;
                        }
                }
                public override XPathResultType ReturnType { get { return XPathResultType.String; }}
+
+               internal override bool Peer {
+                       get { return arg0.Peer && arg1.Peer && (arg2 != null ? arg2.Peer : true); }
+               }
                
                public override object Evaluate (BaseIterator iter)
                {
@@ -353,7 +394,7 @@ namespace Mono.Xml.Xsl
                        QName nm = QName.Empty;
                        
                        if (arg2 != null)
-                               nm = XslNameUtil.FromString (arg2.EvaluateString (iter), nsm);
+                               nm = XslNameUtil.FromString (arg2.EvaluateString (iter), ctx);
                        
                        try {
                                return (iter.NamespaceManager as XsltCompiledContext).Processor.CompiledStyle
@@ -367,7 +408,7 @@ namespace Mono.Xml.Xsl
        class XsltFunctionAvailable : XPathFunction 
        {
                Expression arg0;
-               XmlNamespaceManager nsm;
+               IStaticXsltContext ctx;
                
                public XsltFunctionAvailable (FunctionArguments args, IStaticXsltContext ctx) : base (args)
                {
@@ -375,10 +416,14 @@ namespace Mono.Xml.Xsl
                                throw new XPathException ("element-available takes 1 arg");
                        
                        arg0 = args.Arg;
-                       nsm = ctx.GetNsm ();
+                       this.ctx = ctx;
                }
                
                public override XPathResultType ReturnType { get { return XPathResultType.Boolean; }}
+
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
                
                public override object Evaluate (BaseIterator iter)
                {
@@ -388,7 +433,7 @@ namespace Mono.Xml.Xsl
                        // extension function
                        if (colon > 0)
                                return (iter.NamespaceManager as XsltCompiledContext).ResolveFunction (
-                                       XslNameUtil.FromString (name, nsm),
+                                       XslNameUtil.FromString (name, ctx),
                                        null) != null;
                        
                        return (
@@ -438,6 +483,7 @@ namespace Mono.Xml.Xsl
 
        class XsltGenerateId : XPathFunction 
        {
+               //FIXME: generate short string, not the huge thing it makes now
                Expression arg0;
                public XsltGenerateId (FunctionArguments args) : base (args)
                {
@@ -449,6 +495,11 @@ namespace Mono.Xml.Xsl
                }
                
                public override XPathResultType ReturnType { get { return XPathResultType.String; }}
+
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
+
                public override object Evaluate (BaseIterator iter)
                {
                        XPathNavigator n;
@@ -488,86 +539,45 @@ namespace Mono.Xml.Xsl
        class XsltKey : XPathFunction 
        {
                Expression arg0, arg1;
-               XmlNamespaceManager nsm;
-               XslKey key;
+               IStaticXsltContext staticContext;
                
                public XsltKey (FunctionArguments args, IStaticXsltContext ctx) : base (args)
                {
+                       staticContext = ctx;
                        if (args == null || args.Tail == null)
                                throw new XPathException ("key takes 2 args");
                        arg0 = args.Arg;
                        arg1 = args.Tail.Arg;
-                       nsm = ctx.GetNsm ();
                }
                public Expression KeyName { get { return arg0; } }
                public Expression Field { get { return arg1; } }
-               public XmlNamespaceManager NamespaceManager { get { return nsm; } }
                public override XPathResultType ReturnType { get { return XPathResultType.NodeSet; }}
-               
-               public override object Evaluate (BaseIterator iter)
-               {
-                       QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), nsm);
-                       XsltCompiledContext ctx = iter.NamespaceManager as XsltCompiledContext;
-                       if (key == null)
-                               key = ctx.Processor.CompiledStyle.Style.FindKey (name);
 
-                       ArrayList result = new ArrayList ();
-                       object o = arg1.Evaluate (iter);
-                       XPathNodeIterator it = o as XPathNodeIterator;
-                       
-                       if (it != null) {
-                               while (it.MoveNext())
-                                       FindKeyMatch (ctx, it.Current.Value, result, iter.Current);
-                       } else {
-                               FindKeyMatch (ctx, XPathFunctions.ToString (o), result, iter.Current);
-                       }
-                       
-                       return new ListIterator (result, (iter.NamespaceManager as XsltCompiledContext), true);
+               internal override bool Peer {
+                       get { return arg0.Peer && arg1.Peer; }
                }
-               
-               void FindKeyMatch (XsltCompiledContext xsltContext, string value, ArrayList result, XPathNavigator context)
-               {
-                       XPathNavigator searchDoc = context.Clone ();
-                       searchDoc.MoveToRoot ();
-                       if (key != null) {
-                               XPathNodeIterator desc = searchDoc.SelectDescendants (XPathNodeType.All, true);
-
-                               while (desc.MoveNext ()) {
-                                       if (key.Matches (desc.Current, xsltContext, value))
-                                               AddResult (result, desc.Current);
-                                       
-                                       if (desc.Current.MoveToFirstAttribute ()) {
-                                               do {
-                                                       if (key.Matches (desc.Current, xsltContext, value))
-                                                               AddResult (result, desc.Current);       
-                                               } while (desc.Current.MoveToNextAttribute ());
-                                               
-                                               desc.Current.MoveToParent ();
-                                       }
-                               }
-                       }
+
+               public bool PatternMatches (XPathNavigator nav, XsltContext nsmgr)
+               {
+                       XsltCompiledContext ctx = nsmgr as XsltCompiledContext;
+                       // for key pattern, it must contain literal value
+                       return ctx.MatchesKey (nav, staticContext,
+                               arg0.StaticValueAsString,
+                               arg1.StaticValueAsString);
                }
 
-               void AddResult (ArrayList result, XPathNavigator nav)
+               public override object Evaluate (BaseIterator iter)
                {
-                       for (int i = 0; i < result.Count; i++) {
-                               XmlNodeOrder docOrder = nav.ComparePosition (((XPathNavigator)result [i]));
-                               if (docOrder == XmlNodeOrder.Same)
-                                       return;
-                               
-                               if (docOrder == XmlNodeOrder.Before) {
-                                       result.Insert(i, nav.Clone ());
-                                       return;
-                               }
-                       }
-                       result.Add (nav.Clone ());
+                       XsltCompiledContext ctx = iter.NamespaceManager
+                               as XsltCompiledContext;
+                       return ctx.EvaluateKey (staticContext, iter, arg0, arg1);
                }
        }
        
        class XsltSystemProperty : XPathFunction 
        {
                Expression arg0;
-               XmlNamespaceManager nsm;
+               IStaticXsltContext ctx;
                
                public XsltSystemProperty (FunctionArguments args, IStaticXsltContext ctx) : base (args)
                {
@@ -575,13 +585,18 @@ namespace Mono.Xml.Xsl
                                throw new XPathException ("system-property takes 1 arg");
                        
                        arg0 = args.Arg;
-                       nsm = ctx.GetNsm ();
+                       this.ctx = ctx;
                }
                
                public override XPathResultType ReturnType { get { return XPathResultType.String; }}
+
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
+
                public override object Evaluate (BaseIterator iter)
                {
-                       QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), nsm);
+                       QName name = XslNameUtil.FromString (arg0.EvaluateString (iter), ctx);
                        
                        if (name.Namespace == Compiler.XsltNamespace) {
                                switch (name.Name) {
@@ -608,20 +623,26 @@ namespace Mono.Xml.Xsl
                }
                
                public override XPathResultType ReturnType { get { return XPathResultType.String; }}
+
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
+
                public override object Evaluate (BaseIterator iter)
                {
                        IHasXmlNode xn = iter.Current as IHasXmlNode;
                        if (xn == null)
                                return String.Empty;
                        XmlNode n = xn.GetNode ();
+                       if (n.OwnerDocument == null)
+                               return String.Empty;
                        XmlDocumentType doctype = n.OwnerDocument.DocumentType;
                        if (doctype == null)
                                return String.Empty;
                        XmlEntity ent = doctype.Entities.GetNamedItem (arg0.EvaluateString (iter)) as XmlEntity;
                        if (ent == null)
                                return String.Empty;
-                       else
-                               return ent.BaseURI;
+                       return ent.SystemId != null ? ent.SystemId : String.Empty;
                }
        }
 
@@ -636,27 +657,31 @@ namespace Mono.Xml.Xsl
                        
                        arg0 = args.Arg;
                }
-\r
-               public override XPathResultType ReturnType {\r
-                       get {\r
-                               return XPathResultType.NodeSet;\r
-                       }\r
-               }\r
-
-               public override object Evaluate (BaseIterator iter)\r
-               {\r
-                       XsltCompiledContext ctx = iter.NamespaceManager as XsltCompiledContext;\r
-                       XPathNavigator loc = iter.Current != null ? iter.Current.Clone () : null;\r
-                       XPathNavigator nav = arg0.EvaluateAs (iter, XPathResultType.Navigator) as XPathNavigator;\r
-                       if (nav == null) {\r
-                               if (loc != null)\r
-                                       return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null, loc);\r
-                               else\r
-                                       return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null);\r
-                       }\r
-                       ArrayList al = new ArrayList ();\r
-                       al.Add (nav);\r
-                       return new ListIterator (al, ctx, false);\r
+
+               public override XPathResultType ReturnType {
+                       get {
+                               return XPathResultType.NodeSet;
+                       }
+               }
+
+               internal override bool Peer {
+                       get { return arg0.Peer; }
+               }
+
+               public override object Evaluate (BaseIterator iter)
+               {
+                       XsltCompiledContext ctx = iter.NamespaceManager as XsltCompiledContext;
+                       XPathNavigator loc = iter.Current != null ? iter.Current.Clone () : null;
+                       XPathNavigator nav = arg0.EvaluateAs (iter, XPathResultType.Navigator) as XPathNavigator;
+                       if (nav == null) {
+                               if (loc != null)
+                                       return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null, loc);
+                               else
+                                       return new XsltException ("Cannot convert the XPath argument to a result tree fragment.", null);
+                       }
+                       ArrayList al = new ArrayList ();
+                       al.Add (nav);
+                       return new ListIterator (al, ctx);
                }
        }
 }