Basic operations are now fully supported in the .proto-compiler. Added README.md with brief manual on usage

This commit is contained in:
dsavvinov
2016-07-14 12:45:31 +03:00
parent 5419699cfc
commit d3d91b26be
10 changed files with 388 additions and 296 deletions
+58
View File
@@ -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)
@@ -19,6 +19,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
/** /**
* Field generator should know if it is generating code for builder. * Field generator should know if it is generating code for builder.
* or for fair class to choose between 'val' and 'var'. * or for fair class to choose between 'val' and 'var'.
* Also note that fields should be declared before init section.
*/ */
for (FieldGenerator *gen: properties) { for (FieldGenerator *gen: properties) {
gen->generateCode(printer, isBuilder); gen->generateCode(printer, isBuilder);
@@ -47,7 +48,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
printer->Print("\n"); printer->Print("\n");
generateSerializersNoTag(printer, /* isRead = */ isBuilder); generateSerializersNoTag(printer, /* isRead = */ isBuilder);
// builder and mergeFrom only for fair classes // builder, mergeFrom and only for fair classes
if (!isBuilder) { if (!isBuilder) {
printer->Print("\n"); printer->Print("\n");
generateBuilder(printer); generateBuilder(printer);
@@ -56,10 +57,13 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
generateMergeFrom(printer); generateMergeFrom(printer);
} }
// build() is only for builders // build() and setters are only for builders
if (isBuilder) { if (isBuilder) {
printer->Print("\n"); printer->Print("\n");
generateBuildMethod(printer); generateBuildMethod(printer);
printer->Print("\n");
generateSetters(printer);
} }
printer->Outdent(); printer->Outdent();
@@ -127,18 +131,20 @@ void ClassGenerator::generateMergeFrom(io::Printer * printer) const {
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const { void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
map <string, string> vars; map <string, string> vars;
vars["funName"]= isRead ? "readFrom" : "writeTo"; vars["funName"]= isRead ? "readFrom" : "writeTo";
vars["returnType"] = isRead ? "Builder" + simpleName : "Unit";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output"; vars["arg"] = isRead ? "input" : "output";
vars["maybeSeparator"] = isRead ? "" : ", "; vars["maybeSeparator"] = isRead ? "" : ", ";
vars["maybeReturn"] = isRead ? "return " : "";
// generate function header // generate function header
printer->Print(vars, printer->Print(vars,
"fun $funName$ ($arg$: $stream$) {" "fun $funName$ ($arg$: $stream$): $returnType$ {"
"\n"); "\n");
printer->Indent(); printer->Indent();
//TODO: write message tag and size //TODO: write message tag and size
printer->Print(vars, "$funName$NoTag($arg$)\n"); printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n");
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
@@ -149,10 +155,12 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead)
vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag"; vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output"; vars["arg"] = isRead ? "input" : "output";
vars["returnType"] = isRead ? "Builder" + simpleName : "Unit";
vars["maybeReturn"] = isRead ? "return this\n" : "";
// generate function header // generate function header
printer->Print(vars, printer->Print(vars,
"fun $funName$ ($arg$: $stream$) {" "fun $funName$ ($arg$: $stream$): $returnType$ {"
"\n"); "\n");
printer->Indent(); printer->Indent();
@@ -161,6 +169,7 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead)
properties[i]->generateSerializationCode(printer, isRead); properties[i]->generateSerializationCode(printer, isRead);
} }
printer->Print(vars, "$maybeReturn$");
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); 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, ... ' // build list of arguments like 'field1: Type1, field2: Type2, ... '
string argumentList = ""; string argumentList = "";
for (int i = 0; i < properties.size(); ++i) { 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()) { if (i + 1 != properties.size()) {
argumentList += ", "; argumentList += ", ";
} }
@@ -225,6 +234,13 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const {
printer->Print("}\n"); 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 { const string ClassModifier::getName() const {
string result = ""; string result = "";
@@ -47,7 +47,7 @@ private:
void generateBuilder (io::Printer * printer) const; void generateBuilder (io::Printer * printer) const;
void generateBuildMethod (io::Printer * printer) const; void generateBuildMethod (io::Printer * printer) const;
void generateInitSection (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 * 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 * and for its inner builder are structurally very alike and can be generated
@@ -86,8 +86,8 @@ string FieldGenerator::getInitValue() const {
return "null"; return "null";
} }
if (descriptor->is_repeated()) if (descriptor->is_repeated())
return "listOf()"; return "arrayOf()";
return fieldName + "()"; return fullType + "()";
} }
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const { void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
@@ -108,8 +108,8 @@ FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
: descriptor(descriptor) : descriptor(descriptor)
, modifier(descriptor->label()) , modifier(descriptor->label())
, simpleName(descriptor->name()) , simpleName(descriptor->name())
, fieldName(protobufToKotlinField()) , underlyingType(protobufToKotlinType())
, fieldType(protobufToKotlinType()) , fullType(protobufToKotlinField())
, initValue(getInitValue()) , initValue(getInitValue())
{ } { }
@@ -135,8 +135,8 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
// tag // tag
if (isRead) { if (isRead) {
//TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc. //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, //Currently tag is ignored, and work of the library relies heavily on the field order guarantees.
//backward-compability and extensions are not supported. //Thus, backward-compability and extensions are not supported.
printer->Print(vars, "val tag = input.readTag()\n"); printer->Print(vars, "val tag = input.readTag()\n");
printer->Print(vars, "val listSize = input.readInt32NoTag()\n"); printer->Print(vars, "val listSize = input.readInt32NoTag()\n");
printer->Print(vars, "for (i in 1..listSize) {\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 /* 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. to re-use this function for generating serialization code for elements of array.
More importantly, this will care about nested types too. More importantly, this will care about nested types too.
However, this hack isn't necessary and could be safely removed as soon as target Efficiently, it inlines serialization code for all underlying types.
code will support inheritance and interfaces 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). (then writing CodedOutputStream.writeMessage will be possible).
*/ */
FieldGenerator singleFieldGen = FieldGenerator(descriptor); FieldGenerator singleFieldGen = FieldGenerator(descriptor);
@@ -183,7 +184,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
Example: output.writeEnum(42, enumField.ord) Example: output.writeEnum(42, enumField.ord)
*/ */
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
vars["converter"] = fieldType + ".fromIntTo" + fieldType; vars["converter"] = underlyingType + ".fromIntTo" + underlyingType;
if (isRead) { if (isRead) {
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n"); 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: 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 { string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const {
switch (type) { switch (type) {
case FieldDescriptor::TYPE_DOUBLE: case FieldDescriptor::TYPE_DOUBLE:
@@ -262,6 +262,27 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type
return "SInt64"; return "SInt64";
} }
} }
void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const {
map <string, string> 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 } // namespace kotlin
} // namspace compiler } // namspace compiler
} // namespace protobuf } // namespace protobuf
@@ -34,11 +34,20 @@ private:
public: public:
FieldDescriptor::Label modifier; FieldDescriptor::Label modifier;
string simpleName; string simpleName;
string fieldName; string underlyingType; // unwrapped type.
string fieldType;
/**
* Full type of field.
* fullType = Array<underlyingType> for REPEATED fields
* fullType = underlyingType? for OPTIONAL fields
* fullType = underlyingType for all other cases
*/
string fullType;
string initValue; string initValue;
void generateCode(io::Printer * printer, bool isBuilder = false) const; void generateCode(io::Printer * printer, bool isBuilder = false) const;
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = 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); FieldGenerator(FieldDescriptor const * descriptor);
}; };
@@ -1,4 +1,4 @@
class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) { class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String? var name : kotlin.String?
private set 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? var number : kotlin.String?
private set private set
@@ -56,7 +56,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
output.writeEnum (2, type?.ord) 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 number : kotlin.String?
var type : PhoneType? var type : PhoneType?
@@ -79,6 +79,17 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
fun build(): PhoneNumber { fun build(): PhoneNumber {
return PhoneNumber(number, type) 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) { 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 <PhoneNumber> ) { class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String? var name : kotlin.String?
var id : Int? var id : Int?
@@ -141,6 +152,27 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
fun build(): Person { fun build(): Person {
return Person(name, id, email, phones) 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 <PhoneNumber> ): BuilderPerson {
phones = value
return this
}
} }
fun mergeFrom (input: CodedInputStream) { 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 <Person> ) { class AddressBook private constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person> var people : Array <Person>
private set private set
@@ -180,7 +212,7 @@ class AddressBook private constructor (people: Array <Person> ) {
} }
} }
class BuilderAddressBook constructor (people: Array <Person> ) { class BuilderAddressBook constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person> var people : Array <Person>
@@ -205,6 +237,12 @@ class AddressBook private constructor (people: Array <Person> ) {
fun build(): AddressBook { fun build(): AddressBook {
return AddressBook(people) return AddressBook(people)
} }
fun setPeople(value: Array <Person> ): BuilderAddressBook {
people = value
return this
}
} }
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
-62
View File
@@ -1,62 +0,0 @@
/**
* Created by user on 7/13/16.
*/
class AddressBook private constructor (people: Array <Person> ) {
var people : Array <Person>
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 <Person> ) {
var people : Array <Person>
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)
}
}
}
}
-158
View File
@@ -1,158 +0,0 @@
class Person constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
var name : kotlin.String?
private set
var id : Int?
private set
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
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 <PhoneNumber> ) {
var name : kotlin.String?
var id : Int?
var email : kotlin.String?
var phones : Array <PhoneNumber>
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)
}
}
}
}
+218 -42
View File
@@ -1,48 +1,224 @@
/** package Addressbook;
* Created by user on 7/8/16.
*/
// We don't use Message interface for a moment because of LLVM-translator restrictions class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
class PersonMessage(val name: String, val id: Long, val hasCat: Boolean) // : Message var name : kotlin.String?
{ private set
fun writeTo(output: CodedOutputStream) {
output.writeString(1, name) var id : Int?
output.writeInt64(2, id) private set
output.writeBool(3, hasCat)
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
private set
init {
this.name = name
this.id = id
this.email = email
this.phones = phones
} }
fun readFrom(input: CodedInputStream) : PersonMessage { enum class PhoneType(val ord: Int) {
val newName = input.readString(1) MOBILE (0),
val newId = input.readInt64(2) HOME (1),
val newHasCatFlag = input.readBool(3) WORK (2);
return PersonMessage(newName, newId, newHasCatFlag)
}
fun getBuilder(): PersonBuilder { companion object {
return PersonBuilder() fun fromIntToPhoneType (ord: Int): PhoneType {
} return when (ord) {
0 -> PhoneType.MOBILE
// No interface, see above 1 -> PhoneType.HOME
class PersonBuilder { // : Message.Builder 2 -> PhoneType.WORK
// TODO: think how can we else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing PhoneType ");
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
} }
} }
} 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 <PhoneNumber> = arrayOf()) {
var name : kotlin.String?
var id : Int?
var email : kotlin.String?
var phones : Array <PhoneNumber>
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 <Person> = arrayOf()) {
var people : Array <Person>
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 <Person> = arrayOf()) {
var people : Array <Person>
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)
}
}
}
}
+3 -9
View File
@@ -5,18 +5,12 @@ import java.io.ByteArrayOutputStream
* Created by user on 7/8/16. * Created by user on 7/8/16.
*/ */
import PersonMessage
fun testMessageSerialization() { fun testMessageSerialization() {
val s = ByteArrayOutputStream() val s = ByteArrayOutputStream()
val outs = CodedOutputStream(s) val outs = CodedOutputStream(s)
val msg = Person( val msg =
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)
)
)
msg.writeTo(outs) msg.writeTo(outs)
val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))