context: //
  // System.ComponentModel.Design.Serialization.CodeDomSerializerBase
  //
  // Authors:	 
  //	  Ivan N. Zlatev (contact i-nZ.net)
  //
  // (C) 2007 Ivan N. Zlatev
  
  //
  // 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.Reflection;
  using System.ComponentModel;
  using System.ComponentModel.Design;
  
  using System.CodeDom;
  
  namespace System.ComponentModel.Design.Serialization
  {
  	[EditorBrowsable (EditorBrowsableState.Never)]
  	public abstract class CodeDomSerializerBase
  	{
  
  		// MSDN says that if the deserialization fails a CodeExpression is returned
  		//
  		private sealed class DeserializationErrorMarker : CodeExpression
  		{
  			public override bool Equals (object o)
  			{
  				return false;
  			}
  
  			public override int GetHashCode ()
  			{
  				return base.GetHashCode ();
  			}
  		}
  
  		private static readonly DeserializationErrorMarker _errorMarker = new DeserializationErrorMarker ();
  
  		internal CodeDomSerializerBase ()
  		{
  		}
  
  		private class ExpressionTable : Hashtable // just so that we have a specific type to append to the context stack
  		{
  		}
  
  		protected CodeExpression SerializeToExpression (IDesignerSerializationManager manager, object instance)
  		{
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			CodeExpression expression = null;
  			if (instance != null)
  				expression = this.GetExpression (manager, instance); // 1 - IDesignerSerializationManager.GetExpression
  			if (expression == null) {
  				CodeDomSerializer serializer = this.GetSerializer (manager, instance); // 2 - manager.GetSerializer().Serialize()
  				if (serializer != null) {
  					object serialized = serializer.Serialize (manager, instance);
  					expression = serialized as CodeExpression; // 3 - CodeStatement or CodeStatementCollection
  					if (expression == null) {
  						CodeStatement statement = serialized as CodeStatement;
  						CodeStatementCollection statements = serialized as CodeStatementCollection;
  
  						if (statement != null || statements != null) {
  							CodeStatementCollection contextStatements = null;
  
  							StatementContext context = manager.Context[typeof (StatementContext)] as StatementContext;
  							if (context != null && instance != null)
  								contextStatements = context.StatementCollection[instance];
  
  							if (contextStatements == null)
  								contextStatements = manager.Context[typeof (CodeStatementCollection)] as CodeStatementCollection;
  
  							if (contextStatements != null) {
  								if (statements != null)
  									contextStatements.AddRange (statements);
  								else
  									contextStatements.Add (statement);
  							}
  						}
  					}
  					if (expression == null && instance != null)
  						expression = this.GetExpression (manager, instance); // 4
  				} else {
  					ReportError (manager, "No serializer found for type '" + instance.GetType ().Name + "'");
  				}
  			}
  			return expression;
  		}
  
  		protected CodeDomSerializer GetSerializer (IDesignerSerializationManager manager, object instance)
  		{
  			DesignerSerializerAttribute attrInstance, attrType;
  			attrType = attrInstance = null;
  
  			CodeDomSerializer serializer = null;
  			if (instance == null)
  				serializer = this.GetSerializer (manager, null);
  			else {		
  				AttributeCollection attributes = TypeDescriptor.GetAttributes (instance);
  				foreach (Attribute a in attributes) {
  					DesignerSerializerAttribute designerAttr = a as DesignerSerializerAttribute;
  					if (designerAttr != null && manager.GetType (designerAttr.SerializerBaseTypeName) == typeof (CodeDomSerializer)) {
  						attrInstance = designerAttr;
  						break;
  					}
  				}
  	
  				attributes = TypeDescriptor.GetAttributes (instance.GetType ());
  				foreach (Attribute a in attributes) {
  					DesignerSerializerAttribute designerAttr = a as DesignerSerializerAttribute;
  					if (designerAttr != null && manager.GetType (designerAttr.SerializerBaseTypeName) == typeof (CodeDomSerializer)) {
  						attrType = designerAttr;
  						break;
  					}
  				}
  	
  				// if there is metadata modification in the instance then create the specified serializer instead of the one
  				// in the Type.
  				if (attrType != null && attrInstance != null && attrType.SerializerTypeName != attrInstance.SerializerTypeName)
  					serializer = Activator.CreateInstance (manager.GetType (attrInstance.SerializerTypeName)) as CodeDomSerializer;
  				else
  					serializer = this.GetSerializer (manager, instance.GetType ());
  			}
  
  			return serializer;
  		}
  
  		protected CodeDomSerializer GetSerializer (IDesignerSerializationManager manager, Type instanceType)
  		{
  			return manager.GetSerializer (instanceType, typeof (CodeDomSerializer)) as CodeDomSerializer;
  		}
  
  		protected CodeExpression GetExpression (IDesignerSerializationManager manager, object instance)
  		{
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  			if (instance == null)
  				throw new ArgumentNullException ("instance");
  
  			CodeExpression expression = null;
  
  			ExpressionTable expressions = manager.Context[typeof (ExpressionTable)] as ExpressionTable;
  			if (expressions != null) // 1st try: ExpressionTable
  				expression = expressions [instance] as CodeExpression;
  
  			if (expression == null) { // 2nd try: RootContext
  				RootContext context = manager.Context[typeof (RootContext)] as RootContext;
  				if (context != null && context.Value == instance)
  					expression = context.Expression;
  			}
  
  			if (expression == null) { // 3rd try: IReferenceService (instance.property.property.property
  				string name = manager.GetName (instance);
  				if (name == null || name.IndexOf (".") == -1) {
  					IReferenceService service = manager.GetService (typeof (IReferenceService)) as IReferenceService;
  					if (service != null) {
  						name = service.GetName (instance);
  						if (name != null && name.IndexOf (".") != -1) {
  							string[] parts = name.Split (new char[] { ',' });
  							instance = manager.GetInstance (parts[0]);
  							if (instance != null) {
  								expression = SerializeToExpression (manager, instance);
  								if (expression != null) {
  									for (int i=1; i < parts.Length; i++)
  										expression = new CodePropertyReferenceExpression (expression, parts[i]);
  								}
  							}
  						}
  					}
  				}
  			}
  			return expression;
  		}
  
  		protected void SetExpression (IDesignerSerializationManager manager, object instance, CodeExpression expression)
  		{
  			SetExpression (manager, instance, expression, false);
  		}
  
  		// XXX: isPreset - what does this do when set?
  		//
  		protected void SetExpression (IDesignerSerializationManager manager, object instance, CodeExpression expression, bool isPreset)
  		{
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  			if (instance == null)
  				throw new ArgumentNullException ("instance");
  			if (expression == null)
  				throw new ArgumentNullException ("expression");
  
  			ExpressionTable expressions = manager.Context[typeof (ExpressionTable)] as ExpressionTable;
  			if (expressions == null) {
  				expressions = new ExpressionTable ();
  				manager.Context.Append (expressions);
  			}
  
  			expressions[instance] = expression;
  		}
  
  		protected bool IsSerialized (IDesignerSerializationManager manager, object value) 
  		{
  			return this.IsSerialized (manager, value, false);
  		}
  
  		// XXX: What should honorPreset do?
  		protected bool IsSerialized (IDesignerSerializationManager manager, object instance, bool honorPreset) 
  		{
  			if (instance == null)
  				throw new ArgumentNullException ("instance");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			if (this.GetExpression (manager, instance) != null)
  				return true;
  			else
  				return false;
  		}
  
  		protected CodeExpression SerializeCreationExpression (IDesignerSerializationManager manager, object value, out bool isComplete) 
  		{
  			if (value == null)
  				throw new ArgumentNullException ("value");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			CodeExpression expression = null;
  
  			TypeConverter converter = TypeDescriptor.GetConverter (value);
  			if (converter != null && converter.CanConvertTo (typeof (InstanceDescriptor))) {
  				InstanceDescriptor descriptor = converter.ConvertTo (value, typeof (InstanceDescriptor)) as InstanceDescriptor;
  				isComplete = descriptor.IsComplete;
  				if (descriptor != null && descriptor.MemberInfo != null)
  					expression = this.SerializeInstanceDescriptor (manager, descriptor);
  				else
  					ReportError (manager, "Unable to serialize to InstanceDescriptor", 
  						     "Value Type: " + value.GetType ().Name + System.Environment.NewLine + 
  						     "Value (ToString): " + value.ToString ());
  			} else {
  				if (value.GetType().GetConstructor (Type.EmptyTypes) != null)
  					expression = new CodeObjectCreateExpression (value.GetType ().FullName, new CodeExpression[0]);
  				isComplete = false;
  			}
  			return expression;
  		}
  
  		private CodeExpression SerializeInstanceDescriptor (IDesignerSerializationManager manager, InstanceDescriptor descriptor)
  		{
  			CodeExpression expression = null;
  			MemberInfo member = descriptor.MemberInfo;
  			CodeExpression target = new CodeTypeReferenceExpression (member.DeclaringType);
  
  			if (member is PropertyInfo) {
  				expression = new CodePropertyReferenceExpression (target, member.Name);
  			} else if (member is FieldInfo) {
  				expression = new CodeFieldReferenceExpression (target, member.Name);
  			} else if (member is MethodInfo) {
  				CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression (target, member.Name);
  				if (descriptor.Arguments != null && descriptor.Arguments.Count > 0)
  					methodInvoke.Parameters.AddRange (SerializeParameters (manager, descriptor.Arguments));
  				expression = methodInvoke;
  			} else if (member is ConstructorInfo) {
  				CodeObjectCreateExpression createExpr = new CodeObjectCreateExpression (member.DeclaringType);
  				if (descriptor.Arguments != null && descriptor.Arguments.Count > 0)
  					createExpr.Parameters.AddRange (SerializeParameters (manager, descriptor.Arguments));
  				expression = createExpr;
  			}
  
  			return expression;
  		}
  
  		private CodeExpression[] SerializeParameters (IDesignerSerializationManager manager, ICollection parameters)
  		{
  			CodeExpression[] expressions = null;
  
  			if (parameters != null && parameters.Count > 0) {
  				expressions = new CodeExpression[parameters.Count];
  				int i = 0;
  				foreach (object parameter in parameters) {
  					expressions[i] = this.SerializeToExpression (manager, parameter);
  					i++;
  				}
  			}
  
  			return expressions;
  		}
  
  		protected void SerializeEvent (IDesignerSerializationManager manager, CodeStatementCollection statements, 
  					       object value, EventDescriptor descriptor) 
  		{
  			if (descriptor == null)
  				throw new ArgumentNullException ("descriptor");
  			if (value == null)
  				throw new ArgumentNullException ("value");
  			if (statements == null)
  				throw new ArgumentNullException ("statements");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			MemberCodeDomSerializer serializer = manager.GetSerializer (descriptor.GetType (), typeof (MemberCodeDomSerializer)) as MemberCodeDomSerializer;
  			if (serializer != null && serializer.ShouldSerialize (manager, value, descriptor))
  				serializer.Serialize (manager, value, descriptor, statements);
  		}
  
  		protected void SerializeEvents (IDesignerSerializationManager manager, CodeStatementCollection statements, 
  						object value, params Attribute[] filter)
  		{
  			if (filter == null)
  				throw new ArgumentNullException ("filter");
  			if (value == null)
  				throw new ArgumentNullException ("value");
  			if (statements == null)
  				throw new ArgumentNullException ("statements");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			EventDescriptorCollection events = TypeDescriptor.GetEvents (value, filter);
  			foreach (EventDescriptor e in events) 
  				this.SerializeEvent (manager, statements, value, e);
  		}
  
  		protected void SerializeProperty (IDesignerSerializationManager manager, CodeStatementCollection statements, 
  						  object value, PropertyDescriptor propertyToSerialize)
  		{
  			if (propertyToSerialize == null)
  				throw new ArgumentNullException ("propertyToSerialize");
  			if (value == null)
  				throw new ArgumentNullException ("value");
  			if (statements == null)
  				throw new ArgumentNullException ("statements");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			MemberCodeDomSerializer serializer = manager.GetSerializer (propertyToSerialize.GetType (), 
  										    typeof (MemberCodeDomSerializer)) as MemberCodeDomSerializer;
  			if (serializer != null && serializer.ShouldSerialize (manager, value, propertyToSerialize))
  				serializer.Serialize (manager, value, propertyToSerialize, statements);
  		}
  		
  		protected void SerializeProperties (IDesignerSerializationManager manager, CodeStatementCollection statements, 
  											object value, Attribute[] filter) 
  		{
  			if (filter == null)
  				throw new ArgumentNullException ("filter");
  			if (value == null)
  				throw new ArgumentNullException ("value");
  			if (statements == null)
  				throw new ArgumentNullException ("statements");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (value, filter);
  			foreach (PropertyDescriptor property in properties) {
  				if (!property.Attributes.Contains (DesignerSerializationVisibilityAttribute.Hidden))
  					this.SerializeProperty (manager, statements, value, property);
  			}
  		}
  
  		protected virtual object DeserializeInstance (IDesignerSerializationManager manager, Type type, 
  							      object[] parameters, string name, bool addToContainer)
  		{
  			if (type == null)
  				throw new ArgumentNullException ("type");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			return manager.CreateInstance (type, parameters, name, addToContainer);
  		}
  
  		protected string GetUniqueName (IDesignerSerializationManager manager, object instance)
  		{
  			if (instance == null)
  				throw new ArgumentNullException ("instance");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			string name = manager.GetName (instance);
  			if (name == null) {
  				INameCreationService service = manager.GetService (typeof (INameCreationService)) as INameCreationService;
  				name = service.CreateName (null, instance.GetType ());
  				if (name == null)
  					name = instance.GetType ().Name.ToLower ();
  				manager.SetName (instance, name);
  			}
  			return name;
  		}
  
  		protected object DeserializeExpression (IDesignerSerializationManager manager, string name, CodeExpression expression) 
  		{
  			if (expression == null)
  				throw new ArgumentNullException ("expression");
  			if (manager == null)
  				throw new ArgumentNullException ("manager");
  
  			bool errorOccurred = false;
  			object deserialized = null;
  
  			// CodeThisReferenceExpression
  			//
  			CodeThisReferenceExpression thisExpr = expression as CodeThisReferenceExpression;
  			if (thisExpr != null) {
  				RootContext context = manager.Context[typeof (RootContext)] as RootContext;
  				if (context != null) {
  					deserialized = context.Value;
  				} else {
  					IDesignerHost host = manager.GetService (typeof (IDesignerHost)) as IDesignerHost;
  					if (host != null)
  						deserialized = host.RootComponent;
  				}
  			}
  			
  			// CodeVariableReferenceExpression
  			//
  			CodeVariableReferenceExpression varRef = expression as CodeVariableReferenceExpression;
  			if (deserialized == null && varRef != null) {
  				deserialized = manager.GetInstance (varRef.VariableName);
  				if (deserialized == null) {
  					ReportError (manager, "Variable '" + varRef.VariableName + "' not initialized prior to reference");
  					errorOccurred = true;
  				}
  			}
  
  			// CodeFieldReferenceExpression (used for Enum references as well)
  			//
  			CodeFieldReferenceExpression fieldRef = expression as CodeFieldReferenceExpression;
  			if (deserialized == null && fieldRef != null) {
  				deserialized = manager.GetInstance (fieldRef.FieldName);
  				if (deserialized == null) {
  					object fieldHolder = DeserializeExpression (manager, null, fieldRef.TargetObject);
  					FieldInfo field = null;
  					if (fieldHolder is Type) // static field
  						field = ((Type)fieldHolder).GetField (fieldRef.FieldName, 
  										      BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static);
  					else // instance field
  						field = fieldHolder.GetType().GetField (fieldRef.FieldName, 
  											BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance);
  					if (field != null)
  						deserialized = field.GetValue (fieldHolder);
  				}
  				if (deserialized == null)
  					ReportError (manager, "Field '" + fieldRef.FieldName + "' not initialized prior to reference");
  			}
  				
  
  			// CodePrimitiveExpression
  			//
  			CodePrimitiveExpression primitiveExp = expression as CodePrimitiveExpression;
  			if (deserialized == null && primitiveExp != null)
  				deserialized = primitiveExp.Value;
  
  			// CodePropertyReferenceExpression
  			//
  			CodePropertyReferenceExpression propRef = expression as CodePropertyReferenceExpression;
  			if (deserialized == null && propRef != null) {
  				object target = DeserializeExpression (manager, null, propRef.TargetObject);
  				if (target != null && target != _errorMarker) {
  					bool found = false;
  					if (target is Type) {
  						PropertyInfo property = ((Type)target).GetProperty (propRef.PropertyName,
  												    BindingFlags.GetProperty | 
  												    BindingFlags.Public | BindingFlags.Static);
  						if (property != null) {
  							deserialized = property.GetValue (null, null);
  							found = true;
  						}
  
  						// NRefactory seems to produce PropertyReferences to reference some fields and enums
  						//
  						FieldInfo field = ((Type)target).GetField (propRef.PropertyName,
  											   BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static);
  						if (field != null) {
  							deserialized = field.GetValue (null);
  							found = true;
  						}
  					} else {
  						PropertyDescriptor property = TypeDescriptor.GetProperties (target)[propRef.PropertyName];
  						if (property != null) {
  							deserialized = property.GetValue (target);
  							found = true;
  						}
  
  						FieldInfo field = target.GetType().GetField (propRef.PropertyName,
  											     BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance);
  						if (field != null) {
  							deserialized = field.GetValue (null);
  							found = true;
  						}
  					}
  					
  					if (!found) {
  						ReportError (manager, "Missing field '" + propRef.PropertyName + " 'in type " + 
  							     (target is Type ? ((Type)target).Name : target.GetType ().Name) + "'");
  						errorOccurred = true;
  					}
  				}
  			}
  
  			// CodeObjectCreateExpression
  			//
  			CodeObjectCreateExpression createExpr = expression as CodeObjectCreateExpression;
  			if (deserialized == null && createExpr != null) {
  				Type type = manager.GetType (createExpr.CreateType.BaseType);
  				if (type == null) {
  					ReportError (manager, "Type '" + createExpr.CreateType.BaseType + "' not found." + 
  						     "Are you missing a reference?");
  					errorOccurred = true;
  				} else {
  					object[] arguments = new object[createExpr.Parameters.Count];
  					for (int i=0; i < createExpr.Parameters.Count; i++) {
  						arguments[i] = this.DeserializeExpression (manager, null, createExpr.Parameters[i]);
  						if (arguments[i] == _errorMarker) {
  							errorOccurred = true;
  							break;
  						}
  					}
  					if (!errorOccurred) {
  						bool addToContainer = false;
  						if (typeof(IComponent).IsAssignableFrom (type))
  							addToContainer = true;
  						deserialized = this.DeserializeInstance (manager, type, arguments, name, addToContainer);
  						if (deserialized == _errorMarker || deserialized == null) {
  							string info = "Type to create: " + createExpr.CreateType.BaseType + System.Environment.NewLine +
  								"Name: " + name + System.Environment.NewLine +
  								"addToContainer: " + addToContainer.ToString () + System.Environment.NewLine +
  								"Parameters Count: " + createExpr.Parameters.Count + System.Environment.NewLine;
  	
  							for (int i=0; i < arguments.Length; i++) {
  								info += "Parameter Number: " + i.ToString () + System.Environment.NewLine +
  									"Parameter Type: " + (arguments[i] == null ? "null" : arguments[i].GetType ().Name) +
  									System.Environment.NewLine +
  									"Parameter '" + i.ToString () + "' Value: " + arguments[i].ToString () + System.Environment.NewLine;
  							}
  							ReportError (manager, 
  								     "Unable to create an instance of type '" + createExpr.CreateType.BaseType + "'",
  								     info);
  							errorOccurred = true;
  						}
  					}
  				}
  			}
  
  			// CodeArrayCreateExpression
  			//
  			CodeArrayCreateExpression arrayCreateExpr = expression as CodeArrayCreateExpression;
  			if (deserialized == null && arrayCreateExpr != null) {
  				Type arrayType = manager.GetType (arrayCreateExpr.CreateType.BaseType);
  				if (arrayType == null) {
  					ReportError (manager, "Type '" + arrayCreateExpr.CreateType.BaseType + "' not found." + 
  						     "Are you missing a reference?");
  					errorOccurred = true;
  				} else {
  
gt: 					ArrayList initializers = new ArrayList ();