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 (at) 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 42 namespace google { 43 namespace protobuf { 44 namespace compiler { 45 namespace java { 46 47 string EscapeJavadoc(const string& input) { 48 string result; 49 result.reserve(input.size() * 2); 50 51 char prev = '*'; 52 53 for (string::size_type i = 0; i < input.size(); i++) { 54 char c = input[i]; 55 switch (c) { 56 case '*': 57 // Avoid "/*". 58 if (prev == '/') { 59 result.append("*"); 60 } else { 61 result.push_back(c); 62 } 63 break; 64 case '/': 65 // Avoid "*/". 66 if (prev == '*') { 67 result.append("/"); 68 } else { 69 result.push_back(c); 70 } 71 break; 72 case '@': 73 // '@' starts javadoc tags including the @deprecated tag, which will 74 // cause a compile-time error if inserted before a declaration that 75 // does not have a corresponding @Deprecated annotation. 76 result.append("@"); 77 break; 78 case '<': 79 // Avoid interpretation as HTML. 80 result.append("<"); 81 break; 82 case '>': 83 // Avoid interpretation as HTML. 84 result.append(">"); 85 break; 86 case '&': 87 // Avoid interpretation as HTML. 88 result.append("&"); 89 break; 90 case '\\': 91 // Java interprets Unicode escape sequences anywhere! 92 result.append("\"); 93 break; 94 default: 95 result.push_back(c); 96 break; 97 } 98 99 prev = c; 100 } 101 102 return result; 103 } 104 105 static void WriteDocCommentBodyForLocation( 106 io::Printer* printer, const SourceLocation& location) { 107 string comments = location.leading_comments.empty() ? 108 location.trailing_comments : location.leading_comments; 109 if (!comments.empty()) { 110 // TODO(kenton): Ideally we should parse the comment text as Markdown and 111 // write it back as HTML, but this requires a Markdown parser. For now 112 // we just use <pre> to get fixed-width text formatting. 113 114 // If the comment itself contains block comment start or end markers, 115 // HTML-escape them so that they don't accidentally close the doc comment. 116 comments = EscapeJavadoc(comments); 117 118 vector<string> lines = Split(comments, "\n"); 119 while (!lines.empty() && lines.back().empty()) { 120 lines.pop_back(); 121 } 122 123 printer->Print(" * <pre>\n"); 124 for (int i = 0; i < lines.size(); i++) { 125 // Most lines should start with a space. Watch out for lines that start 126 // with a /, since putting that right after the leading asterisk will 127 // close the comment. 128 if (!lines[i].empty() && lines[i][0] == '/') { 129 printer->Print(" * $line$\n", "line", lines[i]); 130 } else { 131 printer->Print(" *$line$\n", "line", lines[i]); 132 } 133 } 134 printer->Print( 135 " * </pre>\n" 136 " *\n"); 137 } 138 } 139 140 template <typename DescriptorType> 141 static void WriteDocCommentBody( 142 io::Printer* printer, const DescriptorType* descriptor) { 143 SourceLocation location; 144 if (descriptor->GetSourceLocation(&location)) { 145 WriteDocCommentBodyForLocation(printer, location); 146 } 147 } 148 149 static string FirstLineOf(const string& value) { 150 string result = value; 151 152 string::size_type pos = result.find_first_of('\n'); 153 if (pos != string::npos) { 154 result.erase(pos); 155 } 156 157 // If line ends in an opening brace, make it "{ ... }" so it looks nice. 158 if (!result.empty() && result[result.size() - 1] == '{') { 159 result.append(" ... }"); 160 } 161 162 return result; 163 } 164 165 void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) { 166 printer->Print("/**\n"); 167 WriteDocCommentBody(printer, message); 168 printer->Print( 169 " * Protobuf type {@code $fullname$}\n" 170 " */\n", 171 "fullname", EscapeJavadoc(message->full_name())); 172 } 173 174 void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) { 175 // In theory we should have slightly different comments for setters, getters, 176 // etc., but in practice everyone already knows the difference between these 177 // so it's redundant information. 178 179 // We start the comment with the main body based on the comments from the 180 // .proto file (if present). We then end with the field declaration, e.g.: 181 // optional string foo = 5; 182 // If the field is a group, the debug string might end with {. 183 printer->Print("/**\n"); 184 WriteDocCommentBody(printer, field); 185 printer->Print( 186 " * <code>$def$</code>\n", 187 "def", EscapeJavadoc(FirstLineOf(field->DebugString()))); 188 printer->Print(" */\n"); 189 } 190 191 void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) { 192 printer->Print("/**\n"); 193 WriteDocCommentBody(printer, enum_); 194 printer->Print( 195 " * Protobuf enum {@code $fullname$}\n" 196 " */\n", 197 "fullname", EscapeJavadoc(enum_->full_name())); 198 } 199 200 void WriteEnumValueDocComment(io::Printer* printer, 201 const EnumValueDescriptor* value) { 202 printer->Print("/**\n"); 203 WriteDocCommentBody(printer, value); 204 printer->Print( 205 " * <code>$def$</code>\n" 206 " */\n", 207 "def", EscapeJavadoc(FirstLineOf(value->DebugString()))); 208 } 209 210 void WriteServiceDocComment(io::Printer* printer, 211 const ServiceDescriptor* service) { 212 printer->Print("/**\n"); 213 WriteDocCommentBody(printer, service); 214 printer->Print( 215 " * Protobuf service {@code $fullname$}\n" 216 " */\n", 217 "fullname", EscapeJavadoc(service->full_name())); 218 } 219 220 void WriteMethodDocComment(io::Printer* printer, 221 const MethodDescriptor* method) { 222 printer->Print("/**\n"); 223 WriteDocCommentBody(printer, method); 224 printer->Print( 225 " * <code>$def$</code>\n" 226 " */\n", 227 "def", EscapeJavadoc(FirstLineOf(method->DebugString()))); 228 } 229 230 } // namespace java 231 } // namespace compiler 232 } // namespace protobuf 233 } // namespace google 234