In .:
[mono.git] / mcs / class / Mono.Xml.Ext / Mono.Xml.XPath2 / XQueryCliFunction.cs
1 //\r
2 // XQueryCliFunction.cs\r
3 //\r
4 // Author:\r
5 //      Atsushi Enomoto <atsushi@ximian.com>\r
6 //\r
7 //\r
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
9 //\r
10 // Permission is hereby granted, free of charge, to any person obtaining\r
11 // a copy of this software and associated documentation files (the\r
12 // "Software"), to deal in the Software without restriction, including\r
13 // without limitation the rights to use, copy, modify, merge, publish,\r
14 // distribute, sublicense, and/or sell copies of the Software, and to\r
15 // permit persons to whom the Software is furnished to do so, subject to\r
16 // the following conditions:\r
17 // \r
18 // The above copyright notice and this permission notice shall be\r
19 // included in all copies or substantial portions of the Software.\r
20 // \r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
28 //\r
29 \r
30 //\r
31 // Runtime type method wrapper for XPath2 function.\r
32 //\r
33 #if NET_2_0\r
34 using System;\r
35 using System.Collections;\r
36 using System.Reflection;\r
37 using System.Security;\r
38 using System.Security.Policy;\r
39 using System.Xml;\r
40 using System.Xml.Schema;\r
41 using System.Xml.Query;\r
42 using System.Xml.XPath;\r
43 using System.Xml.Xsl;\r
44 using Mono.Xml;\r
45 \r
46 namespace Mono.Xml.XPath2\r
47 {\r
48 \r
49         // Ideas:\r
50         // declare function namespace cli = "http://mono-project.com/xquery/function/cli"\r
51         // declare variable v = cli:invoke (cli:new (Microsoft.CSharp:CSharpCodeProvider), CreateCompiler);\r
52         // declare variable v2 = System.Math:Abs (0.5);\r
53         //\r
54 \r
55         public class XQueryCliFunction : XQueryFunction\r
56         {\r
57                 internal static XQueryCliFunction CreateFromMethodInfo (MethodInfo mi)\r
58                 {\r
59                         return CreateFromMethodInfo (null, mi);\r
60                 }\r
61 \r
62                 internal static XQueryCliFunction CreateFromMethodInfo (XmlQualifiedName name, MethodInfo mi)\r
63                 {\r
64                         return CreateFromMethodInfo (name, new MethodInfo [] {mi});\r
65                 }\r
66 \r
67                 internal static XQueryCliFunction CreateFromMethodInfo (MethodInfo [] methods)\r
68                 {\r
69                         return CreateFromMethodInfo (null, methods);\r
70                 }\r
71 \r
72                 internal static XQueryCliFunction CreateFromMethodInfo (XmlQualifiedName name, MethodInfo [] methodList)\r
73                 {\r
74                         if (methodList == null || methodList.Length == 0)\r
75                                 throw new ArgumentException (String.Format ("Argument methods is missing or zero-length array. Name is {0}", name));\r
76 \r
77                         Type cliReturnType = null;\r
78                         ArrayList arguments = new ArrayList ();\r
79 \r
80                         if (name == null || name == XmlQualifiedName.Empty)\r
81                                 name = new XmlQualifiedName (methodList [0].Name, methodList [0].DeclaringType.FullName);\r
82 \r
83                         int maxArgs = 0;\r
84                         int minArgs = -1;\r
85                         Hashtable methods = new Hashtable ();\r
86 \r
87                         foreach (MethodInfo mi in methodList) {\r
88                                 if (cliReturnType == null)\r
89                                         cliReturnType = mi.ReturnType;\r
90                                 else if (mi.ReturnType != cliReturnType)\r
91                                         throw new ArgumentException (String.Format ("Every XQuery functions which share the same name must have the same return type. Method name is {0}.", mi.Name));\r
92                                 ParameterInfo [] prms = mi.GetParameters ();\r
93 \r
94                                 int args = prms.Length;\r
95 \r
96                                 // Whether it takes "current context" or not.\r
97                                 Type t = args > 0 ? prms [0].ParameterType : null;\r
98                                 bool ctxSeq = mi.GetCustomAttributes (typeof (XQueryFunctionContextAttribute), false).Length > 0;\r
99                                 bool hasContextArg = ctxSeq || t == typeof (XQueryContext);\r
100                                 if (ctxSeq || hasContextArg)\r
101                                         args--;\r
102                                 if (methods [args] != null)\r
103                                         throw new ArgumentException (String.Format ("XQuery does not allow functions that accepts such methods that have the same number of parameters in different types. Method name is {0}", mi.Name));\r
104                                 methods.Add ((int) args, mi);\r
105                                 if (args < minArgs || minArgs < 0)\r
106                                         minArgs = args;\r
107                                 if (args > maxArgs)\r
108                                         maxArgs = args;\r
109                         }\r
110 \r
111                         MethodInfo m = (MethodInfo) methods [(int) maxArgs];\r
112                         if (m == null)\r
113                                 throw new SystemException ("Should not happen: maxArgs is " + maxArgs);\r
114                         ParameterInfo [] pl = m.GetParameters ();\r
115                         for (int i = 0; i < pl.Length; i++) {\r
116                                 Type t = pl [i].ParameterType;\r
117                                 if (t != typeof (XQueryContext))\r
118                                         arguments.Add (\r
119                                                 new XQueryFunctionArgument (new XmlQualifiedName (pl [i].Name), SequenceType.Create (pl [i].ParameterType)));\r
120                         }\r
121 \r
122                         return new XQueryCliFunction (name,\r
123                                 arguments.ToArray (typeof (XQueryFunctionArgument)) as XQueryFunctionArgument [],\r
124                                 SequenceType.Create (cliReturnType),\r
125                                 methods,\r
126                                 minArgs,\r
127                                 maxArgs);\r
128                 }\r
129 \r
130                 private XQueryCliFunction (XmlQualifiedName name, \r
131                         XQueryFunctionArgument [] args,\r
132                         SequenceType returnType,\r
133                         Hashtable methods,\r
134                         int minArgs,\r
135                         int maxArgs)\r
136                         : base (name, args, returnType)\r
137                 {\r
138                         this.methods = methods;\r
139                         this.maxArgs = maxArgs;\r
140                         this.minArgs = minArgs;\r
141                 }\r
142 \r
143                 // instance members\r
144 \r
145                 // [int argsize] -> MethodInfo (according to the spec 1.1,\r
146                 // there should be no overloads that accepts the same parameter\r
147                 // count in different types).\r
148                 Hashtable methods = new Hashtable ();\r
149                 int maxArgs = 0;\r
150                 int minArgs = -1;\r
151                 SequenceType returnType;\r
152 \r
153                 public override int MinArgs {\r
154                         get { return minArgs; }\r
155                 }\r
156 \r
157                 public override int MaxArgs {\r
158                         get { return maxArgs; }\r
159                 }\r
160 \r
161                 public override object Invoke (XPathSequence current, object [] args)\r
162                 {\r
163                         MethodInfo mi = methods [args.Length] as MethodInfo;\r
164                         if (mi == null)\r
165                                 throw new ArgumentException ("The number of custom function parameter does not match with the registered method's signature.");\r
166                         ParameterInfo [] prms = mi.GetParameters ();\r
167 \r
168                         // Use Evidence and PermissionSet.Demand() here\r
169                         // before invoking external function.\r
170                         Evidence e = current.Context.StaticContext.Evidence;\r
171                         if (e != null)\r
172                                 SecurityManager.ResolvePolicy (e).Demand ();\r
173 \r
174                         Type t = prms.Length > 0 ? prms [0].ParameterType : null;\r
175                         bool ctxSeq = mi.GetCustomAttributes (\r
176                                 typeof (XQueryFunctionContextAttribute),\r
177                                 false).Length > 0;\r
178                         if (t == typeof (XQueryContext)) {\r
179                                 ArrayList pl = new ArrayList (args);\r
180                                 pl.Insert (0, current.Context);\r
181                                 args = pl.ToArray ();\r
182                         }\r
183                         else if (ctxSeq) {\r
184                                 ArrayList pl = new ArrayList (args);\r
185                                 pl.Insert (0, current);\r
186                                 args = pl.ToArray ();\r
187                         }\r
188 \r
189                         if (args.Length != prms.Length)\r
190                                 throw new XmlQueryException (String.Format ("Argument numbers were different for function {0}. Signature requires {1} while actual call was {2}.", mi.Name, prms.Length, args.Length));\r
191 \r
192                         // If native parameter type is XPathSequence and the actual values are not, adjust them\r
193                         for (int i = 0; i < args.Length; i++) {\r
194                                 if (prms [i].ParameterType == typeof (XPathSequence) && !(args [i] is XPathSequence)) {\r
195                                         XPathItem item = args [i] as XPathItem;\r
196                                         if (item == null)\r
197                                                 item = args [i] == null ? null : new XPathAtomicValue (args [i], InternalPool.GetBuiltInType (InternalPool.XmlTypeCodeFromRuntimeType (prms [i].ParameterType, true)));\r
198                                         if (item == null)\r
199                                                 args [i] = new XPathEmptySequence (current.Context);\r
200                                         else\r
201                                                 args [i] = new SingleItemIterator (item, current.Context);\r
202                                 }\r
203                         }\r
204 \r
205                         return mi.Invoke (null, args);\r
206                 }\r
207         }\r
208 \r
209 }\r
210 #endif\r