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 (at) 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/java/java_helpers.h> 39 #include <google/protobuf/descriptor.pb.h> 40 #include <google/protobuf/stubs/strutil.h> 41 #include <google/protobuf/stubs/substitute.h> 42 43 namespace google { 44 namespace protobuf { 45 namespace compiler { 46 namespace java { 47 48 const char kThickSeparator[] = 49 "// ===================================================================\n"; 50 const char kThinSeparator[] = 51 "// -------------------------------------------------------------------\n"; 52 53 namespace { 54 55 const char* kDefaultPackage = ""; 56 57 const 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 (GetType(field) == FieldDescriptor::TYPE_GROUP) { 62 return field->message_type()->name(); 63 } else { 64 return field->name(); 65 } 66 } 67 68 string 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 101 string UnderscoresToCamelCase(const FieldDescriptor* field) { 102 return UnderscoresToCamelCaseImpl(FieldName(field), false); 103 } 104 105 string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { 106 return UnderscoresToCamelCaseImpl(FieldName(field), true); 107 } 108 109 string UnderscoresToCamelCase(const MethodDescriptor* method) { 110 return UnderscoresToCamelCaseImpl(method->name(), false); 111 } 112 113 string StripProto(const string& filename) { 114 if (HasSuffixString(filename, ".protodevel")) { 115 return StripSuffixString(filename, ".protodevel"); 116 } else { 117 return StripSuffixString(filename, ".proto"); 118 } 119 } 120 121 string FileClassName(const FileDescriptor* file) { 122 if (file->options().has_java_outer_classname()) { 123 return file->options().java_outer_classname(); 124 } else { 125 string basename; 126 string::size_type last_slash = file->name().find_last_of('/'); 127 if (last_slash == string::npos) { 128 basename = file->name(); 129 } else { 130 basename = file->name().substr(last_slash + 1); 131 } 132 return UnderscoresToCamelCaseImpl(StripProto(basename), true); 133 } 134 } 135 136 string FileJavaPackage(const FileDescriptor* file) { 137 if (file->options().has_java_package()) { 138 return file->options().java_package(); 139 } else { 140 string result = kDefaultPackage; 141 if (!file->package().empty()) { 142 if (!result.empty()) result += '.'; 143 result += file->package(); 144 } 145 return result; 146 } 147 } 148 149 string ToJavaName(const string& full_name, const FileDescriptor* file) { 150 string result; 151 if (file->options().java_multiple_files()) { 152 result = FileJavaPackage(file); 153 } else { 154 result = ClassName(file); 155 } 156 if (!result.empty()) { 157 result += '.'; 158 } 159 if (file->package().empty()) { 160 result += full_name; 161 } else { 162 // Strip the proto package from full_name since we've replaced it with 163 // the Java package. 164 result += full_name.substr(file->package().size() + 1); 165 } 166 return result; 167 } 168 169 string ClassName(const FileDescriptor* descriptor) { 170 string result = FileJavaPackage(descriptor); 171 if (!result.empty()) result += '.'; 172 result += FileClassName(descriptor); 173 return result; 174 } 175 176 string FieldConstantName(const FieldDescriptor *field) { 177 string name = field->name() + "_FIELD_NUMBER"; 178 UpperString(&name); 179 return name; 180 } 181 182 FieldDescriptor::Type GetType(const FieldDescriptor* field) { 183 return field->type(); 184 } 185 186 JavaType GetJavaType(const FieldDescriptor* field) { 187 switch (GetType(field)) { 188 case FieldDescriptor::TYPE_INT32: 189 case FieldDescriptor::TYPE_UINT32: 190 case FieldDescriptor::TYPE_SINT32: 191 case FieldDescriptor::TYPE_FIXED32: 192 case FieldDescriptor::TYPE_SFIXED32: 193 return JAVATYPE_INT; 194 195 case FieldDescriptor::TYPE_INT64: 196 case FieldDescriptor::TYPE_UINT64: 197 case FieldDescriptor::TYPE_SINT64: 198 case FieldDescriptor::TYPE_FIXED64: 199 case FieldDescriptor::TYPE_SFIXED64: 200 return JAVATYPE_LONG; 201 202 case FieldDescriptor::TYPE_FLOAT: 203 return JAVATYPE_FLOAT; 204 205 case FieldDescriptor::TYPE_DOUBLE: 206 return JAVATYPE_DOUBLE; 207 208 case FieldDescriptor::TYPE_BOOL: 209 return JAVATYPE_BOOLEAN; 210 211 case FieldDescriptor::TYPE_STRING: 212 return JAVATYPE_STRING; 213 214 case FieldDescriptor::TYPE_BYTES: 215 return JAVATYPE_BYTES; 216 217 case FieldDescriptor::TYPE_ENUM: 218 return JAVATYPE_ENUM; 219 220 case FieldDescriptor::TYPE_GROUP: 221 case FieldDescriptor::TYPE_MESSAGE: 222 return JAVATYPE_MESSAGE; 223 224 // No default because we want the compiler to complain if any new 225 // types are added. 226 } 227 228 GOOGLE_LOG(FATAL) << "Can't get here."; 229 return JAVATYPE_INT; 230 } 231 232 const char* BoxedPrimitiveTypeName(JavaType type) { 233 switch (type) { 234 case JAVATYPE_INT : return "java.lang.Integer"; 235 case JAVATYPE_LONG : return "java.lang.Long"; 236 case JAVATYPE_FLOAT : return "java.lang.Float"; 237 case JAVATYPE_DOUBLE : return "java.lang.Double"; 238 case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; 239 case JAVATYPE_STRING : return "java.lang.String"; 240 case JAVATYPE_BYTES : return "com.google.protobuf.ByteString"; 241 case JAVATYPE_ENUM : return NULL; 242 case JAVATYPE_MESSAGE: return NULL; 243 244 // No default because we want the compiler to complain if any new 245 // JavaTypes are added. 246 } 247 248 GOOGLE_LOG(FATAL) << "Can't get here."; 249 return NULL; 250 } 251 252 bool AllAscii(const string& text) { 253 for (int i = 0; i < text.size(); i++) { 254 if ((text[i] & 0x80) != 0) { 255 return false; 256 } 257 } 258 return true; 259 } 260 261 string DefaultValue(const FieldDescriptor* field) { 262 // Switch on CppType since we need to know which default_value_* method 263 // of FieldDescriptor to call. 264 switch (field->cpp_type()) { 265 case FieldDescriptor::CPPTYPE_INT32: 266 return SimpleItoa(field->default_value_int32()); 267 case FieldDescriptor::CPPTYPE_UINT32: 268 // Need to print as a signed int since Java has no unsigned. 269 return SimpleItoa(static_cast<int32>(field->default_value_uint32())); 270 case FieldDescriptor::CPPTYPE_INT64: 271 return SimpleItoa(field->default_value_int64()) + "L"; 272 case FieldDescriptor::CPPTYPE_UINT64: 273 return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + 274 "L"; 275 case FieldDescriptor::CPPTYPE_DOUBLE: { 276 double value = field->default_value_double(); 277 if (value == numeric_limits<double>::infinity()) { 278 return "Double.POSITIVE_INFINITY"; 279 } else if (value == -numeric_limits<double>::infinity()) { 280 return "Double.NEGATIVE_INFINITY"; 281 } else if (value != value) { 282 return "Double.NaN"; 283 } else { 284 return SimpleDtoa(value) + "D"; 285 } 286 } 287 case FieldDescriptor::CPPTYPE_FLOAT: { 288 float value = field->default_value_float(); 289 if (value == numeric_limits<float>::infinity()) { 290 return "Float.POSITIVE_INFINITY"; 291 } else if (value == -numeric_limits<float>::infinity()) { 292 return "Float.NEGATIVE_INFINITY"; 293 } else if (value != value) { 294 return "Float.NaN"; 295 } else { 296 return SimpleFtoa(value) + "F"; 297 } 298 } 299 case FieldDescriptor::CPPTYPE_BOOL: 300 return field->default_value_bool() ? "true" : "false"; 301 case FieldDescriptor::CPPTYPE_STRING: 302 if (GetType(field) == FieldDescriptor::TYPE_BYTES) { 303 if (field->has_default_value()) { 304 // See comments in Internal.java for gory details. 305 return strings::Substitute( 306 "com.google.protobuf.Internal.bytesDefaultValue(\"$0\")", 307 CEscape(field->default_value_string())); 308 } else { 309 return "com.google.protobuf.ByteString.EMPTY"; 310 } 311 } else { 312 if (AllAscii(field->default_value_string())) { 313 // All chars are ASCII. In this case CEscape() works fine. 314 return "\"" + CEscape(field->default_value_string()) + "\""; 315 } else { 316 // See comments in Internal.java for gory details. 317 return strings::Substitute( 318 "com.google.protobuf.Internal.stringDefaultValue(\"$0\")", 319 CEscape(field->default_value_string())); 320 } 321 } 322 323 case FieldDescriptor::CPPTYPE_ENUM: 324 return ClassName(field->enum_type()) + "." + 325 field->default_value_enum()->name(); 326 327 case FieldDescriptor::CPPTYPE_MESSAGE: 328 return ClassName(field->message_type()) + ".getDefaultInstance()"; 329 330 // No default because we want the compiler to complain if any new 331 // types are added. 332 } 333 334 GOOGLE_LOG(FATAL) << "Can't get here."; 335 return ""; 336 } 337 338 } // namespace java 339 } // namespace compiler 340 } // namespace protobuf 341 } // namespace google 342