**** Merged from MCS ****
[mono-project.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSerializationWriterInterpreter.cs
blobfcd1cf229950c977a78f8d5bdaebb1ef371140df
1 //
2 // XmlSerializationWriterInterpreter.cs:
3 //
4 // Author:
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using System.Text;
33 using System.Collections;
34 using System.Reflection;
35 using System.Xml.Schema;
37 namespace System.Xml.Serialization
39 internal class XmlSerializationWriterInterpreter: XmlSerializationWriter
41 XmlMapping _typeMap;
42 SerializationFormat _format;
44 public XmlSerializationWriterInterpreter (XmlMapping typeMap)
46 _typeMap = typeMap;
47 _format = typeMap.Format;
50 protected override void InitCallbacks ()
52 ArrayList maps = _typeMap.RelatedMaps;
53 if (maps != null)
55 foreach (XmlTypeMapping map in maps) {
56 CallbackInfo info = new CallbackInfo (this, map);
57 if (map.TypeData.SchemaType == SchemaTypes.Enum) AddWriteCallback(map.TypeData.Type, map.XmlType, map.Namespace, new XmlSerializationWriteCallback (info.WriteEnum));
58 else AddWriteCallback(map.TypeData.Type, map.XmlType, map.Namespace, new XmlSerializationWriteCallback (info.WriteObject));
63 public void WriteRoot (object ob)
65 WriteStartDocument ();
67 if (_typeMap is XmlTypeMapping)
69 XmlTypeMapping mp = (XmlTypeMapping) _typeMap;
70 if (mp.TypeData.SchemaType == SchemaTypes.Class || mp.TypeData.SchemaType == SchemaTypes.Array)
71 TopLevelElement ();
73 if (_format == SerializationFormat.Literal)
74 WriteObject (mp, ob, mp.ElementName, mp.Namespace, true, false, true);
75 else
76 WritePotentiallyReferencingElement (mp.ElementName, mp.Namespace, ob, mp.TypeData.Type, true, false);
78 else if (ob is object[])
79 WriteMessage ((XmlMembersMapping)_typeMap, (object[]) ob);
80 else
81 throw CreateUnknownTypeException (ob);
83 WriteReferencedElements ();
86 protected XmlTypeMapping GetTypeMap (Type type)
88 ArrayList maps = _typeMap.RelatedMaps;
89 if (maps != null)
91 foreach (XmlTypeMapping map in maps)
92 if (map.TypeData.Type == type) return map;
94 throw new InvalidOperationException ("Type " + type + " not mapped");
97 protected virtual void WriteObject (XmlTypeMapping typeMap, object ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem)
99 if (ob == null)
101 if (isNullable)
103 if (_format == SerializationFormat.Literal) WriteNullTagLiteral(element, namesp);
104 else WriteNullTagEncoded (element, namesp);
106 return;
109 if (ob is XmlNode)
111 if (_format == SerializationFormat.Literal) WriteElementLiteral((XmlNode)ob, "", "", true, false);
112 else WriteElementEncoded((XmlNode)ob, "", "", true, false);
113 return;
116 if (typeMap.TypeData.SchemaType == SchemaTypes.XmlSerializable)
118 WriteSerializable ((IXmlSerializable)ob, element, namesp, isNullable);
119 return;
122 XmlTypeMapping map = typeMap.GetRealTypeMap (ob.GetType().FullName);
124 if (map == null)
126 WriteTypedPrimitive (element, namesp, ob, true);
127 return;
130 if (writeWrappingElem)
132 if (map != typeMap || _format == SerializationFormat.Encoded) needType = true;
133 WriteStartElement (element, namesp, ob);
136 if (needType)
137 WriteXsiType(map.XmlType, map.XmlTypeNamespace);
139 switch (map.TypeData.SchemaType)
141 case SchemaTypes.Class: WriteObjectElement (map, ob, element, namesp); break;
142 case SchemaTypes.Array: WriteListElement (map, ob, element, namesp); break;
143 case SchemaTypes.Primitive: WritePrimitiveElement (map, ob, element, namesp); break;
144 case SchemaTypes.Enum: WriteEnumElement (map, ob, element, namesp); break;
147 if (writeWrappingElem)
148 WriteEndElement (ob);
151 protected virtual void WriteMessage (XmlMembersMapping membersMap, object[] parameters)
153 if (membersMap.HasWrapperElement) {
154 TopLevelElement ();
155 WriteStartElement(membersMap.ElementName, membersMap.Namespace, (_format == SerializationFormat.Encoded));
157 if (Writer.LookupPrefix (XmlSchema.Namespace) == null)
158 WriteAttribute ("xmlns","xsd",XmlSchema.Namespace,XmlSchema.Namespace);
160 if (Writer.LookupPrefix (XmlSchema.InstanceNamespace) == null)
161 WriteAttribute ("xmlns","xsi",XmlSchema.InstanceNamespace,XmlSchema.InstanceNamespace);
164 WriteMembers ((ClassMap)membersMap.ObjectMap, parameters, true);
166 if (membersMap.HasWrapperElement)
167 WriteEndElement();
170 protected virtual void WriteObjectElement (XmlTypeMapping typeMap, object ob, string element, string namesp)
172 ClassMap map = (ClassMap)typeMap.ObjectMap;
173 if (map.NamespaceDeclarations != null)
174 WriteNamespaceDeclarations ((XmlSerializerNamespaces) map.NamespaceDeclarations.GetValue (ob));
176 WriteObjectElementAttributes (typeMap, ob);
177 WriteObjectElementElements (typeMap, ob);
180 protected virtual void WriteObjectElementAttributes (XmlTypeMapping typeMap, object ob)
182 ClassMap map = (ClassMap)typeMap.ObjectMap;
183 WriteAttributeMembers (map, ob, false);
186 protected virtual void WriteObjectElementElements (XmlTypeMapping typeMap, object ob)
188 ClassMap map = (ClassMap)typeMap.ObjectMap;
189 WriteElementMembers (map, ob, false);
192 void WriteMembers (ClassMap map, object ob, bool isValueList)
194 WriteAttributeMembers (map, ob, isValueList);
195 WriteElementMembers (map, ob, isValueList);
198 void WriteAttributeMembers (ClassMap map, object ob, bool isValueList)
200 // Write attributes
202 ICollection attributes = map.AttributeMembers;
203 if (attributes != null)
205 foreach (XmlTypeMapMemberAttribute attr in attributes) {
206 if (MemberHasValue (attr, ob, isValueList))
207 WriteAttribute (attr.AttributeName, attr.Namespace, GetStringValue (attr.MappedType, attr.TypeData, GetMemberValue (attr, ob, isValueList)));
211 XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember;
212 if (anyAttrMember != null && MemberHasValue (anyAttrMember, ob, isValueList))
214 ICollection extraAtts = (ICollection) GetMemberValue (anyAttrMember, ob, isValueList);
215 if (extraAtts != null)
217 foreach (XmlAttribute attr in extraAtts)
218 WriteXmlAttribute (attr, ob);
223 void WriteElementMembers (ClassMap map, object ob, bool isValueList)
225 ICollection members = map.ElementMembers;
226 if (members != null)
228 foreach (XmlTypeMapMemberElement member in members)
230 if (!MemberHasValue (member, ob, isValueList)) continue;
231 object memberValue = GetMemberValue (member, ob, isValueList);
232 Type memType = member.GetType();
234 if (memType == typeof(XmlTypeMapMemberList))
236 WriteMemberElement ((XmlTypeMapElementInfo) member.ElementInfo[0], memberValue);
238 else if (memType == typeof(XmlTypeMapMemberFlatList))
240 if (memberValue != null)
241 WriteListContent (member.TypeData, ((XmlTypeMapMemberFlatList)member).ListMap, memberValue, null);
243 else if (memType == typeof(XmlTypeMapMemberAnyElement))
245 if (memberValue != null)
246 WriteAnyElementContent ((XmlTypeMapMemberAnyElement)member, memberValue);
248 else if (memType == typeof(XmlTypeMapMemberAnyAttribute))
250 // Ignore
252 else if (memType == typeof(XmlTypeMapMemberElement))
254 XmlTypeMapElementInfo elem = member.FindElement (ob, memberValue);
255 WriteMemberElement (elem, memberValue);
257 else
258 throw new InvalidOperationException ("Unknown member type");
263 object GetMemberValue (XmlTypeMapMember member, object ob, bool isValueList)
265 if (isValueList) return ((object[])ob)[member.Index];
266 else return member.GetValue (ob);
269 bool MemberHasValue (XmlTypeMapMember member, object ob, bool isValueList)
271 if (isValueList) {
272 return member.Index < ((object[])ob).Length;
274 else if (member.DefaultValue != System.DBNull.Value) {
275 object val = GetMemberValue (member, ob, isValueList);
276 if (val == null && member.DefaultValue == null) return false;
277 if (val != null && val.GetType().IsEnum)
279 if (val.Equals (member.DefaultValue)) return false;
280 Type t = Enum.GetUnderlyingType(val.GetType());
281 val = Convert.ChangeType (val, t);
283 if (val != null && val.Equals (member.DefaultValue)) return false;
285 else if (member.IsOptionalValueType)
286 return member.GetValueSpecified (ob);
288 return true;
291 void WriteMemberElement (XmlTypeMapElementInfo elem, object memberValue)
293 switch (elem.TypeData.SchemaType)
295 case SchemaTypes.XmlNode:
296 string elemName = elem.WrappedElement ? elem.ElementName : "";
297 if (_format == SerializationFormat.Literal) WriteElementLiteral(((XmlNode)memberValue), elemName, elem.Namespace, elem.IsNullable, false);
298 else WriteElementEncoded(((XmlNode)memberValue), elemName, elem.Namespace, elem.IsNullable, false);
299 break;
301 case SchemaTypes.Enum:
302 case SchemaTypes.Primitive:
303 if (_format == SerializationFormat.Literal)
304 WritePrimitiveValueLiteral (memberValue, elem.ElementName, elem.Namespace, elem.MappedType, elem.TypeData, elem.WrappedElement, elem.IsNullable);
305 else
306 WritePrimitiveValueEncoded (memberValue, elem.ElementName, elem.Namespace, new XmlQualifiedName (elem.TypeData.XmlType, elem.DataTypeNamespace), elem.MappedType, elem.TypeData, elem.WrappedElement, elem.IsNullable);
307 break;
309 case SchemaTypes.Array:
310 if (memberValue == null) {
311 if (!elem.IsNullable) return;
312 if (_format == SerializationFormat.Literal) WriteNullTagLiteral (elem.ElementName, elem.Namespace);
313 else WriteNullTagEncoded (elem.ElementName, elem.Namespace);
315 else if (elem.MappedType.MultiReferenceType)
316 WriteReferencingElement (elem.ElementName, elem.Namespace, memberValue, elem.IsNullable);
317 else {
318 WriteStartElement(elem.ElementName, elem.Namespace, memberValue);
319 WriteListContent (elem.TypeData, (ListMap) elem.MappedType.ObjectMap, memberValue, null);
320 WriteEndElement (memberValue);
322 break;
324 case SchemaTypes.Class:
325 if (elem.MappedType.MultiReferenceType) {
326 if (elem.MappedType.TypeData.Type == typeof(object))
327 WritePotentiallyReferencingElement (elem.ElementName, elem.Namespace, memberValue, null, false, elem.IsNullable);
328 else
329 WriteReferencingElement (elem.ElementName, elem.Namespace, memberValue, elem.IsNullable);
331 else WriteObject (elem.MappedType, memberValue, elem.ElementName, elem.Namespace, elem.IsNullable, false, true);
332 break;
334 case SchemaTypes.XmlSerializable:
335 WriteSerializable ((IXmlSerializable) memberValue, elem.ElementName, elem.Namespace, elem.IsNullable);
336 break;
338 default:
339 throw new NotSupportedException ("Invalid value type");
343 void WritePrimitiveValueLiteral (object memberValue, string name, string ns, XmlTypeMapping mappedType, TypeData typeData, bool wrapped, bool isNullable)
345 if (!wrapped) {
346 WriteValue (GetStringValue (mappedType, typeData, memberValue));
348 else if (isNullable) {
349 if (typeData.Type == typeof(XmlQualifiedName)) WriteNullableQualifiedNameLiteral (name, ns, (XmlQualifiedName)memberValue);
350 else WriteNullableStringLiteral (name, ns, GetStringValue (mappedType, typeData, memberValue));
352 else {
353 if (typeData.Type == typeof(XmlQualifiedName)) WriteElementQualifiedName (name, ns, (XmlQualifiedName)memberValue);
354 else WriteElementString (name, ns, GetStringValue (mappedType, typeData, memberValue));
358 void WritePrimitiveValueEncoded (object memberValue, string name, string ns, XmlQualifiedName xsiType, XmlTypeMapping mappedType, TypeData typeData, bool wrapped, bool isNullable)
360 if (!wrapped) {
361 WriteValue (GetStringValue (mappedType, typeData, memberValue));
363 else if (isNullable) {
364 if (typeData.Type == typeof(XmlQualifiedName)) WriteNullableQualifiedNameEncoded (name, ns, (XmlQualifiedName)memberValue, xsiType);
365 else WriteNullableStringEncoded (name, ns, GetStringValue (mappedType, typeData, memberValue), xsiType);
367 else {
368 if (typeData.Type == typeof(XmlQualifiedName)) WriteElementQualifiedName (name, ns, (XmlQualifiedName)memberValue, xsiType);
369 else WriteElementString (name, ns, GetStringValue (mappedType, typeData, memberValue), xsiType);
373 protected virtual void WriteListElement (XmlTypeMapping typeMap, object ob, string element, string namesp)
375 if (_format == SerializationFormat.Encoded)
377 string n, ns;
378 int itemCount = GetListCount (typeMap.TypeData, ob);
379 ((ListMap) typeMap.ObjectMap).GetArrayType (itemCount, out n, out ns);
380 string arrayType = (ns != string.Empty) ? FromXmlQualifiedName (new XmlQualifiedName(n,ns)) : n;
381 WriteAttribute ("arrayType", XmlSerializer.EncodingNamespace, arrayType);
383 WriteListContent (typeMap.TypeData, (ListMap) typeMap.ObjectMap, ob, null);
386 void WriteListContent (TypeData listType, ListMap map, object ob, StringBuilder targetString)
388 if (listType.Type.IsArray)
390 Array array = (Array)ob;
391 for (int n=0; n<array.Length; n++)
393 object item = array.GetValue (n);
394 XmlTypeMapElementInfo info = map.FindElement (item);
395 if (info != null && targetString == null) WriteMemberElement (info, item);
396 else if (info != null && targetString != null) targetString.Append (GetStringValue (info.MappedType, info.TypeData, item)).Append (" ");
397 else if (item != null) throw CreateUnknownTypeException (item);
400 else if (ob is ICollection)
402 int count = (int) listType.Type.GetProperty ("Count").GetValue(ob,null);
403 PropertyInfo itemProp = TypeData.GetIndexerProperty (listType.Type);
404 object[] index = new object[1];
405 for (int n=0; n<count; n++)
407 index[0] = n;
408 object item = itemProp.GetValue (ob, index);
409 XmlTypeMapElementInfo info = map.FindElement (item);
410 if (info != null && targetString == null) WriteMemberElement (info, item);
411 else if (info != null && targetString != null) targetString.Append (GetStringValue (info.MappedType, info.TypeData, item)).Append (" ");
412 else if (item != null) throw CreateUnknownTypeException (item);
415 else if (ob is IEnumerable)
417 IEnumerable e = (IEnumerable)ob;
418 foreach (object item in e)
420 XmlTypeMapElementInfo info = map.FindElement (item);
421 if (info != null && targetString == null) WriteMemberElement (info, item);
422 else if (info != null && targetString != null) targetString.Append (GetStringValue (info.MappedType, info.TypeData, item)).Append (" ");
423 else if (item != null) throw CreateUnknownTypeException (item);
426 else
427 throw new Exception ("Unsupported collection type");
430 int GetListCount (TypeData listType, object ob)
432 if (listType.Type.IsArray)
433 return ((Array)ob).Length;
434 else
435 return (int) listType.Type.GetProperty ("Count").GetValue(ob,null);
438 void WriteAnyElementContent (XmlTypeMapMemberAnyElement member, object memberValue)
440 if (member.TypeData.Type == typeof (XmlElement)) {
441 memberValue = new object[] { memberValue };
444 bool canBeText = member.CanBeText;
445 Array elems = (Array) memberValue;
446 foreach (XmlNode elem in elems)
448 if (elem is XmlElement)
450 if (member.IsElementDefined (elem.Name, elem.NamespaceURI))
452 if (_format == SerializationFormat.Literal) WriteElementLiteral (elem, "", "", false, true);
453 else WriteElementEncoded (elem, "", "", false, true);
455 else
456 throw CreateUnknownAnyElementException (elem.Name, elem.NamespaceURI);
458 else if (elem is XmlCharacterData)
459 elem.WriteTo (Writer);
460 else
461 throw CreateUnknownTypeException (elem);
465 protected virtual void WritePrimitiveElement (XmlTypeMapping typeMap, object ob, string element, string namesp)
467 Writer.WriteString (GetStringValue (typeMap, typeMap.TypeData, ob));
470 protected virtual void WriteEnumElement (XmlTypeMapping typeMap, object ob, string element, string namesp)
472 Writer.WriteString (GetEnumXmlValue (typeMap, ob));
475 string GetStringValue (XmlTypeMapping typeMap, TypeData type, object value)
477 if (type.SchemaType == SchemaTypes.Array) {
478 if (value == null) return null;
479 StringBuilder sb = new StringBuilder ();
480 WriteListContent (typeMap.TypeData, (ListMap)typeMap.ObjectMap, value, sb);
481 return sb.ToString ().Trim ();
483 else if (type.SchemaType == SchemaTypes.Enum)
484 return GetEnumXmlValue (typeMap, value);
485 else if (type.Type == typeof (XmlQualifiedName))
486 return FromXmlQualifiedName ((XmlQualifiedName)value);
487 else if (value == null)
488 return null;
489 else
490 return XmlCustomFormatter.ToXmlString (type, value);
493 string GetEnumXmlValue (XmlTypeMapping typeMap, object ob)
495 EnumMap map = (EnumMap)typeMap.ObjectMap;
496 return map.GetXmlName (ob);
499 class CallbackInfo
501 XmlSerializationWriterInterpreter _swi;
502 XmlTypeMapping _typeMap;
504 public CallbackInfo (XmlSerializationWriterInterpreter swi, XmlTypeMapping typeMap)
506 _swi = swi;
507 _typeMap = typeMap;
510 internal void WriteObject (object ob)
512 _swi.WriteObject (_typeMap, ob, _typeMap.ElementName, _typeMap.Namespace, false, false, false);
515 internal void WriteEnum (object ob)
517 _swi.WriteObject (_typeMap, ob, _typeMap.ElementName, _typeMap.Namespace, false, true, false);