using System;
using System.CodeDom;
using System.Reflection;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Readiness;
using Chernobyl.Switch;
namespace Chernobyl.Reflection.Template.CodeDom
{
///
/// An that can take any
/// instance and generate CodeDom for it.
///
public class CodeDomInstance : InstanceDecorator
{
///
/// Initializes a new instance of the class.
///
/// The instance to
/// generate CodeDom for and provide the
/// implementation for this class.
public CodeDomInstance(IInstance decoratedInstance)
: this(decoratedInstance, false)
{
ConfigureImplementer();
}
///
/// Initializes a new instance of the class.
///
/// The instance to
/// generate CodeDom for and provide the
/// implementation for this class.
/// True if this instance should be configured
/// immediately, false if the
/// method will be
/// invoked at a later time by the implementing type.
protected CodeDomInstance(IInstance decoratedInstance, bool configure)
: base(configure)
{
// set the _decoratedInstance and then configure that implementer
// now that it is set.
_decoratedInstance = decoratedInstance;
}
///
/// The that is the parent to this
/// . This will be
/// added to the parent 's
/// list. If this
/// is already a child to another then it will
/// be removed from that 's
/// list. Setting null on this property
/// will cause this to be removed from it's
/// parent, if necessary.
///
public override IComponent Parent
{
get { return base.Parent; }
set
{
if(base.Parent != value)
{
IComponent previousParent = base.Parent;
base.Parent = value;
// ensure our base class believes this is actually our new
// parent before attempting to do anything
if (base.Parent == value && base.Parent != null)
{
// Remove our changes from the previous parent.
CodeDomMember previousParentMember = previousParent as CodeDomMember;
if(previousParentMember != null)
{
previousParentMember.TypeDeclaration.Members.Remove(TypeDeclaration);
previousParentMember.TypeDeclaration.Members.Remove(ParentField);
previousParentMember.TypeDeclaration.Members.Remove(ParentProperty);
// Remove the creation/assignment statement of the
// IInstance from the previous IMember parent's
// constructor.
previousParentMember.ClassConstructor.Statements.Remove(_memberClassPropertyAssignment);
// Remove the code statement that adds this IInstance
// to the Parent IMember's ComponentChildren collection.
previousParentMember.ClassConstructor.Statements.Remove(_memberClassInstanceAddToComponents);
// Remove the code statement that attaches the
// CountdownSwitch's CountdownSwitch.Countdown(object, EventArgs)
// method to the BecameReady event of the generated
// IInstance
previousParentMember.ClassConstructor.Statements.Remove(_parentMemberInitializationCountdwonEventAttach);
// Remove the expression that references this IInstance
// as one of the arguments.
previousParentMember.ArgumentExpressions.Remove(_instanceReference);
}
// Now make changes to the new parent.
CodeDomMember parentMember = base.Parent as CodeDomMember;
if (parentMember != null)
{
parentMember.TypeDeclaration.Members.Add(TypeDeclaration);
parentMember.TypeDeclaration.Members.Add(ParentField);
parentMember.TypeDeclaration.Members.Add(ParentProperty);
// create a reference from the parent member to the instance's class instance property:
// This code creates (in the case of an String): this.String_36193440_Instance
CodePropertyReferenceExpression instanceClassProperty = new CodePropertyReferenceExpression(parentMember.ThisReference, ParentProperty.Name);
// create the creation of the instance's class in the constructor of the member's class
CodeExpression[] instanceClassArguments = new[] { parentMember.ServicesReference };
_memberClassPropertyAssignment = new CodeAssignStatement(instanceClassProperty, new CodeObjectCreateExpression(TypeDeclaration.Name, instanceClassArguments));
parentMember.ClassConstructor.Statements.Add(_memberClassPropertyAssignment);
// create the expression that adds the instance's class instance to the ComponentChildren
// property of the member's class
CodeMethodInvokeExpression memberClassInstanceAddToComponentsExpression = new CodeMethodInvokeExpression(parentMember.ComponentChildrenReference, "Add", instanceClassProperty);
_memberClassInstanceAddToComponents = new CodeExpressionStatement(memberClassInstanceAddToComponentsExpression);
parentMember.ClassConstructor.Statements.Add(_memberClassInstanceAddToComponents);
// assign the initialization CountdownSwitch's CountdownSwitch.Countdown(object, EventArgs) method
// to the BecameReady event of the generated IInstance. This code creates (in the parent member's
// constructor): [instance's name].BecameReady += this._initializationCounter.Countdown;
_parentMemberInitializationCountdwonEventAttach =
new CodeAttachEventStatement(instanceClassProperty, "BecameReady", new CodeMethodReferenceExpression(parentMember.InitializationCounterFieldReference, "Countdown"));
parentMember.ClassConstructor.Statements.Add(_parentMemberInitializationCountdwonEventAttach);
// now set (property, field) or invoke (method, constructor) the member with
// this instance as one or more of it's arguments
_instanceReference = new CodePropertyReferenceExpression(instanceClassProperty, GenericInstanceName);
parentMember.ArgumentExpressions.Add(_instanceReference);
}
}
}
}
}
///
/// The code generated property that represents an instance of
/// . This field is located in the parent
/// members class.
///
public CodeMemberProperty ParentProperty
{
get
{
if(_parentProperty == null)
{
_parentProperty = new CodeMemberProperty
{
Name = ParentField.Name.TrimStart('_'),
Type = new CodeTypeReference(TypeDeclaration.Name),
HasGet = true,
HasSet = true
};
CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), ParentField.Name);
_parentProperty.GetStatements.Add(new CodeMethodReturnStatement(fieldReference));
_parentProperty.SetStatements.Add(new CodeAssignStatement(fieldReference, new CodePropertySetValueReferenceExpression()));
}
return _parentProperty;
}
}
///
/// The code generated field that represents an instance of
/// . This field is located in the parent
/// members class.
///
public CodeMemberField ParentField
{
get
{
if (_parentField == null)
{
string fieldAndPropertyName = TypeDeclaration.Name + "_Instance";
_parentField = new CodeMemberField(TypeDeclaration.Name, "_" + fieldAndPropertyName);
}
return _parentField;
}
}
///
/// The declaration of the type that can be used to create and initialize
/// this .
///
public virtual CodeTypeDeclaration TypeDeclaration
{
get
{
if (_typeDeclaration == null)
{
_typeDeclaration = new CodeTypeDeclaration(CodeName) {TypeAttributes = TypeAttributes.NotPublic};
// derive from the Instance<> base class and add our constructor
_typeDeclaration.BaseTypes.Add(GenericInstanceTypeReference);
_typeDeclaration.Members.Add(ClassConstructor);
}
return _typeDeclaration;
}
}
///
/// The constructor of the type specified by
/// which initializes the
/// .
///
public virtual CodeConstructor ClassConstructor
{
get
{
if (_classConstructor == null)
{
// create a constructor for this class
_classConstructor = new CodeConstructor
{ Attributes = MemberAttributes.Public };
_classConstructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IEventCollection