• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("&#42;");
61         } else {
62           result.push_back(c);
63         }
64         break;
65       case '/':
66         // Avoid "*/".
67         if (prev == '*') {
68           result.append("&#47;");
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("&#64;");
78         break;
79       case '<':
80         // Avoid interpretation as HTML.
81         result.append("&lt;");
82         break;
83       case '>':
84         // Avoid interpretation as HTML.
85         result.append("&gt;");
86         break;
87       case '&':
88         // Avoid interpretation as HTML.
89         result.append("&amp;");
90         break;
91       case '\\':
92         // Java interprets Unicode escape sequences anywhere!
93         result.append("&#92;");
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