aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Common/XmlRpcCS/XmlRpcDeserializer.cs
blob: bffda57dc09d8ea45cc8f3e597852c8347c6bf8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
* Copyright (c) Contributors, http://www.openmetaverse.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the OpenSim Project nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
*/
namespace Nwc.XmlRpc
{
    using System;
    using System.Collections;
    using System.IO;
    using System.Xml;
    using System.Globalization;

    /// <summary>Parser context, we maintain contexts in a stack to avoiding recursion. </summary>
    struct Context
    {
        public String Name;
        public Object Container;
    }

    /// <summary>Basic XML-RPC data deserializer.</summary>
    /// <remarks>Uses <c>XmlTextReader</c> 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.</remarks>
    public class XmlRpcDeserializer : XmlRpcXmlTokens
    {
        private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo();

        private Object _container;
        private Stack _containerStack;

        /// <summary>Protected reference to last text.</summary>
        protected String _text;
        /// <summary>Protected reference to last deserialized value.</summary>
        protected Object _value;
        /// <summary>Protected reference to last name field.</summary>
        protected String _name;


        /// <summary>Basic constructor.</summary>
        public XmlRpcDeserializer()
        {
            Reset();
            _dateFormat.FullDateTimePattern = ISO_DATETIME;
        }

        /// <summary>Static method that parses XML data into a response using the Singleton.</summary>
        /// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC response.</param>
        /// <returns><c>Object</c> object resulting from the deserialization.</returns>
        virtual public Object Deserialize(TextReader xmlData)
        {
            return null;
        }

        /// <summary>Protected method to parse a node in an XML-RPC XML stream.</summary>
        /// <remarks>Method deals with elements common to all XML-RPC data, subclasses of 
        /// this object deal with request/response spefic elements.</remarks>
        /// <param name="reader"><c>XmlTextReader</c> of the in progress parsing data stream.</param>
        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 <string> tag, they just do <value>

                            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;
            }
        }

        /// <summary>Static method that parses XML in a <c>String</c> into a 
        /// request using the Singleton.</summary>
        /// <param name="xmlData"><c>String</c> containing an XML-RPC request.</param>
        /// <returns><c>XmlRpcRequest</c> object resulting from the parse.</returns>
        public Object Deserialize(String xmlData)
        {
            StringReader sr = new StringReader(xmlData);
            return Deserialize(sr);
        }

        /// <summary>Pop a Context of the stack, an Array or Struct has closed.</summary>
        private void PopContext()
        {
            Context c = (Context)_containerStack.Pop();
            _container = c.Container;
            _name = c.Name;
        }

        /// <summary>Push a Context on the stack, an Array or Struct has opened.</summary>
        private void PushContext()
        {
            Context context;

            context.Container = _container;
            context.Name = _name;

            _containerStack.Push(context);
        }

        /// <summary>Reset the internal state of the deserializer.</summary>
        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

    }
}