DocCommentGenerator.java revision 3462:203a9e1b82b6
1/* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24package sampleapi.generator; 25 26import java.io.File; 27import java.io.BufferedInputStream; 28import java.io.IOException; 29import java.util.Set; 30import javax.lang.model.element.Modifier; 31 32import com.sun.tools.javac.tree.JCTree; 33import com.sun.tools.javac.tree.JCTree.*; 34import com.sun.tools.javac.util.List; 35import java.util.HashMap; 36import java.util.Map; 37 38class DocCommentGenerator { 39 40 public enum Text { 41 BROWNFOX(BROWNFOX_TEXT), 42 NOWISTHETIME(NOWISTHETIME_TEXT), 43 THISPANGRAM(THISPANGRAM_TEXT), 44 LOREMIPSUM(LOREMIPSUM_TEXT), 45 LIEUROPANLINGUES(LIEUROPANLINGUES_TEXT), 46 CODE(CODE_TEXT); 47 48 String commentText; 49 50 Text(String text) { 51 commentText = text; 52 } 53 54 @Override 55 public String toString() { 56 return commentText; 57 } 58 } 59 60 public enum Tag { 61 AUTHOR("@author", "Cody J. Writer"), 62 PARAM("@param", ""), 63 RETURN("@return", Text.NOWISTHETIME.toString()), 64 SINCE("@since", "1.0"), 65 THROWS("@throws", "If problem detected"), 66 EXCEPTION("@exception", "If problem detected"), 67 SERIAL("@serial", ""), 68 SERIALDATA("@serialData", "The types and order of data could be here."), 69 SERIALFIELD("@serialField", "\n Serial field in special array"), 70 FX_PROPSETTER("@propertySetter", "Set the property"), 71 FX_PROPGETTER("@propertyGetter", "Get the property"), 72 FX_PROPDESC("@propertyDescription", ""), 73 FX_DEFVALUE("@defaultValue", ""), 74 FX_TREATASPRIVATE("@treatAsPrivate", ""); 75 76 String tagName; 77 String tagValue; 78 79 Tag(String tagName, String tagValue) { 80 this.tagName = tagName; 81 this.tagValue = tagValue; 82 } 83 84 public String toString() { 85 return value("", ""); 86 } 87 88 public String value(String value) { 89 return value(value, ""); 90 } 91 92 public String value(String value, String extra) { 93 return tagName 94 + ((value.length() != 0) ? " " + value : "") 95 + ((tagValue.length() != 0) ? " " + tagValue : "") 96 + ((extra.length() != 0) ? " " + extra : ""); 97 } 98 } 99 100 public enum InlineTag { 101 LITERAL("@literal", "Use < and > brackets instead of < and > escapes."), 102 CODE("@code", "(i) -> new Abc<Object>((i > 0) ? (i << 1) : 0)"), 103 LINK("@link", ""), 104 VALUE("@value", ""), 105 INDEX("@index", "", true); 106 107 String tagName; 108 String tagValue; 109 boolean counted; 110 Map<String, Integer> counters; 111 112 InlineTag(String tagName, String tagValue) { 113 this(tagName, tagValue, false); 114 } 115 116 InlineTag(String tagName, String tagValue, boolean counted) { 117 this.tagName = tagName; 118 this.tagValue = tagValue; 119 this.counted = counted; 120 if (counted) { 121 counters = new HashMap<>(); 122 } 123 } 124 125 public String toString() { 126 return value(""); 127 } 128 129 public String value(String value) { 130 String name = ((tagValue.length() != 0) ? " " + tagValue : "") 131 + ((value.length() != 0) ? " " + value : ""); 132 if (counted && !counters.containsKey(name)) { 133 counters.put(name, 0); 134 } 135 return "{" + tagName 136 + name 137 + (counted ? "_" + counters.put(name, counters.get(name) + 1) : "") 138 + "}"; 139 } 140 } 141 142 public static class LinkTag { 143 144 static String[] links = new String[] { 145 "Boolean", 146 "Boolean#TRUE", 147 "Boolean#Boolean(boolean)", 148 "Boolean#Boolean(String s)", 149 "Boolean#compare(boolean, boolean)", 150 "Boolean#compareTo(Boolean b)", 151 "java.util.Vector", 152 "java.util.Vector#elementCount", 153 "java.util.Vector#Vector(int)", 154 "java.util.Vector#Vector(int initialCapacity, int capacityIncrement)", 155 "java.util.Vector#indexOf(Object)", 156 "java.util.Vector#indexOf(Object o, int index)", 157 "java.lang.annotation" }; 158 159 static int index = 0; 160 161 public static String nextSee() { 162 String next = "@see " + links[index]; 163 index = (index + 1) % links.length; 164 return next; 165 } 166 167 public static String nextLink() { 168 String next = "Also check " 169 + (((index % 2) == 0) ? "{@link " : "{@linkplain ") 170 + links[index] 171 + "} for details.\n"; 172 index = (index + 1) % links.length; 173 return next; 174 } 175 } 176 177 public static class VersionTag { 178 179 static String[] versions = new String[] { 180 "1.5, 09/01/04", 181 "1.6, 12/11/06", 182 "1.7, 07/28/11", 183 "1.8, 04/19/14", 184 "9, 06/03/16" }; 185 186 static int index = 0; 187 188 public static String nextVersion() { 189 String next = "@version " + versions[index]; 190 index = (index + 1) % versions.length; 191 return next; 192 } 193 } 194 195 // 196 // getters (build comments for entities) 197 // 198 199 public String getPackageComment() { 200 return InlineTag.INDEX.value("PackageCommentLabel") + " " 201 + Text.LOREMIPSUM 202 + "\n<p>" + Text.LIEUROPANLINGUES 203 + "\n" + Text.CODE 204 + "\n" + LinkTag.nextLink() 205 + "\n" + LinkTag.nextSee() 206 + "\n" + Tag.SINCE + "\n"; 207 } 208 209 String[] serialVals = new String[] { "include", "exclude" }; 210 // static index to roll over "include/exclude" 211 static int serialValIdx = 0; 212 213 public String getBaseComment(JCClassDecl baseDecl, boolean toplevel) { 214 String buildComment = InlineTag.INDEX.value("BaseCommentLabel") + " "; 215 216 buildComment += Text.LIEUROPANLINGUES + "\n"; 217 218 buildComment += "<p>It is possible to see inlined code:\n" 219 + InlineTag.CODE 220 + " is the example.\n\n"; 221 222 buildComment += "<p>Literal use example.\n" 223 + InlineTag.LITERAL + "\n\n"; 224 225 buildComment += "<p>" + Text.THISPANGRAM + "\n"; // make comment longer 226 227 buildComment += "<p>" + LinkTag.nextLink() + "\n"; 228 229 if (toplevel) 230 buildComment += "\n" + VersionTag.nextVersion() + "\n"; 231 232 // @param for type params 233 List<JCTypeParameter> params = baseDecl.getTypeParameters(); 234 int paramIndex = 0; 235 for (JCTypeParameter paramDecl : params) { 236 buildComment += Tag.PARAM.value( 237 "<" + paramDecl.getName().toString() + ">", 238 "the type of value set #" + paramIndex++) 239 + "\n"; 240 } 241 242 buildComment += "\n" + LinkTag.nextSee(); 243 244 buildComment += "\n"; 245 246 if (toplevel) 247 buildComment += Tag.AUTHOR + "\n"; 248 249 buildComment += Tag.SINCE + "\n"; 250 251 // for serial; currently no need to dig too deep 252 if (isSerializable(baseDecl) || isErrorOrException(baseDecl)) { 253 buildComment += "\n" + Tag.SERIAL.value(serialVals[serialValIdx]); 254 serialValIdx = (serialValIdx + 1) % 2; 255 } 256 257 return buildComment; 258 } 259 260 public String getConstComment() { 261 String buildComment = InlineTag.INDEX.value("ConstCommentLabel") + " "; 262 263 buildComment += Text.NOWISTHETIME + " " + Text.BROWNFOX + "\n"; 264 buildComment += LinkTag.nextLink() + "\n"; 265 buildComment += LinkTag.nextSee() + "\n"; 266 buildComment += Tag.SINCE + "\n"; 267 268 return buildComment; 269 } 270 271 public String getFieldComment(JCClassDecl baseDecl, 272 JCVariableDecl varDecl, 273 boolean isFxStyle) { 274 String buildComment = InlineTag.INDEX.value("FieldCommentLabel") + " "; 275 276 buildComment += Text.BROWNFOX + "<p>" + Text.NOWISTHETIME + "\n"; 277 Set<Modifier> mods = varDecl.getModifiers().getFlags(); 278 String varName = varDecl.getName().toString(); 279 280 if (mods.contains(Modifier.STATIC) && mods.contains(Modifier.FINAL)) { 281 JCExpression init = varDecl.getInitializer(); 282 if (init != null 283 && !"null".equals(init.toString()) 284 && !"serialPersistentFields".equals(varName)) 285 buildComment += "<p>The value of this constant is " 286 + InlineTag.VALUE 287 + ".\n"; 288 } 289 290 buildComment += "<p>" + LinkTag.nextLink() + "\n"; 291 292 if (isSerializable(baseDecl)) { 293 if (isErrorOrException(baseDecl)) { 294 buildComment += Tag.SERIAL + "\n"; 295 } else if ("serialPersistentFields".equals(varName)) { 296 JCNewArray sfList = (JCNewArray)varDecl.getInitializer(); 297 for (JCExpression sf : sfList.getInitializers()) { 298 List<JCExpression> args = ((JCNewClass)sf).getArguments(); 299 String sfName = ((JCLiteral)args.get(0)).getValue().toString(); 300 String sfClass = ((JCIdent)args.get(1)).getName().toString(); 301 String sfType = sfClass.substring(0, sfClass.indexOf(".class")); 302 303 buildComment += Tag.SERIALFIELD.value(sfName + " " + sfType) 304 + "\n"; 305 } 306 } else { 307 buildComment += Tag.SERIAL.value("Very important value.") + "\n"; 308 } 309 } 310 311 if (isFxStyle) { 312 // set default value 313 String varType = varDecl.getType().toString(); 314 buildComment += Tag.FX_DEFVALUE.value(defValue(varType)) + "\n"; 315 } 316 317 buildComment += LinkTag.nextSee() + "\n"; 318 319 return buildComment; 320 } 321 322 public String getMethodComment(JCClassDecl baseDecl, 323 JCMethodDecl methodDecl, 324 boolean isFxStyle) { 325 String buildComment = InlineTag.INDEX.value("MethodCommentLabel") + " "; 326 327 buildComment += Text.BROWNFOX + "\n<p>" + Text.THISPANGRAM + "\n"; 328 329 buildComment += "<p>" + LinkTag.nextLink() + "\n"; 330 331 buildComment += "<p>Literal use example.\n" 332 + InlineTag.LITERAL + "\n\n"; 333 334 // @param for type params 335 List<JCTypeParameter> tparams = methodDecl.getTypeParameters(); 336 int tparamIndex = 0; 337 for (JCTypeParameter paramDecl : tparams) { 338 String paramDeclString = paramDecl.getName().toString(); 339 // simplify it (could contain 'extend'/'super' clauses 340 int spacePos = paramDeclString.indexOf(' '); 341 if (spacePos != -1) 342 paramDeclString = paramDeclString.substring(0, spacePos); 343 buildComment += Tag.PARAM.value( 344 "<" + paramDeclString + ">", 345 "the type of value set #" + tparamIndex++) 346 + "\n"; 347 } 348 349 // @param 350 List<JCVariableDecl> params = methodDecl.getParameters(); 351 int paramIndex = 0; 352 for (JCVariableDecl paramDecl : params) { 353 buildComment += Tag.PARAM.value( 354 paramDecl.getName().toString(), 355 "an income parameter #" + paramIndex++) 356 + "\n"; 357 } 358 359 // @return 360 JCTree retType = methodDecl.getReturnType(); // null for constructors 361 if (retType != null && !"void".equals(retType.toString())) 362 buildComment += Tag.RETURN + "\n"; 363 364 // @throws/@exception 365 Tag t = isDerived(baseDecl) ? Tag.EXCEPTION : Tag.THROWS; 366 List<JCExpression> throwTypes = methodDecl.getThrows(); 367 for (JCExpression throwsExp : throwTypes) { 368 buildComment += t.value(throwsExp.toString()) + "\n"; 369 } 370 371 if (isSerializable(baseDecl)) { 372 switch (methodDecl.getName().toString()) { 373 case "writeObject": 374 case "readObject": 375 case "writeExternal": 376 case "readExternal": 377 case "writeReplace": 378 case "readResolve": 379 buildComment += Tag.SERIALDATA + "\n"; 380 break; 381 default: 382 } 383 } 384 385 if (isFxStyle) { 386 // @propertySetter/Getter + Description 387 if ("void".equals(retType.toString())) { 388 buildComment += Tag.FX_PROPSETTER + "\n"; 389 } else { 390 buildComment += Tag.FX_PROPGETTER + "\n"; 391 buildComment += Tag.FX_DEFVALUE.value(defValue(retType.toString())) 392 + "\n"; 393 } 394 buildComment += Tag.FX_PROPDESC.value(Text.BROWNFOX.toString()) + "\n"; 395 396 // @treatAsPrivate 397 if (methodDecl.getModifiers().getFlags().contains(Modifier.PUBLIC)) 398 buildComment += Tag.FX_TREATASPRIVATE + "\n"; 399 } 400 401 // @see 402 buildComment += LinkTag.nextSee() + "\n"; 403 404 // @since 405 buildComment += Tag.SINCE + "\n"; 406 407 return buildComment; 408 } 409 410 // 411 // util methods 412 // 413 414 private boolean isErrorOrException(JCClassDecl baseDecl) { 415 JCExpression ext = baseDecl.getExtendsClause(); 416 if (ext != null) { 417 String extClassName = ext.toString(); 418 if (extClassName.contains("Error") || extClassName.contains("Exception")) 419 return true; 420 } 421 return false; 422 } 423 424 private boolean isSerializable(JCClassDecl baseDecl) { 425 List<JCExpression> impls = baseDecl.getImplementsClause(); 426 for (JCExpression impl : impls) { 427 if (impl.toString().contains("Serializable")) 428 return true; 429 } 430 return false; 431 } 432 433 private boolean isDerived(JCClassDecl baseDecl) { 434 return (baseDecl.getExtendsClause() == null) ? false : true; 435 } 436 437 private String defValue(String type) { 438 switch (type) { 439 case "boolean": 440 return "true"; 441 case "byte": case "char": case "int": case "long": 442 case "Integer": case "Long": 443 return "1"; 444 case "float": case "double": case "Float": case "Double": 445 return "1.0"; 446 case "String": 447 return "string"; 448 default: 449 return "null"; 450 } 451 } 452 453 private static final String BROWNFOX_TEXT = 454 "The quick brown fox jumps over the lazy dog.\n"; 455 private static final String NOWISTHETIME_TEXT = 456 "Now is the time for all good men to come to the aid of the party.\n"; 457 private static final String THISPANGRAM_TEXT = 458 "This pangram contains four a's, one b, two c's, one d, thirty e's,\n" + 459 "six f's, five g's, seven h's, eleven i's, one j, one k, two l's,\n" + 460 "two m's, eighteen n's, fifteen o's, two p's, one q, five r's,\n" + 461 "twenty-seven s's, eighteen t's, two u's, seven v's, eight w's,\n" + 462 "two x's, three y's, & one z."; 463 private static final String LOREMIPSUM_TEXT = 464 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n" + 465 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut\n" + 466 "enim ad minim veniam, quis nostrud exercitation ullamco laboris\n" + 467 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor\n" + 468 "in reprehenderit in voluptate velit esse cillum dolore eu fugiat\n" + 469 "nulla pariatur. Excepteur sint occaecat cupidatat non proident,\n" + 470 "sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; 471 private static final String LIEUROPANLINGUES_TEXT = 472 "Li Europan lingues es membres del sam familie. Lor separat existentie\n" + 473 "es un myth. Por scientie, musica, sport etc, litot Europa usa li sam\n" + 474 "vocabular. Li lingues differe solmen in li grammatica, li pronunciation\n" + 475 "e li plu commun vocabules. Omnicos directe al desirabilite de un nov\n" + 476 "lingua franca: On refusa continuar payar custosi traductores.\n" + 477 "\n" + 478 "<p>At solmen va esser necessi far uniform grammatica, pronunciation\n" + 479 "e plu commun paroles. Ma quande lingues coalesce, li grammatica del\n" + 480 "resultant lingue es plu simplic e regulari quam ti del coalescent\n" + 481 "lingues. Li nov lingua franca va esser plu simplic e regulari quam\n" + 482 "li existent Europan lingues. It va esser tam simplic quam Occidental\n" + 483 "in fact, it va esser Occidental. A un Angleso it va semblar un simplificat\n" + 484 "Angles, quam un skeptic Cambridge amico dit me que Occidental es.\n"; 485 private static final String CODE_TEXT = 486 "<pre>\n" + 487 "public void checkLanguage(Language lang) throws Exception {\n" + 488 " if (lang.getName().equals(\"Java\")) {\n" + 489 " System.out.println(\"Warning! Hot!\");\n" + 490 " else {\n" + 491 " throw new LooserException();\n" + 492 " }\n" + 493 "}\n" + 494 "</pre>\n"; 495} 496