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 <google/protobuf/compiler/java/java_doc_comment.h>
36
37 #include <vector>
38
39 #include <google/protobuf/io/printer.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <google/protobuf/descriptor.pb.h>
42
43 namespace google {
44 namespace protobuf {
45 namespace compiler {
46 namespace java {
47
EscapeJavadoc(const std::string & input)48 std::string EscapeJavadoc(const std::string& input) {
49 std::string result;
50 result.reserve(input.size() * 2);
51
52 char prev = '*';
53
54 for (std::string::size_type i = 0; i < input.size(); i++) {
55 char c = input[i];
56 switch (c) {
57 case '*':
58 // Avoid "/*".
59 if (prev == '/') {
60 result.append("*");
61 } else {
62 result.push_back(c);
63 }
64 break;
65 case '/':
66 // Avoid "*/".
67 if (prev == '*') {
68 result.append("/");
69 } else {
70 result.push_back(c);
71 }
72 break;
73 case '@':
74 // '@' starts javadoc tags including the @deprecated tag, which will
75 // cause a compile-time error if inserted before a declaration that
76 // does not have a corresponding @Deprecated annotation.
77 result.append("@");
78 break;
79 case '<':
80 // Avoid interpretation as HTML.
81 result.append("<");
82 break;
83 case '>':
84 // Avoid interpretation as HTML.
85 result.append(">");
86 break;
87 case '&':
88 // Avoid interpretation as HTML.
89 result.append("&");
90 break;
91 case '\\':
92 // Java interprets Unicode escape sequences anywhere!
93 result.append("\");
94 break;
95 default:
96 result.push_back(c);
97 break;
98 }
99
100 prev = c;
101 }
102
103 return result;
104 }
105
WriteDocCommentBodyForLocation(io::Printer * printer,const SourceLocation & location)106 static void WriteDocCommentBodyForLocation(io::Printer* printer,
107 const SourceLocation& location) {
108 std::string comments = location.leading_comments.empty()
109 ? location.trailing_comments
110 : location.leading_comments;
111 if (!comments.empty()) {
112 // TODO(kenton): Ideally we should parse the comment text as Markdown and
113 // write it back as HTML, but this requires a Markdown parser. For now
114 // we just use <pre> to get fixed-width text formatting.
115
116 // If the comment itself contains block comment start or end markers,
117 // HTML-escape them so that they don't accidentally close the doc comment.
118 comments = EscapeJavadoc(comments);
119
120 std::vector<std::string> lines = Split(comments, "\n");
121 while (!lines.empty() && lines.back().empty()) {
122 lines.pop_back();
123 }
124
125 printer->Print(" * <pre>\n");
126 for (int i = 0; i < lines.size(); i++) {
127 // Most lines should start with a space. Watch out for lines that start
128 // with a /, since putting that right after the leading asterisk will
129 // close the comment.
130 if (!lines[i].empty() && lines[i][0] == '/') {
131 printer->Print(" * $line$\n", "line", lines[i]);
132 } else {
133 printer->Print(" *$line$\n", "line", lines[i]);
134 }
135 }
136 printer->Print(
137 " * </pre>\n"
138 " *\n");
139 }
140 }
141
142 template <typename DescriptorType>
WriteDocCommentBody(io::Printer * printer,const DescriptorType * descriptor)143 static void WriteDocCommentBody(io::Printer* printer,
144 const DescriptorType* descriptor) {
145 SourceLocation location;
146 if (descriptor->GetSourceLocation(&location)) {
147 WriteDocCommentBodyForLocation(printer, location);
148 }
149 }
150
FirstLineOf(const std::string & value)151 static std::string FirstLineOf(const std::string& value) {
152 std::string result = value;
153
154 std::string::size_type pos = result.find_first_of('\n');
155 if (pos != std::string::npos) {
156 result.erase(pos);
157 }
158
159 // If line ends in an opening brace, make it "{ ... }" so it looks nice.
160 if (!result.empty() && result[result.size() - 1] == '{') {
161 result.append(" ... }");
162 }
163
164 return result;
165 }
166
WriteMessageDocComment(io::Printer * printer,const Descriptor * message)167 void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) {
168 printer->Print("/**\n");
169 WriteDocCommentBody(printer, message);
170 printer->Print(
171 " * Protobuf type {@code $fullname$}\n"
172 " */\n",
173 "fullname", EscapeJavadoc(message->full_name()));
174 }
175
WriteFieldDocComment(io::Printer * printer,const FieldDescriptor * field)176 void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) {
177 // We start the comment with the main body based on the comments from the
178 // .proto file (if present). We then continue with the field declaration,
179 // e.g.:
180 // optional string foo = 5;
181 // And then we end with the javadoc tags if applicable.
182 // If the field is a group, the debug string might end with {.
183 printer->Print("/**\n");
184 WriteDocCommentBody(printer, field);
185 printer->Print(" * <code>$def$</code>\n", "def",
186 EscapeJavadoc(FirstLineOf(field->DebugString())));
187 printer->Print(" */\n");
188 }
189
WriteDeprecatedJavadoc(io::Printer * printer,const FieldDescriptor * field,const FieldAccessorType type)190 void WriteDeprecatedJavadoc(io::Printer* printer, const FieldDescriptor* field,
191 const FieldAccessorType type) {
192 if (!field->options().deprecated()) {
193 return;
194 }
195
196 // Lite codegen does not annotate set & clear methods with @Deprecated.
197 if (field->file()->options().optimize_for() == FileOptions::LITE_RUNTIME &&
198 (type == SETTER || type == CLEARER)) {
199 return;
200 }
201
202 std::string startLine = "0";
203 SourceLocation location;
204 if (field->GetSourceLocation(&location)) {
205 startLine = std::to_string(location.start_line);
206 }
207
208 printer->Print(" * @deprecated $name$ is deprecated.\n", "name",
209 field->full_name());
210 printer->Print(" * See $file$;l=$line$\n", "file", field->file()->name(),
211 "line", startLine);
212 }
213
WriteFieldAccessorDocComment(io::Printer * printer,const FieldDescriptor * field,const FieldAccessorType type,const bool builder)214 void WriteFieldAccessorDocComment(io::Printer* printer,
215 const FieldDescriptor* field,
216 const FieldAccessorType type,
217 const bool builder) {
218 printer->Print("/**\n");
219 WriteDocCommentBody(printer, field);
220 printer->Print(" * <code>$def$</code>\n", "def",
221 EscapeJavadoc(FirstLineOf(field->DebugString())));
222 WriteDeprecatedJavadoc(printer, field, type);
223 switch (type) {
224 case HAZZER:
225 printer->Print(" * @return Whether the $name$ field is set.\n", "name",
226 field->camelcase_name());
227 break;
228 case GETTER:
229 printer->Print(" * @return The $name$.\n", "name",
230 field->camelcase_name());
231 break;
232 case SETTER:
233 printer->Print(" * @param value The $name$ to set.\n", "name",
234 field->camelcase_name());
235 break;
236 case CLEARER:
237 // Print nothing
238 break;
239 // Repeated
240 case LIST_COUNT:
241 printer->Print(" * @return The count of $name$.\n", "name",
242 field->camelcase_name());
243 break;
244 case LIST_GETTER:
245 printer->Print(" * @return A list containing the $name$.\n", "name",
246 field->camelcase_name());
247 break;
248 case LIST_INDEXED_GETTER:
249 printer->Print(" * @param index The index of the element to return.\n");
250 printer->Print(" * @return The $name$ at the given index.\n", "name",
251 field->camelcase_name());
252 break;
253 case LIST_INDEXED_SETTER:
254 printer->Print(" * @param index The index to set the value at.\n");
255 printer->Print(" * @param value The $name$ to set.\n", "name",
256 field->camelcase_name());
257 break;
258 case LIST_ADDER:
259 printer->Print(" * @param value The $name$ to add.\n", "name",
260 field->camelcase_name());
261 break;
262 case LIST_MULTI_ADDER:
263 printer->Print(" * @param values The $name$ to add.\n", "name",
264 field->camelcase_name());
265 break;
266 }
267 if (builder) {
268 printer->Print(" * @return This builder for chaining.\n");
269 }
270 printer->Print(" */\n");
271 }
272
WriteFieldEnumValueAccessorDocComment(io::Printer * printer,const FieldDescriptor * field,const FieldAccessorType type,const bool builder)273 void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
274 const FieldDescriptor* field,
275 const FieldAccessorType type,
276 const bool builder) {
277 printer->Print("/**\n");
278 WriteDocCommentBody(printer, field);
279 printer->Print(" * <code>$def$</code>\n", "def",
280 EscapeJavadoc(FirstLineOf(field->DebugString())));
281 WriteDeprecatedJavadoc(printer, field, type);
282 switch (type) {
283 case HAZZER:
284 // Should never happen
285 break;
286 case GETTER:
287 printer->Print(
288 " * @return The enum numeric value on the wire for $name$.\n", "name",
289 field->camelcase_name());
290 break;
291 case SETTER:
292 printer->Print(
293 " * @param value The enum numeric value on the wire for $name$ to "
294 "set.\n",
295 "name", field->camelcase_name());
296 break;
297 case CLEARER:
298 // Print nothing
299 break;
300 // Repeated
301 case LIST_COUNT:
302 // Should never happen
303 break;
304 case LIST_GETTER:
305 printer->Print(
306 " * @return A list containing the enum numeric values on the wire "
307 "for $name$.\n",
308 "name", field->camelcase_name());
309 break;
310 case LIST_INDEXED_GETTER:
311 printer->Print(" * @param index The index of the value to return.\n");
312 printer->Print(
313 " * @return The enum numeric value on the wire of $name$ at the "
314 "given index.\n",
315 "name", field->camelcase_name());
316 break;
317 case LIST_INDEXED_SETTER:
318 printer->Print(" * @param index The index to set the value at.\n");
319 printer->Print(
320 " * @param value The enum numeric value on the wire for $name$ to "
321 "set.\n",
322 "name", field->camelcase_name());
323 break;
324 case LIST_ADDER:
325 printer->Print(
326 " * @param value The enum numeric value on the wire for $name$ to "
327 "add.\n",
328 "name", field->camelcase_name());
329 break;
330 case LIST_MULTI_ADDER:
331 printer->Print(
332 " * @param values The enum numeric values on the wire for $name$ to "
333 "add.\n",
334 "name", field->camelcase_name());
335 break;
336 }
337 if (builder) {
338 printer->Print(" * @return This builder for chaining.\n");
339 }
340 printer->Print(" */\n");
341 }
342
WriteFieldStringBytesAccessorDocComment(io::Printer * printer,const FieldDescriptor * field,const FieldAccessorType type,const bool builder)343 void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
344 const FieldDescriptor* field,
345 const FieldAccessorType type,
346 const bool builder) {
347 printer->Print("/**\n");
348 WriteDocCommentBody(printer, field);
349 printer->Print(" * <code>$def$</code>\n", "def",
350 EscapeJavadoc(FirstLineOf(field->DebugString())));
351 WriteDeprecatedJavadoc(printer, field, type);
352 switch (type) {
353 case HAZZER:
354 // Should never happen
355 break;
356 case GETTER:
357 printer->Print(" * @return The bytes for $name$.\n", "name",
358 field->camelcase_name());
359 break;
360 case SETTER:
361 printer->Print(" * @param value The bytes for $name$ to set.\n", "name",
362 field->camelcase_name());
363 break;
364 case CLEARER:
365 // Print nothing
366 break;
367 // Repeated
368 case LIST_COUNT:
369 // Should never happen
370 break;
371 case LIST_GETTER:
372 printer->Print(" * @return A list containing the bytes for $name$.\n",
373 "name", field->camelcase_name());
374 break;
375 case LIST_INDEXED_GETTER:
376 printer->Print(" * @param index The index of the value to return.\n");
377 printer->Print(" * @return The bytes of the $name$ at the given index.\n",
378 "name", field->camelcase_name());
379 break;
380 case LIST_INDEXED_SETTER:
381 printer->Print(" * @param index The index to set the value at.\n");
382 printer->Print(" * @param value The bytes of the $name$ to set.\n",
383 "name", field->camelcase_name());
384 break;
385 case LIST_ADDER:
386 printer->Print(" * @param value The bytes of the $name$ to add.\n",
387 "name", field->camelcase_name());
388 break;
389 case LIST_MULTI_ADDER:
390 printer->Print(" * @param values The bytes of the $name$ to add.\n",
391 "name", field->camelcase_name());
392 break;
393 }
394 if (builder) {
395 printer->Print(" * @return This builder for chaining.\n");
396 }
397 printer->Print(" */\n");
398 }
399
400 // Enum
401
WriteEnumDocComment(io::Printer * printer,const EnumDescriptor * enum_)402 void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) {
403 printer->Print("/**\n");
404 WriteDocCommentBody(printer, enum_);
405 printer->Print(
406 " * Protobuf enum {@code $fullname$}\n"
407 " */\n",
408 "fullname", EscapeJavadoc(enum_->full_name()));
409 }
410
WriteEnumValueDocComment(io::Printer * printer,const EnumValueDescriptor * value)411 void WriteEnumValueDocComment(io::Printer* printer,
412 const EnumValueDescriptor* value) {
413 printer->Print("/**\n");
414 WriteDocCommentBody(printer, value);
415 printer->Print(
416 " * <code>$def$</code>\n"
417 " */\n",
418 "def", EscapeJavadoc(FirstLineOf(value->DebugString())));
419 }
420
WriteServiceDocComment(io::Printer * printer,const ServiceDescriptor * service)421 void WriteServiceDocComment(io::Printer* printer,
422 const ServiceDescriptor* service) {
423 printer->Print("/**\n");
424 WriteDocCommentBody(printer, service);
425 printer->Print(
426 " * Protobuf service {@code $fullname$}\n"
427 " */\n",
428 "fullname", EscapeJavadoc(service->full_name()));
429 }
430
WriteMethodDocComment(io::Printer * printer,const MethodDescriptor * method)431 void WriteMethodDocComment(io::Printer* printer,
432 const MethodDescriptor* method) {
433 printer->Print("/**\n");
434 WriteDocCommentBody(printer, method);
435 printer->Print(
436 " * <code>$def$</code>\n"
437 " */\n",
438 "def", EscapeJavadoc(FirstLineOf(method->DebugString())));
439 }
440
441 } // namespace java
442 } // namespace compiler
443 } // namespace protobuf
444 } // namespace google
445