1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 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 #include <google/protobuf/compiler/java/java_name_resolver.h> 32 33 #include <map> 34 #include <string> 35 36 #include <google/protobuf/compiler/java/java_helpers.h> 37 #include <google/protobuf/stubs/substitute.h> 38 39 namespace google { 40 namespace protobuf { 41 namespace compiler { 42 namespace java { 43 44 namespace { 45 // A suffix that will be appended to the file's outer class name if the name 46 // conflicts with some other types defined in the file. 47 const char* kOuterClassNameSuffix = "OuterClass"; 48 49 // Strip package name from a descriptor's full name. 50 // For example: 51 // Full name : foo.Bar.Baz 52 // Package name: foo 53 // After strip : Bar.Baz 54 string StripPackageName(const string& full_name, 55 const FileDescriptor* file) { 56 if (file->package().empty()) { 57 return full_name; 58 } else { 59 // Strip package name 60 return full_name.substr(file->package().size() + 1); 61 } 62 } 63 64 // Get the name of a message's Java class without package name prefix. 65 string ClassNameWithoutPackage(const Descriptor* descriptor, 66 bool immutable) { 67 return StripPackageName(descriptor->full_name(), 68 descriptor->file()); 69 } 70 71 // Get the name of an enum's Java class without package name prefix. 72 string ClassNameWithoutPackage(const EnumDescriptor* descriptor, 73 bool immutable) { 74 // Doesn't append "Mutable" for enum type's name. 75 const Descriptor* message_descriptor = descriptor->containing_type(); 76 if (message_descriptor == NULL) { 77 return descriptor->name(); 78 } else { 79 return ClassNameWithoutPackage(message_descriptor, immutable) + 80 "." + descriptor->name(); 81 } 82 } 83 84 // Get the name of a service's Java class without package name prefix. 85 string ClassNameWithoutPackage(const ServiceDescriptor* descriptor, 86 bool immutable) { 87 string full_name = StripPackageName(descriptor->full_name(), 88 descriptor->file()); 89 // We don't allow nested service definitions. 90 GOOGLE_CHECK(full_name.find('.') == string::npos); 91 return full_name; 92 } 93 94 // Check whether a given message or its nested types has the given class name. 95 bool MessageHasConflictingClassName(const Descriptor* message, 96 const string& classname) { 97 if (message->name() == classname) return true; 98 for (int i = 0; i < message->nested_type_count(); ++i) { 99 if (MessageHasConflictingClassName(message->nested_type(i), classname)) { 100 return true; 101 } 102 } 103 for (int i = 0; i < message->enum_type_count(); ++i) { 104 if (message->enum_type(i)->name() == classname) { 105 return true; 106 } 107 } 108 return false; 109 } 110 111 } // namespace 112 113 ClassNameResolver::ClassNameResolver() { 114 } 115 116 ClassNameResolver::~ClassNameResolver() { 117 } 118 119 string ClassNameResolver::GetFileDefaultImmutableClassName( 120 const FileDescriptor* file) { 121 string basename; 122 string::size_type last_slash = file->name().find_last_of('/'); 123 if (last_slash == string::npos) { 124 basename = file->name(); 125 } else { 126 basename = file->name().substr(last_slash + 1); 127 } 128 return UnderscoresToCamelCase(StripProto(basename), true); 129 } 130 131 string ClassNameResolver::GetFileImmutableClassName( 132 const FileDescriptor* file) { 133 string& class_name = file_immutable_outer_class_names_[file]; 134 if (class_name.empty()) { 135 if (file->options().has_java_outer_classname()) { 136 class_name = file->options().java_outer_classname(); 137 } else { 138 class_name = GetFileDefaultImmutableClassName(file); 139 if (HasConflictingClassName(file, class_name)) { 140 class_name += kOuterClassNameSuffix; 141 } 142 } 143 } 144 return class_name; 145 } 146 147 string ClassNameResolver::GetFileClassName(const FileDescriptor* file, 148 bool immutable) { 149 if (immutable) { 150 return GetFileImmutableClassName(file); 151 } else { 152 return "Mutable" + GetFileImmutableClassName(file); 153 } 154 } 155 156 // Check whether there is any type defined in the proto file that has 157 // the given class name. 158 bool ClassNameResolver::HasConflictingClassName( 159 const FileDescriptor* file, const string& classname) { 160 for (int i = 0; i < file->enum_type_count(); i++) { 161 if (file->enum_type(i)->name() == classname) { 162 return true; 163 } 164 } 165 for (int i = 0; i < file->service_count(); i++) { 166 if (file->service(i)->name() == classname) { 167 return true; 168 } 169 } 170 for (int i = 0; i < file->message_type_count(); i++) { 171 if (MessageHasConflictingClassName(file->message_type(i), classname)) { 172 return true; 173 } 174 } 175 return false; 176 } 177 178 string ClassNameResolver::GetDescriptorClassName( 179 const FileDescriptor* descriptor) { 180 return GetFileImmutableClassName(descriptor); 181 } 182 183 string ClassNameResolver::GetClassName(const FileDescriptor* descriptor, 184 bool immutable) { 185 string result = FileJavaPackage(descriptor, immutable); 186 if (!result.empty()) result += '.'; 187 result += GetFileClassName(descriptor, immutable); 188 return result; 189 } 190 191 // Get the full name of a Java class by prepending the Java package name 192 // or outer class name. 193 string ClassNameResolver::GetClassFullName(const string& name_without_package, 194 const FileDescriptor* file, 195 bool immutable, 196 bool multiple_files) { 197 string result; 198 if (multiple_files) { 199 result = FileJavaPackage(file, immutable); 200 } else { 201 result = GetClassName(file, immutable); 202 } 203 if (!result.empty()) { 204 result += '.'; 205 } 206 result += name_without_package; 207 return result; 208 } 209 210 string ClassNameResolver::GetClassName(const Descriptor* descriptor, 211 bool immutable) { 212 return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), 213 descriptor->file(), immutable, 214 MultipleJavaFiles(descriptor->file(), immutable)); 215 } 216 217 string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor, 218 bool immutable) { 219 return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), 220 descriptor->file(), immutable, 221 MultipleJavaFiles(descriptor->file(), immutable)); 222 } 223 224 string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor, 225 bool immutable) { 226 return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable), 227 descriptor->file(), immutable, 228 MultipleJavaFiles(descriptor->file(), immutable)); 229 } 230 231 // Get the Java Class style full name of a message. 232 string ClassNameResolver::GetJavaClassFullName( 233 const string& name_without_package, 234 const FileDescriptor* file, 235 bool immutable) { 236 string result; 237 if (MultipleJavaFiles(file, immutable)) { 238 result = FileJavaPackage(file, immutable); 239 if (!result.empty()) result += '.'; 240 } else { 241 result = GetClassName(file, immutable); 242 if (!result.empty()) result += '$'; 243 } 244 result += StringReplace(name_without_package, ".", "$", true); 245 return result; 246 } 247 248 string ClassNameResolver::GetExtensionIdentifierName( 249 const FieldDescriptor* descriptor, bool immutable) { 250 return GetClassName(descriptor->containing_type(), immutable) + "." + 251 descriptor->name(); 252 } 253 254 255 string ClassNameResolver::GetJavaImmutableClassName( 256 const Descriptor* descriptor) { 257 return GetJavaClassFullName( 258 ClassNameWithoutPackage(descriptor, true), 259 descriptor->file(), true); 260 } 261 262 263 } // namespace java 264 } // namespace compiler 265 } // namespace protobuf 266 } // namespace google 267