namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Globalization;
/// Parser context, we maintain contexts in a stack to avoiding recursion.
struct Context
{
public String Name;
public Object Container;
}
/// Basic XML-RPC data deserializer.
/// Uses XmlTextReader to parse the XML data. This level of the class
/// only handles the tokens common to both Requests and Responses. This class is not useful in and of itself
/// but is designed to be subclassed.
public class XmlRpcDeserializer : XmlRpcXmlTokens
{
private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo();
private Object _container;
private Stack _containerStack;
/// Protected reference to last text.
protected String _text;
/// Protected reference to last deserialized value.
protected Object _value;
/// Protected reference to last name field.
protected String _name;
/// Basic constructor.
public XmlRpcDeserializer()
{
Reset();
_dateFormat.FullDateTimePattern = ISO_DATETIME;
}
/// Static method that parses XML data into a response using the Singleton.
/// StreamReader containing an XML-RPC response.
/// Object object resulting from the deserialization.
virtual public Object Deserialize(TextReader xmlData)
{
return null;
}
/// Protected method to parse a node in an XML-RPC XML stream.
/// Method deals with elements common to all XML-RPC data, subclasses of
/// this object deal with request/response spefic elements.
/// XmlTextReader of the in progress parsing data stream.
protected void DeserializeNode(XmlTextReader reader)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (Logger.Delegate != null)
Logger.WriteEntry("START " + reader.Name, LogLevel.Information);
switch (reader.Name)
{
case VALUE:
_value = null;
_text = null;
break;
case STRUCT:
PushContext();
_container = new Hashtable();
break;
case ARRAY:
PushContext();
_container = new ArrayList();
break;
}
break;
case XmlNodeType.EndElement:
if (Logger.Delegate != null)
Logger.WriteEntry("END " + reader.Name, LogLevel.Information);
switch (reader.Name)
{
case BASE64:
_value = Convert.FromBase64String(_text);
break;
case BOOLEAN:
int val = Int16.Parse(_text);
if (val == 0)
_value = false;
else if (val == 1)
_value = true;
break;
case STRING:
_value = _text;
break;
case DOUBLE:
_value = Double.Parse(_text);
break;
case INT:
case ALT_INT:
_value = Int32.Parse(_text);
break;
case DATETIME:
#if __MONO__
_value = DateParse(_text);
#else
_value = DateTime.ParseExact(_text, "F", _dateFormat);
#endif
break;
case NAME:
_name = _text;
break;
case VALUE:
if (_value == null)
_value = _text; // some kits don't use tag, they just do
if ((_container != null) && (_container is IList)) // in an array? If so add value to it.
((IList)_container).Add(_value);
break;
case MEMBER:
if ((_container != null) && (_container is IDictionary)) // in an struct? If so add value to it.
((IDictionary)_container).Add(_name, _value);
break;
case ARRAY:
case STRUCT:
_value = _container;
PopContext();
break;
}
break;
case XmlNodeType.Text:
if (Logger.Delegate != null)
Logger.WriteEntry("Text " + reader.Value, LogLevel.Information);
_text = reader.Value;
break;
default:
break;
}
}
/// Static method that parses XML in a String into a
/// request using the Singleton.
/// String containing an XML-RPC request.
/// XmlRpcRequest object resulting from the parse.
public Object Deserialize(String xmlData)
{
StringReader sr = new StringReader(xmlData);
return Deserialize(sr);
}
/// Pop a Context of the stack, an Array or Struct has closed.
private void PopContext()
{
Context c = (Context)_containerStack.Pop();
_container = c.Container;
_name = c.Name;
}
/// Push a Context on the stack, an Array or Struct has opened.
private void PushContext()
{
Context context;
context.Container = _container;
context.Name = _name;
_containerStack.Push(context);
}
/// Reset the internal state of the deserializer.
protected void Reset()
{
_text = null;
_value = null;
_name = null;
_container = null;
_containerStack = new Stack();
}
#if __MONO__
private DateTime DateParse(String str)
{
int year = Int32.Parse(str.Substring(0,4));
int month = Int32.Parse(str.Substring(4,2));
int day = Int32.Parse(str.Substring(6,2));
int hour = Int32.Parse(str.Substring(9,2));
int min = Int32.Parse(str.Substring(12,2));
int sec = Int32.Parse(str.Substring(15,2));
return new DateTime(year,month,day,hour,min,sec);
}
#endif
}
}