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