示例#1
0
 public StubBuilder(CSharpSourceWriter writer)
 {
     this.writer = writer;
     writer.WriteUsings(
         "System",                              // Action, Func, Type
         "System.Collections.Generic",          // Dictionary
         "HavenSoft.AutoImplement.Delegation"); // PropertyImplementation, EventImplementation
 }
        public ClassStubBuilder(CSharpSourceWriter writer)
        {
            // the main writer will write the Stub class, which contains the new members.
            stubWriter = writer;
            writer.WriteUsings(
                "System",                        // Action, Func, Type, IDisposable
                "System.Collections.Generic",    // Dictionary
                "System.Delegation",             // PropertyImplementation, EventImplementation
                "System.Linq",                   // ConstructionCompletion uses Linq to get the correct constructor.
                "System.Runtime.Serialization"); // FormatterServices

            // the intermediateWriter will write the intermediate class, which contains the override members.
            intermediateWriter = new CSharpSourceWriter(writer.Indentation);
        }
        private void AppendConstructor(Type type, ConstructorInfo info, MemberMetadata constructorMetadata)
        {
            if (info.IsPrivate || info.IsStatic || info.IsFamilyAndAssembly || info.IsAssembly)
            {
                return;
            }
            var typeName = type.CreateCsName(type.Namespace);

            var(basename, genericInfo) = typeName.ExtractImplementationNameParts("<");
            var intermediateName = $"IntermediateStub{basename}_DoNotUse";
            var stubName         = $"Stub{basename}";

            // for every constructor, make both a constructor and a static "DeferConstruction" method
            var deferWriter = new CSharpSourceWriter(stubWriter.Indentation);

            stubWriter.Write($"public {stubName}({constructorMetadata.ParameterTypesAndNames}) : base({constructorMetadata.ParameterNames})");
            var outParam  = $"out {stubTypeName} uninitializedStub";
            var separator = constructorMetadata.ParameterTypesAndNames.Length > 0 ? ", " : string.Empty;

            deferWriter.Write($"public static IDisposable DeferConstruction({constructorMetadata.ParameterTypesAndNames}{separator}{outParam})");
            using (stubWriter.Scope) {
                using (deferWriter.Scope) {
                    deferWriter.Write($"{stubTypeName} stub;");
                    deferWriter.Write($"var disposable = ConstructionCompletion.CreateObjectWithDeferredConstruction<{stubTypeName}>(out stub{separator}{constructorMetadata.ParameterNames});");
                    foreach (var member in Program.FindAllMembers(type))
                    {
                        var metadata = new MemberMetadata(member, type.Namespace);
                        switch (member.MemberType)
                        {
                        case MemberTypes.Method: AppendToConstructorFromMethod((MethodInfo)member, metadata, deferWriter); break;

                        case MemberTypes.Event: AppendToConstructorFromEvent((EventInfo)member, metadata, deferWriter); break;

                        case MemberTypes.Property: AppendToConstructorFromProperty((PropertyInfo)member, metadata, deferWriter); break;

                        default:
                            // the only other options are Field, Type, NestedType, and Constructor
                            // none of those can be virtual/abstract, so we don't need to put anything in the constructor for them.
                            break;
                        }
                    }
                    deferWriter.Write($"uninitializedStub = stub;");
                    deferWriter.Write($"return disposable;");
                }
            }

            stubWriter.Write(deferWriter.ToString());
            intermediateWriter.Write($"protected {intermediateName}({constructorMetadata.ParameterTypesAndNames}) : base({constructorMetadata.ParameterNames}) {{ }}");
        }
示例#4
0
        /// <summary>
        /// Creates a Builder of the given generic type to implement the given interface.
        /// Output is placed in the given fileName.
        /// </summary>
        private static void GenerateImplementation <TPatternBuilder>(Type type)
            where TPatternBuilder : IPatternBuilder
        {
            var writer   = new CSharpSourceWriter(numberOfSpacesToIndent: 4);
            var builder  = (TPatternBuilder)Activator.CreateInstance(typeof(TPatternBuilder), writer);
            var fileName = builder.GetDesiredOutputFileName(type);

            Console.WriteLine($"Generating {fileName} ...");

            writer.Write($"// this file was created by AutoImplement");
            writer.Write($"namespace {type.Namespace}");
            using (writer.Scope) {
                writer.Write($"public class {builder.ClassDeclaration(type)}");
                using (writer.Scope) {
                    builder.AppendExtraMembers(type);
                    var allMembers = FindAllMembers(type);
                    foreach (var member in allMembers)
                    {
                        var metadata = new MemberMetadata(member, type.Namespace);
                        switch (member.MemberType)
                        {
                        case MemberTypes.Method: ImplementMethod(member, metadata, builder); break;

                        case MemberTypes.Event: ImplementEvent(member, metadata, builder); break;

                        case MemberTypes.Property: ImplementProperty(member, metadata, builder); break;

                        default:
                            // the only other options are Field, Type, NestedType, and Constructor
                            // for classes, any of these are possible, and all can be ignored.
                            break;
                        }
                    }
                }
                builder.BuildCompleted();
            }

            File.WriteAllText(fileName, writer.ToString());
        }
        private void AppendToConstructorFromProperty(PropertyInfo info, MemberMetadata metadata, CSharpSourceWriter deferWriter)
        {
            var method = info.GetMethod ?? info.SetMethod;

            if (method.IsAbstract && !method.IsAssembly && !method.IsFamilyAndAssembly)
            {
                if (info.Name == "Item" && info.GetIndexParameters().Length > 0)
                {
                    // item property maps to two methods, get_Item and set_Item
                    // but since the property is abstract, we have no base implementation
                    // so just like methods, give no default values in the constructor (or defer construction call)
                }
                else
                {
                    stubWriter.Write($"if ({metadata.Name} == null) {metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                    deferWriter.Write($"stub.{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                }
            }
            if (method.IsStatic || method.IsPrivate || !method.IsVirtual || method.IsAbstract || method.IsAssembly || method.IsFamilyAndAssembly)
            {
                return;
            }

            if (info.Name == "Item" && info.GetIndexParameters().Length > 0)
            {
                if (info.CanRead)
                {
                    stubWriter.Write($"if (get_Item == null) get_Item = Base_get_Item;");
                }
                if (CanWrite(info))
                {
                    stubWriter.Write($"if (set_Item == null) set_Item = Base_set_Item;");
                }
                if (info.CanRead)
                {
                    deferWriter.Write($"stub.get_Item = stub.Base_get_Item;");
                }
                if (CanWrite(info))
                {
                    deferWriter.Write($"stub.set_Item = stub.Base_set_Item;");
                }
            }
            else
            {
                stubWriter.Write($"if ({metadata.Name} == null)");
                using (stubWriter.Scope) {
                    stubWriter.Write($"{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                    if (info.CanRead)
                    {
                        stubWriter.Write($"{metadata.Name}.get = () => Base{metadata.Name};");
                    }
                    if (CanWrite(info))
                    {
                        stubWriter.Write($"{metadata.Name}.set = value => Base{metadata.Name} = value;");
                    }
                }
                deferWriter.Write($"stub.{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                if (info.CanRead)
                {
                    deferWriter.Write($"stub.{metadata.Name}.get = () => stub.Base{metadata.Name};");
                }
                if (CanWrite(info))
                {
                    deferWriter.Write($"stub.{metadata.Name}.set = value => stub.Base{metadata.Name} = value;");
                }
            }
        }
        private void AppendToConstructorFromEvent(EventInfo info, MemberMetadata metadata, CSharpSourceWriter deferWriter)
        {
            var addMethod = info.AddMethod;

            if (addMethod.IsAbstract && !addMethod.IsAssembly && !addMethod.IsFamilyAndAssembly)
            {
                stubWriter.Write($"if ({metadata.Name} == null) {metadata.Name} = new EventImplementation<{metadata.HandlerArgsType}>();");
                deferWriter.Write($"stub.{metadata.Name} = new EventImplementation<{metadata.HandlerArgsType}>();");
            }
            if (addMethod.IsStatic || addMethod.IsPrivate || !addMethod.IsVirtual || addMethod.IsAbstract || addMethod.IsAssembly || addMethod.IsFamilyAndAssembly)
            {
                return;
            }

            stubWriter.Write($"if ({metadata.Name} == null)");
            using (stubWriter.Scope) {
                stubWriter.Write($"{metadata.Name} = new EventImplementation<{metadata.HandlerArgsType}>();");
                stubWriter.Write($"{metadata.Name}.add = value => Base{metadata.Name}Add(new {metadata.HandlerType}(value));");
                stubWriter.Write($"{metadata.Name}.remove = value => Base{metadata.Name}Remove(new {metadata.HandlerType}(value));");
            }

            deferWriter.Write($"stub.{metadata.Name} = new EventImplementation<{metadata.HandlerArgsType}>();");
            deferWriter.Write($"stub.{metadata.Name}.add = value => stub.Base{metadata.Name}Add(new {metadata.HandlerType}(value));");
            deferWriter.Write($"stub.{metadata.Name}.remove = value => stub.Base{metadata.Name}Remove(new {metadata.HandlerType}(value));");
        }
        private void AppendToConstructorFromMethod(MethodInfo info, MemberMetadata metadata, CSharpSourceWriter deferWriter)
        {
            if (info.IsSpecialName || info.IsStatic || info.IsPrivate || !info.IsVirtual || info.IsAbstract || info.IsAssembly || info.IsFamilyAndAssembly || info.IsGenericMethod)
            {
                return;
            }
            if (info.IsVirtual && info.Name == "Finalize")
            {
                return;                                         // Finalize is special in C#. Use a destructor instead.
            }
            var    typesExtension = StubBuilder.SanitizeMethodName(metadata.ParameterTypes);
            var    methodsWithMatchingNameButNotSignature = implementedMethods.Where(name => name.Split('(')[0] == metadata.Name && name != $"{metadata.Name}({metadata.ParameterTypes})");
            string localImplementationName = methodsWithMatchingNameButNotSignature.Any() ? $"{metadata.Name}_{typesExtension}" : metadata.Name;

            stubWriter.Write($"if ({localImplementationName} == null) {localImplementationName} = Base{metadata.Name};");
            deferWriter.Write($"stub.{localImplementationName} = stub.Base{metadata.Name};");

            implementedMethods.Add($"{metadata.Name}({metadata.ParameterTypes})");
        }
 public DecoratorBuilder(CSharpSourceWriter writer) => this.writer = writer;
 public CompositeBuilder(CSharpSourceWriter writer)
 {
     this.writer = writer;
     writer.WriteUsings("System.Linq");
 }