context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.clearcanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project is free software: you can
  // redistribute it and/or modify it under the terms of the GNU General Public
  // License as published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project is distributed in the hope that it
  // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
  // Public License for more details.
  //
  // You should have received a copy of the GNU General Public License along with
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Globalization;
  using System.Linq;
  using System.Reflection;
  using System.Runtime.Serialization;
  using System.Xml;
  using System.IO;
  using ClearCanvas.Common.Utilities;
  
  
  namespace ClearCanvas.Common.Serialization
  {
  	#region Extensiblity
  
  	/// <summary>
  	/// Defines an interface to an object that hooks into the JSML serialization process
  	/// for the purpose of customizing it.
  	/// </summary>
  	public interface IJsmlSerializerHook
  	{
  		/// <summary>
  		/// Called to allow the hook to perform custom serialization.
  		/// </summary>
  		/// <param name="context"></param>
  		/// <returns>True if the hook has handled serialization, false otherwise.</returns>
  		bool Serialize(IJsmlSerializationContext context);
  
  		/// <summary>
  		/// Called to allow the hook to perform custom deserialization.
  		/// </summary>
  		/// <param name="context"></param>
  		/// <returns>True if the hook has handled deserialization, false otherwise.</returns>
  		bool Deserialize(IJsmlDeserializationContext context);
  	}
  
  	/// <summary>
  	/// Defines an interface to the serialization context information passed to <see cref="IJsmlSerializerHook.Serialize"/>.
  	/// </summary>
  	public interface IJsmlSerializationContext
  	{
  		/// <summary>
  		/// Gets the XML writer that is being used to perform serialization.
  		/// </summary>
  		/// <remarks>
  		/// The hook may use this object to write XML directly.
  		/// </remarks>
  		XmlWriter XmlWriter { get; }
  
  		/// <summary>
  		/// Gets or sets the object being serialized.
  		/// </summary>
  		/// <remarks>
  		/// The hook may set this to a different object.
  		/// </remarks>
  		object Data { get; set; }
  
  		/// <summary>
  		/// Gets or sets the name of the object for serialization.
  		/// </summary>
  		/// <remarks>
  		/// The hook may modify the name.
  		/// </remarks>
  		string Name { get; set; }
  
  		/// <summary>
  		/// Gets the collection of attributes that will be serialized along with this object.
  		/// </summary>
  		/// <remarks>
  		/// The hook may add to this collection.
  		/// </remarks>
  		IDictionary<string, string> Attributes { get; }
  
  		/// <summary>
  		/// Allows the hook to call back into the JSML serializer.
  		/// </summary>
  		/// <param name="obj"></param>
  		/// <param name="objectName"></param>
  		/// <param name="attributes"></param>
  		void Serialize(object obj, string objectName, IDictionary<string, string> attributes);
  	}
  
  	/// <summary>
  	/// Defines an interface to the serialization context information passed to <see cref="IJsmlSerializerHook.Deserialize"/>.
  	/// </summary>
  	public interface IJsmlDeserializationContext
  	{
  		/// <summary>
  		/// Gets the XML element containing the serialized representation.
  		/// </summary>
  		XmlElement XmlElement { get; }
  
  		/// <summary>
  		/// Gets or sets the data-type of the object being deserialized.
  		/// </summary>
  		/// <remarks>
  		/// The hook may set this property to modify the data type.
  		/// </remarks>
  		Type DataType { get; set; }
  
  		/// <summary>
  		/// Gets or sets the object that results from deserialization.
  		/// </summary>
  		/// <remarks>
  		/// If the hook handled the deserialization, it must set this property to reference the resulting object.
  		/// </remarks>
  		object Data { get; set; }
  
  		/// <summary>
  		/// Allows the hook to call back into the JSML deserializer.
  		/// </summary>
  		/// <param name="dataType"></param>
  		/// <param name="xmlElement"></param>
  		/// <returns>The object resulting from deserialization.</returns>
  		object Deserialize(Type dataType, XmlElement xmlElement);
  	}
  
  
  	/// <summary>
  	/// Defines an extension point for customizing the JSML serialization/deserialization services.
  	/// </summary>
  	[ExtensionPoint]
  	public class JsmlSerializerHookExtensionPoint : ExtensionPoint<IJsmlSerializerHook>
  	{
  	}
  
  	#endregion
  
  	/// <summary>
  	/// Provides JSML serialization and deserialization services.
  	/// </summary>
  	public class JsmlSerializer
  	{
  		#region Options classes
  
  		/// <summary>
  		/// Abstract base class for serialization options.
  		/// </summary>
  		public abstract class OptionsBase
  		{
  			private class NullHook : IJsmlSerializerHook
  			{
  				public bool Serialize(IJsmlSerializationContext context)
  				{
  					return false;
  				}
  
  				public bool Deserialize(IJsmlDeserializationContext context)
  				{
  					return false;
  				}
  			}
  
  			protected OptionsBase()
  			{
  				// default filters
  				this.DataContractTest = m => AttributeUtils.HasAttribute<DataContractAttribute>(m, false);
  				this.DataMemberTest = m => AttributeUtils.HasAttribute<DataMemberAttribute>(m, true);
  				this.Hook = new NullHook();
  			}
  
  			/// <summary>
  			/// Gets or sets a predicate that determines whether a member should participate in serialization.
  			/// </summary>
  			/// <remarks>
  			/// By default, a member is considered a data-member if it has the <see cref="DataMemberAttribute"/>,
  			/// but this behaviour can be changed by supplying a custom predicate here.
  			/// </remarks>
  			public Predicate<MemberInfo> DataMemberTest { get; set; }
  
  			/// <summary>
  			/// Gets or sets a value that indicates whether non-public members should be included.
  			/// </summary>
  			/// <remarks>
  			/// This value is set to true by default for backwards compatibility with the behaviour of
  			/// previous versions of <see cref="JsmlSerializer"/>.
  			/// </remarks>
  			public bool IncludeNonPublicMembers { get; set; }
  
  			/// <summary>
  			/// Gets or sets a predicate that determines whether a type is considered a data-contract.
  			/// </summary>
  			/// <remarks>
  			/// If a type is considered a data-contract, its data-members are serialized recursively.
  			/// By default, a type is considered a data-contract if it has the <see cref="DataContractAttribute"/>,
  			/// but this behaviour can be changed by supplying a custom predicate here.
  			/// </remarks>
  			public Predicate<Type> DataContractTest { get; set; }
  
  			/// <summary>
  			/// Gets or sets an instance of <see cref="IJsmlSerializerHook"/>.
  			/// </summary>
  			public IJsmlSerializerHook Hook { get; set; }
  		}
  
  		/// <summary>
  		/// Encapsulates options that customize the serialization process.
  		/// </summary>
  		public class SerializeOptions : OptionsBase
  		{
  			/// <summary>
  			/// Defines the default options.
  			/// </summary>
  			internal static readonly SerializeOptions Default = new SerializeOptions();
  		}
  
  		/// <summary>
  		/// Encapsulates options that customize the deserialization process.
  		/// </summary>
  		public class DeserializeOptions : OptionsBase
  		{
  			/// <summary>
  			/// Defines the default options.
  			/// </summary>
  			internal static readonly DeserializeOptions Default = new DeserializeOptions();
  		}
  
  		#endregion
  
  		#region Public API
  
  		/// <summary>
  		/// Serializes the specified object to JSML format, using the specified objectName as the outermost tag name.
  		/// </summary>
  		/// <param name="obj"></param>
  		/// <param name="objectName"></param>
  		/// <returns></returns>
  		public static string Serialize(object obj, string objectName)
  		{
  			return Serialize(obj, objectName, SerializeOptions.Default);
  		}
  
  		/// <summary>
  		/// Serializes the specified object to JSML format, using the specified objectName as the outermost tag name.
  		/// </summary>
  		public static string Serialize(object obj, string objectName, SerializeOptions options)
  		{
  			if (obj == null)
  				return null;
  
  			using (var sw = new StringWriter())
  			{
  				var writer = new XmlTextWriter(sw) { Formatting = Formatting.Indented };
  				Serialize(writer, obj, objectName, options);
  				writer.Close();
  				return sw.ToString();
  			}
  		}
  
  		/// <summary>
  		/// Serializes the specified object to JSML format, using the specified objectName as the outermost tag name.
  		/// </summary>
  		public static void Serialize(XmlWriter writer, object obj, string objectName)
  		{
  			Serialize(writer, obj, objectName, SerializeOptions.Default);
  		}
  
  		/// <summary>
  		/// Serializes the specified object to JSML format, using the specified objectName as the outermost tag name.
  		/// </summary>
  		public static void Serialize(XmlWriter writer, object obj, string objectName, SerializeOptions options)
  		{
  			if (obj == null)
  				return;
  
  			var serializer = new Serializer(writer, options);
  			serializer.Do(obj, objectName);
  		}
  
  
  		/// <summary>
  		/// Deserializes the specified JSML text into an object of the specified type.
  		/// </summary>
  		public static T Deserialize<T>(string jsml)
  		{
  			return (T)Deserialize(typeof(T), jsml);
  		}
  
  		/// <summary>
  		/// Deserializes the specified JSML text into an object of the specified type.
  		/// </summary>
  		public static T Deserialize<T>(string jsml, DeserializeOptions options)
  		{
  			return (T)Deserialize(typeof(T), jsml, options);
  		}
  
          /// <summary>
          /// Deserializes the specified XML Document into an object of the specified type.
          /// </summary>
          public static T Deserialize<T>(XmlDocument xmlDoc, DeserializeOptions options)
          {
              return (T)Deserialize(typeof(T), xmlDoc, options);
          }
  
  		/// <summary>
  		/// Deserializes the specified JSML text into an object of the specified type.
  		/// </summary>
  		public static object Deserialize(Type dataContract, string jsml)
  		{
  			return Deserialize(dataContract, jsml, DeserializeOptions.Default);
  		}
  
  		/// <summary>
  		/// Deserializes the specified JSML text into an object of the specified type.
  		/// </summary>
  		public static object Deserialize(Type dataContract, string jsml, DeserializeOptions options)
  		{
  			if (String.IsNullOrEmpty(jsml))
  				return null;
  
  			var xmlDoc = new XmlDocument { PreserveWhitespace = true };
  			xmlDoc.LoadXml(jsml);
  
  			var deserializer = new Deserializer(options);
  			return deserializer.Do(dataContract, xmlDoc.DocumentElement);
  		}
  
          /// <summary>
          /// Deserializes the specified XML document into an object of the specified type.
          /// </summary>
          /// 
          public static object Deserialize(Type dataContract, XmlDocument xmlDoc, DeserializeOptions options)
          {
  
gt:             if (xmlDoc == null)