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/context.h> |
32 | |
33 | #include <google/protobuf/descriptor.h> |
34 | #include <google/protobuf/stubs/strutil.h> |
35 | #include <google/protobuf/compiler/java/field.h> |
36 | #include <google/protobuf/compiler/java/helpers.h> |
37 | #include <google/protobuf/compiler/java/name_resolver.h> |
38 | #include <google/protobuf/stubs/map_util.h> |
39 | |
40 | namespace google { |
41 | namespace protobuf { |
42 | namespace compiler { |
43 | namespace java { |
44 | |
45 | Context::Context(const FileDescriptor* file, const Options& options) |
46 | : name_resolver_(new ClassNameResolver), options_(options) { |
47 | InitializeFieldGeneratorInfo(file); |
48 | } |
49 | |
50 | Context::~Context() {} |
51 | |
52 | ClassNameResolver* Context::GetNameResolver() const { |
53 | return name_resolver_.get(); |
54 | } |
55 | |
56 | namespace { |
57 | // Whether two fields have conflicting accessors (assuming name1 and name2 |
58 | // are different). name1 and name2 are field1 and field2's camel-case name |
59 | // respectively. |
60 | bool IsConflicting(const FieldDescriptor* field1, const std::string& name1, |
61 | const FieldDescriptor* field2, const std::string& name2, |
62 | std::string* info) { |
63 | if (field1->is_repeated()) { |
64 | if (field2->is_repeated()) { |
65 | // Both fields are repeated. |
66 | return false; |
67 | } else { |
68 | // field1 is repeated, and field2 is not. |
69 | if (name1 + "Count" == name2) { |
70 | *info = "both repeated field \"" + field1->name() + "\" and singular " + |
71 | "field \"" + field2->name() + "\" generate the method \"" + |
72 | "get" + name1 + "Count()\"" ; |
73 | return true; |
74 | } |
75 | if (name1 + "List" == name2) { |
76 | *info = "both repeated field \"" + field1->name() + "\" and singular " + |
77 | "field \"" + field2->name() + "\" generate the method \"" + |
78 | "get" + name1 + "List()\"" ; |
79 | return true; |
80 | } |
81 | // Well, there are obviously many more conflicting cases, but it probably |
82 | // doesn't worth the effort to exhaust all of them because they rarely |
83 | // happen and as we are continuing adding new methods/changing existing |
84 | // methods the number of different conflicting cases will keep growing. |
85 | // We can just add more cases here when they are found in the real world. |
86 | return false; |
87 | } |
88 | } else { |
89 | if (field2->is_repeated()) { |
90 | return IsConflicting(field1: field2, name1: name2, field2: field1, name2: name1, info); |
91 | } else { |
92 | // None of the two fields are repeated. |
93 | return false; |
94 | } |
95 | } |
96 | } |
97 | } // namespace |
98 | |
99 | void Context::InitializeFieldGeneratorInfo(const FileDescriptor* file) { |
100 | for (int i = 0; i < file->message_type_count(); ++i) { |
101 | InitializeFieldGeneratorInfoForMessage(message: file->message_type(index: i)); |
102 | } |
103 | } |
104 | |
105 | void Context::InitializeFieldGeneratorInfoForMessage( |
106 | const Descriptor* message) { |
107 | for (int i = 0; i < message->nested_type_count(); ++i) { |
108 | InitializeFieldGeneratorInfoForMessage(message: message->nested_type(index: i)); |
109 | } |
110 | std::vector<const FieldDescriptor*> fields; |
111 | fields.reserve(n: message->field_count()); |
112 | for (int i = 0; i < message->field_count(); ++i) { |
113 | fields.push_back(x: message->field(index: i)); |
114 | } |
115 | InitializeFieldGeneratorInfoForFields(fields); |
116 | |
117 | for (int i = 0; i < message->oneof_decl_count(); ++i) { |
118 | const OneofDescriptor* oneof = message->oneof_decl(index: i); |
119 | OneofGeneratorInfo info; |
120 | info.name = UnderscoresToCamelCase(name: oneof->name(), cap_first_letter: false); |
121 | info.capitalized_name = UnderscoresToCamelCase(name: oneof->name(), cap_first_letter: true); |
122 | oneof_generator_info_map_[oneof] = info; |
123 | } |
124 | } |
125 | |
126 | void Context::InitializeFieldGeneratorInfoForFields( |
127 | const std::vector<const FieldDescriptor*>& fields) { |
128 | // Find out all fields that conflict with some other field in the same |
129 | // message. |
130 | std::vector<bool> is_conflict(fields.size()); |
131 | std::vector<std::string> conflict_reason(fields.size()); |
132 | for (int i = 0; i < fields.size(); ++i) { |
133 | const FieldDescriptor* field = fields[i]; |
134 | const std::string& name = UnderscoresToCapitalizedCamelCase(field); |
135 | for (int j = i + 1; j < fields.size(); ++j) { |
136 | const FieldDescriptor* other = fields[j]; |
137 | const std::string& other_name = UnderscoresToCapitalizedCamelCase(field: other); |
138 | if (name == other_name) { |
139 | is_conflict[i] = is_conflict[j] = true; |
140 | conflict_reason[i] = conflict_reason[j] = |
141 | "capitalized name of field \"" + field->name() + |
142 | "\" conflicts with field \"" + other->name() + "\"" ; |
143 | } else if (IsConflicting(field1: field, name1: name, field2: other, name2: other_name, |
144 | info: &conflict_reason[j])) { |
145 | is_conflict[i] = is_conflict[j] = true; |
146 | conflict_reason[i] = conflict_reason[j]; |
147 | } |
148 | } |
149 | if (is_conflict[i]) { |
150 | GOOGLE_LOG(WARNING) << "field \"" << field->full_name() << "\" is conflicting " |
151 | << "with another field: " << conflict_reason[i]; |
152 | } |
153 | } |
154 | for (int i = 0; i < fields.size(); ++i) { |
155 | const FieldDescriptor* field = fields[i]; |
156 | FieldGeneratorInfo info; |
157 | info.name = CamelCaseFieldName(field); |
158 | info.capitalized_name = UnderscoresToCapitalizedCamelCase(field); |
159 | // For fields conflicting with some other fields, we append the field |
160 | // number to their field names in generated code to avoid conflicts. |
161 | if (is_conflict[i]) { |
162 | info.name += StrCat(a: field->number()); |
163 | info.capitalized_name += StrCat(a: field->number()); |
164 | info.disambiguated_reason = conflict_reason[i]; |
165 | } |
166 | field_generator_info_map_[field] = info; |
167 | } |
168 | } |
169 | |
170 | const FieldGeneratorInfo* Context::GetFieldGeneratorInfo( |
171 | const FieldDescriptor* field) const { |
172 | const FieldGeneratorInfo* result = |
173 | FindOrNull(collection: field_generator_info_map_, key: field); |
174 | if (result == NULL) { |
175 | GOOGLE_LOG(FATAL) << "Can not find FieldGeneratorInfo for field: " |
176 | << field->full_name(); |
177 | } |
178 | return result; |
179 | } |
180 | |
181 | const OneofGeneratorInfo* Context::GetOneofGeneratorInfo( |
182 | const OneofDescriptor* oneof) const { |
183 | const OneofGeneratorInfo* result = |
184 | FindOrNull(collection: oneof_generator_info_map_, key: oneof); |
185 | if (result == NULL) { |
186 | GOOGLE_LOG(FATAL) << "Can not find OneofGeneratorInfo for oneof: " |
187 | << oneof->name(); |
188 | } |
189 | return result; |
190 | } |
191 | |
192 | // Does this message class have generated parsing, serialization, and other |
193 | // standard methods for which reflection-based fallback implementations exist? |
194 | bool Context::HasGeneratedMethods(const Descriptor* descriptor) const { |
195 | return options_.enforce_lite || |
196 | descriptor->file()->options().optimize_for() != FileOptions::CODE_SIZE; |
197 | } |
198 | |
199 | } // namespace java |
200 | } // namespace compiler |
201 | } // namespace protobuf |
202 | } // namespace google |
203 | |