Line data Source code
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 : // Author: kenton@google.com (Kenton Varda)
32 : // Based on original Protocol Buffers design by
33 : // Sanjay Ghemawat, Jeff Dean, and others.
34 :
35 : #include <algorithm>
36 : #include <google/protobuf/stubs/hash.h>
37 : #include <limits>
38 : #include <vector>
39 :
40 : #include <google/protobuf/compiler/csharp/csharp_helpers.h>
41 : #include <google/protobuf/descriptor.pb.h>
42 : #include <google/protobuf/io/printer.h>
43 : #include <google/protobuf/wire_format.h>
44 : #include <google/protobuf/stubs/strutil.h>
45 : #include <google/protobuf/stubs/substitute.h>
46 :
47 : #include <google/protobuf/compiler/csharp/csharp_field_base.h>
48 : #include <google/protobuf/compiler/csharp/csharp_enum_field.h>
49 : #include <google/protobuf/compiler/csharp/csharp_map_field.h>
50 : #include <google/protobuf/compiler/csharp/csharp_message_field.h>
51 : #include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
52 : #include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
53 : #include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h>
54 : #include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h>
55 : #include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
56 :
57 : namespace google {
58 : namespace protobuf {
59 : namespace compiler {
60 : namespace csharp {
61 :
62 0 : CSharpType GetCSharpType(FieldDescriptor::Type type) {
63 0 : switch (type) {
64 : case FieldDescriptor::TYPE_INT32:
65 : return CSHARPTYPE_INT32;
66 : case FieldDescriptor::TYPE_INT64:
67 0 : return CSHARPTYPE_INT64;
68 : case FieldDescriptor::TYPE_UINT32:
69 0 : return CSHARPTYPE_UINT32;
70 : case FieldDescriptor::TYPE_UINT64:
71 0 : return CSHARPTYPE_UINT32;
72 : case FieldDescriptor::TYPE_SINT32:
73 : return CSHARPTYPE_INT32;
74 : case FieldDescriptor::TYPE_SINT64:
75 0 : return CSHARPTYPE_INT64;
76 : case FieldDescriptor::TYPE_FIXED32:
77 0 : return CSHARPTYPE_UINT32;
78 : case FieldDescriptor::TYPE_FIXED64:
79 0 : return CSHARPTYPE_UINT64;
80 : case FieldDescriptor::TYPE_SFIXED32:
81 : return CSHARPTYPE_INT32;
82 : case FieldDescriptor::TYPE_SFIXED64:
83 0 : return CSHARPTYPE_INT64;
84 : case FieldDescriptor::TYPE_FLOAT:
85 0 : return CSHARPTYPE_FLOAT;
86 : case FieldDescriptor::TYPE_DOUBLE:
87 0 : return CSHARPTYPE_DOUBLE;
88 : case FieldDescriptor::TYPE_BOOL:
89 0 : return CSHARPTYPE_BOOL;
90 : case FieldDescriptor::TYPE_ENUM:
91 0 : return CSHARPTYPE_ENUM;
92 : case FieldDescriptor::TYPE_STRING:
93 0 : return CSHARPTYPE_STRING;
94 : case FieldDescriptor::TYPE_BYTES:
95 0 : return CSHARPTYPE_BYTESTRING;
96 : case FieldDescriptor::TYPE_GROUP:
97 0 : return CSHARPTYPE_MESSAGE;
98 : case FieldDescriptor::TYPE_MESSAGE:
99 0 : return CSHARPTYPE_MESSAGE;
100 :
101 : // No default because we want the compiler to complain if any new
102 : // types are added.
103 : }
104 0 : GOOGLE_LOG(FATAL)<< "Can't get here.";
105 0 : return (CSharpType) -1;
106 : }
107 :
108 0 : std::string StripDotProto(const std::string& proto_file) {
109 0 : int lastindex = proto_file.find_last_of(".");
110 0 : return proto_file.substr(0, lastindex);
111 : }
112 :
113 0 : std::string GetFileNamespace(const FileDescriptor* descriptor) {
114 0 : if (descriptor->options().has_csharp_namespace()) {
115 0 : return descriptor->options().csharp_namespace();
116 : }
117 0 : return UnderscoresToCamelCase(descriptor->package(), true, true);
118 : }
119 :
120 0 : std::string GetUmbrellaClassUnqualifiedName(const FileDescriptor* descriptor) {
121 : // We manually rename Descriptor to DescriptorProtoFile to avoid collisions with
122 : // the static Descriptor property. It would be nice to be able to do this with an
123 : // option, but it would be rarely used.
124 0 : if (IsDescriptorProto(descriptor)) {
125 0 : return "DescriptorProtoFile";
126 : }
127 : // umbrella_classname can no longer be set using message option.
128 0 : std::string proto_file = descriptor->name();
129 0 : int lastslash = proto_file.find_last_of("/");
130 0 : std::string base = proto_file.substr(lastslash + 1);
131 0 : return UnderscoresToPascalCase(StripDotProto(base));
132 : }
133 :
134 0 : std::string GetUmbrellaClassNestedNamespace(const FileDescriptor* descriptor) {
135 : // TODO(jtattermusch): reintroduce csharp_umbrella_namespace option
136 0 : bool collision = false;
137 0 : std::string umbrella_classname = GetUmbrellaClassUnqualifiedName(descriptor);
138 0 : for(int i = 0; i < descriptor->message_type_count(); i++) {
139 0 : if (descriptor->message_type(i)->name() == umbrella_classname) {
140 : collision = true;
141 : break;
142 : }
143 : }
144 0 : for (int i = 0; i < descriptor->service_count(); i++) {
145 0 : if (descriptor->service(i)->name() == umbrella_classname) {
146 : collision = true;
147 : break;
148 : }
149 : }
150 0 : for (int i = 0; i < descriptor->enum_type_count(); i++) {
151 0 : if (descriptor->enum_type(i)->name() == umbrella_classname) {
152 : collision = true;
153 : break;
154 : }
155 : }
156 0 : return collision ? "Proto" : "";
157 : }
158 :
159 : // TODO(jtattermusch): can we reuse a utility function?
160 0 : std::string UnderscoresToCamelCase(const std::string& input,
161 : bool cap_next_letter,
162 : bool preserve_period) {
163 : string result;
164 : // Note: I distrust ctype.h due to locales.
165 0 : for (int i = 0; i < input.size(); i++) {
166 0 : if ('a' <= input[i] && input[i] <= 'z') {
167 0 : if (cap_next_letter) {
168 0 : result += input[i] + ('A' - 'a');
169 : } else {
170 0 : result += input[i];
171 : }
172 : cap_next_letter = false;
173 0 : } else if ('A' <= input[i] && input[i] <= 'Z') {
174 0 : if (i == 0 && !cap_next_letter) {
175 : // Force first letter to lower-case unless explicitly told to
176 : // capitalize it.
177 0 : result += input[i] + ('a' - 'A');
178 : } else {
179 : // Capital letters after the first are left as-is.
180 0 : result += input[i];
181 : }
182 : cap_next_letter = false;
183 0 : } else if ('0' <= input[i] && input[i] <= '9') {
184 0 : result += input[i];
185 : cap_next_letter = true;
186 : } else {
187 0 : cap_next_letter = true;
188 0 : if (input[i] == '.' && preserve_period) {
189 : result += '.';
190 : }
191 : }
192 : }
193 : // Add a trailing "_" if the name should be altered.
194 0 : if (input[input.size() - 1] == '#') {
195 : result += '_';
196 : }
197 0 : return result;
198 : }
199 :
200 0 : std::string UnderscoresToPascalCase(const std::string& input) {
201 0 : return UnderscoresToCamelCase(input, true);
202 : }
203 :
204 0 : std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
205 0 : std::string result = GetFileNamespace(file);
206 0 : if (result != "") {
207 : result += '.';
208 : }
209 : string classname;
210 0 : if (file->package().empty()) {
211 : classname = name;
212 : } else {
213 : // Strip the proto package from full_name since we've replaced it with
214 : // the C# namespace.
215 0 : classname = name.substr(file->package().size() + 1);
216 : }
217 0 : result += StringReplace(classname, ".", ".Types.", true);
218 0 : return "global::" + result;
219 : }
220 :
221 0 : std::string GetUmbrellaClassName(const FileDescriptor* descriptor) {
222 0 : std::string result = GetFileNamespace(descriptor);
223 0 : if (!result.empty()) {
224 : result += '.';
225 : }
226 0 : std::string umbrellaNamespace = GetUmbrellaClassNestedNamespace(descriptor);
227 0 : if (!umbrellaNamespace.empty()) {
228 0 : result += umbrellaNamespace + ".";
229 : }
230 0 : result += GetUmbrellaClassUnqualifiedName(descriptor);
231 0 : return "global::" + result;
232 : }
233 :
234 0 : std::string GetClassName(const Descriptor* descriptor) {
235 0 : return ToCSharpName(descriptor->full_name(), descriptor->file());
236 : }
237 :
238 0 : std::string GetClassName(const EnumDescriptor* descriptor) {
239 0 : return ToCSharpName(descriptor->full_name(), descriptor->file());
240 : }
241 :
242 : // Groups are hacky: The name of the field is just the lower-cased name
243 : // of the group type. In C#, though, we would like to retain the original
244 : // capitalization of the type name.
245 0 : std::string GetFieldName(const FieldDescriptor* descriptor) {
246 0 : if (descriptor->type() == FieldDescriptor::TYPE_GROUP) {
247 0 : return descriptor->message_type()->name();
248 : } else {
249 0 : return descriptor->name();
250 : }
251 : }
252 :
253 0 : std::string GetFieldConstantName(const FieldDescriptor* field) {
254 0 : return GetPropertyName(field) + "FieldNumber";
255 : }
256 :
257 0 : std::string GetPropertyName(const FieldDescriptor* descriptor) {
258 : // TODO(jtattermusch): consider introducing csharp_property_name field option
259 0 : std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
260 : // Avoid either our own type name or reserved names. Note that not all names
261 : // are reserved - a field called to_string, write_to etc would still cause a problem.
262 : // There are various ways of ending up with naming collisions, but we try to avoid obvious
263 : // ones.
264 0 : if (property_name == descriptor->containing_type()->name()
265 0 : || property_name == "Types"
266 0 : || property_name == "Descriptor") {
267 : property_name += "_";
268 : }
269 0 : return property_name;
270 : }
271 :
272 : // TODO: c&p from Java protoc plugin
273 : // For encodings with fixed sizes, returns that size in bytes. Otherwise
274 : // returns -1.
275 0 : int GetFixedSize(FieldDescriptor::Type type) {
276 0 : switch (type) {
277 : case FieldDescriptor::TYPE_INT32 : return -1;
278 : case FieldDescriptor::TYPE_INT64 : return -1;
279 : case FieldDescriptor::TYPE_UINT32 : return -1;
280 : case FieldDescriptor::TYPE_UINT64 : return -1;
281 : case FieldDescriptor::TYPE_SINT32 : return -1;
282 : case FieldDescriptor::TYPE_SINT64 : return -1;
283 0 : case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size;
284 0 : case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size;
285 0 : case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size;
286 0 : case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size;
287 0 : case FieldDescriptor::TYPE_FLOAT : return internal::WireFormatLite::kFloatSize;
288 0 : case FieldDescriptor::TYPE_DOUBLE : return internal::WireFormatLite::kDoubleSize;
289 :
290 0 : case FieldDescriptor::TYPE_BOOL : return internal::WireFormatLite::kBoolSize;
291 : case FieldDescriptor::TYPE_ENUM : return -1;
292 :
293 : case FieldDescriptor::TYPE_STRING : return -1;
294 : case FieldDescriptor::TYPE_BYTES : return -1;
295 : case FieldDescriptor::TYPE_GROUP : return -1;
296 : case FieldDescriptor::TYPE_MESSAGE : return -1;
297 :
298 : // No default because we want the compiler to complain if any new
299 : // types are added.
300 : }
301 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
302 0 : return -1;
303 : }
304 :
305 : static const char base64_chars[] =
306 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
307 :
308 0 : std::string StringToBase64(const std::string& input) {
309 : std::string result;
310 0 : size_t remaining = input.size();
311 0 : const unsigned char *src = (const unsigned char*) input.c_str();
312 0 : while (remaining > 2) {
313 0 : result += base64_chars[src[0] >> 2];
314 0 : result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
315 0 : result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)];
316 0 : result += base64_chars[src[2] & 0x3f];
317 0 : remaining -= 3;
318 0 : src += 3;
319 : }
320 0 : switch (remaining) {
321 : case 2:
322 0 : result += base64_chars[src[0] >> 2];
323 0 : result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
324 0 : result += base64_chars[(src[1] & 0xf) << 2];
325 : result += '=';
326 : src += 2;
327 : break;
328 : case 1:
329 0 : result += base64_chars[src[0] >> 2];
330 0 : result += base64_chars[((src[0] & 0x3) << 4)];
331 : result += '=';
332 : result += '=';
333 : src += 1;
334 : break;
335 : }
336 0 : return result;
337 : }
338 :
339 0 : std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
340 : std::string fdp_bytes;
341 0 : FileDescriptorProto fdp;
342 0 : descriptor->CopyTo(&fdp);
343 0 : fdp.SerializeToString(&fdp_bytes);
344 0 : return StringToBase64(fdp_bytes);
345 : }
346 :
347 0 : FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
348 : int fieldOrdinal) {
349 0 : switch (descriptor->type()) {
350 : case FieldDescriptor::TYPE_GROUP:
351 : case FieldDescriptor::TYPE_MESSAGE:
352 0 : if (descriptor->is_repeated()) {
353 0 : if (descriptor->is_map()) {
354 0 : return new MapFieldGenerator(descriptor, fieldOrdinal);
355 : } else {
356 0 : return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
357 : }
358 : } else {
359 0 : if (IsWrapperType(descriptor)) {
360 0 : if (descriptor->containing_oneof()) {
361 0 : return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal);
362 : } else {
363 0 : return new WrapperFieldGenerator(descriptor, fieldOrdinal);
364 : }
365 : } else {
366 0 : if (descriptor->containing_oneof()) {
367 0 : return new MessageOneofFieldGenerator(descriptor, fieldOrdinal);
368 : } else {
369 0 : return new MessageFieldGenerator(descriptor, fieldOrdinal);
370 : }
371 : }
372 : }
373 : case FieldDescriptor::TYPE_ENUM:
374 0 : if (descriptor->is_repeated()) {
375 0 : return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal);
376 : } else {
377 0 : if (descriptor->containing_oneof()) {
378 0 : return new EnumOneofFieldGenerator(descriptor, fieldOrdinal);
379 : } else {
380 0 : return new EnumFieldGenerator(descriptor, fieldOrdinal);
381 : }
382 : }
383 : default:
384 0 : if (descriptor->is_repeated()) {
385 0 : return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal);
386 : } else {
387 0 : if (descriptor->containing_oneof()) {
388 0 : return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal);
389 : } else {
390 0 : return new PrimitiveFieldGenerator(descriptor, fieldOrdinal);
391 : }
392 : }
393 : }
394 : }
395 :
396 : } // namespace csharp
397 : } // namespace compiler
398 : } // namespace protobuf
399 : } // namespace google
|