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), ServicesReferenceName)); _classConstructor.BaseConstructorArgs.Add(ServicesReference); if(Name.Length != 0) _classConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(Name)); // add a default constructor call for this instance, but // only if this instance's type supports one. if (CreationStatement != null) _classConstructor.Statements.Add(CreationStatement); } return _classConstructor; } } /// /// The CodeDom method where the value of the instance is set. By /// default, this is set to the . /// public CodeMemberMethod InitializeMethod { get { _initializeMethod = _initializeMethod ?? ClassConstructor; return _initializeMethod; } set { _initializeMethod = value; } } /// /// A that represents the creation of this /// instance. By default, this property returns the statement for the /// creation of the instance using a default constructor or null if this /// 's does not contain a /// parameterless constructor. /// public virtual CodeStatement CreationStatement { get { if (_creationStatement == null && ContainsParameterlessConstructor == true) { CodePropertyReferenceExpression instanceStatement = new CodePropertyReferenceExpression(ThisReference, "TheInstance"); CodeObjectCreateExpression createExpression = new CodeObjectCreateExpression(Type); _creationStatement = new CodeAssignStatement(instanceStatement, createExpression); } return _creationStatement; } } /// /// A for the type /// taking a the generic type parameter /// represented by . /// public CodeTypeReference GenericInstanceTypeReference { get { if(_genericInstanceTypeReference == null) { Type genericType = typeof (Instance<>).MakeGenericType(new[] {Type}); _genericInstanceTypeReference = new CodeTypeReference(genericType); } return _genericInstanceTypeReference; } } /// /// A that references the /// property of the CodeDom /// class generated by this class. /// public CodeExpression ComponentChildrenReference { get { if (_componentChildrenReference == null) _componentChildrenReference = new CodePropertyReferenceExpression(ThisReference, "ComponentChildren"); return _componentChildrenReference; } } /// /// A reference to the "this". Used by children to reference the CodeDom /// class generated by this class. /// public CodeExpression ThisReference { get { return _thisReference ?? (_thisReference = new CodeThisReferenceExpression()); } } /// /// A reference to an by the name of "services". /// public CodeExpression ServicesReference { get { return _servicesReference ?? (_servicesReference = new CodeVariableReferenceExpression(ServicesReferenceName)); } } /// /// The instance that implements the /// functionality of this class. /// public override IInstance DecoratedInstance { get { return _decoratedInstance; } } /// /// The name of the property. /// protected const string GenericInstanceName = "TheGenericInstance"; /// /// True if this instance is of a that has a /// parameterless constructor, false if otherwise. /// bool ContainsParameterlessConstructor { get { if(_containsParameterlessConstructor.HasValue == false) _containsParameterlessConstructor = Type.GetConstructor(Type.EmptyTypes) != null; return _containsParameterlessConstructor.Value; } } /// /// The name of an by the name of "services" /// within the 's classes constructor. /// const string ServicesReferenceName = "services"; /// /// The backing field to . /// CodeTypeDeclaration _typeDeclaration; /// /// The backing field to . /// CodeConstructor _classConstructor; /// /// The backing field to . /// CodeMemberMethod _initializeMethod; /// /// The backing field to . /// CodeStatement _creationStatement; /// /// The backing field to . /// CodeTypeReference _genericInstanceTypeReference; /// /// The backing field to . /// CodeExpression _componentChildrenReference; /// /// The backing field to . /// CodeExpression _thisReference; /// /// The backing field to . /// CodeExpression _servicesReference; /// /// The backing field to /// CodeMemberField _parentField; /// /// The backing field to . /// CodeMemberProperty _parentProperty; /// /// The backing field to . /// IInstance _decoratedInstance; /// /// The backing field to . /// If this value is null, then this 's containment of /// a parameterless constructor hasn't been identified yet. True if it /// does contain one, false if otherwise. /// bool? _containsParameterlessConstructor; /// /// The code statement that creates this 's class /// in the constructor of the /// 's class. /// CodeAssignStatement _memberClassPropertyAssignment; /// /// The code statement that adds this 's class /// instance to the property /// of the 's class /// CodeExpressionStatement _memberClassInstanceAddToComponents; /// /// The code statement that attaches the initialization /// 's /// method /// to the event of the generated /// . /// CodeAttachEventStatement _parentMemberInitializationCountdwonEventAttach; /// /// The code reference to this for use as an /// argument to the the parent . /// CodeExpression _instanceReference; } }