aboutsummaryrefslogtreecommitdiffstats
path: root/src/google/protobuf/compiler/javamicro/javamicro_message.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/compiler/javamicro/javamicro_message.cc')
-rw-r--r--src/google/protobuf/compiler/javamicro/javamicro_message.cc474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/google/protobuf/compiler/javamicro/javamicro_message.cc b/src/google/protobuf/compiler/javamicro/javamicro_message.cc
new file mode 100644
index 0000000..7fc6c3d
--- /dev/null
+++ b/src/google/protobuf/compiler/javamicro/javamicro_message.cc
@@ -0,0 +1,474 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <algorithm>
+#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/compiler/javamicro/javamicro_message.h>
+#include <google/protobuf/compiler/javamicro/javamicro_enum.h>
+#include <google/protobuf/compiler/javamicro/javamicro_helpers.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/descriptor.pb.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace javamicro {
+
+using internal::WireFormat;
+using internal::WireFormatLite;
+
+namespace {
+
+void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
+ // Print the field's proto-syntax definition as a comment. We don't want to
+ // print group bodies so we cut off after the first line.
+ string def = field->DebugString();
+ printer->Print("// $def$\n",
+ "def", def.substr(0, def.find_first_of('\n')));
+}
+
+struct FieldOrderingByNumber {
+ inline bool operator()(const FieldDescriptor* a,
+ const FieldDescriptor* b) const {
+ return a->number() < b->number();
+ }
+};
+
+// Sort the fields of the given Descriptor by number into a new[]'d array
+// and return it.
+const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
+ const FieldDescriptor** fields =
+ new const FieldDescriptor*[descriptor->field_count()];
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ fields[i] = descriptor->field(i);
+ }
+ sort(fields, fields + descriptor->field_count(),
+ FieldOrderingByNumber());
+ return fields;
+}
+
+// Get an identifier that uniquely identifies this type within the file.
+// This is used to declare static variables related to this type at the
+// outermost file scope.
+string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
+ return "static_" + StringReplace(descriptor->full_name(), ".", "_", true);
+}
+
+// Returns true if the message type has any required fields. If it doesn't,
+// we can optimize out calls to its isInitialized() method.
+//
+// already_seen is used to avoid checking the same type multiple times
+// (and also to protect against recursion).
+static bool HasRequiredFields(
+ const Descriptor* type,
+ hash_set<const Descriptor*>* already_seen) {
+ if (already_seen->count(type) > 0) {
+ // The type is already in cache. This means that either:
+ // a. The type has no required fields.
+ // b. We are in the midst of checking if the type has required fields,
+ // somewhere up the stack. In this case, we know that if the type
+ // has any required fields, they'll be found when we return to it,
+ // and the whole call to HasRequiredFields() will return true.
+ // Therefore, we don't have to check if this type has required fields
+ // here.
+ return false;
+ }
+ already_seen->insert(type);
+
+ // If the type has extensions, an extension with message type could contain
+ // required fields, so we have to be conservative and assume such an
+ // extension exists.
+ if (type->extension_range_count() > 0) return true;
+
+ for (int i = 0; i < type->field_count(); i++) {
+ const FieldDescriptor* field = type->field(i);
+ if (field->is_required()) {
+ return true;
+ }
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (HasRequiredFields(field->message_type(), already_seen)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool HasRequiredFields(const Descriptor* type) {
+ hash_set<const Descriptor*> already_seen;
+ return HasRequiredFields(type, &already_seen);
+}
+
+} // namespace
+
+// ===================================================================
+
+MessageGenerator::MessageGenerator(const Descriptor* descriptor, const Params& params)
+ : params_(params),
+ descriptor_(descriptor),
+ field_generators_(descriptor, params) {
+}
+
+MessageGenerator::~MessageGenerator() {}
+
+void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
+ // Generate static members for all nested types.
+ for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+ // TODO(kenton): Reuse MessageGenerator objects?
+ MessageGenerator(descriptor_->nested_type(i), params_)
+ .GenerateStaticVariables(printer);
+ }
+}
+
+void MessageGenerator::GenerateStaticVariableInitializers(
+ io::Printer* printer) {
+ // Generate static member initializers for all nested types.
+ for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+ // TODO(kenton): Reuse MessageGenerator objects?
+ MessageGenerator(descriptor_->nested_type(i), params_)
+ .GenerateStaticVariableInitializers(printer);
+ }
+
+ if (descriptor_->extension_count() != 0) {
+ GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
+ }
+}
+
+void MessageGenerator::Generate(io::Printer* printer) {
+ bool is_own_file =
+ params_.java_multiple_files() || ((descriptor_->containing_type() == NULL)
+ && !params_.has_java_outer_classname(descriptor_->file()->name()));
+
+#if 0
+ GOOGLE_LOG(INFO) << "is_own_file=" << is_own_file;
+ GOOGLE_LOG(INFO) << "containing_type()=" << ((descriptor_->containing_type() == NULL) ? "NULL" : "not null");
+ GOOGLE_LOG(INFO) << "java_multiple_files()=" << params_.java_multiple_files();
+ GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name());
+#endif
+
+ if ((descriptor_->extension_count() != 0)
+ || (descriptor_->extension_range_count() != 0)) {
+ GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
+ }
+
+ printer->Print(
+ "public $modifiers$ final class $classname$ extends\n"
+ " com.google.protobuf.micro.MessageMicro {\n",
+ "modifiers", is_own_file ? "" : "static",
+ "classname", descriptor_->name());
+ printer->Indent();
+ printer->Print(
+ "public $classname$() {}\n"
+ "\n",
+ "classname", descriptor_->name());
+
+ // Nested types and extensions
+ for (int i = 0; i < descriptor_->enum_type_count(); i++) {
+ EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer);
+ }
+
+ for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+ MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer);
+ }
+
+ // Fields
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ PrintFieldComment(printer, descriptor_->field(i));
+ printer->Print("public static final int $constant_name$ = $number$;\n",
+ "constant_name", FieldConstantName(descriptor_->field(i)),
+ "number", SimpleItoa(descriptor_->field(i)->number()));
+ field_generators_.get(descriptor_->field(i)).GenerateMembers(printer);
+ printer->Print("\n");
+ }
+
+ GenerateClear(printer);
+ GenerateIsInitialized(printer);
+ GenerateMessageSerializationMethods(printer);
+ GenerateMergeFromMethods(printer);
+ GenerateParseFromMethods(printer);
+
+ printer->Outdent();
+ printer->Print("}\n\n");
+}
+
+// ===================================================================
+
+void MessageGenerator::
+GenerateMessageSerializationMethods(io::Printer* printer) {
+ scoped_array<const FieldDescriptor*> sorted_fields(
+ SortFieldsByNumber(descriptor_));
+
+ if (descriptor_->extension_range_count() != 0) {
+ GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
+ }
+
+ printer->Print(
+ "public void writeTo(com.google.protobuf.micro.CodedOutputStreamMicro output)\n"
+ " throws java.io.IOException {\n");
+ printer->Indent();
+
+ // Output the fields in sorted order
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ GenerateSerializeOneField(printer, sorted_fields[i]);
+ }
+
+ printer->Outdent();
+ printer->Print(
+ "}\n"
+ "\n"
+ "private int cachedSize = -1;\n"
+ "public int getCachedSize() {\n"
+ " if (cachedSize < 0) {\n"
+ " // getSerializedSize sets cachedSize\n"
+ " getSerializedSize();\n"
+ " }\n"
+ " return cachedSize;\n"
+ "}\n"
+ "\n"
+ "public int getSerializedSize() {\n"
+ " int size = 0;\n");
+ printer->Indent();
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
+ }
+
+ printer->Outdent();
+ printer->Print(
+ " cachedSize = size;\n"
+ " return size;\n"
+ "}\n"
+ "\n");
+}
+
+void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
+ scoped_array<const FieldDescriptor*> sorted_fields(
+ SortFieldsByNumber(descriptor_));
+
+ if (params_.java_use_vector()) {
+ printer->Print(
+ "public com.google.protobuf.micro.MessageMicro mergeFrom(\n"
+ " com.google.protobuf.micro.CodedInputStreamMicro input)\n"
+ " throws java.io.IOException {\n",
+ "classname", descriptor_->name());
+ } else {
+ printer->Print(
+ "public $classname$ mergeFrom(\n"
+ " com.google.protobuf.micro.CodedInputStreamMicro input)\n"
+ " throws java.io.IOException {\n",
+ "classname", descriptor_->name());
+ }
+ printer->Indent();
+
+ printer->Print(
+ "while (true) {\n");
+ printer->Indent();
+
+ printer->Print(
+ "int tag = input.readTag();\n"
+ "switch (tag) {\n");
+ printer->Indent();
+
+ printer->Print(
+ "case 0:\n" // zero signals EOF / limit reached
+ " return this;\n"
+ "default: {\n"
+ " if (!parseUnknownField(input, tag)) {\n"
+ " return this;\n" // it's an endgroup tag
+ " }\n"
+ " break;\n"
+ "}\n");
+
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = sorted_fields[i];
+ uint32 tag = WireFormatLite::MakeTag(field->number(),
+ WireFormat::WireTypeForField(field));
+
+ printer->Print(
+ "case $tag$: {\n",
+ "tag", SimpleItoa(tag));
+ printer->Indent();
+
+ field_generators_.get(field).GenerateParsingCode(printer);
+
+ printer->Outdent();
+ printer->Print(
+ " break;\n"
+ "}\n");
+ }
+
+ printer->Outdent();
+ printer->Outdent();
+ printer->Outdent();
+ printer->Print(
+ " }\n" // switch (tag)
+ " }\n" // while (true)
+ "}\n"
+ "\n");
+}
+
+void MessageGenerator::
+GenerateParseFromMethods(io::Printer* printer) {
+ bool is_own_file =
+ descriptor_->containing_type() == NULL;
+
+ // Note: These are separate from GenerateMessageSerializationMethods()
+ // because they need to be generated even for messages that are optimized
+ // for code size.
+ printer->Print(
+ "public $static$ $classname$ parseFrom(byte[] data)\n"
+ " throws com.google.protobuf.micro.InvalidProtocolBufferMicroException {\n"
+ " return ($classname$) (new $classname$().mergeFrom(data));\n"
+ "}\n"
+ "\n"
+ "public $static$ $classname$ parseFrom(\n"
+ " com.google.protobuf.micro.CodedInputStreamMicro input)\n"
+ " throws java.io.IOException {\n"
+ " return ($classname$) (new $classname$().mergeFrom(input));\n"
+ "}\n"
+ "\n",
+ "static", (is_own_file ? "static" : ""),
+ "classname", descriptor_->name());
+}
+
+void MessageGenerator::GenerateSerializeOneField(
+ io::Printer* printer, const FieldDescriptor* field) {
+ field_generators_.get(field).GenerateSerializationCode(printer);
+}
+
+void MessageGenerator::GenerateClear(io::Printer* printer) {
+ printer->Print(
+ "public final $classname$ clear() {\n",
+ "classname", descriptor_->name());
+ printer->Indent();
+
+ // Call clear for all of the fields.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+
+ printer->Print(
+ "clear$name$();\n",
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ }
+
+ printer->Outdent();
+ printer->Print(
+ " cachedSize = -1;\n"
+ " return this;\n"
+ "}\n"
+ "\n");
+}
+
+
+void MessageGenerator::GenerateIsInitialized(io::Printer* printer) {
+ printer->Print(
+ "public final boolean isInitialized() {\n");
+ printer->Indent();
+
+ // Check that all required fields in this message are set.
+ // TODO(kenton): We can optimize this when we switch to putting all the
+ // "has" fields into a single bitfield.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+
+ if (field->is_required()) {
+ printer->Print(
+ "if (!has$name$) return false;\n",
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ }
+ }
+
+ // Now check that all embedded messages are initialized.
+ for (int i = 0; i < descriptor_->field_count(); i++) {
+ const FieldDescriptor* field = descriptor_->field(i);
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ HasRequiredFields(field->message_type())) {
+ switch (field->label()) {
+ case FieldDescriptor::LABEL_REQUIRED:
+ printer->Print(
+ "if (!get$name$().isInitialized()) return false;\n",
+ "type", ClassName(params_, field->message_type()),
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ break;
+ case FieldDescriptor::LABEL_OPTIONAL:
+ printer->Print(
+ "if (has$name$()) {\n"
+ " if (!get$name$().isInitialized()) return false;\n"
+ "}\n",
+ "type", ClassName(params_, field->message_type()),
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ break;
+ case FieldDescriptor::LABEL_REPEATED:
+ if (params_.java_use_vector()) {
+ printer->Print(
+ "for (int i = 0; i < get$name$List().size(); i++) {\n"
+ " if (get$name$(i).isInitialized()) return false;\n"
+ "}\n",
+ "type", ClassName(params_, field->message_type()),
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ } else {
+ printer->Print(
+ "for ($type$ element : get$name$List()) {\n"
+ " if (!element.isInitialized()) return false;\n"
+ "}\n",
+ "type", ClassName(params_, field->message_type()),
+ "name", UnderscoresToCapitalizedCamelCase(field));
+ }
+ break;
+ }
+ }
+ }
+
+ if (descriptor_->extension_range_count() > 0) {
+ printer->Print(
+ "if (!extensionsAreInitialized()) return false;\n");
+ }
+
+ printer->Outdent();
+ printer->Print(
+ " return true;\n"
+ "}\n"
+ "\n");
+}
+
+// ===================================================================
+
+} // namespace javamicro
+} // namespace compiler
+} // namespace protobuf
+} // namespace google