2009-03-11 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / XmlFormatterDeserializer.cs
1 //
2 // XmlFormatterDeserializer.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005 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 #if NET_2_0
29 using System;
30 using System.Collections;
31 using System.IO;
32 using System.Reflection;
33 using System.Runtime.Serialization.Formatters.Binary;
34 using System.Xml;
35 using System.Xml.Schema;
36
37 using QName = System.Xml.XmlQualifiedName;
38
39 namespace System.Runtime.Serialization
40 {
41         internal class XmlFormatterDeserializer
42         {
43                 KnownTypeCollection types;
44                 IDataContractSurrogate surrogate;
45                 // 3.5 SP1 supports deserialization by reference (id->obj).
46                 // Though unlike XmlSerializer, it does not support forward-
47                 // reference resolution i.e. a referenced object must appear
48                 // before any references to it.
49                 Hashtable references = new Hashtable ();
50
51                 public static object Deserialize (XmlReader reader, Type type,
52                         KnownTypeCollection knownTypes, IDataContractSurrogate surrogate,
53                         string name, string Namespace, bool verifyObjectName)
54                 {
55                         reader.MoveToContent();
56                         if (verifyObjectName)
57                                 Verify (knownTypes, type, name, Namespace, reader);
58                         return new XmlFormatterDeserializer (knownTypes, surrogate).Deserialize (type, reader);
59                 }
60
61                 // Verify the top element name and namespace.
62                 private static void Verify (KnownTypeCollection knownTypes, Type type, string name, string Namespace, XmlReader reader)
63                 {
64                         QName graph_qname = new QName (reader.Name, reader.NamespaceURI);
65                         if (graph_qname.Name == name && graph_qname.Namespace == Namespace)
66                                 return;
67
68                         // <BClass .. i:type="EClass" >..</BClass>
69                         // Expecting type EClass : allowed
70                         // See test Serialize1b, and Serialize1c (for
71                         // negative cases)
72
73                         // Run through inheritance heirarchy .. 
74                         for (Type baseType = type; baseType != null; baseType = baseType.BaseType)
75                                 if (knownTypes.GetQName (baseType) == graph_qname)
76                                         return;
77
78                         QName typeQName = knownTypes.GetQName (type);
79                         throw new SerializationException (String.Format (
80                                 "Expecting element '{0}' from namespace '{1}'. Encountered 'Element' with name '{2}', namespace '{3}'",
81                                 typeQName.Name, typeQName.Namespace, graph_qname.Name, graph_qname.Namespace));
82                 }
83
84                 private XmlFormatterDeserializer (
85                         KnownTypeCollection knownTypes,
86                         IDataContractSurrogate surrogate)
87                 {
88                         this.types = knownTypes;
89                         this.surrogate = surrogate;
90                 }
91
92                 public Hashtable References {
93                         get { return references; }
94                 }
95
96                 // At the beginning phase, we still have to instantiate a new
97                 // target object even if fromContent is true.
98                 public object Deserialize (Type type, XmlReader reader)
99                 {
100                         string label = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
101                         object o = DeserializeCore (type, reader);
102
103                         if (label != null)
104                                 references.Add (label, o);
105
106                         return o;
107                 }
108
109                 public object DeserializeCore (Type type, XmlReader reader)
110                 {
111                         QName graph_qname = types.GetQName (type);
112                         string itype = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
113                         if (itype != null) {
114                                 string[] parts = itype.Split (':');
115                                 if (parts.Length > 1)
116                                         graph_qname = new QName (parts [1], reader.LookupNamespace (reader.NameTable.Get (parts[0])));
117                                 else
118                                         graph_qname = new QName (itype, reader.NamespaceURI);
119                         }
120
121                         string label = reader.GetAttribute ("Ref", KnownTypeCollection.MSSimpleNamespace);
122                         if (label != null) {
123                                 object o = references [label];
124                                 if (o == null)
125                                         throw new SerializationException (String.Format ("Deserialized object with reference Id '{0}' was not found", label));
126                                 reader.Skip ();
127                                 return o;
128                         }
129
130                         bool isNil = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace) == "true";
131
132                         if (isNil) {
133                                 reader.Skip ();
134                                 if (!type.IsValueType)
135                                         return null;
136                                 else if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
137                                         return null;
138                                 else 
139                                         throw new SerializationException (String.Format ("Value type {0} cannot be null.", type));
140                         }
141
142                         bool isEmpty = reader.IsEmptyElement;
143                         reader.ReadStartElement ();
144
145                         object res = DeserializeContent (graph_qname, type, reader);
146
147                         reader.MoveToContent ();
148                         if (reader.NodeType == XmlNodeType.EndElement)
149                                 reader.ReadEndElement ();
150                         else if (!isEmpty && reader.NodeType != XmlNodeType.None)
151                                 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.", reader.NodeType, reader.Name, reader.NamespaceURI, type.FullName));
152                         return res;
153                 }
154
155                 object DeserializeContent (QName name, Type type, XmlReader reader)
156                 {
157                         if (KnownTypeCollection.IsPrimitiveType (name)) {
158                                 string value;
159                                 if (reader.NodeType != XmlNodeType.Text)
160                                         if (type.IsValueType)
161                                                 return Activator.CreateInstance (type);
162                                         else
163                                                 // FIXME: Workaround for creating empty objects of the correct type.
164                                                 value = String.Empty;
165                                 else
166                                         value = reader.ReadContentAsString ();
167                                 return KnownTypeCollection.PredefinedTypeStringToObject (value, name.Name, reader);
168                         }
169
170                         SerializationMap map = types.FindUserMap (name);
171                         if (map == null && name.Namespace.StartsWith (KnownTypeCollection.DefaultClrNamespaceBase, StringComparison.Ordinal)) {
172                                 var it = GetTypeFromNamePair (name.Name, name.Namespace.Substring (KnownTypeCollection.DefaultClrNamespaceBase.Length));
173                                 if (types.TryRegister (it))
174                                         map = types.FindUserMap (name);
175                         }
176                         if (map == null)
177                                 throw new SerializationException (String.Format ("Unknown type {0} is used for DataContract with reference of name {1}. Any derived types of a data contract or a data member should be added to KnownTypes.", type, name));
178
179                         return map.DeserializeContent (reader, this);
180                 }
181
182                 Type GetTypeFromNamePair (string name, string ns)
183                 {
184                         foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ())
185                                 foreach (var t in ass.GetTypes ())
186                                         if (t.Name == name && t.Namespace == ns)
187                                                 return t;
188                         return null;
189                 }
190         }
191 }
192 #endif