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