java_helpers.cc revision fbaaef999ba563838ebd00874ed8a1c01fbf286d
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 <vector>
36
37#include <google/protobuf/compiler/java/java_helpers.h>
38#include <google/protobuf/descriptor.pb.h>
39#include <google/protobuf/stubs/strutil.h>
40#include <google/protobuf/stubs/substitute.h>
41
42namespace google {
43namespace protobuf {
44namespace compiler {
45namespace java {
46
47const char kThickSeparator[] =
48  "// ===================================================================\n";
49const char kThinSeparator[] =
50  "// -------------------------------------------------------------------\n";
51
52namespace {
53
54const char* kDefaultPackage = "";
55
56const string& FieldName(const FieldDescriptor* field) {
57  // Groups are hacky:  The name of the field is just the lower-cased name
58  // of the group type.  In Java, though, we would like to retain the original
59  // capitalization of the type name.
60  if (field->type() == FieldDescriptor::TYPE_GROUP) {
61    return field->message_type()->name();
62  } else {
63    return field->name();
64  }
65}
66
67string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) {
68  string result;
69  // Note:  I distrust ctype.h due to locales.
70  for (int i = 0; i < input.size(); i++) {
71    if ('a' <= input[i] && input[i] <= 'z') {
72      if (cap_next_letter) {
73        result += input[i] + ('A' - 'a');
74      } else {
75        result += input[i];
76      }
77      cap_next_letter = false;
78    } else if ('A' <= input[i] && input[i] <= 'Z') {
79      if (i == 0 && !cap_next_letter) {
80        // Force first letter to lower-case unless explicitly told to
81        // capitalize it.
82        result += input[i] + ('a' - 'A');
83      } else {
84        // Capital letters after the first are left as-is.
85        result += input[i];
86      }
87      cap_next_letter = false;
88    } else if ('0' <= input[i] && input[i] <= '9') {
89      result += input[i];
90      cap_next_letter = true;
91    } else {
92      cap_next_letter = true;
93    }
94  }
95  return result;
96}
97
98}  // namespace
99
100string UnderscoresToCamelCase(const FieldDescriptor* field) {
101  return UnderscoresToCamelCaseImpl(FieldName(field), false);
102}
103
104string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
105  return UnderscoresToCamelCaseImpl(FieldName(field), true);
106}
107
108string UnderscoresToCamelCase(const MethodDescriptor* method) {
109  return UnderscoresToCamelCaseImpl(method->name(), false);
110}
111
112string StripProto(const string& filename) {
113  if (HasSuffixString(filename, ".protodevel")) {
114    return StripSuffixString(filename, ".protodevel");
115  } else {
116    return StripSuffixString(filename, ".proto");
117  }
118}
119
120string FileClassName(const FileDescriptor* file) {
121  if (file->options().has_java_outer_classname()) {
122    return file->options().java_outer_classname();
123  } else {
124    string basename;
125    string::size_type last_slash = file->name().find_last_of('/');
126    if (last_slash == string::npos) {
127      basename = file->name();
128    } else {
129      basename = file->name().substr(last_slash + 1);
130    }
131    return UnderscoresToCamelCaseImpl(StripProto(basename), true);
132  }
133}
134
135string FileJavaPackage(const FileDescriptor* file) {
136  if (file->options().has_java_package()) {
137    return file->options().java_package();
138  } else {
139    string result = kDefaultPackage;
140    if (!file->package().empty()) {
141      if (!result.empty()) result += '.';
142      result += file->package();
143    }
144    return result;
145  }
146}
147
148string ToJavaName(const string& full_name, const FileDescriptor* file) {
149  string result;
150  if (file->options().java_multiple_files()) {
151    result = FileJavaPackage(file);
152  } else {
153    result = ClassName(file);
154  }
155  if (!result.empty()) {
156    result += '.';
157  }
158  if (file->package().empty()) {
159    result += full_name;
160  } else {
161    // Strip the proto package from full_name since we've replaced it with
162    // the Java package.
163    result += full_name.substr(file->package().size() + 1);
164  }
165  return result;
166}
167
168string ClassName(const FileDescriptor* descriptor) {
169  string result = FileJavaPackage(descriptor);
170  if (!result.empty()) result += '.';
171  result += FileClassName(descriptor);
172  return result;
173}
174
175string FieldConstantName(const FieldDescriptor *field) {
176  string name = field->name() + "_FIELD_NUMBER";
177  UpperString(&name);
178  return name;
179}
180
181JavaType GetJavaType(FieldDescriptor::Type field_type) {
182  switch (field_type) {
183    case FieldDescriptor::TYPE_INT32:
184    case FieldDescriptor::TYPE_UINT32:
185    case FieldDescriptor::TYPE_SINT32:
186    case FieldDescriptor::TYPE_FIXED32:
187    case FieldDescriptor::TYPE_SFIXED32:
188      return JAVATYPE_INT;
189
190    case FieldDescriptor::TYPE_INT64:
191    case FieldDescriptor::TYPE_UINT64:
192    case FieldDescriptor::TYPE_SINT64:
193    case FieldDescriptor::TYPE_FIXED64:
194    case FieldDescriptor::TYPE_SFIXED64:
195      return JAVATYPE_LONG;
196
197    case FieldDescriptor::TYPE_FLOAT:
198      return JAVATYPE_FLOAT;
199
200    case FieldDescriptor::TYPE_DOUBLE:
201      return JAVATYPE_DOUBLE;
202
203    case FieldDescriptor::TYPE_BOOL:
204      return JAVATYPE_BOOLEAN;
205
206    case FieldDescriptor::TYPE_STRING:
207      return JAVATYPE_STRING;
208
209    case FieldDescriptor::TYPE_BYTES:
210      return JAVATYPE_BYTES;
211
212    case FieldDescriptor::TYPE_ENUM:
213      return JAVATYPE_ENUM;
214
215    case FieldDescriptor::TYPE_GROUP:
216    case FieldDescriptor::TYPE_MESSAGE:
217      return JAVATYPE_MESSAGE;
218
219    // No default because we want the compiler to complain if any new
220    // types are added.
221  }
222
223  GOOGLE_LOG(FATAL) << "Can't get here.";
224  return JAVATYPE_INT;
225}
226
227const char* BoxedPrimitiveTypeName(JavaType type) {
228  switch (type) {
229    case JAVATYPE_INT    : return "java.lang.Integer";
230    case JAVATYPE_LONG   : return "java.lang.Long";
231    case JAVATYPE_FLOAT  : return "java.lang.Float";
232    case JAVATYPE_DOUBLE : return "java.lang.Double";
233    case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
234    case JAVATYPE_STRING : return "java.lang.String";
235    case JAVATYPE_BYTES  : return "com.google.protobuf.ByteString";
236    case JAVATYPE_ENUM   : return NULL;
237    case JAVATYPE_MESSAGE: return NULL;
238
239    // No default because we want the compiler to complain if any new
240    // JavaTypes are added.
241  }
242
243  GOOGLE_LOG(FATAL) << "Can't get here.";
244  return NULL;
245}
246
247bool AllAscii(const string& text) {
248  for (int i = 0; i < text.size(); i++) {
249    if ((text[i] & 0x80) != 0) {
250      return false;
251    }
252  }
253  return true;
254}
255
256string DefaultValue(const FieldDescriptor* field) {
257  // Switch on cpp_type since we need to know which default_value_* method
258  // of FieldDescriptor to call.
259  switch (field->cpp_type()) {
260    case FieldDescriptor::CPPTYPE_INT32:
261      return SimpleItoa(field->default_value_int32());
262    case FieldDescriptor::CPPTYPE_UINT32:
263      // Need to print as a signed int since Java has no unsigned.
264      return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
265    case FieldDescriptor::CPPTYPE_INT64:
266      return SimpleItoa(field->default_value_int64()) + "L";
267    case FieldDescriptor::CPPTYPE_UINT64:
268      return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
269             "L";
270    case FieldDescriptor::CPPTYPE_DOUBLE:
271      return SimpleDtoa(field->default_value_double()) + "D";
272    case FieldDescriptor::CPPTYPE_FLOAT:
273      return SimpleFtoa(field->default_value_float()) + "F";
274    case FieldDescriptor::CPPTYPE_BOOL:
275      return field->default_value_bool() ? "true" : "false";
276    case FieldDescriptor::CPPTYPE_STRING:
277      if (field->type() == FieldDescriptor::TYPE_BYTES) {
278        if (field->has_default_value()) {
279          // See comments in Internal.java for gory details.
280          return strings::Substitute(
281            "com.google.protobuf.Internal.bytesDefaultValue(\"$0\")",
282            CEscape(field->default_value_string()));
283        } else {
284          return "com.google.protobuf.ByteString.EMPTY";
285        }
286      } else {
287        if (AllAscii(field->default_value_string())) {
288          // All chars are ASCII.  In this case CEscape() works fine.
289          return "\"" + CEscape(field->default_value_string()) + "\"";
290        } else {
291          // See comments in Internal.java for gory details.
292          return strings::Substitute(
293            "com.google.protobuf.Internal.stringDefaultValue(\"$0\")",
294            CEscape(field->default_value_string()));
295        }
296      }
297
298    case FieldDescriptor::CPPTYPE_ENUM:
299      return ClassName(field->enum_type()) + "." +
300             field->default_value_enum()->name();
301
302    case FieldDescriptor::CPPTYPE_MESSAGE:
303      return ClassName(field->message_type()) + ".getDefaultInstance()";
304
305    // No default because we want the compiler to complain if any new
306    // types are added.
307  }
308
309  GOOGLE_LOG(FATAL) << "Can't get here.";
310  return "";
311}
312
313}  // namespace java
314}  // namespace compiler
315}  // namespace protobuf
316}  // namespace google
317