Merge branch 'BigIntegerParse'
[mono.git] / mcs / class / System.ServiceModel.Web / System.ServiceModel.Dispatcher / JsonQueryStringConverter.cs
1 //
2 // JsonQueryStringConverter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Globalization;
30 using System.IO;
31 using System.Runtime.Serialization.Json;
32 using System.ServiceModel;
33 using System.ServiceModel.Description;
34 using System.Text;
35 using System.Xml;
36
37 namespace System.ServiceModel.Dispatcher
38 {
39         public class JsonQueryStringConverter : QueryStringConverter
40         {
41                 DataContractJsonSerializer serializer = new DataContractJsonSerializer (typeof (object));
42
43                 internal string CustomWrapperName { get; set; }
44
45                 public override bool CanConvert (Type type)
46                 {
47                         // almost copy from QueryStringConverter, except that DBNull and XmlQualifiedName are supported
48                         switch (Type.GetTypeCode (type)) {
49                         //case TypeCode.DBNull:
50                         case TypeCode.Empty:
51                                 return false;
52                         case TypeCode.Object:
53                                 if (type == typeof (TimeSpan))
54                                         return true;
55                                 if (type == typeof (DateTimeOffset))
56                                         return true;
57                                 if (type == typeof (Guid))
58                                         return true;
59                                 if (type == typeof (XmlQualifiedName))
60                                         return true;
61                                 if (type == typeof (object))
62                                         return true;
63 //                              if (type.GetCustomAttributes (typeof (TypeConverterAttribute), true).Length > 0)
64 //                                      return true;
65
66                                 // FIXME: it should return false for things like List<OfPrivateType>.
67                                 return type.IsPublic || type.IsNestedPublic;
68                         default:
69                                 return true;
70                         }
71                 }
72
73                 public override object ConvertStringToValue (string parameter, Type parameterType)
74                 {
75                         if (parameterType == null)
76                                 throw new ArgumentNullException ("parameterType");
77
78                         if (!CanConvert (parameterType))
79                                 throw new NotSupportedException (String.Format ("Conversion from the argument parameterType '{0}' is not supported", parameterType));
80
81                         // In general .NET JSON parser is sloppy. It accepts 
82                         // such a string that is actually invalid in terms of
83                         // the target type in JSON context.
84
85                         switch (Type.GetTypeCode (parameterType)) {
86                         case TypeCode.String:
87                                 // LAMESPEC LAMESPEC LAMESPEC: we cannot give "foo" as the string value input (even if they are escaped as %22!)
88                                 if (parameter == null)
89                                         return null;
90                                 if (parameter.Length > 1 && parameter [0] == '"' && parameter [parameter.Length - 1] == '"')
91                                         return parameter.Substring (1, parameter.Length - 2);
92                                 else if (parameter [0] != '"')
93                                         return parameter;
94                                 break;
95                         case TypeCode.Char:
96                                 return parameter != null ? Char.Parse (parameter): default (char);
97                         case TypeCode.SByte:
98                                 return parameter != null ? SByte.Parse (parameter, CultureInfo.InvariantCulture): default (sbyte);
99                         case TypeCode.Byte:
100                                 return parameter != null ? Byte.Parse (parameter, CultureInfo.InvariantCulture): default (byte);
101                         case TypeCode.Int16:
102                                 return parameter != null ? Int16.Parse (parameter, CultureInfo.InvariantCulture): default (short);
103                         case TypeCode.Int32:
104                                 return parameter != null ? Int32.Parse (parameter, CultureInfo.InvariantCulture): default (int);
105                         case TypeCode.Int64:
106                                 return parameter != null ? Int64.Parse (parameter, CultureInfo.InvariantCulture): default (long);
107                         case TypeCode.UInt16:
108                                 return parameter != null ? UInt16.Parse (parameter, CultureInfo.InvariantCulture): default (ushort);
109                         case TypeCode.UInt32:
110                                 return parameter != null ? UInt32.Parse (parameter, CultureInfo.InvariantCulture): default (uint);
111                         case TypeCode.UInt64:
112                                 return parameter != null ? UInt64.Parse (parameter, CultureInfo.InvariantCulture): default (ulong);
113                         case TypeCode.DateTime:
114                                 return parameter != null ? DateTime.Parse (parameter, CultureInfo.InvariantCulture): default (DateTime);
115                         case TypeCode.Boolean:
116                                 return parameter != null ? Boolean.Parse (parameter): default (bool);
117                         case TypeCode.Single:
118                                 return parameter != null ? Single.Parse (parameter, CultureInfo.InvariantCulture): default (float);
119                         case TypeCode.Double:
120                                 return parameter != null ? Double.Parse (parameter, CultureInfo.InvariantCulture): default (double);
121                         case TypeCode.Decimal:
122                                 return parameter != null ? Decimal.Parse (parameter, CultureInfo.InvariantCulture): default (decimal);
123                         }
124
125                         if (parameter == null)
126                                 return null;
127
128
129                         DataContractJsonSerializer serializer =
130                                 new DataContractJsonSerializer (parameterType);
131                         // hmm, it costs so silly though.
132                         return serializer.ReadObject (new MemoryStream (Encoding.UTF8.GetBytes (parameter)));
133                 }
134
135                 bool IsKnownType (Type t)
136                 {
137                         switch (Type.GetTypeCode (t)) {
138                         case TypeCode.Object:
139                                 if (t == typeof (Guid) ||
140                                     t == typeof (DBNull) ||
141                                     t == typeof (DateTimeOffset) ||
142                                     t == typeof (TimeSpan) ||
143                                     t == typeof (XmlQualifiedName))
144                                         return true;
145                                 return false;
146                         default:
147                                 return true;
148                         }
149                 }
150
151                 public override string ConvertValueToString (object parameter, Type parameterType)
152                 {
153                         if (parameterType == null)
154                                 throw new ArgumentNullException ("parameterType");
155
156                         if (!CanConvert (parameterType))
157                                 throw new NotSupportedException (String.Format ("Conversion from the argument parameterType '{0}' is not supported", parameterType));
158
159                         if (parameter == null)
160                                 return null;
161
162                         if (parameter is DBNull)
163                                 return "{}";
164
165                         parameterType = ToActualType (parameterType);
166
167                         if (parameter is IConvertible)
168                                 parameter = ((IConvertible) parameter).ToType (parameterType, CultureInfo.InvariantCulture);
169
170                         switch (Type.GetTypeCode (parameterType)) {
171                         case TypeCode.String:
172                                 string s = parameter is IFormattable ?
173                                         ((IFormattable) parameter).ToString (null, CultureInfo.InvariantCulture) :
174                                         parameter.ToString ();
175                                 StringBuilder sb = new StringBuilder (s);
176                                 sb.Replace ("\"", "\\\"");
177                                 sb.Insert (0, "\"");
178                                 sb.Append ('\"');
179                                 return sb.ToString ();
180                         default:
181                                 if (parameterType == typeof (XmlQualifiedName)) {
182                                         var qname = (XmlQualifiedName) parameter;
183                                         return String.Concat ("\"", qname.Name, ":", qname.Namespace, "\"");
184                                 }
185                                 return parameter.ToString ();
186                         }
187                 }
188
189                 Type ToActualType (Type t)
190                 {
191                         switch (Type.GetTypeCode (t)) {
192                         case TypeCode.DBNull: // though DBNull.Value input is converted to "{}". This result is used for String input.
193                         case TypeCode.Char:
194                         case TypeCode.String:
195                                 return typeof (string);
196                         case TypeCode.SByte:
197                         case TypeCode.Int16:
198                         case TypeCode.Int32:
199                         case TypeCode.Int64:
200 //                              return typeof (long);
201                                 return typeof (decimal);
202                         case TypeCode.Byte:
203                         case TypeCode.UInt16:
204                         case TypeCode.UInt32:
205                         case TypeCode.UInt64:
206 //                              return typeof (ulong);
207                                 return typeof (decimal);
208                         case TypeCode.DateTime:
209                         case TypeCode.Boolean:
210                                 return t;
211                         case TypeCode.Single:
212                         case TypeCode.Double:
213 //                              return typeof (double);
214                                 return typeof (decimal);
215                         case TypeCode.Decimal:
216                                 return typeof (decimal);
217                         default:
218                                 return t;
219                         }
220                 }
221         }
222 }