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/util/type_resolver_util.h>
32
33 #include <google/protobuf/type.pb.h>
34 #include <google/protobuf/wrappers.pb.h>
35 #include <google/protobuf/descriptor.pb.h>
36 #include <google/protobuf/descriptor.h>
37 #include <google/protobuf/util/internal/utility.h>
38 #include <google/protobuf/util/type_resolver.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <google/protobuf/stubs/status.h>
41
42 namespace google {
43 namespace protobuf {
44 namespace util {
45 namespace {
46 using google::protobuf::BoolValue;
47 using google::protobuf::Enum;
48 using google::protobuf::EnumValue;
49 using google::protobuf::Field;
50 using google::protobuf::Option;
51 using google::protobuf::Type;
52
53 using util::Status;
54 using util::error::INVALID_ARGUMENT;
55 using util::error::NOT_FOUND;
56
SplitTypeUrl(const string & type_url,string * url_prefix,string * message_name)57 bool SplitTypeUrl(const string& type_url, string* url_prefix,
58 string* message_name) {
59 size_t pos = type_url.find_last_of("/");
60 if (pos == string::npos) {
61 return false;
62 }
63 *url_prefix = type_url.substr(0, pos);
64 *message_name = type_url.substr(pos + 1);
65 return true;
66 }
67
68 class DescriptorPoolTypeResolver : public TypeResolver {
69 public:
DescriptorPoolTypeResolver(const string & url_prefix,const DescriptorPool * pool)70 DescriptorPoolTypeResolver(const string& url_prefix,
71 const DescriptorPool* pool)
72 : url_prefix_(url_prefix), pool_(pool) {}
73
ResolveMessageType(const string & type_url,Type * type)74 Status ResolveMessageType(const string& type_url, Type* type) {
75 string url_prefix, message_name;
76 if (!SplitTypeUrl(type_url, &url_prefix, &message_name) ||
77 url_prefix != url_prefix_) {
78 return Status(INVALID_ARGUMENT,
79 StrCat("Invalid type URL, type URLs must be of the form '",
80 url_prefix_, "/<typename>', got: ", type_url));
81 }
82 if (url_prefix != url_prefix_) {
83 return Status(INVALID_ARGUMENT,
84 "Cannot resolve types from URL: " + url_prefix);
85 }
86 const Descriptor* descriptor = pool_->FindMessageTypeByName(message_name);
87 if (descriptor == NULL) {
88 return Status(NOT_FOUND,
89 "Invalid type URL, unknown type: " + message_name);
90 }
91 ConvertDescriptor(descriptor, type);
92 return Status();
93 }
94
ResolveEnumType(const string & type_url,Enum * enum_type)95 Status ResolveEnumType(const string& type_url, Enum* enum_type) {
96 string url_prefix, type_name;
97 if (!SplitTypeUrl(type_url, &url_prefix, &type_name) ||
98 url_prefix != url_prefix_) {
99 return Status(INVALID_ARGUMENT,
100 StrCat("Invalid type URL, type URLs must be of the form '",
101 url_prefix_, "/<typename>', got: ", type_url));
102 }
103 if (url_prefix != url_prefix_) {
104 return Status(INVALID_ARGUMENT,
105 "Cannot resolve types from URL: " + url_prefix);
106 }
107 const EnumDescriptor* descriptor = pool_->FindEnumTypeByName(type_name);
108 if (descriptor == NULL) {
109 return Status(NOT_FOUND, "Invalid type URL, unknown type: " + type_name);
110 }
111 ConvertEnumDescriptor(descriptor, enum_type);
112 return Status();
113 }
114
115 private:
ConvertDescriptor(const Descriptor * descriptor,Type * type)116 void ConvertDescriptor(const Descriptor* descriptor, Type* type) {
117 type->Clear();
118 type->set_name(descriptor->full_name());
119 for (int i = 0; i < descriptor->field_count(); ++i) {
120 const FieldDescriptor* field = descriptor->field(i);
121 if (field->type() == FieldDescriptor::TYPE_GROUP) {
122 // Group fields cannot be represented with Type. We discard them.
123 continue;
124 }
125 ConvertFieldDescriptor(descriptor->field(i), type->add_fields());
126 }
127 for (int i = 0; i < descriptor->oneof_decl_count(); ++i) {
128 type->add_oneofs(descriptor->oneof_decl(i)->name());
129 }
130 type->mutable_source_context()->set_file_name(descriptor->file()->name());
131 ConvertMessageOptions(descriptor->options(), type->mutable_options());
132 }
133
ConvertMessageOptions(const MessageOptions & options,RepeatedPtrField<Option> * output)134 void ConvertMessageOptions(const MessageOptions& options,
135 RepeatedPtrField<Option>* output) {
136 if (options.map_entry()) {
137 Option* option = output->Add();
138 option->set_name("map_entry");
139 BoolValue value;
140 value.set_value(true);
141 option->mutable_value()->PackFrom(value);
142 }
143
144 // TODO(xiaofeng): Set other "options"?
145 }
146
ConvertFieldDescriptor(const FieldDescriptor * descriptor,Field * field)147 void ConvertFieldDescriptor(const FieldDescriptor* descriptor, Field* field) {
148 field->set_kind(static_cast<Field::Kind>(descriptor->type()));
149 switch (descriptor->label()) {
150 case FieldDescriptor::LABEL_OPTIONAL:
151 field->set_cardinality(Field::CARDINALITY_OPTIONAL);
152 break;
153 case FieldDescriptor::LABEL_REPEATED:
154 field->set_cardinality(Field::CARDINALITY_REPEATED);
155 break;
156 case FieldDescriptor::LABEL_REQUIRED:
157 field->set_cardinality(Field::CARDINALITY_REQUIRED);
158 break;
159 }
160 field->set_number(descriptor->number());
161 field->set_name(descriptor->name());
162 field->set_json_name(descriptor->json_name());
163 if (descriptor->has_default_value()) {
164 field->set_default_value(DefaultValueAsString(descriptor));
165 }
166 if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
167 field->set_type_url(GetTypeUrl(descriptor->message_type()));
168 } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
169 field->set_type_url(GetTypeUrl(descriptor->enum_type()));
170 }
171 if (descriptor->containing_oneof() != NULL) {
172 field->set_oneof_index(descriptor->containing_oneof()->index() + 1);
173 }
174 if (descriptor->is_packed()) {
175 field->set_packed(true);
176 }
177
178 // TODO(xiaofeng): Set other field "options"?
179 }
180
ConvertEnumDescriptor(const EnumDescriptor * descriptor,Enum * enum_type)181 void ConvertEnumDescriptor(const EnumDescriptor* descriptor,
182 Enum* enum_type) {
183 enum_type->Clear();
184 enum_type->set_name(descriptor->full_name());
185 enum_type->mutable_source_context()->set_file_name(
186 descriptor->file()->name());
187 for (int i = 0; i < descriptor->value_count(); ++i) {
188 const EnumValueDescriptor* value_descriptor = descriptor->value(i);
189 EnumValue* value = enum_type->mutable_enumvalue()->Add();
190 value->set_name(value_descriptor->name());
191 value->set_number(value_descriptor->number());
192
193 // TODO(xiaofeng): Set EnumValue options.
194 }
195 // TODO(xiaofeng): Set Enum "options".
196 }
197
GetTypeUrl(const Descriptor * descriptor)198 string GetTypeUrl(const Descriptor* descriptor) {
199 return url_prefix_ + "/" + descriptor->full_name();
200 }
201
GetTypeUrl(const EnumDescriptor * descriptor)202 string GetTypeUrl(const EnumDescriptor* descriptor) {
203 return url_prefix_ + "/" + descriptor->full_name();
204 }
205
DefaultValueAsString(const FieldDescriptor * descriptor)206 string DefaultValueAsString(const FieldDescriptor* descriptor) {
207 switch (descriptor->cpp_type()) {
208 case FieldDescriptor::CPPTYPE_INT32:
209 return SimpleItoa(descriptor->default_value_int32());
210 break;
211 case FieldDescriptor::CPPTYPE_INT64:
212 return SimpleItoa(descriptor->default_value_int64());
213 break;
214 case FieldDescriptor::CPPTYPE_UINT32:
215 return SimpleItoa(descriptor->default_value_uint32());
216 break;
217 case FieldDescriptor::CPPTYPE_UINT64:
218 return SimpleItoa(descriptor->default_value_uint64());
219 break;
220 case FieldDescriptor::CPPTYPE_FLOAT:
221 return SimpleFtoa(descriptor->default_value_float());
222 break;
223 case FieldDescriptor::CPPTYPE_DOUBLE:
224 return SimpleDtoa(descriptor->default_value_double());
225 break;
226 case FieldDescriptor::CPPTYPE_BOOL:
227 return descriptor->default_value_bool() ? "true" : "false";
228 break;
229 case FieldDescriptor::CPPTYPE_STRING:
230 if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
231 return CEscape(descriptor->default_value_string());
232 } else {
233 return descriptor->default_value_string();
234 }
235 break;
236 case FieldDescriptor::CPPTYPE_ENUM:
237 return descriptor->default_value_enum()->name();
238 break;
239 case FieldDescriptor::CPPTYPE_MESSAGE:
240 GOOGLE_LOG(DFATAL) << "Messages can't have default values!";
241 break;
242 }
243 return "";
244 }
245
246 string url_prefix_;
247 const DescriptorPool* pool_;
248 };
249
250 } // namespace
251
NewTypeResolverForDescriptorPool(const string & url_prefix,const DescriptorPool * pool)252 TypeResolver* NewTypeResolverForDescriptorPool(const string& url_prefix,
253 const DescriptorPool* pool) {
254 return new DescriptorPoolTypeResolver(url_prefix, pool);
255 }
256
257 } // namespace util
258 } // namespace protobuf
259 } // namespace google
260