javanano_helpers.cc revision 64d8d8f89050c5ada85341f967af391f4716a7cb
1324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Protocol Buffers - Google's data interchange format
2324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Copyright 2008 Google Inc.  All rights reserved.
3324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// http://code.google.com/p/protobuf/
4324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//
5324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Redistribution and use in source and binary forms, with or without
6324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// modification, are permitted provided that the following conditions are
7324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// met:
8324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//
9324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//     * Redistributions of source code must retain the above copyright
10324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// notice, this list of conditions and the following disclaimer.
11324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//     * Redistributions in binary form must reproduce the above
12324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// copyright notice, this list of conditions and the following disclaimer
13324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// in the documentation and/or other materials provided with the
14324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// distribution.
15324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//     * Neither the name of Google Inc. nor the names of its
16324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// contributors may be used to endorse or promote products derived from
17324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// this software without specific prior written permission.
18324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//
19324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
31324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Author: kenton@google.com (Kenton Varda)
32324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//  Based on original Protocol Buffers design by
33324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver//  Sanjay Ghemawat, Jeff Dean, and others.
34324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
35324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <vector>
36324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
37324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/compiler/javanano/javanano_helpers.h>
38324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/compiler/javanano/javanano_params.h>
39324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/descriptor.pb.h>
40324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/stubs/strutil.h>
41324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/stubs/substitute.h>
42324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
43namespace google {
44namespace protobuf {
45namespace compiler {
46namespace javanano {
47
48const char kThickSeparator[] =
49  "// ===================================================================\n";
50const char kThinSeparator[] =
51  "// -------------------------------------------------------------------\n";
52
53namespace {
54
55const char* kDefaultPackage = "";
56
57const string& FieldName(const FieldDescriptor* field) {
58  // Groups are hacky:  The name of the field is just the lower-cased name
59  // of the group type.  In Java, though, we would like to retain the original
60  // capitalization of the type name.
61  if (field->type() == FieldDescriptor::TYPE_GROUP) {
62    return field->message_type()->name();
63  } else {
64    return field->name();
65  }
66}
67
68string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
69  string result;
70  // Note:  I distrust ctype.h due to locales.
71  for (int i = 0; i < input.size(); i++) {
72    if ('a' <= input[i] && input[i] <= 'z') {
73      if (cap_next_letter) {
74        result += input[i] + ('A' - 'a');
75      } else {
76        result += input[i];
77      }
78      cap_next_letter = false;
79    } else if ('A' <= input[i] && input[i] <= 'Z') {
80      if (i == 0 && !cap_next_letter) {
81        // Force first letter to lower-case unless explicitly told to
82        // capitalize it.
83        result += input[i] + ('a' - 'A');
84      } else {
85        // Capital letters after the first are left as-is.
86        result += input[i];
87      }
88      cap_next_letter = false;
89    } else if ('0' <= input[i] && input[i] <= '9') {
90      result += input[i];
91      cap_next_letter = true;
92    } else {
93      cap_next_letter = true;
94    }
95  }
96  return result;
97}
98
99}  // namespace
100
101string UnderscoresToCamelCase(const FieldDescriptor* field) {
102  return UnderscoresToCamelCaseImpl(FieldName(field), false);
103}
104
105string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
106  return UnderscoresToCamelCaseImpl(FieldName(field), true);
107}
108
109string UnderscoresToCamelCase(const MethodDescriptor* method) {
110  return UnderscoresToCamelCaseImpl(method->name(), false);
111}
112
113string StripProto(const string& filename) {
114  if (HasSuffixString(filename, ".protodevel")) {
115    return StripSuffixString(filename, ".protodevel");
116  } else {
117    return StripSuffixString(filename, ".proto");
118  }
119}
120
121string FileClassName(const Params& params, const FileDescriptor* file) {
122  string name;
123
124  if (params.has_java_outer_classname(file->name())) {
125      name = params.java_outer_classname(file->name());
126  } else {
127    if ((file->message_type_count() == 1)
128        || (file->enum_type_count() == 0)) {
129      // If no outer calls and only one message then
130      // use the message name as the file name
131      name = file->message_type(0)->name();
132    } else {
133      // Use the filename it self with underscores removed
134      // and a CamelCase style name.
135      string basename;
136      string::size_type last_slash = file->name().find_last_of('/');
137      if (last_slash == string::npos) {
138        basename = file->name();
139      } else {
140        basename = file->name().substr(last_slash + 1);
141      }
142      name = UnderscoresToCamelCaseImpl(StripProto(basename), true);
143    }
144  }
145
146  return name;
147}
148
149string FileJavaPackage(const Params& params, const FileDescriptor* file) {
150  if (params.has_java_package(file->name())) {
151    return params.java_package(file->name());
152  } else {
153    string result = kDefaultPackage;
154    if (!file->package().empty()) {
155      if (!result.empty()) result += '.';
156      result += file->package();
157    }
158    return result;
159  }
160}
161
162string ToJavaName(const Params& params, const string& full_name,
163    const FileDescriptor* file) {
164  string result;
165  if (params.java_multiple_files()) {
166    result = FileJavaPackage(params, file);
167  } else {
168    result = ClassName(params, file);
169  }
170  if (file->package().empty()) {
171    result += '.';
172    result += full_name;
173  } else {
174    // Strip the proto package from full_name since we've replaced it with
175    // the Java package. If there isn't an outer classname then strip it too.
176    int sizeToSkipPackageName = file->package().size();
177    int sizeToSkipOutClassName;
178    if (params.has_java_outer_classname(file->name())) {
179      sizeToSkipOutClassName = 0;
180    } else {
181      sizeToSkipOutClassName =
182                full_name.find_first_of('.', sizeToSkipPackageName + 1);
183    }
184    int sizeToSkip = sizeToSkipOutClassName > 0 ?
185            sizeToSkipOutClassName : sizeToSkipPackageName;
186    string class_name = full_name.substr(sizeToSkip + 1);
187    if (class_name == FileClassName(params, file)) {
188      // Done class_name is already present.
189    } else {
190      result += '.';
191      result += class_name;
192    }
193  }
194  return result;
195}
196
197string ClassName(const Params& params, const FileDescriptor* descriptor) {
198  string result = FileJavaPackage(params, descriptor);
199  if (!result.empty()) result += '.';
200  result += FileClassName(params, descriptor);
201  return result;
202}
203
204string ClassName(const Params& params, const EnumDescriptor* descriptor) {
205  string result;
206  const FileDescriptor* file = descriptor->file();
207  const string file_name = file->name();
208  const string full_name = descriptor->full_name();
209
210  // Remove enum class name as we use int's for enums
211  string base_name = full_name.substr(0, full_name.find_last_of('.'));
212
213  if (!file->package().empty()) {
214    if (file->package() == base_name.substr(0, file->package().size())) {
215      // Remove package name leaving just the parent class of the enum
216      int offset = file->package().size();
217      if (base_name.size() > offset) {
218        // Remove period between package and class name if there is a classname
219        offset += 1;
220      }
221      base_name = base_name.substr(offset);
222    } else {
223      GOOGLE_LOG(FATAL) << "Expected package name to start an enum";
224    }
225  }
226
227  // Construct the path name from the package and outer class
228
229  // Add the java package name if it exsits
230  if (params.has_java_package(file_name)) {
231    result += params.java_package(file_name);
232  }
233
234  // Add the outer classname if it exists
235  if (params.has_java_outer_classname(file_name)) {
236    if (!result.empty()) {
237      result += ".";
238    }
239    result += params.java_outer_classname(file_name);
240  }
241
242  // Create the full class name from the base and path
243  if (!base_name.empty()) {
244    if (!result.empty()) {
245      result += ".";
246    }
247    result += base_name;
248  }
249  return result;
250}
251
252string FieldConstantName(const FieldDescriptor *field) {
253  string name = field->name() + "_FIELD_NUMBER";
254  UpperString(&name);
255  return name;
256}
257
258string FieldDefaultConstantName(const FieldDescriptor *field) {
259  string name = field->name() + "_DEFAULT";
260  UpperString(&name);
261  return name;
262}
263
264JavaType GetJavaType(FieldDescriptor::Type field_type) {
265  switch (field_type) {
266    case FieldDescriptor::TYPE_INT32:
267    case FieldDescriptor::TYPE_UINT32:
268    case FieldDescriptor::TYPE_SINT32:
269    case FieldDescriptor::TYPE_FIXED32:
270    case FieldDescriptor::TYPE_SFIXED32:
271      return JAVATYPE_INT;
272
273    case FieldDescriptor::TYPE_INT64:
274    case FieldDescriptor::TYPE_UINT64:
275    case FieldDescriptor::TYPE_SINT64:
276    case FieldDescriptor::TYPE_FIXED64:
277    case FieldDescriptor::TYPE_SFIXED64:
278      return JAVATYPE_LONG;
279
280    case FieldDescriptor::TYPE_FLOAT:
281      return JAVATYPE_FLOAT;
282
283    case FieldDescriptor::TYPE_DOUBLE:
284      return JAVATYPE_DOUBLE;
285
286    case FieldDescriptor::TYPE_BOOL:
287      return JAVATYPE_BOOLEAN;
288
289    case FieldDescriptor::TYPE_STRING:
290      return JAVATYPE_STRING;
291
292    case FieldDescriptor::TYPE_BYTES:
293      return JAVATYPE_BYTES;
294
295    case FieldDescriptor::TYPE_ENUM:
296      return JAVATYPE_ENUM;
297
298    case FieldDescriptor::TYPE_GROUP:
299    case FieldDescriptor::TYPE_MESSAGE:
300      return JAVATYPE_MESSAGE;
301
302    // No default because we want the compiler to complain if any new
303    // types are added.
304  }
305
306  GOOGLE_LOG(FATAL) << "Can't get here.";
307  return JAVATYPE_INT;
308}
309
310const char* BoxedPrimitiveTypeName(JavaType type) {
311  switch (type) {
312    case JAVATYPE_INT    : return "java.lang.Integer";
313    case JAVATYPE_LONG   : return "java.lang.Long";
314    case JAVATYPE_FLOAT  : return "java.lang.Float";
315    case JAVATYPE_DOUBLE : return "java.lang.Double";
316    case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
317    case JAVATYPE_STRING : return "java.lang.String";
318    case JAVATYPE_BYTES  : return "byte[]";
319    case JAVATYPE_ENUM   : return "java.lang.Integer";
320    case JAVATYPE_MESSAGE: return NULL;
321
322    // No default because we want the compiler to complain if any new
323    // JavaTypes are added.
324  }
325
326  GOOGLE_LOG(FATAL) << "Can't get here.";
327  return NULL;
328}
329
330string EmptyArrayName(const Params& params, const FieldDescriptor* field) {
331  switch (GetJavaType(field)) {
332    case JAVATYPE_INT    : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
333    case JAVATYPE_LONG   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY";
334    case JAVATYPE_FLOAT  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY";
335    case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY";
336    case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY";
337    case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY";
338    case JAVATYPE_BYTES  : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY";
339    case JAVATYPE_ENUM   : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY";
340    case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY";
341
342    // No default because we want the compiler to complain if any new
343    // JavaTypes are added.
344  }
345
346  GOOGLE_LOG(FATAL) << "Can't get here.";
347  return "";
348}
349
350string DefaultValue(const Params& params, const FieldDescriptor* field) {
351  if (field->label() == FieldDescriptor::LABEL_REPEATED) {
352    return EmptyArrayName(params, field);
353  }
354
355  // Switch on cpp_type since we need to know which default_value_* method
356  // of FieldDescriptor to call.
357  switch (field->cpp_type()) {
358    case FieldDescriptor::CPPTYPE_INT32:
359      return SimpleItoa(field->default_value_int32());
360    case FieldDescriptor::CPPTYPE_UINT32:
361      // Need to print as a signed int since Java has no unsigned.
362      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
363    case FieldDescriptor::CPPTYPE_INT64:
364      return SimpleItoa(field->default_value_int64()) + "L";
365    case FieldDescriptor::CPPTYPE_UINT64:
366      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
367             "L";
368    case FieldDescriptor::CPPTYPE_DOUBLE:
369      return SimpleDtoa(field->default_value_double()) + "D";
370    case FieldDescriptor::CPPTYPE_FLOAT:
371      return SimpleFtoa(field->default_value_float()) + "F";
372    case FieldDescriptor::CPPTYPE_BOOL:
373      return field->default_value_bool() ? "true" : "false";
374    case FieldDescriptor::CPPTYPE_STRING:
375      if (!field->default_value_string().empty()) {
376        // Point it to the static final in the generated code.
377        return FieldDefaultConstantName(field);
378      } else {
379        if (field->type() == FieldDescriptor::TYPE_BYTES) {
380          return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES";
381        } else {
382          return "\"\"";
383        }
384      }
385
386    case FieldDescriptor::CPPTYPE_ENUM:
387      return ClassName(params, field->enum_type()) + "." +
388             field->default_value_enum()->name();
389
390    case FieldDescriptor::CPPTYPE_MESSAGE:
391      return "null";
392
393    // No default because we want the compiler to complain if any new
394    // types are added.
395  }
396
397  GOOGLE_LOG(FATAL) << "Can't get here.";
398  return "";
399}
400
401}  // namespace javanano
402}  // namespace compiler
403}  // namespace protobuf
404}  // namespace google
405