2004-03-11 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
41 using QName = System.Xml.XmlQualifiedName;
42
43 namespace Mono.Xml.Xsl
44 {
45         internal class ExprKeyContainer : Expression
46         {
47                 Expression expr;
48                 public ExprKeyContainer (Expression expr)
49                 {
50                         this.expr = expr;
51                 }
52
53                 public Expression BodyExpression {
54                         get { return expr; }
55                 }
56
57                 public override object Evaluate (BaseIterator iter)
58                 {
59                         return expr.Evaluate (iter);
60                 }
61
62                 internal override XPathNodeType EvaluatedNodeType {\r
63                         get { return expr.EvaluatedNodeType; }\r
64                 }\r
65 \r
66                 public override XPathResultType ReturnType {\r
67                         get { return expr.ReturnType; }\r
68                 }
69
70                 public override bool RequireSorting {
71                         get { return true; }
72                 }
73         }
74
75         internal class XslKey
76         {
77                 QName name;
78                 CompiledExpression usePattern;
79                 CompiledExpression matchPattern;
80                 Hashtable map;
81                 Hashtable mappedDocuments;
82
83                 public XslKey (Compiler c)
84                 {
85                         this.name = c.ParseQNameAttribute ("name");
86
87                         c.KeyCompilationMode = true;
88                         usePattern = c.CompileExpression (c.GetAttribute ("use"));
89                         if (usePattern == null)
90                                 usePattern = c.CompileExpression (".");
91
92                         c.AssertAttribute ("match");
93                         string matchString = c.GetAttribute ("match");
94                         this.matchPattern = c.CompileExpression (matchString, true);
95                         c.KeyCompilationMode = false;
96                 }
97
98                 public QName Name { get { return name; }}
99                 internal CompiledExpression UsePattern { get { return usePattern; }}
100                 internal CompiledExpression MatchPattern { get { return matchPattern; }}
101
102                 internal void ClearKeyTable ()
103                 {
104                         if (map != null) {
105                                 map.Clear ();
106                                 map = null;
107                         }
108                         if (mappedDocuments != null) {
109                                 mappedDocuments.Clear ();
110                                 mappedDocuments = null;
111                         }
112                 }
113
114                 internal void CollectTable (XPathNavigator doc)
115                 {
116                         XPathNavigator nav = doc.Clone ();
117                         nav.MoveToRoot ();
118                         CollectRelativeMatchNodes (nav);
119                 }
120
121                 private void CollectAbsoluteMatchNodes (XPathNavigator nav)
122                 {
123                         XPathNodeIterator iter = nav.Select (MatchPattern);
124                         while (iter.MoveNext ())
125                                 CollectIndex (iter.Current);
126                 }
127
128                 private void CollectRelativeMatchNodes (XPathNavigator nav)
129                 {
130                         do {
131                                 if (nav.NodeType != XPathNodeType.Root)
132                                         while (!nav.MoveToNext ())
133                                                 if (!nav.MoveToParent ())
134                                                         // finished
135                                                         return;
136                                 do {
137                                         do {
138                                                 if (nav.Matches (MatchPattern))
139                                                         CollectIndex (nav);
140                                         } while (nav.MoveToFirstChild ());
141                                 } while (nav.MoveToNext ());
142                         } while (nav.MoveToParent ());
143                 }
144
145                 private void CollectIndex (XPathNavigator nav)
146                 {
147                         XPathNavigator target = nav.Clone ();
148                         XPathNodeIterator iter;
149                         switch (UsePattern.ReturnType) {
150                         case XPathResultType.NodeSet:
151                                 iter = nav.Select (UsePattern);
152                                 while (iter.MoveNext ())
153                                         AddIndex (iter.Current.Value, target);
154                                 break;
155                         case XPathResultType.Any:
156                                 object o = nav.Evaluate (UsePattern);
157                                 iter = o as XPathNodeIterator;
158                                 if (iter != null) {
159                                         while (iter.MoveNext ())
160                                                 AddIndex (iter.Current.Value, target);
161                                 }
162                                 else
163                                         AddIndex (nav.EvaluateString (UsePattern, null, null), target);
164                                 break;
165                         default:
166                                 string key = nav.EvaluateString (UsePattern, null, null);
167                                 AddIndex (key, target);
168                                 break;
169                         }
170                 }
171
172                 private void AddIndex (string key, XPathNavigator target)
173                 {
174                         ArrayList al = map [key] as ArrayList;
175                         if (al == null) {
176                                 al = new ArrayList ();
177                                 map [key] = al;
178                         }
179                         al.Add (target);
180                 }
181
182                 public bool Matches (XPathNavigator nav, XmlNamespaceManager nsmgr, string value)
183                 {
184                         if (map == null) {
185                                 mappedDocuments = new Hashtable ();
186                                 map = new Hashtable ();
187                         }
188                         if (mappedDocuments [nav.BaseURI] == null) {
189                                 mappedDocuments.Add (nav.BaseURI, nav.BaseURI);
190                                 MatchPattern.SetContext (nsmgr);
191                                 UsePattern.SetContext (nsmgr);
192                                 CollectTable (nav);
193                                 MatchPattern.SetContext (null);
194                                 UsePattern.SetContext (null);
195                         }
196                         
197                         ArrayList al = map [value] as ArrayList;
198                         if (al == null)
199                                 return false;
200                         for (int i = 0; i < al.Count; i++)
201                                 if (((XPathNavigator) al [i]).IsSamePosition (nav))
202                                         return true;
203                         return false;
204                 }
205         }
206 }