source: xmlrpcnet/trunk/fuentes/src/XmlRpcSerializer.cs @ 379

Last change on this file since 379 was 379, checked in by hectorgh, 5 years ago

adding project files

File size: 65.2 KB
Line 
1/*
2XML-RPC.NET library
3Copyright (c) 2001-2006, Charles Cook <charlescook@cookcomputing.com>
4
5Permission is hereby granted, free of charge, to any person
6obtaining a copy of this software and associated documentation
7files (the "Software"), to deal in the Software without restriction,
8including without limitation the rights to use, copy, modify, merge,
9publish, distribute, sublicense, and/or sell copies of the Software,
10and to permit persons to whom the Software is furnished to do so,
11subject to the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23DEALINGS IN THE SOFTWARE.
24*/
25
26// TODO: overriding default mapping action in a struct should not affect nested structs
27
28namespace CookComputing.XmlRpc
29{
30  using System;
31  using System.Collections;
32  using System.Globalization;
33  using System.IO;
34  using System.Reflection;
35  using System.Text;
36  using System.Text.RegularExpressions;
37  using System.Threading;
38  using System.Xml;
39
40  struct Fault
41  {
42    public int faultCode;
43    public string faultString;
44  }
45
46  public class XmlRpcSerializer
47  {
48    // public properties
49
50    public int Indentation
51    {
52      get { return m_indentation; }
53      set { m_indentation = value; }
54    }
55    int m_indentation = 2;
56
57    public XmlRpcNonStandard NonStandard
58    {
59      get { return m_nonStandard; }
60      set { m_nonStandard = value; }
61    }
62    XmlRpcNonStandard m_nonStandard = XmlRpcNonStandard.None;
63
64    public bool UseEmptyParamsTag
65    {
66      get { return m_bUseEmptyParamsTag; }
67      set { m_bUseEmptyParamsTag = value; }
68    }
69    bool m_bUseEmptyParamsTag = true;
70
71    public bool UseIndentation
72    {
73      get { return m_bUseIndentation; }
74      set { m_bUseIndentation = value; }
75    }
76    bool m_bUseIndentation = true;
77
78    public bool UseIntTag
79    {
80      get { return m_useIntTag; }
81      set { m_useIntTag = value; }
82    }
83    bool m_useIntTag;
84
85    public bool UseStringTag
86    {
87      get { return m_useStringTag; }
88      set { m_useStringTag = value; }
89    }
90    bool m_useStringTag = true;
91
92    public Encoding XmlEncoding
93    {
94      get { return m_encoding; }
95      set { m_encoding = value; }
96    }
97    Encoding m_encoding = null;
98
99    // private properties
100    bool AllowInvalidHTTPContent
101    {
102      get { return (m_nonStandard & XmlRpcNonStandard.AllowInvalidHTTPContent) != 0; }
103    }
104
105    bool AllowNonStandardDateTime
106    {
107      get { return (m_nonStandard & XmlRpcNonStandard.AllowNonStandardDateTime) != 0; }
108    }
109
110    bool AllowStringFaultCode
111    {
112      get { return (m_nonStandard & XmlRpcNonStandard.AllowStringFaultCode) != 0; }
113    }
114
115    bool IgnoreDuplicateMembers
116    {
117      get { return (m_nonStandard & XmlRpcNonStandard.IgnoreDuplicateMembers) != 0; }
118    }
119
120    bool MapEmptyDateTimeToMinValue
121    {
122      get { return (m_nonStandard & XmlRpcNonStandard.MapEmptyDateTimeToMinValue) != 0; }
123    }
124
125    bool MapZerosDateTimeToMinValue
126    {
127      get { return (m_nonStandard & XmlRpcNonStandard.MapZerosDateTimeToMinValue) != 0; }
128    }
129
130    public void SerializeRequest(Stream stm, XmlRpcRequest request) 
131    {
132      XmlTextWriter xtw = new XmlTextWriter(stm, m_encoding);
133      ConfigureXmlFormat(xtw);
134      xtw.WriteStartDocument();
135      xtw.WriteStartElement("", "methodCall", "");
136    {
137      // TODO: use global action setting
138      MappingAction mappingAction = MappingAction.Error; 
139      if (request.xmlRpcMethod == null)
140        xtw.WriteElementString("methodName", request.method);
141      else
142        xtw.WriteElementString("methodName", request.xmlRpcMethod);
143      if (request.args.Length > 0 || UseEmptyParamsTag)
144      {
145        xtw.WriteStartElement("", "params", "");
146        try
147        {
148          if (!IsStructParamsMethod(request.mi))
149            SerializeParams(xtw, request, mappingAction);
150          else
151            SerializeStructParams(xtw, request, mappingAction);
152        }
153        catch (XmlRpcUnsupportedTypeException ex)
154        {
155          throw new XmlRpcUnsupportedTypeException(ex.UnsupportedType,
156            String.Format("A parameter is of, or contains an instance of, "
157            + "type {0} which cannot be mapped to an XML-RPC type",
158            ex.UnsupportedType));
159        }
160        xtw.WriteEndElement();
161      }
162    }
163      xtw.WriteEndElement();
164      xtw.Flush();
165    }
166
167    void SerializeParams(XmlTextWriter xtw, XmlRpcRequest request,
168      MappingAction mappingAction)
169    {
170      ParameterInfo[] pis = null;
171      if (request.mi != null)
172      {
173        pis = request.mi.GetParameters();
174      }
175      for (int i = 0; i < request.args.Length; i++)
176      {
177        if (pis != null)
178        {
179          if (i >= pis.Length)
180            throw new XmlRpcInvalidParametersException("Number of request "
181              + "parameters greater than number of proxy method parameters.");
182          if (i == pis.Length - 1 
183            && Attribute.IsDefined(pis[i], typeof(ParamArrayAttribute)))
184          {
185            Array ary = (Array)request.args[i];
186            foreach (object o in ary)
187            {
188              if (o == null)
189                throw new XmlRpcNullParameterException(
190                  "Null parameter in params array");
191              xtw.WriteStartElement("", "param", "");
192              Serialize(xtw, o, mappingAction);
193              xtw.WriteEndElement();
194            }
195            break;
196          }
197        }
198        if (request.args[i] == null)
199        {
200          throw new XmlRpcNullParameterException(String.Format(
201            "Null method parameter #{0}", i + 1));
202        }
203        xtw.WriteStartElement("", "param", "");
204        Serialize(xtw, request.args[i], mappingAction);
205        xtw.WriteEndElement();
206      }
207    }
208
209    void SerializeStructParams(XmlTextWriter xtw, XmlRpcRequest request,
210      MappingAction mappingAction)
211    {
212      ParameterInfo[] pis = request.mi.GetParameters();
213      if (request.args.Length > pis.Length)
214        throw new XmlRpcInvalidParametersException("Number of request "
215          + "parameters greater than number of proxy method parameters.");
216      if (Attribute.IsDefined(pis[request.args.Length - 1],
217        typeof(ParamArrayAttribute)))
218      {
219        throw new XmlRpcInvalidParametersException("params parameter cannot "
220          + "be used with StructParams.");
221      }
222      xtw.WriteStartElement("", "param", "");
223      xtw.WriteStartElement("", "value", "");
224      xtw.WriteStartElement("", "struct", "");
225      for (int i = 0; i < request.args.Length; i++)
226      {
227        if (request.args[i] == null)
228        {
229          throw new XmlRpcNullParameterException(String.Format(
230            "Null method parameter #{0}", i + 1));
231        }
232        xtw.WriteStartElement("", "member", "");
233        xtw.WriteElementString("name", pis[i].Name);
234        Serialize(xtw, request.args[i], mappingAction);
235        xtw.WriteEndElement();
236      }
237      xtw.WriteEndElement();
238      xtw.WriteEndElement();
239      xtw.WriteEndElement();
240    }
241
242#if (!COMPACT_FRAMEWORK)
243    public XmlRpcRequest DeserializeRequest(Stream stm, Type svcType)
244    {
245      if (stm == null)
246        throw new ArgumentNullException("stm",
247          "XmlRpcSerializer.DeserializeRequest");
248      XmlDocument xdoc = new XmlDocument(); 
249      xdoc.PreserveWhitespace = true;
250      try
251      {
252        using (XmlTextReader xmlRdr = new XmlTextReader(stm))
253        {
254          xmlRdr.ProhibitDtd = true;
255          xdoc.Load(xmlRdr);
256        }
257      }
258      catch (Exception ex)
259      {
260        throw new XmlRpcIllFormedXmlException(
261          "Request from client does not contain valid XML.", ex);
262      }
263      return DeserializeRequest(xdoc, svcType);
264    }
265
266    public XmlRpcRequest DeserializeRequest(TextReader txtrdr, Type svcType)
267    {
268      if (txtrdr == null)
269        throw new ArgumentNullException("txtrdr",
270          "XmlRpcSerializer.DeserializeRequest");
271      XmlDocument xdoc = new XmlDocument();
272      xdoc.PreserveWhitespace = true;
273      try
274      {
275        using (XmlTextReader xmlRdr = new XmlTextReader(txtrdr))
276        {
277          xmlRdr.ProhibitDtd = true;
278          xdoc.Load(xmlRdr);
279        }
280      }
281      catch (Exception ex)
282      {
283        throw new XmlRpcIllFormedXmlException(
284          "Request from client does not contain valid XML.", ex);
285      }
286      return DeserializeRequest(xdoc, svcType);
287    }
288
289    public XmlRpcRequest DeserializeRequest(XmlDocument xdoc, Type svcType)
290    {
291      XmlRpcRequest request = new XmlRpcRequest();
292      XmlNode callNode = SelectSingleNode(xdoc, "methodCall");
293      if (callNode == null)
294      {
295        throw new XmlRpcInvalidXmlRpcException(
296          "Request XML not valid XML-RPC - missing methodCall element.");
297      }
298      XmlNode methodNode = SelectSingleNode(callNode, "methodName");
299      if (methodNode == null)
300      {
301        throw new XmlRpcInvalidXmlRpcException(
302          "Request XML not valid XML-RPC - missing methodName element.");
303      }
304      if (methodNode.FirstChild == null)
305      {
306        throw new XmlRpcInvalidXmlRpcException(
307          "Request XML not valid XML-RPC - missing methodName element.");
308      }
309      request.method = methodNode.FirstChild.Value;
310      if (request.method == "")
311      {
312        throw new XmlRpcInvalidXmlRpcException(
313          "Request XML not valid XML-RPC - empty methodName.");
314      }
315      request.mi = null;
316      ParameterInfo[] pis = new ParameterInfo[0];
317      if (svcType != null)
318      {
319        // retrieve info for the method which handles this XML-RPC method
320        XmlRpcServiceInfo svcInfo
321          = XmlRpcServiceInfo.CreateServiceInfo(svcType);
322        request.mi = svcInfo.GetMethodInfo(request.method);
323        // if a service type has been specified and we cannot find the requested
324        // method then we must throw an exception
325        if (request.mi == null)
326        {
327          string msg = String.Format("unsupported method called: {0}",
328                                      request.method);
329          throw new XmlRpcUnsupportedMethodException(msg);
330        }
331        // method must be marked with XmlRpcMethod attribute
332        Attribute attr = Attribute.GetCustomAttribute(request.mi,
333          typeof(XmlRpcMethodAttribute));
334        if (attr == null)
335        {
336          throw new XmlRpcMethodAttributeException(
337            "Method must be marked with the XmlRpcMethod attribute.");
338        }
339        pis = request.mi.GetParameters();
340      }
341      XmlNode paramsNode = SelectSingleNode(callNode, "params");
342      if (paramsNode == null)
343      {
344        if (svcType != null)
345        {
346          if (pis.Length == 0)
347          {
348            request.args = new object[0];
349            return request;
350          }
351          else
352          {
353            throw new XmlRpcInvalidParametersException(
354              "Method takes parameters and params element is missing.");
355          }
356        }
357        else
358        {
359          request.args = new object[0];
360          return request;
361        }
362      }
363      XmlNode[] paramNodes = SelectNodes(paramsNode, "param");
364      int paramsPos = GetParamsPos(pis);
365      int minParamCount = paramsPos == -1 ? pis.Length : paramsPos;
366      if (svcType != null && paramNodes.Length < minParamCount)
367      {
368        throw new XmlRpcInvalidParametersException(
369          "Request contains too few param elements based on method signature.");
370      }
371      if (svcType != null && paramsPos == -1 && paramNodes.Length > pis.Length)
372      {
373        throw new XmlRpcInvalidParametersException(
374          "Request contains too many param elements based on method signature.");
375      }
376      ParseStack parseStack = new ParseStack("request");
377      // TODO: use global action setting
378      MappingAction mappingAction = MappingAction.Error;
379      int paramObjCount = (paramsPos == -1 ? paramNodes.Length : paramsPos + 1);
380      Object[] paramObjs = new Object[paramObjCount];
381      // parse ordinary parameters
382      int ordinaryParams = (paramsPos == -1 ? paramNodes.Length : paramsPos);
383      for (int i = 0; i < ordinaryParams; i++)
384      {
385        XmlNode paramNode = paramNodes[i];
386        XmlNode valueNode = SelectSingleNode(paramNode, "value");
387        if (valueNode == null)
388          throw new XmlRpcInvalidXmlRpcException("Missing value element.");
389        XmlNode node = SelectValueNode(valueNode);
390        if (svcType != null)
391        {
392          parseStack.Push(String.Format("parameter {0}", i + 1));
393          // TODO: why following commented out?
394          //          parseStack.Push(String.Format("parameter {0} mapped to type {1}",
395          //            i, pis[i].ParameterType.Name));
396          paramObjs[i] = ParseValue(node, pis[i].ParameterType, parseStack,
397            mappingAction);
398        }
399        else
400        {
401          parseStack.Push(String.Format("parameter {0}", i));
402          paramObjs[i] = ParseValue(node, null, parseStack, mappingAction);
403        }
404        parseStack.Pop();
405      }
406      // parse params parameters
407      if (paramsPos != -1)
408      {
409        Type paramsType = pis[paramsPos].ParameterType.GetElementType();
410        Object[] args = new Object[1];
411        args[0] = paramNodes.Length - paramsPos;
412        Array varargs = (Array)CreateArrayInstance(pis[paramsPos].ParameterType,
413          args);
414        for (int i = 0; i < varargs.Length; i++)
415        {
416          XmlNode paramNode = paramNodes[i + paramsPos];
417          XmlNode valueNode = SelectSingleNode(paramNode, "value");
418          if (valueNode == null)
419            throw new XmlRpcInvalidXmlRpcException("Missing value element.");
420          XmlNode node = SelectValueNode(valueNode);
421          parseStack.Push(String.Format("parameter {0}", i + 1 + paramsPos));
422          varargs.SetValue(ParseValue(node, paramsType, parseStack,
423            mappingAction), i);
424          parseStack.Pop();
425        }
426        paramObjs[paramsPos] = varargs;
427      }
428      request.args = paramObjs;
429      return request;
430    }
431
432    int GetParamsPos(ParameterInfo[] pis)
433    {
434      if (pis.Length == 0)
435        return -1;
436      if (Attribute.IsDefined(pis[pis.Length - 1], typeof(ParamArrayAttribute)))
437      {
438        return pis.Length - 1;
439      }
440      else
441        return -1;
442    }
443
444    public void SerializeResponse(Stream stm, XmlRpcResponse response)
445    {
446      Object ret = response.retVal;
447      if (ret is XmlRpcFaultException)
448      {
449        SerializeFaultResponse(stm, (XmlRpcFaultException)ret);
450        return;
451      }
452
453      XmlTextWriter xtw = new XmlTextWriter(stm, m_encoding);
454      ConfigureXmlFormat(xtw);
455      xtw.WriteStartDocument();
456      xtw.WriteStartElement("", "methodResponse", "");
457      xtw.WriteStartElement("", "params", "");
458      // "void" methods actually return an empty string value
459      if (ret == null)
460      {
461        ret = "";
462      }
463      xtw.WriteStartElement("", "param", "");
464      // TODO: use global action setting
465      MappingAction mappingAction = MappingAction.Error;
466      try
467      {
468        Serialize(xtw, ret, mappingAction);
469      }
470      catch (XmlRpcUnsupportedTypeException ex)
471      {
472        throw new XmlRpcInvalidReturnType(string.Format(
473          "Return value is of, or contains an instance of, type {0} which "
474          + "cannot be mapped to an XML-RPC type", ex.UnsupportedType));
475      }
476      xtw.WriteEndElement();
477      xtw.WriteEndElement();
478      xtw.WriteEndElement();
479      xtw.Flush();
480    }
481#endif
482
483    public XmlRpcResponse DeserializeResponse(Stream stm, Type svcType)
484    {
485      if (stm == null)
486        throw new ArgumentNullException("stm",
487          "XmlRpcSerializer.DeserializeResponse");
488      if (AllowInvalidHTTPContent)
489      {
490        Stream newStm = new MemoryStream();
491        Util.CopyStream(stm, newStm);
492        stm = newStm;
493        stm.Position = 0;
494        while (true)
495        {
496          // for now just strip off any leading CR-LF characters
497          int byt = stm.ReadByte();
498          if (byt == -1)
499            throw new XmlRpcIllFormedXmlException(
500              "Response from server does not contain valid XML.");
501          if (byt != 0x0d && byt != 0x0a && byt != ' ' && byt != '\t')
502          {
503            stm.Position = stm.Position - 1;
504            break;
505          }
506        }
507      }
508      XmlDocument xdoc = new XmlDocument();
509      xdoc.PreserveWhitespace = true;
510      try
511      {
512        using (XmlTextReader xmlRdr = new XmlTextReader(stm))
513        {
514#if (!COMPACT_FRAMEWORK)
515          xmlRdr.ProhibitDtd = true;
516#endif
517          xdoc.Load(xmlRdr);
518        }
519      }
520      catch (Exception ex)
521      {
522        throw new XmlRpcIllFormedXmlException(
523          "Response from server does not contain valid XML.", ex);
524      }
525      return DeserializeResponse(xdoc, svcType);
526    }
527
528    public XmlRpcResponse DeserializeResponse(TextReader txtrdr, Type svcType)
529    {
530      if (txtrdr == null)
531        throw new ArgumentNullException("txtrdr",
532          "XmlRpcSerializer.DeserializeResponse");
533      XmlDocument xdoc = new XmlDocument();
534      xdoc.PreserveWhitespace = true;
535      try
536      {
537        using (XmlTextReader xmlRdr = new XmlTextReader(txtrdr))
538        {
539#if (!COMPACT_FRAMEWORK)
540          xmlRdr.ProhibitDtd = true;
541#endif
542          xdoc.Load(xmlRdr);
543        }
544      }
545      catch (Exception ex)
546      {
547        throw new XmlRpcIllFormedXmlException(
548          "Response from server does not contain valid XML.", ex);
549      }
550      return DeserializeResponse(xdoc, svcType);
551    }
552
553    public XmlRpcResponse DeserializeResponse(XmlDocument xdoc, Type returnType)
554    {
555      XmlRpcResponse response = new XmlRpcResponse();
556      Object retObj = null;
557      XmlNode methodResponseNode = SelectSingleNode(xdoc, "methodResponse");
558      if (methodResponseNode == null)
559      {
560        throw new XmlRpcInvalidXmlRpcException(
561          "Response XML not valid XML-RPC - missing methodResponse element.");
562      }
563      // check for fault response
564      XmlNode faultNode = SelectSingleNode(methodResponseNode, "fault");
565      if (faultNode != null)
566      {
567        ParseStack parseStack = new ParseStack("fault response");
568        // TODO: use global action setting
569        MappingAction mappingAction = MappingAction.Error;
570        XmlRpcFaultException faultEx = ParseFault(faultNode, parseStack,
571          mappingAction);
572        throw faultEx;
573      }
574      XmlNode paramsNode = SelectSingleNode(methodResponseNode, "params");
575      if (paramsNode == null && returnType != null)
576      {
577        if (returnType == typeof(void))
578          return new XmlRpcResponse(null);
579        else
580          throw new XmlRpcInvalidXmlRpcException(
581            "Response XML not valid XML-RPC - missing params element.");
582      }
583      XmlNode paramNode = SelectSingleNode(paramsNode, "param");
584      if (paramNode == null && returnType != null)
585      {
586        if (returnType == typeof(void))
587          return new XmlRpcResponse(null);
588        else
589          throw new XmlRpcInvalidXmlRpcException(
590            "Response XML not valid XML-RPC - missing params element.");
591      }
592      XmlNode valueNode = SelectSingleNode(paramNode, "value");
593      if (valueNode == null)
594      {
595        throw new XmlRpcInvalidXmlRpcException(
596          "Response XML not valid XML-RPC - missing value element.");
597      }
598      if (returnType == typeof(void))
599      {
600        retObj = null;
601      }
602      else
603      {
604        ParseStack parseStack = new ParseStack("response");
605        // TODO: use global action setting
606        MappingAction mappingAction = MappingAction.Error;
607        XmlNode node = SelectValueNode(valueNode);
608        retObj = ParseValue(node, returnType, parseStack, mappingAction);
609      }
610      response.retVal = retObj;
611      return response;
612    }
613
614    //#if (DEBUG)
615    public
616      //#endif
617    void Serialize(
618      XmlTextWriter xtw,
619      Object o,
620      MappingAction mappingAction)
621    {
622      Serialize(xtw, o, mappingAction, new ArrayList(16));
623    }
624
625    //#if (DEBUG)
626    public
627      //#endif
628    void Serialize(
629      XmlTextWriter xtw,
630      Object o,
631      MappingAction mappingAction,
632      ArrayList nestedObjs)
633    {
634      if (nestedObjs.Contains(o))
635        throw new XmlRpcUnsupportedTypeException(nestedObjs[0].GetType(),
636          "Cannot serialize recursive data structure");
637      nestedObjs.Add(o);
638      try
639      {
640        xtw.WriteStartElement("", "value", "");
641        XmlRpcType xType = XmlRpcServiceInfo.GetXmlRpcType(o.GetType());
642        if (xType == XmlRpcType.tArray)
643        {
644          xtw.WriteStartElement("", "array", "");
645          xtw.WriteStartElement("", "data", "");
646          Array a = (Array)o;
647          foreach (Object aobj in a)
648          {
649            if (aobj == null)
650              throw new XmlRpcMappingSerializeException(String.Format(
651                "Items in array cannot be null ({0}[]).",
652            o.GetType().GetElementType()));
653            Serialize(xtw, aobj, mappingAction, nestedObjs);
654          }
655          xtw.WriteEndElement();
656          xtw.WriteEndElement();
657        }
658        else if (xType == XmlRpcType.tMultiDimArray)
659        {
660          Array mda = (Array)o;
661          int[] indices = new int[mda.Rank];
662          BuildArrayXml(xtw, mda, 0, indices, mappingAction, nestedObjs);
663        }
664        else if (xType == XmlRpcType.tBase64)
665        {
666          byte[] buf = (byte[])o;
667          xtw.WriteStartElement("", "base64", "");
668          xtw.WriteBase64(buf, 0, buf.Length);
669          xtw.WriteEndElement();
670        }
671        else if (xType == XmlRpcType.tBoolean)
672        {
673          bool boolVal;
674          if (o is bool)
675            boolVal = (bool)o;
676          else
677            boolVal = (bool)(XmlRpcBoolean)o;
678          if (boolVal)
679            xtw.WriteElementString("boolean", "1");
680          else
681            xtw.WriteElementString("boolean", "0");
682        }
683        else if (xType == XmlRpcType.tDateTime)
684        {
685          DateTime dt;
686          if (o is DateTime)
687            dt = (DateTime)o;
688          else
689            dt = (XmlRpcDateTime)o;
690          string sdt = dt.ToString("yyyyMMdd'T'HH':'mm':'ss",
691          DateTimeFormatInfo.InvariantInfo);
692          xtw.WriteElementString("dateTime.iso8601", sdt);
693        }
694        else if (xType == XmlRpcType.tDouble)
695        {
696          double doubleVal;
697          if (o is double)
698            doubleVal = (double)o;
699          else
700            doubleVal = (XmlRpcDouble)o;
701          xtw.WriteElementString("double", doubleVal.ToString(null,
702          CultureInfo.InvariantCulture));
703        }
704        else if (xType == XmlRpcType.tHashtable)
705        {
706          xtw.WriteStartElement("", "struct", "");
707          XmlRpcStruct xrs = o as XmlRpcStruct;
708          foreach (object obj in xrs.Keys)
709          {
710            string skey = obj as string;
711            xtw.WriteStartElement("", "member", "");
712            xtw.WriteElementString("name", skey);
713            Serialize(xtw, xrs[skey], mappingAction, nestedObjs);
714            xtw.WriteEndElement();
715          }
716          xtw.WriteEndElement();
717        }
718        else if (xType == XmlRpcType.tInt32)
719        {
720          if (UseIntTag)
721            xtw.WriteElementString("int", o.ToString());
722          else
723            xtw.WriteElementString("i4", o.ToString());
724        }
725        else if (xType == XmlRpcType.tInt64)
726        {
727          xtw.WriteElementString("i8", o.ToString());
728        }
729        else if (xType == XmlRpcType.tString)
730        {
731          if (UseStringTag)
732            xtw.WriteElementString("string", (string)o);
733          else
734            xtw.WriteString((string)o);
735        }
736        else if (xType == XmlRpcType.tStruct)
737        {
738          MappingAction structAction
739            = StructMappingAction(o.GetType(), mappingAction);
740          xtw.WriteStartElement("", "struct", "");
741          MemberInfo[] mis = o.GetType().GetMembers();
742          foreach (MemberInfo mi in mis)
743          {
744            if (Attribute.IsDefined(mi, typeof(NonSerializedAttribute)))
745              continue;
746            if (mi.MemberType == MemberTypes.Field)
747            {
748              FieldInfo fi = (FieldInfo)mi;
749              string member = fi.Name;
750              Attribute attrchk = Attribute.GetCustomAttribute(fi,
751              typeof(XmlRpcMemberAttribute));
752              if (attrchk != null && attrchk is XmlRpcMemberAttribute)
753              {
754                string mmbr = ((XmlRpcMemberAttribute)attrchk).Member;
755                if (mmbr != "")
756                  member = mmbr;
757              }
758              if (fi.GetValue(o) == null)
759              {
760                MappingAction memberAction = MemberMappingAction(o.GetType(),
761                  fi.Name, structAction);
762                if (memberAction == MappingAction.Ignore)
763                  continue;
764                throw new XmlRpcMappingSerializeException(@"Member """ + member +
765                  @""" of struct """ + o.GetType().Name + @""" cannot be null.");
766              }
767              xtw.WriteStartElement("", "member", "");
768              xtw.WriteElementString("name", member);
769              Serialize(xtw, fi.GetValue(o), mappingAction, nestedObjs);
770              xtw.WriteEndElement();
771            }
772            else if (mi.MemberType == MemberTypes.Property)
773            {
774              PropertyInfo pi = (PropertyInfo)mi;
775              string member = pi.Name;
776              Attribute attrchk = Attribute.GetCustomAttribute(pi,
777              typeof(XmlRpcMemberAttribute));
778              if (attrchk != null && attrchk is XmlRpcMemberAttribute)
779              {
780                string mmbr = ((XmlRpcMemberAttribute)attrchk).Member;
781                if (mmbr != "")
782                  member = mmbr;
783              }
784              if (pi.GetValue(o, null) == null)
785              {
786                MappingAction memberAction = MemberMappingAction(o.GetType(),
787                  pi.Name, structAction);
788                if (memberAction == MappingAction.Ignore)
789                  continue;
790              }
791              xtw.WriteStartElement("", "member", "");
792              xtw.WriteElementString("name", member);
793              Serialize(xtw, pi.GetValue(o, null), mappingAction, nestedObjs);
794              xtw.WriteEndElement();
795            }
796          }
797          xtw.WriteEndElement();
798        }
799        else if (xType == XmlRpcType.tVoid)
800          xtw.WriteElementString("string", "");
801        else
802          throw new XmlRpcUnsupportedTypeException(o.GetType());
803        xtw.WriteEndElement();
804      }
805      catch (System.NullReferenceException)
806      {
807        throw new XmlRpcNullReferenceException("Attempt to serialize data "
808          + "containing null reference");
809      }
810      finally
811      {
812        nestedObjs.RemoveAt(nestedObjs.Count - 1);
813      }
814    }
815
816    void BuildArrayXml(
817      XmlTextWriter xtw,
818      Array ary,
819      int CurRank,
820      int[] indices,
821      MappingAction mappingAction,
822      ArrayList nestedObjs)
823    {
824      xtw.WriteStartElement("", "array", "");
825      xtw.WriteStartElement("", "data", "");
826      if (CurRank < (ary.Rank - 1))
827      {
828        for (int i = 0; i < ary.GetLength(CurRank); i++)
829        {
830          indices[CurRank] = i;
831          xtw.WriteStartElement("", "value", "");
832          BuildArrayXml(xtw, ary, CurRank + 1, indices, mappingAction, nestedObjs);
833          xtw.WriteEndElement();
834        }
835      }
836      else
837      {
838        for (int i = 0; i < ary.GetLength(CurRank); i++)
839        {
840          indices[CurRank] = i;
841          Serialize(xtw, ary.GetValue(indices), mappingAction, nestedObjs);
842        }
843      }
844      xtw.WriteEndElement();
845      xtw.WriteEndElement();
846    }
847
848    Object ParseValue(
849      XmlNode node,
850      Type ValueType,
851      ParseStack parseStack,
852      MappingAction mappingAction)
853    {
854      Type parsedType;
855      Type parsedArrayType;
856      return ParseValue(node, ValueType, parseStack, mappingAction,
857        out parsedType, out parsedArrayType);
858    }
859
860    //#if (DEBUG)
861    public
862      //#endif
863    Object ParseValue(
864      XmlNode node,
865      Type ValueType,
866      ParseStack parseStack,
867      MappingAction mappingAction,
868      out Type ParsedType,
869      out Type ParsedArrayType)
870    {
871      ParsedType = null;
872      ParsedArrayType = null;
873      // if suppplied type is System.Object then ignore it because
874      // if doesn't provide any useful information (parsing methods
875      // expect null in this case)
876      Type valType = ValueType;
877      if (valType != null && valType.BaseType == null)
878        valType = null;
879
880      Object retObj = null;
881      if (node == null)
882      {
883        retObj = "";
884      }
885      else if (node is XmlText || node is XmlWhitespace)
886      {
887        if (valType != null && valType != typeof(string))
888        {
889          throw new XmlRpcTypeMismatchException(parseStack.ParseType
890            + " contains implicit string value where "
891            + XmlRpcServiceInfo.GetXmlRpcTypeString(valType)
892            + " expected " + StackDump(parseStack));
893        }
894        retObj = node.Value;
895      }
896      else
897      {
898        if (node.Name == "array")
899          retObj = ParseArray(node, valType, parseStack, mappingAction);
900        else if (node.Name == "base64")
901          retObj = ParseBase64(node, valType, parseStack, mappingAction);
902        else if (node.Name == "struct")
903        {
904          // if we don't know the expected struct type then we must
905          // parse the XML-RPC struct as an instance of XmlRpcStruct
906          if (valType != null && valType != typeof(XmlRpcStruct)
907            && !valType.IsSubclassOf(typeof(XmlRpcStruct)))
908          {
909            retObj = ParseStruct(node, valType, parseStack, mappingAction);
910          }
911          else
912          {
913            if (valType == null || valType == typeof(object))
914              valType = typeof(XmlRpcStruct);
915            // TODO: do we need to validate type here?
916            retObj = ParseHashtable(node, valType, parseStack, mappingAction);
917          }
918        }
919        else if (node.Name == "i4"  // integer has two representations in XML-RPC spec
920          || node.Name == "int")
921        {
922          retObj = ParseInt(node, valType, parseStack, mappingAction);
923          ParsedType = typeof(int);
924          ParsedArrayType = typeof(int[]);
925        }
926        else if (node.Name == "i8")
927        {
928          retObj = ParseLong(node, valType, parseStack, mappingAction);
929          ParsedType = typeof(long);
930          ParsedArrayType = typeof(long[]);
931        }
932        else if (node.Name == "string")
933        {
934          retObj = ParseString(node, valType, parseStack, mappingAction);
935          ParsedType = typeof(string);
936          ParsedArrayType = typeof(string[]);
937        }
938        else if (node.Name == "boolean")
939        {
940          retObj = ParseBoolean(node, valType, parseStack, mappingAction);
941          ParsedType = typeof(bool);
942          ParsedArrayType = typeof(bool[]);
943        }
944        else if (node.Name == "double")
945        {
946          retObj = ParseDouble(node, valType, parseStack, mappingAction);
947          ParsedType = typeof(double);
948          ParsedArrayType = typeof(double[]);
949        }
950        else if (node.Name == "dateTime.iso8601")
951        {
952          retObj = ParseDateTime(node, valType, parseStack, mappingAction);
953          ParsedType = typeof(DateTime);
954          ParsedArrayType = typeof(DateTime[]);
955        }
956        else
957          throw new XmlRpcInvalidXmlRpcException(
958            "Invalid value element: <" + node.Name + ">");
959      }
960      return retObj;
961    }
962
963    Object ParseArray(
964      XmlNode node,
965      Type ValueType,
966      ParseStack parseStack,
967      MappingAction mappingAction)
968    {
969      // required type must be an array
970      if (ValueType != null
971        && !(ValueType.IsArray == true
972            || ValueType == typeof(Array)
973            || ValueType == typeof(object)))
974      {
975        throw new XmlRpcTypeMismatchException(parseStack.ParseType
976          + " contains array value where "
977          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
978          + " expected " + StackDump(parseStack));
979      }
980      if (ValueType != null)
981      {
982        XmlRpcType xmlRpcType = XmlRpcServiceInfo.GetXmlRpcType(ValueType);
983        if (xmlRpcType == XmlRpcType.tMultiDimArray)
984        {
985          parseStack.Push("array mapped to type " + ValueType.Name);
986          Object ret = ParseMultiDimArray(node, ValueType, parseStack,
987            mappingAction);
988          return ret;
989        }
990        parseStack.Push("array mapped to type " + ValueType.Name);
991      }
992      else
993        parseStack.Push("array");
994      XmlNode dataNode = SelectSingleNode(node, "data");
995      XmlNode[] childNodes = SelectNodes(dataNode, "value");
996      int nodeCount = childNodes.Length;
997      Object[] elements = new Object[nodeCount];
998      // determine type of array elements
999      Type elemType = null;
1000      if (ValueType != null
1001        && ValueType != typeof(Array)
1002        && ValueType != typeof(object))
1003      {
1004#if (!COMPACT_FRAMEWORK)
1005        elemType = ValueType.GetElementType();
1006#else
1007        string[] checkMultiDim = Regex.Split(ValueType.FullName, 
1008          "\\[\\]$");
1009        // determine assembly of array element type
1010        Assembly asmbly = ValueType.Assembly;
1011        string[] asmblyName = asmbly.FullName.Split(',');
1012        string elemTypeName = checkMultiDim[0] + ", " + asmblyName[0]; 
1013        elemType = Type.GetType(elemTypeName);
1014#endif
1015      }
1016      else
1017      {
1018        elemType = typeof(object);
1019      }
1020      bool bGotType = false;
1021      Type useType = null;
1022      int i = 0;
1023      foreach (XmlNode vNode in childNodes)
1024      {
1025        parseStack.Push(String.Format("element {0}", i));
1026        XmlNode vvNode = SelectValueNode(vNode);
1027        Type parsedType;
1028        Type parsedArrayType;
1029        elements[i++] = ParseValue(vvNode, elemType, parseStack, mappingAction,
1030                                    out parsedType, out parsedArrayType);
1031        if (bGotType == false)
1032        {
1033          useType = parsedArrayType;
1034          bGotType = true;
1035        }
1036        else
1037        {
1038          if (useType != parsedArrayType)
1039            useType = null;
1040        }
1041        parseStack.Pop();
1042      }
1043      Object[] args = new Object[1]; args[0] = nodeCount;
1044      Object retObj = null;
1045      if (ValueType != null
1046        && ValueType != typeof(Array)
1047        && ValueType != typeof(object))
1048      {
1049        retObj = CreateArrayInstance(ValueType, args);
1050      }
1051      else
1052      {
1053        if (useType == null)
1054          retObj = CreateArrayInstance(typeof(object[]), args);
1055        else
1056          retObj = CreateArrayInstance(useType, args);
1057      }
1058      for (int j = 0; j < elements.Length; j++)
1059      {
1060        ((Array)retObj).SetValue(elements[j], j);
1061      }
1062      parseStack.Pop();
1063      return retObj;
1064    }
1065
1066    Object ParseMultiDimArray(XmlNode node, Type ValueType,
1067      ParseStack parseStack, MappingAction mappingAction)
1068    {
1069      // parse the type name to get element type and array rank
1070#if (!COMPACT_FRAMEWORK)
1071      Type elemType = ValueType.GetElementType();
1072      int rank = ValueType.GetArrayRank();
1073#else
1074      string[] checkMultiDim = Regex.Split(ValueType.FullName, 
1075        "\\[,[,]*\\]$");
1076      Type elemType = Type.GetType(checkMultiDim[0]);
1077      string commas = ValueType.FullName.Substring(checkMultiDim[0].Length+1, 
1078        ValueType.FullName.Length-checkMultiDim[0].Length-2);
1079      int rank = commas.Length+1;
1080#endif
1081      // elements will be stored sequentially as nested arrays are parsed
1082      ArrayList elements = new ArrayList();
1083      // create array to store length of each dimension - initialize to
1084      // all zeroes so that when parsing we can determine if an array for
1085      // that dimension has been parsed already
1086      int[] dimLengths = new int[rank];
1087      dimLengths.Initialize();
1088      ParseMultiDimElements(node, rank, 0, elemType, elements, dimLengths,
1089        parseStack, mappingAction);
1090      // build arguments to define array dimensions and create the array
1091      Object[] args = new Object[dimLengths.Length];
1092      for (int argi = 0; argi < dimLengths.Length; argi++)
1093      {
1094        args[argi] = dimLengths[argi];
1095      }
1096      Array ret = (Array)CreateArrayInstance(ValueType, args);
1097      // copy elements into new multi-dim array
1098      //!! make more efficient
1099      int length = ret.Length;
1100      for (int e = 0; e < length; e++)
1101      {
1102        int[] indices = new int[dimLengths.Length];
1103        int div = 1;
1104        for (int f = (indices.Length - 1); f >= 0; f--)
1105        {
1106          indices[f] = (e / div) % dimLengths[f];
1107          div *= dimLengths[f];
1108        }
1109        ret.SetValue(elements[e], indices);
1110      }
1111      return ret;
1112    }
1113
1114    void ParseMultiDimElements(XmlNode node, int Rank, int CurRank,
1115      Type elemType, ArrayList elements, int[] dimLengths,
1116      ParseStack parseStack, MappingAction mappingAction)
1117    {
1118      if (node.Name != "array")
1119      {
1120        throw new XmlRpcTypeMismatchException(
1121          "param element does not contain array element.");
1122      }
1123      XmlNode dataNode = SelectSingleNode(node, "data");
1124      XmlNode[] childNodes = SelectNodes(dataNode, "value");
1125      int nodeCount = childNodes.Length;
1126      //!! check that multi dim array is not jagged
1127      if (dimLengths[CurRank] != 0 && nodeCount != dimLengths[CurRank])
1128      {
1129        throw new XmlRpcNonRegularArrayException(
1130          "Multi-dimensional array must not be jagged.");
1131      }
1132      dimLengths[CurRank] = nodeCount;  // in case first array at this rank
1133      if (CurRank < (Rank - 1))
1134      {
1135        foreach (XmlNode vNode in childNodes)
1136        {
1137          XmlNode arrayNode = SelectSingleNode(vNode, "array");
1138          ParseMultiDimElements(arrayNode, Rank, CurRank + 1, elemType,
1139            elements, dimLengths, parseStack, mappingAction);
1140        }
1141      }
1142      else
1143      {
1144        foreach (XmlNode vNode in childNodes)
1145        {
1146          XmlNode vvNode = SelectValueNode(vNode);
1147          elements.Add(ParseValue(vvNode, elemType, parseStack,
1148            mappingAction));
1149        }
1150      }
1151    }
1152
1153    Object ParseStruct(
1154      XmlNode node,
1155      Type valueType,
1156      ParseStack parseStack,
1157      MappingAction mappingAction)
1158    {
1159      if (valueType.IsPrimitive)
1160      {
1161        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1162          + " contains struct value where "
1163          + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType)
1164          + " expected " + StackDump(parseStack));
1165      }
1166#if !FX1_0
1167      if (valueType.IsGenericType
1168        && valueType.GetGenericTypeDefinition() == typeof(Nullable<>))
1169      {
1170        valueType = valueType.GetGenericArguments()[0];
1171      }
1172#endif
1173      object retObj;
1174      try
1175      {
1176        retObj = Activator.CreateInstance(valueType);
1177      }
1178      catch (Exception)
1179      {
1180        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1181          + " contains struct value where "
1182          + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType)
1183          + " expected (as type " + valueType.Name + ") "
1184          + StackDump(parseStack));
1185      }
1186      // Note: mapping action on a struct is only applied locally - it
1187      // does not override the global mapping action when members of the
1188      // struct are parsed
1189      MappingAction localAction = mappingAction;
1190      if (valueType != null)
1191      {
1192        parseStack.Push("struct mapped to type " + valueType.Name);
1193        localAction = StructMappingAction(valueType, mappingAction);
1194      }
1195      else
1196      {
1197        parseStack.Push("struct");
1198      }
1199      // create map of field names and remove each name from it as
1200      // processed so we can determine which fields are missing
1201      // TODO: replace HashTable with lighter collection
1202      Hashtable names = new Hashtable();
1203      foreach (FieldInfo fi in valueType.GetFields())
1204      {
1205        if (Attribute.IsDefined(fi, typeof(NonSerializedAttribute)))
1206          continue;
1207        names.Add(fi.Name, fi.Name);
1208      }
1209      foreach (PropertyInfo pi in valueType.GetProperties())
1210      {
1211        if (Attribute.IsDefined(pi, typeof(NonSerializedAttribute)))
1212          continue;
1213        names.Add(pi.Name, pi.Name);
1214      }
1215      XmlNode[] members = SelectNodes(node, "member");
1216      int fieldCount = 0;
1217      foreach (XmlNode member in members)
1218      {
1219        if (member.Name != "member")
1220          continue;
1221        XmlNode nameNode;
1222        bool dupName;
1223        XmlNode valueNode;
1224        bool dupValue;
1225        SelectTwoNodes(member, "name", out nameNode, out dupName, "value",
1226          out valueNode, out dupValue);
1227        if (nameNode == null || nameNode.FirstChild == null)
1228          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1229            + " contains a member with missing name"
1230            + " " + StackDump(parseStack));
1231        if (dupName)
1232          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1233            + " contains member with more than one name element"
1234            + " " + StackDump(parseStack));
1235        string name = nameNode.FirstChild.Value;
1236        if (valueNode == null)
1237          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1238            + " contains struct member " + name + " with missing value "
1239            + " " + StackDump(parseStack));
1240        if (dupValue)
1241          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1242            + " contains member with more than one value element"
1243            + " " + StackDump(parseStack));
1244        string structName = GetStructName(valueType, name);
1245        if (structName != null)
1246          name = structName;
1247        MemberInfo mi = valueType.GetField(name);
1248        if (mi == null)
1249          mi = valueType.GetProperty(name);
1250        if (mi == null)
1251          continue;
1252        if (names.Contains(name))
1253          names.Remove(name);
1254        else
1255        {
1256          if (Attribute.IsDefined(mi, typeof(NonSerializedAttribute)))
1257          {
1258            parseStack.Push(String.Format("member {0}", name));
1259            throw new XmlRpcNonSerializedMember("Cannot map XML-RPC struct "
1260              + "member onto member marked as [NonSerialized]: "
1261              + " " + StackDump(parseStack));
1262          }
1263          if (!IgnoreDuplicateMembers)
1264            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1265              + " contains struct value with duplicate member "
1266              + nameNode.FirstChild.Value
1267              + " " + StackDump(parseStack));
1268          else
1269            continue;   // ignore duplicate member
1270        }
1271        Object valObj = null;
1272        switch (mi.MemberType)
1273        {
1274          case MemberTypes.Field:
1275            FieldInfo fi = (FieldInfo)mi;
1276            if (valueType == null)
1277              parseStack.Push(String.Format("member {0}", name));
1278            else
1279              parseStack.Push(String.Format("member {0} mapped to type {1}",
1280                name, fi.FieldType.Name));
1281            try
1282            {
1283              XmlNode vvvNode = SelectValueNode(valueNode);
1284              valObj = ParseValue(vvvNode, fi.FieldType,
1285                parseStack, mappingAction);
1286            }
1287            catch (XmlRpcInvalidXmlRpcException)
1288            {
1289              if (valueType != null && localAction == MappingAction.Error)
1290              {
1291                MappingAction memberAction = MemberMappingAction(valueType,
1292                  name, MappingAction.Error);
1293                if (memberAction == MappingAction.Error)
1294                  throw;
1295              }
1296            }
1297            finally
1298            {
1299              parseStack.Pop();
1300            }
1301            fi.SetValue(retObj, valObj);
1302            break;
1303          case MemberTypes.Property:
1304            PropertyInfo pi = (PropertyInfo)mi;
1305            if (valueType == null)
1306              parseStack.Push(String.Format("member {0}", name));
1307            else
1308
1309              parseStack.Push(String.Format("member {0} mapped to type {1}",
1310                name, pi.PropertyType.Name));
1311            XmlNode vvNode = SelectValueNode(valueNode);
1312            valObj = ParseValue(vvNode, pi.PropertyType,
1313              parseStack, mappingAction);
1314            parseStack.Pop();
1315
1316            pi.SetValue(retObj, valObj, null);
1317            break;
1318        }
1319        fieldCount++;
1320      }
1321      if (localAction == MappingAction.Error && names.Count > 0)
1322        ReportMissingMembers(valueType, names, parseStack);
1323      parseStack.Pop();
1324      return retObj;
1325    }
1326
1327    void ReportMissingMembers(
1328      Type valueType,
1329      Hashtable names,
1330      ParseStack parseStack)
1331    {
1332      StringBuilder sb = new StringBuilder();
1333      int errorCount = 0;
1334      string sep = "";
1335      foreach (string s in names.Keys)
1336      {
1337        MappingAction memberAction = MemberMappingAction(valueType, s,
1338          MappingAction.Error);
1339        if (memberAction == MappingAction.Error)
1340        {
1341          sb.Append(sep);
1342          sb.Append(s);
1343          sep = " ";
1344          errorCount++;
1345        }
1346      }
1347      if (errorCount > 0)
1348      {
1349        string plural = "";
1350        if (errorCount > 1)
1351          plural = "s";
1352        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1353          + " contains struct value with missing non-optional member"
1354          + plural + ": " + sb.ToString() + " " + StackDump(parseStack));
1355      }
1356    }
1357
1358    string GetStructName(Type ValueType, string XmlRpcName)
1359    {
1360      // given a member name in an XML-RPC struct, check to see whether
1361      // a field has been associated with this XML-RPC member name, return
1362      // the field name if it has else return null
1363      if (ValueType == null)
1364        return null;
1365      foreach (FieldInfo fi in ValueType.GetFields())
1366      {
1367        Attribute attr = Attribute.GetCustomAttribute(fi,
1368          typeof(XmlRpcMemberAttribute));
1369        if (attr != null
1370          && attr is XmlRpcMemberAttribute
1371          && ((XmlRpcMemberAttribute)attr).Member == XmlRpcName)
1372        {
1373          string ret = fi.Name;
1374          return ret;
1375        }
1376      }
1377      foreach (PropertyInfo pi in ValueType.GetProperties())
1378      {
1379        Attribute attr = Attribute.GetCustomAttribute(pi,
1380          typeof(XmlRpcMemberAttribute));
1381        if (attr != null
1382          && attr is XmlRpcMemberAttribute
1383          && ((XmlRpcMemberAttribute)attr).Member == XmlRpcName)
1384        {
1385          string ret = pi.Name;
1386          return ret;
1387        }
1388      }
1389      return null;
1390    }
1391
1392    MappingAction StructMappingAction(
1393      Type type,
1394      MappingAction currentAction)
1395    {
1396      // if struct member has mapping action attribute, override the current
1397      // mapping action else just return the current action
1398      if (type == null)
1399        return currentAction;
1400      Attribute attr = Attribute.GetCustomAttribute(type,
1401        typeof(XmlRpcMissingMappingAttribute));
1402      if (attr != null)
1403        return ((XmlRpcMissingMappingAttribute)attr).Action;
1404      return currentAction;
1405    }
1406
1407    MappingAction MemberMappingAction(
1408      Type type,
1409      string memberName,
1410      MappingAction currentAction)
1411    {
1412      // if struct member has mapping action attribute, override the current
1413      // mapping action else just return the current action
1414      if (type == null)
1415        return currentAction;
1416      Attribute attr = null;
1417      FieldInfo fi = type.GetField(memberName);
1418      if (fi != null)
1419        attr = Attribute.GetCustomAttribute(fi,
1420          typeof(XmlRpcMissingMappingAttribute));
1421      else
1422      {
1423        PropertyInfo pi = type.GetProperty(memberName);
1424        attr = Attribute.GetCustomAttribute(pi,
1425          typeof(XmlRpcMissingMappingAttribute));
1426      }
1427      if (attr != null)
1428        return ((XmlRpcMissingMappingAttribute)attr).Action;
1429      return currentAction;
1430    }
1431
1432    Object ParseHashtable(
1433      XmlNode node,
1434      Type valueType,
1435      ParseStack parseStack,
1436      MappingAction mappingAction)
1437    {
1438      XmlRpcStruct retObj = new XmlRpcStruct();
1439      parseStack.Push("struct mapped to XmlRpcStruct");
1440      try
1441      {
1442        XmlNode[] members = SelectNodes(node, "member");
1443        foreach (XmlNode member in members)
1444        {
1445          if (member.Name != "member")
1446            continue;
1447          XmlNode nameNode;
1448          bool dupName;
1449          XmlNode valueNode;
1450          bool dupValue;
1451          SelectTwoNodes(member, "name", out nameNode, out dupName, "value",
1452            out valueNode, out dupValue);
1453          if (nameNode == null || nameNode.FirstChild == null)
1454            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1455              + " contains a member with missing name"
1456              + " " + StackDump(parseStack));
1457          if (dupName)
1458            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1459              + " contains member with more than one name element"
1460              + " " + StackDump(parseStack));
1461          string rpcName = nameNode.FirstChild.Value;
1462          if (valueNode == null)
1463            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1464              + " contains struct member " + rpcName + " with missing value "
1465              + " " + StackDump(parseStack));
1466          if (dupValue)
1467            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1468              + " contains member with more than one value element"
1469              + " " + StackDump(parseStack));
1470          if (retObj.Contains(rpcName))
1471          {
1472            if (!IgnoreDuplicateMembers)
1473              throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1474                + " contains struct value with duplicate member "
1475                + nameNode.FirstChild.Value
1476                + " " + StackDump(parseStack));
1477            else
1478              continue;
1479          }
1480          object valObj;
1481          parseStack.Push(String.Format("member {0}", rpcName));
1482          try
1483          {
1484            XmlNode vvNode = SelectValueNode(valueNode);
1485            valObj = ParseValue(vvNode, null, parseStack,
1486              mappingAction);
1487          }
1488          finally
1489          {
1490            parseStack.Pop();
1491          }
1492          retObj.Add(rpcName, valObj);
1493        }
1494      }
1495      finally
1496      {
1497        parseStack.Pop();
1498      }
1499      return retObj;
1500    }
1501
1502    Object ParseInt(
1503      XmlNode node,
1504      Type ValueType,
1505      ParseStack parseStack,
1506      MappingAction mappingAction)
1507    {
1508      if (ValueType != null && ValueType != typeof(Object)
1509        && ValueType != typeof(System.Int32)
1510#if !FX1_0
1511 && ValueType != typeof(int?)
1512#endif
1513 && ValueType != typeof(XmlRpcInt))
1514      {
1515        throw new XmlRpcTypeMismatchException(parseStack.ParseType +
1516          " contains int value where "
1517          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1518          + " expected " + StackDump(parseStack));
1519      }
1520      int retVal;
1521      parseStack.Push("integer");
1522      try
1523      {
1524        XmlNode valueNode = node.FirstChild;
1525        if (valueNode == null)
1526        {
1527          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1528            + " contains invalid int element " + StackDump(parseStack));
1529        }
1530        try
1531        {
1532          String strValue = valueNode.Value;
1533          retVal = Int32.Parse(strValue);
1534        }
1535        catch (Exception)
1536        {
1537          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1538            + " contains invalid int value " + StackDump(parseStack));
1539        }
1540      }
1541      finally
1542      {
1543        parseStack.Pop();
1544      }
1545      if (ValueType == typeof(XmlRpcInt))
1546        return new XmlRpcInt(retVal);
1547      else
1548        return retVal;
1549    }
1550
1551    Object ParseLong(
1552      XmlNode node,
1553      Type ValueType,
1554      ParseStack parseStack,
1555      MappingAction mappingAction)
1556    {
1557      if (ValueType != null && ValueType != typeof(Object)
1558        && ValueType != typeof(System.Int64)
1559#if !FX1_0
1560 && ValueType != typeof(long?)
1561#endif
1562      )
1563      {
1564        throw new XmlRpcTypeMismatchException(parseStack.ParseType +
1565          " contains i8 value where "
1566          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1567          + " expected " + StackDump(parseStack));
1568      }
1569      long retVal;
1570      parseStack.Push("i8");
1571      try
1572      {
1573        XmlNode valueNode = node.FirstChild;
1574        if (valueNode == null)
1575        {
1576          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1577            + " contains invalid i8 element " + StackDump(parseStack));
1578        }
1579        try
1580        {
1581          String strValue = valueNode.Value;
1582          retVal = Int64.Parse(strValue);
1583        }
1584        catch (Exception)
1585        {
1586          throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1587            + " contains invalid i8 value " + StackDump(parseStack));
1588        }
1589      }
1590      finally
1591      {
1592        parseStack.Pop();
1593      }
1594      return retVal;
1595    }
1596
1597    Object ParseString(
1598      XmlNode node,
1599      Type ValueType,
1600      ParseStack parseStack,
1601      MappingAction mappingAction)
1602    {
1603      if (ValueType != null && ValueType != typeof(System.String)
1604        && ValueType != typeof(Object))
1605      {
1606        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1607          + " contains string value where "
1608          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1609          + " expected " + StackDump(parseStack));
1610      }
1611      string ret;
1612      parseStack.Push("string");
1613      try
1614      {
1615        if (node.FirstChild == null)
1616          ret = "";
1617        else
1618          ret = node.FirstChild.Value;
1619      }
1620      finally
1621      {
1622        parseStack.Pop();
1623      }
1624      return ret;
1625    }
1626
1627    Object ParseBoolean(
1628      XmlNode node,
1629      Type ValueType,
1630      ParseStack parseStack,
1631      MappingAction mappingAction)
1632    {
1633      if (ValueType != null && ValueType != typeof(Object)
1634        && ValueType != typeof(System.Boolean)
1635#if !FX1_0
1636 && ValueType != typeof(bool?)
1637#endif
1638 && ValueType != typeof(XmlRpcBoolean))
1639      {
1640        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1641          + " contains boolean value where "
1642          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1643          + " expected " + StackDump(parseStack));
1644      }
1645      bool retVal;
1646      parseStack.Push("boolean");
1647      try
1648      {
1649        string s = node.FirstChild.Value;
1650        if (s == "1")
1651        {
1652          retVal = true;
1653        }
1654        else if (s == "0")
1655        {
1656          retVal = false;
1657        }
1658        else
1659        {
1660          throw new XmlRpcInvalidXmlRpcException(
1661            "reponse contains invalid boolean value "
1662            + StackDump(parseStack));
1663        }
1664      }
1665      finally
1666      {
1667        parseStack.Pop();
1668      }
1669      if (ValueType == typeof(XmlRpcBoolean))
1670        return new XmlRpcBoolean(retVal);
1671      else
1672        return retVal;
1673    }
1674
1675    Object ParseDouble(
1676      XmlNode node,
1677      Type ValueType,
1678      ParseStack parseStack,
1679      MappingAction mappingAction)
1680    {
1681      if (ValueType != null && ValueType != typeof(Object)
1682        && ValueType != typeof(System.Double)
1683#if !FX1_0
1684 && ValueType != typeof(double?)
1685#endif
1686 && ValueType != typeof(XmlRpcDouble))
1687      {
1688        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1689          + " contains double value where "
1690          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1691          + " expected " + StackDump(parseStack));
1692      }
1693      Double retVal;
1694      parseStack.Push("double");
1695      try
1696      {
1697        retVal = Double.Parse(node.FirstChild.Value,
1698          CultureInfo.InvariantCulture.NumberFormat);
1699      }
1700      catch (Exception)
1701      {
1702        throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1703          + " contains invalid double value " + StackDump(parseStack));
1704      }
1705      finally
1706      {
1707        parseStack.Pop();
1708      }
1709      if (ValueType == typeof(XmlRpcDouble))
1710        return new XmlRpcDouble(retVal);
1711      else
1712        return retVal;
1713    }
1714
1715    Object ParseDateTime(
1716      XmlNode node,
1717      Type ValueType,
1718      ParseStack parseStack,
1719      MappingAction mappingAction)
1720    {
1721      if (ValueType != null && ValueType != typeof(Object)
1722        && ValueType != typeof(System.DateTime)
1723#if !FX1_0
1724 && ValueType != typeof(DateTime?)
1725#endif
1726 && ValueType != typeof(XmlRpcDateTime))
1727      {
1728        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1729          + " contains dateTime.iso8601 value where "
1730          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1731          + " expected " + StackDump(parseStack));
1732      }
1733      DateTime retVal;
1734      parseStack.Push("dateTime");
1735      try
1736      {
1737        XmlNode child = node.FirstChild;
1738        if (child == null)
1739        {
1740          if (MapEmptyDateTimeToMinValue)
1741            return DateTime.MinValue;
1742          else
1743            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1744              + " contains empty dateTime value "
1745              + StackDump(parseStack));
1746        }
1747        string s = child.Value;
1748        // Allow various iso8601 formats, e.g.
1749        //   XML-RPC spec yyyyMMddThh:mm:ss
1750        //   WordPress yyyyMMddThh:mm:ssZ
1751        //   TypePad yyyy-MM-ddThh:mm:ssZ
1752        //   other yyyy-MM-ddThh:mm:ss
1753        if (!DateTime8601.TryParseDateTime8601(s, out retVal))
1754        {
1755          if (MapZerosDateTimeToMinValue && s.StartsWith("0000")
1756            && (s == "00000000T00:00:00" || s == "0000-00-00T00:00:00Z"
1757            || s == "00000000T00:00:00Z" || s == "0000-00-00T00:00:00"))
1758            retVal = DateTime.MinValue;
1759          else 
1760            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1761              + " contains invalid dateTime value "
1762              + StackDump(parseStack));
1763        }
1764      }
1765      finally
1766      {
1767        parseStack.Pop();
1768      }
1769      if (ValueType == typeof(XmlRpcDateTime))
1770        return new XmlRpcDateTime(retVal);
1771      else
1772        return retVal;
1773    }
1774
1775    Object ParseBase64(
1776      XmlNode node,
1777      Type ValueType,
1778      ParseStack parseStack,
1779      MappingAction mappingAction)
1780    {
1781      if (ValueType != null && ValueType != typeof(byte[])
1782        && ValueType != typeof(Object))
1783      {
1784        throw new XmlRpcTypeMismatchException(parseStack.ParseType
1785          + " contains base64 value where "
1786          + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType)
1787          + " expected " + StackDump(parseStack));
1788      }
1789      byte[] ret;
1790      parseStack.Push("base64");
1791      try
1792      {
1793        if (node.FirstChild == null)
1794          ret = new byte[0];
1795        else
1796        {
1797          string s = node.FirstChild.Value;
1798          try
1799          {
1800            ret = Convert.FromBase64String(s);
1801          }
1802          catch (Exception)
1803          {
1804            throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
1805              + " contains invalid base64 value "
1806              + StackDump(parseStack));
1807          }
1808        }
1809      }
1810      finally
1811      {
1812        parseStack.Pop();
1813      }
1814      return ret;
1815    }
1816
1817    XmlRpcFaultException ParseFault(
1818      XmlNode faultNode,
1819      ParseStack parseStack,
1820      MappingAction mappingAction)
1821    {
1822      XmlNode valueNode = SelectSingleNode(faultNode, "value");
1823      XmlNode structNode = SelectSingleNode(valueNode, "struct");
1824      if (structNode == null)
1825      {
1826        throw new XmlRpcInvalidXmlRpcException(
1827          "struct element missing from fault response.");
1828      }
1829      Fault fault;
1830      try
1831      {
1832        fault = (Fault)ParseValue(structNode, typeof(Fault), parseStack,
1833          mappingAction);
1834      }
1835      catch (Exception ex)
1836      {
1837        // some servers incorrectly return fault code in a string
1838        if (AllowStringFaultCode)
1839          throw;
1840        else
1841        {
1842          FaultStructStringCode faultStrCode;
1843          try
1844          {
1845            faultStrCode = (FaultStructStringCode)ParseValue(structNode,
1846              typeof(FaultStructStringCode), parseStack, mappingAction);
1847            fault.faultCode = Convert.ToInt32(faultStrCode.faultCode);
1848            fault.faultString = faultStrCode.faultString;
1849          }
1850          catch (Exception)
1851          {
1852            // use exception from when attempting to parse code as integer
1853            throw ex;
1854          }
1855        }
1856      }
1857      return new XmlRpcFaultException(fault.faultCode, fault.faultString);
1858    }
1859
1860    struct FaultStruct
1861    {
1862      public int faultCode;
1863      public string faultString;
1864    }
1865
1866    struct FaultStructStringCode
1867    {
1868      public string faultCode;
1869      public string faultString;
1870    }
1871
1872    public void SerializeFaultResponse(
1873      Stream stm,
1874      XmlRpcFaultException faultEx)
1875    {
1876      FaultStruct fs;
1877      fs.faultCode = faultEx.FaultCode;
1878      fs.faultString = faultEx.FaultString;
1879
1880      XmlTextWriter xtw = new XmlTextWriter(stm, m_encoding);
1881      ConfigureXmlFormat(xtw);
1882      xtw.WriteStartDocument();
1883      xtw.WriteStartElement("", "methodResponse", "");
1884      xtw.WriteStartElement("", "fault", "");
1885      Serialize(xtw, fs, MappingAction.Error);
1886      xtw.WriteEndElement();
1887      xtw.WriteEndElement();
1888      xtw.Flush();
1889    }
1890
1891    void ConfigureXmlFormat(
1892      XmlTextWriter xtw)
1893    {
1894      if (m_bUseIndentation)
1895      {
1896        xtw.Formatting = Formatting.Indented;
1897        xtw.Indentation = m_indentation;
1898      }
1899      else
1900      {
1901        xtw.Formatting = Formatting.None;
1902      }
1903    }
1904
1905    string StackDump(ParseStack parseStack)
1906    {
1907      StringBuilder sb = new StringBuilder();
1908      foreach (string elem in parseStack)
1909      {
1910        sb.Insert(0, elem);
1911        sb.Insert(0, " : ");
1912      }
1913      sb.Insert(0, parseStack.ParseType);
1914      sb.Insert(0, "[");
1915      sb.Append("]");
1916      return sb.ToString();
1917    }
1918
1919    XmlNode SelectSingleNode(XmlNode node, string name)
1920    {
1921#if (COMPACT_FRAMEWORK)
1922      foreach (XmlNode selnode in node.ChildNodes)
1923      {
1924        // For "*" element else return null
1925        if ((name == "*") && !(selnode.Name.StartsWith("#")))
1926          return selnode;
1927        if (selnode.Name == name)
1928          return selnode;
1929      }
1930      return null;
1931#else
1932      return node.SelectSingleNode(name);
1933#endif
1934    }
1935
1936    XmlNode[] SelectNodes(XmlNode node, string name)
1937    {
1938      ArrayList list = new ArrayList();
1939      foreach (XmlNode selnode in node.ChildNodes)
1940      {
1941        if (selnode.Name == name)
1942          list.Add(selnode);
1943      }
1944      return (XmlNode[])list.ToArray(typeof(XmlNode));
1945    }
1946
1947    XmlNode SelectValueNode(XmlNode valueNode)
1948    {
1949      // an XML-RPC value is either held as the child node of a <value> element
1950      // or is just the text of the value node as an implicit string value
1951      XmlNode vvNode = SelectSingleNode(valueNode, "*");
1952      if (vvNode == null)
1953        vvNode = valueNode.FirstChild;
1954      return vvNode;
1955    }
1956
1957    void SelectTwoNodes(XmlNode node, string name1, out XmlNode node1,
1958      out bool dup1, string name2, out XmlNode node2, out bool dup2)
1959    {
1960      node1 = node2 = null;
1961      dup1 = dup2 = false;
1962      foreach (XmlNode selnode in node.ChildNodes)
1963      {
1964        if (selnode.Name == name1)
1965        {
1966          if (node1 == null)
1967            node1 = selnode;
1968          else
1969            dup1 = true;
1970        }
1971        else if (selnode.Name == name2)
1972        {
1973          if (node2 == null)
1974            node2 = selnode;
1975          else
1976            dup2 = true;
1977        }
1978      }
1979    }
1980
1981    // TODO: following to return Array?
1982    object CreateArrayInstance(Type type, object[] args)
1983    {
1984#if (!COMPACT_FRAMEWORK)
1985      return Activator.CreateInstance(type, args);
1986#else
1987                Object Arr = Array.CreateInstance(type.GetElementType(), (int)args[0]);
1988                return Arr;
1989#endif
1990    }
1991
1992    bool IsStructParamsMethod(MethodInfo mi)
1993    {
1994      if (mi == null)
1995        return false;
1996      bool ret = false;
1997      Attribute attr = Attribute.GetCustomAttribute(mi,
1998        typeof(XmlRpcMethodAttribute));
1999      if (attr != null)
2000      {
2001        XmlRpcMethodAttribute mattr = (XmlRpcMethodAttribute)attr;
2002        ret = mattr.StructParams;
2003      }
2004      return ret;
2005    }
2006
2007    //#if (DEBUG)
2008    public
2009      //#endif
2010    class ParseStack : Stack
2011    {
2012      public ParseStack(string parseType)
2013      {
2014        m_parseType = parseType;
2015      }
2016
2017      void Push(string str)
2018      {
2019        base.Push(str);
2020      }
2021
2022      public string ParseType
2023      {
2024        get { return m_parseType; }
2025      }
2026
2027      public string m_parseType = "";
2028    }
2029  }
2030
2031}
Note: See TracBrowser for help on using the repository browser.