System.Drawing: added email to icon and test file headers
[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
72         internal class XslKey
73         {
74                 QName name;
75                 CompiledExpression useExpr;
76                 Pattern matchPattern;
77
78                 public XslKey (Compiler c)
79                 {
80                         this.name = c.ParseQNameAttribute ("name");
81
82                         c.KeyCompilationMode = true;
83                         useExpr = c.CompileExpression (c.GetAttribute ("use"));
84                         if (useExpr == null)
85                                 useExpr = c.CompileExpression (".");
86
87                         c.AssertAttribute ("match");
88                         string matchString = c.GetAttribute ("match");
89                         this.matchPattern = c.CompilePattern (matchString, c.Input);
90                         c.KeyCompilationMode = false;
91                 }
92
93                 public QName Name { get { return name; }}
94                 internal CompiledExpression Use { get { return useExpr; }}
95                 internal Pattern Match { get { return matchPattern; }}
96         }
97
98         // represents part of dynamic context that holds index table for a key
99         internal class KeyIndexTable
100         {
101                 XsltCompiledContext ctx;
102                 ArrayList keys;
103                 Hashtable mappedDocuments;
104
105                 public KeyIndexTable (XsltCompiledContext ctx, ArrayList keys)
106                 {
107                         this.ctx = ctx;
108                         this.keys = keys;
109                 }
110
111                 public ArrayList Keys {
112                         get { return keys; }
113                 }
114
115                 private void CollectTable (XPathNavigator doc, XsltContext ctx, Hashtable map)
116                 {
117                         for (int i = 0; i < keys.Count; i++)
118                                 CollectTable (doc, ctx, map, (XslKey) keys[i]);
119                 }
120
121                 private void CollectTable (XPathNavigator doc, XsltContext ctx, Hashtable map, XslKey key)
122                 {
123                         XPathNavigator nav = doc.Clone ();
124                         nav.MoveToRoot ();
125                         XPathNavigator tmp = doc.Clone ();
126
127                         bool matchesAttributes = false;
128                         switch (key.Match.EvaluatedNodeType) {
129                         case XPathNodeType.All:
130                         case XPathNodeType.Attribute:
131                                 matchesAttributes = true;
132                                 break;
133                         }
134
135                         do {
136                                 if (key.Match.Matches (nav, ctx)) {
137                                         tmp.MoveTo (nav);
138                                         CollectIndex (nav, tmp, map);
139                                 }
140                         } while (MoveNavigatorToNext (nav, matchesAttributes));
141                         if (map != null)
142                                 foreach (ArrayList list in map.Values)
143                                         list.Sort (XPathNavigatorComparer.Instance);
144                 }
145
146                 private bool MoveNavigatorToNext (XPathNavigator nav, bool matchesAttributes)
147                 {
148                         if (matchesAttributes) {
149                                 if (nav.NodeType != XPathNodeType.Attribute &&
150                                         nav.MoveToFirstAttribute ())
151                                         return true;
152                                 else if (nav.NodeType == XPathNodeType.Attribute) {
153                                         if (nav.MoveToNextAttribute ())
154                                                 return true;
155                                         nav.MoveToParent ();
156                                 }
157                         }
158                         if (nav.MoveToFirstChild ())
159                                 return true;
160                         do {
161                                 if (nav.MoveToNext ())
162                                         return true;
163                         } while (nav.MoveToParent ());
164                         return false;
165                 }
166
167                 private void CollectIndex (XPathNavigator nav, XPathNavigator target, Hashtable map)
168                 {
169                         for (int i = 0; i < keys.Count; i++)
170                                 CollectIndex (nav, target, map, (XslKey) keys[i]);
171                 }
172
173                 private void CollectIndex (XPathNavigator nav, XPathNavigator target, Hashtable map, XslKey key)
174                 {
175                         XPathNodeIterator iter;
176                         switch (key.Use.ReturnType) {
177                         case XPathResultType.NodeSet:
178                                 iter = nav.Select (key.Use);
179                                 while (iter.MoveNext ())
180                                         AddIndex (iter.Current.Value, target, map);
181                                 break;
182                         case XPathResultType.Any:
183                                 object o = nav.Evaluate (key.Use);
184                                 iter = o as XPathNodeIterator;
185                                 if (iter != null) {
186                                         while (iter.MoveNext ())
187                                                 AddIndex (iter.Current.Value, target, map);
188                                 }
189                                 else
190                                         AddIndex (XPathFunctions.ToString (o), target, map);
191                                 break;
192                         default:
193                                 string keyValue = nav.EvaluateString (key.Use, null, null);
194                                 AddIndex (keyValue, target, map);
195                                 break;
196                         }
197                 }
198
199                 private void AddIndex (string key, XPathNavigator target, Hashtable map)
200                 {
201                         ArrayList al = map [key] as ArrayList;
202                         if (al == null) {
203                                 al = new ArrayList ();
204                                 map [key] = al;
205                         }
206                         for (int i = 0; i < al.Count; i++)
207                                 if (((XPathNavigator) al [i]).IsSamePosition (target))
208                                         return;
209                         al.Add (target.Clone ());
210                 }
211
212                 private ArrayList GetNodesByValue (XPathNavigator nav, string value, XsltContext ctx)
213                 {
214                         if (mappedDocuments == null)
215                                 mappedDocuments = new Hashtable ();
216                         Hashtable map = (Hashtable) mappedDocuments [nav.BaseURI];
217                         if (map == null) {
218                                 map = new Hashtable ();
219                                 mappedDocuments.Add (nav.BaseURI, map);
220                                 CollectTable (nav, ctx, map);
221                         }
222                         
223                         return map [value] as ArrayList;
224                 }
225
226                 public bool Matches (XPathNavigator nav, string value, XsltContext ctx)
227                 {
228                         ArrayList al = GetNodesByValue (nav, value, ctx);
229                         if (al == null)
230                                 return false;
231                         for (int i = 0; i < al.Count; i++)
232                                 if (((XPathNavigator) al [i]).IsSamePosition (nav))
233                                         return true;
234                         return false;
235                 }
236
237                 // Invoked from XsltKey (XPathFunction)
238                 public BaseIterator Evaluate (BaseIterator iter,
239                         Expression valueExpr)
240                 {
241                         XPathNodeIterator i = iter;
242                         if (iter.CurrentPosition == 0) {
243                                 i = iter.Clone ();
244                                 i.MoveNext ();
245                         }
246                         XPathNavigator nav = i.Current;
247
248                         object o = valueExpr.Evaluate (iter);
249                         XPathNodeIterator it = o as XPathNodeIterator;
250                         XsltContext ctx = iter.NamespaceManager as XsltContext;
251
252                         BaseIterator result = null;
253
254                         if (it != null) {
255                                 while (it.MoveNext()) {
256                                         ArrayList nodes = GetNodesByValue (
257                                                 nav, it.Current.Value, ctx);
258                                         if (nodes == null)
259                                                 continue;
260                                         ListIterator tmp =
261                                                 new ListIterator (nodes, ctx);
262                                         if (result == null)
263                                                 result = tmp;
264                                         else
265                                                 result = new UnionIterator (
266                                                         iter, result, tmp);
267                                 }
268                         }
269                         else if (nav != null) {
270                                 ArrayList nodes = GetNodesByValue (
271                                         nav, XPathFunctions.ToString (o), ctx);
272                                 if (nodes != null)
273                                         result = new ListIterator (nodes, ctx);
274                         }
275
276                         return result != null ? result : new NullIterator (iter);
277                 }
278         }
279 }