javamicro_helpers.cc revision 8170787391efcb6cc6a8babc1cce35d5b1aff420
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/javamicro/javamicro_helpers.h> 39#include <google/protobuf/compiler/javamicro/javamicro_params.h> 40#include <google/protobuf/descriptor.pb.h> 41#include <google/protobuf/stubs/strutil.h> 42#include <google/protobuf/stubs/substitute.h> 43 44namespace google { 45namespace protobuf { 46namespace compiler { 47namespace javamicro { 48 49const char kThickSeparator[] = 50 "// ===================================================================\n"; 51const char kThinSeparator[] = 52 "// -------------------------------------------------------------------\n"; 53 54namespace { 55 56const char* kDefaultPackage = ""; 57 58const string& FieldName(const FieldDescriptor* field) { 59 // Groups are hacky: The name of the field is just the lower-cased name 60 // of the group type. In Java, though, we would like to retain the original 61 // capitalization of the type name. 62 if (field->type() == FieldDescriptor::TYPE_GROUP) { 63 return field->message_type()->name(); 64 } else { 65 return field->name(); 66 } 67} 68 69string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { 70 string result; 71 // Note: I distrust ctype.h due to locales. 72 for (int i = 0; i < input.size(); i++) { 73 if ('a' <= input[i] && input[i] <= 'z') { 74 if (cap_next_letter) { 75 result += input[i] + ('A' - 'a'); 76 } else { 77 result += input[i]; 78 } 79 cap_next_letter = false; 80 } else if ('A' <= input[i] && input[i] <= 'Z') { 81 if (i == 0 && !cap_next_letter) { 82 // Force first letter to lower-case unless explicitly told to 83 // capitalize it. 84 result += input[i] + ('a' - 'A'); 85 } else { 86 // Capital letters after the first are left as-is. 87 result += input[i]; 88 } 89 cap_next_letter = false; 90 } else if ('0' <= input[i] && input[i] <= '9') { 91 result += input[i]; 92 cap_next_letter = true; 93 } else { 94 cap_next_letter = true; 95 } 96 } 97 return result; 98} 99 100} // namespace 101 102string UnderscoresToCamelCase(const FieldDescriptor* field) { 103 return UnderscoresToCamelCaseImpl(FieldName(field), false); 104} 105 106string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { 107 return UnderscoresToCamelCaseImpl(FieldName(field), true); 108} 109 110string UnderscoresToCamelCase(const MethodDescriptor* method) { 111 return UnderscoresToCamelCaseImpl(method->name(), false); 112} 113 114string StripProto(const string& filename) { 115 if (HasSuffixString(filename, ".protodevel")) { 116 return StripSuffixString(filename, ".protodevel"); 117 } else { 118 return StripSuffixString(filename, ".proto"); 119 } 120} 121 122string FileClassName(const Params& params, const FileDescriptor* file) { 123 string name; 124 125 if (params.has_java_outer_classname(file->name())) { 126 name = params.java_outer_classname(file->name()); 127 } else { 128 if ((file->message_type_count() == 1) 129 || (file->enum_type_count() == 0)) { 130 // If no outer calls and only one message then 131 // use the message name as the file name 132 name = file->message_type(0)->name(); 133 } else { 134 // Use the filename it self with underscores removed 135 // and a CamelCase style name. 136 string basename; 137 string::size_type last_slash = file->name().find_last_of('/'); 138 if (last_slash == string::npos) { 139 basename = file->name(); 140 } else { 141 basename = file->name().substr(last_slash + 1); 142 } 143 name = UnderscoresToCamelCaseImpl(StripProto(basename), true); 144 } 145 } 146 147 return name; 148} 149 150string FileJavaPackage(const Params& params, const FileDescriptor* file) { 151 if (params.has_java_package(file->name())) { 152 return params.java_package(file->name()); 153 } else { 154 string result = kDefaultPackage; 155 if (!file->package().empty()) { 156 if (!result.empty()) result += '.'; 157 result += file->package(); 158 } 159 return result; 160 } 161} 162 163string ToJavaName(const Params& params, const string& full_name, 164 const FileDescriptor* file) { 165 string result; 166 if (params.java_multiple_files(file->name())) { 167 result = FileJavaPackage(params, file); 168 } else { 169 result = ClassName(params, file); 170 } 171 if (file->package().empty()) { 172 result += '.'; 173 result += full_name; 174 } else { 175 // Strip the proto package from full_name since we've replaced it with 176 // the Java package. If there isn't an outer classname then strip it too. 177 int sizeToSkipPackageName = file->package().size(); 178 int sizeToSkipOutClassName; 179 if (params.has_java_outer_classname(file->name())) { 180 sizeToSkipOutClassName = 0; 181 } else { 182 sizeToSkipOutClassName = 183 full_name.find_first_of('.', sizeToSkipPackageName + 1); 184 } 185 int sizeToSkip = sizeToSkipOutClassName > 0 ? 186 sizeToSkipOutClassName : sizeToSkipPackageName; 187 string class_name = full_name.substr(sizeToSkip + 1); 188 if (class_name == FileClassName(params, file)) { 189 // Done class_name is already present. 190 } else { 191 result += '.'; 192 result += class_name; 193 } 194 } 195 return result; 196} 197 198string ClassName(const Params& params, const FileDescriptor* descriptor) { 199 string result = FileJavaPackage(params, descriptor); 200 if (!result.empty()) result += '.'; 201 result += FileClassName(params, descriptor); 202 return result; 203} 204 205string ClassName(const Params& params, const EnumDescriptor* descriptor) { 206 string result; 207 const FileDescriptor* file = descriptor->file(); 208 const string file_name = file->name(); 209 const string full_name = descriptor->full_name(); 210 211 // Remove enum class name as we use int's for enums 212 string base_name = full_name.substr(0, full_name.find_last_of('.')); 213 214 if (!file->package().empty()) { 215 if (file->package() == base_name.substr(0, file->package().size())) { 216 // Remove package name leaving just the parent class of the enum 217 int offset = file->package().size(); 218 if (base_name.size() > offset) { 219 // Remove period between package and class name if there is a classname 220 offset += 1; 221 } 222 base_name = base_name.substr(offset); 223 } else { 224 GOOGLE_LOG(FATAL) << "Expected package name to start an enum"; 225 } 226 } 227 228 // Construct the path name from the package and outer class 229 230 // Add the java package name if it exsits 231 if (params.has_java_package(file_name)) { 232 result += params.java_package(file_name); 233 } 234 235 // Add the outer classname if it exists 236 if (params.has_java_outer_classname(file_name)) { 237 if (!result.empty()) { 238 result += "."; 239 } 240 result += params.java_outer_classname(file_name); 241 } 242 243 // Create the full class name from the base and path 244 if (!base_name.empty()) { 245 if (!result.empty()) { 246 result += "."; 247 } 248 result += base_name; 249 } 250 return result; 251} 252 253string FieldConstantName(const FieldDescriptor *field) { 254 string name = field->name() + "_FIELD_NUMBER"; 255 UpperString(&name); 256 return name; 257} 258 259JavaType GetJavaType(FieldDescriptor::Type field_type) { 260 switch (field_type) { 261 case FieldDescriptor::TYPE_INT32: 262 case FieldDescriptor::TYPE_UINT32: 263 case FieldDescriptor::TYPE_SINT32: 264 case FieldDescriptor::TYPE_FIXED32: 265 case FieldDescriptor::TYPE_SFIXED32: 266 return JAVATYPE_INT; 267 268 case FieldDescriptor::TYPE_INT64: 269 case FieldDescriptor::TYPE_UINT64: 270 case FieldDescriptor::TYPE_SINT64: 271 case FieldDescriptor::TYPE_FIXED64: 272 case FieldDescriptor::TYPE_SFIXED64: 273 return JAVATYPE_LONG; 274 275 case FieldDescriptor::TYPE_FLOAT: 276 return JAVATYPE_FLOAT; 277 278 case FieldDescriptor::TYPE_DOUBLE: 279 return JAVATYPE_DOUBLE; 280 281 case FieldDescriptor::TYPE_BOOL: 282 return JAVATYPE_BOOLEAN; 283 284 case FieldDescriptor::TYPE_STRING: 285 return JAVATYPE_STRING; 286 287 case FieldDescriptor::TYPE_BYTES: 288 return JAVATYPE_BYTES; 289 290 case FieldDescriptor::TYPE_ENUM: 291 return JAVATYPE_ENUM; 292 293 case FieldDescriptor::TYPE_GROUP: 294 case FieldDescriptor::TYPE_MESSAGE: 295 return JAVATYPE_MESSAGE; 296 297 // No default because we want the compiler to complain if any new 298 // types are added. 299 } 300 301 GOOGLE_LOG(FATAL) << "Can't get here."; 302 return JAVATYPE_INT; 303} 304 305const char* BoxedPrimitiveTypeName(JavaType type) { 306 switch (type) { 307 case JAVATYPE_INT : return "java.lang.Integer"; 308 case JAVATYPE_LONG : return "java.lang.Long"; 309 case JAVATYPE_FLOAT : return "java.lang.Float"; 310 case JAVATYPE_DOUBLE : return "java.lang.Double"; 311 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 312 case JAVATYPE_STRING : return "java.lang.String"; 313 case JAVATYPE_BYTES : return "com.google.protobuf.micro.ByteStringMicro"; 314 case JAVATYPE_ENUM : return "java.lang.Integer"; 315 case JAVATYPE_MESSAGE: return NULL; 316 317 // No default because we want the compiler to complain if any new 318 // JavaTypes are added. 319 } 320 321 GOOGLE_LOG(FATAL) << "Can't get here."; 322 return NULL; 323} 324 325bool AllAscii(const string& text) { 326 for (int i = 0; i < text.size(); i++) { 327 if ((text[i] & 0x80) != 0) { 328 return false; 329 } 330 } 331 return true; 332} 333 334string DefaultValue(const Params& params, const FieldDescriptor* field) { 335 // Switch on cpp_type since we need to know which default_value_* method 336 // of FieldDescriptor to call. 337 switch (field->cpp_type()) { 338 case FieldDescriptor::CPPTYPE_INT32: 339 return SimpleItoa(field->default_value_int32()); 340 case FieldDescriptor::CPPTYPE_UINT32: 341 // Need to print as a signed int since Java has no unsigned. 342 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 343 case FieldDescriptor::CPPTYPE_INT64: 344 return SimpleItoa(field->default_value_int64()) + "L"; 345 case FieldDescriptor::CPPTYPE_UINT64: 346 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 347 "L"; 348 case FieldDescriptor::CPPTYPE_DOUBLE: { 349 double value = field->default_value_double(); 350 if (value == numeric_limits<double>::infinity()) { 351 return "Double.POSITIVE_INFINITY"; 352 } else if (value == -numeric_limits<double>::infinity()) { 353 return "Double.NEGATIVE_INFINITY"; 354 } else if (value != value) { 355 return "Double.NaN"; 356 } else { 357 return SimpleDtoa(value) + "D"; 358 } 359 } 360 case FieldDescriptor::CPPTYPE_FLOAT: { 361 float value = field->default_value_float(); 362 if (value == numeric_limits<float>::infinity()) { 363 return "Float.POSITIVE_INFINITY"; 364 } else if (value == -numeric_limits<float>::infinity()) { 365 return "Float.NEGATIVE_INFINITY"; 366 } else if (value != value) { 367 return "Float.NaN"; 368 } else { 369 return SimpleFtoa(value) + "F"; 370 } 371 } 372 case FieldDescriptor::CPPTYPE_BOOL: 373 return field->default_value_bool() ? "true" : "false"; 374 case FieldDescriptor::CPPTYPE_STRING: 375 if (field->type() == FieldDescriptor::TYPE_BYTES) { 376 if (field->has_default_value()) { 377 // See comments in Internal.java for gory details. 378 return strings::Substitute( 379 "com.google.protobuf.micro.ByteStringMicro.copyFromUtf8(\"$0\")", 380 CEscape(field->default_value_string())); 381 } else { 382 return "com.google.protobuf.micro.ByteStringMicro.EMPTY"; 383 } 384 } else { 385 if (AllAscii(field->default_value_string())) { 386 // All chars are ASCII. In this case CEscape() works fine. 387 return "\"" + CEscape(field->default_value_string()) + "\""; 388 } else { 389 // See comments in Internal.java for gory details. 390 // BUG: Internal NOT SUPPORTED need to fix!! 391 return strings::Substitute( 392 "com.google.protobuf.micro.Internal.stringDefaultValue(\"$0\")", 393 CEscape(field->default_value_string())); 394 } 395 } 396 397 case FieldDescriptor::CPPTYPE_ENUM: 398 return ClassName(params, field->enum_type()) + "." + 399 field->default_value_enum()->name(); 400 401 case FieldDescriptor::CPPTYPE_MESSAGE: 402 return ClassName(params, field->message_type()) + ".getDefaultInstance()"; 403 404 // No default because we want the compiler to complain if any new 405 // types are added. 406 } 407 408 GOOGLE_LOG(FATAL) << "Can't get here."; 409 return ""; 410} 411 412} // namespace javamicro 413} // namespace compiler 414} // namespace protobuf 415} // namespace google 416