diff --git a/proto/compiler/README.md b/proto/compiler/README.md new file mode 100644 index 00000000000..85b0e3627b7 --- /dev/null +++ b/proto/compiler/README.md @@ -0,0 +1,58 @@ +# ProtoKot compiler + +## Building + + $ cd google/protobuf/compiler/kotlin + $ make + +This will build current version of .proto compiler, placed as *protoc* executable file in current directory. + +*Note:* You need g++ and protobuf installed (-lprotoc used for linking) + +## Using compiler + + $ ./protoc kotlin_out==$(DST_DIR) $(PATH_TO_PROTO) + +where $(DST_DIR) stand for path to place, where generated files should be stored, and $(PATH_TO_PROTO) stand for path to .proto-file. + +## Linking with Kotlin-runtime + +In folder /src/ you can find sources for ProtoKot-runtime, that will be used by generated messages. + +## Using generated code + +Example: + // Messages work only with CodedStream classes, provided by ProtoKot-runtime library. + // One can create CodedStream passing any instance of corresponding Stream from Java's library. + val s = ByteArrayOutputStream() + val outs = CodedOutputStream(s) + + // All messages are immutable. Use Builders for creating new messages + val msg = Person.BuilderPerson() + .setEmail("wtf@dasda.com") // all setters return this builder, so you could chain modifiers in LINQ-style + .setId(42) + .setName("John Doe") + .setPhones(arrayOf( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields + Person.PhoneNumber.BuilderPhoneNumber() + .setNumber("342143-23423-42") + .setType(Person.PhoneType.HOME) + .build() + )) + .build() // don't forget to call build() to produce message + msg.writeTo(outs) + + // Now let's use output stream as input to read our message from it! + var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + + // Create default instance of message + var readMsg = Person.BuilderPerson().build() + // Read in that message data from input stream. + readMsg.mergeFrom(ins) + + // Note, that currently mergeFrom is the only way to mutate instance of message. + // Don't rely on it, probably mergeFrom will be refactored lately to guarantee full immutability of mesages. + + // Better way to read a message: + ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) + readMsg = Person.BuilderPerson().readFrom(ins).build() + assert(msg == readMsg) \ No newline at end of file diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc index 2b1bb5cc57f..28838690de2 100644 --- a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc @@ -19,6 +19,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { /** * Field generator should know if it is generating code for builder. * or for fair class to choose between 'val' and 'var'. + * Also note that fields should be declared before init section. */ for (FieldGenerator *gen: properties) { gen->generateCode(printer, isBuilder); @@ -47,7 +48,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { printer->Print("\n"); generateSerializersNoTag(printer, /* isRead = */ isBuilder); - // builder and mergeFrom only for fair classes + // builder, mergeFrom and only for fair classes if (!isBuilder) { printer->Print("\n"); generateBuilder(printer); @@ -56,10 +57,13 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { generateMergeFrom(printer); } - // build() is only for builders + // build() and setters are only for builders if (isBuilder) { printer->Print("\n"); generateBuildMethod(printer); + + printer->Print("\n"); + generateSetters(printer); } printer->Outdent(); @@ -127,18 +131,20 @@ void ClassGenerator::generateMergeFrom(io::Printer * printer) const { void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const { map vars; vars["funName"]= isRead ? "readFrom" : "writeTo"; + vars["returnType"] = isRead ? "Builder" + simpleName : "Unit"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["arg"] = isRead ? "input" : "output"; vars["maybeSeparator"] = isRead ? "" : ", "; + vars["maybeReturn"] = isRead ? "return " : ""; // generate function header printer->Print(vars, - "fun $funName$ ($arg$: $stream$) {" + "fun $funName$ ($arg$: $stream$): $returnType$ {" "\n"); printer->Indent(); //TODO: write message tag and size - printer->Print(vars, "$funName$NoTag($arg$)\n"); + printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n"); printer->Outdent(); printer->Print("}\n"); @@ -149,10 +155,12 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead) vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["arg"] = isRead ? "input" : "output"; + vars["returnType"] = isRead ? "Builder" + simpleName : "Unit"; + vars["maybeReturn"] = isRead ? "return this\n" : ""; // generate function header printer->Print(vars, - "fun $funName$ ($arg$: $stream$) {" + "fun $funName$ ($arg$: $stream$): $returnType$ {" "\n"); printer->Indent(); @@ -161,6 +169,7 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead) properties[i]->generateSerializationCode(printer, isRead); } + printer->Print(vars, "$maybeReturn$"); printer->Outdent(); printer->Print("}\n"); } @@ -171,7 +180,7 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const // build list of arguments like 'field1: Type1, field2: Type2, ... ' string argumentList = ""; for (int i = 0; i < properties.size(); ++i) { - argumentList += properties[i]->simpleName + ": " + properties[i]->fieldName; + argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->initValue; if (i + 1 != properties.size()) { argumentList += ", "; } @@ -225,6 +234,13 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const { printer->Print("}\n"); } +void ClassGenerator::generateSetters(io::Printer *printer) const { + for (int i = 0; i < properties.size(); ++i) { + properties[i]->generateSetter(printer, "Builder" + simpleName); + printer->Print("\n"); + } +} + const string ClassModifier::getName() const { string result = ""; diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h index adcfac50453..6b9da71742f 100644 --- a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h @@ -47,7 +47,7 @@ private: void generateBuilder (io::Printer * printer) const; void generateBuildMethod (io::Printer * printer) const; void generateInitSection (io::Printer * printer) const; - + void generateSetters (io::Printer * printer) const; /** * Flag isBuilder used for reducing code repeating, as code for class itself * and for its inner builder are structurally very alike and can be generated diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc index 2fbdea90979..dcd9b714c44 100644 --- a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc @@ -86,8 +86,8 @@ string FieldGenerator::getInitValue() const { return "null"; } if (descriptor->is_repeated()) - return "listOf()"; - return fieldName + "()"; + return "arrayOf()"; + return fullType + "()"; } void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const { @@ -108,8 +108,8 @@ FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor) : descriptor(descriptor) , modifier(descriptor->label()) , simpleName(descriptor->name()) - , fieldName(protobufToKotlinField()) - , fieldType(protobufToKotlinType()) + , underlyingType(protobufToKotlinType()) + , fullType(protobufToKotlinField()) , initValue(getInitValue()) { } @@ -135,8 +135,8 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead // tag if (isRead) { //TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc. - //Currently tag is ignored, and fields order is critical for serialization/deserialization. Therefore, - //backward-compability and extensions are not supported. + //Currently tag is ignored, and work of the library relies heavily on the field order guarantees. + //Thus, backward-compability and extensions are not supported. printer->Print(vars, "val tag = input.readTag()\n"); printer->Print(vars, "val listSize = input.readInt32NoTag()\n"); printer->Print(vars, "for (i in 1..listSize) {\n"); @@ -156,8 +156,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead /* hack: copy current FieldGenerator and change label to OPTIONAL. This will allow to re-use this function for generating serialization code for elements of array. More importantly, this will care about nested types too. - However, this hack isn't necessary and could be safely removed as soon as target - code will support inheritance and interfaces + Efficiently, it inlines serialization code for all underlying types. + This hack isn't necessary from the architectural point of view and could be safely5 + removed as soon as target code will support inheritance and interfaces. (then writing CodedOutputStream.writeMessage will be possible). */ FieldGenerator singleFieldGen = FieldGenerator(descriptor); @@ -183,7 +184,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead Example: output.writeEnum(42, enumField.ord) */ if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { - vars["converter"] = fieldType + ".fromIntTo" + fieldType; + vars["converter"] = underlyingType + ".fromIntTo" + underlyingType; if (isRead) { printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n"); } @@ -221,7 +222,6 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead // TODO: support tricky types like enums/messages/repeated fields/etc } -// TODO: think about refactoring this method to FieldGenerator, as it is related to field, not to Class in general string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const { switch (type) { case FieldDescriptor::TYPE_DOUBLE: @@ -262,6 +262,27 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type return "SInt64"; } } + +void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const { + map vars; + // TODO: refactor work with names into separate class + string camelCaseName = simpleName; + camelCaseName[0] = char(std::toupper(camelCaseName[0])); + vars["camelCaseName"] = camelCaseName; + vars["name"] = simpleName; + vars["builderName"] = builderName; + vars["type"] = fullType; + printer->Print(vars, + "fun set$camelCaseName$(value: $type$): $builderName$ {\n"); + printer->Indent(); + printer->Print(vars, + "$name$ = value\n" + "return this\n"); + printer->Outdent(); + printer->Print("}\n"); +} + + } // namespace kotlin } // namspace compiler } // namespace protobuf diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h index 4859bef0a3d..e11c0dccec8 100644 --- a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h @@ -34,11 +34,20 @@ private: public: FieldDescriptor::Label modifier; string simpleName; - string fieldName; - string fieldType; + string underlyingType; // unwrapped type. + + /** + * Full type of field. + * fullType = Array for REPEATED fields + * fullType = underlyingType? for OPTIONAL fields + * fullType = underlyingType for all other cases + */ + string fullType; + string initValue; void generateCode(io::Printer * printer, bool isBuilder = false) const; void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const; + void generateSetter(io::Printer * printer, string builderName) const; FieldGenerator(FieldDescriptor const * descriptor); }; diff --git a/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt index 8a930e848c9..a35c9e9c4f6 100644 --- a/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt +++ b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt @@ -1,4 +1,4 @@ -class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array ) { +class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { var name : kotlin.String? private set @@ -34,7 +34,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. } } } - class PhoneNumber private constructor (number: kotlin.String?, type: PhoneType?) { + class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) { var number : kotlin.String? private set @@ -56,7 +56,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. output.writeEnum (2, type?.ord) } - class BuilderPhoneNumber constructor (number: kotlin.String?, type: PhoneType?) { + class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) { var number : kotlin.String? var type : PhoneType? @@ -79,6 +79,17 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. fun build(): PhoneNumber { return PhoneNumber(number, type) } + + fun setNumber(value: kotlin.String?): BuilderPhoneNumber { + number = value + return this + } + + fun setType(value: PhoneType?): BuilderPhoneNumber { + type = value + return this + } + } fun mergeFrom (input: CodedInputStream) { @@ -104,7 +115,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. } } - class BuilderPerson constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array ) { + class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { var name : kotlin.String? var id : Int? @@ -141,6 +152,27 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. fun build(): Person { return Person(name, id, email, phones) } + + fun setName(value: kotlin.String?): BuilderPerson { + name = value + return this + } + + fun setId(value: Int?): BuilderPerson { + id = value + return this + } + + fun setEmail(value: kotlin.String?): BuilderPerson { + email = value + return this + } + + fun setPhones(value: Array ): BuilderPerson { + phones = value + return this + } + } fun mergeFrom (input: CodedInputStream) { @@ -158,7 +190,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin. } -class AddressBook private constructor (people: Array ) { +class AddressBook private constructor (people: Array = arrayOf()) { var people : Array private set @@ -180,7 +212,7 @@ class AddressBook private constructor (people: Array ) { } } - class BuilderAddressBook constructor (people: Array ) { + class BuilderAddressBook constructor (people: Array = arrayOf()) { var people : Array @@ -205,6 +237,12 @@ class AddressBook private constructor (people: Array ) { fun build(): AddressBook { return AddressBook(people) } + + fun setPeople(value: Array ): BuilderAddressBook { + people = value + return this + } + } fun mergeFrom (input: CodedInputStream) { diff --git a/proto/compiler/src/AddressBook.kt b/proto/compiler/src/AddressBook.kt deleted file mode 100644 index 58ebeb27c62..00000000000 --- a/proto/compiler/src/AddressBook.kt +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Created by user on 7/13/16. - */ -class AddressBook private constructor (people: Array ) { - var people : Array - private set - - init { - this.people = people - } - - fun writeTo (output: CodedOutputStream) { - writeToNoTag(output) - } - - fun writeToNoTag (output: CodedOutputStream) { - if (people.size > 0) { - output.writeInt32NoTag(people.size) - for (item in people) { - item.writeToNoTag(output) - } - } - } - - class BuilderAddressBook constructor (people: Array ) { - var people : Array - - init { - this.people = people - } - - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) - } - - fun readFromNoTag (input: CodedInputStream) { - if (people.size > 0) { - val tag = input.readTag() - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } - } - } - - fun build(): AddressBook { - return AddressBook(people) - } - } - - fun mergeFrom (input: CodedInputStream) { - if (people.size > 0) { - val tag = input.readTag() - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } - } - } -} - - diff --git a/proto/compiler/src/Person.kt b/proto/compiler/src/Person.kt deleted file mode 100644 index 02b683ac651..00000000000 --- a/proto/compiler/src/Person.kt +++ /dev/null @@ -1,158 +0,0 @@ -class Person constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array ) { - var name : kotlin.String? - private set - - var id : Int? - private set - - var email : kotlin.String? - private set - - var phones : Array - private set - - - init { - this.name = name - this.id = id - this.email = email - this.phones = phones - } - enum class PhoneType(val ord: Int) { - MOBILE (0), - HOME (1), - WORK (2); - - companion object { - fun fromIntToPhoneType (ord: Int): PhoneType { - return when (ord) { - 0 -> PhoneType.MOBILE - 1 -> PhoneType.HOME - 2 -> PhoneType.WORK - else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing PhoneType "); - } - } - } - } - class PhoneNumber constructor (number: kotlin.String?, type: PhoneType?) { - var number : kotlin.String? - private set - - var type : PhoneType? - private set - - - init { - this.number = number - this.type = type - } - - fun writeTo (output: CodedOutputStream) { - writeToNoTag(output) - } - - fun writeToNoTag (output: CodedOutputStream) { - output.writeString (1, number) - output.writeEnum (2, type?.ord) - } - - class BuilderPhoneNumber constructor (number: kotlin.String?, type: PhoneType?) { - var number : kotlin.String? - - var type : PhoneType? - - - init { - this.number = number - this.type = type - } - - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) - } - - fun readFromNoTag (input: CodedInputStream) { - number = input.readString(1) - type = PhoneType.fromIntToPhoneType(input.readEnum(2)) - } - - fun build(): PhoneNumber { - return PhoneNumber(number, type) - } - } - - fun mergeFrom (input: CodedInputStream) { - number = input.readString(1) - type = PhoneType.fromIntToPhoneType(input.readEnum(2)) - } - } - - - fun writeTo (output: CodedOutputStream) { - writeToNoTag(output) - } - - fun writeToNoTag (output: CodedOutputStream) { - output.writeString (1, name) - output.writeInt32 (2, id) - output.writeString (3, email) - if (phones.size > 0) { - output.writeInt32NoTag(phones.size) - for (item in phones) { - item.writeToNoTag(output) - } - } - } - - class BuilderPerson constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array ) { - var name : kotlin.String? - - var id : Int? - - var email : kotlin.String? - - var phones : Array - - - init { - this.name = name - this.id = id - this.email = email - this.phones = phones - } - - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) - } - - fun readFromNoTag (input: CodedInputStream) { - name = input.readString(1) - id = input.readInt32(2) - email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag() - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } - } - } - - fun build(): Person { - return Person(name, id, email, phones) - } - } - - fun mergeFrom (input: CodedInputStream) { - name = input.readString(1) - id = input.readInt32(2) - email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag() - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } - } - } -} diff --git a/proto/compiler/src/PersonMessage.kt b/proto/compiler/src/PersonMessage.kt index 1aa727a801a..e29e0fb689b 100644 --- a/proto/compiler/src/PersonMessage.kt +++ b/proto/compiler/src/PersonMessage.kt @@ -1,48 +1,224 @@ -/** - * Created by user on 7/8/16. - */ +package Addressbook; -// We don't use Message interface for a moment because of LLVM-translator restrictions -class PersonMessage(val name: String, val id: Long, val hasCat: Boolean) // : Message -{ - fun writeTo(output: CodedOutputStream) { - output.writeString(1, name) - output.writeInt64(2, id) - output.writeBool(3, hasCat) +class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { + var name : kotlin.String? + private set + + var id : Int? + private set + + var email : kotlin.String? + private set + + var phones : Array + private set + + + init { + this.name = name + this.id = id + this.email = email + this.phones = phones } - fun readFrom(input: CodedInputStream) : PersonMessage { - val newName = input.readString(1) - val newId = input.readInt64(2) - val newHasCatFlag = input.readBool(3) - return PersonMessage(newName, newId, newHasCatFlag) - } + enum class PhoneType(val ord: Int) { + MOBILE (0), + HOME (1), + WORK (2); - fun getBuilder(): PersonBuilder { - return PersonBuilder() - } - - // No interface, see above - class PersonBuilder { // : Message.Builder - // TODO: think how can we - var name_: String = "" - var id_: Long = 0 - var hasCat_: Boolean = false - - fun setName(name: String) { - name_ = name - } - - fun setId(id: Long) { - id_ = id - } - - fun setHasCat(hasCat: Boolean) { - hasCat_ = hasCat - } - - fun build(): PersonMessage { - return PersonMessage(name_, id_, hasCat_) // Java implementation caches such instances + companion object { + fun fromIntToPhoneType (ord: Int): PhoneType { + return when (ord) { + 0 -> PhoneType.MOBILE + 1 -> PhoneType.HOME + 2 -> PhoneType.WORK + else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing PhoneType "); + } + } } } -} \ No newline at end of file + class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) { + var number : kotlin.String? + private set + + var type : PhoneType? + private set + + + init { + this.number = number + this.type = type + } + + fun writeTo (output: CodedOutputStream) { + writeToNoTag(output) + } + + fun writeToNoTag (output: CodedOutputStream) { + output.writeString (1, number) + output.writeEnum (2, type?.ord) + } + + class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) { + var number : kotlin.String? + + var type : PhoneType? + + + init { + this.number = number + this.type = type + } + + fun readFrom (input: CodedInputStream) { + readFromNoTag(input) + } + + fun readFromNoTag (input: CodedInputStream) { + number = input.readString(1) + type = PhoneType.fromIntToPhoneType(input.readEnum(2)) + } + + fun build(): PhoneNumber { + return PhoneNumber(number, type) + } + } + + fun mergeFrom (input: CodedInputStream) { + number = input.readString(1) + type = PhoneType.fromIntToPhoneType(input.readEnum(2)) + } + } + + + fun writeTo (output: CodedOutputStream) { + writeToNoTag(output) + } + + fun writeToNoTag (output: CodedOutputStream) { + output.writeString (1, name) + output.writeInt32 (2, id) + output.writeString (3, email) + if (phones.size > 0) { + output.writeInt32NoTag(phones.size) + for (item in phones) { + item.writeToNoTag(output) + } + } + } + + class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { + var name : kotlin.String? + + var id : Int? + + var email : kotlin.String? + + var phones : Array + + + init { + this.name = name + this.id = id + this.email = email + this.phones = phones + } + + fun readFrom (input: CodedInputStream) { + readFromNoTag(input) + } + + fun readFromNoTag (input: CodedInputStream) { + name = input.readString(1) + id = input.readInt32(2) + email = input.readString(3) + if (phones.size > 0) { + val tag = input.readTag() + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + phones[i - 1].mergeFrom(input) + } + } + } + + fun build(): Person { + return Person(name, id, email, phones) + } + } + + fun mergeFrom (input: CodedInputStream) { + name = input.readString(1) + id = input.readInt32(2) + email = input.readString(3) + if (phones.size > 0) { + val tag = input.readTag() + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + phones[i - 1].mergeFrom(input) + } + } + } +} + + +class AddressBook private constructor (people: Array = arrayOf()) { + var people : Array + private set + + + init { + this.people = people + } + + fun writeTo (output: CodedOutputStream) { + writeToNoTag(output) + } + + fun writeToNoTag (output: CodedOutputStream) { + if (people.size > 0) { + output.writeInt32NoTag(people.size) + for (item in people) { + item.writeToNoTag(output) + } + } + } + + class BuilderAddressBook constructor (people: Array = arrayOf()) { + var people : Array + + + init { + this.people = people + } + + fun readFrom (input: CodedInputStream) { + readFromNoTag(input) + } + + fun readFromNoTag (input: CodedInputStream) { + if (people.size > 0) { + val tag = input.readTag() + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + people[i - 1].mergeFrom(input) + } + } + } + + fun build(): AddressBook { + return AddressBook(people) + } + } + + fun mergeFrom (input: CodedInputStream) { + if (people.size > 0) { + val tag = input.readTag() + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + people[i - 1].mergeFrom(input) + } + } + } +} + + diff --git a/proto/compiler/src/TestMessages.kt b/proto/compiler/src/TestMessages.kt index 9392e5b9939..b37fa3e48f5 100644 --- a/proto/compiler/src/TestMessages.kt +++ b/proto/compiler/src/TestMessages.kt @@ -5,18 +5,12 @@ import java.io.ByteArrayOutputStream * Created by user on 7/8/16. */ +import PersonMessage + fun testMessageSerialization() { val s = ByteArrayOutputStream() val outs = CodedOutputStream(s) - val msg = Person( - name = "John Doe", - id = 42, - email = "wtf@dsada.com", - phones = arrayOf ( - Person.PhoneNumber("8-800-555-35-35", Person.PhoneType.WORK), - Person.PhoneNumber("228-322", Person.PhoneType.HOME) - ) - ) + val msg = msg.writeTo(outs) val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))