java_helpers.cc revision d0332953cda33fb4f8e24ebff9c49159b69c43d6
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 <limits>
36#include <vector>
37
38#include <google/protobuf/compiler/java/java_helpers.h>
39#include <google/protobuf/descriptor.pb.h>
40#include <google/protobuf/stubs/strutil.h>
41#include <google/protobuf/stubs/substitute.h>
42
43namespace google {
44namespace protobuf {
45namespace compiler {
46namespace java {
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 (GetType(field) == 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 FileDescriptor* file) {
122  if (file->options().has_java_outer_classname()) {
123    return file->options().java_outer_classname();
124  } else {
125    string basename;
126    string::size_type last_slash = file->name().find_last_of('/');
127    if (last_slash == string::npos) {
128      basename = file->name();
129    } else {
130      basename = file->name().substr(last_slash + 1);
131    }
132    return UnderscoresToCamelCaseImpl(StripProto(basename), true);
133  }
134}
135
136string FileJavaPackage(const FileDescriptor* file) {
137  if (file->options().has_java_package()) {
138    return file->options().java_package();
139  } else {
140    string result = kDefaultPackage;
141    if (!file->package().empty()) {
142      if (!result.empty()) result += '.';
143      result += file->package();
144    }
145    return result;
146  }
147}
148
149string ToJavaName(const string& full_name, const FileDescriptor* file) {
150  string result;
151  if (file->options().java_multiple_files()) {
152    result = FileJavaPackage(file);
153  } else {
154    result = ClassName(file);
155  }
156  if (!result.empty()) {
157    result += '.';
158  }
159  if (file->package().empty()) {
160    result += full_name;
161  } else {
162    // Strip the proto package from full_name since we've replaced it with
163    // the Java package.
164    result += full_name.substr(file->package().size() + 1);
165  }
166  return result;
167}
168
169string ClassName(const FileDescriptor* descriptor) {
170  string result = FileJavaPackage(descriptor);
171  if (!result.empty()) result += '.';
172  result += FileClassName(descriptor);
173  return result;
174}
175
176string FieldConstantName(const FieldDescriptor *field) {
177  string name = field->name() + "_FIELD_NUMBER";
178  UpperString(&name);
179  return name;
180}
181
182FieldDescriptor::Type GetType(const FieldDescriptor* field) {
183  return field->type();
184}
185
186JavaType GetJavaType(const FieldDescriptor* field) {
187  switch (GetType(field)) {
188    case FieldDescriptor::TYPE_INT32:
189    case FieldDescriptor::TYPE_UINT32:
190    case FieldDescriptor::TYPE_SINT32:
191    case FieldDescriptor::TYPE_FIXED32:
192    case FieldDescriptor::TYPE_SFIXED32:
193      return JAVATYPE_INT;
194
195    case FieldDescriptor::TYPE_INT64:
196    case FieldDescriptor::TYPE_UINT64:
197    case FieldDescriptor::TYPE_SINT64:
198    case FieldDescriptor::TYPE_FIXED64:
199    case FieldDescriptor::TYPE_SFIXED64:
200      return JAVATYPE_LONG;
201
202    case FieldDescriptor::TYPE_FLOAT:
203      return JAVATYPE_FLOAT;
204
205    case FieldDescriptor::TYPE_DOUBLE:
206      return JAVATYPE_DOUBLE;
207
208    case FieldDescriptor::TYPE_BOOL:
209      return JAVATYPE_BOOLEAN;
210
211    case FieldDescriptor::TYPE_STRING:
212      return JAVATYPE_STRING;
213
214    case FieldDescriptor::TYPE_BYTES:
215      return JAVATYPE_BYTES;
216
217    case FieldDescriptor::TYPE_ENUM:
218      return JAVATYPE_ENUM;
219
220    case FieldDescriptor::TYPE_GROUP:
221    case FieldDescriptor::TYPE_MESSAGE:
222      return JAVATYPE_MESSAGE;
223
224    // No default because we want the compiler to complain if any new
225    // types are added.
226  }
227
228  GOOGLE_LOG(FATAL) << "Can't get here.";
229  return JAVATYPE_INT;
230}
231
232const char* BoxedPrimitiveTypeName(JavaType type) {
233  switch (type) {
234    case JAVATYPE_INT    : return "java.lang.Integer";
235    case JAVATYPE_LONG   : return "java.lang.Long";
236    case JAVATYPE_FLOAT  : return "java.lang.Float";
237    case JAVATYPE_DOUBLE : return "java.lang.Double";
238    case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
239    case JAVATYPE_STRING : return "java.lang.String";
240    case JAVATYPE_BYTES  : return "com.google.protobuf.ByteString";
241    case JAVATYPE_ENUM   : return NULL;
242    case JAVATYPE_MESSAGE: return NULL;
243
244    // No default because we want the compiler to complain if any new
245    // JavaTypes are added.
246  }
247
248  GOOGLE_LOG(FATAL) << "Can't get here.";
249  return NULL;
250}
251
252bool AllAscii(const string& text) {
253  for (int i = 0; i < text.size(); i++) {
254    if ((text[i] & 0x80) != 0) {
255      return false;
256    }
257  }
258  return true;
259}
260
261string DefaultValue(const FieldDescriptor* field) {
262  // Switch on CppType since we need to know which default_value_* method
263  // of FieldDescriptor to call.
264  switch (field->cpp_type()) {
265    case FieldDescriptor::CPPTYPE_INT32:
266      return SimpleItoa(field->default_value_int32());
267    case FieldDescriptor::CPPTYPE_UINT32:
268      // Need to print as a signed int since Java has no unsigned.
269      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
270    case FieldDescriptor::CPPTYPE_INT64:
271      return SimpleItoa(field->default_value_int64()) + "L";
272    case FieldDescriptor::CPPTYPE_UINT64:
273      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
274             "L";
275    case FieldDescriptor::CPPTYPE_DOUBLE: {
276      double value = field->default_value_double();
277      if (value == numeric_limits<double>::infinity()) {
278        return "Double.POSITIVE_INFINITY";
279      } else if (value == -numeric_limits<double>::infinity()) {
280        return "Double.NEGATIVE_INFINITY";
281      } else if (value != value) {
282        return "Double.NaN";
283      } else {
284        return SimpleDtoa(value) + "D";
285      }
286    }
287    case FieldDescriptor::CPPTYPE_FLOAT: {
288      float value = field->default_value_float();
289      if (value == numeric_limits<float>::infinity()) {
290        return "Float.POSITIVE_INFINITY";
291      } else if (value == -numeric_limits<float>::infinity()) {
292        return "Float.NEGATIVE_INFINITY";
293      } else if (value != value) {
294        return "Float.NaN";
295      } else {
296        return SimpleFtoa(value) + "F";
297      }
298    }
299    case FieldDescriptor::CPPTYPE_BOOL:
300      return field->default_value_bool() ? "true" : "false";
301    case FieldDescriptor::CPPTYPE_STRING:
302      if (GetType(field) == FieldDescriptor::TYPE_BYTES) {
303        if (field->has_default_value()) {
304          // See comments in Internal.java for gory details.
305          return strings::Substitute(
306            "com.google.protobuf.Internal.bytesDefaultValue(\"$0\")",
307            CEscape(field->default_value_string()));
308        } else {
309          return "com.google.protobuf.ByteString.EMPTY";
310        }
311      } else {
312        if (AllAscii(field->default_value_string())) {
313          // All chars are ASCII.  In this case CEscape() works fine.
314          return "\"" + CEscape(field->default_value_string()) + "\"";
315        } else {
316          // See comments in Internal.java for gory details.
317          return strings::Substitute(
318            "com.google.protobuf.Internal.stringDefaultValue(\"$0\")",
319            CEscape(field->default_value_string()));
320        }
321      }
322
323    case FieldDescriptor::CPPTYPE_ENUM:
324      return ClassName(field->enum_type()) + "." +
325             field->default_value_enum()->name();
326
327    case FieldDescriptor::CPPTYPE_MESSAGE:
328      return ClassName(field->message_type()) + ".getDefaultInstance()";
329
330    // No default because we want the compiler to complain if any new
331    // types are added.
332  }
333
334  GOOGLE_LOG(FATAL) << "Can't get here.";
335  return "";
336}
337
338}  // namespace java
339}  // namespace compiler
340}  // namespace protobuf
341}  // namespace google
342