javanano_helpers.cc revision 5cc242074f189837b38e7768b57ccfb0bca258df
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 return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default"; 265} 266 267JavaType GetJavaType(FieldDescriptor::Type field_type) { 268 switch (field_type) { 269 case FieldDescriptor::TYPE_INT32: 270 case FieldDescriptor::TYPE_UINT32: 271 case FieldDescriptor::TYPE_SINT32: 272 case FieldDescriptor::TYPE_FIXED32: 273 case FieldDescriptor::TYPE_SFIXED32: 274 return JAVATYPE_INT; 275 276 case FieldDescriptor::TYPE_INT64: 277 case FieldDescriptor::TYPE_UINT64: 278 case FieldDescriptor::TYPE_SINT64: 279 case FieldDescriptor::TYPE_FIXED64: 280 case FieldDescriptor::TYPE_SFIXED64: 281 return JAVATYPE_LONG; 282 283 case FieldDescriptor::TYPE_FLOAT: 284 return JAVATYPE_FLOAT; 285 286 case FieldDescriptor::TYPE_DOUBLE: 287 return JAVATYPE_DOUBLE; 288 289 case FieldDescriptor::TYPE_BOOL: 290 return JAVATYPE_BOOLEAN; 291 292 case FieldDescriptor::TYPE_STRING: 293 return JAVATYPE_STRING; 294 295 case FieldDescriptor::TYPE_BYTES: 296 return JAVATYPE_BYTES; 297 298 case FieldDescriptor::TYPE_ENUM: 299 return JAVATYPE_ENUM; 300 301 case FieldDescriptor::TYPE_GROUP: 302 case FieldDescriptor::TYPE_MESSAGE: 303 return JAVATYPE_MESSAGE; 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 JAVATYPE_INT; 311} 312 313const char* BoxedPrimitiveTypeName(JavaType type) { 314 switch (type) { 315 case JAVATYPE_INT : return "java.lang.Integer"; 316 case JAVATYPE_LONG : return "java.lang.Long"; 317 case JAVATYPE_FLOAT : return "java.lang.Float"; 318 case JAVATYPE_DOUBLE : return "java.lang.Double"; 319 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 320 case JAVATYPE_STRING : return "java.lang.String"; 321 case JAVATYPE_BYTES : return "byte[]"; 322 case JAVATYPE_ENUM : return "java.lang.Integer"; 323 case JAVATYPE_MESSAGE: return NULL; 324 325 // No default because we want the compiler to complain if any new 326 // JavaTypes are added. 327 } 328 329 GOOGLE_LOG(FATAL) << "Can't get here."; 330 return NULL; 331} 332 333string EmptyArrayName(const Params& params, const FieldDescriptor* field) { 334 switch (GetJavaType(field)) { 335 case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 336 case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY"; 337 case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY"; 338 case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY"; 339 case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY"; 340 case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY"; 341 case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY"; 342 case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 343 case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY"; 344 345 // No default because we want the compiler to complain if any new 346 // JavaTypes are added. 347 } 348 349 GOOGLE_LOG(FATAL) << "Can't get here."; 350 return ""; 351} 352 353string DefaultValue(const Params& params, const FieldDescriptor* field) { 354 if (field->label() == FieldDescriptor::LABEL_REPEATED) { 355 return EmptyArrayName(params, field); 356 } 357 358 if (params.use_reference_types_for_primitives()) { 359 return "null"; 360 } 361 362 // Switch on cpp_type since we need to know which default_value_* method 363 // of FieldDescriptor to call. 364 switch (field->cpp_type()) { 365 case FieldDescriptor::CPPTYPE_INT32: 366 return SimpleItoa(field->default_value_int32()); 367 case FieldDescriptor::CPPTYPE_UINT32: 368 // Need to print as a signed int since Java has no unsigned. 369 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 370 case FieldDescriptor::CPPTYPE_INT64: 371 return SimpleItoa(field->default_value_int64()) + "L"; 372 case FieldDescriptor::CPPTYPE_UINT64: 373 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 374 "L"; 375 case FieldDescriptor::CPPTYPE_DOUBLE: { 376 double value = field->default_value_double(); 377 if (value == numeric_limits<double>::infinity()) { 378 return "Double.POSITIVE_INFINITY"; 379 } else if (value == -numeric_limits<double>::infinity()) { 380 return "Double.NEGATIVE_INFINITY"; 381 } else if (value != value) { 382 return "Double.NaN"; 383 } else { 384 return SimpleDtoa(value) + "D"; 385 } 386 } 387 case FieldDescriptor::CPPTYPE_FLOAT: { 388 float value = field->default_value_float(); 389 if (value == numeric_limits<float>::infinity()) { 390 return "Float.POSITIVE_INFINITY"; 391 } else if (value == -numeric_limits<float>::infinity()) { 392 return "Float.NEGATIVE_INFINITY"; 393 } else if (value != value) { 394 return "Float.NaN"; 395 } else { 396 return SimpleFtoa(value) + "F"; 397 } 398 } 399 case FieldDescriptor::CPPTYPE_BOOL: 400 return field->default_value_bool() ? "true" : "false"; 401 case FieldDescriptor::CPPTYPE_STRING: 402 if (!field->default_value_string().empty()) { 403 // Point it to the static final in the generated code. 404 return FieldDefaultConstantName(field); 405 } else { 406 if (field->type() == FieldDescriptor::TYPE_BYTES) { 407 return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; 408 } else { 409 return "\"\""; 410 } 411 } 412 413 case FieldDescriptor::CPPTYPE_ENUM: 414 return ClassName(params, field->enum_type()) + "." + 415 field->default_value_enum()->name(); 416 417 case FieldDescriptor::CPPTYPE_MESSAGE: 418 return "null"; 419 420 // No default because we want the compiler to complain if any new 421 // types are added. 422 } 423 424 GOOGLE_LOG(FATAL) << "Can't get here."; 425 return ""; 426} 427 428 429static const char* kBitMasks[] = { 430 "0x00000001", 431 "0x00000002", 432 "0x00000004", 433 "0x00000008", 434 "0x00000010", 435 "0x00000020", 436 "0x00000040", 437 "0x00000080", 438 439 "0x00000100", 440 "0x00000200", 441 "0x00000400", 442 "0x00000800", 443 "0x00001000", 444 "0x00002000", 445 "0x00004000", 446 "0x00008000", 447 448 "0x00010000", 449 "0x00020000", 450 "0x00040000", 451 "0x00080000", 452 "0x00100000", 453 "0x00200000", 454 "0x00400000", 455 "0x00800000", 456 457 "0x01000000", 458 "0x02000000", 459 "0x04000000", 460 "0x08000000", 461 "0x10000000", 462 "0x20000000", 463 "0x40000000", 464 "0x80000000", 465}; 466 467string GetBitFieldName(int index) { 468 string var_name = "bitField"; 469 var_name += SimpleItoa(index); 470 var_name += "_"; 471 return var_name; 472} 473 474string GetBitFieldNameForBit(int bit_index) { 475 return GetBitFieldName(bit_index / 32); 476} 477 478string GenerateGetBit(int bit_index) { 479 string var_name = GetBitFieldNameForBit(bit_index); 480 int bit_in_var_index = bit_index % 32; 481 482 string mask = kBitMasks[bit_in_var_index]; 483 string result = "((" + var_name + " & " + mask + ") != 0)"; 484 return result; 485} 486 487string GenerateSetBit(int bit_index) { 488 string var_name = GetBitFieldNameForBit(bit_index); 489 int bit_in_var_index = bit_index % 32; 490 491 string mask = kBitMasks[bit_in_var_index]; 492 string result = var_name + " |= " + mask; 493 return result; 494} 495 496string GenerateClearBit(int bit_index) { 497 string var_name = GetBitFieldNameForBit(bit_index); 498 int bit_in_var_index = bit_index % 32; 499 500 string mask = kBitMasks[bit_in_var_index]; 501 string result = var_name + " = (" + var_name + " & ~" + mask + ")"; 502 return result; 503} 504 505string GenerateDifferentBit(int bit_index) { 506 string var_name = GetBitFieldNameForBit(bit_index); 507 int bit_in_var_index = bit_index % 32; 508 509 string mask = kBitMasks[bit_in_var_index]; 510 string result = "((" + var_name + " & " + mask 511 + ") != (other." + var_name + " & " + mask + "))"; 512 return result; 513} 514 515void SetBitOperationVariables(const string name, 516 int bitIndex, map<string, string>* variables) { 517 (*variables)["get_" + name] = GenerateGetBit(bitIndex); 518 (*variables)["set_" + name] = GenerateSetBit(bitIndex); 519 (*variables)["clear_" + name] = GenerateClearBit(bitIndex); 520 (*variables)["different_" + name] = GenerateDifferentBit(bitIndex); 521} 522 523} // namespace javanano 524} // namespace compiler 525} // namespace protobuf 526} // namespace google 527