2004-03-22 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / XslKey.cs
1 //
2 // XslKey.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //      
8 // (C) 2003 Ben Maurer
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Xml.XPath;
39 using System.Xml.Xsl;
40 using Mono.Xml.XPath;
41
42 using QName = System.Xml.XmlQualifiedName;
43
44 namespace Mono.Xml.Xsl
45 {
46         internal class ExprKeyContainer : Expression
47         {
48                 Expression expr;
49                 public ExprKeyContainer (Expression expr)
50                 {
51                         this.expr = expr;
52                 }
53
54                 public Expression BodyExpression {
55                         get { return expr; }
56                 }
57
58                 public override object Evaluate (BaseIterator iter)
59                 {
60                         return expr.Evaluate (iter);
61                 }
62
63                 internal override XPathNodeType EvaluatedNodeType {
64                         get { return expr.EvaluatedNodeType; }
65                 }
66
67                 public override XPathResultType ReturnType {
68                         get { return expr.ReturnType; }
69                 }
70
71                 public override bool RequireSorting {
72                         get { return true; }
73                 }
74         }
75
76         internal class XslKey
77         {
78                 QName name;
79                 CompiledExpression useExpr;
80                 Pattern matchPattern;
81 //              Hashtable map;
82 //              Hashtable mappedDocuments;
83
84                 public XslKey (Compiler c)
85                 {
86                         this.name = c.ParseQNameAttribute ("name");
87
88                         c.KeyCompilationMode = true;
89                         useExpr = c.CompileExpression (c.GetAttribute ("use"));
90                         if (useExpr == null)
91                                 useExpr = c.CompileExpression (".");
92
93                         c.AssertAttribute ("match");
94                         string matchString = c.GetAttribute ("match");
95                         this.matchPattern = c.CompilePattern (matchString, c.Input);
96                         c.KeyCompilationMode = false;
97                 }
98
99                 public QName Name { get { return name; }}
100                 internal CompiledExpression Use { get { return useExpr; }}
101                 internal Pattern Match { get { return matchPattern; }}
102         }
103
104         // represents part of dynamic context that holds index table for a key
105         internal class KeyIndexTable
106         {
107                 XsltCompiledContext ctx;
108                 XslKey key;
109                 Hashtable map;
110                 Hashtable mappedDocuments;
111
112                 public KeyIndexTable (XsltCompiledContext ctx, XslKey key)
113                 {
114                         this.ctx = ctx;
115                         this.key = key;
116                 }
117
118                 public XslKey Key {
119                         get { return key; }
120                 }
121
122                 private void CollectTable (XPathNavigator doc, XsltContext ctx)
123                 {
124                         XPathNavigator nav = doc.Clone ();
125                         nav.MoveToRoot ();
126                         XPathNavigator tmp = doc.Clone ();
127
128                         do {
129                                 if (key.Match.Matches (nav, ctx)) {
130                                         tmp.MoveTo (nav);
131                                         CollectIndex (nav, tmp);
132                                 }
133                         } while (MoveNavigatorToNext (nav));
134                         if (map != null)
135                                 foreach (ArrayList list in map.Values)
136                                         list.Sort (XPathNavigatorComparer.Instance);
137                 }
138
139                 private bool MoveNavigatorToNext (XPathNavigator nav)
140                 {
141                         if (nav.MoveToFirstChild ())
142                                 return true;
143                         do {
144                                 if (nav.MoveToNext ())
145                                         return true;
146                         } while (nav.MoveToParent ());
147                         return false;
148                 }
149
150                 private void CollectIndex (XPathNavigator nav, XPathNavigator target)
151                 {
152                         XPathNodeIterator iter;
153                         switch (key.Use.ReturnType) {
154                         case XPathResultType.NodeSet:
155                                 iter = nav.Select (key.Use);
156                                 while (iter.MoveNext ())
157                                         AddIndex (iter.Current.Value, target);
158                                 break;
159                         case XPathResultType.Any:
160                                 object o = nav.Evaluate (key.Use);
161                                 iter = o as XPathNodeIterator;
162                                 if (iter != null) {
163                                         while (iter.MoveNext ())
164                                                 AddIndex (iter.Current.Value, target);
165                                 }
166                                 else
167                                         AddIndex (XPathFunctions.ToString (o), target);
168                                 break;
169                         default:
170                                 string keyValue = nav.EvaluateString (key.Use, null, null);
171                                 AddIndex (keyValue, target);
172                                 break;
173                         }
174                 }
175
176                 private void AddIndex (string key, XPathNavigator target)
177                 {
178                         ArrayList al = map [key] as ArrayList;
179                         if (al == null) {
180                                 al = new ArrayList ();
181                                 map [key] = al;
182                         }
183                         for (int i = 0; i < al.Count; i++)
184                                 if (((XPathNavigator) al [i]).IsSamePosition (target))
185                                         return;
186                         al.Add (target.Clone ());
187                 }
188
189                 private ArrayList GetNodesByValue (XPathNavigator nav, string value, XsltContext ctx)
190                 {
191                         if (map == null) {
192                                 mappedDocuments = new Hashtable ();
193                                 map = new Hashtable ();
194                         }
195                         if (!mappedDocuments.ContainsKey (nav.BaseURI)) {
196                                 mappedDocuments.Add (nav.BaseURI, nav.BaseURI);
197                                 CollectTable (nav, ctx);
198                         }
199                         
200                         return map [value] as ArrayList;
201                 }
202
203                 public bool Matches (XPathNavigator nav, string value, XsltContext ctx)
204                 {
205                         ArrayList al = GetNodesByValue (nav, value, ctx);
206                         if (al == null)
207                                 return false;
208                         for (int i = 0; i < al.Count; i++)
209                                 if (((XPathNavigator) al [i]).IsSamePosition (nav))
210                                         return true;
211                         return false;
212                 }
213
214                 // Invoked from XsltKey (XPathFunction)
215                 public BaseIterator Evaluate (BaseIterator iter,
216                         Expression valueExpr)
217                 {
218                         /*
219                         ArrayList result = new ArrayList ();
220                         object o = valueExpr.Evaluate (iter);
221                         XPathNodeIterator it = o as XPathNodeIterator;
222                         XsltContext ctx = iter.NamespaceManager as XsltContext;
223
224                         if (it != null) {
225                                 while (it.MoveNext())
226                                         FindKeyMatch (it.Current.Value, result, iter.Current, ctx);
227                         } else {
228                                 FindKeyMatch (XPathFunctions.ToString (o), result, iter.Current, ctx);
229                         }
230                         result.Sort (XPathNavigatorComparer.Instance);
231                         return new ListIterator (result, (ctx));
232                         */
233
234                         XPathNodeIterator i = iter;
235                         if (iter.CurrentPosition == 0) {
236                                 i = iter.Clone ();
237                                 i.MoveNext ();
238                         }
239                         XPathNavigator nav = i.Current;
240
241                         object o = valueExpr.Evaluate (iter);
242                         XPathNodeIterator it = o as XPathNodeIterator;
243                         XsltContext ctx = iter.NamespaceManager as XsltContext;
244
245                         BaseIterator result = null;
246
247                         if (it != null) {
248                                 while (it.MoveNext()) {
249                                         ArrayList nodes = GetNodesByValue (
250                                                 it.Current, it.Current.Value, ctx);
251                                         if (nodes == null)
252                                                 continue;
253                                         ListIterator tmp =
254                                                 new ListIterator (nodes, ctx);
255                                         if (result == null)
256                                                 result = tmp;
257                                         else
258                                                 result = new UnionIterator (
259                                                         iter, result, tmp);
260                                 }
261                         }
262                         else {
263                                 ArrayList nodes = GetNodesByValue (
264                                         nav, XPathFunctions.ToString (o), ctx);
265                                 if (nodes != null)
266                                         result = new ListIterator (nodes, ctx);
267                         }
268
269                         return result != null ? result : new NullIterator (iter);
270                 }
271                 
272                 /*
273                 void FindKeyMatch (string value, ArrayList result, XPathNavigator context, XsltContext xsltContext)
274                 {
275                         XPathNavigator searchDoc = context;//.Clone ();
276                         searchDoc.MoveToRoot ();
277                         if (key != null) {
278                                 XPathNodeIterator desc = searchDoc.SelectDescendants (XPathNodeType.All, true);
279
280                                 while (desc.MoveNext ()) {
281                                         if (Matches (desc.Current, value, xsltContext))
282                                                 AddResult (result, desc.Current);
283                                         
284                                         if (!desc.Current.MoveToFirstAttribute ())
285                                                 continue;
286                                         do {
287                                                 if (Matches (desc.Current, value, xsltContext))
288                                                         AddResult (result, desc.Current);       
289                                         } while (desc.Current.MoveToNextAttribute ());
290                                         
291                                         desc.Current.MoveToParent ();
292                                 }
293                         }
294                 }
295
296                 void AddResult (ArrayList result, XPathNavigator nav)
297                 {
298                         for (int i = 0; i < result.Count; i++) {
299                                 XmlNodeOrder docOrder = nav.ComparePosition (((XPathNavigator)result [i]));
300                                 if (docOrder == XmlNodeOrder.Same)
301                                         return;
302                                 
303                                 if (docOrder == XmlNodeOrder.Before) {
304                                         result.Insert(i, nav.Clone ());
305                                         return;
306                                 }
307                         }
308                         result.Add (nav.Clone ());
309                 }
310                 */
311         }
312 }