| 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/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 |  | 
| 48 | std::string EscapeJavadoc(const std::string& input) { | 
| 49 |   std::string result; | 
| 50 |   result.reserve(res_arg: 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(s: "*" ); | 
| 61 |         } else { | 
| 62 |           result.push_back(c: c); | 
| 63 |         } | 
| 64 |         break; | 
| 65 |       case '/': | 
| 66 |         // Avoid "*/". | 
| 67 |         if (prev == '*') { | 
| 68 |           result.append(s: "/" ); | 
| 69 |         } else { | 
| 70 |           result.push_back(c: 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(s: "@" ); | 
| 78 |         break; | 
| 79 |       case '<': | 
| 80 |         // Avoid interpretation as HTML. | 
| 81 |         result.append(s: "<" ); | 
| 82 |         break; | 
| 83 |       case '>': | 
| 84 |         // Avoid interpretation as HTML. | 
| 85 |         result.append(s: ">" ); | 
| 86 |         break; | 
| 87 |       case '&': | 
| 88 |         // Avoid interpretation as HTML. | 
| 89 |         result.append(s: "&" ); | 
| 90 |         break; | 
| 91 |       case '\\': | 
| 92 |         // Java interprets Unicode escape sequences anywhere! | 
| 93 |         result.append(s: "\" ); | 
| 94 |         break; | 
| 95 |       default: | 
| 96 |         result.push_back(c: c); | 
| 97 |         break; | 
| 98 |     } | 
| 99 |  | 
| 100 |     prev = c; | 
| 101 |   } | 
| 102 |  | 
| 103 |   return result; | 
| 104 | } | 
| 105 |  | 
| 106 | static void WriteDocCommentBodyForLocation(io::Printer* printer, | 
| 107 |                                            const SourceLocation& location) { | 
| 108 |   std::string  = 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(input: comments); | 
| 119 |  | 
| 120 |     std::vector<std::string> lines = Split(full: comments, delim: "\n" ); | 
| 121 |     while (!lines.empty() && lines.back().empty()) { | 
| 122 |       lines.pop_back(); | 
| 123 |     } | 
| 124 |  | 
| 125 |     printer->Print(text: " * <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(text: " * $line$\n" , args: "line" , args: lines[i]); | 
| 132 |       } else { | 
| 133 |         printer->Print(text: " *$line$\n" , args: "line" , args: lines[i]); | 
| 134 |       } | 
| 135 |     } | 
| 136 |     printer->Print( | 
| 137 |         text: " * </pre>\n"  | 
| 138 |         " *\n" ); | 
| 139 |   } | 
| 140 | } | 
| 141 |  | 
| 142 | template <typename DescriptorType> | 
| 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 |  | 
| 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(c: '\n'); | 
| 155 |   if (pos != std::string::npos) { | 
| 156 |     result.erase(pos: 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(s: " ... }" ); | 
| 162 |   } | 
| 163 |  | 
| 164 |   return result; | 
| 165 | } | 
| 166 |  | 
| 167 | void (io::Printer* printer, const Descriptor* message) { | 
| 168 |   printer->Print(text: "/**\n" ); | 
| 169 |   WriteDocCommentBody(printer, descriptor: message); | 
| 170 |   printer->Print( | 
| 171 |       text: " * Protobuf type {@code $fullname$}\n"  | 
| 172 |       " */\n" , | 
| 173 |       args: "fullname" , args: EscapeJavadoc(input: message->full_name())); | 
| 174 | } | 
| 175 |  | 
| 176 | void (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(text: "/**\n" ); | 
| 184 |   WriteDocCommentBody(printer, descriptor: field); | 
| 185 |   printer->Print(text: " * <code>$def$</code>\n" , args: "def" , | 
| 186 |                  args: EscapeJavadoc(input: FirstLineOf(value: field->DebugString()))); | 
| 187 |   printer->Print(text: " */\n" ); | 
| 188 | } | 
| 189 |  | 
| 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(out_location: &location)) { | 
| 205 |     startLine = std::to_string(val: location.start_line); | 
| 206 |   } | 
| 207 |  | 
| 208 |   printer->Print(text: " * @deprecated $name$ is deprecated.\n" , args: "name" , | 
| 209 |                  args: field->full_name()); | 
| 210 |   printer->Print(text: " *     See $file$;l=$line$\n" , args: "file" , args: field->file()->name(), | 
| 211 |                  args: "line" , args: startLine); | 
| 212 | } | 
| 213 |  | 
| 214 | void (io::Printer* printer, | 
| 215 |                                   const FieldDescriptor* field, | 
| 216 |                                   const FieldAccessorType type, | 
| 217 |                                   const bool builder) { | 
| 218 |   printer->Print(text: "/**\n" ); | 
| 219 |   WriteDocCommentBody(printer, descriptor: field); | 
| 220 |   printer->Print(text: " * <code>$def$</code>\n" , args: "def" , | 
| 221 |                  args: EscapeJavadoc(input: FirstLineOf(value: field->DebugString()))); | 
| 222 |   WriteDeprecatedJavadoc(printer, field, type); | 
| 223 |   switch (type) { | 
| 224 |     case HAZZER: | 
| 225 |       printer->Print(text: " * @return Whether the $name$ field is set.\n" , args: "name" , | 
| 226 |                      args: field->camelcase_name()); | 
| 227 |       break; | 
| 228 |     case GETTER: | 
| 229 |       printer->Print(text: " * @return The $name$.\n" , args: "name" , | 
| 230 |                      args: field->camelcase_name()); | 
| 231 |       break; | 
| 232 |     case SETTER: | 
| 233 |       printer->Print(text: " * @param value The $name$ to set.\n" , args: "name" , | 
| 234 |                      args: field->camelcase_name()); | 
| 235 |       break; | 
| 236 |     case CLEARER: | 
| 237 |       // Print nothing | 
| 238 |       break; | 
| 239 |     // Repeated | 
| 240 |     case LIST_COUNT: | 
| 241 |       printer->Print(text: " * @return The count of $name$.\n" , args: "name" , | 
| 242 |                      args: field->camelcase_name()); | 
| 243 |       break; | 
| 244 |     case LIST_GETTER: | 
| 245 |       printer->Print(text: " * @return A list containing the $name$.\n" , args: "name" , | 
| 246 |                      args: field->camelcase_name()); | 
| 247 |       break; | 
| 248 |     case LIST_INDEXED_GETTER: | 
| 249 |       printer->Print(text: " * @param index The index of the element to return.\n" ); | 
| 250 |       printer->Print(text: " * @return The $name$ at the given index.\n" , args: "name" , | 
| 251 |                      args: field->camelcase_name()); | 
| 252 |       break; | 
| 253 |     case LIST_INDEXED_SETTER: | 
| 254 |       printer->Print(text: " * @param index The index to set the value at.\n" ); | 
| 255 |       printer->Print(text: " * @param value The $name$ to set.\n" , args: "name" , | 
| 256 |                      args: field->camelcase_name()); | 
| 257 |       break; | 
| 258 |     case LIST_ADDER: | 
| 259 |       printer->Print(text: " * @param value The $name$ to add.\n" , args: "name" , | 
| 260 |                      args: field->camelcase_name()); | 
| 261 |       break; | 
| 262 |     case LIST_MULTI_ADDER: | 
| 263 |       printer->Print(text: " * @param values The $name$ to add.\n" , args: "name" , | 
| 264 |                      args: field->camelcase_name()); | 
| 265 |       break; | 
| 266 |   } | 
| 267 |   if (builder) { | 
| 268 |     printer->Print(text: " * @return This builder for chaining.\n" ); | 
| 269 |   } | 
| 270 |   printer->Print(text: " */\n" ); | 
| 271 | } | 
| 272 |  | 
| 273 | void (io::Printer* printer, | 
| 274 |                                            const FieldDescriptor* field, | 
| 275 |                                            const FieldAccessorType type, | 
| 276 |                                            const bool builder) { | 
| 277 |   printer->Print(text: "/**\n" ); | 
| 278 |   WriteDocCommentBody(printer, descriptor: field); | 
| 279 |   printer->Print(text: " * <code>$def$</code>\n" , args: "def" , | 
| 280 |                  args: EscapeJavadoc(input: FirstLineOf(value: 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 |           text: " * @return The enum numeric value on the wire for $name$.\n" , args: "name" , | 
| 289 |           args: field->camelcase_name()); | 
| 290 |       break; | 
| 291 |     case SETTER: | 
| 292 |       printer->Print( | 
| 293 |           text: " * @param value The enum numeric value on the wire for $name$ to "  | 
| 294 |           "set.\n" , | 
| 295 |           args: "name" , args: 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 |           text: " * @return A list containing the enum numeric values on the wire "  | 
| 307 |           "for $name$.\n" , | 
| 308 |           args: "name" , args: field->camelcase_name()); | 
| 309 |       break; | 
| 310 |     case LIST_INDEXED_GETTER: | 
| 311 |       printer->Print(text: " * @param index The index of the value to return.\n" ); | 
| 312 |       printer->Print( | 
| 313 |           text: " * @return The enum numeric value on the wire of $name$ at the "  | 
| 314 |           "given index.\n" , | 
| 315 |           args: "name" , args: field->camelcase_name()); | 
| 316 |       break; | 
| 317 |     case LIST_INDEXED_SETTER: | 
| 318 |       printer->Print(text: " * @param index The index to set the value at.\n" ); | 
| 319 |       printer->Print( | 
| 320 |           text: " * @param value The enum numeric value on the wire for $name$ to "  | 
| 321 |           "set.\n" , | 
| 322 |           args: "name" , args: field->camelcase_name()); | 
| 323 |       break; | 
| 324 |     case LIST_ADDER: | 
| 325 |       printer->Print( | 
| 326 |           text: " * @param value The enum numeric value on the wire for $name$ to "  | 
| 327 |           "add.\n" , | 
| 328 |           args: "name" , args: field->camelcase_name()); | 
| 329 |       break; | 
| 330 |     case LIST_MULTI_ADDER: | 
| 331 |       printer->Print( | 
| 332 |           text: " * @param values The enum numeric values on the wire for $name$ to "  | 
| 333 |           "add.\n" , | 
| 334 |           args: "name" , args: field->camelcase_name()); | 
| 335 |       break; | 
| 336 |   } | 
| 337 |   if (builder) { | 
| 338 |     printer->Print(text: " * @return This builder for chaining.\n" ); | 
| 339 |   } | 
| 340 |   printer->Print(text: " */\n" ); | 
| 341 | } | 
| 342 |  | 
| 343 | void (io::Printer* printer, | 
| 344 |                                              const FieldDescriptor* field, | 
| 345 |                                              const FieldAccessorType type, | 
| 346 |                                              const bool builder) { | 
| 347 |   printer->Print(text: "/**\n" ); | 
| 348 |   WriteDocCommentBody(printer, descriptor: field); | 
| 349 |   printer->Print(text: " * <code>$def$</code>\n" , args: "def" , | 
| 350 |                  args: EscapeJavadoc(input: FirstLineOf(value: 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(text: " * @return The bytes for $name$.\n" , args: "name" , | 
| 358 |                      args: field->camelcase_name()); | 
| 359 |       break; | 
| 360 |     case SETTER: | 
| 361 |       printer->Print(text: " * @param value The bytes for $name$ to set.\n" , args: "name" , | 
| 362 |                      args: 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(text: " * @return A list containing the bytes for $name$.\n" , | 
| 373 |                      args: "name" , args: field->camelcase_name()); | 
| 374 |       break; | 
| 375 |     case LIST_INDEXED_GETTER: | 
| 376 |       printer->Print(text: " * @param index The index of the value to return.\n" ); | 
| 377 |       printer->Print(text: " * @return The bytes of the $name$ at the given index.\n" , | 
| 378 |                      args: "name" , args: field->camelcase_name()); | 
| 379 |       break; | 
| 380 |     case LIST_INDEXED_SETTER: | 
| 381 |       printer->Print(text: " * @param index The index to set the value at.\n" ); | 
| 382 |       printer->Print(text: " * @param value The bytes of the $name$ to set.\n" , | 
| 383 |                      args: "name" , args: field->camelcase_name()); | 
| 384 |       break; | 
| 385 |     case LIST_ADDER: | 
| 386 |       printer->Print(text: " * @param value The bytes of the $name$ to add.\n" , | 
| 387 |                      args: "name" , args: field->camelcase_name()); | 
| 388 |       break; | 
| 389 |     case LIST_MULTI_ADDER: | 
| 390 |       printer->Print(text: " * @param values The bytes of the $name$ to add.\n" , | 
| 391 |                      args: "name" , args: field->camelcase_name()); | 
| 392 |       break; | 
| 393 |   } | 
| 394 |   if (builder) { | 
| 395 |     printer->Print(text: " * @return This builder for chaining.\n" ); | 
| 396 |   } | 
| 397 |   printer->Print(text: " */\n" ); | 
| 398 | } | 
| 399 |  | 
| 400 | // Enum | 
| 401 |  | 
| 402 | void (io::Printer* printer, const EnumDescriptor* enum_) { | 
| 403 |   printer->Print(text: "/**\n" ); | 
| 404 |   WriteDocCommentBody(printer, descriptor: enum_); | 
| 405 |   printer->Print( | 
| 406 |       text: " * Protobuf enum {@code $fullname$}\n"  | 
| 407 |       " */\n" , | 
| 408 |       args: "fullname" , args: EscapeJavadoc(input: enum_->full_name())); | 
| 409 | } | 
| 410 |  | 
| 411 | void (io::Printer* printer, | 
| 412 |                               const EnumValueDescriptor* value) { | 
| 413 |   printer->Print(text: "/**\n" ); | 
| 414 |   WriteDocCommentBody(printer, descriptor: value); | 
| 415 |   printer->Print( | 
| 416 |       text: " * <code>$def$</code>\n"  | 
| 417 |       " */\n" , | 
| 418 |       args: "def" , args: EscapeJavadoc(input: FirstLineOf(value: value->DebugString()))); | 
| 419 | } | 
| 420 |  | 
| 421 | void (io::Printer* printer, | 
| 422 |                             const ServiceDescriptor* service) { | 
| 423 |   printer->Print(text: "/**\n" ); | 
| 424 |   WriteDocCommentBody(printer, descriptor: service); | 
| 425 |   printer->Print( | 
| 426 |       text: " * Protobuf service {@code $fullname$}\n"  | 
| 427 |       " */\n" , | 
| 428 |       args: "fullname" , args: EscapeJavadoc(input: service->full_name())); | 
| 429 | } | 
| 430 |  | 
| 431 | void (io::Printer* printer, | 
| 432 |                            const MethodDescriptor* method) { | 
| 433 |   printer->Print(text: "/**\n" ); | 
| 434 |   WriteDocCommentBody(printer, descriptor: method); | 
| 435 |   printer->Print( | 
| 436 |       text: " * <code>$def$</code>\n"  | 
| 437 |       " */\n" , | 
| 438 |       args: "def" , args: EscapeJavadoc(input: FirstLineOf(value: method->DebugString()))); | 
| 439 | } | 
| 440 |  | 
| 441 | }  // namespace java | 
| 442 | }  // namespace compiler | 
| 443 | }  // namespace protobuf | 
| 444 | }  // namespace google | 
| 445 |  |