duke@1: /*
jjg@1606: * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.doclets.internal.toolkit.taglets;
duke@1:
duke@1: import java.io.*;
duke@1: import java.lang.reflect.*;
duke@1: import java.net.*;
duke@1: import java.util.*;
duke@1:
jjg@1413: import javax.tools.DocumentationTool;
jjg@1413: import javax.tools.JavaFileManager;
jjg@1413:
jjg@1357: import com.sun.javadoc.*;
jjg@1357: import com.sun.tools.doclets.internal.toolkit.util.*;
jjg@1357:
duke@1: /**
duke@1: * Manages theTaglet
s used by doclets.
duke@1: *
jjg@1359: *
This is NOT part of any supported API.
jjg@1359: * If you write code that depends on this, you do so at your own risk.
jjg@1359: * This code and its internal interfaces are subject to change or
jjg@1359: * deletion without notice.
duke@1: *
duke@1: * @author Jamie Ho
duke@1: * @since 1.4
duke@1: */
duke@1:
duke@1: public class TagletManager {
duke@1:
duke@1: /**
jjg@1413: * The default separator for the simple tag option.
duke@1: */
jjg@1413: public static final char SIMPLE_TAGLET_OPT_SEPARATOR = ':';
duke@1:
duke@1: /**
jjg@1413: * The alternate separator for simple tag options. Use this
jjg@1413: * when you want the default separator to be in the name of the
duke@1: * custom tag.
duke@1: */
jjg@1413: public static final String ALT_SIMPLE_TAGLET_OPT_SEPARATOR = "-";
duke@1:
duke@1: /**
duke@1: * The map of custom tags.
duke@1: */
jjg@74: private LinkedHashMapTagletManager
.
duke@1: * @param nosince true if we do not want to use @since tags.
duke@1: * @param showversion true if we want to use @version tags.
duke@1: * @param showauthor true if we want to use @author tags.
duke@1: * @param message the message retriever to print warnings.
duke@1: */
duke@1: public TagletManager(boolean nosince, boolean showversion,
jjg@1606: boolean showauthor, boolean javafx,
jjg@1606: MessageRetriever message) {
jjg@74: overridenStandardTags = new HashSetCustomTag
. This is used to add a Taglet from within
duke@1: * a Doclet. No message is printed to indicate that the Taglet is properly
duke@1: * registered because these Taglets are typically added for every execution of the
duke@1: * Doclet. We don't want to see this type of error message every time.
duke@1: * @param customTag the new CustomTag
to add.
duke@1: */
duke@1: public void addCustomTag(Taglet customTag) {
duke@1: if (customTag != null) {
duke@1: String name = customTag.getName();
duke@1: if (customTags.containsKey(name)) {
duke@1: customTags.remove(name);
duke@1: }
duke@1: customTags.put(name, customTag);
duke@1: checkTagName(name);
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Add a new Taglet
. Print a message to indicate whether or not
duke@1: * the Taglet was registered properly.
duke@1: * @param classname the name of the class representing the custom tag.
duke@1: * @param tagletPath the path to the class representing the custom tag.
duke@1: */
jjg@1413: public void addCustomTag(String classname, JavaFileManager fileManager, String tagletPath) {
duke@1: try {
jjg@74: Class> customTagClass = null;
duke@1: // construct class loader
duke@1: String cpString = null; // make sure env.class.path defaults to dot
duke@1:
jjg@1413: ClassLoader tagClassLoader;
jjg@1413: if (fileManager != null && fileManager.hasLocation(DocumentationTool.Location.TAGLET_PATH)) {
jjg@1413: tagClassLoader = fileManager.getClassLoader(DocumentationTool.Location.TAGLET_PATH);
jjg@1413: } else {
jjg@1413: // do prepends to get correct ordering
jjg@1413: cpString = appendPath(System.getProperty("env.class.path"), cpString);
jjg@1413: cpString = appendPath(System.getProperty("java.class.path"), cpString);
jjg@1413: cpString = appendPath(tagletPath, cpString);
jjg@1413: tagClassLoader = new URLClassLoader(pathToURLs(cpString));
jjg@1413: }
jjg@1413:
jjg@1413: customTagClass = tagClassLoader.loadClass(classname);
duke@1: Method meth = customTagClass.getMethod("register",
mcimadamore@184: new Class>[] {java.util.Map.class});
duke@1: Object[] list = customTags.values().toArray();
duke@1: Taglet lastTag = (list != null && list.length > 0)
duke@1: ? (Taglet) list[list.length-1] : null;
duke@1: meth.invoke(null, new Object[] {customTags});
duke@1: list = customTags.values().toArray();
duke@1: Object newLastTag = (list != null&& list.length > 0)
jjg@74: ? list[list.length-1] : null;
duke@1: if (lastTag != newLastTag) {
duke@1: //New taglets must always be added to the end of the LinkedHashMap.
duke@1: //If the current and previous last taglet are not equal, that
duke@1: //means a new Taglet has been added.
duke@1: message.notice("doclet.Notice_taglet_registered", classname);
duke@1: if (newLastTag != null) {
duke@1: checkTaglet(newLastTag);
duke@1: }
duke@1: }
duke@1: } catch (Exception exc) {
duke@1: message.error("doclet.Error_taglet_not_registered", exc.getClass().getName(), classname);
duke@1: }
duke@1:
duke@1: }
duke@1:
duke@1: private String appendPath(String path1, String path2) {
duke@1: if (path1 == null || path1.length() == 0) {
duke@1: return path2 == null ? "." : path2;
duke@1: } else if (path2 == null || path2.length() == 0) {
duke@1: return path1;
duke@1: } else {
duke@1: return path1 + File.pathSeparator + path2;
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Utility method for converting a search path string to an array
duke@1: * of directory and JAR file URLs.
duke@1: *
duke@1: * @param path the search path string
duke@1: * @return the resulting array of directory and JAR file URLs
duke@1: */
jjg@1383: private URL[] pathToURLs(String path) {
jjg@1383: SetSimpleTaglet
. If this tag already exists
duke@1: * and the header passed as an argument is null, move tag to the back of the
duke@1: * list. If this tag already exists and the header passed as an argument is
duke@1: * not null, overwrite previous tag with new one. Otherwise, add new
duke@1: * SimpleTaglet to list.
duke@1: * @param tagName the name of this tag
duke@1: * @param header the header to output.
duke@1: * @param locations the possible locations that this tag
duke@1: * can appear in.
duke@1: */
duke@1: public void addNewSimpleCustomTag(String tagName, String header, String locations) {
duke@1: if (tagName == null || locations == null) {
duke@1: return;
duke@1: }
jjg@74: Taglet tag = customTags.get(tagName);
duke@1: locations = locations.toLowerCase();
duke@1: if (tag == null || header != null) {
duke@1: customTags.remove(tagName);
duke@1: customTags.put(tagName, new SimpleTaglet(tagName, header, locations));
duke@1: if (locations != null && locations.indexOf('x') == -1) {
duke@1: checkTagName(tagName);
duke@1: }
duke@1: } else {
duke@1: //Move to back
duke@1: customTags.remove(tagName);
duke@1: customTags.put(tagName, tag);
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Given a tag name, add it to the set of tags it belongs to.
duke@1: */
duke@1: private void checkTagName(String name) {
duke@1: if (standardTags.contains(name)) {
duke@1: overridenStandardTags.add(name);
duke@1: } else {
duke@1: if (name.indexOf('.') == -1) {
duke@1: potentiallyConflictingTags.add(name);
duke@1: }
duke@1: unseenCustomTags.add(name);
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Check the taglet to see if it is a legacy taglet. Also
duke@1: * check its name for errors.
duke@1: */
duke@1: private void checkTaglet(Object taglet) {
duke@1: if (taglet instanceof Taglet) {
duke@1: checkTagName(((Taglet) taglet).getName());
duke@1: } else if (taglet instanceof com.sun.tools.doclets.Taglet) {
duke@1: com.sun.tools.doclets.Taglet legacyTaglet = (com.sun.tools.doclets.Taglet) taglet;
duke@1: customTags.remove(legacyTaglet.getName());
duke@1: customTags.put(legacyTaglet.getName(), new LegacyTaglet(legacyTaglet));
duke@1: checkTagName(legacyTaglet.getName());
duke@1: } else {
duke@1: throw new IllegalArgumentException("Given object is not a taglet.");
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Given a name of a seen custom tag, remove it from the set of unseen
duke@1: * custom tags.
duke@1: * @param name the name of the seen custom tag.
duke@1: */
duke@1: public void seenCustomTag(String name) {
duke@1: unseenCustomTags.remove(name);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Given an array of Tag
s, check for spelling mistakes.
duke@1: * @param doc the Doc object that holds the tags.
duke@1: * @param tags the list of Tag
s to check.
duke@1: * @param areInlineTags true if the array of tags are inline and false otherwise.
duke@1: */
duke@1: public void checkTags(Doc doc, Tag[] tags, boolean areInlineTags) {
duke@1: if (tags == null) {
duke@1: return;
duke@1: }
duke@1: Taglet taglet;
duke@1: for (int i = 0; i < tags.length; i++) {
duke@1: String name = tags[i].name();
duke@1: if (name.length() > 0 && name.charAt(0) == '@') {
duke@1: name = name.substring(1, name.length());
duke@1: }
duke@1: if (! (standardTags.contains(name) || customTags.containsKey(name))) {
duke@1: if (standardTagsLowercase.contains(name.toLowerCase())) {
duke@1: message.warning(tags[i].position(), "doclet.UnknownTagLowercase", tags[i].name());
duke@1: continue;
duke@1: } else {
duke@1: message.warning(tags[i].position(), "doclet.UnknownTag", tags[i].name());
duke@1: continue;
duke@1: }
duke@1: }
duke@1: //Check if this tag is being used in the wrong location.
jjg@74: if ((taglet = customTags.get(name)) != null) {
duke@1: if (areInlineTags && ! taglet.isInlineTag()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "inline");
duke@1: }
duke@1: if ((doc instanceof RootDoc) && ! taglet.inOverview()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "overview");
duke@1: } else if ((doc instanceof PackageDoc) && ! taglet.inPackage()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "package");
duke@1: } else if ((doc instanceof ClassDoc) && ! taglet.inType()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "class");
duke@1: } else if ((doc instanceof ConstructorDoc) && ! taglet.inConstructor()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "constructor");
duke@1: } else if ((doc instanceof FieldDoc) && ! taglet.inField()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "field");
duke@1: } else if ((doc instanceof MethodDoc) && ! taglet.inMethod()) {
duke@1: printTagMisuseWarn(taglet, tags[i], "method");
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Given the taglet, the tag and the type of documentation that the tag
duke@1: * was found in, print a tag misuse warning.
duke@1: * @param taglet the taglet representing the misused tag.
duke@1: * @param tag the misused tag.
duke@1: * @param holderType the type of documentation that the misused tag was found in.
duke@1: */
duke@1: private void printTagMisuseWarn(Taglet taglet, Tag tag, String holderType) {
jjg@74: SetTaglet
s that can
duke@1: * appear in packages.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in packages.
duke@1: */
duke@1: public Taglet[] getPackageCustomTags() {
duke@1: if (packageTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return packageTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in classes or interfaces.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in classes or interfaces.
duke@1: */
duke@1: public Taglet[] getTypeCustomTags() {
duke@1: if (typeTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return typeTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of inline Taglet
s that can
duke@1: * appear in comments.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in comments.
duke@1: */
duke@1: public Taglet[] getInlineCustomTags() {
duke@1: if (inlineTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return inlineTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in fields.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in field.
duke@1: */
duke@1: public Taglet[] getFieldCustomTags() {
duke@1: if (fieldTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return fieldTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in the serialized form.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in the serialized form.
duke@1: */
duke@1: public Taglet[] getSerializedFormTags() {
duke@1: if (serializedFormTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return serializedFormTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in the given Doc.
duke@1: */
duke@1: public Taglet[] getCustomTags(Doc doc) {
duke@1: if (doc instanceof ConstructorDoc) {
duke@1: return getConstructorCustomTags();
duke@1: } else if (doc instanceof MethodDoc) {
duke@1: return getMethodCustomTags();
duke@1: } else if (doc instanceof FieldDoc) {
duke@1: return getFieldCustomTags();
duke@1: } else if (doc instanceof ClassDoc) {
duke@1: return getTypeCustomTags();
duke@1: } else if (doc instanceof PackageDoc) {
duke@1: return getPackageCustomTags();
duke@1: } else if (doc instanceof RootDoc) {
duke@1: return getOverviewCustomTags();
duke@1: }
duke@1: return null;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in constructors.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in constructors.
duke@1: */
duke@1: public Taglet[] getConstructorCustomTags() {
duke@1: if (constructorTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return constructorTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in methods.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in methods.
duke@1: */
duke@1: public Taglet[] getMethodCustomTags() {
duke@1: if (methodTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return methodTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the array of Taglet
s that can
duke@1: * appear in an overview.
duke@1: * @return the array of Taglet
s that can
duke@1: * appear in overview.
duke@1: */
duke@1: public Taglet[] getOverviewCustomTags() {
duke@1: if (overviewTags == null) {
duke@1: initCustomTagArrays();
duke@1: }
duke@1: return overviewTags;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Initialize the custom tag arrays.
duke@1: */
duke@1: private void initCustomTagArrays() {
jjg@74: Iterator