public Message(Message parent) { this.Parent = parent; this.OptionNamespace = null; this.OptionTriggers = false; }
public override string GenerateClass(Message m) { string code = ""; code += GenerateInterface (m); code += "\n"; //Default class code += "public partial class " + m.CSType + " : I" + m.CSType + "\n"; code += "{\n"; string enums = GenerateEnums (m); if (enums.Length > 0) { code += Code.Indent (enums); code += "\n"; } code += Code.Indent (GenerateProperties (m)); code += "\n"; foreach (Message sub in m.Messages) { code += "\n"; code += Code.Indent (GenerateClass (sub)); } code += "}\n"; return code; }
/// <summary> /// Generates code for writing a class/message /// </summary> static string GenerateGenericWriter(Message m) { string code = ""; code += m.OptionAccess + " static void Write(Stream stream, " + m.FullCSType + " instance)\n"; code += "{\n"; code += " " + m.FullCSType + ".Serialize(stream, instance);\n"; code += "}\n"; return code; }
/// <summary> /// Adds BinaryWriter only if it will be used /// </summary> static bool GenerateBinaryWriter(Message m) { foreach (Field f in m.Fields.Values) { if (f.WireType == Wire.Fixed32 || f.WireType == Wire.Fixed64) { return true; } } return false; }
/// <summary> /// Gets the C# CamelCase version of a given name. /// Name collisions with enums are avoided. /// </summary> static string GetCSPropertyName(Message m, string name) { string csname = GetCamelCase (name); foreach (MessageEnum me in m.Enums) if (me.CSType == csname) return name; return csname; }
public MessageName(Message parent, string fullNamespaceClass) : base(parent) { path = fullNamespaceClass; int pos = fullNamespaceClass.LastIndexOf ("."); if (pos < 0) { this.CSType = fullNamespaceClass; } else { this.OptionNamespace = fullNamespaceClass.Substring (0, pos); this.CSType = fullNamespaceClass.Substring (pos + 1); } }
public static string GenerateGenericClassSerializer(Message m) { string code = ""; code += "\n"; code += GenerateGenericReader (m); code += "\n"; code += GenerateGenericWriter (m); code += "\n"; foreach (Message sub in m.Messages) { code += "\n"; code += GenerateGenericClassSerializer (sub); } return code; }
public static string GenerateClassSerializer(Message m) { string code = ""; code += m.OptionAccess + " partial class " + m.CSType + "\n"; code += "{\n"; code += Code.Indent (GenerateReader (m)); code += "\n"; code += Code.Indent (GenerateWriter (m)); foreach (Message sub in m.Messages) { code += "\n"; code += Code.Indent (GenerateClassSerializer (sub)); } code += "}\n"; code += "\n"; return code; }
static string GenerateGenericReader(Message m) { string code = ""; code += m.OptionAccess + " static " + m.FullCSType + " Read (Stream stream, " + m.FullCSType + " instance)\n"; code += "{\n"; code += " return " + m.FullCSType + ".Deserialize(stream, instance);\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static " + m.FullCSType + " Read(byte[] buffer, " + m.FullCSType + " instance)\n"; code += "{\n"; code += " using (MemoryStream ms = new MemoryStream(buffer))\n"; code += " " + m.FullCSType + ".Deserialize (ms, instance);\n"; code += " return instance;\n"; code += "}\n"; return code; }
private string GenerateInterface(Message m) { string properties = ""; foreach (Field f in m.Fields.Values) { if (f.OptionDeprecated) properties += "[Obsolete]\n"; properties += f.PropertyType + " " + f.Name + " { get; set; }\n"; } string code = ""; code += "public interface I" + m.CSType + "\n"; code += "{\n"; code += Code.Indent (properties); code += "}\n"; return code; }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> protected string GenerateProperties(Message m) { string code = ""; foreach (Field f in m.Fields.Values) { if (f.OptionGenerate) { if (f.Comments != null) { code += "/// <summary>\n"; code += Code.Prefix ("/// ", f.Comments) + "\n"; code += "/// </summary>\n"; } code += GenerateProperty (f) + "\n"; } else { code += "//" + GenerateProperty (f) + " //Implemented by user elsewhere\n"; } } return code; }
protected string GenerateEnums(Message m) { string code = ""; foreach (MessageEnum me in m.Enums) { code += "public enum " + me.CSType + "\n"; code += "{\n"; foreach (var epair in me.Enums) { if (me.EnumsComments.ContainsKey (epair.Key)) { code += " /// <summary>\n"; code += Code.Prefix (" /// ", me.EnumsComments [epair.Key]) + "\n"; code += " /// </summary>\n"; } code += " " + epair.Key + " = " + epair.Value + ",\n"; } code += "}\n"; } return code; }
public override string GenerateClass(Message m) { string code = ""; //Base class code += m.OptionAccess + " abstract class " + m.CSType + "Base\n"; code += "{\n"; code += Code.Indent (GenerateProperties (m)); code += "\n"; if (m.OptionTriggers) { code += " protected virtual void BeforeSerialize()\n"; code += " {\n"; code += " }\n"; code += "\n"; code += " protected virtual void AfterDeserialize()\n"; code += " {\n"; code += " }\n"; } code += "}\n\n"; //Default class code += m.OptionAccess + " partial class " + m.CSType + " : " + m.CSType + "Base\n"; code += "{\n"; string enums = GenerateEnums (m); if (enums.Length > 0) { code += Code.Indent (enums); code += "\n"; } #if !GENERATE_BASE code += Code.Indent (GenerateProperties (m)); code += "\n"; #endif foreach (Message sub in m.Messages) { code += "\n"; code += Code.Indent (GenerateClass (sub)); } code += "}\n"; return code; }
static MessageEnum ParseEnum(TokenReader tr, Message parent) { MessageEnum me = new MessageEnum (parent); me.Comments = lastComment; lastComment = null; me.ProtoName = tr.ReadNext (); if (tr.ReadNext () != "{") throw new ProtoFormatException ("Expected: {"); while (true) { string name = tr.ReadNext (); if (ParseComment (name)) continue; if (name == "}") return me; //Ignore options if (name == "option") { ParseOption (tr, null); lastComment = null; continue; } if (tr.ReadNext () != "=") throw new ProtoFormatException ("Expected: ="); int id = int.Parse (tr.ReadNext ()); me.Enums.Add (name, id); if (lastComment != null) me.EnumsComments.Add (name, lastComment); lastComment = null; if (tr.ReadNext () != ";") throw new ProtoFormatException ("Expected: ;"); } }
public virtual string GenerateClass(Message m) { string code = ""; //Default class if (m.Comments != null) { code += "/// <summary>\n"; code += Code.Prefix ("/// ", m.Comments) + "\n"; code += "/// </summary>\n"; } code += m.OptionAccess + " partial class " + m.CSType + "\n"; code += "{\n"; string enums = GenerateEnums (m); if (enums.Length > 0) { code += Code.Indent (enums); code += "\n"; } code += Code.Indent (GenerateProperties (m)); code += "\n"; if (m.OptionTriggers) { code += Code.Indent (Code.Comment ( "protected virtual void BeforeSerialize() {}\n" + "protected virtual void AfterDeserialize() {}\n")); code += "\n"; } foreach (Message sub in m.Messages) { code += Code.Indent (GenerateClass (sub)); code += "\n"; } code = code.TrimEnd ('\n'); code += "\n}\n"; return code; }
//Search for name. static MessageEnumBase GetProtoType(Message m, string path) { string[] parts = path.Split ('.'); return SearchMessageUp (m, parts); }
static Message ParseMessage(TokenReader tr, Message parent) { Message msg = new Message (parent); msg.Comments = lastComment; lastComment = null; msg.ProtoName = tr.ReadNext (); tr.ReadNextOrThrow ("{"); while (ParseField (tr, msg)) continue; return msg; }
static bool ParseField(TokenReader tr, Message m) { string rule = tr.ReadNext (); while (true) { if (ParseComment (rule) == false) break; rule = tr.ReadNext (); } if (rule == "}") return false; if (rule == "enum") { MessageEnum me = ParseEnum (tr, m); m.Enums.Add (me); return true; } Field f = new Field (); f.Comments = lastComment; lastComment = null; //Rule switch (rule) { case "required": f.Rule = FieldRule.Required; break; case "optional": f.Rule = FieldRule.Optional; break; case "repeated": f.Rule = FieldRule.Repeated; break; case "option": //Save options ParseOption (tr, m); return true; case "message": m.Messages.Add (ParseMessage (tr, m)); return true; default: throw new ProtoFormatException ("unknown rule: " + rule); } //Type f.ProtoTypeName = tr.ReadNext (); //Name f.Name = tr.ReadNext (); //ID tr.ReadNextOrThrow ("="); f.ID = int.Parse (tr.ReadNext ()); if (19000 <= f.ID && f.ID <= 19999) throw new ProtoFormatException ("Can't use reserved field ID 19000-19999"); if (f.ID > (1 << 29) - 1) throw new ProtoFormatException ("Maximum field id is 2^29 - 1"); //Add Field to message m.Fields.Add (f.ID, f); //Determine if extra options string extra = tr.ReadNext (); if (extra == ";") return true; //Field options if (extra != "[") throw new ProtoFormatException ("Expected: [ got " + extra); while (true) { string key = tr.ReadNext (); tr.ReadNextOrThrow ("="); string val = tr.ReadNext (); ParseFieldOption (key, val, f); string optionSep = tr.ReadNext (); if (optionSep == "]") break; if (optionSep == ",") continue; throw new ProtoFormatException (@"Expected "","" or ""]"" got " + tr.Next); } tr.ReadNextOrThrow (";"); return true; }
/// <summary> /// Generates code for writing a class/message /// </summary> static string GenerateWriter(Message m) { string code = m.OptionAccess + " static void Serialize(Stream stream, " + m.CSType + " instance)\n"; code += "{\n"; if (m.OptionTriggers) { code += " instance.BeforeSerialize();\n"; code += "\n"; } if (GenerateBinaryWriter (m)) code += " BinaryWriter bw = new BinaryWriter(stream);\n"; foreach (Field f in m.Fields.Values) { code += Code.Indent (FieldCode.GenerateFieldWriter (m, f)); } code += "}\n\n"; code += m.OptionAccess + " static byte[] SerializeToBytes(" + m.CSType + " instance)\n"; code += "{\n"; code += " using(MemoryStream ms = new MemoryStream())\n"; code += " {\n"; code += " Serialize(ms, instance);\n"; code += " return ms.ToArray();\n"; code += " }\n"; code += "}\n"; return code; }
/// <summary> /// File or Message options /// </summary> static void ParseOption(TokenReader tr, Message m) { //Read name string key = tr.ReadNext (); if (tr.ReadNext () != "=") throw new ProtoFormatException ("Expected: = got " + tr.Next); //Read value string value = tr.ReadNext (); if (tr.ReadNext () != ";") throw new ProtoFormatException ("Expected: ; got " + tr.Next); //null = ignore option if (m == null) return; switch (key) { case "namespace": m.OptionNamespace = value; break; case "triggers": m.OptionTriggers = Boolean.Parse (value); break; case "access": m.OptionAccess = value; break; default: Console.WriteLine ("Warning: Unknown option: " + key); break; } }
public MessageEnum(Message parent) { this.Parent = parent; }
static string GenerateReader(Message m) { string code = ""; code += m.OptionAccess + " static " + m.CSType + " Deserialize(Stream stream)\n"; code += "{\n"; code += " " + m.CSType + " instance = new " + m.CSType + "();\n"; code += " Deserialize(stream, instance);\n"; code += " return instance;\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static " + m.CSType + " Deserialize(byte[] buffer)\n"; code += "{\n"; code += " using(MemoryStream ms = new MemoryStream(buffer))\n"; code += " return Deserialize(ms);\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static T Deserialize<T> (Stream stream) where T : " + m.FullCSType + ", new()\n"; code += "{\n"; code += " T instance = new T ();\n"; code += " Deserialize (stream, instance);\n"; code += " return instance;\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static T Deserialize<T> (byte[] buffer) where T : " + m.FullCSType + ", new()\n"; code += "{\n"; code += " T instance = new T ();\n"; code += " Deserialize(buffer, instance);\n"; code += " return instance;\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static " + m.FullCSType + " Deserialize (byte[] buffer, " + m.FullCSType + " instance)\n"; code += "{\n"; code += " using (MemoryStream ms = new MemoryStream(buffer))\n"; code += " Deserialize (ms, instance);\n"; code += " return instance;\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static " + m.FullCSType + " Deserialize(Stream stream, " + m.FullCSType + " instance)\n"; code += "{\n"; foreach (Field f in m.Fields.Values) { if (f.WireType == Wire.Fixed32 || f.WireType == Wire.Fixed64) { code += " BinaryReader br = new BinaryReader (stream);\n"; break; } } foreach (Field f in m.Fields.Values) { if (f.Rule == FieldRule.Repeated) { code += " if(instance." + f.Name + " == null)\n"; code += " instance." + f.Name + " = new List<" + f.PropertyItemType + ">();\n"; } else if (f.OptionDefault != null) { if (f.ProtoType == ProtoTypes.Enum) code += " instance." + f.Name + " = " + f.FullPath + "." + f.OptionDefault + ";\n"; else code += " instance." + f.Name + " = " + f.OptionDefault + ";\n"; } else if (f.Rule == FieldRule.Optional) { if (f.ProtoType == ProtoTypes.Enum) { //the default value is the first value listed in the enum's type definition foreach (var kvp in f.ProtoTypeEnum.Enums) { code += " instance." + f.Name + " = " + kvp.Key + ";\n"; break; } } } } code += " while (true)\n"; code += " {\n"; code += " ProtocolBuffers.Key key = null;\n"; code += " int keyByte = stream.ReadByte ();\n"; code += " if (keyByte == -1)\n"; code += " break;\n"; code += " //Optimized reading of known fields with field ID < 16\n"; code += " switch (keyByte) {\n"; foreach (Field f in m.Fields.Values) { if (f.ID >= 16) continue; code += " case " + ((f.ID << 3) | (int)f.WireType) + ": //Field " + f.ID + " " + f.WireType + "\n"; code += Code.Indent (3, FieldCode.GenerateFieldReader (f)) + "\n"; code += " break;\n"; } code += " default:\n"; code += " key = ProtocolParser.ReadKey ((byte)keyByte, stream);\n"; code += " break;\n"; code += " }\n"; code += "\n"; code += " if (key == null)\n"; code += " continue;\n"; code += "\n"; code += " //Reading field ID > 16 and unknown field ID/wire type combinations\n"; code += " switch (key.Field) {\n"; code += " case 0:\n"; code += " throw new InvalidDataException(\"Invalid field id: 0, something went wrong in the stream\");\n"; foreach (Field f in m.Fields.Values) { if (f.ID < 16) continue; code += " case " + f.ID + ":\n"; code += Code.Indent (3, FieldCode.GenerateFieldReader (f)) + "\n"; code += " break;\n"; } code += " default:\n"; code += " ProtocolParser.SkipKey(stream, key);\n"; code += " break;\n"; code += " }\n"; code += " }\n"; code += " \n"; if (m.OptionTriggers) code += " instance.AfterDeserialize();\n"; code += " return instance;\n"; code += "}\n"; code += "\n"; code += m.OptionAccess + " static " + m.FullCSType + " Read(byte[] buffer, " + m.FullCSType + " instance)\n"; code += "{\n"; code += " using (MemoryStream ms = new MemoryStream(buffer))\n"; code += " Deserialize (ms, instance);\n"; code += " return instance;\n"; code += "}\n"; return code; }
/// <summary> /// Generates code for writing one field /// </summary> public static string GenerateFieldWriter(Message m, Field f) { string code = ""; if (f.Rule == FieldRule.Repeated) { if (f.OptionPacked == true) { string binaryWriter = ""; switch (f.ProtoType) { case ProtoTypes.Double: case ProtoTypes.Float: case ProtoTypes.Fixed32: case ProtoTypes.Fixed64: case ProtoTypes.Sfixed32: case ProtoTypes.Sfixed64: binaryWriter = "\nBinaryWriter bw" + f.ID + " = new BinaryWriter(ms" + f.ID + ");"; break; } code += "if(instance." + f.Name + " != null)\n"; code += "{\n"; code += " ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += " using(MemoryStream ms" + f.ID + " = new MemoryStream())\n"; code += " { " + binaryWriter + "\n"; code += " foreach(" + f.PropertyItemType + " i" + f.ID + " in instance." + f.Name + ")\n"; code += " {\n"; code += Code.Indent (3, GenerateFieldTypeWriter (f, "ms" + f.ID, "bw" + f.ID, "i" + f.ID)) + "\n"; code += " }\n"; code += " ProtocolParser.WriteBytes(stream, ms" + f.ID + ".ToArray());\n"; code += " }\n"; code += "}\n"; return code; } else { code += "if(instance." + f.Name + " != null)\n"; code += "{\n"; code += " foreach(" + f.PropertyItemType + " i" + f.ID + " in instance." + f.Name + ")\n"; code += " {\n"; code += " ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += Code.Indent (2, GenerateFieldTypeWriter (f, "stream", "bw", "i" + f.ID)) + "\n"; code += " }\n"; code += "}\n"; return code; } } else if (f.Rule == FieldRule.Optional) { switch (f.ProtoType) { case ProtoTypes.String: case ProtoTypes.Message: case ProtoTypes.Bytes: code += "if(instance." + f.Name + " != null)\n"; code += "{\n"; code += " ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += Code.Indent (GenerateFieldTypeWriter (f, "stream", "bw", "instance." + f.Name)); code += "}\n"; return code; case ProtoTypes.Enum: code += "if(instance." + f.Name + " != " + f.PropertyItemType + "." + f.OptionDefault + ")\n"; code += "{\n"; code += " ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += Code.Indent (GenerateFieldTypeWriter (f, "stream", "bw", "instance." + f.Name)); code += "}\n"; return code; default: code += "ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += GenerateFieldTypeWriter (f, "stream", "bw", "instance." + f.Name); return code; } } else if (f.Rule == FieldRule.Required) { switch (f.ProtoType) { case ProtoTypes.String: case ProtoTypes.Message: case ProtoTypes.Bytes: code += "if(instance." + f.Name + " == null)\n"; code += " throw new ArgumentNullException(\"" + f.Name + "\", \"Required by proto specification.\");\n"; break; } code += "ProtocolParser.WriteKey(stream, new ProtocolBuffers.Key(" + f.ID + ", Wire." + f.WireType + "));\n"; code += GenerateFieldTypeWriter (f, "stream", "bw", "instance." + f.Name); return code; } throw new NotImplementedException ("Unknown rule: " + f.Rule); }
static void PrepareMessage(Message m) { //Name of message and enums m.CSType = ProtoPrepare.GetCamelCase (m.ProtoName); foreach (MessageEnum e in m.Enums) { e.CSType = GetCamelCase (e.ProtoName); } foreach (Message sub in m.Messages) PrepareMessage (sub); //Prepare fields foreach (Field f in m.Fields.Values) { PrepareProtoType (m, f); if (f.OptionDefault != null) f.OptionDefault = GetCSDefaultValue (f); } }
/// <summary> /// Searchs the message for matchink classes /// </summary> /// <param name='name'> /// name from .proto /// </param> static MessageEnumBase SearchMessageUp(Message p, string[] name) { if (p is Proto) return SearchMessageDown (p, name); Message m = p as Message; if (m.ProtoName == name [0]) { if (name.Length == 1) return m; string[] subName = new string[name.Length - 1]; Array.Copy (name, 1, subName, 0, subName.Length); return SearchMessageDown (m, subName); } MessageEnumBase down = SearchMessageDown (p, name); if (down != null) return down; return SearchMessageUp (m.Parent, name); }
/// <summary> /// Search down for matching name /// </summary> /// <param name='name'> /// Split .proto type name /// </param> static MessageEnumBase SearchMessageDown(Message p, string[] name) { if (name.Length == 1) { foreach (MessageEnum me in p.Enums) { if (me.ProtoName == name [0]) return me; } } foreach (Message sub in p.Messages) { if (sub.ProtoName == name [0]) { if (name.Length == 1) return sub; string[] subName = new string[name.Length - 1]; Array.Copy (name, 1, subName, 0, subName.Length); return SearchMessageDown (sub, subName); } } return null; }
/// <summary> /// Prepare: ProtoType, WireType and CSType /// </summary> static void PrepareProtoType(Message m, Field f) { //Change property name to C# style, CamelCase. f.Name = GetCSPropertyName (m, f.Name); f.ProtoType = GetScalarProtoType (f.ProtoTypeName); //Wire, and set type switch (f.ProtoType) { case ProtoTypes.Double: case ProtoTypes.Fixed64: case ProtoTypes.Sfixed64: f.WireType = Wire.Fixed64; break; case ProtoTypes.Float: case ProtoTypes.Fixed32: case ProtoTypes.Sfixed32: f.WireType = Wire.Fixed32; break; case ProtoTypes.Int32: case ProtoTypes.Int64: case ProtoTypes.Uint32: case ProtoTypes.Uint64: case ProtoTypes.Sint32: case ProtoTypes.Sint64: case ProtoTypes.Bool: f.WireType = Wire.Varint; break; case ProtoTypes.String: case ProtoTypes.Bytes: f.WireType = Wire.LengthDelimited; break; default: MessageEnumBase pt = GetProtoType (m, f.ProtoTypeName); if (pt == null) { //Assumed to be a message defined elsewhere f.ProtoType = ProtoTypes.Message; f.WireType = Wire.LengthDelimited; f.ProtoTypeMessage = new MessageName (m, f.ProtoTypeName); } if (pt is MessageEnum) { f.ProtoType = ProtoTypes.Enum; f.WireType = Wire.Varint; f.ProtoTypeEnum = (MessageEnum)pt; } if (pt is Message) { f.ProtoType = ProtoTypes.Message; f.WireType = Wire.LengthDelimited; f.ProtoTypeMessage = (Message)pt; } string[] parts = f.ProtoTypeName.Split ('.'); string cc = GetCamelCase (parts [parts.Length - 1]); if (pt is Message) { f.CSClass = cc; #if GENERATE_INTERFACE f.CSType += "I" + cc; #else f.CSType += cc; #endif break; } else f.CSType = cc; break; } if (f.OptionPacked) { if (f.WireType == Wire.LengthDelimited) throw new InvalidDataException ("Packed field not allowed for length delimited types"); f.WireType = Wire.LengthDelimited; } if (f.OptionCodeType != null) { f.CSClass = f.OptionCodeType; f.CSType = f.OptionCodeType; } if (f.CSType == null) { f.CSType = GetCSType (f.ProtoType); f.CSClass = f.CSType; } }