context: //
  // System.Xml.Serialization.XmlSerializationWriter.cs
  //
  // Author:
  //   Tim Coleman (tim@timcoleman.com)
  //   Lluis Sanchez Gual (lluis@ximian.com)
  //
  // Copyright (C) Tim Coleman, 2002
  //
  
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Globalization;
  using System.Text;
  using System.Xml;
  using System.Xml.Schema;
  using System.Runtime.Serialization;
  using System.Reflection;
  
  namespace System.Xml.Serialization 
  {
  	public abstract class XmlSerializationWriter 
  		: XmlSerializationGeneratedCode
  	{
  
  		#region Fields
  
  		ObjectIDGenerator idGenerator;
  		int qnameCount;
  		bool topLevelElement = false;
  
  		ArrayList namespaces;
  		XmlWriter writer;
  		Queue referencedElements;
  		Hashtable callbacks;
  		Hashtable serializedObjects;
  		const string xmlNamespace = "http://www.w3.org/2000/xmlns/";
  		const string unexpectedTypeError = "The type {0} was not expected. Use the" +
  			" XmlInclude or SoapInclude attribute to specify types that are not known statically.";
  
  		#endregion // Fields
  
  		#region Constructors
  
  		protected XmlSerializationWriter ()
  		{
  			qnameCount = 0;
  			serializedObjects = new Hashtable ();
  		}
  		
  		internal void Initialize (XmlWriter writer, XmlSerializerNamespaces nss)
  		{
  			this.writer = writer;
  			if (nss != null)
  			{
  				namespaces = new ArrayList ();
  				foreach (XmlQualifiedName ns in nss.ToArray())
  					if (ns.Name != "" && ns.Namespace != "")
  						namespaces.Add (ns);
  			}	
  		}
  
  		#endregion // Constructors
  
  		#region Properties
  
  		protected ArrayList Namespaces {
  			get { return namespaces; }
  			set { namespaces = value; }
  		}
  
  		protected XmlWriter Writer {
  			get { return writer; }
  			set { writer = value; }
  		}
  
  		#endregion // Properties
  
  		#region Methods
  		
  		protected void AddWriteCallback (Type type, string typeName, string typeNs, XmlSerializationWriteCallback callback)
  		{
  			WriteCallbackInfo info = new WriteCallbackInfo ();
  			info.Type = type;
  			info.TypeName = typeName;
  			info.TypeNs = typeNs;
  			info.Callback = callback;
  
  			if (callbacks == null) callbacks = new Hashtable ();
  			callbacks.Add (type, info);
  		}
  		
  		protected Exception CreateChoiceIdentifierValueException (string value, string identifier, string name, string ns)
  		{
  			string message = string.Format ("Value '{0}' of the choice"
  				+ " identifier '{1}' does not match element '{2}'"
  				+ " from namespace '{3}'.", value, identifier,
  				name, ns);
  			return new InvalidOperationException (message);
  		}
  
  		protected Exception CreateInvalidChoiceIdentifierValueException (string type, string identifier)
  		{
  			string message = string.Format ("Invalid or missing choice"
  				+ " identifier '{0}' of type '{1}'.", identifier,
  				type);
  			return new InvalidOperationException (message);
  		}
  
  		protected Exception CreateMismatchChoiceException (string value, string elementName, string enumValue)
  		{
  			string message = String.Format ("Value of {0} mismatches the type of {1}, you need to set it to {2}.", elementName, value, enumValue);
  			return new InvalidOperationException (message);
  		}
  
  		protected Exception CreateUnknownAnyElementException (string name, string ns)
  		{
  			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);
  			return new InvalidOperationException (message);
  		}
  
  		protected Exception CreateUnknownTypeException (object o)
  		{
  			return CreateUnknownTypeException (o.GetType ());
  		}
  
  		protected Exception CreateUnknownTypeException (Type type)
  		{
  			string message = String.Format ("The type {0} may not be used in this context.", type);
  			return new InvalidOperationException (message);
  		}
  
  		protected static byte[] FromByteArrayBase64 (byte[] value)
  		{
  			return value;
  		}
  
  		protected static string FromByteArrayHex (byte[] value)
  		{
  			return XmlCustomFormatter.FromByteArrayHex (value);
  		}
  
  		protected static string FromChar (char value)
  		{
  			return XmlCustomFormatter.FromChar (value);
  		}
  
  		protected static string FromDate (DateTime value)
  		{
  			return XmlCustomFormatter.FromDate (value);
  		}
  
  		protected static string FromDateTime (DateTime value)
  		{
  			return XmlCustomFormatter.FromDateTime (value);
  		}
  
  		protected static string FromEnum (long value, string[] values, long[] ids)
  		{
  			return XmlCustomFormatter.FromEnum (value, values, ids);
  		}
  
  		protected static string FromTime (DateTime value)
  		{
  			return XmlCustomFormatter.FromTime (value);
  		}
  
  		protected static string FromXmlName (string name)
  		{
  			return XmlCustomFormatter.FromXmlName (name);
  		}
  
  		protected static string FromXmlNCName (string ncName)
  		{
  			return XmlCustomFormatter.FromXmlNCName (ncName);
  		}
  
  		protected static string FromXmlNmToken (string nmToken)
  		{
  			return XmlCustomFormatter.FromXmlNmToken (nmToken);
  		}
  
  		protected static string FromXmlNmTokens (string nmTokens)
  		{
  			return XmlCustomFormatter.FromXmlNmTokens (nmTokens);
  		}
  
  		protected string FromXmlQualifiedName (XmlQualifiedName xmlQualifiedName)
  		{
  			if (xmlQualifiedName == null || xmlQualifiedName == XmlQualifiedName.Empty)
  				return null;
  				
  			return GetQualifiedName (xmlQualifiedName.Name, xmlQualifiedName.Namespace);
  		}
  
  		private string GetId (object o, bool addToReferencesList)
  		{
  			if (idGenerator == null) idGenerator = new ObjectIDGenerator ();
  
  			bool firstTime;
  			long lid = idGenerator.GetId (o, out firstTime);
  			return String.Format (CultureInfo.InvariantCulture, "id{0}", lid);
  		}
  
  		
  		bool AlreadyQueued (object ob)
  		{
  			if (idGenerator == null) return false;
  
  			bool firstTime;
  			idGenerator.HasId (ob, out firstTime);
  			return !firstTime;
  		}
  
  		private string GetNamespacePrefix (string ns)
  		{
  			string prefix = Writer.LookupPrefix (ns);
  			if (prefix == null) 
  			{
  				prefix = String.Format (CultureInfo.InvariantCulture, "q{0}", ++qnameCount);
  				WriteAttribute ("xmlns", prefix, null, ns);
  			}
  			return prefix;
  		}
  
  		private string GetQualifiedName (string name, string ns)
  		{
  			if (ns == String.Empty) return name;
  			
  			string prefix = GetNamespacePrefix (ns);
  			if (prefix == String.Empty)
  				return name;
  			else
  				return String.Format ("{0}:{1}", prefix, name);
  		}
  
  		protected abstract void InitCallbacks ();
  
  		protected void TopLevelElement ()
  		{
  			topLevelElement = true;
  		}
  
  		protected void WriteAttribute (string localName, byte[] value)
  		{
  			WriteAttribute (localName, String.Empty, value);
  		}
  
  		protected void WriteAttribute (string localName, string value)
  		{
  			WriteAttribute (String.Empty, localName, String.Empty, value);
  		}
  
  		protected void WriteAttribute (string localName, string ns, byte[] value)
  		{
  			if (value == null)
  				return;
  
  			Writer.WriteStartAttribute (localName, ns);
  			WriteValue (value);
  			Writer.WriteEndAttribute ();
  		}
  
  		protected void WriteAttribute (string localName, string ns, string value)
  		{
  			WriteAttribute (null, localName, ns, value);
  		}
  
  		protected void WriteAttribute (string prefix, string localName, string ns, string value)
  		{
  			if (value == null)
  				return;
  
  			Writer.WriteAttributeString (prefix, localName, ns, value);
  		}
  
  		void WriteXmlNode (XmlNode node)
  		{
  			if (node is XmlDocument)
  
gt: 				node = ((XmlDocument) node).DocumentElement;