--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java Tue Mar 19 15:18:35 2019 -0400
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseConfiguration.java Wed Mar 20 09:58:12 2019 +0530
@@ -837,39 +837,47 @@
tagletManager = tagletManager == null ?
new TagletManager(nosince, showversion, showauthor, javafx, this) :
tagletManager;
- for (List<String> args : customTagStrs) {
- if (args.get(0).equals("-taglet")) {
- tagletManager.addCustomTag(args.get(1), getFileManager(), tagletpath);
- continue;
+ JavaFileManager fileManager = getFileManager();
+ Messages messages = getMessages();
+ try {
+ tagletManager.initTagletPath(fileManager, tagletpath);
+ tagletManager.loadTaglets(fileManager);
+
+ for (List<String> args : customTagStrs) {
+ if (args.get(0).equals("-taglet")) {
+ tagletManager.addCustomTag(args.get(1), fileManager);
+ continue;
+ }
+ List<String> tokens = tokenize(args.get(1), TagletManager.SIMPLE_TAGLET_OPT_SEPARATOR, 3);
+ switch (tokens.size()) {
+ case 1:
+ String tagName = args.get(1);
+ if (tagletManager.isKnownCustomTag(tagName)) {
+ //reorder a standard tag
+ tagletManager.addNewSimpleCustomTag(tagName, null, "");
+ } else {
+ //Create a simple tag with the heading that has the same name as the tag.
+ StringBuilder heading = new StringBuilder(tagName + ":");
+ heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0)));
+ tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a");
+ }
+ break;
+
+ case 2:
+ //Add simple taglet without heading, probably to excluding it in the output.
+ tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(1), "");
+ break;
+
+ case 3:
+ tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(2), tokens.get(1));
+ break;
+
+ default:
+ messages.error("doclet.Error_invalid_custom_tag_argument", args.get(1));
+ }
}
- List<String> tokens = tokenize(args.get(1), TagletManager.SIMPLE_TAGLET_OPT_SEPARATOR, 3);
- switch (tokens.size()) {
- case 1:
- String tagName = args.get(1);
- if (tagletManager.isKnownCustomTag(tagName)) {
- //reorder a standard tag
- tagletManager.addNewSimpleCustomTag(tagName, null, "");
- } else {
- //Create a simple tag with the heading that has the same name as the tag.
- StringBuilder heading = new StringBuilder(tagName + ":");
- heading.setCharAt(0, Character.toUpperCase(tagName.charAt(0)));
- tagletManager.addNewSimpleCustomTag(tagName, heading.toString(), "a");
- }
- break;
-
- case 2:
- //Add simple taglet without heading, probably to excluding it in the output.
- tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(1), "");
- break;
-
- case 3:
- tagletManager.addNewSimpleCustomTag(tokens.get(0), tokens.get(2), tokens.get(1));
- break;
-
- default:
- Messages messages = getMessages();
- messages.error("doclet.Error_invalid_custom_tag_argument", args.get(1));
- }
+ } catch (IOException e) {
+ messages.error("doclet.taglet_could_not_set_location", e.toString());
}
}
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties Tue Mar 19 15:18:35 2019 -0400
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties Wed Mar 20 09:58:12 2019 +0530
@@ -56,6 +56,8 @@
doclet.Notice_taglet_conflict_warn=Note: Custom tags that could override future standard tags: {0}. To avoid potential overrides, use at least one period character (.) in custom tag names.
doclet.Error_taglet_not_registered=Error - Exception {0} thrown while trying to register Taglet {1}...
doclet.Error_invalid_custom_tag_argument=Error - {0} is an invalid argument to the -tag option...
+doclet.taglet_could_not_set_location = Could not set the taglet path: {0}
+doclet.not_standard_file_manager = Cannot set taglet path; the file manager is not a StandardJavaFileManager
doclet.Author=Author:
doclet.DefaultValue=Default value:
doclet.PropertyDescription=Property description:
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java Tue Mar 19 15:18:35 2019 -0400
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletManager.java Wed Mar 20 09:58:12 2019 +0530
@@ -204,45 +204,78 @@
}
/**
- * Add a new {@code Taglet}. Print a message to indicate whether or not
+ * Initializes the location TAGLET_PATH which is used to locate the custom taglets.
+ * @param fileManager the filemanager to load classes and resources.
+ * @param tagletPath the path to the custom taglet.
+ * @throws IOException if an error occurs while setting the location.
+ */
+ public void initTagletPath(JavaFileManager fileManager, String tagletPath) throws IOException {
+ if (fileManager instanceof StandardJavaFileManager) {
+ StandardJavaFileManager sfm = (StandardJavaFileManager)fileManager;
+ if (tagletPath != null) {
+ List<File> paths = new ArrayList<>();
+ for (String pathname : tagletPath.split(File.pathSeparator)) {
+ paths.add(new File(pathname));
+ }
+ sfm.setLocation(TAGLET_PATH, paths);
+ } else if (!sfm.hasLocation(TAGLET_PATH)) {
+ sfm.setLocation(TAGLET_PATH, Collections.emptyList());
+ }
+ } else if (tagletPath != null) {
+ messages.error("doclet.not_standard_file_manager");
+ }
+ }
+
+ /**
+ * Adds a new {@code Taglet}. Print a message to indicate whether or not
* the Taglet was registered properly.
* @param classname the name of the class representing the custom tag.
* @param fileManager the filemanager to load classes and resources.
- * @param tagletPath the path to the class representing the custom tag.
*/
- public void addCustomTag(String classname, JavaFileManager fileManager, String tagletPath) {
+ public void addCustomTag(String classname, JavaFileManager fileManager) {
try {
ClassLoader tagClassLoader;
- if (!fileManager.hasLocation(TAGLET_PATH)) {
- List<File> paths = new ArrayList<>();
- if (tagletPath != null) {
- for (String pathname : tagletPath.split(File.pathSeparator)) {
- paths.add(new File(pathname));
- }
- }
- if (fileManager instanceof StandardJavaFileManager) {
- ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, paths);
- }
- }
tagClassLoader = fileManager.getClassLoader(TAGLET_PATH);
Class<? extends jdk.javadoc.doclet.Taglet> customTagClass =
tagClassLoader.loadClass(classname).asSubclass(jdk.javadoc.doclet.Taglet.class);
jdk.javadoc.doclet.Taglet instance = customTagClass.getConstructor().newInstance();
- instance.init(docEnv, doclet);
- Taglet newLegacy = new UserTaglet(instance);
- String tname = newLegacy.getName();
- Taglet t = allTaglets.get(tname);
- if (t != null) {
- allTaglets.remove(tname);
+ registerTaglet(instance);
+ } catch (ReflectiveOperationException exc) {
+ messages.error("doclet.Error_taglet_not_registered", exc.getClass().getName(),
+ classname);
+ }
+ }
+
+ /**
+ * Loads taglets from a taglet path using service loader.
+ * @param fileManager the filemanager to load the taglets.
+ * @throws IOException if an error occurs while getting the service loader.
+ */
+ public void loadTaglets(JavaFileManager fileManager) throws IOException {
+ Iterable<? extends File> location = ((StandardJavaFileManager)fileManager).getLocation(TAGLET_PATH);
+ if (location != null && location.iterator().hasNext()) {
+ ServiceLoader<jdk.javadoc.doclet.Taglet> serviceLoader =
+ fileManager.getServiceLoader(TAGLET_PATH, jdk.javadoc.doclet.Taglet.class);
+ Iterator<jdk.javadoc.doclet.Taglet> iterator = serviceLoader.iterator();
+ while (iterator.hasNext()) {
+ jdk.javadoc.doclet.Taglet taglet = iterator.next();
+ registerTaglet(taglet);
}
- allTaglets.put(tname, newLegacy);
- messages.notice("doclet.Notice_taglet_registered", classname);
- } catch (Exception exc) {
- messages.error("doclet.Error_taglet_not_registered", exc.getClass().getName(), classname);
}
}
/**
+ * Registers the {@code Taglet}. Prints a message if a {@code Taglet} got registered properly.
+ * @param instance the {@code Taglet} instance.
+ */
+ private void registerTaglet(jdk.javadoc.doclet.Taglet instance) {
+ instance.init(docEnv, doclet);
+ Taglet newLegacy = new UserTaglet(instance);
+ allTaglets.put(newLegacy.getName(), newLegacy);
+ messages.notice("doclet.Notice_taglet_registered", instance.getClass().getName());
+ }
+
+ /**
* Add a new {@code SimpleTaglet}. If this tag already exists
* and the header passed as an argument is null, move tag to the back of the
* list. If this tag already exists and the header passed as an argument is
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/testAutoLoadTaglets/TestAutoLoadTaglets.java Wed Mar 20 09:58:12 2019 +0530
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8219958
+ * @summary Automatically load taglets from a jar file
+ * @library /tools/lib ../../lib
+ * @modules
+ * jdk.javadoc/jdk.javadoc.internal.tool
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * @build javadoc.tester.* toolbox.ToolBox builder.ClassBuilder toolbox.JarTask
+ * @run main/othervm TestAutoLoadTaglets
+ */
+
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import builder.ClassBuilder;
+import toolbox.JarTask;
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+import javadoc.tester.JavadocTester;
+
+public class TestAutoLoadTaglets extends JavadocTester {
+
+ final ToolBox tb;
+
+ public static void main(String... args) throws Exception {
+ TestAutoLoadTaglets tester = new TestAutoLoadTaglets();
+ tester.runTests(m -> new Object[]{Paths.get(m.getName())});
+ }
+
+ TestAutoLoadTaglets() {
+ tb = new ToolBox();
+ }
+
+ @Test
+ public void test(Path base) throws Exception {
+ Path srcDir = base.resolve("src");
+ Path outDir = base.resolve("out");
+
+ createTagletsJar(base, srcDir);
+
+ new ClassBuilder(tb, "pkg.A")
+ .setComments("test {@taglet1} and {@taglet2}")
+ .setModifiers("public", "class")
+ .write(srcDir);
+
+ javadoc("-d", outDir.toString(),
+ "-sourcepath", srcDir.toString(),
+ "-tagletpath", "taglets.jar",
+ "pkg");
+
+ checkExit(Exit.OK);
+
+ checkOutput("pkg/A.html", true,
+ "test user taglet taglet1 and user taglet taglet2");
+ }
+
+ private void createTagletsJar(Path base, Path srcDir) throws Exception {
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+ createTaglets(srcDir);
+
+ new JavacTask(tb).files(srcDir.resolve("Taglet1.java"), srcDir.resolve("Taglet2.java"))
+ .outdir(classes).run();
+
+ Path services = classes.resolve("META-INF").resolve("services").resolve("jdk.javadoc.doclet.Taglet");
+ tb.writeFile(services,
+ "Taglet1\n"
+ + "Taglet2");
+
+ new JarTask(tb, srcDir).run("cf", "taglets.jar", "-C", classes.toString(), ".");
+ }
+
+ private void createTaglets(Path srcDir) throws Exception {
+ for (int i = 1; i < 3; i++) {
+ tb.writeJavaFiles(srcDir,
+ "import com.sun.source.doctree.DocTree;\n"
+ + "import jdk.javadoc.doclet.Taglet;\n"
+ + "import javax.lang.model.element.Element;\n"
+ + "import java.util.List;\n"
+ + "import java.util.Set;\n"
+ + "public class Taglet" + i + " implements Taglet {\n"
+ + " @Override\n"
+ + " public Set<Location> getAllowedLocations() {\n"
+ + " return null;\n"
+ + " }\n"
+ + " @Override\n"
+ + " public boolean isInlineTag() {\n"
+ + " return true;\n"
+ + " }\n"
+ + " @Override\n"
+ + " public String getName() {\n"
+ + " return \"taglet" + i + "\";\n"
+ + " }\n"
+ + " @Override\n"
+ + " public String toString(List<? extends DocTree> tags, Element "
+ + "element) {\n"
+ + " return \"user taglet taglet" + i + "\";\n"
+ + " }\n"
+ + "}\n");
+ }
+ }
+
+}