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