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_context.h>
32
33#include <google/protobuf/compiler/java/java_field.h>
34#include <google/protobuf/compiler/java/java_helpers.h>
35#include <google/protobuf/compiler/java/java_name_resolver.h>
36#include <google/protobuf/descriptor.h>
37#include <google/protobuf/stubs/strutil.h>
38#include <google/protobuf/stubs/map_util.h>
39
40namespace google {
41namespace protobuf {
42namespace compiler {
43namespace java {
44
45Context::Context(const FileDescriptor* file)
46    : name_resolver_(new ClassNameResolver) {
47  InitializeFieldGeneratorInfo(file);
48}
49
50Context::~Context() {
51}
52
53ClassNameResolver* Context::GetNameResolver() {
54  return name_resolver_.get();
55}
56
57namespace {
58// Whether two fields have conflicting accessors (assuming name1 and name2
59// are different). name1 and name2 are field1 and field2's camel-case name
60// respectively.
61bool IsConflicting(const FieldDescriptor* field1, const string& name1,
62                   const FieldDescriptor* field2, const string& name2,
63                   string* info) {
64  if (field1->is_repeated()) {
65    if (field2->is_repeated()) {
66      // Both fields are repeated.
67      return false;
68    } else {
69      // field1 is repeated, and field2 is not.
70      if (name1 + "Count" == name2) {
71        *info = "both repeated field \"" + field1->name() + "\" and singular " +
72            "field \"" + field2->name() + "\" generates the method \"" +
73            "get" + name1 + "Count()\"";
74        return true;
75      }
76      if (name1 + "List" == name2) {
77        *info = "both repeated field \"" + field1->name() + "\" and singular " +
78            "field \"" + field2->name() + "\" generates the method \"" +
79            "get" + name1 + "List()\"";
80        return true;
81      }
82      // Well, there are obviously many more conflicting cases, but it probably
83      // doesn't worth the effort to exhaust all of them because they rarely
84      // happen and as we are continuing adding new methods/changing existing
85      // methods the number of different conflicting cases will keep growing.
86      // We can just add more cases here when they are found in the real world.
87      return false;
88    }
89  } else {
90    if (field2->is_repeated()) {
91      return IsConflicting(field2, name2, field1, name1, info);
92    } else {
93      // None of the two fields are repeated.
94      return false;
95    }
96  }
97}
98}  // namespace
99
100void Context::InitializeFieldGeneratorInfo(const FileDescriptor* file) {
101  for (int i = 0; i < file->message_type_count(); ++i) {
102    InitializeFieldGeneratorInfoForMessage(file->message_type(i));
103  }
104}
105
106void Context::InitializeFieldGeneratorInfoForMessage(
107    const Descriptor* message) {
108  for (int i = 0; i < message->nested_type_count(); ++i) {
109    InitializeFieldGeneratorInfoForMessage(message->nested_type(i));
110  }
111  vector<const FieldDescriptor*> fields;
112  for (int i = 0; i < message->field_count(); ++i) {
113    fields.push_back(message->field(i));
114  }
115  InitializeFieldGeneratorInfoForFields(fields);
116
117  for (int i = 0; i < message->oneof_decl_count(); ++i) {
118    const OneofDescriptor* oneof = message->oneof_decl(i);
119    OneofGeneratorInfo info;
120    info.name = UnderscoresToCamelCase(oneof->name(), false);
121    info.capitalized_name = UnderscoresToCamelCase(oneof->name(), true);
122    oneof_generator_info_map_[oneof] = info;
123  }
124}
125
126void Context::InitializeFieldGeneratorInfoForFields(
127    const vector<const FieldDescriptor*>& fields) {
128  // Find out all fields that conflict with some other field in the same
129  // message.
130  vector<bool> is_conflict(fields.size());
131  vector<string> conflict_reason(fields.size());
132  for (int i = 0; i < fields.size(); ++i) {
133    const FieldDescriptor* field = fields[i];
134    const string& name = UnderscoresToCapitalizedCamelCase(field);
135    for (int j = i + 1; j < fields.size(); ++j) {
136      const FieldDescriptor* other = fields[j];
137      const string& other_name = UnderscoresToCapitalizedCamelCase(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(field, name, other, other_name,
144                               &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 = UnderscoresToCamelCase(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 += SimpleItoa(field->number());
163      info.capitalized_name += SimpleItoa(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(field_generator_info_map_, 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(oneof_generator_info_map_, 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}  // namespace java
193}  // namespace compiler
194}  // namespace protobuf
195}  // namespace google
196