System.Drawing: added email to icon and test file headers
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / XslKey.cs
index 7b1f25fb4528b179a447ec32f637a29e5175668e..d3f1103ff8393b4095d2b3d0bd79a45069dd2475 100644 (file)
 // (C) 2003 Atsushi Enomoto
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
-using System.CodeDom;
 using System.Collections;
 using System.Collections.Specialized;
 using System.Xml;
 using System.Xml.Schema;
 using System.Xml.XPath;
 using System.Xml.Xsl;
+using Mono.Xml.XPath;
 
 using QName = System.Xml.XmlQualifiedName;
 
-namespace Mono.Xml.Xsl {
-       public class XslKey {
+namespace Mono.Xml.Xsl
+{
+       internal class ExprKeyContainer : Expression
+       {
+               Expression expr;
+               public ExprKeyContainer (Expression expr)
+               {
+                       this.expr = expr;
+               }
+
+               public Expression BodyExpression {
+                       get { return expr; }
+               }
+
+               public override object Evaluate (BaseIterator iter)
+               {
+                       return expr.Evaluate (iter);
+               }
+
+               internal override XPathNodeType EvaluatedNodeType {
+                       get { return expr.EvaluatedNodeType; }
+               }
+
+               public override XPathResultType ReturnType {
+                       get { return expr.ReturnType; }
+               }
+       }
+
+       internal class XslKey
+       {
                QName name;
-               XPathExpression usePattern;
-               XPathExpression matchPattern;
+               CompiledExpression useExpr;
+               Pattern matchPattern;
 
                public XslKey (Compiler c)
                {
                        this.name = c.ParseQNameAttribute ("name");
-                       
-                       usePattern = c.CompileExpression (c.GetAttribute ("use"));
-                       if (usePattern == null)
-                               usePattern = c.CompileExpression (".");
+
+                       c.KeyCompilationMode = true;
+                       useExpr = c.CompileExpression (c.GetAttribute ("use"));
+                       if (useExpr == null)
+                               useExpr = c.CompileExpression (".");
 
                        c.AssertAttribute ("match");
-                       this.matchPattern = c.CompileExpression (c.GetAttribute ("match"));
+                       string matchString = c.GetAttribute ("match");
+                       this.matchPattern = c.CompilePattern (matchString, c.Input);
+                       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 Use { get { return useExpr; }}
+               internal Pattern Match { get { return matchPattern; }}
+       }
+
+       // represents part of dynamic context that holds index table for a key
+       internal class KeyIndexTable
+       {
+               XsltCompiledContext ctx;
+               ArrayList keys;
+               Hashtable mappedDocuments;
+
+               public KeyIndexTable (XsltCompiledContext ctx, ArrayList keys)
                {
-                       MatchPattern.SetContext (nsmgr);
-                       UsePattern.SetContext (nsmgr);
-                       if (!nav.Matches (MatchPattern)) 
-                               return false;
-//                     Debug.WriteLine ("? " + nav.Name);
-                       switch (UsePattern.ReturnType)
-                       {
-                       case XPathResultType.NodeSet:
-                               XPathNodeIterator matches = nav.Select (UsePattern);
-                               while (matches.MoveNext ()) {
-                                       if (matches.Current.Value == value)
+                       this.ctx = ctx;
+                       this.keys = keys;
+               }
+
+               public ArrayList Keys {
+                       get { return keys; }
+               }
+
+               private void CollectTable (XPathNavigator doc, XsltContext ctx, Hashtable map)
+               {
+                       for (int i = 0; i < keys.Count; i++)
+                               CollectTable (doc, ctx, map, (XslKey) keys[i]);
+               }
+
+               private void CollectTable (XPathNavigator doc, XsltContext ctx, Hashtable map, XslKey key)
+               {
+                       XPathNavigator nav = doc.Clone ();
+                       nav.MoveToRoot ();
+                       XPathNavigator tmp = doc.Clone ();
+
+                       bool matchesAttributes = false;
+                       switch (key.Match.EvaluatedNodeType) {
+                       case XPathNodeType.All:
+                       case XPathNodeType.Attribute:
+                               matchesAttributes = true;
+                               break;
+                       }
+
+                       do {
+                               if (key.Match.Matches (nav, ctx)) {
+                                       tmp.MoveTo (nav);
+                                       CollectIndex (nav, tmp, map);
+                               }
+                       } while (MoveNavigatorToNext (nav, matchesAttributes));
+                       if (map != null)
+                               foreach (ArrayList list in map.Values)
+                                       list.Sort (XPathNavigatorComparer.Instance);
+               }
+
+               private bool MoveNavigatorToNext (XPathNavigator nav, bool matchesAttributes)
+               {
+                       if (matchesAttributes) {
+                               if (nav.NodeType != XPathNodeType.Attribute &&
+                                       nav.MoveToFirstAttribute ())
+                                       return true;
+                               else if (nav.NodeType == XPathNodeType.Attribute) {
+                                       if (nav.MoveToNextAttribute ())
                                                return true;
+                                       nav.MoveToParent ();
                                }
-                               
-                               return false;
+                       }
+                       if (nav.MoveToFirstChild ())
+                               return true;
+                       do {
+                               if (nav.MoveToNext ())
+                                       return true;
+                       } while (nav.MoveToParent ());
+                       return false;
+               }
+
+               private void CollectIndex (XPathNavigator nav, XPathNavigator target, Hashtable map)
+               {
+                       for (int i = 0; i < keys.Count; i++)
+                               CollectIndex (nav, target, map, (XslKey) keys[i]);
+               }
+
+               private void CollectIndex (XPathNavigator nav, XPathNavigator target, Hashtable map, XslKey key)
+               {
+                       XPathNodeIterator iter;
+                       switch (key.Use.ReturnType) {
+                       case XPathResultType.NodeSet:
+                               iter = nav.Select (key.Use);
+                               while (iter.MoveNext ())
+                                       AddIndex (iter.Current.Value, target, map);
+                               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);
+                               object o = nav.Evaluate (key.Use);
+                               iter = o as XPathNodeIterator;
+                               if (iter != null) {
+                                       while (iter.MoveNext ())
+                                               AddIndex (iter.Current.Value, target, map);
                                }
+                               else
+                                       AddIndex (XPathFunctions.ToString (o), target, map);
+                               break;
                        default:
-                               return value == nav.EvaluateString (UsePattern, null, null);
+                               string keyValue = nav.EvaluateString (key.Use, null, null);
+                               AddIndex (keyValue, target, map);
+                               break;
                        }
                }
+
+               private void AddIndex (string key, XPathNavigator target, Hashtable map)
+               {
+                       ArrayList al = map [key] as ArrayList;
+                       if (al == null) {
+                               al = new ArrayList ();
+                               map [key] = al;
+                       }
+                       for (int i = 0; i < al.Count; i++)
+                               if (((XPathNavigator) al [i]).IsSamePosition (target))
+                                       return;
+                       al.Add (target.Clone ());
+               }
+
+               private ArrayList GetNodesByValue (XPathNavigator nav, string value, XsltContext ctx)
+               {
+                       if (mappedDocuments == null)
+                               mappedDocuments = new Hashtable ();
+                       Hashtable map = (Hashtable) mappedDocuments [nav.BaseURI];
+                       if (map == null) {
+                               map = new Hashtable ();
+                               mappedDocuments.Add (nav.BaseURI, map);
+                               CollectTable (nav, ctx, map);
+                       }
+                       
+                       return map [value] as ArrayList;
+               }
+
+               public bool Matches (XPathNavigator nav, string value, XsltContext ctx)
+               {
+                       ArrayList al = GetNodesByValue (nav, value, ctx);
+                       if (al == null)
+                               return false;
+                       for (int i = 0; i < al.Count; i++)
+                               if (((XPathNavigator) al [i]).IsSamePosition (nav))
+                                       return true;
+                       return false;
+               }
+
+               // Invoked from XsltKey (XPathFunction)
+               public BaseIterator Evaluate (BaseIterator iter,
+                       Expression valueExpr)
+               {
+                       XPathNodeIterator i = iter;
+                       if (iter.CurrentPosition == 0) {
+                               i = iter.Clone ();
+                               i.MoveNext ();
+                       }
+                       XPathNavigator nav = i.Current;
+
+                       object o = valueExpr.Evaluate (iter);
+                       XPathNodeIterator it = o as XPathNodeIterator;
+                       XsltContext ctx = iter.NamespaceManager as XsltContext;
+
+                       BaseIterator result = null;
+
+                       if (it != null) {
+                               while (it.MoveNext()) {
+                                       ArrayList nodes = GetNodesByValue (
+                                               nav, it.Current.Value, ctx);
+                                       if (nodes == null)
+                                               continue;
+                                       ListIterator tmp =
+                                               new ListIterator (nodes, ctx);
+                                       if (result == null)
+                                               result = tmp;
+                                       else
+                                               result = new UnionIterator (
+                                                       iter, result, tmp);
+                               }
+                       }
+                       else if (nav != null) {
+                               ArrayList nodes = GetNodesByValue (
+                                       nav, XPathFunctions.ToString (o), ctx);
+                               if (nodes != null)
+                                       result = new ListIterator (nodes, ctx);
+                       }
+
+                       return result != null ? result : new NullIterator (iter);
+               }
        }
 }
\ No newline at end of file