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
40namespace google {
41namespace protobuf {
42namespace compiler {
43namespace java {
44
45Context::Context(const FileDescriptor* file, const Options& options)
46 : name_resolver_(new ClassNameResolver), options_(options) {
47 InitializeFieldGeneratorInfo(file);
48}
49
50Context::~Context() {}
51
52ClassNameResolver* Context::GetNameResolver() const {
53 return name_resolver_.get();
54}
55
56namespace {
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.
60bool 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
99void 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
105void 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
126void 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
170const 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
181const 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?
194bool 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