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
49namespace google {
50namespace protobuf {
51namespace compiler {
52namespace csharp {
53
54ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file,
55 const Options* options)
56 : SourceGeneratorBase(options),
57 file_(file) {
58 namespace_ = GetFileNamespace(descriptor: file);
59 reflectionClassname_ = GetReflectionClassUnqualifiedName(descriptor: file);
60 extensionClassname_ = GetExtensionClassUnqualifiedName(descriptor: file);
61}
62
63ReflectionClassGenerator::~ReflectionClassGenerator() {
64}
65
66void ReflectionClassGenerator::Generate(io::Printer* printer) {
67 WriteIntroduction(printer);
68
69 WriteDescriptor(printer);
70 // Close the class declaration.
71 printer->Outdent();
72 printer->Print(text: "}\n");
73
74 if (file_->extension_count() > 0) {
75 printer->Print(
76 text: "/// <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 args: "access_level", args: class_access_level(), args: "class_name", args: extensionClassname_,
80 args: "file_name", args: file_->name());
81 printer->Indent();
82 for (int i = 0; i < file_->extension_count(); i++) {
83 std::unique_ptr<FieldGeneratorBase> generator(
84 CreateFieldGenerator(descriptor: file_->extension(index: i), presenceIndex: -1, options: this->options()));
85 generator->GenerateExtensionCode(printer);
86 }
87 printer->Outdent();
88 printer->Print(
89 text: "}\n"
90 "\n");
91 }
92
93 // write children: Enums
94 if (file_->enum_type_count() > 0) {
95 printer->Print(text: "#region Enums\n");
96 for (int i = 0; i < file_->enum_type_count(); i++) {
97 EnumGenerator enumGenerator(file_->enum_type(index: i), this->options());
98 enumGenerator.Generate(printer);
99 }
100 printer->Print(text: "#endregion\n");
101 printer->Print(text: "\n");
102 }
103
104 // write children: Messages
105 if (file_->message_type_count() > 0) {
106 printer->Print(text: "#region Messages\n");
107 for (int i = 0; i < file_->message_type_count(); i++) {
108 MessageGenerator messageGenerator(file_->message_type(index: i), this->options());
109 messageGenerator.Generate(printer);
110 }
111 printer->Print(text: "#endregion\n");
112 printer->Print(text: "\n");
113 }
114
115 // TODO(jtattermusch): add insertion point for services.
116
117 if (!namespace_.empty()) {
118 printer->Outdent();
119 printer->Print(text: "}\n");
120 }
121 printer->Print(text: "\n");
122 printer->Print(text: "#endregion Designer generated code\n");
123}
124
125void ReflectionClassGenerator::WriteIntroduction(io::Printer* printer) {
126 printer->Print(
127 text: "// <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 args: "file_name", args: file_->name());
139
140 if (!namespace_.empty()) {
141 printer->Print(text: "namespace $namespace$ {\n", args: "namespace", args: namespace_);
142 printer->Indent();
143 printer->Print(text: "\n");
144 }
145
146 printer->Print(
147 text: "/// <summary>Holder for reflection information generated from $file_name$</summary>\n"
148 "$access_level$ static partial class $reflection_class_name$ {\n"
149 "\n",
150 args: "file_name", args: file_->name(),
151 args: "access_level", args: class_access_level(),
152 args: "reflection_class_name", args: reflectionClassname_);
153 printer->Indent();
154}
155
156void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
157 printer->Print(
158 text: "#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 args: "file_name", args: file_->name(),
167 args: "reflection_class_name", args: reflectionClassname_);
168 printer->Indent();
169 printer->Print(
170 text: "byte[] descriptorData = global::System.Convert.FromBase64String(\n");
171 printer->Indent();
172 printer->Indent();
173 printer->Print(text: "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(descriptor: file_);
178 while (base64.size() > 60) {
179 printer->Print(text: "\"$base64$\",\n", args: "base64", args: base64.substr(pos: 0, n: 60));
180 base64 = base64.substr(pos: 60);
181 }
182 printer->Print(text: "\"$base64$\"));\n", args: "base64", args: base64);
183 printer->Outdent();
184 printer->Outdent();
185 printer->Outdent();
186
187 // -----------------------------------------------------------------
188 // Invoke InternalBuildGeneratedFileFrom() to build the file.
189 printer->Print(
190 text: "descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n");
191 printer->Print(text: " new pbr::FileDescriptor[] { ");
192 for (int i = 0; i < file_->dependency_count(); i++) {
193 printer->Print(
194 text: "$full_reflection_class_name$.Descriptor, ",
195 args: "full_reflection_class_name",
196 args: GetReflectionClassName(descriptor: file_->dependency(index: i)));
197 }
198 printer->Print(text: "},\n"
199 " new pbr::GeneratedClrTypeInfo(");
200 // Specify all the generated code information, recursively.
201 if (file_->enum_type_count() > 0) {
202 printer->Print(text: "new[] {");
203 for (int i = 0; i < file_->enum_type_count(); i++) {
204 printer->Print(text: "typeof($type_name$), ", args: "type_name", args: GetClassName(descriptor: file_->enum_type(index: i)));
205 }
206 printer->Print(text: "}, ");
207 }
208 else {
209 printer->Print(text: "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(x: GetFullExtensionName(descriptor: file_->extension(index: i)));
215 }
216 printer->Print(text: "new pb::Extension[] { $extensions$ }, ", args: "extensions", args: Join(components: extensions, delim: ", "));
217 }
218 else {
219 printer->Print(text: "null, ");
220 }
221 if (file_->message_type_count() > 0) {
222 printer->Print(text: "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(descriptor: file_->message_type(index: i), printer, last: i == file_->message_type_count() - 1);
228 }
229 printer->Outdent();
230 printer->Print(text: "\n}));\n");
231 printer->Outdent();
232 printer->Outdent();
233 }
234 else {
235 printer->Print(text: "null));\n");
236 }
237
238 printer->Outdent();
239 printer->Print(text: "}\n");
240 printer->Print(text: "#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.
253void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) {
254 if (IsMapEntryMessage(descriptor)) {
255 printer->Print(text: "null, ");
256 return;
257 }
258 // Generated message type
259 printer->Print(text: "new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", args: "type_name", args: GetClassName(descriptor));
260
261 // Fields
262 if (descriptor->field_count() > 0) {
263 std::vector<std::string> fields;
264 fields.reserve(n: descriptor->field_count());
265 for (int i = 0; i < descriptor->field_count(); i++) {
266 fields.push_back(x: GetPropertyName(descriptor: descriptor->field(index: i)));
267 }
268 printer->Print(text: "new[]{ \"$fields$\" }, ", args: "fields", args: Join(components: fields, delim: "\", \""));
269 }
270 else {
271 printer->Print(text: "null, ");
272 }
273
274 // Oneofs
275 if (descriptor->oneof_decl_count() > 0) {
276 std::vector<std::string> oneofs;
277 oneofs.reserve(n: descriptor->oneof_decl_count());
278 for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
279 oneofs.push_back(x: UnderscoresToCamelCase(input: descriptor->oneof_decl(index: i)->name(), cap_next_letter: true));
280 }
281 printer->Print(text: "new[]{ \"$oneofs$\" }, ", args: "oneofs", args: Join(components: oneofs, delim: "\", \""));
282 }
283 else {
284 printer->Print(text: "null, ");
285 }
286
287 // Nested enums
288 if (descriptor->enum_type_count() > 0) {
289 std::vector<std::string> enums;
290 enums.reserve(n: descriptor->enum_type_count());
291 for (int i = 0; i < descriptor->enum_type_count(); i++) {
292 enums.push_back(x: GetClassName(descriptor: descriptor->enum_type(index: i)));
293 }
294 printer->Print(text: "new[]{ typeof($enums$) }, ", args: "enums", args: Join(components: enums, delim: "), typeof("));
295 }
296 else {
297 printer->Print(text: "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(x: GetFullExtensionName(descriptor: descriptor->extension(index: i)));
305 }
306 printer->Print(text: "new pb::Extension[] { $extensions$ }, ", args: "extensions", args: Join(components: extensions, delim: ", "));
307 }
308 else {
309 printer->Print(text: "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(text: "new pbr::GeneratedClrTypeInfo[] { ");
316 for (int i = 0; i < descriptor->nested_type_count(); i++) {
317 WriteGeneratedCodeInfo(descriptor: descriptor->nested_type(index: i), printer, last: i == descriptor->nested_type_count() - 1);
318 }
319 printer->Print(text: "}");
320 }
321 else {
322 printer->Print(text: "null");
323 }
324 printer->Print(text: last ? ")" : "),\n");
325}
326
327} // namespace csharp
328} // namespace compiler
329} // namespace protobuf
330} // namespace google
331