2005-03-14 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                 private void CollectTable (XPathNavigator doc)
115                 {
116                         XPathNavigator nav = doc.Clone ();
117                         nav.MoveToRoot ();
118
119                         do {
120                                 if (nav.Matches (MatchPattern))
121                                         CollectIndex (nav);
122                         } while (MoveNavigatorToNext (nav));
123                 }
124
125                 private bool MoveNavigatorToNext (XPathNavigator nav)
126                 {
127                         if (nav.MoveToFirstChild ())
128                                 return true;
129                         do {
130                                 if (nav.MoveToNext ())
131                                         return true;
132                         } while (nav.MoveToParent ());
133                         return false;
134                 }
135
136                 private void CollectIndex (XPathNavigator nav)
137                 {
138                         XPathNavigator target = nav.Clone ();
139                         XPathNodeIterator iter;
140                         switch (UsePattern.ReturnType) {
141                         case XPathResultType.NodeSet:
142                                 iter = nav.Select (UsePattern);
143                                 while (iter.MoveNext ())
144                                         AddIndex (iter.Current.Value, target);
145                                 break;
146                         case XPathResultType.Any:
147                                 object o = nav.Evaluate (UsePattern);
148                                 iter = o as XPathNodeIterator;
149                                 if (iter != null) {
150                                         while (iter.MoveNext ())
151                                                 AddIndex (iter.Current.Value, target);
152                                 }
153                                 else
154                                         AddIndex (nav.EvaluateString (UsePattern, null, null), target);
155                                 break;
156                         default:
157                                 string key = nav.EvaluateString (UsePattern, null, null);
158                                 AddIndex (key, target);
159                                 break;
160                         }
161                 }
162
163                 private void AddIndex (string key, XPathNavigator target)
164                 {
165                         ArrayList al = map [key] as ArrayList;
166                         if (al == null) {
167                                 al = new ArrayList ();
168                                 map [key] = al;
169                         }
170                         al.Add (target);
171                 }
172
173                 public bool Matches (XPathNavigator nav, XmlNamespaceManager nsmgr, string value)
174                 {
175                         if (map == null) {
176                                 mappedDocuments = new Hashtable ();
177                                 map = new Hashtable ();
178                         }
179                         if (!mappedDocuments.ContainsKey (nav.BaseURI)) {
180                                 mappedDocuments.Add (nav.BaseURI, nav.BaseURI);
181                                 MatchPattern.SetContext (nsmgr);
182                                 UsePattern.SetContext (nsmgr);
183                                 CollectTable (nav);
184                                 MatchPattern.SetContext (null);
185                                 UsePattern.SetContext (null);
186                         }
187                         
188                         ArrayList al = map [value] as ArrayList;
189                         if (al == null)
190                                 return false;
191                         for (int i = 0; i < al.Count; i++)
192                                 if (((XPathNavigator) al [i]).IsSamePosition (nav))
193                                         return true;
194                         return false;
195                 }
196         }
197 }