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 (at) 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/javanano/javanano_helpers.h> 39 #include <google/protobuf/compiler/javanano/javanano_params.h> 40 #include <google/protobuf/descriptor.pb.h> 41 #include <google/protobuf/stubs/hash.h> 42 #include <google/protobuf/stubs/strutil.h> 43 #include <google/protobuf/stubs/substitute.h> 44 45 namespace google { 46 namespace protobuf { 47 namespace compiler { 48 namespace javanano { 49 50 const char kThickSeparator[] = 51 "// ===================================================================\n"; 52 const char kThinSeparator[] = 53 "// -------------------------------------------------------------------\n"; 54 55 class RenameKeywords { 56 private: 57 hash_set<string> java_keywords_set_; 58 59 public: 60 RenameKeywords() { 61 static const char* kJavaKeywordsList[] = { 62 // Reserved Java Keywords 63 "abstract", "assert", "boolean", "break", "byte", "case", "catch", 64 "char", "class", "const", "continue", "default", "do", "double", "else", 65 "enum", "extends", "final", "finally", "float", "for", "goto", "if", 66 "implements", "import", "instanceof", "int", "interface", "long", 67 "native", "new", "package", "private", "protected", "public", "return", 68 "short", "static", "strictfp", "super", "switch", "synchronized", 69 "this", "throw", "throws", "transient", "try", "void", "volatile", "while", 70 71 // Reserved Keywords for Literals 72 "false", "null", "true" 73 }; 74 75 for (int i = 0; i < GOOGLE_ARRAYSIZE(kJavaKeywordsList); i++) { 76 java_keywords_set_.insert(kJavaKeywordsList[i]); 77 } 78 } 79 80 // Used to rename the a field name if it's a java keyword. Specifically 81 // this is used to rename the ["name"] or ["capitalized_name"] field params. 82 // (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html) 83 string RenameJavaKeywordsImpl(const string& input) { 84 string result = input; 85 86 if (java_keywords_set_.find(result) != java_keywords_set_.end()) { 87 result += "_"; 88 } 89 90 return result; 91 } 92 93 }; 94 95 static RenameKeywords sRenameKeywords; 96 97 namespace { 98 99 const char* kDefaultPackage = ""; 100 101 const string& FieldName(const FieldDescriptor* field) { 102 // Groups are hacky: The name of the field is just the lower-cased name 103 // of the group type. In Java, though, we would like to retain the original 104 // capitalization of the type name. 105 if (field->type() == FieldDescriptor::TYPE_GROUP) { 106 return field->message_type()->name(); 107 } else { 108 return field->name(); 109 } 110 } 111 112 string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { 113 string result; 114 // Note: I distrust ctype.h due to locales. 115 for (int i = 0; i < input.size(); i++) { 116 if ('a' <= input[i] && input[i] <= 'z') { 117 if (cap_next_letter) { 118 result += input[i] + ('A' - 'a'); 119 } else { 120 result += input[i]; 121 } 122 cap_next_letter = false; 123 } else if ('A' <= input[i] && input[i] <= 'Z') { 124 if (i == 0 && !cap_next_letter) { 125 // Force first letter to lower-case unless explicitly told to 126 // capitalize it. 127 result += input[i] + ('a' - 'A'); 128 } else { 129 // Capital letters after the first are left as-is. 130 result += input[i]; 131 } 132 cap_next_letter = false; 133 } else if ('0' <= input[i] && input[i] <= '9') { 134 result += input[i]; 135 cap_next_letter = true; 136 } else { 137 cap_next_letter = true; 138 } 139 } 140 return result; 141 } 142 143 } // namespace 144 145 string UnderscoresToCamelCase(const FieldDescriptor* field) { 146 return UnderscoresToCamelCaseImpl(FieldName(field), false); 147 } 148 149 string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { 150 return UnderscoresToCamelCaseImpl(FieldName(field), true); 151 } 152 153 string UnderscoresToCamelCase(const MethodDescriptor* method) { 154 return UnderscoresToCamelCaseImpl(method->name(), false); 155 } 156 157 string RenameJavaKeywords(const string& input) { 158 return sRenameKeywords.RenameJavaKeywordsImpl(input); 159 } 160 161 string StripProto(const string& filename) { 162 if (HasSuffixString(filename, ".protodevel")) { 163 return StripSuffixString(filename, ".protodevel"); 164 } else { 165 return StripSuffixString(filename, ".proto"); 166 } 167 } 168 169 string FileClassName(const Params& params, const FileDescriptor* file) { 170 if (params.has_java_outer_classname(file->name())) { 171 return params.java_outer_classname(file->name()); 172 } else { 173 // Use the filename itself with underscores removed 174 // and a CamelCase style name. 175 string basename; 176 string::size_type last_slash = file->name().find_last_of('/'); 177 if (last_slash == string::npos) { 178 basename = file->name(); 179 } else { 180 basename = file->name().substr(last_slash + 1); 181 } 182 return UnderscoresToCamelCaseImpl(StripProto(basename), true); 183 } 184 } 185 186 string FileJavaPackage(const Params& params, const FileDescriptor* file) { 187 if (params.has_java_package(file->name())) { 188 return params.java_package(file->name()); 189 } else { 190 string result = kDefaultPackage; 191 if (!file->package().empty()) { 192 if (!result.empty()) result += '.'; 193 result += file->package(); 194 } 195 return result; 196 } 197 } 198 199 bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) { 200 // Enums and extensions need the outer class as the scope. 201 if (file->enum_type_count() != 0 || file->extension_count() != 0) { 202 return true; 203 } 204 // Messages need the outer class only if java_multiple_files is false. 205 return !params.java_multiple_files(file->name()); 206 } 207 208 string ToJavaName(const Params& params, const string& name, bool is_class, 209 const Descriptor* parent, const FileDescriptor* file) { 210 string result; 211 if (parent != NULL) { 212 result.append(ClassName(params, parent)); 213 } else if (is_class && params.java_multiple_files(file->name())) { 214 result.append(FileJavaPackage(params, file)); 215 } else { 216 result.append(ClassName(params, file)); 217 } 218 if (!result.empty()) result.append(1, '.'); 219 result.append(RenameJavaKeywords(name)); 220 return result; 221 } 222 223 string ClassName(const Params& params, const FileDescriptor* descriptor) { 224 string result = FileJavaPackage(params, descriptor); 225 if (!result.empty()) result += '.'; 226 result += FileClassName(params, descriptor); 227 return result; 228 } 229 230 string ClassName(const Params& params, const EnumDescriptor* descriptor) { 231 // An enum's class name is the enclosing message's class name or the outer 232 // class name. 233 const Descriptor* parent = descriptor->containing_type(); 234 if (parent != NULL) { 235 return ClassName(params, parent); 236 } else { 237 return ClassName(params, descriptor->file()); 238 } 239 } 240 241 string FieldConstantName(const FieldDescriptor *field) { 242 string name = field->name() + "_FIELD_NUMBER"; 243 UpperString(&name); 244 return name; 245 } 246 247 string FieldDefaultConstantName(const FieldDescriptor *field) { 248 string name = field->name() + "_DEFAULT"; 249 UpperString(&name); 250 return name; 251 } 252 253 JavaType GetJavaType(FieldDescriptor::Type field_type) { 254 switch (field_type) { 255 case FieldDescriptor::TYPE_INT32: 256 case FieldDescriptor::TYPE_UINT32: 257 case FieldDescriptor::TYPE_SINT32: 258 case FieldDescriptor::TYPE_FIXED32: 259 case FieldDescriptor::TYPE_SFIXED32: 260 return JAVATYPE_INT; 261 262 case FieldDescriptor::TYPE_INT64: 263 case FieldDescriptor::TYPE_UINT64: 264 case FieldDescriptor::TYPE_SINT64: 265 case FieldDescriptor::TYPE_FIXED64: 266 case FieldDescriptor::TYPE_SFIXED64: 267 return JAVATYPE_LONG; 268 269 case FieldDescriptor::TYPE_FLOAT: 270 return JAVATYPE_FLOAT; 271 272 case FieldDescriptor::TYPE_DOUBLE: 273 return JAVATYPE_DOUBLE; 274 275 case FieldDescriptor::TYPE_BOOL: 276 return JAVATYPE_BOOLEAN; 277 278 case FieldDescriptor::TYPE_STRING: 279 return JAVATYPE_STRING; 280 281 case FieldDescriptor::TYPE_BYTES: 282 return JAVATYPE_BYTES; 283 284 case FieldDescriptor::TYPE_ENUM: 285 return JAVATYPE_ENUM; 286 287 case FieldDescriptor::TYPE_GROUP: 288 case FieldDescriptor::TYPE_MESSAGE: 289 return JAVATYPE_MESSAGE; 290 291 // No default because we want the compiler to complain if any new 292 // types are added. 293 } 294 295 GOOGLE_LOG(FATAL) << "Can't get here."; 296 return JAVATYPE_INT; 297 } 298 299 const char* BoxedPrimitiveTypeName(JavaType type) { 300 switch (type) { 301 case JAVATYPE_INT : return "java.lang.Integer"; 302 case JAVATYPE_LONG : return "java.lang.Long"; 303 case JAVATYPE_FLOAT : return "java.lang.Float"; 304 case JAVATYPE_DOUBLE : return "java.lang.Double"; 305 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 306 case JAVATYPE_STRING : return "java.lang.String"; 307 case JAVATYPE_BYTES : return "byte[]"; 308 case JAVATYPE_ENUM : return "java.lang.Integer"; 309 case JAVATYPE_MESSAGE: return NULL; 310 311 // No default because we want the compiler to complain if any new 312 // JavaTypes are added. 313 } 314 315 GOOGLE_LOG(FATAL) << "Can't get here."; 316 return NULL; 317 } 318 319 string EmptyArrayName(const Params& params, const FieldDescriptor* field) { 320 switch (GetJavaType(field)) { 321 case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 322 case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY"; 323 case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY"; 324 case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY"; 325 case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY"; 326 case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY"; 327 case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY"; 328 case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 329 case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY"; 330 331 // No default because we want the compiler to complain if any new 332 // JavaTypes are added. 333 } 334 335 GOOGLE_LOG(FATAL) << "Can't get here."; 336 return ""; 337 } 338 339 string DefaultValue(const Params& params, const FieldDescriptor* field) { 340 if (field->label() == FieldDescriptor::LABEL_REPEATED) { 341 return EmptyArrayName(params, field); 342 } 343 344 // Switch on cpp_type since we need to know which default_value_* method 345 // of FieldDescriptor to call. 346 switch (field->cpp_type()) { 347 case FieldDescriptor::CPPTYPE_INT32: 348 return SimpleItoa(field->default_value_int32()); 349 case FieldDescriptor::CPPTYPE_UINT32: 350 // Need to print as a signed int since Java has no unsigned. 351 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 352 case FieldDescriptor::CPPTYPE_INT64: 353 return SimpleItoa(field->default_value_int64()) + "L"; 354 case FieldDescriptor::CPPTYPE_UINT64: 355 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 356 "L"; 357 case FieldDescriptor::CPPTYPE_DOUBLE: { 358 double value = field->default_value_double(); 359 if (value == numeric_limits<double>::infinity()) { 360 return "Double.POSITIVE_INFINITY"; 361 } else if (value == -numeric_limits<double>::infinity()) { 362 return "Double.NEGATIVE_INFINITY"; 363 } else if (value != value) { 364 return "Double.NaN"; 365 } else { 366 return SimpleDtoa(value) + "D"; 367 } 368 } 369 case FieldDescriptor::CPPTYPE_FLOAT: { 370 float value = field->default_value_float(); 371 if (value == numeric_limits<float>::infinity()) { 372 return "Float.POSITIVE_INFINITY"; 373 } else if (value == -numeric_limits<float>::infinity()) { 374 return "Float.NEGATIVE_INFINITY"; 375 } else if (value != value) { 376 return "Float.NaN"; 377 } else { 378 return SimpleFtoa(value) + "F"; 379 } 380 } 381 case FieldDescriptor::CPPTYPE_BOOL: 382 return field->default_value_bool() ? "true" : "false"; 383 case FieldDescriptor::CPPTYPE_STRING: 384 if (!field->default_value_string().empty()) { 385 // Point it to the static final in the generated code. 386 return FieldDefaultConstantName(field); 387 } else { 388 if (field->type() == FieldDescriptor::TYPE_BYTES) { 389 return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; 390 } else { 391 return "\"\""; 392 } 393 } 394 395 case FieldDescriptor::CPPTYPE_ENUM: 396 return ClassName(params, field->enum_type()) + "." + 397 field->default_value_enum()->name(); 398 399 case FieldDescriptor::CPPTYPE_MESSAGE: 400 return "null"; 401 402 // No default because we want the compiler to complain if any new 403 // types are added. 404 } 405 406 GOOGLE_LOG(FATAL) << "Can't get here."; 407 return ""; 408 } 409 410 } // namespace javanano 411 } // namespace compiler 412 } // namespace protobuf 413 } // namespace google 414