javanano_helpers.cc revision 0b8579237336f221711a0aac42400eb31a58fed3
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/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 45namespace google { 46namespace protobuf { 47namespace compiler { 48namespace javanano { 49 50const char kThickSeparator[] = 51 "// ===================================================================\n"; 52const char kThinSeparator[] = 53 "// -------------------------------------------------------------------\n"; 54 55class 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 95static RenameKeywords sRenameKeywords; 96 97namespace { 98 99const char* kDefaultPackage = ""; 100 101const 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 112string 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 145string UnderscoresToCamelCase(const FieldDescriptor* field) { 146 return UnderscoresToCamelCaseImpl(FieldName(field), false); 147} 148 149string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { 150 return UnderscoresToCamelCaseImpl(FieldName(field), true); 151} 152 153string UnderscoresToCamelCase(const MethodDescriptor* method) { 154 return UnderscoresToCamelCaseImpl(method->name(), false); 155} 156 157string RenameJavaKeywords(const string& input) { 158 return sRenameKeywords.RenameJavaKeywordsImpl(input); 159} 160 161string StripProto(const string& filename) { 162 if (HasSuffixString(filename, ".protodevel")) { 163 return StripSuffixString(filename, ".protodevel"); 164 } else { 165 return StripSuffixString(filename, ".proto"); 166 } 167} 168 169string 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 186string 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 199bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) { 200 // If java_multiple_files is false, the outer class is always needed. 201 if (!params.java_multiple_files(file->name())) { 202 return true; 203 } 204 205 // File-scope extensions need the outer class as the scope. 206 if (file->extension_count() != 0) { 207 return true; 208 } 209 210 // If container interfaces are not generated, file-scope enums need the 211 // outer class as the scope. 212 if (file->enum_type_count() != 0 && !params.java_enum_style()) { 213 return true; 214 } 215 216 return false; 217} 218 219string ToJavaName(const Params& params, const string& name, bool is_class, 220 const Descriptor* parent, const FileDescriptor* file) { 221 string result; 222 if (parent != NULL) { 223 result.append(ClassName(params, parent)); 224 } else if (is_class && params.java_multiple_files(file->name())) { 225 result.append(FileJavaPackage(params, file)); 226 } else { 227 result.append(ClassName(params, file)); 228 } 229 if (!result.empty()) result.append(1, '.'); 230 result.append(RenameJavaKeywords(name)); 231 return result; 232} 233 234string ClassName(const Params& params, const FileDescriptor* descriptor) { 235 string result = FileJavaPackage(params, descriptor); 236 if (!result.empty()) result += '.'; 237 result += FileClassName(params, descriptor); 238 return result; 239} 240 241string ClassName(const Params& params, const EnumDescriptor* descriptor) { 242 const Descriptor* parent = descriptor->containing_type(); 243 // When using Java enum style, an enum's class name contains the enum name. 244 // Use the standard ToJavaName translation. 245 if (params.java_enum_style()) { 246 return ToJavaName(params, descriptor->name(), true, parent, 247 descriptor->file()); 248 } 249 // Otherwise the enum members are accessed from the enclosing class. 250 if (parent != NULL) { 251 return ClassName(params, parent); 252 } else { 253 return ClassName(params, descriptor->file()); 254 } 255} 256 257string FieldConstantName(const FieldDescriptor *field) { 258 string name = field->name() + "_FIELD_NUMBER"; 259 UpperString(&name); 260 return name; 261} 262 263string FieldDefaultConstantName(const FieldDescriptor *field) { 264 string name = field->name() + "_DEFAULT"; 265 UpperString(&name); 266 return name; 267} 268 269JavaType GetJavaType(FieldDescriptor::Type field_type) { 270 switch (field_type) { 271 case FieldDescriptor::TYPE_INT32: 272 case FieldDescriptor::TYPE_UINT32: 273 case FieldDescriptor::TYPE_SINT32: 274 case FieldDescriptor::TYPE_FIXED32: 275 case FieldDescriptor::TYPE_SFIXED32: 276 return JAVATYPE_INT; 277 278 case FieldDescriptor::TYPE_INT64: 279 case FieldDescriptor::TYPE_UINT64: 280 case FieldDescriptor::TYPE_SINT64: 281 case FieldDescriptor::TYPE_FIXED64: 282 case FieldDescriptor::TYPE_SFIXED64: 283 return JAVATYPE_LONG; 284 285 case FieldDescriptor::TYPE_FLOAT: 286 return JAVATYPE_FLOAT; 287 288 case FieldDescriptor::TYPE_DOUBLE: 289 return JAVATYPE_DOUBLE; 290 291 case FieldDescriptor::TYPE_BOOL: 292 return JAVATYPE_BOOLEAN; 293 294 case FieldDescriptor::TYPE_STRING: 295 return JAVATYPE_STRING; 296 297 case FieldDescriptor::TYPE_BYTES: 298 return JAVATYPE_BYTES; 299 300 case FieldDescriptor::TYPE_ENUM: 301 return JAVATYPE_ENUM; 302 303 case FieldDescriptor::TYPE_GROUP: 304 case FieldDescriptor::TYPE_MESSAGE: 305 return JAVATYPE_MESSAGE; 306 307 // No default because we want the compiler to complain if any new 308 // types are added. 309 } 310 311 GOOGLE_LOG(FATAL) << "Can't get here."; 312 return JAVATYPE_INT; 313} 314 315const char* BoxedPrimitiveTypeName(JavaType type) { 316 switch (type) { 317 case JAVATYPE_INT : return "java.lang.Integer"; 318 case JAVATYPE_LONG : return "java.lang.Long"; 319 case JAVATYPE_FLOAT : return "java.lang.Float"; 320 case JAVATYPE_DOUBLE : return "java.lang.Double"; 321 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 322 case JAVATYPE_STRING : return "java.lang.String"; 323 case JAVATYPE_BYTES : return "byte[]"; 324 case JAVATYPE_ENUM : return "java.lang.Integer"; 325 case JAVATYPE_MESSAGE: return NULL; 326 327 // No default because we want the compiler to complain if any new 328 // JavaTypes are added. 329 } 330 331 GOOGLE_LOG(FATAL) << "Can't get here."; 332 return NULL; 333} 334 335string EmptyArrayName(const Params& params, const FieldDescriptor* field) { 336 switch (GetJavaType(field)) { 337 case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 338 case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY"; 339 case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY"; 340 case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY"; 341 case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY"; 342 case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY"; 343 case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY"; 344 case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 345 case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY"; 346 347 // No default because we want the compiler to complain if any new 348 // JavaTypes are added. 349 } 350 351 GOOGLE_LOG(FATAL) << "Can't get here."; 352 return ""; 353} 354 355string DefaultValue(const Params& params, const FieldDescriptor* field) { 356 if (field->label() == FieldDescriptor::LABEL_REPEATED) { 357 return EmptyArrayName(params, field); 358 } 359 360 if (params.use_reference_types_for_primitives()) { 361 return "null"; 362 } 363 364 // Switch on cpp_type since we need to know which default_value_* method 365 // of FieldDescriptor to call. 366 switch (field->cpp_type()) { 367 case FieldDescriptor::CPPTYPE_INT32: 368 return SimpleItoa(field->default_value_int32()); 369 case FieldDescriptor::CPPTYPE_UINT32: 370 // Need to print as a signed int since Java has no unsigned. 371 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 372 case FieldDescriptor::CPPTYPE_INT64: 373 return SimpleItoa(field->default_value_int64()) + "L"; 374 case FieldDescriptor::CPPTYPE_UINT64: 375 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 376 "L"; 377 case FieldDescriptor::CPPTYPE_DOUBLE: { 378 double value = field->default_value_double(); 379 if (value == numeric_limits<double>::infinity()) { 380 return "Double.POSITIVE_INFINITY"; 381 } else if (value == -numeric_limits<double>::infinity()) { 382 return "Double.NEGATIVE_INFINITY"; 383 } else if (value != value) { 384 return "Double.NaN"; 385 } else { 386 return SimpleDtoa(value) + "D"; 387 } 388 } 389 case FieldDescriptor::CPPTYPE_FLOAT: { 390 float value = field->default_value_float(); 391 if (value == numeric_limits<float>::infinity()) { 392 return "Float.POSITIVE_INFINITY"; 393 } else if (value == -numeric_limits<float>::infinity()) { 394 return "Float.NEGATIVE_INFINITY"; 395 } else if (value != value) { 396 return "Float.NaN"; 397 } else { 398 return SimpleFtoa(value) + "F"; 399 } 400 } 401 case FieldDescriptor::CPPTYPE_BOOL: 402 return field->default_value_bool() ? "true" : "false"; 403 case FieldDescriptor::CPPTYPE_STRING: 404 if (!field->default_value_string().empty()) { 405 // Point it to the static final in the generated code. 406 return FieldDefaultConstantName(field); 407 } else { 408 if (field->type() == FieldDescriptor::TYPE_BYTES) { 409 return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; 410 } else { 411 return "\"\""; 412 } 413 } 414 415 case FieldDescriptor::CPPTYPE_ENUM: 416 return ClassName(params, field->enum_type()) + "." + 417 field->default_value_enum()->name(); 418 419 case FieldDescriptor::CPPTYPE_MESSAGE: 420 return "null"; 421 422 // No default because we want the compiler to complain if any new 423 // types are added. 424 } 425 426 GOOGLE_LOG(FATAL) << "Can't get here."; 427 return ""; 428} 429 430 431static const char* kBitMasks[] = { 432 "0x00000001", 433 "0x00000002", 434 "0x00000004", 435 "0x00000008", 436 "0x00000010", 437 "0x00000020", 438 "0x00000040", 439 "0x00000080", 440 441 "0x00000100", 442 "0x00000200", 443 "0x00000400", 444 "0x00000800", 445 "0x00001000", 446 "0x00002000", 447 "0x00004000", 448 "0x00008000", 449 450 "0x00010000", 451 "0x00020000", 452 "0x00040000", 453 "0x00080000", 454 "0x00100000", 455 "0x00200000", 456 "0x00400000", 457 "0x00800000", 458 459 "0x01000000", 460 "0x02000000", 461 "0x04000000", 462 "0x08000000", 463 "0x10000000", 464 "0x20000000", 465 "0x40000000", 466 "0x80000000", 467}; 468 469string GetBitFieldName(int index) { 470 string var_name = "bitField"; 471 var_name += SimpleItoa(index); 472 var_name += "_"; 473 return var_name; 474} 475 476string GetBitFieldNameForBit(int bit_index) { 477 return GetBitFieldName(bit_index / 32); 478} 479 480string GenerateGetBit(int bit_index) { 481 string var_name = GetBitFieldNameForBit(bit_index); 482 int bit_in_var_index = bit_index % 32; 483 484 string mask = kBitMasks[bit_in_var_index]; 485 string result = "((" + var_name + " & " + mask + ") == " + mask + ")"; 486 return result; 487} 488 489string GenerateSetBit(int bit_index) { 490 string var_name = GetBitFieldNameForBit(bit_index); 491 int bit_in_var_index = bit_index % 32; 492 493 string mask = kBitMasks[bit_in_var_index]; 494 string result = var_name + " |= " + mask; 495 return result; 496} 497 498string GenerateClearBit(int bit_index) { 499 string var_name = GetBitFieldNameForBit(bit_index); 500 int bit_in_var_index = bit_index % 32; 501 502 string mask = kBitMasks[bit_in_var_index]; 503 string result = var_name + " = (" + var_name + " & ~" + mask + ")"; 504 return result; 505} 506 507void SetBitOperationVariables(const string name, 508 int bitIndex, map<string, string>* variables) { 509 (*variables)["get_" + name] = GenerateGetBit(bitIndex); 510 (*variables)["set_" + name] = GenerateSetBit(bitIndex); 511 (*variables)["clear_" + name] = GenerateClearBit(bitIndex); 512} 513 514} // namespace javanano 515} // namespace compiler 516} // namespace protobuf 517} // namespace google 518