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