Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / System.ServiceModel.Web / System.Runtime.Serialization.Json / TypeMap.cs
1 //
2 // TypeMap.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.Collections;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Globalization;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.Text;
38 using System.Xml;
39
40 namespace System.Runtime.Serialization.Json
41 {
42         class TypeMap
43         {
44                 static bool IsInvalidNCName (string name)
45                 {
46                         if (name == null || name.Length == 0)
47                                 return true;
48                         try {
49                                 XmlConvert.VerifyNCName (name);
50                         } catch (XmlException) {
51                                 return true;
52                         }
53                         return false;
54                 }
55
56                 public static TypeMap CreateTypeMap (Type type)
57                 {
58                         object [] atts = type.GetCustomAttributes (typeof (DataContractAttribute), true);
59                         if (atts.Length == 1)
60                                 return CreateTypeMap (type, (DataContractAttribute) atts [0]);
61
62                         atts = type.GetCustomAttributes (typeof (SerializableAttribute), false);
63                         if (atts.Length == 1)
64                                 return CreateTypeMap (type, null);
65
66                         if (IsPrimitiveType (type))
67                                 return null;
68
69 #if MOONLIGHT
70                         if (ExternalTypeMap.HasType (type))
71                                 return new ExternalTypeMap (type);
72 #endif
73                         return CreateDefaultTypeMap (type);
74                 }
75
76                 static bool IsPrimitiveType (Type type)
77                 {
78                         if (type.IsEnum)
79                                 return true;
80                         if (Type.GetTypeCode (type) != TypeCode.Object)
81                                 return true; // FIXME: it is likely hacky
82                         return false;
83                 }
84
85                 static TypeMap CreateDefaultTypeMap (Type type)
86                 {
87                         var l = new List<TypeMapMember> ();
88                         foreach (var fi in type.GetFields ())
89                                 if (!fi.IsStatic)
90                                         l.Add (new TypeMapField (fi, null));
91                         foreach (var pi in type.GetProperties ())
92                                 if (pi.CanRead && pi.CanWrite && !pi.GetGetMethod ().IsStatic && pi.GetIndexParameters ().Length == 0)
93                                         l.Add (new TypeMapProperty (pi, null));
94                         l.Sort ((x, y) => x.Order != y.Order ? x.Order - y.Order : String.Compare (x.Name, y.Name, StringComparison.Ordinal));
95                         return new TypeMap (type, null, l.ToArray ());
96                 }
97
98                 internal static bool IsDictionary (Type type)
99                 {
100                         if (type.GetInterface ("System.Collections.IDictionary", false) != null)
101                                 return true;
102                         if (type.GetInterface ("System.Collections.Generic.IDictionary`2", false) != null)
103                                 return true;
104                         return false;
105                 }
106
107                 internal static bool IsCollection (Type type)
108                 {
109                         if (IsPrimitiveType (type) || IsDictionary (type))
110                                 return false;
111                         if (type.GetInterface ("System.Collections.IEnumerable", false) != null)
112                                 return true;
113                         return false;
114                 }
115
116                 static TypeMap CreateTypeMap (Type type, DataContractAttribute dca)
117                 {
118                         if (dca != null && dca.Name != null && IsInvalidNCName (dca.Name))
119                                 throw new InvalidDataContractException (String.Format ("DataContractAttribute for type '{0}' has an invalid name", type));
120
121                         List<TypeMapMember> members = new List<TypeMapMember> ();
122
123                         foreach (FieldInfo fi in type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
124                                 if (dca != null) {
125                                         object [] atts = fi.GetCustomAttributes (typeof (DataMemberAttribute), true);
126                                         if (atts.Length == 0)
127                                                 continue;
128                                         DataMemberAttribute dma = (DataMemberAttribute) atts [0];
129                                         members.Add (new TypeMapField (fi, dma));
130                                 } else {
131                                         if (fi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length > 0)
132                                                 continue;
133                                         members.Add (new TypeMapField (fi, null));
134                                 }
135                         }
136
137                         if (dca != null) {
138                                 foreach (PropertyInfo pi in type.GetProperties (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
139                                         object [] atts = pi.GetCustomAttributes (typeof (DataMemberAttribute), true);
140                                         if (atts.Length == 0)
141                                                 continue;
142                                         if (pi.GetIndexParameters ().Length > 0)
143                                                 continue;
144                                         if (IsCollection (pi.PropertyType)) {
145                                                 if (!pi.CanRead)
146                                                         throw new InvalidDataContractException (String.Format ("Property {0} must have a getter", pi));
147                                         }
148                                         else if (!pi.CanRead || !pi.CanWrite)
149                                                 throw new InvalidDataContractException (String.Format ("Non-collection property {0} must have both getter and setter", pi));
150                                         DataMemberAttribute dma = (DataMemberAttribute) atts [0];
151                                         members.Add (new TypeMapProperty (pi, dma));
152                                 }
153                         }
154
155                         members.Sort (delegate (TypeMapMember m1, TypeMapMember m2) { return m1.Order != m2.Order ? m1.Order - m2.Order : String.CompareOrdinal (m1.Name, m2.Name); });
156                         return new TypeMap (type, dca == null ? null : dca.Name, members.ToArray ());
157                 }
158
159                 Type type;
160                 string element;
161                 TypeMapMember [] members;
162
163                 static readonly Type [] deser_methods_args = new Type [] { typeof (StreamingContext) };
164                 const BindingFlags binding_flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
165
166                 public TypeMap (Type type, string element, TypeMapMember [] orderedMembers)
167                 {
168                         this.type = type;
169                         this.element = element;
170                         this.members = orderedMembers;
171
172                         foreach (var mi in type.GetMethods (binding_flags)) {
173                                 if (mi.GetCustomAttributes (typeof (OnDeserializingAttribute), false).Length > 0)
174                                         OnDeserializing = mi;
175                                 else if (mi.GetCustomAttributes (typeof (OnDeserializedAttribute), false).Length > 0)
176                                         OnDeserialized = mi;
177                                 else if (mi.GetCustomAttributes (typeof (OnSerializingAttribute), false).Length > 0)
178                                         OnSerializing = mi;
179                                 else if (mi.GetCustomAttributes (typeof (OnSerializedAttribute), false).Length > 0)
180                                         OnSerialized = mi;
181                         }
182                 }
183
184                 public MethodInfo OnDeserializing { get; set; }
185                 public MethodInfo OnDeserialized { get; set; }
186                 public MethodInfo OnSerializing { get; set; }
187                 public MethodInfo OnSerialized { get; set; }
188
189                 public virtual void Serialize (JsonSerializationWriter outputter, object graph, string type)
190                 {
191                         if (OnSerializing != null)
192                                 OnSerializing.Invoke (graph, new object [] {new StreamingContext (StreamingContextStates.All)});
193
194                         outputter.Writer.WriteAttributeString ("type", type);
195                         foreach (TypeMapMember member in members) {
196                                 object memberObj = member.GetMemberOf (graph);
197                                 // FIXME: consider EmitDefaultValue
198                                 outputter.Writer.WriteStartElement (member.Name);
199                                 outputter.WriteObjectContent (memberObj, false, false);
200                                 outputter.Writer.WriteEndElement ();
201                         }
202
203                         if (OnSerialized != null)
204                                 OnSerialized.Invoke (graph, new object [] {new StreamingContext (StreamingContextStates.All)});
205                 }
206
207                 internal static object CreateInstance (Type type)
208                 {
209                         if (TypeMap.IsDictionary (type)) {
210                                 if (type.IsGenericType)
211                                         return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (type.GetGenericArguments ()));
212                                 else
213                                         return new Hashtable ();
214                         } else if (TypeMap.IsCollection (type)) {
215                                 if (type.IsGenericType)
216                                         return Activator.CreateInstance (typeof (List<>).MakeGenericType (type.GetGenericArguments ()));
217                                 else
218                                         return new ArrayList ();
219                         }
220                         else
221                                 return FormatterServices.GetUninitializedObject (type);
222                 }
223
224                 public virtual object Deserialize (JsonSerializationReader jsr)
225                 {
226                         XmlReader reader = jsr.Reader;
227                         bool isNull = reader.GetAttribute ("type") == "null";
228
229                         object ret = isNull ? null : CreateInstance (type);
230                         if (ret != null && OnDeserializing != null)
231                                 OnDeserializing.Invoke (ret, new object [] {new StreamingContext (StreamingContextStates.All)});
232                         Dictionary<TypeMapMember,bool> filled = new Dictionary<TypeMapMember,bool> ();
233
234                         reader.ReadStartElement ();
235                         for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
236                                 bool consumed = false;
237                                 for (int i = 0; i < members.Length; i++) {
238                                         TypeMapMember mm = members [i];
239                                         if (mm.Name == reader.LocalName && reader.NamespaceURI == String.Empty) {
240                                                 if (filled.ContainsKey (mm))
241                                                         throw new SerializationException (String.Format ("Object content '{0}' for '{1}' already appeared in the reader", reader.LocalName, type));
242                                                 mm.SetMemberValue (ret, jsr.ReadObject (mm.Type));
243                                                 filled [mm] = true;
244                                                 consumed = true;
245                                                 break;
246                                         }
247                                 }
248                                 if (!consumed)
249                                         reader.Skip ();
250                         }
251                         reader.ReadEndElement ();
252                         if (ret != null && OnDeserialized != null)
253                                 OnDeserialized.Invoke (ret, new object [] {new StreamingContext (StreamingContextStates.All)});
254                         return ret;
255                 }
256         }
257
258         abstract class TypeMapMember
259         {
260                 MemberInfo mi;
261                 DataMemberAttribute dma;
262
263                 protected TypeMapMember (MemberInfo mi, DataMemberAttribute dma)
264                 {
265                         this.mi = mi;
266                         this.dma = dma;
267                 }
268
269                 public string Name {
270                         get { return dma == null ? mi.Name : dma.Name ?? mi.Name; }
271                 }
272
273                 public bool EmitDefaultValue {
274                         get { return dma != null && dma.EmitDefaultValue; }
275                 }
276
277                 public bool IsRequired {
278                         get { return dma != null && dma.IsRequired; }
279                 }
280
281                 public int Order {
282                         get { return dma != null ? dma.Order : -1; }
283                 }
284
285                 public abstract Type Type { get; }
286
287                 public abstract object GetMemberOf (object owner);
288
289                 public abstract void SetMemberValue (object owner, object value);
290         }
291
292         class TypeMapField : TypeMapMember
293         {
294                 FieldInfo field;
295
296                 public TypeMapField (FieldInfo fi, DataMemberAttribute dma)
297                         : base (fi, dma)
298                 {
299                         this.field = fi;
300                 }
301
302                 public override Type Type {
303                         get { return field.FieldType; }
304                 }
305
306                 public override object GetMemberOf (object owner)
307                 {
308                         return field.GetValue (owner);
309                 }
310
311                 public override void SetMemberValue (object owner, object value)
312                 {
313                         field.SetValue (owner, value);
314                 }
315         }
316
317         class TypeMapProperty : TypeMapMember
318         {
319                 PropertyInfo property;
320
321                 public TypeMapProperty (PropertyInfo pi, DataMemberAttribute dma)
322                         : base (pi, dma)
323                 {
324                         this.property = pi;
325                 }
326
327                 public override Type Type {
328                         get { return property.PropertyType; }
329                 }
330
331                 public override object GetMemberOf (object owner)
332                 {
333                         return property.GetValue (owner, null);
334                 }
335
336                 public override void SetMemberValue (object owner, object value)
337                 {
338                         property.SetValue (owner, value, null);
339                 }
340         }
341 }