Basic operations are now fully supported in the .proto-compiler. Added README.md with brief manual on usage
This commit is contained in:
@@ -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.
|
||||
* 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 <string, string> 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 = "";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <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
|
||||
} // namspace compiler
|
||||
} // namespace protobuf
|
||||
|
||||
@@ -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<underlyingType> 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);
|
||||
|
||||
};
|
||||
|
||||
@@ -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?
|
||||
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 <PhoneNumber> ) {
|
||||
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?
|
||||
@@ -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 <PhoneNumber> ): 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 <Person> ) {
|
||||
class AddressBook private constructor (people: Array <Person> = arrayOf()) {
|
||||
var people : Array <Person>
|
||||
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>
|
||||
|
||||
|
||||
@@ -205,6 +237,12 @@ class AddressBook private constructor (people: Array <Person> ) {
|
||||
fun build(): AddressBook {
|
||||
return AddressBook(people)
|
||||
}
|
||||
|
||||
fun setPeople(value: Array <Person> ): BuilderAddressBook {
|
||||
people = value
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun mergeFrom (input: CodedInputStream) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <PhoneNumber> = arrayOf()) {
|
||||
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
|
||||
}
|
||||
|
||||
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 ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()))
|
||||
|
||||
Reference in New Issue
Block a user