1 //
2 // System.Xml.Serialization.XmlSerializationWriter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10 
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 
32 using System;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.Globalization;
36 using System.Text;
37 using System.Xml;
38 using System.Xml.Schema;
39 using System.Runtime.Serialization;
40 using System.Reflection;
41 
42 namespace System.Xml.Serialization
43 {
44 	public abstract class XmlSerializationWriter
45 		: XmlSerializationGeneratedCode
46 	{
47 
48 		#region Fields
49 
50 		ObjectIDGenerator idGenerator;
51 		int qnameCount;
52 		bool topLevelElement = false;
53 
54 		ArrayList namespaces;
55 		XmlWriter writer;
56 		Queue referencedElements;
57 		Hashtable callbacks;
58 		Hashtable serializedObjects;
59 		const string xmlNamespace = "http://www.w3.org/2000/xmlns/";
60 		const string unexpectedTypeError = "The type {0} was not expected. Use the" +
61 			" XmlInclude or SoapInclude attribute to specify types that are not known statically.";
62 
63 		#endregion // Fields
64 
65 		#region Constructors
66 
XmlSerializationWriter()67 		protected XmlSerializationWriter ()
68 		{
69 			qnameCount = 0;
70 			serializedObjects = new Hashtable ();
71 		}
72 
Initialize(XmlWriter writer, XmlSerializerNamespaces nss)73 		internal void Initialize (XmlWriter writer, XmlSerializerNamespaces nss)
74 		{
75 			this.writer = writer;
76 			if (nss != null)
77 			{
78 				namespaces = new ArrayList ();
79 				foreach (XmlQualifiedName ns in nss.ToArray())
80 					if (ns.Name != "" && ns.Namespace != "")
81 						namespaces.Add (ns);
82 			}
83 		}
84 
85 		#endregion // Constructors
86 
87 		#region Properties
88 
89 		protected ArrayList Namespaces {
90 			get { return namespaces; }
91 			set { namespaces = value; }
92 		}
93 
94 		protected XmlWriter Writer {
95 			get { return writer; }
96 			set { writer = value; }
97 		}
98 
99 		#endregion // Properties
100 
101 		#region Methods
102 
AddWriteCallback(Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)103 		protected void AddWriteCallback (Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)
104 		{
105 			WriteCallbackInfo info = new WriteCallbackInfo ();
106 			info.Type = type;
107 			info.TypeName = typeName;
108 			info.TypeNs = typeNs;
109 			info.Callback = callback;
110 
111 			if (callbacks == null) callbacks = new Hashtable ();
112 			callbacks.Add (type, info);
113 		}
114 
CreateChoiceIdentifierValueException(string value, string identifier, string name, string ns)115 		protected Exception CreateChoiceIdentifierValueException (string value, string identifier, string name, string ns)
116 		{
117 			string message = string.Format ("Value '{0}' of the choice"
118 				+ " identifier '{1}' does not match element '{2}'"
119 				+ " from namespace '{3}'.", value, identifier,
120 				name, ns);
121 			return new InvalidOperationException (message);
122 		}
123 
CreateInvalidChoiceIdentifierValueException(string type, string identifier)124 		protected Exception CreateInvalidChoiceIdentifierValueException (string type, string identifier)
125 		{
126 			string message = string.Format ("Invalid or missing choice"
127 				+ " identifier '{0}' of type '{1}'.", identifier,
128 				type);
129 			return new InvalidOperationException (message);
130 		}
131 
CreateMismatchChoiceException(string value, string elementName, string enumValue)132 		protected Exception CreateMismatchChoiceException (string value, string elementName, string enumValue)
133 		{
134 			string message = String.Format ("Value of {0} mismatches the type of {1}, you need to set it to {2}.", elementName, value, enumValue);
135 			return new InvalidOperationException (message);
136 		}
137 
CreateUnknownAnyElementException(string name, string ns)138 		protected Exception CreateUnknownAnyElementException (string name, string ns)
139 		{
140 			string message = String.Format ("The XML element named '{0}' from namespace '{1}' was not expected. The XML element name and namespace must match those provided via XmlAnyElementAttribute(s).", name, ns);
141 			return new InvalidOperationException (message);
142 		}
143 
CreateUnknownTypeException(object o)144 		protected Exception CreateUnknownTypeException (object o)
145 		{
146 			return CreateUnknownTypeException (o.GetType ());
147 		}
148 
CreateUnknownTypeException(Type type)149 		protected Exception CreateUnknownTypeException (Type type)
150 		{
151 			string message = String.Format ("The type {0} may not be used in this context.", type);
152 			return new InvalidOperationException (message);
153 		}
154 
FromByteArrayBase64(byte[] value)155 		protected static byte[] FromByteArrayBase64 (byte[] value)
156 		{
157 			return value;
158 		}
159 
FromByteArrayHex(byte[] value)160 		protected static string FromByteArrayHex (byte[] value)
161 		{
162 			return XmlCustomFormatter.FromByteArrayHex (value);
163 		}
164 
FromChar(char value)165 		protected static string FromChar (char value)
166 		{
167 			return XmlCustomFormatter.FromChar (value);
168 		}
169 
FromDate(DateTime value)170 		protected static string FromDate (DateTime value)
171 		{
172 			return XmlCustomFormatter.FromDate (value);
173 		}
174 
FromDateTime(DateTime value)175 		protected static string FromDateTime (DateTime value)
176 		{
177 			return XmlCustomFormatter.FromDateTime (value);
178 		}
179 
FromEnum(long value, string[] values, long[] ids)180 		protected static string FromEnum (long value, string[] values, long[] ids)
181 		{
182 			return XmlCustomFormatter.FromEnum (value, values, ids);
183 		}
184 
FromTime(DateTime value)185 		protected static string FromTime (DateTime value)
186 		{
187 			return XmlCustomFormatter.FromTime (value);
188 		}
189 
FromXmlName(string name)190 		protected static string FromXmlName (string name)
191 		{
192 			return XmlCustomFormatter.FromXmlName (name);
193 		}
194 
FromXmlNCName(string ncName)195 		protected static string FromXmlNCName (string ncName)
196 		{
197 			return XmlCustomFormatter.FromXmlNCName (ncName);
198 		}
199 
FromXmlNmToken(string nmToken)200 		protected static string FromXmlNmToken (string nmToken)
201 		{
202 			return XmlCustomFormatter.FromXmlNmToken (nmToken);
203 		}
204 
FromXmlNmTokens(string nmTokens)205 		protected static string FromXmlNmTokens (string nmTokens)
206 		{
207 			return XmlCustomFormatter.FromXmlNmTokens (nmTokens);
208 		}
209 
FromXmlQualifiedName(XmlQualifiedName xmlQualifiedName)210 		protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName)
211 		{
212 			if (xmlQualifiedName == null || xmlQualifiedName == XmlQualifiedName.Empty)
213 				return null;
214 
215 			return GetQualifiedName (xmlQualifiedName.Name, xmlQualifiedName.Namespace);
216 		}
217 
GetId(object o, bool addToReferencesList)218 		private string GetId (object o, bool addToReferencesList)
219 		{
220 			if (idGenerator == null) idGenerator = new ObjectIDGenerator ();
221 
222 			bool firstTime;
223 			long lid = idGenerator.GetId (o, out firstTime);
224 			return String.Format (CultureInfo.InvariantCulture, "id{0}", lid);
225 		}
226 
227 
AlreadyQueued(object ob)228 		bool AlreadyQueued (object ob)
229 		{
230 			if (idGenerator == null) return false;
231 
232 			bool firstTime;
233 			idGenerator.HasId (ob, out firstTime);
234 			return !firstTime;
235 		}
236 
GetNamespacePrefix(string ns)237 		private string GetNamespacePrefix (string ns)
238 		{
239 			string prefix = Writer.LookupPrefix (ns);
240 			if (prefix == null)
241 			{
242 				prefix = String.Format (CultureInfo.InvariantCulture, "q{0}", ++qnameCount);
243 				WriteAttribute ("xmlns", prefix, null, ns);
244 			}
245 			return prefix;
246 		}
247 
GetQualifiedName(string name, string ns)248 		private string GetQualifiedName (string name, string ns)
249 		{
250 			if (ns == String.Empty) return name;
251 
252 			string prefix = GetNamespacePrefix (ns);
253 			if (prefix == String.Empty)
254 				return name;
255 			else
256 				return String.Format ("{0}:{1}", prefix, name);
257 		}
258 
InitCallbacks()259 		protected abstract void InitCallbacks ();
260 
TopLevelElement()261 		protected void TopLevelElement ()
262 		{
263 			topLevelElement = true;
264 		}
265 
WriteAttribute(string localName, byte[] value)266 		protected void WriteAttribute (string localName, byte[] value)
267 		{
268 			WriteAttribute (localName, String.Empty, value);
269 		}
270 
WriteAttribute(string localName, string value)271 		protected void WriteAttribute (string localName, string value)
272 		{
273 			WriteAttribute (String.Empty, localName, String.Empty, value);
274 		}
275 
WriteAttribute(string localName, string ns, byte[] value)276 		protected void WriteAttribute (string localName, string ns, byte[] value)
277 		{
278 			if (value == null)
279 				return;
280 
281 			Writer.WriteStartAttribute (localName, ns);
282 			WriteValue (value);
283 			Writer.WriteEndAttribute ();
284 		}
285 
WriteAttribute(string localName, string ns, string value)286 		protected void WriteAttribute (string localName, string ns, string value)
287 		{
288 			WriteAttribute (null, localName, ns, value);
289 		}
290 
WriteAttribute(string prefix, string localName, string ns, string value)291 		protected void WriteAttribute (string prefix, string localName, string ns, string value)
292 		{
293 			if (value == null)
294 				return;
295 
296 			Writer.WriteAttributeString (prefix, localName, ns, value);
297 		}
298 
WriteXmlNode(XmlNode node)299 		void WriteXmlNode (XmlNode node)
300 		{
301 			if (node is XmlDocument)
302 				node = ((XmlDocument) node).DocumentElement;
303 
304 			node.WriteTo (Writer);
305 		}
306 
WriteElementEncoded(XmlNode node, string name, string ns, bool isNullable, bool any)307 		protected void WriteElementEncoded (XmlNode node, string name, string ns, bool isNullable, bool any)
308 		{
309 			if (name != string.Empty)
310 			{
311 				if (node == null)
312 				{
313 					if (isNullable)
314 						WriteNullTagEncoded (name, ns);
315 				}
316 				else if (any)
317 					WriteXmlNode (node);
318 				else
319 				{
320 					Writer.WriteStartElement (name, ns);
321 					WriteXmlNode (node);
322 					Writer.WriteEndElement ();
323 				}
324 			}
325 			else
326 				WriteXmlNode(node);
327 		}
328 
WriteElementLiteral(XmlNode node, string name, string ns, bool isNullable, bool any)329 		protected void WriteElementLiteral (XmlNode node, string name, string ns, bool isNullable, bool any)
330 		{
331 			if (name != string.Empty)
332 			{
333 				if (node == null)
334 				{
335 					if (isNullable)
336 						WriteNullTagLiteral (name, ns);
337 				}
338 				else if (any)
339 					WriteXmlNode (node);
340 				else
341 				{
342 					Writer.WriteStartElement (name, ns);
343 					WriteXmlNode (node);
344 					Writer.WriteEndElement ();
345 				}
346 			}
347 			else
348 				WriteXmlNode (node);
349 		}
350 
WriteElementQualifiedName(string localName, XmlQualifiedName value)351 		protected void WriteElementQualifiedName (string localName, XmlQualifiedName value)
352 		{
353 			WriteElementQualifiedName (localName, String.Empty, value, null);
354 		}
355 
WriteElementQualifiedName(string localName, string ns, XmlQualifiedName value)356 		protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value)
357 		{
358 			WriteElementQualifiedName (localName, ns, value, null);
359 		}
360 
WriteElementQualifiedName(string localName, XmlQualifiedName value, XmlQualifiedName xsiType)361 		protected void WriteElementQualifiedName (string localName, XmlQualifiedName value, XmlQualifiedName xsiType)
362 		{
363 			WriteElementQualifiedName (localName, String.Empty, value, xsiType);
364 		}
365 
WriteElementQualifiedName(string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)366 		protected void WriteElementQualifiedName (string localName, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
367 		{
368 			localName = XmlCustomFormatter.FromXmlNCName (localName);
369 			WriteStartElement (localName, ns);
370 			if (xsiType != null) WriteXsiType (xsiType.Name, xsiType.Namespace);
371 			Writer.WriteString (FromXmlQualifiedName (value));
372 			WriteEndElement ();
373 		}
374 
WriteElementString(string localName, string value)375 		protected void WriteElementString (string localName, string value)
376 		{
377 			WriteElementString (localName, String.Empty, value, null);
378 		}
379 
WriteElementString(string localName, string ns, string value)380 		protected void WriteElementString (string localName, string ns, string value)
381 		{
382 			WriteElementString (localName, ns, value, null);
383 		}
384 
WriteElementString(string localName, string value, XmlQualifiedName xsiType)385 		protected void WriteElementString (string localName, string value, XmlQualifiedName xsiType)
386 		{
387 			WriteElementString (localName, String.Empty, value, xsiType);
388 		}
389 
WriteElementString(string localName, string ns, string value, XmlQualifiedName xsiType)390 		protected void WriteElementString (string localName, string ns, string value, XmlQualifiedName xsiType)
391 		{
392 			if (value == null) return;
393 
394 			if (xsiType != null) {
395 				localName = XmlCustomFormatter.FromXmlNCName (localName);
396 				WriteStartElement (localName, ns);
397 				WriteXsiType (xsiType.Name, xsiType.Namespace);
398 				Writer.WriteString (value);
399 				WriteEndElement ();
400 			}
401 			else
402 				Writer.WriteElementString (localName, ns, value);
403 		}
404 
WriteElementStringRaw(string localName, byte[] value)405 		protected void WriteElementStringRaw (string localName, byte[] value)
406 		{
407 			WriteElementStringRaw (localName, String.Empty, value, null);
408 		}
409 
WriteElementStringRaw(string localName, string value)410 		protected void WriteElementStringRaw (string localName, string value)
411 		{
412 			WriteElementStringRaw (localName, String.Empty, value, null);
413 		}
414 
WriteElementStringRaw(string localName, byte[] value, XmlQualifiedName xsiType)415 		protected void WriteElementStringRaw (string localName, byte[] value, XmlQualifiedName xsiType)
416 		{
417 			WriteElementStringRaw (localName, String.Empty, value, xsiType);
418 		}
419 
WriteElementStringRaw(string localName, string ns, byte[] value)420 		protected void WriteElementStringRaw (string localName, string ns, byte[] value)
421 		{
422 			WriteElementStringRaw (localName, ns, value, null);
423 		}
424 
WriteElementStringRaw(string localName, string ns, string value)425 		protected void WriteElementStringRaw (string localName, string ns, string value)
426 		{
427 			WriteElementStringRaw (localName, ns, value, null);
428 		}
429 
WriteElementStringRaw(string localName, string value, XmlQualifiedName xsiType)430 		protected void WriteElementStringRaw (string localName, string value, XmlQualifiedName xsiType)
431 		{
432 			WriteElementStringRaw (localName, String.Empty, value, null);
433 		}
434 
WriteElementStringRaw(string localName, string ns, byte[] value, XmlQualifiedName xsiType)435 		protected void WriteElementStringRaw (string localName, string ns, byte[] value, XmlQualifiedName xsiType)
436 		{
437 			if (value == null)
438 				return;
439 
440 			WriteStartElement (localName, ns);
441 
442 			if (xsiType != null)
443 				WriteXsiType (xsiType.Name, xsiType.Namespace);
444 
445 			if (value.Length > 0)
446 				Writer.WriteBase64(value,0,value.Length);
447 			WriteEndElement ();
448 		}
449 
WriteElementStringRaw(string localName, string ns, string value, XmlQualifiedName xsiType)450 		protected void WriteElementStringRaw (string localName, string ns, string value, XmlQualifiedName xsiType)
451 		{
452 			localName = XmlCustomFormatter.FromXmlNCName (localName);
453 			WriteStartElement (localName, ns);
454 
455 			if (xsiType != null)
456 				WriteXsiType (xsiType.Name, xsiType.Namespace);
457 
458 			Writer.WriteRaw (value);
459 			WriteEndElement ();
460 		}
461 
WriteEmptyTag(string name)462 		protected void WriteEmptyTag (string name)
463 		{
464 			WriteEmptyTag (name, String.Empty);
465 		}
466 
WriteEmptyTag(string name, string ns)467 		protected void WriteEmptyTag (string name, string ns)
468 		{
469 			name = XmlCustomFormatter.FromXmlName (name);
470 			WriteStartElement (name, ns);
471 			WriteEndElement ();
472 		}
473 
WriteEndElement()474 		protected void WriteEndElement ()
475 		{
476 			WriteEndElement (null);
477 		}
478 
WriteEndElement(object o)479 		protected void WriteEndElement (object o)
480 		{
481 			if (o != null)
482 				serializedObjects.Remove (o);
483 
484 			Writer.WriteEndElement ();
485 		}
486 
WriteId(object o)487 		protected void WriteId (object o)
488 		{
489 			WriteAttribute ("id", GetId (o, true));
490 		}
491 
WriteNamespaceDeclarations(XmlSerializerNamespaces xmlns)492 		protected void WriteNamespaceDeclarations (XmlSerializerNamespaces xmlns)
493 		{
494 			if (xmlns == null)
495 				return;
496 			foreach (DictionaryEntry qn in xmlns.Namespaces) {
497 				if ((string) qn.Value != String.Empty && Writer.LookupPrefix ((string) qn.Value) != (string) qn.Key)
498 					WriteAttribute ("xmlns", (string) qn.Key, xmlNamespace, (string) qn.Value);
499 			}
500 		}
501 
WriteNullableQualifiedNameEncoded(string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)502 		protected void WriteNullableQualifiedNameEncoded (string name, string ns, XmlQualifiedName value, XmlQualifiedName xsiType)
503 		{
504 			if (value != null)
505 				WriteElementQualifiedName (name, ns, value, xsiType);
506 			else
507 				WriteNullTagEncoded (name, ns);
508 		}
509 
WriteNullableQualifiedNameLiteral(string name, string ns, XmlQualifiedName value)510 		protected void WriteNullableQualifiedNameLiteral (string name, string ns, XmlQualifiedName value)
511 		{
512 			if (value != null)
513 				WriteElementQualifiedName (name, ns, value);
514 			else
515 				WriteNullTagLiteral (name, ns);
516 		}
517 
WriteNullableStringEncoded(string name, string ns, string value, XmlQualifiedName xsiType)518 		protected void WriteNullableStringEncoded (string name, string ns, string value, XmlQualifiedName xsiType)
519 		{
520 			if (value != null)
521 				WriteElementString (name, ns, value, xsiType);
522 			else
523 				WriteNullTagEncoded (name, ns);
524 		}
525 
WriteNullableStringEncodedRaw(string name, string ns, byte[] value, XmlQualifiedName xsiType)526 		protected void WriteNullableStringEncodedRaw (string name, string ns, byte[] value, XmlQualifiedName xsiType)
527 		{
528 			if (value == null)
529 				WriteNullTagEncoded (name, ns);
530 			else
531 				WriteElementStringRaw (name, ns, value, xsiType);
532 		}
533 
WriteNullableStringEncodedRaw(string name, string ns, string value, XmlQualifiedName xsiType)534 		protected void WriteNullableStringEncodedRaw (string name, string ns, string value, XmlQualifiedName xsiType)
535 		{
536 			if (value == null)
537 				WriteNullTagEncoded (name, ns);
538 			else
539 				WriteElementStringRaw (name, ns, value, xsiType);
540 		}
541 
WriteNullableStringLiteral(string name, string ns, string value)542 		protected void WriteNullableStringLiteral (string name, string ns, string value)
543 		{
544 			if (value != null)
545 				WriteElementString (name, ns, value, null);
546 			else
547 				WriteNullTagLiteral (name, ns);
548 		}
549 
WriteNullableStringLiteralRaw(string name, string ns, byte[] value)550 		protected void WriteNullableStringLiteralRaw (string name, string ns, byte[] value)
551 		{
552 			if (value == null)
553 				WriteNullTagLiteral (name, ns);
554 			else
555 				WriteElementStringRaw (name, ns, value);
556 		}
557 
WriteNullableStringLiteralRaw(string name, string ns, string value)558 		protected void WriteNullableStringLiteralRaw (string name, string ns, string value)
559 		{
560 			if (value == null)
561 				WriteNullTagLiteral (name, ns);
562 			else
563 				WriteElementStringRaw (name, ns, value);
564 		}
565 
WriteNullTagEncoded(string name)566 		protected void WriteNullTagEncoded (string name)
567 		{
568 			WriteNullTagEncoded (name, String.Empty);
569 		}
570 
WriteNullTagEncoded(string name, string ns)571 		protected void WriteNullTagEncoded (string name, string ns)
572 		{
573 			Writer.WriteStartElement (name, ns);
574 			Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
575 			Writer.WriteEndElement ();
576 		}
577 
WriteNullTagLiteral(string name)578 		protected void WriteNullTagLiteral (string name)
579 		{
580 			WriteNullTagLiteral (name, String.Empty);
581 		}
582 
WriteNullTagLiteral(string name, string ns)583 		protected void WriteNullTagLiteral (string name, string ns)
584 		{
585 			WriteStartElement (name, ns);
586 			Writer.WriteAttributeString ("nil", XmlSchema.InstanceNamespace, "true");
587 			WriteEndElement ();
588 		}
589 
WritePotentiallyReferencingElement(string n, string ns, object o)590 		protected void WritePotentiallyReferencingElement (string n, string ns, object o)
591 		{
592 			WritePotentiallyReferencingElement (n, ns, o, null, false, false);
593 		}
594 
WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType)595 		protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType)
596 		{
597 			WritePotentiallyReferencingElement (n, ns, o, ambientType, false, false);
598 		}
599 
WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType, bool suppressReference)600 		protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference)
601 		{
602 			WritePotentiallyReferencingElement (n, ns, o, ambientType, suppressReference, false);
603 		}
604 
WritePotentiallyReferencingElement(string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable)605 		protected void WritePotentiallyReferencingElement (string n, string ns, object o, Type ambientType, bool suppressReference, bool isNullable)
606 		{
607 			if (o == null)
608 			{
609 				if (isNullable) WriteNullTagEncoded (n, ns);
610 				return;
611 			}
612 
613 			var t = o.GetType ();
614 
615 			WriteStartElement (n, ns, true);
616 
617 			CheckReferenceQueue ();
618 
619 			if (callbacks != null && callbacks.ContainsKey (o.GetType ()))
620 			{
621 				WriteCallbackInfo info = (WriteCallbackInfo) callbacks[t];
622 				if (t.IsEnum) {
623 					info.Callback (o);
624 				}
625 				else if (suppressReference) {
626 					Writer.WriteAttributeString ("id", GetId (o, false));
627 					if (ambientType != t) WriteXsiType(info.TypeName, info.TypeNs);
628 					info.Callback (o);
629 				}
630 				else {
631 					if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
632 					Writer.WriteAttributeString ("href", "#" + GetId (o, true));
633 				}
634 			}
635 			else
636 			{
637 				// Must be a primitive type or array of primitives
638 				TypeData td = TypeTranslator.GetTypeData (t, null, true);
639 				if (td.SchemaType == SchemaTypes.Primitive) {
640 					if (t != ambientType)
641 						WriteXsiType (td.XmlType, XmlSchema.Namespace);
642 					Writer.WriteString (XmlCustomFormatter.ToXmlString (td, o));
643 				} else if (IsPrimitiveArray (td)) {
644 					if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
645 					Writer.WriteAttributeString ("href", "#" + GetId (o, true));
646 				} else {
647 					throw new InvalidOperationException ("Invalid type: " + t.FullName);
648 				}
649 			}
650 
651 			WriteEndElement ();
652 		}
653 
WriteReferencedElements()654 		protected void WriteReferencedElements ()
655 		{
656 			if (referencedElements == null) return;
657 			if (callbacks == null) return;
658 
659 			while (referencedElements.Count > 0)
660 			{
661 				object o = referencedElements.Dequeue ();
662 				TypeData td = TypeTranslator.GetTypeData (o.GetType ());
663 				WriteCallbackInfo info = (WriteCallbackInfo) callbacks[o.GetType()];
664 
665 				if (info != null) {
666 					WriteStartElement (info.TypeName, info.TypeNs, true);
667 					Writer.WriteAttributeString ("id", GetId (o, false));
668 
669 					if (td.SchemaType != SchemaTypes.Array)	// Array use its own "arrayType" attribute
670 						WriteXsiType(info.TypeName, info.TypeNs);
671 
672 					info.Callback (o);
673 					WriteEndElement ();
674 				} else if (IsPrimitiveArray (td)) {
675 					WriteArray (o, td);
676 				}
677 			}
678 		}
679 
IsPrimitiveArray(TypeData td)680 		bool IsPrimitiveArray (TypeData td)
681 		{
682 			if (td.SchemaType == SchemaTypes.Array) {
683 				if (td.ListItemTypeData.SchemaType == SchemaTypes.Primitive || td.ListItemType == typeof(object))
684 					return true;
685 				return IsPrimitiveArray (td.ListItemTypeData);
686 			} else
687 				return false;
688 		}
689 
WriteArray(object o, TypeData td)690 		void WriteArray (object o, TypeData td)
691 		{
692 			TypeData itemTypeData = td;
693 			string xmlType;
694 			int nDims = -1;
695 
696 			do {
697 				itemTypeData = itemTypeData.ListItemTypeData;
698 				xmlType = itemTypeData.XmlType;
699 				nDims++;
700 			}
701 			while (itemTypeData.SchemaType == SchemaTypes.Array );
702 
703 			while (nDims-- > 0)
704 				xmlType += "[]";
705 
706 			WriteStartElement("Array", XmlSerializer.EncodingNamespace, true);
707 			Writer.WriteAttributeString("id", GetId(o, false));
708 			if (td.SchemaType == SchemaTypes.Array) {
709 				Array a = (Array)o;
710 				int len = a.Length;
711 				Writer.WriteAttributeString("arrayType", XmlSerializer.EncodingNamespace, GetQualifiedName(xmlType, XmlSchema.Namespace) + "[" + len.ToString() + "]");
712 				for (int i = 0; i < len; i++) {
713 					WritePotentiallyReferencingElement("Item", "", a.GetValue(i), td.ListItemType, false, true);
714 				}
715 			}
716 			WriteEndElement();
717 		}
718 
719 
WriteReferencingElement(string n, string ns, object o)720 		protected void WriteReferencingElement (string n, string ns, object o)
721 		{
722 			WriteReferencingElement (n, ns, o, false);
723 		}
724 
WriteReferencingElement(string n, string ns, object o, bool isNullable)725 		protected void WriteReferencingElement (string n, string ns, object o, bool isNullable)
726 		{
727 			if (o == null)
728 			{
729 				if (isNullable) WriteNullTagEncoded (n, ns);
730 				return;
731 			}
732 			else
733 			{
734 				CheckReferenceQueue ();
735 				if (!AlreadyQueued (o)) referencedElements.Enqueue (o);
736 
737 				Writer.WriteStartElement (n, ns);
738 				Writer.WriteAttributeString ("href", "#" + GetId (o, true));
739 				Writer.WriteEndElement ();
740 			}
741 		}
742 
CheckReferenceQueue()743 		void CheckReferenceQueue ()
744 		{
745 			if (referencedElements == null)
746 			{
747 				referencedElements = new Queue ();
748 				InitCallbacks ();
749 			}
750 		}
751 
752 		[MonoTODO]
WriteRpcResult(string name, string ns)753 		protected void WriteRpcResult (string name, string ns)
754 		{
755 			throw new NotImplementedException ();
756 		}
757 
WriteSerializable(IXmlSerializable serializable, string name, string ns, bool isNullable)758 		protected void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable)
759 		{
760 			WriteSerializable (serializable, name, ns, isNullable, true);
761 		}
762 
763 		protected
WriteSerializable(IXmlSerializable serializable, string name, string ns, bool isNullable, bool wrapped)764 		void WriteSerializable (IXmlSerializable serializable, string name, string ns, bool isNullable, bool wrapped)
765 		{
766 			if (serializable == null)
767 			{
768 				if (isNullable && wrapped) WriteNullTagLiteral (name, ns);
769 				return;
770 			}
771 			else
772 			{
773 				if (wrapped)
774 					Writer.WriteStartElement (name, ns);
775 				serializable.WriteXml (Writer);
776 				if (wrapped)
777 					Writer.WriteEndElement ();
778 			}
779 		}
780 
WriteStartDocument()781 		protected void WriteStartDocument ()
782 		{
783 			if (Writer.WriteState == WriteState.Start)
784 				Writer.WriteStartDocument ();
785 		}
786 
WriteStartElement(string name)787 		protected void WriteStartElement (string name)
788 		{
789 			WriteStartElement (name, String.Empty, null, false);
790 		}
791 
WriteStartElement(string name, string ns)792 		protected void WriteStartElement (string name, string ns)
793 		{
794 			WriteStartElement (name, ns, null, false);
795 		}
796 
WriteStartElement(string name, string ns, bool writePrefixed)797 		protected void WriteStartElement (string name, string ns, bool writePrefixed)
798 		{
799 			WriteStartElement (name, ns, null, writePrefixed);
800 		}
801 
WriteStartElement(string name, string ns, object o)802 		protected void WriteStartElement (string name, string ns, object o)
803 		{
804 			WriteStartElement (name, ns, o, false);
805 		}
806 
WriteStartElement(string name, string ns, object o, bool writePrefixed)807 		protected void WriteStartElement (string name, string ns, object o, bool writePrefixed)
808 		{
809 			WriteStartElement (name, ns, o, writePrefixed, namespaces);
810 		}
811 
WriteStartElement(string name, string ns, Object o, bool writePrefixed, XmlSerializerNamespaces xmlns)812 		protected void WriteStartElement (string name, string ns, Object o, bool writePrefixed, XmlSerializerNamespaces xmlns)
813 		{
814 			WriteStartElement (name, ns, o, writePrefixed, xmlns != null ? xmlns.ToArray () : null);
815 		}
816 
WriteStartElement(string name, string ns, object o, bool writePrefixed, ICollection namespaces)817 		void WriteStartElement (string name, string ns, object o, bool writePrefixed, ICollection namespaces)
818 		{
819 			if (o != null)
820 			{
821 				if (serializedObjects.Contains (o))
822 					throw new InvalidOperationException ("A circular reference was detected while serializing an object of type " + o.GetType().Name);
823 				else
824 					serializedObjects [o] = o;
825 			}
826 
827 			string prefix = null;
828 
829 			if (topLevelElement && ns != null && ns.Length != 0)
830 			{
831 				if (namespaces != null)
832 					foreach (XmlQualifiedName qn in namespaces)
833 						if (qn.Namespace == ns) {
834 							prefix = qn.Name;
835 							writePrefixed = true;
836 							break;
837 						}
838 			}
839 
840 			if (writePrefixed && ns != string.Empty)
841 			{
842 				name = XmlCustomFormatter.FromXmlName (name);
843 
844 				if (prefix == null)
845 					prefix = Writer.LookupPrefix (ns);
846 				if (prefix == null || prefix.Length == 0)
847 					prefix = "q" + (++qnameCount);
848 				Writer.WriteStartElement (prefix, name, ns);
849 			} else
850 				Writer.WriteStartElement (name, ns);
851 
852 			if (topLevelElement)
853 			{
854 				if (namespaces != null) {
855 					foreach (XmlQualifiedName qn in namespaces)
856 					{
857 						string currentPrefix = Writer.LookupPrefix (qn.Namespace);
858 						if (currentPrefix != null && currentPrefix.Length != 0) continue;
859 
860 						WriteAttribute ("xmlns",qn.Name,xmlNamespace,qn.Namespace);
861 					}
862 				}
863 				topLevelElement = false;
864 			}
865 		}
866 
WriteTypedPrimitive(string name, string ns, object o, bool xsiType)867 		protected void WriteTypedPrimitive (string name, string ns, object o, bool xsiType)
868 		{
869 			string value;
870 			TypeData td = TypeTranslator.GetTypeData (o.GetType (), null, true);
871 			if (td.SchemaType != SchemaTypes.Primitive)
872 				throw new InvalidOperationException (String.Format ("The type of the argument object '{0}' is not primitive.", td.FullTypeName));
873 
874 			if (name == null) {
875 				ns = td.IsXsdType ? XmlSchema.Namespace : XmlSerializer.WsdlTypesNamespace;
876 				name = td.XmlType;
877 			}
878 			else
879 				name = XmlCustomFormatter.FromXmlName (name);
880 			Writer.WriteStartElement (name, ns);
881 
882 			if (o is XmlQualifiedName)
883 				value = FromXmlQualifiedName ((XmlQualifiedName) o);
884 			else
885 				value = XmlCustomFormatter.ToXmlString (td, o);
886 
887 			if (xsiType)
888 			{
889 				if (td.SchemaType != SchemaTypes.Primitive)
890 					throw new InvalidOperationException (string.Format (unexpectedTypeError, o.GetType().FullName));
891 				WriteXsiType (td.XmlType, td.IsXsdType ? XmlSchema.Namespace : XmlSerializer.WsdlTypesNamespace);
892 			}
893 
894 			WriteValue (value);
895 
896 			Writer.WriteEndElement ();
897 		}
898 
WriteValue(byte[] value)899 		protected void WriteValue (byte[] value)
900 		{
901 			Writer.WriteBase64 (value, 0, value.Length);
902 		}
903 
WriteValue(string value)904 		protected void WriteValue (string value)
905 		{
906 			if (value != null)
907 				Writer.WriteString (value);
908 		}
909 
WriteXmlAttribute(XmlNode node)910 		protected void WriteXmlAttribute (XmlNode node)
911 		{
912 			WriteXmlAttribute (node, null);
913 		}
914 
WriteXmlAttribute(XmlNode node, object container)915 		protected void WriteXmlAttribute (XmlNode node, object container)
916 		{
917 			XmlAttribute attr = node as XmlAttribute;
918 			if (attr == null)
919 				throw new InvalidOperationException ("The node must be either type XmlAttribute or a derived type.");
920 
921 			if (attr.NamespaceURI == XmlSerializer.WsdlNamespace)
922 			{
923 				// The wsdl arrayType attribute needs special handling
924 				if (attr.LocalName == "arrayType") {
925 					string ns, type, dimensions;
926 					TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
927 					string value = GetQualifiedName (type + dimensions, ns);
928 					WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, value);
929 					return;
930 				}
931 			}
932 
933 			WriteAttribute (attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value);
934 		}
935 
WriteXsiType(string name, string ns)936 		protected void WriteXsiType (string name, string ns)
937 		{
938 			if (ns != null && ns != string.Empty)
939 				WriteAttribute ("type", XmlSchema.InstanceNamespace, GetQualifiedName (name, ns));
940 			else
941 				WriteAttribute ("type", XmlSchema.InstanceNamespace, name);
942 		}
943 
944 
CreateInvalidAnyTypeException(object o)945 		protected Exception CreateInvalidAnyTypeException (object o)
946 		{
947 			if (o == null)
948 				return new InvalidOperationException ("null is invalid as anyType in XmlSerializer");
949 			else
950 				return CreateInvalidAnyTypeException (o.GetType ());
951 		}
952 
CreateInvalidAnyTypeException(Type type)953 		protected Exception CreateInvalidAnyTypeException (Type type)
954 		{
955 			return new InvalidOperationException (String.Format ("An object of type '{0}' is invalid as anyType in XmlSerializer", type));
956 		}
957 
CreateInvalidEnumValueException(object value, string typeName)958 		protected Exception CreateInvalidEnumValueException (object value, string typeName)
959 		{
960 			return new InvalidOperationException (string.Format(CultureInfo.CurrentCulture,
961 				"'{0}' is not a valid value for {1}.", value, typeName));
962 		}
963 
FromEnum(long value, string[] values, long[] ids, string typeName)964 		protected static string FromEnum (long value, string[] values, long[] ids, string typeName)
965 		{
966 			return XmlCustomFormatter.FromEnum (value, values, ids, typeName);
967 		}
968 
969 		[MonoTODO]
FromXmlQualifiedName(XmlQualifiedName xmlQualifiedName, bool ignoreEmpty)970 		protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName, bool ignoreEmpty)
971 		{
972 			throw new NotImplementedException ();
973 		}
974 
975 		[MonoTODO]
ResolveDynamicAssembly(string assemblyFullName)976 		protected static Assembly ResolveDynamicAssembly (string assemblyFullName)
977 		{
978 			throw new NotImplementedException ();
979 		}
980 
981 		[MonoTODO]
982 		protected bool EscapeName
983 		{
984 			get { throw new NotImplementedException(); }
985 			set { throw new NotImplementedException(); }
986 		}
987 
988 		#endregion
989 
990 		class WriteCallbackInfo
991 		{
992 			public Type Type;
993 			public string TypeName;
994 			public string TypeNs;
995 			public XmlSerializationWriteCallback Callback;
996 		}
997 	}
998 }
999