• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/javamicro/javamicro_helpers.h>
38 #include <google/protobuf/compiler/javamicro/javamicro_params.h>
39 #include <google/protobuf/descriptor.pb.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <google/protobuf/stubs/substitute.h>
42 
43 namespace google {
44 namespace protobuf {
45 namespace compiler {
46 namespace javamicro {
47 
48 const char kThickSeparator[] =
49   "// ===================================================================\n";
50 const char kThinSeparator[] =
51   "// -------------------------------------------------------------------\n";
52 
53 namespace {
54 
55 const char* kDefaultPackage = "";
56 
FieldName(const FieldDescriptor * field)57 const 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 
UnderscoresToCamelCaseImpl(const string & input,bool cap_next_letter)68 string 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 
UnderscoresToCamelCase(const FieldDescriptor * field)101 string UnderscoresToCamelCase(const FieldDescriptor* field) {
102   return UnderscoresToCamelCaseImpl(FieldName(field), false);
103 }
104 
UnderscoresToCapitalizedCamelCase(const FieldDescriptor * field)105 string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) {
106   return UnderscoresToCamelCaseImpl(FieldName(field), true);
107 }
108 
UnderscoresToCamelCase(const MethodDescriptor * method)109 string UnderscoresToCamelCase(const MethodDescriptor* method) {
110   return UnderscoresToCamelCaseImpl(method->name(), false);
111 }
112 
StripProto(const string & filename)113 string StripProto(const string& filename) {
114   if (HasSuffixString(filename, ".protodevel")) {
115     return StripSuffixString(filename, ".protodevel");
116   } else {
117     return StripSuffixString(filename, ".proto");
118   }
119 }
120 
FileClassName(const Params & params,const FileDescriptor * file)121 string 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 
FileJavaPackage(const Params & params,const FileDescriptor * file)149 string 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 
ToJavaName(const Params & params,const string & full_name,const FileDescriptor * file)162 string 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 
ClassName(const Params & params,const FileDescriptor * descriptor)197 string 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 
ClassName(const Params & params,const EnumDescriptor * descriptor)204 string 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 
FieldConstantName(const FieldDescriptor * field)252 string FieldConstantName(const FieldDescriptor *field) {
253   string name = field->name() + "_FIELD_NUMBER";
254   UpperString(&name);
255   return name;
256 }
257 
GetJavaType(FieldDescriptor::Type field_type)258 JavaType GetJavaType(FieldDescriptor::Type field_type) {
259   switch (field_type) {
260     case FieldDescriptor::TYPE_INT32:
261     case FieldDescriptor::TYPE_UINT32:
262     case FieldDescriptor::TYPE_SINT32:
263     case FieldDescriptor::TYPE_FIXED32:
264     case FieldDescriptor::TYPE_SFIXED32:
265       return JAVATYPE_INT;
266 
267     case FieldDescriptor::TYPE_INT64:
268     case FieldDescriptor::TYPE_UINT64:
269     case FieldDescriptor::TYPE_SINT64:
270     case FieldDescriptor::TYPE_FIXED64:
271     case FieldDescriptor::TYPE_SFIXED64:
272       return JAVATYPE_LONG;
273 
274     case FieldDescriptor::TYPE_FLOAT:
275       return JAVATYPE_FLOAT;
276 
277     case FieldDescriptor::TYPE_DOUBLE:
278       return JAVATYPE_DOUBLE;
279 
280     case FieldDescriptor::TYPE_BOOL:
281       return JAVATYPE_BOOLEAN;
282 
283     case FieldDescriptor::TYPE_STRING:
284       return JAVATYPE_STRING;
285 
286     case FieldDescriptor::TYPE_BYTES:
287       return JAVATYPE_BYTES;
288 
289     case FieldDescriptor::TYPE_ENUM:
290       return JAVATYPE_ENUM;
291 
292     case FieldDescriptor::TYPE_GROUP:
293     case FieldDescriptor::TYPE_MESSAGE:
294       return JAVATYPE_MESSAGE;
295 
296     // No default because we want the compiler to complain if any new
297     // types are added.
298   }
299 
300   GOOGLE_LOG(FATAL) << "Can't get here.";
301   return JAVATYPE_INT;
302 }
303 
BoxedPrimitiveTypeName(JavaType type)304 const char* BoxedPrimitiveTypeName(JavaType type) {
305   switch (type) {
306     case JAVATYPE_INT    : return "java.lang.Integer";
307     case JAVATYPE_LONG   : return "java.lang.Long";
308     case JAVATYPE_FLOAT  : return "java.lang.Float";
309     case JAVATYPE_DOUBLE : return "java.lang.Double";
310     case JAVATYPE_BOOLEAN: return "java.lang.Boolean";
311     case JAVATYPE_STRING : return "java.lang.String";
312     case JAVATYPE_BYTES  : return "com.google.protobuf.micro.ByteStringMicro";
313     case JAVATYPE_ENUM   : return "java.lang.Integer";
314     case JAVATYPE_MESSAGE: return NULL;
315 
316     // No default because we want the compiler to complain if any new
317     // JavaTypes are added.
318   }
319 
320   GOOGLE_LOG(FATAL) << "Can't get here.";
321   return NULL;
322 }
323 
AllAscii(const string & text)324 bool AllAscii(const string& text) {
325   for (int i = 0; i < text.size(); i++) {
326     if ((text[i] & 0x80) != 0) {
327       return false;
328     }
329   }
330   return true;
331 }
332 
DefaultValue(const Params & params,const FieldDescriptor * field)333 string DefaultValue(const Params& params, const FieldDescriptor* field) {
334   // Switch on cpp_type since we need to know which default_value_* method
335   // of FieldDescriptor to call.
336   switch (field->cpp_type()) {
337     case FieldDescriptor::CPPTYPE_INT32:
338       return SimpleItoa(field->default_value_int32());
339     case FieldDescriptor::CPPTYPE_UINT32:
340       // Need to print as a signed int since Java has no unsigned.
341       return SimpleItoa(static_cast<int32>(field->default_value_uint32()));
342     case FieldDescriptor::CPPTYPE_INT64:
343       return SimpleItoa(field->default_value_int64()) + "L";
344     case FieldDescriptor::CPPTYPE_UINT64:
345       return SimpleItoa(static_cast<int64>(field->default_value_uint64())) +
346              "L";
347     case FieldDescriptor::CPPTYPE_DOUBLE:
348       return SimpleDtoa(field->default_value_double()) + "D";
349     case FieldDescriptor::CPPTYPE_FLOAT:
350       return SimpleFtoa(field->default_value_float()) + "F";
351     case FieldDescriptor::CPPTYPE_BOOL:
352       return field->default_value_bool() ? "true" : "false";
353     case FieldDescriptor::CPPTYPE_STRING:
354       if (field->type() == FieldDescriptor::TYPE_BYTES) {
355         if (field->has_default_value()) {
356           // See comments in Internal.java for gory details.
357           return strings::Substitute(
358             "com.google.protobuf.micro.ByteStringMicro.copyFromUtf8(\"$0\")",
359             CEscape(field->default_value_string()));
360         } else {
361           return "com.google.protobuf.micro.ByteStringMicro.EMPTY";
362         }
363       } else {
364         if (AllAscii(field->default_value_string())) {
365           // All chars are ASCII.  In this case CEscape() works fine.
366           return "\"" + CEscape(field->default_value_string()) + "\"";
367         } else {
368           // See comments in Internal.java for gory details.
369           // BUG: Internal NOT SUPPORTED need to fix!!
370           return strings::Substitute(
371             "com.google.protobuf.micro.Internal.stringDefaultValue(\"$0\")",
372             CEscape(field->default_value_string()));
373         }
374       }
375 
376     case FieldDescriptor::CPPTYPE_ENUM:
377       return ClassName(params, field->enum_type()) + "." +
378              field->default_value_enum()->name();
379 
380     case FieldDescriptor::CPPTYPE_MESSAGE:
381       return ClassName(params, field->message_type()) + ".getDefaultInstance()";
382 
383     // No default because we want the compiler to complain if any new
384     // types are added.
385   }
386 
387   GOOGLE_LOG(FATAL) << "Can't get here.";
388   return "";
389 }
390 
391 }  // namespace javamicro
392 }  // namespace compiler
393 }  // namespace protobuf
394 }  // namespace google
395