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/code_generator.h>
37 #include <google/protobuf/stubs/substitute.h>
38 #include <google/protobuf/compiler/java/java_helpers.h>
39 #include <google/protobuf/compiler/java/java_names.h>
40 
41 namespace google {
42 namespace protobuf {
43 namespace compiler {
44 namespace java {
45 
46 namespace {
47 // A suffix that will be appended to the file's outer class name if the name
48 // conflicts with some other types defined in the file.
49 const char* kOuterClassNameSuffix = "OuterClass";
50 
51 // Strip package name from a descriptor's full name.
52 // For example:
53 //   Full name   : foo.Bar.Baz
54 //   Package name: foo
55 //   After strip : Bar.Baz
StripPackageName(const std::string & full_name,const FileDescriptor * file)56 std::string StripPackageName(const std::string& full_name,
57                              const FileDescriptor* file) {
58   if (file->package().empty()) {
59     return full_name;
60   } else {
61     // Strip package name
62     return full_name.substr(file->package().size() + 1);
63   }
64 }
65 
66 // Get the name of a message's Java class without package name prefix.
ClassNameWithoutPackage(const Descriptor * descriptor,bool immutable)67 std::string ClassNameWithoutPackage(const Descriptor* descriptor,
68                                     bool immutable) {
69   return StripPackageName(descriptor->full_name(), descriptor->file());
70 }
71 
ClassNameWithoutPackageKotlin(const Descriptor * descriptor)72 std::string ClassNameWithoutPackageKotlin(const Descriptor* descriptor) {
73   std::string result = descriptor->name();
74   const Descriptor* temp = descriptor->containing_type();
75 
76   while (temp) {
77     result = temp->name() + "Kt." + result;
78     temp = temp->containing_type();
79   }
80   return result;
81 }
82 
83 // Get the name of an enum's Java class without package name prefix.
ClassNameWithoutPackage(const EnumDescriptor * descriptor,bool immutable)84 std::string ClassNameWithoutPackage(const EnumDescriptor* descriptor,
85                                     bool immutable) {
86   // Doesn't append "Mutable" for enum type's name.
87   const Descriptor* message_descriptor = descriptor->containing_type();
88   if (message_descriptor == NULL) {
89     return descriptor->name();
90   } else {
91     return ClassNameWithoutPackage(message_descriptor, immutable) + "." +
92            descriptor->name();
93   }
94 }
95 
96 // Get the name of a service's Java class without package name prefix.
ClassNameWithoutPackage(const ServiceDescriptor * descriptor,bool immutable)97 std::string ClassNameWithoutPackage(const ServiceDescriptor* descriptor,
98                                     bool immutable) {
99   std::string full_name =
100       StripPackageName(descriptor->full_name(), descriptor->file());
101   // We don't allow nested service definitions.
102   GOOGLE_CHECK(full_name.find('.') == std::string::npos);
103   return full_name;
104 }
105 
106 // Return true if a and b are equals (case insensitive).
CheckNameEquality(const std::string & a,const std::string & b)107 NameEquality CheckNameEquality(const std::string& a, const std::string& b) {
108   if (ToUpper(a) == ToUpper(b)) {
109     if (a == b) {
110       return NameEquality::EXACT_EQUAL;
111     }
112     return NameEquality::EQUAL_IGNORE_CASE;
113   }
114   return NameEquality::NO_MATCH;
115 }
116 
117 // Check whether a given message or its nested types has the given class name.
MessageHasConflictingClassName(const Descriptor * message,const std::string & classname,NameEquality equality_mode)118 bool MessageHasConflictingClassName(const Descriptor* message,
119                                     const std::string& classname,
120                                     NameEquality equality_mode) {
121   if (CheckNameEquality(message->name(), classname) == equality_mode) {
122     return true;
123   }
124   for (int i = 0; i < message->nested_type_count(); ++i) {
125     if (MessageHasConflictingClassName(message->nested_type(i), classname,
126                                        equality_mode)) {
127       return true;
128     }
129   }
130   for (int i = 0; i < message->enum_type_count(); ++i) {
131     if (CheckNameEquality(message->enum_type(i)->name(), classname) ==
132         equality_mode) {
133       return true;
134     }
135   }
136   return false;
137 }
138 
139 }  // namespace
140 
ClassNameResolver()141 ClassNameResolver::ClassNameResolver() {}
142 
~ClassNameResolver()143 ClassNameResolver::~ClassNameResolver() {}
144 
GetFileDefaultImmutableClassName(const FileDescriptor * file)145 std::string ClassNameResolver::GetFileDefaultImmutableClassName(
146     const FileDescriptor* file) {
147   std::string basename;
148   std::string::size_type last_slash = file->name().find_last_of('/');
149   if (last_slash == std::string::npos) {
150     basename = file->name();
151   } else {
152     basename = file->name().substr(last_slash + 1);
153   }
154   return UnderscoresToCamelCase(StripProto(basename), true);
155 }
156 
GetFileImmutableClassName(const FileDescriptor * file)157 std::string ClassNameResolver::GetFileImmutableClassName(
158     const FileDescriptor* file) {
159   std::string& class_name = file_immutable_outer_class_names_[file];
160   if (class_name.empty()) {
161     if (file->options().has_java_outer_classname()) {
162       class_name = file->options().java_outer_classname();
163     } else {
164       class_name = GetFileDefaultImmutableClassName(file);
165       if (HasConflictingClassName(file, class_name,
166                                   NameEquality::EXACT_EQUAL)) {
167         class_name += kOuterClassNameSuffix;
168       }
169     }
170   }
171   return class_name;
172 }
173 
GetFileClassName(const FileDescriptor * file,bool immutable)174 std::string ClassNameResolver::GetFileClassName(const FileDescriptor* file,
175                                                 bool immutable) {
176   return GetFileClassName(file, immutable, false);
177 }
178 
GetFileClassName(const FileDescriptor * file,bool immutable,bool kotlin)179 std::string ClassNameResolver::GetFileClassName(const FileDescriptor* file,
180                                                 bool immutable, bool kotlin) {
181   if (kotlin) {
182     return GetFileImmutableClassName(file) + "Kt";
183   } else if (immutable) {
184     return GetFileImmutableClassName(file);
185   } else {
186     return "Mutable" + GetFileImmutableClassName(file);
187   }
188 }
189 
190 // Check whether there is any type defined in the proto file that has
191 // the given class name.
HasConflictingClassName(const FileDescriptor * file,const std::string & classname,NameEquality equality_mode)192 bool ClassNameResolver::HasConflictingClassName(const FileDescriptor* file,
193                                                 const std::string& classname,
194                                                 NameEquality equality_mode) {
195   for (int i = 0; i < file->enum_type_count(); i++) {
196     if (CheckNameEquality(file->enum_type(i)->name(), classname) ==
197         equality_mode) {
198       return true;
199     }
200   }
201   for (int i = 0; i < file->service_count(); i++) {
202     if (CheckNameEquality(file->service(i)->name(), classname) ==
203         equality_mode) {
204       return true;
205     }
206   }
207   for (int i = 0; i < file->message_type_count(); i++) {
208     if (MessageHasConflictingClassName(file->message_type(i), classname,
209                                        equality_mode)) {
210       return true;
211     }
212   }
213   return false;
214 }
215 
GetDescriptorClassName(const FileDescriptor * descriptor)216 std::string ClassNameResolver::GetDescriptorClassName(
217     const FileDescriptor* descriptor) {
218   return GetFileImmutableClassName(descriptor);
219 }
220 
GetClassName(const FileDescriptor * descriptor,bool immutable)221 std::string ClassNameResolver::GetClassName(const FileDescriptor* descriptor,
222                                             bool immutable) {
223   return GetClassName(descriptor, immutable, false);
224 }
225 
GetClassName(const FileDescriptor * descriptor,bool immutable,bool kotlin)226 std::string ClassNameResolver::GetClassName(const FileDescriptor* descriptor,
227                                             bool immutable, bool kotlin) {
228   std::string result = FileJavaPackage(descriptor, immutable);
229   if (!result.empty()) result += '.';
230   result += GetFileClassName(descriptor, immutable, kotlin);
231   return result;
232 }
233 
234 // Get the full name of a Java class by prepending the Java package name
235 // or outer class name.
GetClassFullName(const std::string & name_without_package,const FileDescriptor * file,bool immutable,bool is_own_file)236 std::string ClassNameResolver::GetClassFullName(
237     const std::string& name_without_package, const FileDescriptor* file,
238     bool immutable, bool is_own_file) {
239   return GetClassFullName(name_without_package, file, immutable, is_own_file,
240                           false);
241 }
242 
GetClassFullName(const std::string & name_without_package,const FileDescriptor * file,bool immutable,bool is_own_file,bool kotlin)243 std::string ClassNameResolver::GetClassFullName(
244     const std::string& name_without_package, const FileDescriptor* file,
245     bool immutable, bool is_own_file, bool kotlin) {
246   std::string result;
247   if (is_own_file) {
248     result = FileJavaPackage(file, immutable);
249   } else {
250     result = GetClassName(file, immutable, kotlin);
251   }
252   if (!result.empty()) {
253     result += '.';
254   }
255   result += name_without_package;
256   if (kotlin) result += "Kt";
257   return result;
258 }
259 
GetClassName(const Descriptor * descriptor,bool immutable)260 std::string ClassNameResolver::GetClassName(const Descriptor* descriptor,
261                                             bool immutable) {
262   return GetClassName(descriptor, immutable, false);
263 }
264 
GetClassName(const Descriptor * descriptor,bool immutable,bool kotlin)265 std::string ClassNameResolver::GetClassName(const Descriptor* descriptor,
266                                             bool immutable, bool kotlin) {
267   return GetClassFullName(
268       ClassNameWithoutPackage(descriptor, immutable), descriptor->file(),
269       immutable, MultipleJavaFiles(descriptor->file(), immutable), kotlin);
270 }
271 
GetClassName(const EnumDescriptor * descriptor,bool immutable)272 std::string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor,
273                                             bool immutable) {
274   return GetClassName(descriptor, immutable, false);
275 }
276 
GetClassName(const EnumDescriptor * descriptor,bool immutable,bool kotlin)277 std::string ClassNameResolver::GetClassName(const EnumDescriptor* descriptor,
278                                             bool immutable, bool kotlin) {
279   return GetClassFullName(
280       ClassNameWithoutPackage(descriptor, immutable), descriptor->file(),
281       immutable, MultipleJavaFiles(descriptor->file(), immutable), kotlin);
282 }
283 
GetClassName(const ServiceDescriptor * descriptor,bool immutable)284 std::string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor,
285                                             bool immutable) {
286   return GetClassName(descriptor, immutable, false);
287 }
288 
GetClassName(const ServiceDescriptor * descriptor,bool immutable,bool kotlin)289 std::string ClassNameResolver::GetClassName(const ServiceDescriptor* descriptor,
290                                             bool immutable, bool kotlin) {
291   return GetClassFullName(ClassNameWithoutPackage(descriptor, immutable),
292                           descriptor->file(), immutable,
293                           IsOwnFile(descriptor, immutable), kotlin);
294 }
295 
296 // Get the Java Class style full name of a message.
GetJavaClassFullName(const std::string & name_without_package,const FileDescriptor * file,bool immutable)297 std::string ClassNameResolver::GetJavaClassFullName(
298     const std::string& name_without_package, const FileDescriptor* file,
299     bool immutable) {
300   return GetJavaClassFullName(name_without_package, file, immutable, false);
301 }
302 
GetJavaClassFullName(const std::string & name_without_package,const FileDescriptor * file,bool immutable,bool kotlin)303 std::string ClassNameResolver::GetJavaClassFullName(
304     const std::string& name_without_package, const FileDescriptor* file,
305     bool immutable, bool kotlin) {
306   std::string result;
307   if (MultipleJavaFiles(file, immutable)) {
308     result = FileJavaPackage(file, immutable);
309     if (!result.empty()) result += '.';
310   } else {
311     result = GetClassName(file, immutable, kotlin);
312     if (!result.empty()) result += '$';
313   }
314   result += StringReplace(name_without_package, ".", "$", true);
315   return result;
316 }
317 
GetExtensionIdentifierName(const FieldDescriptor * descriptor,bool immutable)318 std::string ClassNameResolver::GetExtensionIdentifierName(
319     const FieldDescriptor* descriptor, bool immutable) {
320   return GetExtensionIdentifierName(descriptor, immutable, false);
321 }
322 
GetExtensionIdentifierName(const FieldDescriptor * descriptor,bool immutable,bool kotlin)323 std::string ClassNameResolver::GetExtensionIdentifierName(
324     const FieldDescriptor* descriptor, bool immutable, bool kotlin) {
325   return GetClassName(descriptor->containing_type(), immutable, kotlin) + "." +
326          descriptor->name();
327 }
328 
GetKotlinFactoryName(const Descriptor * descriptor)329 std::string ClassNameResolver::GetKotlinFactoryName(
330     const Descriptor* descriptor) {
331   std::string name = ToCamelCase(descriptor->name(), /* lower_first = */ true);
332   return IsForbiddenKotlin(name) ? name + "_" : name;
333 }
334 
GetJavaImmutableClassName(const Descriptor * descriptor)335 std::string ClassNameResolver::GetJavaImmutableClassName(
336     const Descriptor* descriptor) {
337   return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true),
338                               descriptor->file(), true);
339 }
340 
GetJavaImmutableClassName(const EnumDescriptor * descriptor)341 std::string ClassNameResolver::GetJavaImmutableClassName(
342     const EnumDescriptor* descriptor) {
343   return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true),
344                               descriptor->file(), true);
345 }
346 
GetKotlinExtensionsClassName(const Descriptor * descriptor)347 std::string ClassNameResolver::GetKotlinExtensionsClassName(
348     const Descriptor* descriptor) {
349   return GetClassFullName(ClassNameWithoutPackageKotlin(descriptor),
350                           descriptor->file(), true, true, true);
351 }
352 
GetJavaMutableClassName(const Descriptor * descriptor)353 std::string ClassNameResolver::GetJavaMutableClassName(
354     const Descriptor* descriptor) {
355   return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, false),
356                               descriptor->file(), false);
357 }
358 
GetJavaMutableClassName(const EnumDescriptor * descriptor)359 std::string ClassNameResolver::GetJavaMutableClassName(
360     const EnumDescriptor* descriptor) {
361   return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, false),
362                               descriptor->file(), false);
363 }
364 
GetDowngradedFileClassName(const FileDescriptor * file)365 std::string ClassNameResolver::GetDowngradedFileClassName(
366     const FileDescriptor* file) {
367   return "Downgraded" + GetFileClassName(file, false);
368 }
369 
GetDowngradedClassName(const Descriptor * descriptor)370 std::string ClassNameResolver::GetDowngradedClassName(
371     const Descriptor* descriptor) {
372   return FileJavaPackage(descriptor->file()) + "." +
373          GetDowngradedFileClassName(descriptor->file()) + "." +
374          ClassNameWithoutPackage(descriptor, false);
375 }
376 
377 }  // namespace java
378 }  // namespace compiler
379 }  // namespace protobuf
380 }  // namespace google
381