diff --git a/proto/compiler/README.md b/proto/compiler/README.md index 2ba1d1c433e..1deeebe1ffa 100644 --- a/proto/compiler/README.md +++ b/proto/compiler/README.md @@ -22,7 +22,7 @@ In folder /src/ you can find sources for ProtoKot-runtime, that will be used by ## 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() diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.d b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.d deleted file mode 100644 index 095d3ebe60e..00000000000 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.d +++ /dev/null @@ -1,3 +0,0 @@ -bin/kotlin_class_generator.o: src/kotlin_class_generator.cc \ - src/kotlin_class_generator.h src/kotlin_field_generator.h \ - src/kotlin_enum_generator.h diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.o b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.o deleted file mode 100644 index ea513f2069d..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_class_generator.o and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.d b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.d deleted file mode 100644 index 443a361d921..00000000000 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.d +++ /dev/null @@ -1,2 +0,0 @@ -bin/kotlin_enum_generator.o: src/kotlin_enum_generator.cc \ - src/kotlin_enum_generator.h diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.o b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.o deleted file mode 100644 index acd6c6424c8..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_enum_generator.o and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.d b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.d deleted file mode 100644 index 7e7089bbf82..00000000000 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.d +++ /dev/null @@ -1,2 +0,0 @@ -bin/kotlin_field_generator.o: src/kotlin_field_generator.cc \ - src/kotlin_field_generator.h diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.o b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.o deleted file mode 100644 index a5945fe0345..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_field_generator.o and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.d b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.d deleted file mode 100644 index 04d6f449aab..00000000000 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.d +++ /dev/null @@ -1,3 +0,0 @@ -bin/kotlin_file_generator.o: src/kotlin_file_generator.cc \ - src/kotlin_file_generator.h src/kotlin_class_generator.h \ - src/kotlin_field_generator.h src/kotlin_enum_generator.h diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.o b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.o deleted file mode 100644 index a097a89d3ce..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/kotlin_file_generator.o and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.d b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.d deleted file mode 100644 index ab59dd96054..00000000000 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.d +++ /dev/null @@ -1,3 +0,0 @@ -bin/main.o: src/main.cc src/kotlin_file_generator.h \ - src/kotlin_class_generator.h src/kotlin_field_generator.h \ - src/kotlin_enum_generator.h diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.o b/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.o deleted file mode 100644 index 4fc05d90668..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/bin/main.o and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/protoc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/protoc deleted file mode 100755 index 33dfa788ef6..00000000000 Binary files a/proto/compiler/google/src/google/protobuf/compiler/kotlin/protoc and /dev/null differ diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc index 28838690de2..376c7aa5eec 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc @@ -22,7 +22,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { * Also note that fields should be declared before init section. */ for (FieldGenerator *gen: properties) { - gen->generateCode(printer, isBuilder); + gen->generateCode(printer, isBuilder, simpleName); printer->Print("\n"); } @@ -61,9 +61,6 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { if (isBuilder) { printer->Print("\n"); generateBuildMethod(printer); - - printer->Print("\n"); - generateSetters(printer); } printer->Outdent(); @@ -72,7 +69,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { ClassGenerator::ClassGenerator(Descriptor const *descriptor) : descriptor(descriptor) { - simpleName = descriptor->name(); // TODO: think about more careful class naming + simpleName = descriptor->name(); int field_count = descriptor->field_count(); for (int i = 0; i < field_count; ++i) { @@ -143,7 +140,6 @@ void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) con "\n"); printer->Indent(); - //TODO: write message tag and size printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n"); printer->Outdent(); @@ -234,14 +230,6 @@ 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 = ""; switch (type) { diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h index 6b9da71742f..27eeee7409c 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h @@ -47,7 +47,6 @@ 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 @@ -67,7 +66,7 @@ private: }; } // namespace kotlin -} // namspace compiler +} // namespace compiler } // namespace protobuf } // namespace google #endif //SRC_KOTLIN_CLASS_GENERATOR_H diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc index 1f976e31025..8d5bd9ad3b1 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc @@ -114,6 +114,6 @@ EnumGenerator::EnumGenerator(EnumDescriptor const *descriptor) { } } // namespace kotlin -} // namspace compiler +} // namespace compiler } // namespace protobuf } // namespace google diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h index 3265777a488..5e9c9f1b9b0 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h @@ -36,7 +36,7 @@ private: }; } // namespace kotlin -} // namspace compiler +} // namespace compiler } // namespace protobuf } // namespace google diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc index dcd9b714c44..7f7f13d78f2 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc @@ -6,6 +6,7 @@ #include #include #include +#include "kotlin_name_resolver.h" #include namespace google { @@ -28,12 +29,19 @@ string FieldGenerator::protobufToKotlinField() const { postamble = "?"; break; case FieldDescriptor::LABEL_REPEATED: + #ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW + preamble = "MutableList <"; + postamble = "> "; + break; + #else preamble = "Array <"; postamble = "> "; break; + #endif } return preamble + protobufToKotlinType() + postamble; } + /** * Simply maps protobuf field type to corresponding Kotlin. */ @@ -43,7 +51,7 @@ string FieldGenerator::protobufToKotlinType() const { case FieldDescriptor::TYPE_BOOL: return "Boolean"; case FieldDescriptor::TYPE_BYTES: - return ""; // TODO: support bytes type + return "ByteArray"; case FieldDescriptor::TYPE_DOUBLE: return "Double"; case FieldDescriptor::TYPE_ENUM: @@ -56,8 +64,6 @@ string FieldGenerator::protobufToKotlinType() const { return "Long"; case FieldDescriptor::TYPE_FLOAT: return "Float"; - case FieldDescriptor::TYPE_GROUP: - return ""; // @deprecated //TODO: make proper error handling there case FieldDescriptor::TYPE_INT32: return "Int"; case FieldDescriptor::TYPE_INT64: @@ -82,25 +88,73 @@ string FieldGenerator::protobufToKotlinType() const { } string FieldGenerator::getInitValue() const { - if (descriptor->is_optional()) { - return "null"; - } if (descriptor->is_repeated()) + #ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW + return "mutableListOf()"; + #else return "arrayOf()"; - return fullType + "()"; + #endif + + FieldDescriptor::Type type = descriptor->type(); + switch(type) { + case FieldDescriptor::TYPE_BOOL: + return "false"; + case FieldDescriptor::TYPE_BYTES: + return "ByteArray(0)"; + case FieldDescriptor::TYPE_DOUBLE: + return "0.0"; + case FieldDescriptor::TYPE_ENUM: { + string enumType = descriptor->enum_type()->name(); + return enumType + ".fromIntTo" + enumType + "(0)"; // produce enum from 0, as demanded by Google + } + case FieldDescriptor::TYPE_FIXED32: + return "0"; + case FieldDescriptor::TYPE_FIXED64: + return "0L"; + case FieldDescriptor::TYPE_FLOAT: + return "0f"; + case FieldDescriptor::TYPE_INT32: + return "0"; + case FieldDescriptor::TYPE_INT64: + return "0L"; + case FieldDescriptor::TYPE_MESSAGE: + return string(descriptor->message_type()->name()) + "()"; + case FieldDescriptor::TYPE_SFIXED32: + return "0"; + case FieldDescriptor::TYPE_SFIXED64: + return "0L"; + case FieldDescriptor::TYPE_SINT32: + return "0"; + case FieldDescriptor::TYPE_SINT64: + return "0L"; + case FieldDescriptor::TYPE_STRING: + return "\"\""; + case FieldDescriptor::TYPE_UINT32: + return "0"; // see notes for TYPE_FIXED32 + case FieldDescriptor::TYPE_UINT64: + return "0L"; // see notes for TYPE_FIXED64 + } } -void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const { +void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string className) const { map vars; vars["name"] = simpleName; vars["field"] = protobufToKotlinField(); printer->Print(vars, "var $name$ : $field$\n"); - // make setter private for fair classes - if (!isBuilder) { - printer->Indent(); - printer->Print("private set\n"); - printer->Outdent(); + // make setter private + printer->Indent(); + printer->Print("private set\n"); + printer->Outdent(); + + // generate setter for builder + if (isBuilder) { + generateSetter(printer, /* builderName = */ "Builder" + className); + } + + // generate additional methods for repeated fields + if (modifier == FieldDescriptor::LABEL_REPEATED) { + generateRepeatedMethods(printer, isBuilder, /* builderName = */ "Builder" + className); } } @@ -137,7 +191,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool 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 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 tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); printer->Print(vars, "val listSize = input.readInt32NoTag()\n"); printer->Print(vars, "for (i in 1..listSize) {\n"); printer->Indent(); @@ -146,8 +200,12 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead printer->Print("}\n"); } else { + // tag + printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); + // length - printer->Print(vars, "$arg$.writeInt32NoTag($fieldName$.size)\n"); + printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n"); + printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n"); // all elements printer->Print(vars, "for (item in $fieldName$) {\n"); @@ -201,11 +259,16 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { vars["fieldName"] = noTag ? "item" : simpleName; vars["maybeNoTag"] = noTag ? "NoTag" : ""; + vars["fieldNumber"] = std::to_string(descriptor->number()); if (isRead) { - printer->Print(vars, "$fieldName$.readFrom$maybeNoTag$(input)\n"); + printer->Print(vars, + "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n" + "$fieldName$.readFrom$maybeNoTag$(input)\n"); } else { - printer->Print(vars, "$fieldName$.writeTo$maybeNoTag$(output)\n"); + printer->Print(vars, + "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n" + "$fieldName$.writeTo$maybeNoTag$(output)\n"); } return; } @@ -217,9 +280,6 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead else { printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$)\n"); } - - - // TODO: support tricky types like enums/messages/repeated fields/etc } string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const { @@ -242,12 +302,10 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type return "Bool"; case FieldDescriptor::TYPE_STRING: return "String"; - case FieldDescriptor::TYPE_GROUP: - return ""; // deprecated // TODO: think about proper error handling here case FieldDescriptor::TYPE_MESSAGE: - return "Message"; // TODO: support messages + return "Message"; case FieldDescriptor::TYPE_BYTES: - return ""; // TODO: support bytes + return "Bytes"; case FieldDescriptor::TYPE_UINT32: return "UInt32"; case FieldDescriptor::TYPE_ENUM: @@ -265,25 +323,67 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const { map vars; - // TODO: refactor work with names into separate class - string camelCaseName = simpleName; - camelCaseName[0] = char(std::toupper(camelCaseName[0])); - vars["camelCaseName"] = camelCaseName; - vars["name"] = simpleName; + vars["camelCaseName"] = name_resolving::makeFirstLetterUpper(simpleName); + vars["fieldName"] = 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" + "$fieldName$ = value\n" "return this\n"); printer->Outdent(); printer->Print("}\n"); } +void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const { + map vars; + vars["elementType"] = underlyingType; + vars["arg"] = "value"; + vars["fieldName"] = simpleName; + vars["builderName"] = builderName; + + // generate indexed setter for builders + if (isBuilder) { + printer->Print(vars, "fun set$elementType$(index: Int, $arg$: $elementType$): $builderName$ {\n"); + printer->Indent(); + printer->Print(vars, "$fieldName$[index] = $arg$\n"); + printer->Print(vars, "return this\n"); + printer->Outdent(); + printer->Print("}\n"); + } + + #ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW + if (isBuilder) { + // generate single-add for builders + printer->Print(vars, "fun add$elementType$($arg$: $elementType$): $builderName$ {\n"); + printer->Indent(); + printer->Print(vars, "$fieldName$.add($arg$)\n" + "return this\n"); + printer->Outdent(); + printer->Print(vars, "}\n"); + + // generate addAll for builders + printer->Print(vars, "fun addAll$elementType$($arg$: Iterable<$elementType$>): $builderName$ {\n"); + printer->Indent(); + printer->Print(vars, "for (item in $arg$) {\n"); + printer->Indent(); + printer->Print(vars, "$fieldName$.add(item)\n"); + + printer->Outdent(); // for-loop + printer->Print("}\n"); + + printer->Print("return this\n"); + + printer->Outdent(); // function body + printer->Print("}\n"); + } + #endif +} + } // namespace kotlin -} // namspace compiler +} // namespace compiler } // namespace protobuf } // namespace google \ No newline at end of file diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h index e11c0dccec8..3e61f4beea4 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h @@ -30,7 +30,8 @@ private: * CodedInputStream.readSFixed32(fieldNumber: Int) */ string protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const; - + void generateSetter(io::Printer * printer, string builderName) const; + void generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const; public: FieldDescriptor::Label modifier; string simpleName; @@ -45,15 +46,14 @@ public: string fullType; string initValue; - void generateCode(io::Printer * printer, bool isBuilder = false) const; + void generateCode(io::Printer * printer, bool isBuilder, string className) 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); }; } // namespace kotlin -} // namspace compiler +} // namespace compiler } // namespace protobuf } // namespace google diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc index 5ff54c22c5d..ee36d46f73d 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc @@ -10,6 +10,7 @@ #endif #include "kotlin_file_generator.h" #include +#include "kotlin_name_resolver.h" namespace google { namespace protobuf { @@ -26,14 +27,7 @@ void FileGenerator::generateCode(io::Printer *printer, std::vector classes; - - //TODO: maybe wrap work with class names and stuff in separate class (like in Java implementation) - // Get output file name - string const proto_file_name = file->name(); - size_t file_extension_index = proto_file_name.find(".proto"); - string const file_name = proto_file_name.substr(0, file_extension_index) + ".kt"; - - + string const file_name = name_resolving::getKotlinOutputByProtoName(file->name()); google::protobuf::scoped_ptr output(context->Open(file_name)); io::Printer printer(output.get(), '$', /* annotation_collector = */ NULL); diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.cc new file mode 100644 index 00000000000..d0ff147e446 --- /dev/null +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.cc @@ -0,0 +1,36 @@ +// +// Created by user on 7/14/16. +// + +#include "kotlin_name_resolver.h" +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +namespace name_resolving { + +using namespace std; + +string makeFirstLetterUpper(std::string s) { + s[0] = std::toupper(s[0]); + return s; +} + +string getFileNameWithoutExtension(string fullName) { + size_t file_extension_index = fullName.find_last_of("."); + return fullName.substr(0, file_extension_index); +} + +string getKotlinOutputByProtoName(string protoName) { + string justName = getFileNameWithoutExtension(protoName); + return justName + ".kt"; +} + +} // namespace name_resolving +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google \ No newline at end of file diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.h new file mode 100644 index 00000000000..ddf0453598b --- /dev/null +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_name_resolver.h @@ -0,0 +1,29 @@ +// +// Created by user on 7/14/16. +// + +#ifndef GOOGLE_KOTLIN_NAME_RESOLVER_H +#define GOOGLE_KOTLIN_NAME_RESOLVER_H + +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { +namespace name_resolving { + +std::string makeFirstLetterUpper(std::string s); + +std::string getFileNameWithoutExtension(std::string fullName); + +std::string getKotlinOutputByProtoName(std::string protoName); + +} // namespace name_resolving +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + + +#endif //GOOGLE_KOTLIN_NAME_RESOLVER_H diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/main.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/main.cc index 04e3267731a..9e4c462ef47 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/main.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/main.cc @@ -3,8 +3,6 @@ #include #include #include -// TODO(teboring): Add it back when php implementation is ready -// #include #include #include #include @@ -12,6 +10,7 @@ #include "kotlin_file_generator.h" #include +//#define KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW int main(int argc, const char* const * argv) { google::protobuf::compiler::CommandLineInterface cli; diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt index a35c9e9c4f6..8f90ec5b6f0 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt @@ -1,4 +1,4 @@ -class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { +class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { var name : kotlin.String? private set @@ -8,7 +8,10 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, var email : kotlin.String? private set - var phones : Array + var phones : MutableList + private set + + var someBytes : ByteArray? private set @@ -17,6 +20,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.id = id this.email = email this.phones = phones + this.someBytes = someBytes } enum class PhoneType(val ord: Int) { MOBILE (0), @@ -34,7 +38,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, } } } - class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) { + class PhoneNumber private constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { var number : kotlin.String? private set @@ -47,19 +51,29 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.type = type } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { output.writeString (1, number) output.writeEnum (2, type?.ord) } - class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) { + class BuilderPhoneNumber constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { var number : kotlin.String? + private set + fun setNumber(value: kotlin.String?): BuilderPhoneNumber { + number = value + return this + } var type : PhoneType? + private set + fun setType(value: PhoneType?): BuilderPhoneNumber { + type = value + return this + } init { @@ -67,29 +81,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.type = type } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderPhoneNumber { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber { number = input.readString(1) type = PhoneType.fromIntToPhoneType(input.readEnum(2)) - } + return this +} 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) { @@ -99,30 +103,75 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { output.writeString (1, name) output.writeInt32 (2, id) output.writeString (3, email) if (phones.size > 0) { + output.writeTag(4, WireType.LENGTH_DELIMITED) + output.writeInt32NoTag(phones.size) output.writeInt32NoTag(phones.size) for (item in phones) { + output.writeTag(4, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } + output.writeBytes (5, someBytes) } - class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { + class BuilderPerson constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { var name : kotlin.String? + private set + fun setName(value: kotlin.String?): BuilderPerson { + name = value + return this + } var id : Int? + private set + fun setId(value: Int?): BuilderPerson { + id = value + return this + } var email : kotlin.String? + private set + fun setEmail(value: kotlin.String?): BuilderPerson { + email = value + return this + } - var phones : Array + var phones : MutableList + private set + fun setPhones(value: MutableList ): BuilderPerson { + phones = value + return this + } + fun setPhoneNumber(index: Int, value: PhoneNumber): BuilderPerson { + phones[index] = value + return this + } + fun addPhoneNumber(value: PhoneNumber): BuilderPerson { + phones.add(value) + return this + } + fun addAllPhoneNumber(value: Iterable): BuilderPerson { + for (item in value) { + phones.add(item) + } + return this + } + + var someBytes : ByteArray? + private set + fun setSomeBytes(value: ByteArray?): BuilderPerson { + someBytes = value + return this + } init { @@ -130,49 +179,31 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.id = id this.email = email this.phones = phones + this.someBytes = someBytes } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderPerson { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderPerson { name = input.readString(1) id = input.readInt32(2) email = input.readString(3) if (phones.size > 0) { - val tag = input.readTag() + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { phones[i - 1].mergeFrom(input) } } - } + someBytes = input.readBytes(5) + return this +} fun build(): Person { - return Person(name, id, email, phones) + return Person(name, id, email, phones, someBytes) } - - fun setName(value: kotlin.String?): BuilderPerson { - name = value - return this - } - - fun setId(value: Int?): BuilderPerson { - id = value - return this - } - - fun setEmail(value: kotlin.String?): BuilderPerson { - email = value - return this - } - - fun setPhones(value: Array ): BuilderPerson { - phones = value - return this - } - } fun mergeFrom (input: CodedInputStream) { @@ -180,18 +211,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, id = input.readInt32(2) email = input.readString(3) if (phones.size > 0) { - val tag = input.readTag() + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { phones[i - 1].mergeFrom(input) } } + someBytes = input.readBytes(5) } } -class AddressBook private constructor (people: Array = arrayOf()) { - var people : Array +class AddressBook private constructor (people: MutableList = mutableListOf()) { + var people : MutableList private set @@ -199,55 +231,72 @@ class AddressBook private constructor (people: Array = arrayOf()) { this.people = people } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { if (people.size > 0) { + output.writeTag(1, WireType.LENGTH_DELIMITED) + output.writeInt32NoTag(people.size) output.writeInt32NoTag(people.size) for (item in people) { + output.writeTag(1, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } } - class BuilderAddressBook constructor (people: Array = arrayOf()) { - var people : Array + class BuilderAddressBook constructor (people: MutableList = mutableListOf()) { + var people : MutableList + private set + fun setPeople(value: MutableList ): BuilderAddressBook { + people = value + return this + } + fun setPerson(index: Int, value: Person): BuilderAddressBook { + people[index] = value + return this + } + fun addPerson(value: Person): BuilderAddressBook { + people.add(value) + return this + } + fun addAllPerson(value: Iterable): BuilderAddressBook { + for (item in value) { + people.add(item) + } + return this + } init { this.people = people } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderAddressBook { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderAddressBook { if (people.size > 0) { - val tag = input.readTag() + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { people[i - 1].mergeFrom(input) } } - } + return this +} fun build(): AddressBook { return AddressBook(people) } - - fun setPeople(value: Array ): BuilderAddressBook { - people = value - return this - } - } fun mergeFrom (input: CodedInputStream) { if (people.size > 0) { - val tag = input.readTag() + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { people[i - 1].mergeFrom(input) diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.proto b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.proto index 23cc2f97e76..35bb149cd54 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.proto +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.proto @@ -38,6 +38,8 @@ message Person { } repeated PhoneNumber phones = 4; + + bytes someBytes = 5; } // Our address book file is just one of these. diff --git a/proto/compiler/src/CodedInputStream.kt b/proto/compiler/src/CodedInputStream.kt index 1168cd65d94..d60c3ddc290 100644 --- a/proto/compiler/src/CodedInputStream.kt +++ b/proto/compiler/src/CodedInputStream.kt @@ -11,7 +11,7 @@ import java.nio.ByteOrder * */ -// TODO: decide, if we want to optimize for performance (then we have to deal with spaghetti-code) +// TODO: refactor correctness checks into readTag class CodedInputStream(input: java.io.InputStream) { val bufferedInput: java.io.BufferedInputStream @@ -20,7 +20,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readInt32(expectedFieldNumber: Int): Int { - val tag = readTag() + val tag = readTag(expectedFieldNumber, WireType.VARINT) val actualFieldNumber = WireFormat.getTagFieldNumber(tag) val actualWireType = WireFormat.getTagWireType(tag) checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType) @@ -31,10 +31,7 @@ class CodedInputStream(input: java.io.InputStream) { // simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all // methods reading unsigned ints simply redirect call to corresponding signed-reading method fun readUInt32(expectedFieldNumber: Int): Int { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readUInt32NoTag() } @@ -43,19 +40,13 @@ class CodedInputStream(input: java.io.InputStream) { } fun readInt64(expectedFieldNumber: Int): Long { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readInt64NoTag() } // See note on unsigned integers implementations above fun readUInt64(expectedFieldNumber: Int): Long { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readUInt64NoTag() } @@ -64,10 +55,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readBool(expectedFieldNumber: Int): Boolean { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readBoolNoTag() } @@ -83,10 +71,7 @@ class CodedInputStream(input: java.io.InputStream) { // Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object fun readEnum(expectedFieldNumber: Int): Int { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readEnumNoTag() } @@ -95,10 +80,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readSInt32(expectedFieldNumber: Int): Int { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readSInt32NoTag() } @@ -107,18 +89,12 @@ class CodedInputStream(input: java.io.InputStream) { } fun readSInt64(expectedFieldNumber: Int): Long { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.VARINT) return readZigZag64NoTag() } fun readFixed32(expectedFieldNumber: Int): Int { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_32) return readFixed32NoInt() } @@ -127,10 +103,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readSFixed32(expectedFieldNumber: Int): Int { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_32) return readSFixed32NoTag() } @@ -139,10 +112,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readFixed64(expectedFieldNumber: Int): Long { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_64) return readFixed64NoTag() } @@ -151,10 +121,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readSFixed64(expectedFieldNumber: Int): Long { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_64) return readSFixed64NoTag() } @@ -163,10 +130,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readDouble(expectedFieldNumber: Int): Double { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_64) return readDoubleNoTag() } @@ -175,10 +139,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readFloat(expectedFieldNumber: Int): Float { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.FIX_32) return readFloatNoTag() } @@ -187,10 +148,7 @@ class CodedInputStream(input: java.io.InputStream) { } fun readString(expectedFieldNumber: Int): String { - val tag = readTag() - val actualFieldNumber = WireFormat.getTagFieldNumber(tag) - val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType) + val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED) return readStringNoTag() } @@ -199,6 +157,17 @@ class CodedInputStream(input: java.io.InputStream) { val value = String(readRawBytes(length)) return value } + + fun readBytes(expectedFieldNumber: Int): ByteArray { + val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED) + return readBytesNoTag() + } + + fun readBytesNoTag(): ByteArray { + val length = readInt32NoTag() + return readRawBytes(length) + } + /** ============ Utility methods ================== * They are left non-private for cases when one wants to implement her/his own protocol format. * Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags. @@ -254,7 +223,7 @@ class CodedInputStream(input: java.io.InputStream) { } // reads tag. Note that it returns 0 for the end of message! - fun readTag(): Int { + fun readTag(expectedFieldNumber: Int, expectedWireType: WireType): Int { if (isAtEnd()) { return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal } @@ -262,6 +231,10 @@ class CodedInputStream(input: java.io.InputStream) { if (tag == 0) { // if we somehow had read 0-tag, then message is corrupted throw InvalidProtocolBufferException("Invalid tag 0") } + + val actualFieldNumber = WireFormat.getTagFieldNumber(tag) + val actualWireType = WireFormat.getTagWireType(tag) + checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType) return tag } diff --git a/proto/compiler/src/CodedOutputStream.kt b/proto/compiler/src/CodedOutputStream.kt index d5e132bc344..ea1fc24f0d3 100644 --- a/proto/compiler/src/CodedOutputStream.kt +++ b/proto/compiler/src/CodedOutputStream.kt @@ -151,6 +151,22 @@ class CodedOutputStream(val output: java.io.OutputStream) { output.write(value.toByteArray()) } + fun writeBytes(fieldNumber: Int, value: ByteArray?) { + value ?: return + writeTag(fieldNumber, WireType.LENGTH_DELIMITED) + writeBytesNoTag(value) + } + + fun writeBytesNoTag(value: ByteArray) { + writeInt32NoTag(value.size) + output.write(value) + } + + /** ============ Utility methods ================== + * They are left non-private for cases when one wants to implement her/his own protocol format. + * Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags. + */ + fun writeLittleEndian(value: Int?) { value ?: return val bb = ByteBuffer.allocate(4) diff --git a/proto/compiler/src/PersonMessage.kt b/proto/compiler/src/PersonMessage.kt index e29e0fb689b..cde8860f6a1 100644 --- a/proto/compiler/src/PersonMessage.kt +++ b/proto/compiler/src/PersonMessage.kt @@ -1,6 +1,4 @@ -package Addressbook; - -class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { +class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { var name : kotlin.String? private set @@ -10,7 +8,10 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, var email : kotlin.String? private set - var phones : Array + var phones : MutableList + private set + + var someBytes : ByteArray? private set @@ -19,8 +20,8 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.id = id this.email = email this.phones = phones + this.someBytes = someBytes } - enum class PhoneType(val ord: Int) { MOBILE (0), HOME (1), @@ -37,7 +38,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, } } } - class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) { + class PhoneNumber private constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { var number : kotlin.String? private set @@ -50,19 +51,29 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.type = type } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { output.writeString (1, number) output.writeEnum (2, type?.ord) } - class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) { + class BuilderPhoneNumber constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { var number : kotlin.String? + private set + fun setNumber(value: kotlin.String?): BuilderPhoneNumber { + number = value + return this + } var type : PhoneType? + private set + fun setType(value: PhoneType?): BuilderPhoneNumber { + type = value + return this + } init { @@ -70,13 +81,14 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.type = type } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderPhoneNumber { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber { number = input.readString(1) type = PhoneType.fromIntToPhoneType(input.readEnum(2)) + return this } fun build(): PhoneNumber { @@ -91,30 +103,75 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { output.writeString (1, name) output.writeInt32 (2, id) output.writeString (3, email) if (phones.size > 0) { + output.writeTag(4, WireType.LENGTH_DELIMITED) + output.writeInt32NoTag(phones.size) output.writeInt32NoTag(phones.size) for (item in phones) { + output.writeTag(4, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } + output.writeBytes (5, someBytes) } - class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array = arrayOf()) { + class BuilderPerson constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { var name : kotlin.String? + private set + fun setName(value: kotlin.String?): BuilderPerson { + name = value + return this + } var id : Int? + private set + fun setId(value: Int?): BuilderPerson { + id = value + return this + } var email : kotlin.String? + private set + fun setEmail(value: kotlin.String?): BuilderPerson { + email = value + return this + } - var phones : Array + var phones : MutableList + private set + fun setPhones(value: MutableList ): BuilderPerson { + phones = value + return this + } + fun setPhoneNumber(index: Int, value: PhoneNumber): BuilderPerson { + phones[index] = value + return this + } + fun addPhoneNumber(value: PhoneNumber): BuilderPerson { + phones.add(value) + return this + } + fun addAllPhoneNumber(value: Iterable): BuilderPerson { + for (item in value) { + phones.add(item) + } + return this + } + + var someBytes : ByteArray? + private set + fun setSomeBytes(value: ByteArray?): BuilderPerson { + someBytes = value + return this + } init { @@ -122,27 +179,30 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, this.id = id this.email = email this.phones = phones + this.someBytes = someBytes } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderPerson { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderPerson { name = input.readString(1) id = input.readInt32(2) email = input.readString(3) if (phones.size > 0) { - val tag = input.readTag() + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { phones[i - 1].mergeFrom(input) } } + someBytes = input.readBytes(5) + return this } fun build(): Person { - return Person(name, id, email, phones) + return Person(name, id, email, phones, someBytes) } } @@ -151,18 +211,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null, id = input.readInt32(2) email = input.readString(3) if (phones.size > 0) { - val tag = input.readTag() + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { phones[i - 1].mergeFrom(input) } } + someBytes = input.readBytes(5) } } -class AddressBook private constructor (people: Array = arrayOf()) { - var people : Array +class AddressBook private constructor (people: MutableList = mutableListOf()) { + var people : MutableList private set @@ -170,39 +231,62 @@ class AddressBook private constructor (people: Array = arrayOf()) { this.people = people } - fun writeTo (output: CodedOutputStream) { + fun writeTo (output: CodedOutputStream): Unit { writeToNoTag(output) } - fun writeToNoTag (output: CodedOutputStream) { + fun writeToNoTag (output: CodedOutputStream): Unit { if (people.size > 0) { + output.writeTag(1, WireType.LENGTH_DELIMITED) + output.writeInt32NoTag(people.size) output.writeInt32NoTag(people.size) for (item in people) { + output.writeTag(1, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } } - class BuilderAddressBook constructor (people: Array = arrayOf()) { - var people : Array + class BuilderAddressBook constructor (people: MutableList = mutableListOf()) { + var people : MutableList + private set + fun setPeople(value: MutableList ): BuilderAddressBook { + people = value + return this + } + fun setPerson(index: Int, value: Person): BuilderAddressBook { + people[index] = value + return this + } + fun addPerson(value: Person): BuilderAddressBook { + people.add(value) + return this + } + fun addAllPerson(value: Iterable): BuilderAddressBook { + for (item in value) { + people.add(item) + } + return this + } init { this.people = people } - fun readFrom (input: CodedInputStream) { - readFromNoTag(input) + fun readFrom (input: CodedInputStream): BuilderAddressBook { + return readFromNoTag(input) } - fun readFromNoTag (input: CodedInputStream) { + fun readFromNoTag (input: CodedInputStream): BuilderAddressBook { if (people.size > 0) { - val tag = input.readTag() + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { people[i - 1].mergeFrom(input) } } + return this } fun build(): AddressBook { @@ -212,7 +296,7 @@ class AddressBook private constructor (people: Array = arrayOf()) { fun mergeFrom (input: CodedInputStream) { if (people.size > 0) { - val tag = input.readTag() + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val listSize = input.readInt32NoTag() for (i in 1..listSize) { people[i - 1].mergeFrom(input) diff --git a/proto/compiler/src/TestMessages.kt b/proto/compiler/src/TestMessages.kt index b37fa3e48f5..c2d25d0f9af 100644 --- a/proto/compiler/src/TestMessages.kt +++ b/proto/compiler/src/TestMessages.kt @@ -5,18 +5,40 @@ import java.io.ByteArrayOutputStream * Created by user on 7/8/16. */ -import PersonMessage - fun testMessageSerialization() { + // 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) - val msg = + + // 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) - val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) - val readMsg = Person("", 0, "", arrayOf()) + // 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) }