8205593: Javadoc -link makes broken links if module name matches package name

Reviewed-by: jjg
This commit is contained in:
Priya Lakshmi Muthuswamy 2018-09-25 12:36:45 +05:30
parent 25295df059
commit 4b47d2c9e7
18 changed files with 306 additions and 99 deletions

View file

@ -208,12 +208,6 @@ public class HtmlConfiguration extends BaseConfiguration {
*/
public HtmlVersion htmlVersion = null;
/**
* Flag to enable/disable use of module directories when generating docs for modules
* Default: on (module directories are enabled).
*/
public boolean useModuleDirectories = true;
/**
* Collected set of doclint options
*/
@ -840,13 +834,6 @@ public class HtmlConfiguration extends BaseConfiguration {
}
return true;
}
},
new XOption(resources, "--no-module-directories") {
@Override
public boolean process(String option, List<String> args) {
useModuleDirectories = false;
return true;
}
}
};
Set<Doclet.Option> oset = new TreeSet<>();

View file

@ -610,7 +610,7 @@ public class HtmlDocletWriter {
return links.createLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
label);
} else {
DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement));
DocLink crossPkgLink = getCrossPackageLink(packageElement);
if (crossPkgLink != null) {
return links.createLink(crossPkgLink, label);
} else {
@ -693,11 +693,10 @@ public class HtmlDocletWriter {
/*************************************************************
* Return a class cross link to external class documentation.
* The name must be fully qualified to determine which package
* the class is in. The -link option does not allow users to
* The -link option does not allow users to
* link to external classes in the "default" package.
*
* @param qualifiedClassName the qualified name of the external class.
* @param classElement the class element
* @param refMemName the name of the member being referenced. This should
* be null or empty string if no member is being referenced.
* @param label the label for the external link.
@ -705,19 +704,15 @@ public class HtmlDocletWriter {
* @param code true if the label should be code font.
* @return the link
*/
public Content getCrossClassLink(String qualifiedClassName, String refMemName,
public Content getCrossClassLink(TypeElement classElement, String refMemName,
Content label, boolean strong, boolean code) {
String className = "";
String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
int periodIndex;
while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
className = packageName.substring(periodIndex + 1, packageName.length()) +
(className.length() > 0 ? "." + className : "");
if (classElement != null) {
String className = utils.getSimpleName(classElement);
PackageElement packageElement = utils.containingPackage(classElement);
Content defaultLabel = new StringContent(className);
if (code)
defaultLabel = HtmlTree.CODE(defaultLabel);
packageName = packageName.substring(0, periodIndex);
if (getCrossPackageLink(packageName) != null) {
if (getCrossPackageLink(packageElement) != null) {
/*
The package exists in external documentation, so link to the external
class (assuming that it exists). This is definitely a limitation of
@ -725,13 +720,13 @@ public class HtmlDocletWriter {
exists, but no way to determine if the external class exists. We just
have to assume that it does.
*/
DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
DocLink link = configuration.extern.getExternalLink(packageElement, pathToRoot,
className + ".html", refMemName);
return links.createLink(link,
(label == null) || label.isEmpty() ? defaultLabel : label,
strong,
resources.getText("doclet.Href_Class_Or_Interface_Title", packageName),
"", true);
resources.getText("doclet.Href_Class_Or_Interface_Title",
utils.getPackageName(packageElement)), "", true);
}
}
return null;
@ -744,14 +739,14 @@ public class HtmlDocletWriter {
return configuration.extern.isExternal(typeElement);
}
public DocLink getCrossPackageLink(String pkgName) {
return configuration.extern.getExternalLink(pkgName, pathToRoot,
public DocLink getCrossPackageLink(PackageElement element) {
return configuration.extern.getExternalLink(element, pathToRoot,
DocPaths.PACKAGE_SUMMARY.getPath());
}
public DocLink getCrossModuleLink(String mdleName) {
return configuration.extern.getExternalLink(mdleName, pathToRoot,
docPaths.moduleSummary(mdleName).getPath());
public DocLink getCrossModuleLink(ModuleElement element) {
return configuration.extern.getExternalLink(element, pathToRoot,
docPaths.moduleSummary(utils.getModuleName(element)).getPath());
}
/**
@ -1024,17 +1019,13 @@ public class HtmlDocletWriter {
return getPackageLink(refPackage, label);
} else {
// @see is not referencing an included class, module or package. Check for cross links.
Content classCrossLink;
DocLink elementCrossLink = (configuration.extern.isModule(refClassName))
? getCrossModuleLink(refClassName) : getCrossPackageLink(refClassName);
? getCrossModuleLink(utils.elementUtils.getModuleElement(refClassName)) :
(refPackage != null) ? getCrossPackageLink(refPackage) : null;
if (elementCrossLink != null) {
// Element cross link found
return links.createLink(elementCrossLink,
(label.isEmpty() ? text : label), true);
} else if ((classCrossLink = getCrossClassLink(refClassName,
refMemName, label, false, !isLinkPlain)) != null) {
// Class cross link found (possibly to a member in the class)
return classCrossLink;
} else {
// No cross link found so print warning
messages.warning(ch.getDocTreePath(see),

View file

@ -111,7 +111,7 @@ public class LinkFactoryImpl extends LinkFactory {
}
} else {
Content crossLink = m_writer.getCrossClassLink(
typeElement.getQualifiedName().toString(), classLinkInfo.where,
typeElement, classLinkInfo.where,
label, classLinkInfo.isStrong, true);
if (crossLink != null) {
link.addContent(crossLink);

View file

@ -866,8 +866,7 @@ public class Navigation {
contents.packageLabel)));
} else {
DocLink crossPkgLink = configuration.extern.getExternalLink(
configuration.utils.getPackageName(packageElement), pathToRoot,
DocPaths.PACKAGE_SUMMARY.getPath());
packageElement, pathToRoot, DocPaths.PACKAGE_SUMMARY.getPath());
if (crossPkgLink != null) {
tree.addContent(HtmlTree.LI(links.createLink(crossPkgLink, contents.packageLabel)));
} else {

View file

@ -295,6 +295,11 @@ public abstract class BaseConfiguration {
// A list of pairs containing urls and package list
private final List<Pair<String, String>> linkOfflineList = new ArrayList<>();
/**
* Flag to enable/disable use of module directories when generating docs for modules
* Default: on (module directories are enabled).
*/
public boolean useModuleDirectories = true;
public boolean dumpOnError = false;
@ -740,6 +745,13 @@ public abstract class BaseConfiguration {
return true;
}
},
new XOption(resources, "--no-module-directories") {
@Override
public boolean process(String option, List<String> args) {
useModuleDirectories = false;
return true;
}
}
};
Set<Doclet.Option> set = new TreeSet<>();
set.addAll(Arrays.asList(options));

View file

@ -226,6 +226,10 @@ doclet.Enum_Constant=Enum Constant
doclet.Description=Description
doclet.ConstantField=Constant Field
doclet.Value=Value
doclet.linkMismatch_PackagedLinkedtoModule=The code being documented uses packages in the unnamed module, \
but the packages defined in {0} are in named modules.
doclet.linkMismatch_ModuleLinkedtoPackage=The code being documented uses modules but the packages defined \
in {0} are in the unnamed module.
#Documentation for Enums
doclet.enum_values_doc.fullbody=\

View file

@ -29,8 +29,10 @@ import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.lang.model.element.Element;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.PackageElement;
import javax.tools.Diagnostic;
import javax.tools.DocumentationTool;
@ -59,7 +61,8 @@ public class Extern {
* Map element names onto Extern Item objects.
* Lazily initialized.
*/
private Map<String, Item> elementToItemMap;
private Map<String, Item> moduleItems = new HashMap<>();
private Map<String, Map<String, Item>> packageItems = new HashMap<>();
/**
* The global configuration information for this run.
@ -85,18 +88,13 @@ public class Extern {
* The URL or the directory path at which the element documentation will be
* avaliable.
*/
final String path;
final DocPath path;
/**
* If given path is directory path then true else if it is a URL then false.
*/
final boolean relative;
/**
* If the item is a module then true else if it is a package then false.
*/
boolean isModule = false;
/**
* Constructor to build a Extern Item object and map it with the element name.
* If the same element name is found in the map, then the first mapped
@ -106,19 +104,11 @@ public class Extern {
* @param path URL or Directory path from where the "element-list"
* file is picked.
* @param relative True if path is URL, false if directory path.
* @param isModule True if the item is a module. False if it is a package.
*/
Item(String elementName, String path, boolean relative, boolean isModule) {
Item(String elementName, DocPath path, boolean relative) {
this.elementName = elementName;
this.path = path;
this.relative = relative;
this.isModule = isModule;
if (elementToItemMap == null) {
elementToItemMap = new HashMap<>();
}
if (!elementToItemMap.containsKey(elementName)) { // save the previous
elementToItemMap.put(elementName, this); // mapped location
}
}
/**
@ -126,7 +116,7 @@ public class Extern {
*/
@Override
public String toString() {
return elementName + (relative? " -> " : " => ") + path;
return elementName + (relative? " -> " : " => ") + path.getPath();
}
}
@ -141,14 +131,15 @@ public class Extern {
* @return true if the element is externally documented
*/
public boolean isExternal(Element element) {
if (elementToItemMap == null) {
if (packageItems.isEmpty()) {
return false;
}
PackageElement pe = configuration.utils.containingPackage(element);
if (pe.isUnnamed()) {
return false;
}
return elementToItemMap.get(configuration.utils.getPackageName(pe)) != null;
return findElementItem(pe) != null;
}
/**
@ -158,25 +149,25 @@ public class Extern {
* @return true if the element is a module
*/
public boolean isModule(String elementName) {
Item elem = findElementItem(elementName);
return (elem == null) ? false : elem.isModule;
Item elem = moduleItems.get(elementName);
return (elem == null) ? false : true;
}
/**
* Convert a link to be an external link if appropriate.
*
* @param elemName The element name.
* @param element The element .
* @param relativepath The relative path.
* @param filename The link to convert.
* @return if external return converted link else return null
*/
public DocLink getExternalLink(String elemName, DocPath relativepath, String filename) {
return getExternalLink(elemName, relativepath, filename, null);
public DocLink getExternalLink(Element element, DocPath relativepath, String filename) {
return getExternalLink(element, relativepath, filename, null);
}
public DocLink getExternalLink(String elemName, DocPath relativepath, String filename,
public DocLink getExternalLink(Element element, DocPath relativepath, String filename,
String memberName) {
Item fnd = findElementItem(elemName);
Item fnd = findElementItem(element);
if (fnd == null)
return null;
@ -184,7 +175,7 @@ public class Extern {
// to contain external URLs!
DocPath p = fnd.relative ?
relativepath.resolve(fnd.path).resolve(filename) :
DocPath.create(fnd.path).resolve(filename);
fnd.path.resolve(filename);
return new DocLink(p, "is-external=true", memberName);
}
@ -266,13 +257,20 @@ public class Extern {
/**
* Get the Extern Item object associated with this element name.
*
* @param elemName Element name.
* @param element Element
*/
private Item findElementItem(String elemName) {
if (elementToItemMap == null) {
return null;
private Item findElementItem(Element element) {
Item item = null;
if (element instanceof ModuleElement) {
item = moduleItems.get(configuration.utils.getModuleName((ModuleElement)element));
}
return elementToItemMap.get(elemName);
else if (element instanceof PackageElement) {
PackageElement packageElement = (PackageElement)element;
ModuleElement moduleElement = configuration.utils.containingModule(packageElement);
Map<String, Item> pkgMap = packageItems.get(configuration.utils.getModuleName(moduleElement));
item = (pkgMap != null) ? pkgMap.get(configuration.utils.getPackageName(packageElement)) : null;
}
return item;
}
/**
@ -370,23 +368,34 @@ public class Extern {
* @throws IOException if there is a problem reading or closing the stream
*/
private void readElementList(InputStream input, String path, boolean relative)
throws IOException {
throws Fault, IOException {
try (BufferedReader in = new BufferedReader(new InputStreamReader(input))) {
in.lines().forEach((elemname) -> {
String elemname = null;
String moduleName = null;
DocPath elempath = null;
DocPath basePath = DocPath.create(path);
while ((elemname = in.readLine()) != null) {
if (elemname.length() > 0) {
boolean module;
String elempath;
elempath = basePath;
if (elemname.startsWith(DocletConstants.MODULE_PREFIX)) {
elemname = elemname.replace(DocletConstants.MODULE_PREFIX, "");
elempath = path;
module = true;
moduleName = elemname.replace(DocletConstants.MODULE_PREFIX, "");
Item item = new Item(moduleName, elempath, relative);
moduleItems.put(moduleName, item);
} else {
elempath = path + elemname.replace('.', '/') + '/';
module = false;
DocPath pkgPath = DocPath.create(elemname.replace('.', '/'));
if (configuration.useModuleDirectories && moduleName != null) {
elempath = elempath.resolve(DocPath.create(moduleName).resolve(pkgPath));
} else {
elempath = elempath.resolve(pkgPath);
}
checkLinkCompatibility(elemname, moduleName, path);
Item item = new Item(elemname, elempath, relative);
packageItems.computeIfAbsent(moduleName == null ?
DocletConstants.DEFAULT_ELEMENT_NAME : moduleName, k -> new TreeMap<>())
.put(elemname, item);
}
Item ignore = new Item(elemname, elempath, relative, module);
}
});
}
}
}
@ -400,4 +409,18 @@ public class Extern {
return false;
}
}
private void checkLinkCompatibility(String packageName, String moduleName, String path) throws Fault {
PackageElement pe = configuration.utils.elementUtils.getPackageElement(packageName);
if (pe != null) {
ModuleElement me = (ModuleElement)pe.getEnclosingElement();
if (me == null || me.isUnnamed()) {
if (moduleName != null)
throw new Fault(configuration.getText("doclet.linkMismatch_PackagedLinkedtoModule",
path), null);
} else if (moduleName == null)
throw new Fault(configuration.getText("doclet.linkMismatch_ModuleLinkedtoPackage",
path), null);
}
}
}