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.
|
* 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) {
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
/**
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
|||||||
Reference in New Issue
Block a user