/// <summary> /// Build the view types for a standard contract /// </summary> /// <param name="contractType">Type of input contract</param> /// <param name="component">Pipeline component the source should be added to</param> /// <param name="componentType">Pipeline view type (hav or AIB or generic) </param> /// <param name="activatable">Is this type an activatable contract</param> internal void BuildView(Type contractType, PipelineSegmentSource component, SegmentType componentType,bool activatable) { if (IsEvent(contractType)) { //Contract type is an event contract and does not have a corresponding view return; } String typeName = _symbols.GetNameFromType(contractType, componentType,SegmentDirection.None,false); CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(componentType,contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(typeName); type.TypeAttributes = TypeAttributes.Abstract | TypeAttributes.Public; object[] typeComments = contractType.GetCustomAttributes(typeof(PipelineHints.CommentAttribute), false); foreach (PipelineHints.CommentAttribute comment in typeComments) { type.Comments.Add(new CodeCommentStatement(comment.Comment)); } if (IsViewInterface(contractType)) { type.TypeAttributes |= TypeAttributes.Interface; } //This will consult the type hierarchy we built earlier, currently we only support one base type or 1 implemented interface Type baseType = GetBaseContract(contractType); if (baseType != null) { CodeTypeReference baseRef = new CodeTypeReference(_symbols.GetNameFromType(baseType, componentType, SegmentDirection.None, contractType)); type.BaseTypes.Add(baseRef); } if (IsEventArgs(contractType)) { //Contract type is an event args type and needs it as a base PipelineHints.EventArgsAttribute argsType = GetEventArgs(contractType); if (argsType.Cancelable) { type.BaseTypes.Add(typeof(System.ComponentModel.CancelEventArgs)); } else { type.BaseTypes.Add(typeof(EventArgs)); } } //Only the add-in base and shared views need an attribute, the HAV doesn't need one. if (activatable && (componentType == SegmentType.AIB || componentType == SegmentType.VIEW)) { CodeAttributeDeclaration marker = new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.AddIn.Pipeline.AddInBaseAttribute))); type.CustomAttributes.Add(marker); } Dictionary<String, CodeMemberProperty> props = new Dictionary<string, CodeMemberProperty>(); foreach (MethodInfo mi in GetMethodsFromContract(contractType,false)) { //We only need to build the event once, so we decided to do it on event add. //We do not do error checking to match up adds and removes. if (IsEventAdd(mi)) { CodeTypeReference eventType = GetEventViewType(componentType, mi,false); CodeMemberEvent abstractEvent = new CodeMemberEvent(); PipelineHints.EventAddAttribute attr = GetEventAdd(mi); //TODO: remove this line. Abstract events are not supported by codedom since VB can't handle them. abstractEvent.Attributes = MemberAttributes.Abstract; abstractEvent.Name = attr.Name; abstractEvent.Type = eventType; type.Members.Add(abstractEvent); //We only look for comments on the event add method. Any comments on the event remove method will be ignored object[] eventComments = mi.GetCustomAttributes(typeof(PipelineHints.CommentAttribute), false); foreach (PipelineHints.CommentAttribute comment in eventComments) { abstractEvent.Comments.Add(new CodeCommentStatement(comment.Comment)); } continue; } if (IsEventRemove(mi)) { continue; } //If this method is marked as a property method using an attribute or declared directly as a property in the contrac then //we should express it as a property in the view, rather than as a method. if (IsProperty(mi)) { CodeMemberProperty prop; SegmentDirection direction = SegmentDirection.None; bool prefix = false; prop = GetProperyDecl(contractType,type,mi,props,componentType, direction, prefix); switch (GetPropertyAttribute(mi).Type) { case PropertyType.set: prop.HasSet = true; break; case PropertyType.get: prop.HasGet = true; break; } object[] propComments = mi.GetCustomAttributes(typeof(PipelineHints.CommentAttribute), false); foreach (PipelineHints.CommentAttribute comment in propComments) { prop.Comments.Add(new CodeCommentStatement(comment.Comment)); } continue; } CodeMemberMethod method = new CodeMemberMethod(); method.Attributes = MemberAttributes.Abstract | MemberAttributes.Public; method.Name = mi.Name; //Setup the return type for this member in the view method.ReturnType = GetViewTypeReference(componentType, mi.ReturnType,contractType,SegmentDirection.None); //For each parameter in the method in the contract, add the right one to the view AddParametersToViewMethod(componentType, mi, method); object[] methodComments = mi.GetCustomAttributes(typeof(PipelineHints.CommentAttribute), false); foreach (PipelineHints.CommentAttribute comment in methodComments) { method.Comments.Add(new CodeCommentStatement(comment.Comment)); } type.Members.Add(method); } codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(typeName, ccu)); }
internal void BuildViewToContractAdapter(Type contractType, PipelineSegmentSource component,SegmentType componentType,bool activatable) { //Set up type String typeName = _symbols.GetNameFromType(contractType, componentType,SegmentDirection.ViewToContract,false); Dictionary<String, CodeMemberProperty> props = new Dictionary<string, CodeMemberProperty>(); SegmentType viewType; if (componentType == SegmentType.ASA) { viewType = SegmentType.AIB; } else { viewType = SegmentType.HAV; } String viewName = _symbols.GetNameFromType(contractType,viewType); //If this is an event type determine which real type this is an event on if (contractType.GetCustomAttributes(typeof(PipelineHints.EventHandlerAttribute), false).Length > 0) { viewName = "System.Object"; } //Set up the namespace and the type declaration //Derive from contractbase and the specific contract CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(componentType,contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(typeName); type.TypeAttributes = TypeAttributes.Public; type.BaseTypes.Add(new CodeTypeReference(typeof(ContractBase))); type.BaseTypes.Add(new CodeTypeReference(contractType)); //If this is activatable mark it with the addinadapterattribute //The viewtocontract adapter is only ever activatable in the add-in side adapter so no need to check which adapter we're in if (activatable) { CodeAttributeDeclaration marker = new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.AddIn.Pipeline.AddInAdapterAttribute))); type.CustomAttributes.Add(marker); } CodeMemberField aib = new CodeMemberField(viewName, "_view"); type.Members.Add(aib); //Build constructor //Add parameter for view type and assign it to member field _view CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; CodeParameterDeclarationExpression parameter = new CodeParameterDeclarationExpression(viewName, "view"); constructor.Parameters.Add(parameter); CodeAssignStatement assign = new CodeAssignStatement(new CodeVariableReferenceExpression("_view"), new CodeVariableReferenceExpression("view")); constructor.Statements.Add(assign); type.Members.Add(constructor); if (IsEvent(contractType)) { //If this is an event type we have an additional constructor paramter that is the fieldinfo object for the eventhandler we need to invoke. //Add this parameter to the constructor and then store it in a member variable. CodeMemberField eventMember = new CodeMemberField(typeof(System.Reflection.MethodInfo), "_event"); eventMember.Attributes |= MemberAttributes.Private; type.Members.Add(eventMember); constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(System.Reflection.MethodInfo), "eventProp")); constructor.Statements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression("_event"), new CodeVariableReferenceExpression("eventProp"))); //We have already validated that this is an event so the attribute exists //We have also validated, while creating the views, that this contract has one method and that one method takes in one parameter //The one method on this type acts as the delegate with its parameter representing the event args. MethodInfo mi = contractType.GetMethods()[0]; CodeExpression args = null; CodeTypeReference parameterType; ParameterInfo pi = mi.GetParameters()[0]; parameterType = new CodeTypeReference(pi.ParameterType); CodeMemberMethod method = new CodeMemberMethod(); method.Name = mi.Name; method.Parameters.Add(new CodeParameterDeclarationExpression(parameterType, "args")); method.Attributes = MemberAttributes.Public | MemberAttributes.Final; CodeStatementCollection adaptArgs = new CodeStatementCollection(); //If the parameter type needs to be adapted to pass it to the contract then new up an adapter and pass that along as a parameter //Else simply pass the args in directly as the parameter. CodeTypeReference eventArgsViewType; if (TypeNeedsAdapting(pi.ParameterType)) { CodeObjectCreateExpression adaptedArgs = new CodeObjectCreateExpression(); adaptedArgs.Parameters.Add(new CodeVariableReferenceExpression("args")); adaptedArgs.CreateType = new CodeTypeReference(_symbols.GetNameFromType(pi.ParameterType, componentType, SegmentDirection.ContractToView, contractType)); adaptedArgs.CreateType = GetViewTypeReference(viewType, pi.ParameterType, contractType, SegmentDirection.ContractToView); CodeVariableDeclarationStatement adapterArgsDeclare = new CodeVariableDeclarationStatement(adaptedArgs.CreateType, "adaptedArgs"); CodeAssignStatement assignArgs = new CodeAssignStatement(new CodeVariableReferenceExpression("adaptedArgs"), adaptedArgs); assignArgs.Right = CallStaticAdapter(componentType, pi.ParameterType, new CodeTypeReferenceExpression("args"), SegmentDirection.ContractToView); adaptArgs.Add(adapterArgsDeclare); adaptArgs.Add(assignArgs); args = new CodeVariableReferenceExpression("adaptedArgs"); eventArgsViewType = new CodeTypeReference(_symbols.GetNameFromType(pi.ParameterType, viewType)); } else { args = new CodeVariableReferenceExpression("args"); eventArgsViewType = new CodeTypeReference(pi.ParameterType); } method.Statements.AddRange(adaptArgs); CodeVariableDeclarationStatement argsArray = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(object[])), "argsArray"); argsArray.InitExpression = new CodeArrayCreateExpression(new CodeTypeReference(typeof(object)), new CodePrimitiveExpression(1)); CodeAssignStatement addToArgsArray = new CodeAssignStatement(); addToArgsArray.Left = new CodeArrayIndexerExpression(new CodeVariableReferenceExpression("argsArray"),new CodePrimitiveExpression(0)); addToArgsArray.Right = args; CodeMethodInvokeExpression eventInvoke = new CodeMethodInvokeExpression(); eventInvoke.Method.MethodName = "Invoke"; eventInvoke.Method.TargetObject = new CodeVariableReferenceExpression("_event"); eventInvoke.Parameters.Add(new CodeVariableReferenceExpression("_view")); eventInvoke.Parameters.Add(new CodeVariableReferenceExpression("argsArray")); method.Statements.Add(argsArray); method.Statements.Add(addToArgsArray); method.Statements.Add(eventInvoke); //This is a cancelable event args //Return args.Cancel if event is hooked up //If event is not hooked up return false (don't cancel) if (mi.ReturnType.Equals(typeof(bool))) { if (!GetEventArgs(pi.ParameterType).Cancelable) { throw new InvalidOperationException("Event handler method returns a bool but the event args are not cancelable:"+mi.DeclaringType.FullName + "."+mi.Name); } method.ReturnType = new CodeTypeReference(typeof(bool)); CodeFieldReferenceExpression cancel = new CodeFieldReferenceExpression(new CodeVariableReferenceExpression("adaptedArgs"), "Cancel"); CodeMethodReturnStatement retCancelCheck = new CodeMethodReturnStatement(cancel); method.Statements.Add(retCancelCheck); } type.Members.Add(method); } else { //For standard contract types we simply iterate through each method and build an adapter one by one foreach (MethodInfo mi in GetMethodsFromContract(contractType, true)) { CodeMemberMethod method = new CodeMemberMethod(); CodeMethodInvokeExpression cmi = new CodeMethodInvokeExpression(); CodeStatementCollection prologue = new CodeStatementCollection(); CodeStatementCollection epilogue = new CodeStatementCollection(); CodeMethodReturnStatement ret = new CodeMethodReturnStatement(); cmi.Method = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("_view"), mi.Name); method.Attributes = MemberAttributes.Public; method.Name = mi.Name; //Set the methods return type appropriately if (mi.ReturnType.Equals(typeof(void))) { //If return type is void return type is null and there is no return statement. method.ReturnType = new CodeTypeReference(mi.ReturnType); ret = null; } else if (!TypeNeedsAdapting(mi.ReturnType)) { //If the return type does not need adapting return type mirrors the contract type and the return value is the direct result of the method call method.ReturnType = new CodeTypeReference(mi.ReturnType); ret.Expression = cmi; } else { method.ReturnType = new CodeTypeReference(mi.ReturnType); //If the return type needs adapting call method, pass its return value to the static adapter, and then return that CodeMethodInvokeExpression adaptExpr = CallStaticAdapter(componentType, mi.ReturnType, cmi, SegmentDirection.ViewToContract); ret.Expression = adaptExpr; } if (IsProperty(mi)) { method = null; CodeMemberProperty myProp; bool prefix = false; myProp = GetProperyDecl(contractType, type, mi, props, componentType, SegmentDirection.ViewToContract, prefix); CodeStatement statement = GetPropertyViewToContractAdapterImpl(componentType, viewType, mi, null); switch (GetPropertyAttribute(mi).Type) { case PropertyType.set: myProp.HasSet = true; myProp.SetStatements.Add(statement); break; case PropertyType.get: myProp.HasGet = true; myProp.GetStatements.Add(statement); break; } //If this method needs to be expressed as a property in the views we need to treat it differently. } else if (IsEventAdd(mi)) { //This indicates that the method we are in really represents an event handler on the view type rather than a method //We need to hook this up such that firing this event on one side causes it to fire on the other side //We've already validated that these are valid setevents when building the views PipelineHints.EventAddAttribute attr = GetEventAdd(mi); CodeTypeReference eventArgsType = GetEventArgsType(viewType, mi, true); ParameterInfo pi = mi.GetParameters()[0]; //Build a reference to the event handler on the view object CodeEventReferenceExpression eventHandler = new CodeEventReferenceExpression(new CodeVariableReferenceExpression("_view"), attr.Name); //Get the adapter for the event args type CodeTypeReference adapterType = new CodeTypeReference(_symbols.GetNameFromType(pi.ParameterType, componentType, SegmentDirection.ContractToView, contractType)); CodeObjectCreateExpression adapterConstruct = new CodeObjectCreateExpression(adapterType, new CodeVariableReferenceExpression(pi.Name)); CodeDelegateCreateExpression handler = new CodeDelegateCreateExpression(); //Build the event handler handler.DelegateType = new CodeTypeReference(typeof(EventHandler<>)); handler.DelegateType.TypeArguments.Add(eventArgsType); handler.TargetObject = adapterConstruct; handler.MethodName = "Handler"; CodeVariableDeclarationStatement handlerVar = new CodeVariableDeclarationStatement(); handlerVar.Type = handler.DelegateType; handlerVar.Name = "adaptedHandler"; handlerVar.InitExpression = handler; method.Statements.Add(handlerVar); //Add attach the handler to the eventhandler type on the view and finish building the method CodeAttachEventStatement attach = new CodeAttachEventStatement(eventHandler, new CodeVariableReferenceExpression(handlerVar.Name)); CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(pi.ParameterType, pi.Name); method.Parameters.Add(cp); method.Statements.Add(attach); //Add Dictionary of adapters CodeTypeReference dictionaryType = new CodeTypeReference("System.Collections.Generic.Dictionary"); dictionaryType.TypeArguments.Add(new CodeTypeReference(pi.ParameterType)); dictionaryType.TypeArguments.Add(handler.DelegateType); CodeMemberField handlers = new CodeMemberField(); handlers.Name = attr.Name + "_handlers"; handlers.Type = dictionaryType; type.Members.Add(handlers); //Initialize Dictionary of Adapters; CodeObjectCreateExpression createDictionary = new CodeObjectCreateExpression(); createDictionary.CreateType = dictionaryType; CodeAssignStatement initDictionary = new CodeAssignStatement(); initDictionary.Left = new CodeVariableReferenceExpression(handlers.Name); initDictionary.Right = createDictionary; constructor.Statements.Add(initDictionary); //Add current handler to the dictionary CodeAssignStatement storeHandler = new CodeAssignStatement(); CodeArrayIndexerExpression dictLocation = new CodeArrayIndexerExpression(); dictLocation.TargetObject = new CodeVariableReferenceExpression(handlers.Name); dictLocation.Indices.Add(new CodeVariableReferenceExpression(pi.Name)); storeHandler.Left = dictLocation; storeHandler.Right = new CodeVariableReferenceExpression(handlerVar.Name); method.Statements.Add(storeHandler); } else if (IsEventRemove(mi)) { PipelineHints.EventRemoveAttribute attr = GetEventRemove(mi); ParameterInfo pi = mi.GetParameters()[0]; //Declare the handler CodeTypeReference eventArgsType = GetEventArgsType(viewType, mi, true); CodeVariableDeclarationStatement handlerVar = new CodeVariableDeclarationStatement(); handlerVar.Name = "adaptedHandler"; handlerVar.Type = new CodeTypeReference(typeof(EventHandler<>)); handlerVar.Type.TypeArguments.Add(eventArgsType); method.Statements.Add(handlerVar); //TryGet handler from handlers CodeMethodInvokeExpression tryGet = new CodeMethodInvokeExpression(); tryGet.Method.TargetObject = new CodeVariableReferenceExpression(attr.Name + "_handlers"); tryGet.Method.MethodName = "TryGetValue"; tryGet.Parameters.Add(new CodeVariableReferenceExpression(pi.Name)); tryGet.Parameters.Add(new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression(handlerVar.Name))); CodeConditionStatement ifGotValue = new CodeConditionStatement(); ifGotValue.Condition = tryGet; //Remove handler CodeMethodInvokeExpression removeHandler = new CodeMethodInvokeExpression(); removeHandler.Method.MethodName = "Remove"; removeHandler.Method.TargetObject = new CodeVariableReferenceExpression(attr.Name + "_handlers"); removeHandler.Parameters.Add(new CodeVariableReferenceExpression(pi.Name)); ifGotValue.TrueStatements.Add(removeHandler); CodeRemoveEventStatement detach = new CodeRemoveEventStatement(); detach.Event = new CodeEventReferenceExpression(new CodeVariableReferenceExpression("_view"), attr.Name); detach.Listener = new CodeVariableReferenceExpression(handlerVar.Name); ifGotValue.TrueStatements.Add(detach); method.Statements.Add(ifGotValue); //Add parameters to method decl CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(pi.ParameterType, pi.Name); method.Parameters.Add(cp); } else { //This is a standard method to adapt, go through each parameter, check to see if it needs adapting. //If no adapting is needed just pass on through, else find the right adapter and use it foreach (ParameterInfo pi in mi.GetParameters()) { CodeTypeReference paramViewType = GetViewTypeReference(viewType, pi.ParameterType, contractType,SegmentDirection.ViewToContract); Type paramContractType = GetCannonicalContractType(pi.ParameterType); if (!TypeNeedsAdapting(paramContractType)) { CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(paramContractType, pi.Name); CodeExpression param; if (IsByRef(pi)) { cp.Direction = FieldDirection.Ref; param = new CodeDirectionExpression(FieldDirection.Ref, new CodeVariableReferenceExpression(pi.Name)); } else if (IsOut(pi)) { cp.Direction = FieldDirection.Out; param = new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression(pi.Name)); } else { param = new CodeVariableReferenceExpression(pi.Name); } method.Parameters.Add(cp); cmi.Parameters.Add(param); } else { CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(paramContractType, pi.Name); CodeMethodInvokeExpression adapterExpr = CallStaticAdapter(componentType, paramContractType, new CodeVariableReferenceExpression(pi.Name), SegmentDirection.ContractToView); if (IsByRef(pi)) { cp.Direction = FieldDirection.Ref; } if (IsOut(pi)) { cp.Direction = FieldDirection.Out; } method.Parameters.Add(cp); if (!IsByRef(pi) && !IsOut(pi)) { cmi.Parameters.Add(adapterExpr); } else { CodeVariableDeclarationStatement var = new CodeVariableDeclarationStatement(paramViewType, pi.Name + "_view"); prologue.Add(var); CodeDirectionExpression varRef; if (IsByRef(pi)) { var.InitExpression = adapterExpr; varRef = new CodeDirectionExpression(FieldDirection.Ref,new CodeVariableReferenceExpression(var.Name)); } else { var.InitExpression = new CodeDefaultValueExpression(var.Type); varRef = new CodeDirectionExpression(FieldDirection.Out,new CodeVariableReferenceExpression(var.Name)); } CodeAssignStatement refVar = new CodeAssignStatement( new CodeVariableReferenceExpression(pi.Name), CallStaticAdapter(componentType,paramContractType,new CodeVariableReferenceExpression(var.Name),SegmentDirection.ViewToContract)); cmi.Parameters.Add(varRef); epilogue.Add(refVar); } } } if (ret != null) { //If the previously computed return statement is not null add it to the method //It already has the call to cmi's invocation so no need to add cmi again method.Statements.AddRange(prologue); if (epilogue.Count > 0) { CodeVariableDeclarationStatement retVar = new CodeVariableDeclarationStatement(mi.ReturnType, "return_variable"); retVar.InitExpression = ret.Expression; ret = new CodeMethodReturnStatement(new CodeVariableReferenceExpression(retVar.Name)); method.Statements.Add(retVar); method.Statements.AddRange(epilogue); method.Statements.Add(ret); } else { method.Statements.Add(ret); } } else { //If the previously computed return statement is null then add cmi directly to the method statements method.Statements.AddRange(prologue); method.Statements.Add(cmi); method.Statements.AddRange(epilogue); } } if (method != null) { type.Members.Add(method); } } } //Add the method to unwrap the original view CodeMemberMethod unadapt = new CodeMemberMethod(); unadapt.Name = "GetSourceView"; unadapt.ReturnType = new CodeTypeReference(viewName); unadapt.Attributes = MemberAttributes.Assembly | MemberAttributes.Final; unadapt.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("_view"))); type.Members.Add(unadapt); codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(typeName, ccu)); }
internal void BuildStructView(Type contractType, PipelineSegmentSource component, SegmentType viewType) { CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(viewType, contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(_symbols.GetNameFromType(contractType, viewType, SegmentDirection.None, false)); type.Attributes = MemberAttributes.Public; type.IsStruct = true; foreach (PropertyInfo pi in contractType.GetProperties()) { CodeMemberProperty prop = new CodeMemberProperty(); prop.Attributes = MemberAttributes.Public | MemberAttributes.Final; prop.Name = pi.Name; prop.HasGet = true; prop.HasSet = false; prop.Type = GetViewTypeReference(viewType, pi.PropertyType, contractType,SegmentDirection.None); prop.GetStatements.Add( new CodeMethodReturnStatement( new CodeVariableReferenceExpression(ConvertNameToField(pi.Name)))); if (pi.GetSetMethod() != null) { prop.SetStatements.Add( new CodeAssignStatement( new CodeVariableReferenceExpression(ConvertNameToField(pi.Name)), new CodePropertySetValueReferenceExpression())); } type.Members.Add(prop); } CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; foreach (ParameterInfo pi in contractType.GetConstructors()[0].GetParameters()) { CodeParameterDeclarationExpression param = new CodeParameterDeclarationExpression(); param.Name = pi.Name; param.Type = GetViewTypeReference(viewType, pi.ParameterType, contractType,SegmentDirection.None); constructor.Parameters.Add(param); CodeMemberField field = new CodeMemberField(); field.Name = ConvertNameToField(pi.Name); field.Type = param.Type; type.Members.Add(field); constructor.Statements.Add( new CodeAssignStatement( new CodeVariableReferenceExpression(field.Name), new CodeVariableReferenceExpression(pi.Name))); } type.Members.Add(constructor); codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(type.Name, ccu)); }
/// <summary> /// This class builds the static adapters that are called directly from the adapters for other types for adapting of return values and parameters. /// They are responsible for calling the constructors of the appropriate types to create the adapters and for knowing when to adapt a type and when to unwrap /// </summary> /// <param name="contractType">Type of the contract to be adapted</param> /// <param name="component">Component that the source should be placed in. </param> /// <param name="componentType">Type of component to be built: either asa or hsa</param> internal void BuildStaticAdapters(Type contractType, PipelineSegmentSource component, SegmentType componentType) { if (contractType.GetCustomAttributes(typeof(PipelineHints.EventHandlerAttribute), false).Length > 0) { return; } String typeName = _symbols.GetNameFromType(contractType, componentType, SegmentDirection.None, false); CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(componentType,contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(typeName); type.Attributes = MemberAttributes.Assembly | MemberAttributes.Static; SegmentType viewComponentType; if (componentType == SegmentType.ASA) { viewComponentType = SegmentType.AIB; } else if (componentType == SegmentType.HSA) { viewComponentType = SegmentType.HAV; } else { throw new InvalidOperationException("Wrong component type"); } CodeTypeReference viewType = new CodeTypeReference(_symbols.GetNameFromType(contractType,viewComponentType,SegmentDirection.None,true)); //Contract to view adapter CodeMemberMethod cva; CodeMemberMethod vca; cva = CreateContractToViewStaticAdapter(contractType, componentType, viewType); vca = CreateViewToContractStaticAdapter(contractType, componentType, viewType); type.Members.Add(cva); type.Members.Add(vca); codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(typeName, ccu)); }
/// <summary> /// Build the view for an enum. This essentially creates a mirror image of the enum and it's values using the proper names. /// </summary> /// <param name="contractType">Input enum type</param> /// <param name="component">The pipeline component this source should be part of</param> /// <param name="viewType">Either AIB or HAV</param> internal void BuildEnumView(Type contractType, PipelineSegmentSource component, SegmentType viewType) { CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(viewType,contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(_symbols.GetNameFromType(contractType,viewType,SegmentDirection.None,false)); type.Attributes = MemberAttributes.Public; type.IsEnum = true; foreach (FieldInfo fi in contractType.GetFields()) { if (!fi.Name.Equals("value__")) { CodeMemberField field = new CodeMemberField(fi.FieldType, fi.Name); field.InitExpression = new CodePrimitiveExpression(fi.GetRawConstantValue()); type.Members.Add(field); } } if (contractType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) { type.CustomAttributes.Add(new CodeAttributeDeclaration("System.Flags")); } codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(type.Name, ccu)); }
public List<PipelineSegmentSource> BuildPipeline() { //If we haven't loaded the contract assembly yet it means we want to avoid loading the contract assembly in this domain //and should do it remotely. Once we're in the new domain we'll have loaded the contract asm and we'll fall through this //and do the work. if (_contractAsm == null) { List<PipelineSegmentSource> source = BuildRemotePipeline(); return source; } _symbols = new SymbolTable(_contractAsm); _typeHierarchy = new Dictionary<Type, List<Type>>(); List<PipelineSegmentSource> components = new List<PipelineSegmentSource>(); _aib = new PipelineSegmentSource(SegmentType.AIB, _symbols); _asa = new PipelineSegmentSource(SegmentType.ASA, _symbols); _hsa = new PipelineSegmentSource(SegmentType.HSA, _symbols); _hav = new PipelineSegmentSource(SegmentType.HAV, _symbols); _view = new PipelineSegmentSource(SegmentType.VIEW, _symbols); components.Add(_asa); components.Add(_hsa); //If the contract assembly is marked as having the views shared we should add a generic "view" component. //If not then we build both add-in side and host-side view source files. if (ShouldShareViews()) { components.Add(_view); } else { components.Add(_aib); components.Add(_hav); } //Parse the type hierarchy in the contract so we know how to express it in the views. BuildUpCastableTypeHierarchy(); //Iterate through all of the contract types foreach (Type t in _contractAsm.GetExportedTypes()) { //Check to see if the type is a contract if (typeof(IContract).IsAssignableFrom(t)) { bool activatable = false; //Check to see if type is an activatable contract foreach (object obj in t.GetCustomAttributes(false)) { if (obj.GetType().Equals(typeof(System.AddIn.Pipeline.AddInContractAttribute))) { activatable = true; break; } } PipelineHints.PipelineSegment customSettings = PipelineHints.PipelineSegment.None; if (t.GetCustomAttributes(typeof(PipelineHints.CustomPipelineAttribute), false).Length > 0) { customSettings = ((PipelineHints.CustomPipelineAttribute)t.GetCustomAttributes(typeof(PipelineHints.CustomPipelineAttribute), false)[0]).Segment; } //Build host, add-in views, and shared views if ((customSettings & PipelineHints.PipelineSegment.AddInView) != PipelineHints.PipelineSegment.AddInView) { BuildView(t, _aib, SegmentType.AIB, activatable); } if ((customSettings & PipelineHints.PipelineSegment.HostView) != PipelineHints.PipelineSegment.HostView) { BuildView(t, _hav, SegmentType.HAV, activatable); } if ((customSettings & PipelineHints.PipelineSegment.Views) != PipelineHints.PipelineSegment.Views) { BuildView(t, _view, SegmentType.VIEW, activatable); } //Build add-in side adapters if ((customSettings & PipelineHints.PipelineSegment.AddInSideAdapter) != PipelineHints.PipelineSegment.AddInSideAdapter) { BuildViewToContractAdapter(t, _asa, SegmentType.ASA, activatable); BuildContractToViewAdapter(t, _asa, SegmentType.ASA, false); } BuildStaticAdapters(t, _asa, SegmentType.ASA); //Build host side adapters if ((customSettings & PipelineHints.PipelineSegment.HostSideAdapter) != PipelineHints.PipelineSegment.HostSideAdapter) { BuildViewToContractAdapter(t, _hsa, SegmentType.HSA, false); BuildContractToViewAdapter(t, _hsa, SegmentType.HSA, activatable); } BuildStaticAdapters(t, _hsa, SegmentType.HSA); } else if (t.IsEnum) { //If type is an enum build adapters and view for that BuildEnumView(t, _aib, SegmentType.AIB); BuildEnumView(t, _hav, SegmentType.HAV); BuildEnumView(t, _view, SegmentType.VIEW); BuildStaticAdapters(t, _hsa, SegmentType.HSA); BuildStaticAdapters(t, _asa, SegmentType.ASA); BuildStaticAdapters(t.MakeArrayType(), _hsa, SegmentType.HSA); BuildStaticAdapters(t.MakeArrayType(), _asa, SegmentType.ASA); } else if (t.IsValueType) { ValidateStructContract(t); PipelineHints.PipelineSegment customSettings = PipelineHints.PipelineSegment.None; if (t.GetCustomAttributes(typeof(PipelineHints.CustomPipelineAttribute), false).Length > 0) { customSettings = ((PipelineHints.CustomPipelineAttribute)t.GetCustomAttributes(typeof(PipelineHints.CustomPipelineAttribute), false)[0]).Segment; } //Build host, add-in views, and shared views if ((customSettings & PipelineHints.PipelineSegment.AddInView) != PipelineHints.PipelineSegment.AddInView) { BuildStructView(t, _aib, SegmentType.AIB); } if ((customSettings & PipelineHints.PipelineSegment.HostView) != PipelineHints.PipelineSegment.HostView) { BuildStructView(t, _hav, SegmentType.HAV); } if ((customSettings & PipelineHints.PipelineSegment.Views) != PipelineHints.PipelineSegment.Views) { BuildStructView(t, _view, SegmentType.VIEW); } Type arrayVersion = t.MakeArrayType(); //Build add-in side adapters if ((customSettings & PipelineHints.PipelineSegment.AddInSideAdapter) != PipelineHints.PipelineSegment.AddInSideAdapter) { BuildStaticAdapters(t, _asa, SegmentType.ASA); } BuildStaticAdapters(arrayVersion, _asa, SegmentType.ASA); //Build host side adapters if ((customSettings & PipelineHints.PipelineSegment.HostSideAdapter) != PipelineHints.PipelineSegment.HostSideAdapter) { BuildStaticAdapters(t, _hsa, SegmentType.HSA); } BuildStaticAdapters(arrayVersion, _hsa, SegmentType.HSA); } } return components; }
internal void BuildContractToViewAdapter(Type contractType, PipelineSegmentSource component, SegmentType componentType, bool activatable) { //Set up type String typeName = _symbols.GetNameFromType(contractType, componentType,SegmentDirection.ContractToView,false); SegmentType viewType; if (componentType == SegmentType.ASA) { viewType = SegmentType.AIB; } else { viewType = SegmentType.HAV; } String viewName = _symbols.GetNameFromType(contractType, viewType); CodeCompileUnit ccu = new CodeCompileUnit(); CodeNamespace codeNamespace = new CodeNamespace(_symbols.GetNameSpace(componentType,contractType)); CodeTypeDeclaration type = new CodeTypeDeclaration(typeName); type.TypeAttributes = TypeAttributes.Public; type.BaseTypes.Add(new CodeTypeReference(viewName)); if (activatable) { CodeAttributeDeclaration marker = new CodeAttributeDeclaration(new CodeTypeReference(typeof(System.AddIn.Pipeline.HostAdapterAttribute))); type.CustomAttributes.Add(marker); } CodeMemberField contract = new CodeMemberField(contractType, "_contract"); CodeMemberField handle = new CodeMemberField(typeof(ContractHandle), "_handle"); //AddDisposePattern(type, "_handle", "_contract"); type.Members.Add(contract); type.Members.Add(handle); //Build constructor CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; CodeParameterDeclarationExpression parameter = new CodeParameterDeclarationExpression(contractType, "contract"); constructor.Parameters.Add(parameter); CodeAssignStatement assign = new CodeAssignStatement(new CodeVariableReferenceExpression("_contract"), new CodeVariableReferenceExpression("contract")); constructor.Statements.Add(assign); CodeObjectCreateExpression createHandle = new CodeObjectCreateExpression(typeof(ContractHandle), new CodeVariableReferenceExpression("contract")); assign = new CodeAssignStatement(new CodeVariableReferenceExpression("_handle"), createHandle); constructor.Statements.Add(assign); type.Members.Add(constructor); SegmentType viewComponentType; switch (componentType) { case SegmentType.ASA: viewComponentType = SegmentType.AIB; break; case SegmentType.HSA: viewComponentType = SegmentType.HAV; break; default: throw new InvalidOperationException("Must be asa or hsa"); } if (IsEvent(contractType)) { CodeMemberMethod handler = new CodeMemberMethod(); handler.Name = "Handler"; handler.Attributes = MemberAttributes.Public | MemberAttributes.Final; CodeParameterDeclarationExpression sender = new CodeParameterDeclarationExpression(typeof(Object), "sender"); MethodInfo mi = contractType.GetMethods()[0]; ParameterInfo pi = mi.GetParameters()[0]; CodeTypeReference eventArgsType = new CodeTypeReference(_symbols.GetNameFromType(pi.ParameterType, viewType, SegmentDirection.None, true)); CodeParameterDeclarationExpression args = new CodeParameterDeclarationExpression(eventArgsType, "args"); handler.Parameters.Add(sender); handler.Parameters.Add(args); handler.ReturnType = new CodeTypeReference(typeof(void)); CodeMethodInvokeExpression cmi = new CodeMethodInvokeExpression(); if (TypeNeedsAdapting(pi.ParameterType)) { CodeMethodInvokeExpression argsAdapter = CallStaticAdapter(componentType, pi.ParameterType, new CodeVariableReferenceExpression("args"), SegmentDirection.ViewToContract); cmi.Parameters.Add(argsAdapter); } else { cmi.Parameters.Add(new CodeVariableReferenceExpression("args")); } cmi.Method = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("_contract"), mi.Name); if (mi.ReturnType.Equals(typeof(bool))) { CodeConditionStatement ifStatement = new CodeConditionStatement(); CodeAssignStatement assignCancel = new CodeAssignStatement(); assignCancel.Left = new CodeFieldReferenceExpression(new CodeVariableReferenceExpression("args"), "Cancel"); assignCancel.Right = new CodePrimitiveExpression(true); ifStatement.Condition = cmi; ifStatement.TrueStatements.Add(assignCancel); handler.Statements.Add(ifStatement); } else { handler.Statements.Add(cmi); } type.Members.Add(handler); type.BaseTypes.Clear(); } else { Dictionary<String, CodeMemberProperty> props = new Dictionary<string, CodeMemberProperty>(); List<MethodInfo> setEvents = new List<MethodInfo>(); foreach (MethodInfo mi in GetMethodsFromContract(contractType,true)) { CodeMethodInvokeExpression cmi = new CodeMethodInvokeExpression(); if (IsEventAdd(mi)) { PipelineHints.EventAddAttribute attr = GetEventAdd(mi); //Add event to list for Static Constructor Initialization setEvents.Add(mi); //Hook up event during constructor ParameterInfo pi = mi.GetParameters()[0]; CodeTypeReference adapterType = new CodeTypeReference(_symbols.GetNameFromType(pi.ParameterType, componentType, SegmentDirection.ViewToContract, contractType)); CodeObjectCreateExpression adapter = new CodeObjectCreateExpression(adapterType, new CodeThisReferenceExpression(),new CodeVariableReferenceExpression("s_" + mi.Name + "Fire")); CodeMemberField handlerField = new CodeMemberField(); handlerField.Name = attr.Name + "_Handler"; handlerField.Type = adapterType; type.Members.Add(handlerField); CodeAssignStatement assignHandlerField = new CodeAssignStatement(); assignHandlerField.Left = new CodeVariableReferenceExpression(handlerField.Name); assignHandlerField.Right = adapter; constructor.Statements.Add(assignHandlerField); //Add field CodeMemberEvent eventField = new CodeMemberEvent(); eventField.Name = "_" + attr.Name; eventField.Type = GetEventViewType(viewComponentType, mi, true); type.Members.Add(eventField); //Add FireMethod CodeMemberMethod eventFire = new CodeMemberMethod(); eventFire.Attributes = MemberAttributes.Assembly; eventFire.Name = "Fire" + eventField.Name; eventFire.Parameters.Add( new CodeParameterDeclarationExpression(eventField.Type.TypeArguments[0], "args")); CodeMethodInvokeExpression eventFireInvoke = new CodeMethodInvokeExpression(); eventFireInvoke.Method = new CodeMethodReferenceExpression(); eventFireInvoke.Method.MethodName = "Invoke"; eventFireInvoke.Method.TargetObject = new CodeVariableReferenceExpression(eventField.Name); eventFireInvoke.Parameters.Add(new CodeThisReferenceExpression()); eventFireInvoke.Parameters.Add(new CodeVariableReferenceExpression("args")); CodeConditionStatement nullConditionalFire = new CodeConditionStatement(); CodeBinaryOperatorExpression eventNullCheck = new CodeBinaryOperatorExpression(); eventNullCheck.Left = new CodeVariableReferenceExpression(eventField.Name); eventNullCheck.Right = new CodePrimitiveExpression(null); eventNullCheck.Operator = CodeBinaryOperatorType.IdentityEquality; nullConditionalFire.Condition = eventNullCheck; nullConditionalFire.FalseStatements.Add(eventFireInvoke); eventFire.Statements.Add(nullConditionalFire); type.Members.Add(eventFire); CodeSnippetTypeMember snippet = GetEventContractToViewFromSnippet(contractType,mi,eventField); //Add override property; type.Members.Add(snippet); continue; } else if (IsEventRemove(mi)) { continue; } CodeMemberMethod method = new CodeMemberMethod(); CodeMethodReturnStatement ret = new CodeMethodReturnStatement(); cmi.Method = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("_contract"), mi.Name); method.Attributes = MemberAttributes.Public; if (!IsViewInterface(contractType)) { method.Attributes |= MemberAttributes.Override; } else { method.Attributes |= MemberAttributes.Final; } method.Name = mi.Name; if (mi.ReturnType.Equals(typeof(void))) { method.ReturnType = new CodeTypeReference(mi.ReturnType); ret = null; } else if (!TypeNeedsAdapting(mi.ReturnType)) { method.ReturnType = new CodeTypeReference(mi.ReturnType); ret.Expression = cmi; } else { method.ReturnType = GetViewTypeReference(viewType, mi.ReturnType, mi.DeclaringType,SegmentDirection.ContractToView); ret.Expression = CallStaticAdapter(componentType, mi.ReturnType, cmi, SegmentDirection.ContractToView); } CodeStatementCollection prologue = new CodeStatementCollection(); CodeStatementCollection epilogue = new CodeStatementCollection(); foreach (ParameterInfo pi in mi.GetParameters()) { CodeTypeReference paramType = GetViewTypeReference(viewType, pi.ParameterType, contractType,SegmentDirection.ContractToView); if (!TypeNeedsAdapting(pi.ParameterType)) { CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(paramType, pi.Name); CodeExpression param; if (IsByRef(pi)) { cp.Direction = FieldDirection.Ref; param = new CodeDirectionExpression(FieldDirection.Ref, new CodeVariableReferenceExpression(pi.Name)); } else if (IsOut(pi)) { cp.Direction = FieldDirection.Out; param = new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression(pi.Name)); } else { param = new CodeVariableReferenceExpression(pi.Name); } method.Parameters.Add(cp); cmi.Parameters.Add(param); } else { Type paramContractType = GetCannonicalContractType(pi.ParameterType); CodeMethodInvokeExpression adaptExpr = CallStaticAdapter(componentType, paramContractType, new CodeVariableReferenceExpression(pi.Name), SegmentDirection.ViewToContract); CodeParameterDeclarationExpression cp = new CodeParameterDeclarationExpression(paramType, pi.Name); if (IsByRef(pi)) { cp.Direction = FieldDirection.Ref; } if (IsOut(pi)) { cp.Direction = FieldDirection.Out; } method.Parameters.Add(cp); if (!IsByRef(pi) && !IsOut(pi)) { cmi.Parameters.Add(adaptExpr); } else { CodeVariableDeclarationStatement var = new CodeVariableDeclarationStatement(paramContractType, pi.Name + "_contract"); prologue.Add(var); CodeDirectionExpression varRef; if (IsByRef(pi)) { var.InitExpression = adaptExpr; varRef = new CodeDirectionExpression(FieldDirection.Ref, new CodeVariableReferenceExpression(var.Name)); } else { var.InitExpression = new CodeDefaultValueExpression(var.Type); varRef = new CodeDirectionExpression(FieldDirection.Out, new CodeVariableReferenceExpression(var.Name)); } CodeAssignStatement refVar = new CodeAssignStatement( new CodeVariableReferenceExpression(pi.Name), CallStaticAdapter(componentType, paramContractType, new CodeVariableReferenceExpression(var.Name), SegmentDirection.ContractToView)); cmi.Parameters.Add(varRef); epilogue.Add(refVar); } } } if (IsProperty(mi)) { CodeMemberProperty myProp = GetProperyDecl(contractType, type, mi, props, viewComponentType, SegmentDirection.ContractToView, true); CodeStatement statement = GetPropertyContractToViewAdapterImpl(componentType, viewType, mi, null); switch (GetPropertyAttribute(mi).Type) { case PropertyType.set: myProp.HasSet = true; myProp.SetStatements.Add(statement); break; case PropertyType.get: myProp.HasGet = true; myProp.GetStatements.Add(statement); break; } } else { if (ret != null) { method.Statements.AddRange(prologue); if (epilogue.Count > 0) { CodeVariableDeclarationStatement retVar = new CodeVariableDeclarationStatement(GetViewTypeReference(viewType, mi.ReturnType, mi.DeclaringType,SegmentDirection.ViewToContract), "return_variable"); retVar.InitExpression = ret.Expression; ret = new CodeMethodReturnStatement(new CodeVariableReferenceExpression(retVar.Name)); method.Statements.Add(retVar); method.Statements.AddRange(epilogue); method.Statements.Add(ret); } else { method.Statements.Add(ret); } } else { method.Statements.AddRange(prologue); method.Statements.Add(cmi); method.Statements.AddRange(epilogue); } type.Members.Add(method); } } ProcessSetEvents(viewName, type, setEvents); } CodeMemberMethod unadapt = new CodeMemberMethod(); unadapt.Name = "GetSourceContract"; unadapt.ReturnType = new CodeTypeReference(contractType); unadapt.Attributes = MemberAttributes.Assembly | MemberAttributes.Final; unadapt.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("_contract"))); type.Members.Add(unadapt); codeNamespace.Types.Add(type); ccu.Namespaces.Add(codeNamespace); component.Files.Add(new SourceFile(typeName,ccu)); }