Пример #1
0
        public string GenerateHeaderFile(MessageParser.PostalDefinition parsed)
        {
            var constants = from def in parsed.PostalTypes
                            where def is MessageParser.ConstantDefinition
                            let constDef = def as MessageParser.ConstantDefinition
                                           select string.Format("#define {0} {1}", constDef.Name, constDef.Value);

            return(string.Format(HeaderFileTemplate, parsed.Namespace, string.Join("\n", constants)));
        }
Пример #2
0
        public CodeCompileUnit GenerateCodeDOM(string filename, MessageParser.PostalDefinition parsed)
        {
            var className = Path.GetFileNameWithoutExtension(filename);

            var codeUnit = new CodeCompileUnit();
            var ns       = new CodeNamespace(parsed.Namespace);

            codeUnit.Namespaces.Add(ns);

            ns.Imports.Add(new CodeNamespaceImport("global::System"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.Collections.Generic"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.Collections.ObjectModel"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.ComponentModel"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.IO"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.Linq"));
            ns.Imports.Add(new CodeNamespaceImport("global::System.Threading.Tasks"));
            ns.Imports.Add(new CodeNamespaceImport("global::ProtoBuf"));
            ns.Imports.Add(new CodeNamespaceImport("global::Postal.Core"));

            var messagesType = new CodeTypeDeclaration(className)
            {
                // Hack to make this class static so we can define extension methods
                // http://stackoverflow.com/a/6308395/802203
                Attributes      = MemberAttributes.Final,
                TypeAttributes  = TypeAttributes.Public,
                StartDirectives = { new CodeRegionDirective(CodeRegionMode.Start, "\nstatic") },
                EndDirectives   = { new CodeRegionDirective(CodeRegionMode.End, string.Empty) },
                IsPartial       = true
            };

            ns.Types.Add(messagesType);

            CodeTypeDeclaration messagesContainerType = null;

            messagesContainerType = new CodeTypeDeclaration(string.Format("{0}_MessagesContainer", messagesType.Name))
            {
                TypeAttributes   = TypeAttributes.Public,
                CustomAttributes = { new CodeAttributeDeclaration("ProtoContract") }
            };
            messagesType.Members.Add(messagesContainerType);

            messagesType.Members.Add(new CodeSnippetTypeMember("public delegate TResponse ProcessRequestDelegate<TRequest, TResponse>(TRequest request) where TRequest : IRequest where TResponse : IResponse;"));

            messagesType.Members.Add(new CodeSnippetTypeMember(string.Format("private readonly static Dictionary<int, Type> _messageRequestTypes = new Dictionary<int, Type>(){{ {0} }};",
                                                                             string.Join(",\n", from message in parsed.PostalTypes
                                                                                         where message is MessageParser.MessageDefinition
                                                                                         select string.Format("{{ {0}.{0}RequestTag, typeof({0}.Request) }}", (message as MessageParser.MessageDefinition).Name)))));
            messagesType.Members.Add(new CodeSnippetTypeMember("public readonly static ReadOnlyDictionary<int, Type> MessageRequestTypes = new ReadOnlyDictionary<int, Type>(_messageRequestTypes);"));

            messagesType.Members.Add(new CodeSnippetTypeMember(string.Format("private readonly static Dictionary<int, Type> _messageResponseTypes = new Dictionary<int, Type>(){{ {0} }};",
                                                                             string.Join(",\n", from message in parsed.PostalTypes
                                                                                         where message is MessageParser.MessageDefinition
                                                                                         select string.Format("{{ {0}.{0}ResponseTag, typeof({0}.Response) }}", (message as MessageParser.MessageDefinition).Name)))));
            messagesType.Members.Add(new CodeSnippetTypeMember("public readonly static ReadOnlyDictionary<int, Type> MessageResponseTypes = new ReadOnlyDictionary<int, Type>(_messageResponseTypes);"));

            messagesType.Members.Add(new CodeSnippetTypeMember(@"
private static void Serialize<T>(Stream stream, T request) where T: IRequest
{
    Serializer.SerializeWithLengthPrefix(stream, request, PrefixStyle.Base128, request.Tag);
}"));
            messagesType.Members.Add(new CodeSnippetTypeMember(@"
private static T Deserialize<T>(Stream stream) where T : IResponse
{
    object value;
    Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128,
        tag =>
        {
            Type type;
            return _messageResponseTypes.TryGetValue(tag, out type) ? type : null;
        }, out value);
    return (T)value;
}"));
            messagesType.Members.Add(new CodeSnippetTypeMember(@"
public static void ProcessRequest(this Stream stream, Func<Stream, IRequest, bool> postDeserialize = null)
{
    object value;
    Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128,
        tag =>
        {
            Type type;
            return _messageRequestTypes.TryGetValue(tag, out type) ? type : null;
        }, out value);
    var request = (IRequest)value;
    if (request == null)
        return;
    if (postDeserialize != null)
        if (!postDeserialize(stream, request))
            return;
    var response = (IResponse)request.InvokeReceived();
    Serializer.NonGeneric.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128, response.Tag);
}"));

            int dummyFieldsTag = 1;

            foreach (var type in parsed.PostalTypes)
            {
                var constDef = type as MessageParser.ConstantDefinition;
                if (constDef != null)
                {
                    var qualifiedType = constDef.Type.ReplaceAll(builtInTypeReplacements);
                    var constant      = new CodeMemberField(qualifiedType, constDef.Name)
                    {
                        InitExpression = new CodeSnippetExpression(string.Format("({0}){1}", qualifiedType, constDef.Value)),
                        Attributes     = MemberAttributes.Const | MemberAttributes.Public
                    };
                    messagesType.Members.Add(constant);

                    continue;
                }

                var enumDef = type as MessageParser.EnumDefinition;
                if (enumDef != null)
                {
                    var enumType = new CodeTypeDeclaration(enumDef.Name)
                    {
                        IsEnum = true
                    };
                    foreach (var enumValue in enumDef.Values)
                    {
                        var field = new CodeMemberField(enumDef.Name, enumValue.Item1);
                        if (enumValue.Item2.HasValue)
                        {
                            field.InitExpression = new CodePrimitiveExpression(enumValue.Item2.Value);
                        }
                        enumType.Members.Add(field);
                    }

                    messagesType.Members.Add(enumType);

                    continue;
                }

                var structDef = type as MessageParser.StructDefinition;
                if (structDef != null)
                {
                    var structType = new CodeTypeDeclaration(structDef.Name)
                    {
                        IsStruct         = true,
                        IsPartial        = true,
                        CustomAttributes = { new CodeAttributeDeclaration("ProtoContract") }
                    };

                    int fieldTag = 1;
                    foreach (var structField in structDef.Fields)
                    {
                        var field = new CodeMemberField(structField.Type.ReplaceAll(builtInTypeReplacements), structField.Name)
                        {
                            CustomAttributes = { new CodeAttributeDeclaration("ProtoMember", new CodeAttributeArgument(new CodeSnippetExpression(fieldTag.ToString()))) },
                            Attributes       = MemberAttributes.Public
                        };
                        if (structField.DefaultValue != null)
                        {
                            field.CustomAttributes.Add(new CodeAttributeDeclaration("DefaultValue", new CodeAttributeArgument(new CodeSnippetExpression(structField.DefaultValue))));
                        }
                        structType.Members.Add(field);
                        fieldTag++;
                    }

                    messagesType.Members.Add(structType);
                    messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1} struct{1} {{ get; set; }}", dummyFieldsTag++, structDef.Name)));
                    continue;
                }

                var messageDef = type as MessageParser.MessageDefinition;
                if (messageDef != null)
                {
                    var messageType = new CodeTypeDeclaration(messageDef.Name)
                    {
                        Attributes     = MemberAttributes.Final,
                        TypeAttributes = TypeAttributes.Public
                    };
                    messagesType.Members.Add(messageType);

                    messageType.Members.Add(new CodeSnippetTypeMember(string.Format("public const int {0}RequestTag = {1};", messageType.Name, GetStableHash(string.Format("{0}.{1}.{2}.Request", ns.Name, messagesType.Name, messageType.Name)))));
                    messageType.Members.Add(new CodeSnippetTypeMember(string.Format("public const int {0}ResponseTag = {1};", messageType.Name, GetStableHash(string.Format("{0}.{1}.{2}.Response", ns.Name, messagesType.Name, messageType.Name)))));
                    messageType.Members.Add(new CodeSnippetTypeMember(@"public static event ProcessRequestDelegate<Request, Response> MessageReceived;"));

                    messageType.Members.Add(new CodeSnippetTypeMember(string.Format(
                                                                          messageDef.Response == null ?
                                                                          @"public static Task SendAsync({1})
{{
    return Task.Factory.StartNew(() =>
    {{
        Serialize(stream, new {0}.Request
        {{
            {2}
        }});
    }});
}}" :
                                                                          @"public static Task<{0}.Response> SendAsync({1})
{{
    return Task.Factory.StartNew(() =>
    {{
        Serialize(stream, new {0}.Request
        {{
            {2}
        }});
        return Deserialize<{0}.Response>(stream);
    }});
}}", messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields
                                                                          let paramName = getFormalParamName(field)
                                                                                          select string.Format("{0} {1}", field.Type, paramName))),
                                                                          string.Join(", \n", from field in messageDef.Request.Fields
                                                                                      let paramName = getFormalParamName(field)
                                                                                                      select string.Format("{0} = {1}", field.Name, paramName)))));
                    messageType.Members.Add(new CodeSnippetTypeMember(string.Format(
                                                                          messageDef.Response == null ?
                                                                          @"public static void Send({1})
{{
    Serialize(stream, new {0}.Request
    {{
        {2}
    }});
}}" :
                                                                          @"public static {0}.Response Send({1})
{{
    Serialize(stream, new {0}.Request
    {{
        {2}
    }});
    return Deserialize<{0}.Response>(stream);
}}", messageType.Name, string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields
                                                                          let paramName = getFormalParamName(field)
                                                                                          select string.Format("{0} {1}", field.Type, paramName))),
                                                                          string.Join(", \n", from field in messageDef.Request.Fields
                                                                                      let paramName = getFormalParamName(field)
                                                                                                      select string.Format("{0} = {1}", field.Name, paramName)))));

                    // Add extensions methods for true RPC
                    messagesType.Members.Add(new CodeSnippetTypeMember(string.Format(
                                                                           messageDef.Response == null ?
                                                                           @"public static void {1}(this {2})
{{
    {3}.Send({4});
}}" :
                                                                           @"public static {0}.Response {1}(this {2})
{{
    return {3}.Send({4});
}}",
                                                                           messageType.Name,
                                                                           messagesType.Name + messageType.Name,
                                                                           string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields
                                                                                                                              let paramName = getFormalParamName(field)
                                                                                                                                              select string.Format("{0} {1}", field.Type, paramName))),
                                                                           messageType.Name,
                                                                           string.Join(", ", new[] { "stream" }.Concat(from field in messageDef.Request.Fields
                                                                                                                       let paramName = getFormalParamName(field)
                                                                                                                                       select paramName))
                                                                           )));

                    messagesType.Members.Add(new CodeSnippetTypeMember(string.Format(
                                                                           messageDef.Response == null ?
                                                                           @"public static Task {1}Async(this {2})
{{
    {3}.SendAsync({4});
}}" :
                                                                           @"public static Task<{0}.Response> {1}Async(this {2})
{{
    return {3}.SendAsync({4});
}}",
                                                                           messageType.Name,
                                                                           messagesType.Name + messageType.Name,
                                                                           string.Join(", ", new[] { "Stream stream" }.Concat(from field in messageDef.Request.Fields
                                                                                                                              let paramName = getFormalParamName(field)
                                                                                                                                              select string.Format("{0} {1}", field.Type, paramName))),
                                                                           messageType.Name,
                                                                           string.Join(", ", new[] { "stream" }.Concat(from field in messageDef.Request.Fields
                                                                                                                       let paramName = getFormalParamName(field)
                                                                                                                                       select paramName))
                                                                           )));

                    CodeTypeDeclaration messageRequestType = null;
                    if (messageDef.Request != null)
                    {
                        messageRequestType = new CodeTypeDeclaration("Request")
                        {
                            Attributes       = MemberAttributes.Final,
                            TypeAttributes   = TypeAttributes.Public,
                            BaseTypes        = { new CodeTypeReference("IRequest") },
                            CustomAttributes = { new CodeAttributeDeclaration("ProtoContract",
                                                                              new[]
                                {
                                    new CodeAttributeArgument
                                    {
                                        Name  = "Name",
                                        Value = new CodeSnippetExpression(string.Format("\"{0}{1}\"",messageType.Name, "Request"))
                                    }
                                }) }
                        };
                        messageRequestType.Members.Add(new CodeSnippetTypeMember(string.Format("int IRequest.Tag {{ get {{ return {0}RequestTag; }} }}", messageType.Name)));
                        messageRequestType.Members.Add(new CodeSnippetTypeMember(string.Format("object IRequest.InvokeReceived() {{ return {0}.MessageReceived(this); }}", messageType.Name)));
                        messageType.Members.Add(messageRequestType);

                        int fieldTag = 1;
                        foreach (var field in messageDef.Request.Fields)
                        {
                            var member = new CodeSnippetTypeMember(string.Format("[ProtoMember({2}, IsRequired = {3})] {4} public {0} {1} {{ get; set; }}",
                                                                                 field.Type, field.Name, fieldTag++, field.Mandatory.ToString().ToLowerInvariant(),
                                                                                 field.DefaultValue != null ? string.Format("[DefaultValue({0})]", field.DefaultValue) : ""));
                            messageRequestType.Members.Add(member);
                        }

                        messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1}.{2}.{3} {1}_{2}_{3} {{ get; set; }}", dummyFieldsTag++, messagesType.Name, messageType.Name, "Request")));
                    }

                    CodeTypeDeclaration messageResponseType = null;
                    if (messageDef.Response != null)
                    {
                        messageResponseType = new CodeTypeDeclaration("Response")
                        {
                            Attributes       = MemberAttributes.Final,
                            TypeAttributes   = TypeAttributes.Public,
                            BaseTypes        = { new CodeTypeReference("IResponse") },
                            CustomAttributes = { new CodeAttributeDeclaration("ProtoContract",
                                                                              new[]
                                {
                                    new CodeAttributeArgument
                                    {
                                        Name  = "Name",
                                        Value = new CodeSnippetExpression(string.Format("\"{0}{1}\"",messageType.Name, "Response"))
                                    }
                                }) }
                        };
                        messageResponseType.Members.Add(new CodeSnippetTypeMember(string.Format("int IResponse.Tag {{ get {{ return {0}ResponseTag; }} }}", messageType.Name)));
                        messageType.Members.Add(messageResponseType);

                        int fieldTag = 1;
                        foreach (var field in messageDef.Response.Fields)
                        {
                            var member = new CodeSnippetTypeMember(string.Format("[ProtoMember({2}, IsRequired = {3})] {4} public {0} {1} {{ get; set; }}",
                                                                                 field.Type, field.Name, fieldTag++, field.Mandatory.ToString().ToLowerInvariant(),
                                                                                 field.DefaultValue != null ? string.Format("[DefaultValue({0})]", field.DefaultValue) : ""));
                            messageResponseType.Members.Add(member);
                        }

                        messagesContainerType.Members.Add(new CodeSnippetTypeMember(string.Format("[ProtoMember({0})] public {1}.{2}.{3} {1}_{2}_{3} {{ get; set; }}", dummyFieldsTag++, messagesType.Name, messageType.Name, "Response")));
                    }
                }
            } // foreach type

            return(codeUnit);
        }
Пример #3
0
        public string GenerateProtoFile(string sourceFilename, CodeCompileUnit compileUnit, MessageParser.PostalDefinition parsed)
        {
            var provider   = new CSharpCodeProvider();
            var sourceCode = File.ReadAllText(sourceFilename);

            var parameters = new CompilerParameters
            {
                GenerateExecutable   = false,
                GenerateInMemory     = true,
                ReferencedAssemblies = { "Microsoft.CSharp.dll",               "System.dll", "System.Core.dll",   "System.Xml.dll",
                                         Path.Combine(OutputDir.Replace("obj", "bin"),       "protobuf-net.dll"),
                                         Path.Combine(OutputDir.Replace("obj", "bin"),       "Postal.Core.dll"),
                                         Path.Combine(OutputDir.Replace("obj", "bin"),       "Sprache.dll") },
                OutputAssembly = string.Format("{0}\\{1}.dll", OutputDir.Replace("obj", "bin"), Guid.NewGuid().ToString()),
            };
            var results = provider.CompileAssemblyFromSource(parameters, sourceCode);

            if (results.Errors.Count > 0)
            {
                for (int i = 0; i < results.Errors.Count; i++)
                {
                    BuildEngine.LogErrorEvent(new BuildErrorEventArgs("Postal", results.Errors[i].ErrorNumber, results.Errors[i].FileName, results.Errors[i].Line, results.Errors[i].Column, results.Errors[i].Line, results.Errors[i].Column, results.Errors[i].ErrorText, "Postal", "Postal"));
                }
            }

            var assembly = results.CompiledAssembly; // Loads this assembly into our namespace

            var type = (from t in assembly.GetTypes()
                        where t.IsDefined(typeof(ProtoContractAttribute), false) && t.Name.EndsWith("_MessagesContainer")
                        select t).FirstOrDefault();

            if (type == null)
            {
                return(string.Empty);
            }

            var method = GetProtoMethod.MakeGenericMethod(type);

            if (method == null)
            {
                return("method was null");
            }

            var proto = (string)method.Invoke(null, null);

            proto = RemoveMessageContainer.Replace(proto, "");
            var messagesTypeName = RemoveAllExtensions(Path.GetFileName(sourceFilename));
            var sb = new StringBuilder();

            foreach (var postalType in parsed.PostalTypes)
            {
                var message = postalType as MessageParser.MessageDefinition;
                if (message == null)
                {
                    continue;
                }
                sb.AppendFormat("message {0}RequestWrapper\n", message.Name);
                sb.AppendLine("{");
                sb.AppendFormat("    optional {0}Request {0}Request = {1};\n", message.Name, GetStableHash(string.Format("{0}.{1}.{2}.Request", parsed.Namespace, messagesTypeName, message.Name)));
                sb.AppendLine("}");

                sb.AppendFormat("message {0}ResponseWrapper\n", message.Name);
                sb.AppendLine("{");
                sb.AppendFormat("    optional {0}Response {0}Response = {1};\n", message.Name, GetStableHash(string.Format("{0}.{1}.{2}.Response", parsed.Namespace, messagesTypeName, message.Name)));
                sb.AppendLine("}");
            }

            return(proto + "\n" + sb.ToString());
        }