javamicro_helpers.cc revision 624c448fbef20a1a2fad2289f622b468c25763d1
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 if (params.has_java_outer_classname(file->name())) { 124 return params.java_outer_classname(file->name()); 125 } else { 126 // Use the filename itself with underscores removed 127 // and a CamelCase style name. 128 string basename; 129 string::size_type last_slash = file->name().find_last_of('/'); 130 if (last_slash == string::npos) { 131 basename = file->name(); 132 } else { 133 basename = file->name().substr(last_slash + 1); 134 } 135 return UnderscoresToCamelCaseImpl(StripProto(basename), true); 136 } 137} 138 139string FileJavaPackage(const Params& params, const FileDescriptor* file) { 140 if (params.has_java_package(file->name())) { 141 return params.java_package(file->name()); 142 } else { 143 string result = kDefaultPackage; 144 if (!file->package().empty()) { 145 if (!result.empty()) result += '.'; 146 result += file->package(); 147 } 148 return result; 149 } 150} 151 152bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) { 153 // Enums and extensions need the outer class as the scope. 154 if (file->enum_type_count() != 0 || file->extension_count() != 0) { 155 return true; 156 } 157 // Messages need the outer class only if java_multiple_files is false. 158 return !params.java_multiple_files(file->name()); 159} 160 161string ToJavaName(const Params& params, const string& name, bool is_class, 162 const Descriptor* parent, const FileDescriptor* file) { 163 string result; 164 if (parent != NULL) { 165 result.append(ClassName(params, parent)); 166 } else if (is_class && params.java_multiple_files(file->name())) { 167 result.append(FileJavaPackage(params, file)); 168 } else { 169 result.append(ClassName(params, file)); 170 } 171 if (!result.empty()) result.append(1, '.'); 172 result.append(name); // TODO(maxtroy): add '_' if name is a Java keyword. 173 return result; 174} 175 176string ClassName(const Params& params, const FileDescriptor* descriptor) { 177 string result = FileJavaPackage(params, descriptor); 178 if (!result.empty()) result += '.'; 179 result += FileClassName(params, descriptor); 180 return result; 181} 182 183string ClassName(const Params& params, const EnumDescriptor* descriptor) { 184 // An enum's class name is the enclosing message's class name or the outer 185 // class name. 186 const Descriptor* parent = descriptor->containing_type(); 187 if (parent != NULL) { 188 return ClassName(params, parent); 189 } else { 190 return ClassName(params, descriptor->file()); 191 } 192} 193 194string FieldConstantName(const FieldDescriptor *field) { 195 string name = field->name() + "_FIELD_NUMBER"; 196 UpperString(&name); 197 return name; 198} 199 200JavaType GetJavaType(FieldDescriptor::Type field_type) { 201 switch (field_type) { 202 case FieldDescriptor::TYPE_INT32: 203 case FieldDescriptor::TYPE_UINT32: 204 case FieldDescriptor::TYPE_SINT32: 205 case FieldDescriptor::TYPE_FIXED32: 206 case FieldDescriptor::TYPE_SFIXED32: 207 return JAVATYPE_INT; 208 209 case FieldDescriptor::TYPE_INT64: 210 case FieldDescriptor::TYPE_UINT64: 211 case FieldDescriptor::TYPE_SINT64: 212 case FieldDescriptor::TYPE_FIXED64: 213 case FieldDescriptor::TYPE_SFIXED64: 214 return JAVATYPE_LONG; 215 216 case FieldDescriptor::TYPE_FLOAT: 217 return JAVATYPE_FLOAT; 218 219 case FieldDescriptor::TYPE_DOUBLE: 220 return JAVATYPE_DOUBLE; 221 222 case FieldDescriptor::TYPE_BOOL: 223 return JAVATYPE_BOOLEAN; 224 225 case FieldDescriptor::TYPE_STRING: 226 return JAVATYPE_STRING; 227 228 case FieldDescriptor::TYPE_BYTES: 229 return JAVATYPE_BYTES; 230 231 case FieldDescriptor::TYPE_ENUM: 232 return JAVATYPE_ENUM; 233 234 case FieldDescriptor::TYPE_GROUP: 235 case FieldDescriptor::TYPE_MESSAGE: 236 return JAVATYPE_MESSAGE; 237 238 // No default because we want the compiler to complain if any new 239 // types are added. 240 } 241 242 GOOGLE_LOG(FATAL) << "Can't get here."; 243 return JAVATYPE_INT; 244} 245 246const char* BoxedPrimitiveTypeName(JavaType type) { 247 switch (type) { 248 case JAVATYPE_INT : return "java.lang.Integer"; 249 case JAVATYPE_LONG : return "java.lang.Long"; 250 case JAVATYPE_FLOAT : return "java.lang.Float"; 251 case JAVATYPE_DOUBLE : return "java.lang.Double"; 252 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 253 case JAVATYPE_STRING : return "java.lang.String"; 254 case JAVATYPE_BYTES : return "com.google.protobuf.micro.ByteStringMicro"; 255 case JAVATYPE_ENUM : return "java.lang.Integer"; 256 case JAVATYPE_MESSAGE: return NULL; 257 258 // No default because we want the compiler to complain if any new 259 // JavaTypes are added. 260 } 261 262 GOOGLE_LOG(FATAL) << "Can't get here."; 263 return NULL; 264} 265 266bool AllAscii(const string& text) { 267 for (int i = 0; i < text.size(); i++) { 268 if ((text[i] & 0x80) != 0) { 269 return false; 270 } 271 } 272 return true; 273} 274 275string DefaultValue(const Params& params, const FieldDescriptor* field) { 276 // Switch on cpp_type since we need to know which default_value_* method 277 // of FieldDescriptor to call. 278 switch (field->cpp_type()) { 279 case FieldDescriptor::CPPTYPE_INT32: 280 return SimpleItoa(field->default_value_int32()); 281 case FieldDescriptor::CPPTYPE_UINT32: 282 // Need to print as a signed int since Java has no unsigned. 283 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 284 case FieldDescriptor::CPPTYPE_INT64: 285 return SimpleItoa(field->default_value_int64()) + "L"; 286 case FieldDescriptor::CPPTYPE_UINT64: 287 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 288 "L"; 289 case FieldDescriptor::CPPTYPE_DOUBLE: { 290 double value = field->default_value_double(); 291 if (value == numeric_limits<double>::infinity()) { 292 return "Double.POSITIVE_INFINITY"; 293 } else if (value == -numeric_limits<double>::infinity()) { 294 return "Double.NEGATIVE_INFINITY"; 295 } else if (value != value) { 296 return "Double.NaN"; 297 } else { 298 return SimpleDtoa(value) + "D"; 299 } 300 } 301 case FieldDescriptor::CPPTYPE_FLOAT: { 302 float value = field->default_value_float(); 303 if (value == numeric_limits<float>::infinity()) { 304 return "Float.POSITIVE_INFINITY"; 305 } else if (value == -numeric_limits<float>::infinity()) { 306 return "Float.NEGATIVE_INFINITY"; 307 } else if (value != value) { 308 return "Float.NaN"; 309 } else { 310 return SimpleFtoa(value) + "F"; 311 } 312 } 313 case FieldDescriptor::CPPTYPE_BOOL: 314 return field->default_value_bool() ? "true" : "false"; 315 case FieldDescriptor::CPPTYPE_STRING: 316 if (field->type() == FieldDescriptor::TYPE_BYTES) { 317 if (field->has_default_value()) { 318 // See comments in Internal.java for gory details. 319 return strings::Substitute( 320 "com.google.protobuf.micro.ByteStringMicro.copyFromUtf8(\"$0\")", 321 CEscape(field->default_value_string())); 322 } else { 323 return "com.google.protobuf.micro.ByteStringMicro.EMPTY"; 324 } 325 } else { 326 if (AllAscii(field->default_value_string())) { 327 // All chars are ASCII. In this case CEscape() works fine. 328 return "\"" + CEscape(field->default_value_string()) + "\""; 329 } else { 330 // See comments in Internal.java for gory details. 331 // BUG: Internal NOT SUPPORTED need to fix!! 332 return strings::Substitute( 333 "com.google.protobuf.micro.Internal.stringDefaultValue(\"$0\")", 334 CEscape(field->default_value_string())); 335 } 336 } 337 338 case FieldDescriptor::CPPTYPE_ENUM: 339 return ClassName(params, field->enum_type()) + "." + 340 field->default_value_enum()->name(); 341 342 case FieldDescriptor::CPPTYPE_MESSAGE: 343 return ClassName(params, field->message_type()) + ".getDefaultInstance()"; 344 345 // No default because we want the compiler to complain if any new 346 // types are added. 347 } 348 349 GOOGLE_LOG(FATAL) << "Can't get here."; 350 return ""; 351} 352 353} // namespace javamicro 354} // namespace compiler 355} // namespace protobuf 356} // namespace google 357