javanano_helpers.cc revision 64d8d8f89050c5ada85341f967af391f4716a7cb
1324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Protocol Buffers - Google's data interchange format 2324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Copyright 2008 Google Inc. All rights reserved. 3324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// http://code.google.com/p/protobuf/ 4324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// 5324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Redistribution and use in source and binary forms, with or without 6324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// modification, are permitted provided that the following conditions are 7324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// met: 8324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// 9324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// * Redistributions of source code must retain the above copyright 10324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// notice, this list of conditions and the following disclaimer. 11324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// * Redistributions in binary form must reproduce the above 12324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// copyright notice, this list of conditions and the following disclaimer 13324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// in the documentation and/or other materials provided with the 14324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// distribution. 15324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// * Neither the name of Google Inc. nor the names of its 16324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// contributors may be used to endorse or promote products derived from 17324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// this software without specific prior written permission. 18324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// 19324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 31324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Author: kenton@google.com (Kenton Varda) 32324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Based on original Protocol Buffers design by 33324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver// Sanjay Ghemawat, Jeff Dean, and others. 34324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 35324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <vector> 36324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 37324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/compiler/javanano/javanano_helpers.h> 38324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/compiler/javanano/javanano_params.h> 39324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/descriptor.pb.h> 40324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/stubs/strutil.h> 41324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#include <google/protobuf/stubs/substitute.h> 42324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver 43namespace google { 44namespace protobuf { 45namespace compiler { 46namespace javanano { 47 48const char kThickSeparator[] = 49 "// ===================================================================\n"; 50const char kThinSeparator[] = 51 "// -------------------------------------------------------------------\n"; 52 53namespace { 54 55const char* kDefaultPackage = ""; 56 57const string& FieldName(const FieldDescriptor* field) { 58 // Groups are hacky: The name of the field is just the lower-cased name 59 // of the group type. In Java, though, we would like to retain the original 60 // capitalization of the type name. 61 if (field->type() == FieldDescriptor::TYPE_GROUP) { 62 return field->message_type()->name(); 63 } else { 64 return field->name(); 65 } 66} 67 68string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { 69 string result; 70 // Note: I distrust ctype.h due to locales. 71 for (int i = 0; i < input.size(); i++) { 72 if ('a' <= input[i] && input[i] <= 'z') { 73 if (cap_next_letter) { 74 result += input[i] + ('A' - 'a'); 75 } else { 76 result += input[i]; 77 } 78 cap_next_letter = false; 79 } else if ('A' <= input[i] && input[i] <= 'Z') { 80 if (i == 0 && !cap_next_letter) { 81 // Force first letter to lower-case unless explicitly told to 82 // capitalize it. 83 result += input[i] + ('a' - 'A'); 84 } else { 85 // Capital letters after the first are left as-is. 86 result += input[i]; 87 } 88 cap_next_letter = false; 89 } else if ('0' <= input[i] && input[i] <= '9') { 90 result += input[i]; 91 cap_next_letter = true; 92 } else { 93 cap_next_letter = true; 94 } 95 } 96 return result; 97} 98 99} // namespace 100 101string UnderscoresToCamelCase(const FieldDescriptor* field) { 102 return UnderscoresToCamelCaseImpl(FieldName(field), false); 103} 104 105string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { 106 return UnderscoresToCamelCaseImpl(FieldName(field), true); 107} 108 109string UnderscoresToCamelCase(const MethodDescriptor* method) { 110 return UnderscoresToCamelCaseImpl(method->name(), false); 111} 112 113string StripProto(const string& filename) { 114 if (HasSuffixString(filename, ".protodevel")) { 115 return StripSuffixString(filename, ".protodevel"); 116 } else { 117 return StripSuffixString(filename, ".proto"); 118 } 119} 120 121string FileClassName(const Params& params, const FileDescriptor* file) { 122 string name; 123 124 if (params.has_java_outer_classname(file->name())) { 125 name = params.java_outer_classname(file->name()); 126 } else { 127 if ((file->message_type_count() == 1) 128 || (file->enum_type_count() == 0)) { 129 // If no outer calls and only one message then 130 // use the message name as the file name 131 name = file->message_type(0)->name(); 132 } else { 133 // Use the filename it self with underscores removed 134 // and a CamelCase style name. 135 string basename; 136 string::size_type last_slash = file->name().find_last_of('/'); 137 if (last_slash == string::npos) { 138 basename = file->name(); 139 } else { 140 basename = file->name().substr(last_slash + 1); 141 } 142 name = UnderscoresToCamelCaseImpl(StripProto(basename), true); 143 } 144 } 145 146 return name; 147} 148 149string FileJavaPackage(const Params& params, const FileDescriptor* file) { 150 if (params.has_java_package(file->name())) { 151 return params.java_package(file->name()); 152 } else { 153 string result = kDefaultPackage; 154 if (!file->package().empty()) { 155 if (!result.empty()) result += '.'; 156 result += file->package(); 157 } 158 return result; 159 } 160} 161 162string ToJavaName(const Params& params, const string& full_name, 163 const FileDescriptor* file) { 164 string result; 165 if (params.java_multiple_files()) { 166 result = FileJavaPackage(params, file); 167 } else { 168 result = ClassName(params, file); 169 } 170 if (file->package().empty()) { 171 result += '.'; 172 result += full_name; 173 } else { 174 // Strip the proto package from full_name since we've replaced it with 175 // the Java package. If there isn't an outer classname then strip it too. 176 int sizeToSkipPackageName = file->package().size(); 177 int sizeToSkipOutClassName; 178 if (params.has_java_outer_classname(file->name())) { 179 sizeToSkipOutClassName = 0; 180 } else { 181 sizeToSkipOutClassName = 182 full_name.find_first_of('.', sizeToSkipPackageName + 1); 183 } 184 int sizeToSkip = sizeToSkipOutClassName > 0 ? 185 sizeToSkipOutClassName : sizeToSkipPackageName; 186 string class_name = full_name.substr(sizeToSkip + 1); 187 if (class_name == FileClassName(params, file)) { 188 // Done class_name is already present. 189 } else { 190 result += '.'; 191 result += class_name; 192 } 193 } 194 return result; 195} 196 197string ClassName(const Params& params, const FileDescriptor* descriptor) { 198 string result = FileJavaPackage(params, descriptor); 199 if (!result.empty()) result += '.'; 200 result += FileClassName(params, descriptor); 201 return result; 202} 203 204string ClassName(const Params& params, const EnumDescriptor* descriptor) { 205 string result; 206 const FileDescriptor* file = descriptor->file(); 207 const string file_name = file->name(); 208 const string full_name = descriptor->full_name(); 209 210 // Remove enum class name as we use int's for enums 211 string base_name = full_name.substr(0, full_name.find_last_of('.')); 212 213 if (!file->package().empty()) { 214 if (file->package() == base_name.substr(0, file->package().size())) { 215 // Remove package name leaving just the parent class of the enum 216 int offset = file->package().size(); 217 if (base_name.size() > offset) { 218 // Remove period between package and class name if there is a classname 219 offset += 1; 220 } 221 base_name = base_name.substr(offset); 222 } else { 223 GOOGLE_LOG(FATAL) << "Expected package name to start an enum"; 224 } 225 } 226 227 // Construct the path name from the package and outer class 228 229 // Add the java package name if it exsits 230 if (params.has_java_package(file_name)) { 231 result += params.java_package(file_name); 232 } 233 234 // Add the outer classname if it exists 235 if (params.has_java_outer_classname(file_name)) { 236 if (!result.empty()) { 237 result += "."; 238 } 239 result += params.java_outer_classname(file_name); 240 } 241 242 // Create the full class name from the base and path 243 if (!base_name.empty()) { 244 if (!result.empty()) { 245 result += "."; 246 } 247 result += base_name; 248 } 249 return result; 250} 251 252string FieldConstantName(const FieldDescriptor *field) { 253 string name = field->name() + "_FIELD_NUMBER"; 254 UpperString(&name); 255 return name; 256} 257 258string FieldDefaultConstantName(const FieldDescriptor *field) { 259 string name = field->name() + "_DEFAULT"; 260 UpperString(&name); 261 return name; 262} 263 264JavaType GetJavaType(FieldDescriptor::Type field_type) { 265 switch (field_type) { 266 case FieldDescriptor::TYPE_INT32: 267 case FieldDescriptor::TYPE_UINT32: 268 case FieldDescriptor::TYPE_SINT32: 269 case FieldDescriptor::TYPE_FIXED32: 270 case FieldDescriptor::TYPE_SFIXED32: 271 return JAVATYPE_INT; 272 273 case FieldDescriptor::TYPE_INT64: 274 case FieldDescriptor::TYPE_UINT64: 275 case FieldDescriptor::TYPE_SINT64: 276 case FieldDescriptor::TYPE_FIXED64: 277 case FieldDescriptor::TYPE_SFIXED64: 278 return JAVATYPE_LONG; 279 280 case FieldDescriptor::TYPE_FLOAT: 281 return JAVATYPE_FLOAT; 282 283 case FieldDescriptor::TYPE_DOUBLE: 284 return JAVATYPE_DOUBLE; 285 286 case FieldDescriptor::TYPE_BOOL: 287 return JAVATYPE_BOOLEAN; 288 289 case FieldDescriptor::TYPE_STRING: 290 return JAVATYPE_STRING; 291 292 case FieldDescriptor::TYPE_BYTES: 293 return JAVATYPE_BYTES; 294 295 case FieldDescriptor::TYPE_ENUM: 296 return JAVATYPE_ENUM; 297 298 case FieldDescriptor::TYPE_GROUP: 299 case FieldDescriptor::TYPE_MESSAGE: 300 return JAVATYPE_MESSAGE; 301 302 // No default because we want the compiler to complain if any new 303 // types are added. 304 } 305 306 GOOGLE_LOG(FATAL) << "Can't get here."; 307 return JAVATYPE_INT; 308} 309 310const char* BoxedPrimitiveTypeName(JavaType type) { 311 switch (type) { 312 case JAVATYPE_INT : return "java.lang.Integer"; 313 case JAVATYPE_LONG : return "java.lang.Long"; 314 case JAVATYPE_FLOAT : return "java.lang.Float"; 315 case JAVATYPE_DOUBLE : return "java.lang.Double"; 316 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 317 case JAVATYPE_STRING : return "java.lang.String"; 318 case JAVATYPE_BYTES : return "byte[]"; 319 case JAVATYPE_ENUM : return "java.lang.Integer"; 320 case JAVATYPE_MESSAGE: return NULL; 321 322 // No default because we want the compiler to complain if any new 323 // JavaTypes are added. 324 } 325 326 GOOGLE_LOG(FATAL) << "Can't get here."; 327 return NULL; 328} 329 330string EmptyArrayName(const Params& params, const FieldDescriptor* field) { 331 switch (GetJavaType(field)) { 332 case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 333 case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY"; 334 case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY"; 335 case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY"; 336 case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY"; 337 case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY"; 338 case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY"; 339 case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; 340 case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY"; 341 342 // No default because we want the compiler to complain if any new 343 // JavaTypes are added. 344 } 345 346 GOOGLE_LOG(FATAL) << "Can't get here."; 347 return ""; 348} 349 350string DefaultValue(const Params& params, const FieldDescriptor* field) { 351 if (field->label() == FieldDescriptor::LABEL_REPEATED) { 352 return EmptyArrayName(params, field); 353 } 354 355 // Switch on cpp_type since we need to know which default_value_* method 356 // of FieldDescriptor to call. 357 switch (field->cpp_type()) { 358 case FieldDescriptor::CPPTYPE_INT32: 359 return SimpleItoa(field->default_value_int32()); 360 case FieldDescriptor::CPPTYPE_UINT32: 361 // Need to print as a signed int since Java has no unsigned. 362 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 363 case FieldDescriptor::CPPTYPE_INT64: 364 return SimpleItoa(field->default_value_int64()) + "L"; 365 case FieldDescriptor::CPPTYPE_UINT64: 366 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 367 "L"; 368 case FieldDescriptor::CPPTYPE_DOUBLE: 369 return SimpleDtoa(field->default_value_double()) + "D"; 370 case FieldDescriptor::CPPTYPE_FLOAT: 371 return SimpleFtoa(field->default_value_float()) + "F"; 372 case FieldDescriptor::CPPTYPE_BOOL: 373 return field->default_value_bool() ? "true" : "false"; 374 case FieldDescriptor::CPPTYPE_STRING: 375 if (!field->default_value_string().empty()) { 376 // Point it to the static final in the generated code. 377 return FieldDefaultConstantName(field); 378 } else { 379 if (field->type() == FieldDescriptor::TYPE_BYTES) { 380 return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; 381 } else { 382 return "\"\""; 383 } 384 } 385 386 case FieldDescriptor::CPPTYPE_ENUM: 387 return ClassName(params, field->enum_type()) + "." + 388 field->default_value_enum()->name(); 389 390 case FieldDescriptor::CPPTYPE_MESSAGE: 391 return "null"; 392 393 // No default because we want the compiler to complain if any new 394 // types are added. 395 } 396 397 GOOGLE_LOG(FATAL) << "Can't get here."; 398 return ""; 399} 400 401} // namespace javanano 402} // namespace compiler 403} // namespace protobuf 404} // namespace google 405