1 | /* |
---|
2 | XML-RPC.NET library |
---|
3 | Copyright (c) 2001-2006, Charles Cook <charlescook@cookcomputing.com> |
---|
4 | |
---|
5 | Permission is hereby granted, free of charge, to any person |
---|
6 | obtaining a copy of this software and associated documentation |
---|
7 | files (the "Software"), to deal in the Software without restriction, |
---|
8 | including without limitation the rights to use, copy, modify, merge, |
---|
9 | publish, distribute, sublicense, and/or sell copies of the Software, |
---|
10 | and to permit persons to whom the Software is furnished to do so, |
---|
11 | subject to the following conditions: |
---|
12 | |
---|
13 | The above copyright notice and this permission notice shall be |
---|
14 | included in all copies or substantial portions of the Software. |
---|
15 | |
---|
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
---|
18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
---|
20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
---|
21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
---|
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
23 | DEALINGS IN THE SOFTWARE. |
---|
24 | */ |
---|
25 | |
---|
26 | namespace CookComputing.XmlRpc |
---|
27 | { |
---|
28 | using System; |
---|
29 | using System.Collections; |
---|
30 | using System.Collections.Generic; |
---|
31 | using System.Reflection; |
---|
32 | using System.Text.RegularExpressions; |
---|
33 | |
---|
34 | public enum XmlRpcType |
---|
35 | { |
---|
36 | tInvalid, |
---|
37 | tInt32, |
---|
38 | tInt64, |
---|
39 | tBoolean, |
---|
40 | tString, |
---|
41 | tDouble, |
---|
42 | tDateTime, |
---|
43 | tBase64, |
---|
44 | tStruct, |
---|
45 | tHashtable, |
---|
46 | tArray, |
---|
47 | tMultiDimArray, |
---|
48 | tVoid |
---|
49 | } |
---|
50 | |
---|
51 | public class XmlRpcServiceInfo |
---|
52 | { |
---|
53 | public static XmlRpcServiceInfo CreateServiceInfo(Type type) |
---|
54 | { |
---|
55 | XmlRpcServiceInfo svcInfo = new XmlRpcServiceInfo(); |
---|
56 | // extract service info |
---|
57 | XmlRpcServiceAttribute svcAttr = (XmlRpcServiceAttribute) |
---|
58 | Attribute.GetCustomAttribute(type, typeof(XmlRpcServiceAttribute)); |
---|
59 | if (svcAttr != null && svcAttr.Description != "") |
---|
60 | svcInfo.doc = svcAttr.Description; |
---|
61 | if (svcAttr != null && svcAttr.Name != "") |
---|
62 | svcInfo.Name = svcAttr.Name; |
---|
63 | else |
---|
64 | svcInfo.Name = type.Name; |
---|
65 | // extract method info |
---|
66 | Hashtable methods = new Hashtable(); |
---|
67 | |
---|
68 | foreach (Type itf in type.GetInterfaces()) |
---|
69 | { |
---|
70 | XmlRpcServiceAttribute itfAttr = (XmlRpcServiceAttribute) |
---|
71 | Attribute.GetCustomAttribute(itf, typeof(XmlRpcServiceAttribute)); |
---|
72 | if (itfAttr != null) |
---|
73 | svcInfo.doc = itfAttr.Description; |
---|
74 | #if (!COMPACT_FRAMEWORK) |
---|
75 | InterfaceMapping imap = type.GetInterfaceMap(itf); |
---|
76 | foreach (MethodInfo mi in imap.InterfaceMethods) |
---|
77 | { |
---|
78 | ExtractMethodInfo(methods, mi, itf); |
---|
79 | } |
---|
80 | #else |
---|
81 | foreach (MethodInfo mi in itf.GetMethods()) |
---|
82 | { |
---|
83 | ExtractMethodInfo(methods, mi, itf); |
---|
84 | } |
---|
85 | #endif |
---|
86 | } |
---|
87 | |
---|
88 | foreach (MethodInfo mi in type.GetMethods()) |
---|
89 | { |
---|
90 | ArrayList mthds = new ArrayList(); |
---|
91 | mthds.Add(mi); |
---|
92 | MethodInfo curMi = mi; |
---|
93 | while (true) |
---|
94 | { |
---|
95 | MethodInfo baseMi = curMi.GetBaseDefinition(); |
---|
96 | if (baseMi.DeclaringType == curMi.DeclaringType) |
---|
97 | break; |
---|
98 | mthds.Insert(0, baseMi); |
---|
99 | curMi = baseMi; |
---|
100 | } |
---|
101 | foreach (MethodInfo mthd in mthds) |
---|
102 | { |
---|
103 | ExtractMethodInfo(methods, mthd, type); |
---|
104 | } |
---|
105 | } |
---|
106 | svcInfo.methodInfos = new XmlRpcMethodInfo[methods.Count]; |
---|
107 | methods.Values.CopyTo(svcInfo.methodInfos, 0); |
---|
108 | Array.Sort(svcInfo.methodInfos); |
---|
109 | return svcInfo; |
---|
110 | } |
---|
111 | |
---|
112 | static void ExtractMethodInfo(Hashtable methods, MethodInfo mi, Type type) |
---|
113 | { |
---|
114 | XmlRpcMethodAttribute attr = (XmlRpcMethodAttribute) |
---|
115 | Attribute.GetCustomAttribute(mi, |
---|
116 | typeof(XmlRpcMethodAttribute)); |
---|
117 | if (attr == null) |
---|
118 | return; |
---|
119 | XmlRpcMethodInfo mthdInfo = new XmlRpcMethodInfo(); |
---|
120 | mthdInfo.MethodInfo = mi; |
---|
121 | mthdInfo.XmlRpcName = GetXmlRpcMethodName(mi); |
---|
122 | mthdInfo.MiName = mi.Name; |
---|
123 | mthdInfo.Doc = attr.Description; |
---|
124 | mthdInfo.IsHidden = attr.IntrospectionMethod | attr.Hidden; |
---|
125 | // extract parameters information |
---|
126 | ArrayList parmList = new ArrayList(); |
---|
127 | ParameterInfo[] parms = mi.GetParameters(); |
---|
128 | foreach (ParameterInfo parm in parms) |
---|
129 | { |
---|
130 | XmlRpcParameterInfo parmInfo = new XmlRpcParameterInfo(); |
---|
131 | parmInfo.Name = parm.Name; |
---|
132 | parmInfo.Type = parm.ParameterType; |
---|
133 | parmInfo.XmlRpcType = GetXmlRpcTypeString(parm.ParameterType); |
---|
134 | // retrieve optional attributed info |
---|
135 | parmInfo.Doc = ""; |
---|
136 | XmlRpcParameterAttribute pattr = (XmlRpcParameterAttribute) |
---|
137 | Attribute.GetCustomAttribute(parm, |
---|
138 | typeof(XmlRpcParameterAttribute)); |
---|
139 | if (pattr != null) |
---|
140 | { |
---|
141 | parmInfo.Doc = pattr.Description; |
---|
142 | parmInfo.XmlRpcName = pattr.Name; |
---|
143 | } |
---|
144 | parmInfo.IsParams = Attribute.IsDefined(parm, |
---|
145 | typeof(ParamArrayAttribute)); |
---|
146 | parmList.Add(parmInfo); |
---|
147 | } |
---|
148 | mthdInfo.Parameters = (XmlRpcParameterInfo[]) |
---|
149 | parmList.ToArray(typeof(XmlRpcParameterInfo)); |
---|
150 | // extract return type information |
---|
151 | mthdInfo.ReturnType = mi.ReturnType; |
---|
152 | mthdInfo.ReturnXmlRpcType = GetXmlRpcTypeString(mi.ReturnType); |
---|
153 | object[] orattrs = mi.ReturnTypeCustomAttributes.GetCustomAttributes( |
---|
154 | typeof(XmlRpcReturnValueAttribute), false); |
---|
155 | if (orattrs.Length > 0) |
---|
156 | { |
---|
157 | mthdInfo.ReturnDoc = ((XmlRpcReturnValueAttribute)orattrs[0]).Description; |
---|
158 | } |
---|
159 | |
---|
160 | if (methods[mthdInfo.XmlRpcName] != null) |
---|
161 | { |
---|
162 | throw new XmlRpcDupXmlRpcMethodNames(String.Format("Method " |
---|
163 | + "{0} in type {1} has duplicate XmlRpc method name {2}", |
---|
164 | mi.Name, type.Name, mthdInfo.XmlRpcName)); |
---|
165 | } |
---|
166 | else |
---|
167 | methods.Add(mthdInfo.XmlRpcName, mthdInfo); |
---|
168 | } |
---|
169 | |
---|
170 | public MethodInfo GetMethodInfo(string xmlRpcMethodName) |
---|
171 | { |
---|
172 | foreach (XmlRpcMethodInfo xmi in methodInfos) |
---|
173 | { |
---|
174 | if (xmlRpcMethodName == xmi.XmlRpcName) |
---|
175 | { |
---|
176 | return xmi.MethodInfo; |
---|
177 | } |
---|
178 | } |
---|
179 | return null; |
---|
180 | } |
---|
181 | |
---|
182 | static bool IsVisibleXmlRpcMethod(MethodInfo mi) |
---|
183 | { |
---|
184 | bool ret = false; |
---|
185 | Attribute attr = Attribute.GetCustomAttribute(mi, |
---|
186 | typeof(XmlRpcMethodAttribute)); |
---|
187 | if (attr != null) |
---|
188 | { |
---|
189 | XmlRpcMethodAttribute mattr = (XmlRpcMethodAttribute)attr; |
---|
190 | ret = !(mattr.Hidden || mattr.IntrospectionMethod == true); |
---|
191 | } |
---|
192 | return ret; |
---|
193 | } |
---|
194 | |
---|
195 | public static string GetXmlRpcMethodName(MethodInfo mi) |
---|
196 | { |
---|
197 | XmlRpcMethodAttribute attr = (XmlRpcMethodAttribute) |
---|
198 | Attribute.GetCustomAttribute(mi, |
---|
199 | typeof(XmlRpcMethodAttribute)); |
---|
200 | if (attr != null |
---|
201 | && attr.Method != null |
---|
202 | && attr.Method != "") |
---|
203 | { |
---|
204 | return attr.Method; |
---|
205 | } |
---|
206 | else |
---|
207 | { |
---|
208 | return mi.Name; |
---|
209 | } |
---|
210 | } |
---|
211 | |
---|
212 | public string GetMethodName(string XmlRpcMethodName) |
---|
213 | { |
---|
214 | foreach (XmlRpcMethodInfo methodInfo in methodInfos) |
---|
215 | { |
---|
216 | if (methodInfo.XmlRpcName == XmlRpcMethodName) |
---|
217 | return methodInfo.MiName; |
---|
218 | } |
---|
219 | return null; |
---|
220 | } |
---|
221 | |
---|
222 | public String Doc |
---|
223 | { |
---|
224 | get { return doc; } |
---|
225 | set { doc = value; } |
---|
226 | } |
---|
227 | |
---|
228 | public String Name |
---|
229 | { |
---|
230 | get { return name; } |
---|
231 | set { name = value; } |
---|
232 | } |
---|
233 | |
---|
234 | public XmlRpcMethodInfo[] Methods |
---|
235 | { |
---|
236 | get { return methodInfos; } |
---|
237 | } |
---|
238 | |
---|
239 | public XmlRpcMethodInfo GetMethod( |
---|
240 | String methodName) |
---|
241 | { |
---|
242 | foreach (XmlRpcMethodInfo mthdInfo in methodInfos) |
---|
243 | { |
---|
244 | if (mthdInfo.XmlRpcName == methodName) |
---|
245 | return mthdInfo; |
---|
246 | } |
---|
247 | return null; |
---|
248 | } |
---|
249 | |
---|
250 | private XmlRpcServiceInfo() |
---|
251 | { |
---|
252 | } |
---|
253 | |
---|
254 | public static XmlRpcType GetXmlRpcType(Type t) |
---|
255 | { |
---|
256 | return GetXmlRpcType(t, new Stack()); |
---|
257 | } |
---|
258 | |
---|
259 | private static XmlRpcType GetXmlRpcType(Type t, Stack typeStack) |
---|
260 | { |
---|
261 | XmlRpcType ret; |
---|
262 | if (t == typeof(Int32)) |
---|
263 | ret = XmlRpcType.tInt32; |
---|
264 | else if (t == typeof(XmlRpcInt)) |
---|
265 | ret = XmlRpcType.tInt32; |
---|
266 | else if (t == typeof(Int64)) |
---|
267 | ret = XmlRpcType.tInt64; |
---|
268 | else if (t == typeof(Boolean)) |
---|
269 | ret = XmlRpcType.tBoolean; |
---|
270 | else if (t == typeof(XmlRpcBoolean)) |
---|
271 | ret = XmlRpcType.tBoolean; |
---|
272 | else if (t == typeof(String)) |
---|
273 | ret = XmlRpcType.tString; |
---|
274 | else if (t == typeof(Double)) |
---|
275 | ret = XmlRpcType.tDouble; |
---|
276 | else if (t == typeof(XmlRpcDouble)) |
---|
277 | ret = XmlRpcType.tDouble; |
---|
278 | else if (t == typeof(DateTime)) |
---|
279 | ret = XmlRpcType.tDateTime; |
---|
280 | else if (t == typeof(XmlRpcDateTime)) |
---|
281 | ret = XmlRpcType.tDateTime; |
---|
282 | else if (t == typeof(byte[])) |
---|
283 | ret = XmlRpcType.tBase64; |
---|
284 | else if (t == typeof(XmlRpcStruct)) |
---|
285 | { |
---|
286 | ret = XmlRpcType.tHashtable; |
---|
287 | } |
---|
288 | else if (t == typeof(Array)) |
---|
289 | ret = XmlRpcType.tArray; |
---|
290 | else if (t.IsArray) |
---|
291 | { |
---|
292 | #if (!COMPACT_FRAMEWORK) |
---|
293 | Type elemType = t.GetElementType(); |
---|
294 | if (elemType != typeof(Object) |
---|
295 | && GetXmlRpcType(elemType, typeStack) == XmlRpcType.tInvalid) |
---|
296 | { |
---|
297 | ret = XmlRpcType.tInvalid; |
---|
298 | } |
---|
299 | else |
---|
300 | { |
---|
301 | if (t.GetArrayRank() == 1) // single dim array |
---|
302 | ret = XmlRpcType.tArray; |
---|
303 | else |
---|
304 | ret = XmlRpcType.tMultiDimArray; |
---|
305 | } |
---|
306 | #else |
---|
307 | //!! check types of array elements if not Object[] |
---|
308 | Type elemType = null; |
---|
309 | string[] checkSingleDim = Regex.Split(t.FullName, "\\[\\]$"); |
---|
310 | if (checkSingleDim.Length > 1) // single dim array |
---|
311 | { |
---|
312 | elemType = Type.GetType(checkSingleDim[0]); |
---|
313 | ret = XmlRpcType.tArray; |
---|
314 | } |
---|
315 | else |
---|
316 | { |
---|
317 | string[] checkMultiDim = Regex.Split(t.FullName, "\\[,[,]*\\]$"); |
---|
318 | if (checkMultiDim.Length > 1) |
---|
319 | { |
---|
320 | elemType = Type.GetType(checkMultiDim[0]); |
---|
321 | ret = XmlRpcType.tMultiDimArray; |
---|
322 | } |
---|
323 | else |
---|
324 | ret = XmlRpcType.tInvalid; |
---|
325 | } |
---|
326 | if (elemType != null) |
---|
327 | { |
---|
328 | if (elemType != typeof(Object) |
---|
329 | && GetXmlRpcType(elemType, typeStack) == XmlRpcType.tInvalid) |
---|
330 | { |
---|
331 | ret = XmlRpcType.tInvalid; |
---|
332 | } |
---|
333 | } |
---|
334 | #endif |
---|
335 | |
---|
336 | } |
---|
337 | #if !FX1_0 |
---|
338 | else if (t == typeof(int?)) |
---|
339 | ret = XmlRpcType.tInt32; |
---|
340 | else if (t == typeof(long?)) |
---|
341 | ret = XmlRpcType.tInt64; |
---|
342 | else if (t == typeof(Boolean?)) |
---|
343 | ret = XmlRpcType.tBoolean; |
---|
344 | else if (t == typeof(Double?)) |
---|
345 | ret = XmlRpcType.tDouble; |
---|
346 | else if (t == typeof(DateTime?)) |
---|
347 | ret = XmlRpcType.tDateTime; |
---|
348 | #endif |
---|
349 | else if (t == typeof(void)) |
---|
350 | { |
---|
351 | ret = XmlRpcType.tVoid; |
---|
352 | } |
---|
353 | else if ((t.IsValueType && !t.IsPrimitive && !t.IsEnum) |
---|
354 | || t.IsClass) |
---|
355 | { |
---|
356 | // if type is struct or class its only valid for XML-RPC mapping if all |
---|
357 | // its members have a valid mapping or are of type object which |
---|
358 | // maps to any XML-RPC type |
---|
359 | MemberInfo[] mis = t.GetMembers(); |
---|
360 | foreach (MemberInfo mi in mis) |
---|
361 | { |
---|
362 | if (mi.MemberType == MemberTypes.Field) |
---|
363 | { |
---|
364 | FieldInfo fi = (FieldInfo)mi; |
---|
365 | if (typeStack.Contains(fi.FieldType)) |
---|
366 | continue; |
---|
367 | try |
---|
368 | { |
---|
369 | typeStack.Push(fi.FieldType); |
---|
370 | if ((fi.FieldType != typeof(Object) |
---|
371 | && GetXmlRpcType(fi.FieldType, typeStack) == XmlRpcType.tInvalid)) |
---|
372 | { |
---|
373 | return XmlRpcType.tInvalid; |
---|
374 | } |
---|
375 | } |
---|
376 | finally |
---|
377 | { |
---|
378 | typeStack.Pop(); |
---|
379 | } |
---|
380 | } |
---|
381 | else if (mi.MemberType == MemberTypes.Property) |
---|
382 | { |
---|
383 | PropertyInfo pi = (PropertyInfo)mi; |
---|
384 | if (typeStack.Contains(pi.PropertyType)) |
---|
385 | continue; |
---|
386 | try |
---|
387 | { |
---|
388 | typeStack.Push(pi.PropertyType); |
---|
389 | if ((pi.PropertyType != typeof(Object) |
---|
390 | && GetXmlRpcType(pi.PropertyType, typeStack) == XmlRpcType.tInvalid)) |
---|
391 | { |
---|
392 | return XmlRpcType.tInvalid; |
---|
393 | } |
---|
394 | } |
---|
395 | finally |
---|
396 | { |
---|
397 | typeStack.Pop(); |
---|
398 | } |
---|
399 | } |
---|
400 | } |
---|
401 | ret = XmlRpcType.tStruct; |
---|
402 | } |
---|
403 | else |
---|
404 | ret = XmlRpcType.tInvalid; |
---|
405 | return ret; |
---|
406 | } |
---|
407 | |
---|
408 | static public string GetXmlRpcTypeString(Type t) |
---|
409 | { |
---|
410 | XmlRpcType rpcType = GetXmlRpcType(t); |
---|
411 | return GetXmlRpcTypeString(rpcType); |
---|
412 | } |
---|
413 | |
---|
414 | static public string GetXmlRpcTypeString(XmlRpcType t) |
---|
415 | { |
---|
416 | string ret = null; |
---|
417 | if (t == XmlRpcType.tInt32) |
---|
418 | ret = "integer"; |
---|
419 | else if (t == XmlRpcType.tInt64) |
---|
420 | ret = "i8"; |
---|
421 | else if (t == XmlRpcType.tBoolean) |
---|
422 | ret = "boolean"; |
---|
423 | else if (t == XmlRpcType.tString) |
---|
424 | ret = "string"; |
---|
425 | else if (t == XmlRpcType.tDouble) |
---|
426 | ret = "double"; |
---|
427 | else if (t == XmlRpcType.tDateTime) |
---|
428 | ret = "dateTime"; |
---|
429 | else if (t == XmlRpcType.tBase64) |
---|
430 | ret = "base64"; |
---|
431 | else if (t == XmlRpcType.tStruct) |
---|
432 | ret = "struct"; |
---|
433 | else if (t == XmlRpcType.tHashtable) |
---|
434 | ret = "struct"; |
---|
435 | else if (t == XmlRpcType.tArray) |
---|
436 | ret = "array"; |
---|
437 | else if (t == XmlRpcType.tMultiDimArray) |
---|
438 | ret = "array"; |
---|
439 | else if (t == XmlRpcType.tVoid) |
---|
440 | ret = "void"; |
---|
441 | else |
---|
442 | ret = null; |
---|
443 | return ret; |
---|
444 | } |
---|
445 | |
---|
446 | XmlRpcMethodInfo[] methodInfos; |
---|
447 | String doc; |
---|
448 | string name; |
---|
449 | } |
---|
450 | } |
---|