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