/* * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sampleapi.generator; import java.io.File; import java.io.BufferedInputStream; import java.io.IOException; import java.util.Set; import javax.lang.model.element.Modifier; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.List; import java.util.HashMap; import java.util.Map; class DocCommentGenerator { public enum Text { BROWNFOX(BROWNFOX_TEXT), NOWISTHETIME(NOWISTHETIME_TEXT), THISPANGRAM(THISPANGRAM_TEXT), LOREMIPSUM(LOREMIPSUM_TEXT), LIEUROPANLINGUES(LIEUROPANLINGUES_TEXT), CODE(CODE_TEXT); String commentText; Text(String text) { commentText = text; } @Override public String toString() { return commentText; } } public enum Tag { AUTHOR("@author", "Cody J. Writer"), PARAM("@param", ""), RETURN("@return", Text.NOWISTHETIME.toString()), SINCE("@since", "1.0"), THROWS("@throws", "If problem detected"), EXCEPTION("@exception", "If problem detected"), SERIAL("@serial", ""), SERIALDATA("@serialData", "The types and order of data could be here."), SERIALFIELD("@serialField", "\n Serial field in special array"), FX_PROPSETTER("@propertySetter", "Set the property"), FX_PROPGETTER("@propertyGetter", "Get the property"), FX_PROPDESC("@propertyDescription", ""), FX_DEFVALUE("@defaultValue", ""), FX_TREATASPRIVATE("@treatAsPrivate", ""); String tagName; String tagValue; Tag(String tagName, String tagValue) { this.tagName = tagName; this.tagValue = tagValue; } public String toString() { return value("", ""); } public String value(String value) { return value(value, ""); } public String value(String value, String extra) { return tagName + ((value.length() != 0) ? " " + value : "") + ((tagValue.length() != 0) ? " " + tagValue : "") + ((extra.length() != 0) ? " " + extra : ""); } } public enum InlineTag { LITERAL("@literal", "Use < and > brackets instead of < and > escapes."), CODE("@code", "(i) -> new Abc<Object>((i > 0) ? (i << 1) : 0)"), LINK("@link", ""), VALUE("@value", ""), INDEX("@index", "", true); String tagName; String tagValue; boolean counted; Map<String, Integer> counters; InlineTag(String tagName, String tagValue) { this(tagName, tagValue, false); } InlineTag(String tagName, String tagValue, boolean counted) { this.tagName = tagName; this.tagValue = tagValue; this.counted = counted; if (counted) { counters = new HashMap<>(); } } public String toString() { return value(""); } public String value(String value) { String name = ((tagValue.length() != 0) ? " " + tagValue : "") + ((value.length() != 0) ? " " + value : ""); if (counted && !counters.containsKey(name)) { counters.put(name, 0); } return "{" + tagName + name + (counted ? "_" + counters.put(name, counters.get(name) + 1) : "") + "}"; } } public static class LinkTag { static String[] links = new String[] { "Boolean", "Boolean#TRUE", "Boolean#Boolean(boolean)", "Boolean#Boolean(String s)", "Boolean#compare(boolean, boolean)", "Boolean#compareTo(Boolean b)", "java.util.Vector", "java.util.Vector#elementCount", "java.util.Vector#Vector(int)", "java.util.Vector#Vector(int initialCapacity, int capacityIncrement)", "java.util.Vector#indexOf(Object)", "java.util.Vector#indexOf(Object o, int index)", "java.lang.annotation" }; static int index = 0; public static String nextSee() { String next = "@see " + links[index]; index = (index + 1) % links.length; return next; } public static String nextLink() { String next = "Also check " + (((index % 2) == 0) ? "{@link " : "{@linkplain ") + links[index] + "} for details.\n"; index = (index + 1) % links.length; return next; } } public static class VersionTag { static String[] versions = new String[] { "1.5, 09/01/04", "1.6, 12/11/06", "1.7, 07/28/11", "1.8, 04/19/14", "9, 06/03/16" }; static int index = 0; public static String nextVersion() { String next = "@version " + versions[index]; index = (index + 1) % versions.length; return next; } } // // getters (build comments for entities) // public String getPackageComment() { return InlineTag.INDEX.value("PackageCommentLabel") + " " + Text.LOREMIPSUM + "\n<p>" + Text.LIEUROPANLINGUES + "\n" + Text.CODE + "\n" + LinkTag.nextLink() + "\n" + LinkTag.nextSee() + "\n" + Tag.SINCE + "\n"; } String[] serialVals = new String[] { "include", "exclude" }; // static index to roll over "include/exclude" static int serialValIdx = 0; public String getBaseComment(JCClassDecl baseDecl, boolean toplevel) { String buildComment = InlineTag.INDEX.value("BaseCommentLabel") + " "; buildComment += Text.LIEUROPANLINGUES + "\n"; buildComment += "<p>It is possible to see inlined code:\n" + InlineTag.CODE + " is the example.\n\n"; buildComment += "<p>Literal use example.\n" + InlineTag.LITERAL + "\n\n"; buildComment += "<p>" + Text.THISPANGRAM + "\n"; // make comment longer buildComment += "<p>" + LinkTag.nextLink() + "\n"; if (toplevel) buildComment += "\n" + VersionTag.nextVersion() + "\n"; // @param for type params List<JCTypeParameter> params = baseDecl.getTypeParameters(); int paramIndex = 0; for (JCTypeParameter paramDecl : params) { buildComment += Tag.PARAM.value( "<" + paramDecl.getName().toString() + ">", "the type of value set #" + paramIndex++) + "\n"; } buildComment += "\n" + LinkTag.nextSee(); buildComment += "\n"; if (toplevel) buildComment += Tag.AUTHOR + "\n"; buildComment += Tag.SINCE + "\n"; // for serial; currently no need to dig too deep if (isSerializable(baseDecl) || isErrorOrException(baseDecl)) { buildComment += "\n" + Tag.SERIAL.value(serialVals[serialValIdx]); serialValIdx = (serialValIdx + 1) % 2; } return buildComment; } public String getConstComment() { String buildComment = InlineTag.INDEX.value("ConstCommentLabel") + " "; buildComment += Text.NOWISTHETIME + " " + Text.BROWNFOX + "\n"; buildComment += LinkTag.nextLink() + "\n"; buildComment += LinkTag.nextSee() + "\n"; buildComment += Tag.SINCE + "\n"; return buildComment; } public String getFieldComment(JCClassDecl baseDecl, JCVariableDecl varDecl, boolean isFxStyle) { String buildComment = InlineTag.INDEX.value("FieldCommentLabel") + " "; buildComment += Text.BROWNFOX + "<p>" + Text.NOWISTHETIME + "\n"; Set<Modifier> mods = varDecl.getModifiers().getFlags(); String varName = varDecl.getName().toString(); if (mods.contains(Modifier.STATIC) && mods.contains(Modifier.FINAL)) { JCExpression init = varDecl.getInitializer(); if (init != null && !"null".equals(init.toString()) && !"serialPersistentFields".equals(varName)) buildComment += "<p>The value of this constant is " + InlineTag.VALUE + ".\n"; } buildComment += "<p>" + LinkTag.nextLink() + "\n"; if (isSerializable(baseDecl)) { if (isErrorOrException(baseDecl)) { buildComment += Tag.SERIAL + "\n"; } else if ("serialPersistentFields".equals(varName)) { JCNewArray sfList = (JCNewArray)varDecl.getInitializer(); for (JCExpression sf : sfList.getInitializers()) { List<JCExpression> args = ((JCNewClass)sf).getArguments(); String sfName = ((JCLiteral)args.get(0)).getValue().toString(); String sfClass = ((JCIdent)args.get(1)).getName().toString(); String sfType = sfClass.substring(0, sfClass.indexOf(".class")); buildComment += Tag.SERIALFIELD.value(sfName + " " + sfType) + "\n"; } } else { buildComment += Tag.SERIAL.value("Very important value.") + "\n"; } } if (isFxStyle) { // set default value String varType = varDecl.getType().toString(); buildComment += Tag.FX_DEFVALUE.value(defValue(varType)) + "\n"; } buildComment += LinkTag.nextSee() + "\n"; return buildComment; } public String getMethodComment(JCClassDecl baseDecl, JCMethodDecl methodDecl, boolean isFxStyle) { String buildComment = InlineTag.INDEX.value("MethodCommentLabel") + " "; buildComment += Text.BROWNFOX + "\n<p>" + Text.THISPANGRAM + "\n"; buildComment += "<p>" + LinkTag.nextLink() + "\n"; buildComment += "<p>Literal use example.\n" + InlineTag.LITERAL + "\n\n"; // @param for type params List<JCTypeParameter> tparams = methodDecl.getTypeParameters(); int tparamIndex = 0; for (JCTypeParameter paramDecl : tparams) { String paramDeclString = paramDecl.getName().toString(); // simplify it (could contain 'extend'/'super' clauses int spacePos = paramDeclString.indexOf(' '); if (spacePos != -1) paramDeclString = paramDeclString.substring(0, spacePos); buildComment += Tag.PARAM.value( "<" + paramDeclString + ">", "the type of value set #" + tparamIndex++) + "\n"; } // @param List<JCVariableDecl> params = methodDecl.getParameters(); int paramIndex = 0; for (JCVariableDecl paramDecl : params) { buildComment += Tag.PARAM.value( paramDecl.getName().toString(), "an income parameter #" + paramIndex++) + "\n"; } // @return JCTree retType = methodDecl.getReturnType(); // null for constructors if (retType != null && !"void".equals(retType.toString())) buildComment += Tag.RETURN + "\n"; // @throws/@exception Tag t = isDerived(baseDecl) ? Tag.EXCEPTION : Tag.THROWS; List<JCExpression> throwTypes = methodDecl.getThrows(); for (JCExpression throwsExp : throwTypes) { buildComment += t.value(throwsExp.toString()) + "\n"; } if (isSerializable(baseDecl)) { switch (methodDecl.getName().toString()) { case "writeObject": case "readObject": case "writeExternal": case "readExternal": case "writeReplace": case "readResolve": buildComment += Tag.SERIALDATA + "\n"; break; default: } } if (isFxStyle) { // @propertySetter/Getter + Description if ("void".equals(retType.toString())) { buildComment += Tag.FX_PROPSETTER + "\n"; } else { buildComment += Tag.FX_PROPGETTER + "\n"; buildComment += Tag.FX_DEFVALUE.value(defValue(retType.toString())) + "\n"; } buildComment += Tag.FX_PROPDESC.value(Text.BROWNFOX.toString()) + "\n"; // @treatAsPrivate if (methodDecl.getModifiers().getFlags().contains(Modifier.PUBLIC)) buildComment += Tag.FX_TREATASPRIVATE + "\n"; } // @see buildComment += LinkTag.nextSee() + "\n"; // @since buildComment += Tag.SINCE + "\n"; return buildComment; } // // util methods // private boolean isErrorOrException(JCClassDecl baseDecl) { JCExpression ext = baseDecl.getExtendsClause(); if (ext != null) { String extClassName = ext.toString(); if (extClassName.contains("Error") || extClassName.contains("Exception")) return true; } return false; } private boolean isSerializable(JCClassDecl baseDecl) { List<JCExpression> impls = baseDecl.getImplementsClause(); for (JCExpression impl : impls) { if (impl.toString().contains("Serializable")) return true; } return false; } private boolean isDerived(JCClassDecl baseDecl) { return (baseDecl.getExtendsClause() == null) ? false : true; } private String defValue(String type) { switch (type) { case "boolean": return "true"; case "byte": case "char": case "int": case "long": case "Integer": case "Long": return "1"; case "float": case "double": case "Float": case "Double": return "1.0"; case "String": return "string"; default: return "null"; } } private static final String BROWNFOX_TEXT = "The quick brown fox jumps over the lazy dog.\n"; private static final String NOWISTHETIME_TEXT = "Now is the time for all good men to come to the aid of the party.\n"; private static final String THISPANGRAM_TEXT = "This pangram contains four a's, one b, two c's, one d, thirty e's,\n" + "six f's, five g's, seven h's, eleven i's, one j, one k, two l's,\n" + "two m's, eighteen n's, fifteen o's, two p's, one q, five r's,\n" + "twenty-seven s's, eighteen t's, two u's, seven v's, eight w's,\n" + "two x's, three y's, & one z."; private static final String LOREMIPSUM_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n" + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut\n" + "enim ad minim veniam, quis nostrud exercitation ullamco laboris\n" + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor\n" + "in reprehenderit in voluptate velit esse cillum dolore eu fugiat\n" + "nulla pariatur. Excepteur sint occaecat cupidatat non proident,\n" + "sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; private static final String LIEUROPANLINGUES_TEXT = "Li Europan lingues es membres del sam familie. Lor separat existentie\n" + "es un myth. Por scientie, musica, sport etc, litot Europa usa li sam\n" + "vocabular. Li lingues differe solmen in li grammatica, li pronunciation\n" + "e li plu commun vocabules. Omnicos directe al desirabilite de un nov\n" + "lingua franca: On refusa continuar payar custosi traductores.\n" + "\n" + "<p>At solmen va esser necessi far uniform grammatica, pronunciation\n" + "e plu commun paroles. Ma quande lingues coalesce, li grammatica del\n" + "resultant lingue es plu simplic e regulari quam ti del coalescent\n" + "lingues. Li nov lingua franca va esser plu simplic e regulari quam\n" + "li existent Europan lingues. It va esser tam simplic quam Occidental\n" + "in fact, it va esser Occidental. A un Angleso it va semblar un simplificat\n" + "Angles, quam un skeptic Cambridge amico dit me que Occidental es.\n"; private static final String CODE_TEXT = "<pre>\n" + "public void checkLanguage(Language lang) throws Exception {\n" + " if (lang.getName().equals(\"Java\")) {\n" + " System.out.println(\"Warning! Hot!\");\n" + " else {\n" + " throw new LooserException();\n" + " }\n" + "}\n" + "</pre>\n"; }