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 <sstream>
32
33 #include <google/protobuf/compiler/code_generator.h>
34 #include <google/protobuf/descriptor.h>
35 #include <google/protobuf/descriptor.pb.h>
36 #include <google/protobuf/io/printer.h>
37 #include <google/protobuf/io/zero_copy_stream.h>
38 #include <google/protobuf/stubs/strutil.h>
39
40
41 #include <google/protobuf/compiler/csharp/csharp_enum.h>
42 #include <google/protobuf/compiler/csharp/csharp_helpers.h>
43 #include <google/protobuf/compiler/csharp/csharp_field_base.h>
44 #include <google/protobuf/compiler/csharp/csharp_message.h>
45 #include <google/protobuf/compiler/csharp/csharp_names.h>
46 #include <google/protobuf/compiler/csharp/csharp_options.h>
47 #include <google/protobuf/compiler/csharp/csharp_reflection_class.h>
48
49 namespace google {
50 namespace protobuf {
51 namespace compiler {
52 namespace csharp {
53
ReflectionClassGenerator(const FileDescriptor * file,const Options * options)54 ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file,
55 const Options* options)
56 : SourceGeneratorBase(options),
57 file_(file) {
58 namespace_ = GetFileNamespace(file);
59 reflectionClassname_ = GetReflectionClassUnqualifiedName(file);
60 extensionClassname_ = GetExtensionClassUnqualifiedName(file);
61 }
62
~ReflectionClassGenerator()63 ReflectionClassGenerator::~ReflectionClassGenerator() {
64 }
65
Generate(io::Printer * printer)66 void ReflectionClassGenerator::Generate(io::Printer* printer) {
67 WriteIntroduction(printer);
68
69 WriteDescriptor(printer);
70 // Close the class declaration.
71 printer->Outdent();
72 printer->Print("}\n");
73
74 if (file_->extension_count() > 0) {
75 printer->Print(
76 "/// <summary>Holder for extension identifiers generated from the top "
77 "level of $file_name$</summary>\n"
78 "$access_level$ static partial class $class_name$ {\n",
79 "access_level", class_access_level(), "class_name", extensionClassname_,
80 "file_name", file_->name());
81 printer->Indent();
82 for (int i = 0; i < file_->extension_count(); i++) {
83 std::unique_ptr<FieldGeneratorBase> generator(
84 CreateFieldGenerator(file_->extension(i), -1, this->options()));
85 generator->GenerateExtensionCode(printer);
86 }
87 printer->Outdent();
88 printer->Print(
89 "}\n"
90 "\n");
91 }
92
93 // write children: Enums
94 if (file_->enum_type_count() > 0) {
95 printer->Print("#region Enums\n");
96 for (int i = 0; i < file_->enum_type_count(); i++) {
97 EnumGenerator enumGenerator(file_->enum_type(i), this->options());
98 enumGenerator.Generate(printer);
99 }
100 printer->Print("#endregion\n");
101 printer->Print("\n");
102 }
103
104 // write children: Messages
105 if (file_->message_type_count() > 0) {
106 printer->Print("#region Messages\n");
107 for (int i = 0; i < file_->message_type_count(); i++) {
108 MessageGenerator messageGenerator(file_->message_type(i), this->options());
109 messageGenerator.Generate(printer);
110 }
111 printer->Print("#endregion\n");
112 printer->Print("\n");
113 }
114
115 // TODO(jtattermusch): add insertion point for services.
116
117 if (!namespace_.empty()) {
118 printer->Outdent();
119 printer->Print("}\n");
120 }
121 printer->Print("\n");
122 printer->Print("#endregion Designer generated code\n");
123 }
124
WriteIntroduction(io::Printer * printer)125 void ReflectionClassGenerator::WriteIntroduction(io::Printer* printer) {
126 printer->Print(
127 "// <auto-generated>\n"
128 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
129 "// source: $file_name$\n"
130 "// </auto-generated>\n"
131 "#pragma warning disable 1591, 0612, 3021, 8981\n"
132 "#region Designer generated code\n"
133 "\n"
134 "using pb = global::Google.Protobuf;\n"
135 "using pbc = global::Google.Protobuf.Collections;\n"
136 "using pbr = global::Google.Protobuf.Reflection;\n"
137 "using scg = global::System.Collections.Generic;\n",
138 "file_name", file_->name());
139
140 if (!namespace_.empty()) {
141 printer->Print("namespace $namespace$ {\n", "namespace", namespace_);
142 printer->Indent();
143 printer->Print("\n");
144 }
145
146 printer->Print(
147 "/// <summary>Holder for reflection information generated from $file_name$</summary>\n"
148 "$access_level$ static partial class $reflection_class_name$ {\n"
149 "\n",
150 "file_name", file_->name(),
151 "access_level", class_access_level(),
152 "reflection_class_name", reflectionClassname_);
153 printer->Indent();
154 }
155
WriteDescriptor(io::Printer * printer)156 void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
157 printer->Print(
158 "#region Descriptor\n"
159 "/// <summary>File descriptor for $file_name$</summary>\n"
160 "public static pbr::FileDescriptor Descriptor {\n"
161 " get { return descriptor; }\n"
162 "}\n"
163 "private static pbr::FileDescriptor descriptor;\n"
164 "\n"
165 "static $reflection_class_name$() {\n",
166 "file_name", file_->name(),
167 "reflection_class_name", reflectionClassname_);
168 printer->Indent();
169 printer->Print(
170 "byte[] descriptorData = global::System.Convert.FromBase64String(\n");
171 printer->Indent();
172 printer->Indent();
173 printer->Print("string.Concat(\n");
174 printer->Indent();
175
176 // TODO(jonskeet): Consider a C#-escaping format here instead of just Base64.
177 std::string base64 = FileDescriptorToBase64(file_);
178 while (base64.size() > 60) {
179 printer->Print("\"$base64$\",\n", "base64", base64.substr(0, 60));
180 base64 = base64.substr(60);
181 }
182 printer->Print("\"$base64$\"));\n", "base64", base64);
183 printer->Outdent();
184 printer->Outdent();
185 printer->Outdent();
186
187 // -----------------------------------------------------------------
188 // Invoke InternalBuildGeneratedFileFrom() to build the file.
189 printer->Print(
190 "descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n");
191 printer->Print(" new pbr::FileDescriptor[] { ");
192 for (int i = 0; i < file_->dependency_count(); i++) {
193 printer->Print(
194 "$full_reflection_class_name$.Descriptor, ",
195 "full_reflection_class_name",
196 GetReflectionClassName(file_->dependency(i)));
197 }
198 printer->Print("},\n"
199 " new pbr::GeneratedClrTypeInfo(");
200 // Specify all the generated code information, recursively.
201 if (file_->enum_type_count() > 0) {
202 printer->Print("new[] {");
203 for (int i = 0; i < file_->enum_type_count(); i++) {
204 printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
205 }
206 printer->Print("}, ");
207 }
208 else {
209 printer->Print("null, ");
210 }
211 if (file_->extension_count() > 0) {
212 std::vector<std::string> extensions;
213 for (int i = 0; i < file_->extension_count(); i++) {
214 extensions.push_back(GetFullExtensionName(file_->extension(i)));
215 }
216 printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", Join(extensions, ", "));
217 }
218 else {
219 printer->Print("null, ");
220 }
221 if (file_->message_type_count() > 0) {
222 printer->Print("new pbr::GeneratedClrTypeInfo[] {\n");
223 printer->Indent();
224 printer->Indent();
225 printer->Indent();
226 for (int i = 0; i < file_->message_type_count(); i++) {
227 WriteGeneratedCodeInfo(file_->message_type(i), printer, i == file_->message_type_count() - 1);
228 }
229 printer->Outdent();
230 printer->Print("\n}));\n");
231 printer->Outdent();
232 printer->Outdent();
233 }
234 else {
235 printer->Print("null));\n");
236 }
237
238 printer->Outdent();
239 printer->Print("}\n");
240 printer->Print("#endregion\n\n");
241 }
242
243 // Write out the generated code for a particular message. This consists of the CLR type, property names
244 // corresponding to fields, names corresponding to oneofs, nested enums, and nested types. Each array part
245 // can be specified as null if it would be empty, to make the generated code somewhat simpler to read.
246 // We write a line break at the end of each generated code info, so that in the final file we'll see all
247 // the types, pre-ordered depth first, one per line. The indentation will be slightly unusual,
248 // in that it will look like a single array when it's actually constructing a tree, but it'll be easy to
249 // read even with multiple levels of nesting.
250 // The "last" parameter indicates whether this message descriptor is the last one being printed in this immediate
251 // context. It governs whether or not a trailing comma and newline is written after the constructor, effectively
252 // just controlling the formatting in the generated code.
WriteGeneratedCodeInfo(const Descriptor * descriptor,io::Printer * printer,bool last)253 void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) {
254 if (IsMapEntryMessage(descriptor)) {
255 printer->Print("null, ");
256 return;
257 }
258 // Generated message type
259 printer->Print("new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
260
261 // Fields
262 if (descriptor->field_count() > 0) {
263 std::vector<std::string> fields;
264 fields.reserve(descriptor->field_count());
265 for (int i = 0; i < descriptor->field_count(); i++) {
266 fields.push_back(GetPropertyName(descriptor->field(i)));
267 }
268 printer->Print("new[]{ \"$fields$\" }, ", "fields", Join(fields, "\", \""));
269 }
270 else {
271 printer->Print("null, ");
272 }
273
274 // Oneofs
275 if (descriptor->oneof_decl_count() > 0) {
276 std::vector<std::string> oneofs;
277 oneofs.reserve(descriptor->oneof_decl_count());
278 for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
279 oneofs.push_back(UnderscoresToCamelCase(descriptor->oneof_decl(i)->name(), true));
280 }
281 printer->Print("new[]{ \"$oneofs$\" }, ", "oneofs", Join(oneofs, "\", \""));
282 }
283 else {
284 printer->Print("null, ");
285 }
286
287 // Nested enums
288 if (descriptor->enum_type_count() > 0) {
289 std::vector<std::string> enums;
290 enums.reserve(descriptor->enum_type_count());
291 for (int i = 0; i < descriptor->enum_type_count(); i++) {
292 enums.push_back(GetClassName(descriptor->enum_type(i)));
293 }
294 printer->Print("new[]{ typeof($enums$) }, ", "enums", Join(enums, "), typeof("));
295 }
296 else {
297 printer->Print("null, ");
298 }
299
300 // Extensions
301 if (descriptor->extension_count() > 0) {
302 std::vector<std::string> extensions;
303 for (int i = 0; i < descriptor->extension_count(); i++) {
304 extensions.push_back(GetFullExtensionName(descriptor->extension(i)));
305 }
306 printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", Join(extensions, ", "));
307 }
308 else {
309 printer->Print("null, ");
310 }
311
312 // Nested types
313 if (descriptor->nested_type_count() > 0) {
314 // Need to specify array type explicitly here, as all elements may be null.
315 printer->Print("new pbr::GeneratedClrTypeInfo[] { ");
316 for (int i = 0; i < descriptor->nested_type_count(); i++) {
317 WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1);
318 }
319 printer->Print("}");
320 }
321 else {
322 printer->Print("null");
323 }
324 printer->Print(last ? ")" : "),\n");
325 }
326
327 } // namespace csharp
328 } // namespace compiler
329 } // namespace protobuf
330 } // namespace google
331