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