javamicro_message.cc revision ede38fe9b9f93888e6e41afc7abb09525f44da95
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32//  Based on original Protocol Buffers design by
33//  Sanjay Ghemawat, Jeff Dean, and others.
34
35#include <algorithm>
36#include <google/protobuf/stubs/hash.h>
37#include <google/protobuf/compiler/javamicro/javamicro_message.h>
38#include <google/protobuf/compiler/javamicro/javamicro_enum.h>
39#include <google/protobuf/compiler/javamicro/javamicro_helpers.h>
40#include <google/protobuf/stubs/strutil.h>
41#include <google/protobuf/io/printer.h>
42#include <google/protobuf/io/coded_stream.h>
43#include <google/protobuf/wire_format.h>
44#include <google/protobuf/descriptor.pb.h>
45
46namespace google {
47namespace protobuf {
48namespace compiler {
49namespace javamicro {
50
51using internal::WireFormat;
52using internal::WireFormatLite;
53
54namespace {
55
56void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
57  // Print the field's proto-syntax definition as a comment.  We don't want to
58  // print group bodies so we cut off after the first line.
59  string def = field->DebugString();
60  printer->Print("// $def$\n",
61    "def", def.substr(0, def.find_first_of('\n')));
62}
63
64struct FieldOrderingByNumber {
65  inline bool operator()(const FieldDescriptor* a,
66                         const FieldDescriptor* b) const {
67    return a->number() < b->number();
68  }
69};
70
71// Sort the fields of the given Descriptor by number into a new[]'d array
72// and return it.
73const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
74  const FieldDescriptor** fields =
75    new const FieldDescriptor*[descriptor->field_count()];
76  for (int i = 0; i < descriptor->field_count(); i++) {
77    fields[i] = descriptor->field(i);
78  }
79  sort(fields, fields + descriptor->field_count(),
80       FieldOrderingByNumber());
81  return fields;
82}
83
84// Get an identifier that uniquely identifies this type within the file.
85// This is used to declare static variables related to this type at the
86// outermost file scope.
87string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
88  return "static_" + StringReplace(descriptor->full_name(), ".", "_", true);
89}
90
91// Returns true if the message type has any required fields.  If it doesn't,
92// we can optimize out calls to its isInitialized() method.
93//
94// already_seen is used to avoid checking the same type multiple times
95// (and also to protect against recursion).
96static bool HasRequiredFields(
97    const Descriptor* type,
98    hash_set<const Descriptor*>* already_seen) {
99  if (already_seen->count(type) > 0) {
100    // The type is already in cache.  This means that either:
101    // a. The type has no required fields.
102    // b. We are in the midst of checking if the type has required fields,
103    //    somewhere up the stack.  In this case, we know that if the type
104    //    has any required fields, they'll be found when we return to it,
105    //    and the whole call to HasRequiredFields() will return true.
106    //    Therefore, we don't have to check if this type has required fields
107    //    here.
108    return false;
109  }
110  already_seen->insert(type);
111
112  // If the type has extensions, an extension with message type could contain
113  // required fields, so we have to be conservative and assume such an
114  // extension exists.
115  if (type->extension_range_count() > 0) return true;
116
117  for (int i = 0; i < type->field_count(); i++) {
118    const FieldDescriptor* field = type->field(i);
119    if (field->is_required()) {
120      return true;
121    }
122    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
123      if (HasRequiredFields(field->message_type(), already_seen)) {
124        return true;
125      }
126    }
127  }
128
129  return false;
130}
131
132static bool HasRequiredFields(const Descriptor* type) {
133  hash_set<const Descriptor*> already_seen;
134  return HasRequiredFields(type, &already_seen);
135}
136
137}  // namespace
138
139// ===================================================================
140
141MessageGenerator::MessageGenerator(const Descriptor* descriptor, const Params& params)
142  : params_(params),
143    descriptor_(descriptor),
144    field_generators_(descriptor, params) {
145}
146
147MessageGenerator::~MessageGenerator() {}
148
149void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
150  // Generate static members for all nested types.
151  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
152    // TODO(kenton):  Reuse MessageGenerator objects?
153    MessageGenerator(descriptor_->nested_type(i), params_)
154      .GenerateStaticVariables(printer);
155  }
156}
157
158void MessageGenerator::GenerateStaticVariableInitializers(
159    io::Printer* printer) {
160  // Generate static member initializers for all nested types.
161  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
162    // TODO(kenton):  Reuse MessageGenerator objects?
163    MessageGenerator(descriptor_->nested_type(i), params_)
164      .GenerateStaticVariableInitializers(printer);
165  }
166
167  if (descriptor_->extension_count() != 0) {
168    GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
169  }
170}
171
172void MessageGenerator::Generate(io::Printer* printer) {
173  bool is_own_file =
174    params_.java_multiple_files() || ((descriptor_->containing_type() == NULL)
175        && !params_.has_java_outer_classname(descriptor_->file()->name()));
176
177#if 0
178  GOOGLE_LOG(INFO) << "is_own_file=" << is_own_file;
179  GOOGLE_LOG(INFO) << "containing_type()=" << ((descriptor_->containing_type() == NULL) ? "NULL" : "not null");
180  GOOGLE_LOG(INFO) << "java_multiple_files()=" << params_.java_multiple_files();
181  GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name());
182#endif
183
184  if ((descriptor_->extension_count() != 0)
185      || (descriptor_->extension_range_count() != 0)) {
186    GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
187  }
188
189  printer->Print(
190    "public $modifiers$ final class $classname$ extends\n"
191    "    com.google.protobuf.micro.MessageMicro {\n",
192    "modifiers", is_own_file ? "" : "static",
193    "classname", descriptor_->name());
194  printer->Indent();
195  printer->Print(
196    "public $classname$() {}\n"
197    "\n",
198    "classname", descriptor_->name());
199
200  // Nested types and extensions
201  for (int i = 0; i < descriptor_->enum_type_count(); i++) {
202    EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer);
203  }
204
205  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
206    MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer);
207  }
208
209  // Fields
210  for (int i = 0; i < descriptor_->field_count(); i++) {
211    PrintFieldComment(printer, descriptor_->field(i));
212    printer->Print("public static final int $constant_name$ = $number$;\n",
213      "constant_name", FieldConstantName(descriptor_->field(i)),
214      "number", SimpleItoa(descriptor_->field(i)->number()));
215    field_generators_.get(descriptor_->field(i)).GenerateMembers(printer);
216    printer->Print("\n");
217  }
218
219  GenerateClear(printer);
220  GenerateIsInitialized(printer);
221  GenerateMessageSerializationMethods(printer);
222  GenerateMergeFromMethods(printer);
223  GenerateParseFromMethods(printer);
224
225  printer->Outdent();
226  printer->Print("}\n\n");
227}
228
229// ===================================================================
230
231void MessageGenerator::
232GenerateMessageSerializationMethods(io::Printer* printer) {
233  scoped_array<const FieldDescriptor*> sorted_fields(
234    SortFieldsByNumber(descriptor_));
235
236  if (descriptor_->extension_range_count() != 0) {
237    GOOGLE_LOG(FATAL) << "Extensions not supported in MICRO_RUNTIME\n";
238  }
239
240  printer->Print(
241    "public void writeTo(com.google.protobuf.micro.CodedOutputStreamMicro output)\n"
242    "                    throws java.io.IOException {\n");
243  printer->Indent();
244
245  // Output the fields in sorted order
246  for (int i = 0; i < descriptor_->field_count(); i++) {
247      GenerateSerializeOneField(printer, sorted_fields[i]);
248  }
249
250  printer->Outdent();
251  printer->Print(
252    "}\n"
253    "\n"
254    "private int cachedSize = -1;\n"
255    "public int getCachedSize() {\n"
256    "  if (cachedSize < 0) {\n"
257    "    // getSerializedSize sets cachedSize\n"
258    "    getSerializedSize();\n"
259    "  }\n"
260    "  return cachedSize;\n"
261    "}\n"
262    "\n"
263    "public int getSerializedSize() {\n"
264    "  int size = 0;\n");
265  printer->Indent();
266
267  for (int i = 0; i < descriptor_->field_count(); i++) {
268    field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
269  }
270
271  printer->Outdent();
272  printer->Print(
273    "  cachedSize = size;\n"
274    "  return size;\n"
275    "}\n"
276    "\n");
277}
278
279void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
280  scoped_array<const FieldDescriptor*> sorted_fields(
281    SortFieldsByNumber(descriptor_));
282
283  if (params_.java_use_vector()) {
284    printer->Print(
285      "public com.google.protobuf.micro.MessageMicro mergeFrom(\n"
286      "    com.google.protobuf.micro.CodedInputStreamMicro input)\n"
287      "    throws java.io.IOException {\n",
288      "classname", descriptor_->name());
289  } else {
290    printer->Print(
291      "public $classname$ mergeFrom(\n"
292      "    com.google.protobuf.micro.CodedInputStreamMicro input)\n"
293      "    throws java.io.IOException {\n",
294      "classname", descriptor_->name());
295  }
296  printer->Indent();
297
298  printer->Print(
299    "while (true) {\n");
300  printer->Indent();
301
302  printer->Print(
303    "int tag = input.readTag();\n"
304    "switch (tag) {\n");
305  printer->Indent();
306
307  printer->Print(
308    "case 0:\n"          // zero signals EOF / limit reached
309    "  return this;\n"
310    "default: {\n"
311    "  if (!parseUnknownField(input, tag)) {\n"
312    "    return this;\n"   // it's an endgroup tag
313    "  }\n"
314    "  break;\n"
315    "}\n");
316
317  for (int i = 0; i < descriptor_->field_count(); i++) {
318    const FieldDescriptor* field = sorted_fields[i];
319    uint32 tag = WireFormatLite::MakeTag(field->number(),
320      WireFormat::WireTypeForField(field));
321
322    printer->Print(
323      "case $tag$: {\n",
324      "tag", SimpleItoa(tag));
325    printer->Indent();
326
327    field_generators_.get(field).GenerateParsingCode(printer);
328
329    printer->Outdent();
330    printer->Print(
331      "  break;\n"
332      "}\n");
333  }
334
335  printer->Outdent();
336  printer->Outdent();
337  printer->Outdent();
338  printer->Print(
339    "    }\n"     // switch (tag)
340    "  }\n"       // while (true)
341    "}\n"
342    "\n");
343}
344
345void MessageGenerator::
346GenerateParseFromMethods(io::Printer* printer) {
347  bool is_own_file =
348    descriptor_->containing_type() == NULL;
349
350  // Note:  These are separate from GenerateMessageSerializationMethods()
351  //   because they need to be generated even for messages that are optimized
352  //   for code size.
353  printer->Print(
354    "public $static$ $classname$ parseFrom(byte[] data)\n"
355    "    throws com.google.protobuf.micro.InvalidProtocolBufferMicroException {\n"
356    "  return ($classname$) (new $classname$().mergeFrom(data));\n"
357    "}\n"
358    "\n"
359    "public $static$ $classname$ parseFrom(\n"
360    "        com.google.protobuf.micro.CodedInputStreamMicro input)\n"
361    "    throws java.io.IOException {\n"
362    "  return ($classname$) (new $classname$().mergeFrom(input));\n"
363    "}\n"
364    "\n",
365    "static", (is_own_file ? "static" : ""),
366    "classname", descriptor_->name());
367}
368
369void MessageGenerator::GenerateSerializeOneField(
370    io::Printer* printer, const FieldDescriptor* field) {
371  field_generators_.get(field).GenerateSerializationCode(printer);
372}
373
374void MessageGenerator::GenerateClear(io::Printer* printer) {
375  printer->Print(
376    "public final $classname$ clear() {\n",
377    "classname", descriptor_->name());
378  printer->Indent();
379
380  // Call clear for all of the fields.
381  for (int i = 0; i < descriptor_->field_count(); i++) {
382    const FieldDescriptor* field = descriptor_->field(i);
383
384    printer->Print(
385      "clear$name$();\n",
386      "name", UnderscoresToCapitalizedCamelCase(field));
387  }
388
389  printer->Outdent();
390  printer->Print(
391    "  cachedSize = -1;\n"
392    "  return this;\n"
393    "}\n"
394    "\n");
395}
396
397
398void MessageGenerator::GenerateIsInitialized(io::Printer* printer) {
399  printer->Print(
400    "public final boolean isInitialized() {\n");
401  printer->Indent();
402
403  // Check that all required fields in this message are set.
404  // TODO(kenton):  We can optimize this when we switch to putting all the
405  //   "has" fields into a single bitfield.
406  for (int i = 0; i < descriptor_->field_count(); i++) {
407    const FieldDescriptor* field = descriptor_->field(i);
408
409    if (field->is_required()) {
410      printer->Print(
411        "if (!has$name$) return false;\n",
412        "name", UnderscoresToCapitalizedCamelCase(field));
413    }
414  }
415
416  // Now check that all embedded messages are initialized.
417  for (int i = 0; i < descriptor_->field_count(); i++) {
418    const FieldDescriptor* field = descriptor_->field(i);
419    if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
420        HasRequiredFields(field->message_type())) {
421      switch (field->label()) {
422        case FieldDescriptor::LABEL_REQUIRED:
423          printer->Print(
424            "if (!get$name$().isInitialized()) return false;\n",
425            "type", ClassName(params_, field->message_type()),
426            "name", UnderscoresToCapitalizedCamelCase(field));
427          break;
428        case FieldDescriptor::LABEL_OPTIONAL:
429          printer->Print(
430            "if (has$name$()) {\n"
431            "  if (!get$name$().isInitialized()) return false;\n"
432            "}\n",
433            "type", ClassName(params_, field->message_type()),
434            "name", UnderscoresToCapitalizedCamelCase(field));
435          break;
436        case FieldDescriptor::LABEL_REPEATED:
437          if (params_.java_use_vector()) {
438            printer->Print(
439              "for (int i = 0; i < get$name$List().size(); i++) {\n"
440              "  if (get$name$(i).isInitialized()) return false;\n"
441              "}\n",
442              "type", ClassName(params_, field->message_type()),
443              "name", UnderscoresToCapitalizedCamelCase(field));
444          } else {
445            printer->Print(
446              "for ($type$ element : get$name$List()) {\n"
447              "  if (!element.isInitialized()) return false;\n"
448              "}\n",
449              "type", ClassName(params_, field->message_type()),
450              "name", UnderscoresToCapitalizedCamelCase(field));
451          }
452          break;
453      }
454    }
455  }
456
457  if (descriptor_->extension_range_count() > 0) {
458    printer->Print(
459      "if (!extensionsAreInitialized()) return false;\n");
460  }
461
462  printer->Outdent();
463  printer->Print(
464    "  return true;\n"
465    "}\n"
466    "\n");
467}
468
469// ===================================================================
470
471}  // namespace javamicro
472}  // namespace compiler
473}  // namespace protobuf
474}  // namespace google
475