2003-07-31 Ben Maurer <bmaurer@users.sourceforge.net>
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl / XsltCompiledContext.cs
1 //
2 // XsltCompiledContext.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      
7 // (C) 2003 Ben Maurer
8 //
9
10 using System;
11 using System.CodeDom;
12 using System.Collections;
13 using System.Collections.Specialized;
14 using System.Xml;
15 using System.Xml.Schema;
16 using System.Xml.XPath;
17 using System.Xml.Xsl;
18 using System.IO;
19 using Mono.Xml.Xsl.Functions;
20 using Mono.Xml.Xsl.Operations;
21 using System.Reflection;
22 using BF = System.Reflection.BindingFlags;
23
24 using QName = System.Xml.XmlQualifiedName;
25
26
27 namespace Mono.Xml.Xsl {
28
29         internal class XsltCompiledContext : XsltContext {
30                 protected static Hashtable xsltFunctions = new Hashtable ();
31
32                 static XsltCompiledContext ()
33                 {
34                         xsltFunctions.Add ("current", new XsltCurrent ());
35                         xsltFunctions.Add ("document", new XsltDocument ());
36                         xsltFunctions.Add ("element-available", new XsltElementAvailable ());
37                         xsltFunctions.Add ("format-number", new XsltFormatNumber ());
38                         xsltFunctions.Add ("function-available", new XsltFunctionAvailable ());
39                         xsltFunctions.Add ("generate-id", new XsltGenerateId ());
40                         xsltFunctions.Add ("key", new XsltKey ());
41                         xsltFunctions.Add ("system-property", new XsltSystemProperty ());
42                         xsltFunctions.Add ("unparsed-entity-uri", new XsltUnparsedEntityUri ());
43                 }
44                         
45                 XslTransformProcessor p;
46                 VariableScope v;
47                         
48                 public XslTransformProcessor Processor { get { return p; }}
49                         
50                 public XsltCompiledContext (XslTransformProcessor p, VariableScope v)
51                 {
52                         this.p = p;
53                         this.v = v;
54                 }
55
56                 public override string DefaultNamespace { get { return String.Empty; }}
57
58
59                 public override string LookupNamespace (string prefix)
60                 {
61                         if (prefix == "" || prefix == null)
62                                 return "";
63                         
64                         return p.CompiledStyle.NamespaceManager.LookupNamespace (prefix);
65                 }
66                 
67                 public override IXsltContextFunction ResolveFunction (string prefix, string name, XPathResultType[] argTypes)
68                 {
69                         IXsltContextFunction func = null;
70                         if (prefix == String.Empty || prefix == null) {
71                                 return xsltFunctions [name] as IXsltContextFunction;
72                         } else {
73                                 string ns = this.LookupNamespace (prefix);
74
75                                 if (ns == null || p.Arguments == null) return null;
76
77                                 object extension = p.Arguments.GetExtensionObject (ns);
78                                         
79                                 if (extension == null)
80                                         return null;                    
81                                 
82                                 MethodInfo method = FindBestMethod (extension.GetType (), name, argTypes);
83                                 
84                                 if (method != null) 
85                                         return new XsltExtensionFunction (extension, method);
86                                 return null;
87                                 
88                         }
89                 }
90                 
91                 MethodInfo FindBestMethod (Type t, string name, XPathResultType [] argTypes)
92                 {
93                         int free, length;
94                         
95                         MethodInfo [] mi = t.GetMethods (BF.Public | BF.Instance | BF.Static);
96                         if (mi.Length == 0)
97                                 return null;
98
99
100                         free = 0;
101                         // filter on name + num args
102                         int numArgs = argTypes == null ? 0 : argTypes.Length;
103                         for (int i = 0; i < mi.Length; i ++) {
104                                 if (mi [i].Name == name && mi [i].GetParameters ().Length == numArgs) 
105                                         mi [free++] = mi [i];
106                         }
107                         length = free;
108                         
109                         // No method
110                         if (length == 0)
111                                 return null;
112                         
113                         // Thats it!
114                         if (length == 1)
115                                 return mi [0];
116                         
117                         free = 0;
118                         for (int i = 0; i < length; i ++) {
119                                 bool match = true;
120                                 ParameterInfo [] pi = mi [i].GetParameters ();
121                                 
122                                 for (int par = 0; par < pi.Length; par++) {
123                                         XPathResultType required = argTypes [par];
124                                         if (required == XPathResultType.Any)
125                                                 continue; // dunno what it is
126                                         
127                                         XPathResultType actual = XPFuncImpl.GetXPathType (pi [par].ParameterType);
128                                         if (actual != required && actual != XPathResultType.Any) {
129                                                 match = false;
130                                                 break;
131                                         }
132                                         
133                                         if (actual == XPathResultType.Any) {
134                                                 // try to get a stronger gind
135                                                 if (required != XPathResultType.NodeSet && !(pi [par].ParameterType == typeof (object)))
136                                                 {
137                                                         match = false;
138                                                         break;
139                                                 }
140                                         }
141                                 }
142                                 if (match) return mi [i]; // TODO look for exact match
143                         }
144                         return null;
145                 }
146                         
147
148                 public override System.Xml.Xsl.IXsltContextVariable ResolveVariable(string prefix, string name)
149                 {
150                         if (v != null) {
151                                 XslGeneralVariable var = v.Resolve (p, new QName (name));
152         
153                                 if (var != null)
154                                         return var;
155                         }
156                         return p.CompiledStyle.ResolveVariable (new QName (name));
157                 }
158
159                 public override int CompareDocument (string baseUri, string nextBaseUri) { throw new NotImplementedException (); }
160                 public override bool PreserveWhitespace (XPathNavigator nav) { throw new NotImplementedException (); }
161                 public override bool Whitespace { get { throw new NotImplementedException (); }}
162         }
163
164
165 }
166 namespace Mono.Xml.Xsl.Functions {
167
168         internal abstract class XPFuncImpl : IXsltContextFunction {
169                 int minargs, maxargs;
170                 XPathResultType returnType;
171                 XPathResultType [] argTypes;
172
173                 public XPFuncImpl () {}
174                 public XPFuncImpl (int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
175                 {
176                         this.Init(minArgs, maxArgs, returnType, argTypes);
177                 }
178                 
179                 protected void Init (int minArgs, int maxArgs, XPathResultType returnType, XPathResultType[] argTypes)
180                 {
181                         this.minargs    = minArgs;
182                         this.maxargs    = maxArgs;
183                         this.returnType = returnType;
184                         this.argTypes   = argTypes;
185                 }
186
187                 public int Minargs { get { return this.minargs; }}
188                 public int Maxargs { get { return this.maxargs; }}
189                 public XPathResultType ReturnType { get { return this.returnType; }}
190                 public XPathResultType [] ArgTypes { get { return this.argTypes; }}
191                 public object Invoke (XsltContext xsltContext, object [] args, XPathNavigator docContext)
192                 {
193                         return Invoke ((XsltCompiledContext)xsltContext, args, docContext);
194                 }
195                 
196                 public abstract object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext);
197                 
198                 public static XPathResultType GetXPathType (Type type) {
199                         switch (Type.GetTypeCode(type)) {
200                         case TypeCode.String:
201                                 return XPathResultType.String;
202                         case TypeCode.Boolean:
203                                 return XPathResultType.Boolean;
204                         case TypeCode.Object:
205                                 if (typeof (XPathNavigator).IsAssignableFrom (type) || typeof (IXPathNavigable).IsAssignableFrom (type))
206                                         return XPathResultType.Navigator;
207                                 
208                                 if (typeof (XPathNodeIterator).IsAssignableFrom (type))
209                                         return XPathResultType.NodeSet;
210                                 
211                                 return XPathResultType.Any;
212                         case TypeCode.DateTime :
213                                 throw new Exception ();
214                         default: // Numeric
215                                 return XPathResultType.Number;
216                         } 
217                 }
218         }
219         
220         class XsltExtensionFunction : XPFuncImpl {
221                 private object extension;
222                 private MethodInfo method;
223                 private TypeCode [] typeCodes;
224
225                 public XsltExtensionFunction (object extension, MethodInfo method)
226                 {
227                         this.extension = extension;
228                         this.method = method;
229
230                         ParameterInfo [] parameters = method.GetParameters ();
231                         int minArgs = parameters.Length;
232                         int maxArgs = parameters.Length;
233                         
234                         this.typeCodes = new TypeCode [parameters.Length];
235                         XPathResultType[] argTypes = new XPathResultType [parameters.Length];
236                         
237                         bool canBeOpt = true;
238                         for (int i = parameters.Length - 1; 0 <= i; i--) { // optionals at the end
239                                 typeCodes [i] = Type.GetTypeCode (parameters [i].ParameterType);
240                                 argTypes [i] = GetXPathType (parameters [i].ParameterType);
241                                 if (canBeOpt) {
242                                         if (parameters[i].IsOptional)
243                                                 minArgs --;
244                                         else
245                                                 canBeOpt = false;
246                                 }
247                         }
248                         base.Init (minArgs, maxArgs, GetXPathType (method.ReturnType), argTypes);
249                 }
250
251                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
252                 {
253                         try {
254                                 object result = method.Invoke(extension, args);
255                                 IXPathNavigable navigable = result as IXPathNavigable;
256                                 if (navigable != null)
257                                         return navigable.CreateNavigator ();
258
259                                 return result;
260                         } catch {
261                                 Debug.WriteLine ("****** INCORRECT RESOLUTION **********");
262                                 return "";
263                         }
264                 }
265         }
266                 
267         class XsltCurrent : XPFuncImpl {
268                 public XsltCurrent () : base (0, 0, XPathResultType.NodeSet, null) {}
269                 
270                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
271                 {
272                         return new SelfIterator (xsltContext.Processor.CurrentNode, null);
273                 }
274         }
275         
276         class XsltDocument : XPFuncImpl {
277                 public XsltDocument () : base (1, 2, XPathResultType.NodeSet, new XPathResultType [] { XPathResultType.Any, XPathResultType.NodeSet }) {}
278                 
279                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
280                 {
281                         throw new NotImplementedException ();
282                 }
283         }
284         
285         class XsltElementAvailable : XPFuncImpl {
286                 public XsltElementAvailable () : base (1, 1, XPathResultType.Boolean, new XPathResultType [] { XPathResultType.String }) {}
287                 
288                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
289                 {
290                         throw new NotImplementedException ();
291                 }
292         }
293
294         class XsltFormatNumber : XPFuncImpl {
295                 public XsltFormatNumber () : base (2, 3, XPathResultType.String , new XPathResultType [] { XPathResultType.Number, XPathResultType.String, XPathResultType.String }) {}
296                 
297                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
298                 {
299                         throw new NotImplementedException ();
300                 }
301         }
302         
303         class XsltFunctionAvailable : XPFuncImpl {
304                 public XsltFunctionAvailable () : base (1, 1, XPathResultType.Boolean, new XPathResultType [] { XPathResultType.String }) {}
305                 
306                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
307                 {
308                         throw new NotImplementedException ();
309                 }
310         } 
311
312         class XsltGenerateId : XPFuncImpl {
313                 public XsltGenerateId () : base (0, 1, XPathResultType.String , new XPathResultType [] { XPathResultType.NodeSet }) {}
314                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
315                 {
316                         throw new NotImplementedException ();
317                 }
318         } 
319         
320         class XsltKey : XPFuncImpl {
321                 public XsltKey () : base (2, 2, XPathResultType.NodeSet, new XPathResultType [] { XPathResultType.String, XPathResultType.Any }) {}
322                 
323                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
324                 {
325                         throw new NotImplementedException ();
326                 }
327         }
328         
329         class XsltSystemProperty : XPFuncImpl {
330                 public XsltSystemProperty () : base (1, 1, XPathResultType.String , new XPathResultType [] { XPathResultType.String }) {}
331                 
332                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
333                 {
334                         throw new NotImplementedException ();
335                 }
336         } 
337
338         class XsltUnparsedEntityUri : XPFuncImpl {
339                 public XsltUnparsedEntityUri () : base (1, 1, XPathResultType.String , new XPathResultType [] { XPathResultType.String }) {}
340                 
341                 public override object Invoke (XsltCompiledContext xsltContext, object [] args, XPathNavigator docContext)
342                 {
343                         throw new NotImplementedException ();
344                 }
345         }
346 }