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