javanano_helpers.cc revision 26266cd4660ffe1f3d6015b715713ee654c5b936
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 // Switch on cpp_type since we need to know which default_value_* method 361 // of FieldDescriptor to call. 362 switch (field->cpp_type()) { 363 case FieldDescriptor::CPPTYPE_INT32: 364 return SimpleItoa(field->default_value_int32()); 365 case FieldDescriptor::CPPTYPE_UINT32: 366 // Need to print as a signed int since Java has no unsigned. 367 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 368 case FieldDescriptor::CPPTYPE_INT64: 369 return SimpleItoa(field->default_value_int64()) + "L"; 370 case FieldDescriptor::CPPTYPE_UINT64: 371 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 372 "L"; 373 case FieldDescriptor::CPPTYPE_DOUBLE: { 374 double value = field->default_value_double(); 375 if (value == numeric_limits<double>::infinity()) { 376 return "Double.POSITIVE_INFINITY"; 377 } else if (value == -numeric_limits<double>::infinity()) { 378 return "Double.NEGATIVE_INFINITY"; 379 } else if (value != value) { 380 return "Double.NaN"; 381 } else { 382 return SimpleDtoa(value) + "D"; 383 } 384 } 385 case FieldDescriptor::CPPTYPE_FLOAT: { 386 float value = field->default_value_float(); 387 if (value == numeric_limits<float>::infinity()) { 388 return "Float.POSITIVE_INFINITY"; 389 } else if (value == -numeric_limits<float>::infinity()) { 390 return "Float.NEGATIVE_INFINITY"; 391 } else if (value != value) { 392 return "Float.NaN"; 393 } else { 394 return SimpleFtoa(value) + "F"; 395 } 396 } 397 case FieldDescriptor::CPPTYPE_BOOL: 398 return field->default_value_bool() ? "true" : "false"; 399 case FieldDescriptor::CPPTYPE_STRING: 400 if (!field->default_value_string().empty()) { 401 // Point it to the static final in the generated code. 402 return FieldDefaultConstantName(field); 403 } else { 404 if (field->type() == FieldDescriptor::TYPE_BYTES) { 405 return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; 406 } else { 407 return "\"\""; 408 } 409 } 410 411 case FieldDescriptor::CPPTYPE_ENUM: 412 return ClassName(params, field->enum_type()) + "." + 413 field->default_value_enum()->name(); 414 415 case FieldDescriptor::CPPTYPE_MESSAGE: 416 return "null"; 417 418 // No default because we want the compiler to complain if any new 419 // types are added. 420 } 421 422 GOOGLE_LOG(FATAL) << "Can't get here."; 423 return ""; 424} 425 426 427static const char* kBitMasks[] = { 428 "0x00000001", 429 "0x00000002", 430 "0x00000004", 431 "0x00000008", 432 "0x00000010", 433 "0x00000020", 434 "0x00000040", 435 "0x00000080", 436 437 "0x00000100", 438 "0x00000200", 439 "0x00000400", 440 "0x00000800", 441 "0x00001000", 442 "0x00002000", 443 "0x00004000", 444 "0x00008000", 445 446 "0x00010000", 447 "0x00020000", 448 "0x00040000", 449 "0x00080000", 450 "0x00100000", 451 "0x00200000", 452 "0x00400000", 453 "0x00800000", 454 455 "0x01000000", 456 "0x02000000", 457 "0x04000000", 458 "0x08000000", 459 "0x10000000", 460 "0x20000000", 461 "0x40000000", 462 "0x80000000", 463}; 464 465string GetBitFieldName(int index) { 466 string var_name = "bitField"; 467 var_name += SimpleItoa(index); 468 var_name += "_"; 469 return var_name; 470} 471 472string GetBitFieldNameForBit(int bit_index) { 473 return GetBitFieldName(bit_index / 32); 474} 475 476string GenerateGetBit(int bit_index) { 477 string var_name = GetBitFieldNameForBit(bit_index); 478 int bit_in_var_index = bit_index % 32; 479 480 string mask = kBitMasks[bit_in_var_index]; 481 string result = "((" + var_name + " & " + mask + ") == " + mask + ")"; 482 return result; 483} 484 485string GenerateSetBit(int bit_index) { 486 string var_name = GetBitFieldNameForBit(bit_index); 487 int bit_in_var_index = bit_index % 32; 488 489 string mask = kBitMasks[bit_in_var_index]; 490 string result = var_name + " |= " + mask; 491 return result; 492} 493 494string GenerateClearBit(int bit_index) { 495 string var_name = GetBitFieldNameForBit(bit_index); 496 int bit_in_var_index = bit_index % 32; 497 498 string mask = kBitMasks[bit_in_var_index]; 499 string result = var_name + " = (" + var_name + " & ~" + mask + ")"; 500 return result; 501} 502 503void SetBitOperationVariables(const string name, 504 int bitIndex, map<string, string>* variables) { 505 (*variables)["get_" + name] = GenerateGetBit(bitIndex); 506 (*variables)["set_" + name] = GenerateSetBit(bitIndex); 507 (*variables)["clear_" + name] = GenerateClearBit(bitIndex); 508} 509 510} // namespace javanano 511} // namespace compiler 512} // namespace protobuf 513} // namespace google 514