context: //
  // SyndicationElementExtension.cs
  //
  // Author:
  //	Atsushi Enomoto  <atsushi@ximian.com>
  //
  // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
  //
  // 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.Generic;
  using System.Collections.ObjectModel;
  using System.IO;
  using System.Runtime.Serialization;
  using System.Text;
  using System.Xml;
  using System.Xml.Serialization;
  
  namespace System.ServiceModel.Syndication
  {
  	public class SyndicationElementExtension
  	{
  		ReadWriteHandler handler;
  
  		public SyndicationElementExtension (object dataContractExtension)
  			: this (dataContractExtension, (XmlObjectSerializer) null)
  		{
  		}
  
  		public SyndicationElementExtension (object dataContractExtension, XmlObjectSerializer dataContractSerializer)
  			: this (null, null, dataContractExtension, dataContractSerializer)
  		{
  		}
  
  		public SyndicationElementExtension (string outerName, string outerNamespace, object dataContractExtension)
  			: this (outerName, outerNamespace, dataContractExtension, null)
  		{
  		}
  
  		public SyndicationElementExtension (string outerName, string outerNamespace, object dataContractExtension, XmlObjectSerializer dataContractSerializer)
  		{
  			if (dataContractExtension == null)
  				throw new ArgumentNullException ("dataContractExtension");
  			handler = new DataContractReadWriteHandler (outerName, outerNamespace, dataContractExtension, dataContractSerializer);
  		}
  
  		public SyndicationElementExtension (object xmlSerializerExtension, XmlSerializer serializer)
  		{
  			if (xmlSerializerExtension == null)
  				throw new ArgumentNullException ("xmlSerializerExtension");
  			handler = new XmlSerializationReadWriteHandler (xmlSerializerExtension, serializer);
  		}
  
  		public SyndicationElementExtension (XmlReader xmlReader)
  		{
  			if (xmlReader == null)
  				throw new ArgumentNullException ("xmlReader");
  			xmlReader.MoveToContent ();
  			if (xmlReader.NodeType != XmlNodeType.Element)
  				throw new XmlException ("Element node is expected on the argument xmlReader");
  
  			handler = new XmlReaderReadWriteHandler (xmlReader);
  		}
  
  		public string OuterName {
  			get { return handler != null ? handler.Name : null; }
  		}
  
  		public string OuterNamespace {
  			get { return handler != null ? handler.Namespace : null; }
  		}
  
  		public TExtension GetObject<TExtension> ()
  		{
  			return GetObject<TExtension> (new DataContractSerializer (typeof (TExtension)));
  		}
  
  		public TExtension GetObject<TExtension> (XmlObjectSerializer serializer)
  		{
  			if (serializer == null)
  				throw new ArgumentNullException ("serializer");
  			return (TExtension) serializer.ReadObject (GetReader (), false);
  		}
  
  		public TExtension GetObject<TExtension> (XmlSerializer serializer)
  		{
  			if (serializer == null)
  				throw new ArgumentNullException ("serializer");
  			return (TExtension) serializer.Deserialize (GetReader ());
  		}
  
  		public XmlReader GetReader ()
  		{
  			var r = handler.GetReader ();
  			r.MoveToContent ();
  			return r;
  		}
  
  		public void WriteTo (XmlWriter writer)
  		{
  			if (writer == null)
  				throw new ArgumentNullException ("writer");
  
  			handler.WriteTo (writer);
  		}
  
  		abstract class ReadWriteHandler
  		{
  			public string Name { get; protected set; }
  
  			public string Namespace { get; protected set; }
  
  			public virtual XmlReader GetReader ()
  			{
  				StringWriter sw = new StringWriter ();
  				using (XmlWriter w = XmlWriter.Create (sw))
  					WriteTo (w);
  				return XmlReader.Create (new StringReader (sw.ToString ()));
  			}
  
  			public abstract void WriteTo (XmlWriter writer);
  		}
  
  		class DataContractReadWriteHandler : ReadWriteHandler
  		{
  			object extension;
  			XmlObjectSerializer serializer;
  			
  			public DataContractReadWriteHandler (string name, string ns, object extension, XmlObjectSerializer serializer)
  			{
  				if (serializer == null)
  					serializer = new DataContractSerializer (extension.GetType ());
  #if NET_4_0
  				// FIXME: this is a nasty workaround that mcs somehow fails to resolve InternalVisibleTo(System.ServiceModel) in System.Runtime.Serialization.dll and thus rejects the use of KnownTypeCollection unlike NET_2_0 case (where System.ServiceModel.Web.dll is referenced).
  				XmlQualifiedName qname = null;
  				if (name == null || ns == null) {
  					var ms = new MemoryStream ();
  					using (var xw = XmlWriter.Create (ms))
  						serializer.WriteStartObject (xw, extension);
  					ms.Position = 0;
  					var xr = XmlReader.Create (ms);
  					xr.MoveToContent ();
  					qname = new XmlQualifiedName (xr.LocalName, xr.NamespaceURI);
  				}
  #else
  				var qname = name == null || ns == null ? KnownTypeCollection.GetStaticQName (extension.GetType ()) : null;
  #endif
  				this.Name = name ?? qname.Name;
  				this.Namespace = ns ?? qname.Namespace;
  				this.extension = extension;
  				this.serializer = serializer;
  			}
  
  			public override void WriteTo (XmlWriter writer)
  			{
  				if (Name != null) {
  					writer.WriteStartElement (Name, Namespace);
  					serializer.WriteObjectContent (writer, extension);
  					writer.WriteFullEndElement ();
  				}
  				else
  					serializer.WriteObject (writer, extension);
  			}
  		}
  
  		class XmlSerializationReadWriteHandler : ReadWriteHandler
  		{
  			object extension;
  			XmlSerializer serializer;
  
  			public XmlSerializationReadWriteHandler (object extension, XmlSerializer serializer)
  			{
  				this.extension = extension;
  				this.serializer = serializer;
  
  				if (serializer == null)
  					serializer = new XmlSerializer (extension.GetType ());
  			}
  
  			public override void WriteTo (XmlWriter writer)
  			{
  				serializer.Serialize (writer, extension);
  			}
  		}
  
  		class XmlReaderReadWriteHandler : ReadWriteHandler
  		{
  			string xml;
  
  			public XmlReaderReadWriteHandler (XmlReader reader)
  			{
  				reader.MoveToContent ();
  				Name = reader.LocalName;
  				Namespace = reader.NamespaceURI;
  				xml = reader.ReadOuterXml ();
  			}
  
  			public override XmlReader GetReader ()
  			{
  				var r = XmlReader.Create (new StringReader (xml));
  				r.MoveToContent ();
  				return r;
  			}
  
  			public override void WriteTo (XmlWriter writer)
  			{
  
gt: 				writer.WriteNode (GetReader (), false);