8004832: Add new doclint package

Reviewed-by: mcimadamore
This commit is contained in:
Jonathan Gibbons 2012-12-17 07:47:05 -08:00
parent c5acce4569
commit 4a3e904c6e
100 changed files with 4459 additions and 28 deletions

View File

@ -117,7 +117,8 @@ javac.includes = \
javax/lang/model/ \
javax/tools/ \
com/sun/source/ \
com/sun/tools/javac/
com/sun/tools/javac/ \
com/sun/tools/doclint/
javac.tests = \
tools/javac

View File

@ -45,9 +45,7 @@ public abstract class DocTrees extends Trees {
* @throws IllegalArgumentException if the task does not support the Trees API.
*/
public static DocTrees instance(CompilationTask task) {
if (!task.getClass().getName().equals("com.sun.tools.javac.api.JavacTaskImpl"))
throw new IllegalArgumentException();
return (DocTrees) getJavacTrees(CompilationTask.class, task);
return (DocTrees) Trees.instance(task);
}
/**

View File

@ -139,6 +139,7 @@ public abstract class JavacTask implements CompilationTask {
* @see com.sun.source.util.Trees#getTypeMirror
*/
public abstract TypeMirror getTypeMirror(Iterable<? extends Tree> path);
/**
* Get a utility object for dealing with program elements.
*/

View File

@ -60,14 +60,20 @@ public class TreePath implements Iterable<Tree> {
this.path = path;
}
}
class PathFinder extends TreePathScanner<TreePath,Tree> {
public TreePath scan(Tree tree, Tree target) {
if (tree == target)
if (tree == target) {
throw new Result(new TreePath(getCurrentPath(), target));
}
return super.scan(tree, target);
}
}
if (path.getLeaf() == target) {
return path;
}
try {
new PathFinder().scan(path, target);
} catch (Result result) {

View File

@ -0,0 +1,754 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.util.regex.Matcher;
import com.sun.source.doctree.LinkTree;
import java.net.URI;
import java.util.regex.Pattern;
import java.io.IOException;
import com.sun.tools.javac.tree.DocPretty;
import java.io.StringWriter;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
import com.sun.source.doctree.AttributeTree;
import com.sun.source.doctree.AuthorTree;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.EndElementTree;
import com.sun.source.doctree.EntityTree;
import com.sun.source.doctree.ErroneousTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.InheritDocTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.doctree.ReturnTree;
import com.sun.source.doctree.SerialDataTree;
import com.sun.source.doctree.SerialFieldTree;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.StartElementTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.ThrowsTree;
import com.sun.source.doctree.VersionTree;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.TreePath;
import com.sun.tools.doclint.HtmlTag.AttrKind;
import java.net.URISyntaxException;
import static com.sun.tools.doclint.Messages.Group.*;
/**
* Validate a doc comment.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class Checker extends DocTreeScanner<Void, Void> {
final Env env;
Set<Element> foundParams = new HashSet<Element>();
Set<TypeMirror> foundThrows = new HashSet<TypeMirror>();
Set<String> foundAnchors = new HashSet<String>();
boolean foundInheritDoc = false;
boolean foundReturn = false;
enum Flag {
TABLE_HAS_CAPTION,
HAS_ELEMENT,
HAS_TEXT
}
static class TagStackItem {
final DocTree tree; // typically, but not always, StartElementTree
final HtmlTag tag;
final Set<HtmlTag.Attr> attrs;
final Set<Flag> flags;
TagStackItem(DocTree tree, HtmlTag tag) {
this.tree = tree;
this.tag = tag;
attrs = EnumSet.noneOf(HtmlTag.Attr.class);
flags = EnumSet.noneOf(Flag.class);
}
@Override
public String toString() {
return String.valueOf(tag);
}
}
private Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
private HtmlTag currHeaderTag;
// <editor-fold defaultstate="collapsed" desc="Top level">
Checker(Env env) {
env.getClass();
this.env = env;
tagStack = new LinkedList<TagStackItem>();
}
public Void scan(DocCommentTree tree, TreePath p) {
env.setCurrent(p, tree);
boolean isOverridingMethod = !env.currOverriddenMethods.isEmpty();
if (tree == null) {
if (!isSynthetic() && !isOverridingMethod)
reportMissing("dc.missing.comment");
return null;
}
tagStack.clear();
currHeaderTag = null;
foundParams.clear();
foundThrows.clear();
foundInheritDoc = false;
foundReturn = false;
scan(tree, (Void) null);
if (!isOverridingMethod) {
switch (env.currElement.getKind()) {
case METHOD:
case CONSTRUCTOR: {
ExecutableElement ee = (ExecutableElement) env.currElement;
checkParamsDocumented(ee.getTypeParameters());
checkParamsDocumented(ee.getParameters());
switch (ee.getReturnType().getKind()) {
case VOID:
case NONE:
break;
default:
if (!foundReturn
&& !foundInheritDoc
&& !env.types.isSameType(ee.getReturnType(), env.java_lang_Void)) {
reportMissing("dc.missing.return");
}
}
checkThrowsDocumented(ee.getThrownTypes());
}
}
}
return null;
}
private void reportMissing(String code, Object... args) {
env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args);
}
@Override
public Void visitDocComment(DocCommentTree tree, Void ignore) {
super.visitDocComment(tree, ignore);
for (TagStackItem tsi: tagStack) {
if (tsi.tree.getKind() == DocTree.Kind.START_ELEMENT
&& tsi.tag.endKind == HtmlTag.EndKind.REQUIRED) {
StartElementTree t = (StartElementTree) tsi.tree;
env.messages.error(HTML, t, "dc.tag.not.closed", t.getName());
}
}
return null;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Text and entities.">
@Override
public Void visitText(TextTree tree, Void ignore) {
if (!tree.getBody().trim().isEmpty()) {
markEnclosingTag(Flag.HAS_TEXT);
}
return null;
}
@Override
public Void visitEntity(EntityTree tree, Void ignore) {
markEnclosingTag(Flag.HAS_TEXT);
String name = tree.getName().toString();
if (name.startsWith("#")) {
int v = name.toLowerCase().startsWith("#x")
? Integer.parseInt(name.substring(2), 16)
: Integer.parseInt(name.substring(1), 10);
if (!Entity.isValid(v)) {
env.messages.error(HTML, tree, "dc.entity.invalid", name);
}
} else if (!Entity.isValid(name)) {
env.messages.error(HTML, tree, "dc.entity.invalid", name);
}
return null;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="HTML elements">
@Override
public Void visitStartElement(StartElementTree tree, Void ignore) {
markEnclosingTag(Flag.HAS_ELEMENT);
final Name treeName = tree.getName();
final HtmlTag t = HtmlTag.get(treeName);
if (t == null) {
env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
} else {
// tag specific checks
switch (t) {
// check for out of sequence headers, such as <h1>...</h1> <h3>...</h3>
case H1: case H2: case H3: case H4: case H5: case H6:
checkHeader(tree, t);
break;
// <p> inside <pre>
case P:
TagStackItem top = tagStack.peek();
if (top != null && top.tag == HtmlTag.PRE)
env.messages.warning(HTML, tree, "dc.tag.p.in.pre");
break;
}
// check that only block tags and inline tags are used,
// and that blocks tags are not used within inline tags
switch (t.blockType) {
case INLINE:
break;
case BLOCK:
TagStackItem top = tagStack.peek();
if (top != null && top.tag != null && top.tag.blockType == HtmlTag.BlockType.INLINE) {
switch (top.tree.getKind()) {
case START_ELEMENT: {
Name name = ((StartElementTree) top.tree).getName();
env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.element",
treeName, name);
break;
}
case LINK:
case LINK_PLAIN: {
String name = top.tree.getKind().tagName;
env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.tag",
treeName, name);
break;
}
default:
env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.other",
treeName);
}
}
break;
case OTHER:
env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
break;
default:
throw new AssertionError();
}
if (t.flags.contains(HtmlTag.Flag.NO_NEST)) {
for (TagStackItem i: tagStack) {
if (t == i.tag) {
env.messages.warning(HTML, tree, "dc.tag.nested.not.allowed", treeName);
break;
}
}
}
}
// check for self closing tags, such as <a id="name"/>
if (tree.isSelfClosing()) {
env.messages.error(HTML, tree, "dc.tag.self.closing", treeName);
}
try {
TagStackItem parent = tagStack.peek();
TagStackItem top = new TagStackItem(tree, t);
tagStack.push(top);
super.visitStartElement(tree, ignore);
// handle attributes that may or may not have been found in start element
if (t != null) {
switch (t) {
case CAPTION:
if (parent != null && parent.tag == HtmlTag.TABLE)
parent.flags.add(Flag.TABLE_HAS_CAPTION);
break;
case IMG:
if (!top.attrs.contains(HtmlTag.Attr.ALT))
env.messages.error(ACCESSIBILITY, tree, "dc.no.alt.attr.for.image");
break;
}
}
return null;
} finally {
if (t == null || t.endKind == HtmlTag.EndKind.NONE)
tagStack.pop();
}
}
private void checkHeader(StartElementTree tree, HtmlTag tag) {
// verify the new tag
if (getHeaderLevel(tag) > getHeaderLevel(currHeaderTag) + 1) {
if (currHeaderTag == null) {
env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.1", tag);
} else {
env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.2",
tag, currHeaderTag);
}
}
currHeaderTag = tag;
}
private int getHeaderLevel(HtmlTag tag) {
if (tag == null)
return 0;
switch (tag) {
case H1: return 1;
case H2: return 2;
case H3: return 3;
case H4: return 4;
case H5: return 5;
case H6: return 6;
default: throw new IllegalArgumentException();
}
}
@Override
public Void visitEndElement(EndElementTree tree, Void ignore) {
final Name treeName = tree.getName();
final HtmlTag t = HtmlTag.get(treeName);
if (t == null) {
env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
} else if (t.endKind == HtmlTag.EndKind.NONE) {
env.messages.error(HTML, tree, "dc.tag.end.not.permitted", treeName);
} else if (tagStack.isEmpty()) {
env.messages.error(HTML, tree, "dc.tag.end.unexpected", treeName);
} else {
while (!tagStack.isEmpty()) {
TagStackItem top = tagStack.peek();
if (t == top.tag) {
switch (t) {
case TABLE:
if (!top.attrs.contains(HtmlTag.Attr.SUMMARY)
&& !top.flags.contains(Flag.TABLE_HAS_CAPTION)) {
env.messages.error(ACCESSIBILITY, tree,
"dc.no.summary.or.caption.for.table");
}
}
if (t.flags.contains(HtmlTag.Flag.EXPECT_CONTENT)
&& !top.flags.contains(Flag.HAS_TEXT)
&& !top.flags.contains(Flag.HAS_ELEMENT)) {
env.messages.warning(HTML, tree, "dc.tag.empty", treeName);
}
if (t.flags.contains(HtmlTag.Flag.NO_TEXT)
&& top.flags.contains(Flag.HAS_TEXT)) {
env.messages.error(HTML, tree, "dc.text.not.allowed", treeName);
}
tagStack.pop();
break;
} else if (top.tag == null || top.tag.endKind != HtmlTag.EndKind.REQUIRED) {
tagStack.pop();
} else {
boolean found = false;
for (TagStackItem si: tagStack) {
if (si.tag == t) {
found = true;
break;
}
}
if (found && top.tree.getKind() == DocTree.Kind.START_ELEMENT) {
env.messages.error(HTML, top.tree, "dc.tag.start.unmatched",
((StartElementTree) top.tree).getName());
tagStack.pop();
} else {
env.messages.error(HTML, tree, "dc.tag.end.unexpected", treeName);
break;
}
}
}
}
return super.visitEndElement(tree, ignore);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="HTML attributes">
@Override @SuppressWarnings("fallthrough")
public Void visitAttribute(AttributeTree tree, Void ignore) {
HtmlTag currTag = tagStack.peek().tag;
if (currTag != null) {
Name name = tree.getName();
HtmlTag.Attr attr = currTag.getAttr(name);
if (attr != null) {
boolean first = tagStack.peek().attrs.add(attr);
if (!first)
env.messages.error(HTML, tree, "dc.attr.repeated", name);
}
AttrKind k = currTag.getAttrKind(name);
switch (k) {
case OK:
break;
case INVALID:
env.messages.error(HTML, tree, "dc.attr.unknown", name);
break;
case OBSOLETE:
env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name);
break;
case USE_CSS:
env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name);
break;
}
if (attr != null) {
switch (attr) {
case NAME:
if (currTag != HtmlTag.A) {
break;
}
// fallthrough
case ID:
String value = getAttrValue(tree);
if (!validName.matcher(value).matches()) {
env.messages.error(HTML, tree, "dc.invalid.anchor", value);
}
if (!foundAnchors.add(value)) {
env.messages.error(HTML, tree, "dc.anchor.already.defined", value);
}
break;
case HREF:
if (currTag == HtmlTag.A) {
String v = getAttrValue(tree);
if (v == null || v.isEmpty()) {
env.messages.error(HTML, tree, "dc.attr.lacks.value");
} else {
Matcher m = docRoot.matcher(v);
if (m.matches()) {
String rest = m.group(2);
if (!rest.isEmpty())
checkURI(tree, rest);
} else {
checkURI(tree, v);
}
}
}
break;
}
}
}
// TODO: basic check on value
return super.visitAttribute(tree, ignore);
}
// http://www.w3.org/TR/html401/types.html#type-name
private static final Pattern validName = Pattern.compile("[A-Za-z][A-Za-z0-9-_:.]*");
// pattern to remove leading {@docRoot}/?
private static final Pattern docRoot = Pattern.compile("(?i)(\\{@docRoot *\\}/?)?(.*)");
private String getAttrValue(AttributeTree tree) {
if (tree.getValue() == null)
return null;
StringWriter sw = new StringWriter();
try {
new DocPretty(sw).print(tree.getValue());
} catch (IOException e) {
// cannot happen
}
// ignore potential use of entities for now
return sw.toString();
}
private void checkURI(AttributeTree tree, String uri) {
try {
URI u = new URI(uri);
} catch (URISyntaxException e) {
env.messages.error(HTML, tree, "dc.invalid.uri", uri);
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="javadoc tags">
@Override
public Void visitAuthor(AuthorTree tree, Void ignore) {
warnIfEmpty(tree, tree.getName());
return super.visitAuthor(tree, ignore);
}
@Override
public Void visitInheritDoc(InheritDocTree tree, Void ignore) {
// TODO: verify on overridden method
foundInheritDoc = true;
return super.visitInheritDoc(tree, ignore);
}
@Override
public Void visitLink(LinkTree tree, Void ignore) {
// simulate inline context on tag stack
HtmlTag t = (tree.getKind() == DocTree.Kind.LINK)
? HtmlTag.CODE : HtmlTag.SPAN;
tagStack.push(new TagStackItem(tree, t));
try {
return super.visitLink(tree, ignore);
} finally {
tagStack.pop();
}
}
@Override
public Void visitParam(ParamTree tree, Void ignore) {
boolean typaram = tree.isTypeParameter();
IdentifierTree nameTree = tree.getName();
Element e = env.currElement;
switch (e.getKind()) {
case METHOD: case CONSTRUCTOR: {
ExecutableElement ee = (ExecutableElement) e;
checkParamDeclared(nameTree, typaram ? ee.getTypeParameters() : ee.getParameters());
break;
}
case CLASS: case INTERFACE: {
TypeElement te = (TypeElement) e;
if (typaram) {
checkParamDeclared(nameTree, te.getTypeParameters());
} else {
env.messages.error(REFERENCE, tree, "dc.invalid.param");
}
break;
}
default:
env.messages.error(REFERENCE, tree, "dc.invalid.param");
break;
}
warnIfEmpty(tree, tree.getDescription());
return super.visitParam(tree, ignore);
}
// where
private void checkParamDeclared(IdentifierTree nameTree, List<? extends Element> list) {
Name name = nameTree.getName();
boolean found = false;
for (Element e: list) {
if (name.equals(e.getSimpleName())) {
foundParams.add(e);
found = true;
}
}
if (!found)
env.messages.error(REFERENCE, nameTree, "dc.param.name.not.found");
}
private void checkParamsDocumented(List<? extends Element> list) {
if (foundInheritDoc)
return;
for (Element e: list) {
if (!foundParams.contains(e)) {
CharSequence paramName = (e.getKind() == ElementKind.TYPE_PARAMETER)
? "<" + e.getSimpleName() + ">"
: e.getSimpleName();
reportMissing("dc.missing.param", paramName);
}
}
}
@Override
public Void visitReference(ReferenceTree tree, Void ignore) {
Element e = env.trees.getElement(env.currPath, tree);
if (e == null)
env.messages.error(REFERENCE, tree, "dc.ref.not.found");
return super.visitReference(tree, ignore);
}
@Override
public Void visitReturn(ReturnTree tree, Void ignore) {
Element e = env.trees.getElement(env.currPath);
if (e.getKind() != ElementKind.METHOD
|| ((ExecutableElement) e).getReturnType().getKind() == TypeKind.VOID)
env.messages.error(REFERENCE, tree, "dc.invalid.return");
foundReturn = true;
warnIfEmpty(tree, tree.getDescription());
return super.visitReturn(tree, ignore);
}
@Override
public Void visitSerialData(SerialDataTree tree, Void ignore) {
warnIfEmpty(tree, tree.getDescription());
return super.visitSerialData(tree, ignore);
}
@Override
public Void visitSerialField(SerialFieldTree tree, Void ignore) {
warnIfEmpty(tree, tree.getDescription());
return super.visitSerialField(tree, ignore);
}
@Override
public Void visitSince(SinceTree tree, Void ignore) {
warnIfEmpty(tree, tree.getBody());
return super.visitSince(tree, ignore);
}
@Override
public Void visitThrows(ThrowsTree tree, Void ignore) {
ReferenceTree exName = tree.getExceptionName();
Element ex = env.trees.getElement(env.currPath, exName);
if (ex == null) {
env.messages.error(REFERENCE, tree, "dc.ref.not.found");
} else if (ex.asType().getKind() == TypeKind.DECLARED
&& env.types.isAssignable(ex.asType(), env.java_lang_Throwable)) {
switch (env.currElement.getKind()) {
case CONSTRUCTOR:
case METHOD:
if (isCheckedException(ex.asType())) {
ExecutableElement ee = (ExecutableElement) env.currElement;
checkThrowsDeclared(exName, ex.asType(), ee.getThrownTypes());
}
break;
default:
env.messages.error(REFERENCE, tree, "dc.invalid.throws");
}
} else {
env.messages.error(REFERENCE, tree, "dc.invalid.throws");
}
warnIfEmpty(tree, tree.getDescription());
return scan(tree.getDescription(), ignore);
}
private void checkThrowsDeclared(ReferenceTree tree, TypeMirror t, List<? extends TypeMirror> list) {
boolean found = false;
for (TypeMirror tl : list) {
if (env.types.isAssignable(t, tl)) {
foundThrows.add(tl);
found = true;
}
}
if (!found)
env.messages.error(REFERENCE, tree, "dc.exception.not.thrown", t);
}
private void checkThrowsDocumented(List<? extends TypeMirror> list) {
if (foundInheritDoc)
return;
for (TypeMirror tl: list) {
if (isCheckedException(tl) && !foundThrows.contains(tl))
reportMissing("dc.missing.throws", tl);
}
}
@Override
public Void visitVersion(VersionTree tree, Void ignore) {
warnIfEmpty(tree, tree.getBody());
return super.visitVersion(tree, ignore);
}
@Override
public Void visitErroneous(ErroneousTree tree, Void ignore) {
env.messages.error(SYNTAX, tree, null, tree.getDiagnostic().getMessage(null));
return null;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Utility methods">
private boolean isCheckedException(TypeMirror t) {
return !(env.types.isAssignable(t, env.java_lang_Error)
|| env.types.isAssignable(t, env.java_lang_RuntimeException));
}
private boolean isSynthetic() {
switch (env.currElement.getKind()) {
case CONSTRUCTOR:
// A synthetic default constructor has the same pos as the
// enclosing class
TreePath p = env.currPath;
return env.getPos(p) == env.getPos(p.getParentPath());
}
return false;
}
void markEnclosingTag(Flag flag) {
TagStackItem top = tagStack.peek();
if (top != null)
top.flags.add(flag);
}
String toString(TreePath p) {
StringBuilder sb = new StringBuilder("TreePath[");
toString(p, sb);
sb.append("]");
return sb.toString();
}
void toString(TreePath p, StringBuilder sb) {
TreePath parent = p.getParentPath();
if (parent != null) {
toString(parent, sb);
sb.append(",");
}
sb.append(p.getLeaf().getKind()).append(":").append(env.getPos(p)).append(":S").append(env.getStartPos(p));
}
void warnIfEmpty(DocTree tree, List<? extends DocTree> list) {
for (DocTree d: list) {
switch (d.getKind()) {
case TEXT:
if (!((TextTree) d).getBody().trim().isEmpty())
return;
break;
default:
return;
}
}
env.messages.warning(SYNTAX, tree, "dc.empty", tree.getKind().tagName);
}
// </editor-fold>
}

View File

@ -0,0 +1,376 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Name;
import javax.tools.StandardLocation;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.util.Context;
/**
* Multi-function entry point for the doc check utility.
*
* This class can be invoked in the following ways:
* <ul>
* <li>From the command line
* <li>From javac, as a plugin
* <li>Directly, via a simple API
* </ul>
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class DocLint implements Plugin {
public static final String XMSGS_OPTION = "-Xmsgs";
public static final String XMSGS_CUSTOM_PREFIX = "-Xmsgs:";
private static final String STATS = "-stats";
// <editor-fold defaultstate="collapsed" desc="Command-line entry point">
public static void main(String... args) {
try {
new DocLint().run(args);
} catch (BadArgs e) {
System.err.println(e.getMessage());
System.exit(1);
} catch (IOException e) {
System.err.println(e);
System.exit(2);
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Simple API">
public static class BadArgs extends Exception {
private static final long serialVersionUID = 0;
BadArgs(String code, Object... args) {
this.code = code;
this.args = args;
}
final String code;
final Object[] args;
}
/**
* Simple API entry point.
*/
public void run(String... args) throws BadArgs, IOException {
PrintWriter out = new PrintWriter(System.out);
try {
run(out, args);
} finally {
out.flush();
}
}
public void run(PrintWriter out, String... args) throws BadArgs, IOException {
env = new Env();
processArgs(args);
if (needHelp)
showHelp(out);
if (javacFiles.isEmpty()) {
if (!needHelp)
System.out.println("no files given");
}
JavacTool tool = JavacTool.create();
JavacFileManager fm = new JavacFileManager(new Context(), false, null);
fm.setSymbolFileEnabled(false);
fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath);
fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath);
fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath);
JavacTask task = tool.getTask(out, fm, null, javacOpts, null,
fm.getJavaFileObjectsFromFiles(javacFiles));
Iterable<? extends CompilationUnitTree> units = task.parse();
((JavacTaskImpl) task).enter();
env.init(task);
checker = new Checker(env);
DeclScanner ds = new DeclScanner() {
@Override
void visitDecl(Tree tree, Name name) {
TreePath p = getCurrentPath();
DocCommentTree dc = env.trees.getDocCommentTree(p);
checker.scan(dc, p);
}
};
ds.scan(units, null);
reportStats(out);
Context ctx = ((JavacTaskImpl) task).getContext();
JavaCompiler c = JavaCompiler.instance(ctx);
c.printCount("error", c.errorCount());
c.printCount("warn", c.warningCount());
}
void processArgs(String... args) throws BadArgs {
javacOpts = new ArrayList<String>();
javacFiles = new ArrayList<File>();
if (args.length == 0)
needHelp = true;
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.matches("-Xmax(errs|warns)") && i + 1 < args.length) {
if (args[++i].matches("[0-9]+")) {
javacOpts.add(arg);
javacOpts.add(args[i]);
} else {
throw new BadArgs("dc.bad.value.for.option", arg, args[i]);
}
} else if (arg.equals(STATS)) {
env.messages.setStatsEnabled(true);
} else if (arg.matches("-bootclasspath") && i + 1 < args.length) {
javacBootClassPath = splitPath(args[++i]);
} else if (arg.matches("-classpath") && i + 1 < args.length) {
javacClassPath = splitPath(args[++i]);
} else if (arg.matches("-sourcepath") && i + 1 < args.length) {
javacSourcePath = splitPath(args[++i]);
} else if (arg.equals(XMSGS_OPTION)) {
env.messages.setOptions(null);
} else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) {
env.messages.setOptions(arg.substring(arg.indexOf(":") + 1));
} else if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")
|| arg.equals("-?") || arg.equals("-usage")) {
needHelp = true;
} else if (arg.startsWith("-")) {
throw new BadArgs("dc.bad.option", arg);
} else {
while (i < args.length)
javacFiles.add(new File(args[i++]));
}
}
}
void showHelp(PrintWriter out) {
out.println("Usage:");
out.println(" doclint [options] source-files...");
out.println("");
out.println("Options:");
out.println(" -Xmsgs ");
out.println(" Same as -Xmsgs:all");
out.println(" -Xmsgs:values");
out.println(" Specify categories of issues to be checked, where 'values'");
out.println(" is a comma-separated list of any of the following:");
out.println(" reference show places where comments contain incorrect");
out.println(" references to Java source code elements");
out.println(" syntax show basic syntax errors within comments");
out.println(" html show issues with HTML tags and attributes");
out.println(" accessibility show issues for accessibility");
out.println(" missing show issues with missing documentation");
out.println(" all all of the above");
out.println(" Precede a value with '-' to negate it");
out.println(" Categories may be qualified by one of:");
out.println(" /public /protected /package /private");
out.println(" For positive categories (not beginning with '-')");
out.println(" the qualifier applies to that access level and above.");
out.println(" For negative categories (beginning with '-')");
out.println(" the qualifier applies to that access level and below.");
out.println(" If a qualifier is missing, the category applies to");
out.println(" all access levels.");
out.println(" For example, -Xmsgs:all,-syntax/private");
out.println(" This will enable all messages, except syntax errors");
out.println(" in the doc comments of private methods.");
out.println(" If no -Xmsgs options are provided, the default is");
out.println(" equivalent to -Xmsgs:all/protected, meaning that");
out.println(" all messages are reported for protected and public");
out.println(" declarations only. ");
out.println(" -h -help --help -usage -?");
out.println(" Show this message.");
out.println("");
out.println("The following javac options are also supported");
out.println(" -bootclasspath, -classpath, -sourcepath, -Xmaxerrs, -Xmaxwarns");
out.println("");
out.println("To run doclint on part of a project, put the compiled classes for your");
out.println("project on the classpath (or bootclasspath), then specify the source files");
out.println("to be checked on the command line.");
}
List<File> splitPath(String path) {
List<File> files = new ArrayList<File>();
for (String f: path.split(File.separator)) {
if (f.length() > 0)
files.add(new File(f));
}
return files;
}
List<File> javacBootClassPath;
List<File> javacClassPath;
List<File> javacSourcePath;
List<String> javacOpts;
List<File> javacFiles;
boolean needHelp = false;
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="javac Plugin">
@Override
public String getName() {
return "doclint";
}
@Override
public void call(JavacTask task, String... args) {
init(task, args, true);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Embedding API">
public void init(JavacTask task, String[] args, boolean addTaskListener) {
env = new Env();
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.equals(XMSGS_OPTION)) {
env.messages.setOptions(null);
} else if (arg.startsWith(XMSGS_CUSTOM_PREFIX)) {
env.messages.setOptions(arg.substring(arg.indexOf(":") + 1));
} else
throw new IllegalArgumentException(arg);
}
env.init(task);
checker = new Checker(env);
if (addTaskListener) {
final DeclScanner ds = new DeclScanner() {
@Override
void visitDecl(Tree tree, Name name) {
TreePath p = getCurrentPath();
DocCommentTree dc = env.trees.getDocCommentTree(p);
checker.scan(dc, p);
}
};
TaskListener tl = new TaskListener() {
@Override
public void started(TaskEvent e) {
return;
}
@Override
public void finished(TaskEvent e) {
switch (e.getKind()) {
case ENTER:
ds.scan(e.getCompilationUnit(), null);
}
}
};
task.addTaskListener(tl);
}
}
public void scan(TreePath p) {
DocCommentTree dc = env.trees.getDocCommentTree(p);
checker.scan(dc, p);
}
public void reportStats(PrintWriter out) {
env.messages.reportStats(out);
}
// </editor-fold>
Env env;
Checker checker;
public static boolean isValidOption(String opt) {
if (opt.equals(XMSGS_OPTION))
return true;
if (opt.startsWith(XMSGS_CUSTOM_PREFIX))
return Messages.Options.isValidOptions(opt.substring(XMSGS_CUSTOM_PREFIX.length()));
return false;
}
// <editor-fold defaultstate="collapsed" desc="DeclScanner">
static abstract class DeclScanner extends TreePathScanner<Void, Void> {
abstract void visitDecl(Tree tree, Name name);
@Override
public Void visitClass(ClassTree tree, Void ignore) {
visitDecl(tree, tree.getSimpleName());
return super.visitClass(tree, ignore);
}
@Override
public Void visitMethod(MethodTree tree, Void ignore) {
visitDecl(tree, tree.getName());
//return super.visitMethod(tree, ignore);
return null;
}
@Override
public Void visitVariable(VariableTree tree, Void ignore) {
visitDecl(tree, tree.getName());
return super.visitVariable(tree, ignore);
}
}
// </editor-fold>
}

View File

@ -0,0 +1,326 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.util.HashMap;
import java.util.Map;
/**
* Table of entities defined in HTML 4.01.
*
* <p> Derived from
* <a href="http://www.w3.org/TR/html4/sgml/entities.html">Character entity references in HTML 4</a>.
*
* The name of the member follows the name of the entity,
* except when it clashes with a keyword, in which case
* it is prefixed by '_'.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
enum Entity {
nbsp(160),
iexcl(161),
cent(162),
pound(163),
curren(164),
yen(165),
brvbar(166),
sect(167),
uml(168),
copy(169),
ordf(170),
laquo(171),
not(172),
shy(173),
reg(174),
macr(175),
deg(176),
plusmn(177),
sup2(178),
sup3(179),
acute(180),
micro(181),
para(182),
middot(183),
cedil(184),
sup1(185),
ordm(186),
raquo(187),
frac14(188),
frac12(189),
frac34(190),
iquest(191),
Agrave(192),
Aacute(193),
Acirc(194),
Atilde(195),
Auml(196),
Aring(197),
AElig(198),
Ccedil(199),
Egrave(200),
Eacute(201),
Ecirc(202),
Euml(203),
Igrave(204),
Iacute(205),
Icirc(206),
Iuml(207),
ETH(208),
Ntilde(209),
Ograve(210),
Oacute(211),
Ocirc(212),
Otilde(213),
Ouml(214),
times(215),
Oslash(216),
Ugrave(217),
Uacute(218),
Ucirc(219),
Uuml(220),
Yacute(221),
THORN(222),
szlig(223),
agrave(224),
aacute(225),
acirc(226),
atilde(227),
auml(228),
aring(229),
aelig(230),
ccedil(231),
egrave(232),
eacute(233),
ecirc(234),
euml(235),
igrave(236),
iacute(237),
icirc(238),
iuml(239),
eth(240),
ntilde(241),
ograve(242),
oacute(243),
ocirc(244),
otilde(245),
ouml(246),
divide(247),
oslash(248),
ugrave(249),
uacute(250),
ucirc(251),
uuml(252),
yacute(253),
thorn(254),
yuml(255),
fnof(402),
Alpha(913),
Beta(914),
Gamma(915),
Delta(916),
Epsilon(917),
Zeta(918),
Eta(919),
Theta(920),
Iota(921),
Kappa(922),
Lambda(923),
Mu(924),
Nu(925),
Xi(926),
Omicron(927),
Pi(928),
Rho(929),
Sigma(931),
Tau(932),
Upsilon(933),
Phi(934),
Chi(935),
Psi(936),
Omega(937),
alpha(945),
beta(946),
gamma(947),
delta(948),
epsilon(949),
zeta(950),
eta(951),
theta(952),
iota(953),
kappa(954),
lambda(955),
mu(956),
nu(957),
xi(958),
omicron(959),
pi(960),
rho(961),
sigmaf(962),
sigma(963),
tau(964),
upsilon(965),
phi(966),
chi(967),
psi(968),
omega(969),
thetasym(977),
upsih(978),
piv(982),
bull(8226),
hellip(8230),
prime(8242),
Prime(8243),
oline(8254),
frasl(8260),
weierp(8472),
image(8465),
real(8476),
trade(8482),
alefsym(8501),
larr(8592),
uarr(8593),
rarr(8594),
darr(8595),
harr(8596),
crarr(8629),
lArr(8656),
uArr(8657),
rArr(8658),
dArr(8659),
hArr(8660),
forall(8704),
part(8706),
exist(8707),
empty(8709),
nabla(8711),
isin(8712),
notin(8713),
ni(8715),
prod(8719),
sum(8721),
minus(8722),
lowast(8727),
radic(8730),
prop(8733),
infin(8734),
ang(8736),
and(8743),
or(8744),
cap(8745),
cup(8746),
_int(8747),
there4(8756),
sim(8764),
cong(8773),
asymp(8776),
ne(8800),
equiv(8801),
le(8804),
ge(8805),
sub(8834),
sup(8835),
nsub(8836),
sube(8838),
supe(8839),
oplus(8853),
otimes(8855),
perp(8869),
sdot(8901),
lceil(8968),
rceil(8969),
lfloor(8970),
rfloor(8971),
lang(9001),
rang(9002),
loz(9674),
spades(9824),
clubs(9827),
hearts(9829),
diams(9830),
quot(34),
amp(38),
lt(60),
gt(62),
OElig(338),
oelig(339),
Scaron(352),
scaron(353),
Yuml(376),
circ(710),
tilde(732),
ensp(8194),
emsp(8195),
thinsp(8201),
zwnj(8204),
zwj(8205),
lrm(8206),
rlm(8207),
ndash(8211),
mdash(8212),
lsquo(8216),
rsquo(8217),
sbquo(8218),
ldquo(8220),
rdquo(8221),
bdquo(8222),
dagger(8224),
Dagger(8225),
permil(8240),
lsaquo(8249),
rsaquo(8250),
euro(8364);
int code;
private Entity(int code) {
this.code = code;
}
static boolean isValid(String name) {
return names.containsKey(name);
}
static boolean isValid(int code) {
// allow numeric codes for standard ANSI characters
return codes.containsKey(code) || ( 32 <= code && code < 2127);
}
private static final Map<String,Entity> names = new HashMap<String,Entity>();
private static final Map<Integer,Entity> codes = new HashMap<Integer,Entity>();
static {
for (Entity e: values()) {
String name = e.name();
int code = e.code;
if (name.startsWith("_")) name = name.substring(1);
names.put(name, e);
codes.put(code, e);
}
}
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
/**
* Utility container for current execution environment,
* providing the current declaration and its doc comment.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class Env {
/**
* Access kinds for declarations.
*/
public enum AccessKind {
PRIVATE,
PACKAGE,
PROTECTED,
PUBLIC;
static boolean accepts(String opt) {
for (AccessKind g: values())
if (opt.equals(g.name().toLowerCase())) return true;
return false;
}
static AccessKind of(Set<Modifier> mods) {
if (mods.contains(Modifier.PUBLIC))
return AccessKind.PUBLIC;
else if (mods.contains(Modifier.PROTECTED))
return AccessKind.PROTECTED;
else if (mods.contains(Modifier.PRIVATE))
return AccessKind.PRIVATE;
else
return AccessKind.PACKAGE;
}
};
/** Message handler. */
final Messages messages;
// Utility classes
DocTrees trees;
Elements elements;
Types types;
// Types used when analysing doc comments.
TypeMirror java_lang_Error;
TypeMirror java_lang_RuntimeException;
TypeMirror java_lang_Throwable;
TypeMirror java_lang_Void;
/** The path for the declaration containing the comment currently being analyzed. */
TreePath currPath;
/** The element for the declaration containing the comment currently being analyzed. */
Element currElement;
/** The comment current being analyzed. */
DocCommentTree currDocComment;
/**
* The access kind of the declaration containing the comment currently being analyzed.
* This is the minimum (most restrictive) access kind of the declaration iteself
* and that of its containers. For example, a public method in a private class is
* noted as private.
*/
AccessKind currAccess;
/** The set of methods, if any, that the current declaration overrides. */
Set<? extends ExecutableElement> currOverriddenMethods;
Env() {
messages = new Messages(this);
}
void init(JavacTask task) {
init(DocTrees.instance(task), task.getElements(), task.getTypes());
}
void init(DocTrees trees, Elements elements, Types types) {
this.trees = trees;
this.elements = elements;
this.types = types;
java_lang_Error = elements.getTypeElement("java.lang.Error").asType();
java_lang_RuntimeException = elements.getTypeElement("java.lang.RuntimeException").asType();
java_lang_Throwable = elements.getTypeElement("java.lang.Throwable").asType();
java_lang_Void = elements.getTypeElement("java.lang.Void").asType();
}
/** Set the current declaration and its doc comment. */
void setCurrent(TreePath path, DocCommentTree comment) {
currPath = path;
currDocComment = comment;
currElement = trees.getElement(currPath);
currOverriddenMethods = ((JavacTypes) types).getOverriddenMethods(currElement);
AccessKind ak = null;
for (TreePath p = path; p != null; p = p.getParentPath()) {
Element e = trees.getElement(p);
if (e != null) {
ak = min(ak, AccessKind.of(e.getModifiers()));
}
}
currAccess = ak;
}
AccessKind getAccessKind() {
return currAccess;
}
long getPos(TreePath p) {
return ((JCTree) p.getLeaf()).pos;
}
long getStartPos(TreePath p) {
SourcePositions sp = trees.getSourcePositions();
return sp.getStartPosition(p.getCompilationUnit(), p.getLeaf());
}
private <T extends Comparable<T>> T min(T item1, T item2) {
return (item1 == null) ? item2
: (item2 == null) ? item1
: item1.compareTo(item2) <= 0 ? item1 : item2;
}
}

View File

@ -0,0 +1,356 @@
/*
* Copyright (c) 2010, 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.util.Set;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.lang.model.element.Name;
import static com.sun.tools.doclint.HtmlTag.Attr.*;
/**
* Enum representing HTML tags.
*
* The intent of this class is to embody the semantics of W3C HTML 4.01
* to the extent supported/used by javadoc.
*
* This is derivative of com.sun.tools.doclets.formats.html.markup.HtmlTag.
* Eventually, these two should be merged back together, and possibly made
* public.
*
* @see <a href="http://www.w3.org/TR/REC-html40/">HTML 4.01 Specification</a>
* @author Bhavesh Patel
* @author Jonathan Gibbons (revised)
*/
public enum HtmlTag {
A(BlockType.INLINE, EndKind.REQUIRED,
attrs(AttrKind.OK, HREF, TARGET, NAME)),
B(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
BLOCKQUOTE,
BODY(BlockType.OTHER, EndKind.REQUIRED),
BR(BlockType.INLINE, EndKind.NONE,
attrs(AttrKind.USE_CSS, CLEAR)),
CAPTION(EnumSet.of(Flag.EXPECT_CONTENT)),
CENTER,
CITE(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
CODE(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
DD(BlockType.BLOCK, EndKind.OPTIONAL,
EnumSet.of(Flag.EXPECT_CONTENT)),
DIV,
DL(BlockType.BLOCK, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
attrs(AttrKind.USE_CSS, COMPACT)),
DT(BlockType.BLOCK, EndKind.OPTIONAL,
EnumSet.of(Flag.EXPECT_CONTENT)),
EM(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.NO_NEST)),
FONT(BlockType.INLINE, EndKind.REQUIRED, // tag itself is deprecated
EnumSet.of(Flag.EXPECT_CONTENT),
attrs(AttrKind.USE_CSS, SIZE, COLOR, FACE)),
FRAME(BlockType.OTHER, EndKind.NONE),
FRAMESET(BlockType.OTHER, EndKind.REQUIRED),
H1,
H2,
H3,
H4,
H5,
H6,
HEAD(BlockType.OTHER, EndKind.REQUIRED),
HR(BlockType.BLOCK, EndKind.NONE),
HTML(BlockType.OTHER, EndKind.REQUIRED),
I(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
IMG(BlockType.INLINE, EndKind.NONE,
attrs(AttrKind.OK, SRC, ALT, HEIGHT, WIDTH),
attrs(AttrKind.OBSOLETE, NAME),
attrs(AttrKind.USE_CSS, ALIGN, HSPACE, VSPACE, BORDER)),
LI(BlockType.BLOCK, EndKind.OPTIONAL),
LINK(BlockType.OTHER, EndKind.NONE),
MENU,
META(BlockType.OTHER, EndKind.NONE),
NOFRAMES(BlockType.OTHER, EndKind.REQUIRED),
NOSCRIPT(BlockType.OTHER, EndKind.REQUIRED),
OL(BlockType.BLOCK, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
attrs(AttrKind.USE_CSS, START, TYPE)),
P(BlockType.BLOCK, EndKind.OPTIONAL,
EnumSet.of(Flag.EXPECT_CONTENT),
attrs(AttrKind.USE_CSS, ALIGN)),
PRE(EnumSet.of(Flag.EXPECT_CONTENT)),
SCRIPT(BlockType.OTHER, EndKind.REQUIRED),
SMALL(BlockType.INLINE, EndKind.REQUIRED),
SPAN(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT)),
STRONG(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT)),
SUB(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
SUP(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
TABLE(BlockType.BLOCK, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
attrs(AttrKind.OK, SUMMARY, Attr.FRAME, RULES, BORDER,
CELLPADDING, CELLSPACING),
attrs(AttrKind.USE_CSS, ALIGN, WIDTH, BGCOLOR)),
TBODY(BlockType.BLOCK, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)),
TD(BlockType.BLOCK, EndKind.OPTIONAL,
attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, ABBR, AXIS,
ALIGN, CHAR, CHAROFF, VALIGN),
attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
TFOOT(BlockType.BLOCK, EndKind.REQUIRED,
attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)),
TH(BlockType.BLOCK, EndKind.OPTIONAL,
attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, ABBR, AXIS,
ALIGN, CHAR, CHAROFF, VALIGN),
attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
THEAD(BlockType.BLOCK, EndKind.REQUIRED,
attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)),
TITLE(BlockType.OTHER, EndKind.REQUIRED),
TR(BlockType.BLOCK, EndKind.OPTIONAL,
EnumSet.of(Flag.NO_TEXT),
attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN),
attrs(AttrKind.USE_CSS, BGCOLOR)),
TT(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
U(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
UL(BlockType.BLOCK, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
attrs(AttrKind.USE_CSS, COMPACT, TYPE)),
VAR(BlockType.INLINE, EndKind.REQUIRED);
/**
* Enum representing the type of HTML element.
*/
public static enum BlockType {
BLOCK,
INLINE,
OTHER;
}
/**
* Enum representing HTML end tag requirement.
*/
public static enum EndKind {
NONE,
OPTIONAL,
REQUIRED;
}
public static enum Flag {
EXPECT_CONTENT,
NO_NEST,
NO_TEXT
}
public static enum Attr {
ABBR,
ALIGN,
ALT,
AXIS,
BGCOLOR,
BORDER,
CELLSPACING,
CELLPADDING,
CHAR,
CHAROFF,
CLEAR,
CLASS,
COLOR,
COLSPAN,
COMPACT,
FACE,
FRAME,
HEADERS,
HEIGHT,
HREF,
HSPACE,
ID,
NAME,
NOWRAP,
REVERSED,
ROWSPAN,
RULES,
SCOPE,
SIZE,
SPACE,
SRC,
START,
STYLE,
SUMMARY,
TARGET,
TYPE,
VALIGN,
VSPACE,
WIDTH;
public String getText() {
return name().toLowerCase();
}
static final Map<String,Attr> index = new HashMap<String,Attr>();
static {
for (Attr t: values()) {
index.put(t.name().toLowerCase(), t);
}
}
}
public static enum AttrKind {
INVALID,
OBSOLETE,
USE_CSS,
OK
}
// This class exists to avoid warnings from using parameterized vararg type
// Map<Attr,AttrKind> in signature of HtmlTag constructor.
private static class AttrMap extends EnumMap<Attr,AttrKind> {
private static final long serialVersionUID = 0;
AttrMap() {
super(Attr.class);
}
}
public final BlockType blockType;
public final EndKind endKind;
public final Set<Flag> flags;
private final Map<Attr,AttrKind> attrs;
HtmlTag() {
this(BlockType.BLOCK, EndKind.REQUIRED);
}
HtmlTag(Set<Flag> flags) {
this(BlockType.BLOCK, EndKind.REQUIRED, flags);
}
HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) {
this(blockType, endKind, Collections.<Flag>emptySet(), attrMaps);
}
HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) {
this.blockType = blockType;
this.endKind = endKind;this.flags = flags;
this.attrs = new EnumMap<Attr,AttrKind>(Attr.class);
for (Map<Attr,AttrKind> m: attrMaps)
this.attrs.putAll(m);
attrs.put(Attr.CLASS, AttrKind.OK);
attrs.put(Attr.ID, AttrKind.OK);
attrs.put(Attr.STYLE, AttrKind.OK);
}
public String getText() {
return name().toLowerCase();
}
public Attr getAttr(Name attrName) {
return Attr.index.get(attrName.toString().toLowerCase());
}
public AttrKind getAttrKind(Name attrName) {
AttrKind k = attrs.get(getAttr(attrName)); // null-safe
return (k == null) ? AttrKind.INVALID : k;
}
private static AttrMap attrs(AttrKind k, Attr... attrs) {
AttrMap map = new AttrMap();
for (Attr a: attrs) map.put(a, k);
return map;
}
private static final Map<String,HtmlTag> index = new HashMap<String,HtmlTag>();
static {
for (HtmlTag t: values()) {
index.put(t.name().toLowerCase(), t);
}
}
static HtmlTag get(Name tagName) {
return index.get(tagName.toString().toLowerCase());
}
}

View File

@ -0,0 +1,348 @@
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.doclint;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.tools.Diagnostic;
import com.sun.source.doctree.DocTree;
import com.sun.source.tree.Tree;
import com.sun.tools.doclint.Env.AccessKind;
/**
* Message reporting for DocLint.
*
* Options are used to filter out messages based on group and access level.
* Support can be enabled for accumulating statistics of different kinds of
* messages.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own
* risk. This code and its internal interfaces are subject to change
* or deletion without notice.</b></p>
*/
public class Messages {
/**
* Groups used to categorize messages, so that messages in each group
* can be enabled or disabled via options.
*/
public enum Group {
ACCESSIBILITY,
HTML,
MISSING,
SYNTAX,
REFERENCE;
String optName() { return name().toLowerCase(); }
String notOptName() { return "-" + optName(); }
static boolean accepts(String opt) {
for (Group g: values())
if (opt.equals(g.optName())) return true;
return false;
}
};
private final Options options;
private final Stats stats;
ResourceBundle bundle;
Env env;
Messages(Env env) {
this.env = env;
String name = getClass().getPackage().getName() + ".resources.doclint";
bundle = ResourceBundle.getBundle(name, Locale.ENGLISH);
stats = new Stats(bundle);
options = new Options(stats);
}
void error(Group group, DocTree tree, String code, Object... args) {
report(group, Diagnostic.Kind.ERROR, tree, code, args);
}
void warning(Group group, DocTree tree, String code, Object... args) {
report(group, Diagnostic.Kind.WARNING, tree, code, args);
}
void setOptions(String opts) {
options.setOptions(opts);
}
void setStatsEnabled(boolean b) {
stats.setEnabled(b);
}
void reportStats(PrintWriter out) {
stats.report(out);
}
protected void report(Group group, Diagnostic.Kind dkind, DocTree tree, String code, Object... args) {
if (options.isEnabled(group, env.currAccess)) {
String msg = (code == null) ? (String) args[0] : localize(code, args);
env.trees.printMessage(dkind, msg, tree,
env.currDocComment, env.currPath.getCompilationUnit());
stats.record(group, dkind, code);
}
}
protected void report(Group group, Diagnostic.Kind dkind, Tree tree, String code, Object... args) {
if (options.isEnabled(group, env.currAccess)) {
String msg = localize(code, args);
env.trees.printMessage(dkind, msg, tree, env.currPath.getCompilationUnit());
stats.record(group, dkind, code);
}
}
String localize(String code, Object... args) {
String msg = bundle.getString(code);
if (msg == null) {
StringBuilder sb = new StringBuilder();
sb.append("message file broken: code=").append(code);
if (args.length > 0) {
sb.append(" arguments={0}");
for (int i = 1; i < args.length; i++) {
sb.append(", {").append(i).append("}");
}
}
msg = sb.toString();
}
return MessageFormat.format(msg, args);
}
// <editor-fold defaultstate="collapsed" desc="Options">
/**
* Handler for (sub)options specific to message handling.
*/
static class Options {
Map<String, Env.AccessKind> map = new HashMap<String, Env.AccessKind>();
private final Stats stats;
static boolean isValidOptions(String opts) {
for (String opt: opts.split(",")) {
if (!isValidOption(opt.trim().toLowerCase()))
return false;
}
return true;
}
private static boolean isValidOption(String opt) {
if (opt.equals("none") || opt.equals(Stats.OPT))
return true;
int begin = opt.startsWith("-") ? 1 : 0;
int sep = opt.indexOf("/");
String grp = opt.substring(begin, (sep != -1) ? sep : opt.length());
return ((begin == 0 && grp.equals("all")) || Group.accepts(grp))
&& ((sep == -1) || AccessKind.accepts(opt.substring(sep + 1)));
}
Options(Stats stats) {
this.stats = stats;
}
/** Determine if a message group is enabled for a particular access level. */
boolean isEnabled(Group g, Env.AccessKind access) {
if (map.isEmpty())
map.put("all", Env.AccessKind.PROTECTED);
Env.AccessKind ak = map.get(g.optName());
if (ak != null && access.compareTo(ak) >= 0)
return true;
ak = map.get(ALL);
if (ak != null && access.compareTo(ak) >= 0) {
ak = map.get(g.notOptName());
if (ak == null || access.compareTo(ak) > 0) // note >, not >=
return true;
}
return false;
}
void setOptions(String opts) {
if (opts == null)
setOption(ALL, Env.AccessKind.PRIVATE);
else {
for (String opt: opts.split(","))
setOption(opt.trim().toLowerCase());
}
}
private void setOption(String arg) throws IllegalArgumentException {
if (arg.equals(Stats.OPT)) {
stats.setEnabled(true);
return;
}
int sep = arg.indexOf("/");
if (sep > 0) {
Env.AccessKind ak = Env.AccessKind.valueOf(arg.substring(sep + 1).toUpperCase());
setOption(arg.substring(0, sep), ak);
} else {
setOption(arg, null);
}
}
private void setOption(String opt, Env.AccessKind ak) {
map.put(opt, (ak != null) ? ak
: opt.startsWith("-") ? Env.AccessKind.PUBLIC : Env.AccessKind.PRIVATE);
}
private static final String ALL = "all";
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Statistics">
/**
* Optionally record statistics of different kinds of message.
*/
static class Stats {
public static final String OPT = "stats";
public static final String NO_CODE = "";
final ResourceBundle bundle;
// tables only initialized if enabled
int[] groupCounts;
int[] dkindCounts;
Map<String, Integer> codeCounts;
Stats(ResourceBundle bundle) {
this.bundle = bundle;
}
void setEnabled(boolean b) {
if (b) {
groupCounts = new int[Messages.Group.values().length];
dkindCounts = new int[Diagnostic.Kind.values().length];
codeCounts = new HashMap<String, Integer>();
} else {
groupCounts = null;
dkindCounts = null;
codeCounts = null;
}
}
void record(Messages.Group g, Diagnostic.Kind dkind, String code) {
if (codeCounts == null) {
return;
}
groupCounts[g.ordinal()]++;
dkindCounts[dkind.ordinal()]++;
if (code == null) {
code = NO_CODE;
}
Integer i = codeCounts.get(code);
codeCounts.put(code, (i == null) ? 1 : i + 1);
}
void report(PrintWriter out) {
if (codeCounts == null) {
return;
}
out.println("By group...");
Table groupTable = new Table();
for (Messages.Group g : Messages.Group.values()) {
groupTable.put(g.optName(), groupCounts[g.ordinal()]);
}
groupTable.print(out);
out.println();
out.println("By diagnostic kind...");
Table dkindTable = new Table();
for (Diagnostic.Kind k : Diagnostic.Kind.values()) {
dkindTable.put(k.toString().toLowerCase(), dkindCounts[k.ordinal()]);
}
dkindTable.print(out);
out.println();
out.println("By message kind...");
Table codeTable = new Table();
for (Map.Entry<String, Integer> e : codeCounts.entrySet()) {
String code = e.getKey();
String msg;
try {
msg = code.equals(NO_CODE) ? "OTHER" : bundle.getString(code);
} catch (MissingResourceException ex) {
msg = code;
}
codeTable.put(msg, e.getValue());
}
codeTable.print(out);
}
/**
* A table of (int, String) sorted by decreasing int.
*/
private static class Table {
private static final Comparator<Integer> DECREASING = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
};
private final TreeMap<Integer, Set<String>> map = new TreeMap<Integer, Set<String>>(DECREASING);
void put(String label, int n) {
if (n == 0) {
return;
}
Set<String> labels = map.get(n);
if (labels == null) {
map.put(n, labels = new TreeSet<String>());
}
labels.add(label);
}
void print(PrintWriter out) {
for (Map.Entry<Integer, Set<String>> e : map.entrySet()) {
int count = e.getKey();
Set<String> labels = e.getValue();
for (String label : labels) {
out.println(String.format("%6d: %s", count, label));
}
}
}
}
}
// </editor-fold>
}

View File

@ -0,0 +1,65 @@
#
# Copyright (c) 2012, 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. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# 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.
#
dc.anchor.already.defined = anchor already defined: {0}
dc.attr.lacks.value = attribute lacks value
dc.attr.obsolete = attribute obsolete: {0}
dc.attr.obsolete.use.css = attribute obsolete, use CSS instead: {0}
dc.attr.repeated = repeated attribute: {0}
dc.attr.unknown = unknown attribute: {0}
dc.bad.option = bad option: {0}
dc.bad.value.for.option = bad value for option: {0} {1}
dc.empty = no description for @{0}
dc.entity.invalid = invalid entity &{0};
dc.exception.not.thrown = exception not thrown: {0}
dc.invalid.anchor = invalid name for anchor: "{0}"
dc.invalid.param = invalid use of @param
dc.invalid.return = invalid use of @return
dc.invalid.throws = invalid use of @throws
dc.invalid.uri = invalid uri: "{0}"
dc.missing.comment = no comment
dc.missing.param = no @param for {0}
dc.missing.return = no @return
dc.missing.throws = no @throws for {0}
dc.no.alt.attr.for.image = no "alt" attribute for image
dc.no.summary.or.caption.for.table=no summary or caption for table
dc.param.name.not.found = @param name not found
dc.ref.not.found = reference not found
dc.tag.empty = empty <{0}> tag
dc.tag.end.not.permitted = invalid end tag: </{0}>
dc.tag.end.unexpected = unexpected end tag: </{0}>
dc.tag.header.sequence.1 = header used out of sequence: <{0}>
dc.tag.header.sequence.2 = header used out of sequence: <{0}>
dc.tag.nested.not.allowed=nested tag not allowed: <{0}>
dc.tag.not.allowed = element not allowed in documentation comments: <{0}>
dc.tag.not.allowed.inline.element = block element not allowed within inline element <{1}>: {0}
dc.tag.not.allowed.inline.tag = block element not allowed within @{1}: {0}
dc.tag.not.allowed.inline.other = block element not allowed here: {0}
dc.tag.not.closed= element not closed: {0}
dc.tag.p.in.pre= unexpected use of <p> inside <pre> element
dc.tag.self.closing = self-closing element not allowed
dc.tag.start.unmatched = end tag missing: </{0}>
dc.tag.unknown = unknown tag: {0}
dc.text.not.allowed = text not allowed in <{0}> element

View File

@ -57,6 +57,13 @@ public class BasicJavacTask extends JavacTask {
protected Context context;
private TaskListener taskListener;
public static JavacTask instance(Context context) {
JavacTask instance = context.get(JavacTask.class);
if (instance == null)
instance = new BasicJavacTask(context, true);
return instance;
}
public BasicJavacTask(Context c, boolean register) {
context = c;
if (register)

View File

@ -65,7 +65,6 @@ import com.sun.tools.javac.util.List;
* @author Jonathan Gibbons
*/
public class JavacTaskImpl extends BasicJavacTask {
private ClientCodeWrapper ccw;
private Main compilerMain;
private JavaCompiler compiler;
private Locale locale;
@ -85,7 +84,6 @@ public class JavacTaskImpl extends BasicJavacTask {
Context context,
List<JavaFileObject> fileObjects) {
super(null, false);
this.ccw = ClientCodeWrapper.instance(context);
this.compilerMain = compilerMain;
this.args = args;
this.classNames = classNames;

View File

@ -84,6 +84,7 @@ import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.TreeCopier;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Abort;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
@ -236,19 +237,26 @@ public class JavacTrees extends DocTrees {
public Element getElement(TreePath path) {
JCTree tree = (JCTree) path.getLeaf();
Symbol sym = TreeInfo.symbolFor(tree);
if (sym == null && TreeInfo.isDeclaration(tree)) {
for (TreePath p = path; p != null; p = p.getParentPath()) {
JCTree t = (JCTree) p.getLeaf();
if (t.hasTag(JCTree.Tag.CLASSDEF)) {
JCClassDecl ct = (JCClassDecl) t;
if (ct.sym != null) {
if ((ct.sym.flags_field & Flags.UNATTRIBUTED) != 0) {
attr.attribClass(ct.pos(), ct.sym);
sym = TreeInfo.symbolFor(tree);
if (sym == null) {
if (TreeInfo.isDeclaration(tree)) {
for (TreePath p = path; p != null; p = p.getParentPath()) {
JCTree t = (JCTree) p.getLeaf();
if (t.hasTag(JCTree.Tag.CLASSDEF)) {
JCClassDecl ct = (JCClassDecl) t;
if (ct.sym != null) {
if ((ct.sym.flags_field & Flags.UNATTRIBUTED) != 0) {
attr.attribClass(ct.pos(), ct.sym);
sym = TreeInfo.symbolFor(tree);
}
break;
}
break;
}
}
} else if (tree.hasTag(Tag.TOPLEVEL)) {
JCCompilationUnit cu = (JCCompilationUnit) tree;
if (cu.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE)) {
sym = cu.packge;
}
}
}
return sym;
@ -332,6 +340,8 @@ public class JavacTrees extends DocTrees {
} else {
return msym;
}
} catch (Abort e) { // may be thrown by Check.completionError in case of bad class file
return null;
} finally {
log.popDiagnosticHandler(deferredDiagnosticHandler);
}

View File

@ -1675,7 +1675,7 @@ public class JavaCompiler implements ClassReader.SourceCompleter {
/** Print numbers of errors and warnings.
*/
protected void printCount(String kind, int count) {
public void printCount(String kind, int count) {
if (count != 0) {
String key;
if (count == 1)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2012, 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
@ -25,11 +25,16 @@
package com.sun.tools.javac.model;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.EnumSet;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.util.*;
@ -301,4 +306,31 @@ public class JavacTypes implements javax.lang.model.util.Types {
throw new IllegalArgumentException(o.toString());
return clazz.cast(o);
}
public Set<MethodSymbol> getOverriddenMethods(Element elem) {
if (elem.getKind() != ElementKind.METHOD
|| elem.getModifiers().contains(Modifier.STATIC)
|| elem.getModifiers().contains(Modifier.PRIVATE))
return Collections.emptySet();
if (!(elem instanceof MethodSymbol))
throw new IllegalArgumentException();
MethodSymbol m = (MethodSymbol) elem;
ClassSymbol origin = (ClassSymbol) m.owner;
Set<MethodSymbol> results = new LinkedHashSet<MethodSymbol>();
for (Type t : types.closure(origin.type)) {
if (t != origin.type) {
ClassSymbol c = (ClassSymbol) t.tsym;
for (Scope.Entry e = c.members().lookup(m.name); e.scope != null; e = e.next()) {
if (e.sym.kind == Kinds.MTH && m.overrides(e.sym, origin, types, true)) {
results.add((MethodSymbol) e.sym);
}
}
}
}
return results;
}
}

View File

@ -25,15 +25,12 @@
package com.sun.tools.javac.parser;
import com.sun.tools.javac.util.Filter;
import java.text.BreakIterator;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import com.sun.source.doctree.AttributeTree.ValueKind;
@ -52,7 +49,6 @@ import com.sun.tools.javac.tree.DCTree.DCText;
import com.sun.tools.javac.tree.DocTreeMaker;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.DiagnosticSource;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
@ -736,7 +732,9 @@ public class DocCommentParser {
nextChar();
return m.at(p).Entity(names.fromChars(buf, namep, bp - namep - 1));
} else {
String code = checkSemi ? "dc.missing.semicolon" : "dc.bad.entity";
String code = checkSemi
? "dc.missing.semicolon"
: "dc.bad.entity";
return erroneous(code, p);
}
}
@ -888,8 +886,10 @@ public class DocCommentParser {
}
protected void addPendingText(ListBuffer<DCTree> list, int textEnd) {
if (textStart != -1 && textStart <= textEnd) {
list.add(m.at(textStart).Text(newString(textStart, textEnd + 1)));
if (textStart != -1) {
if (textStart <= textEnd) {
list.add(m.at(textStart).Text(newString(textStart, textEnd + 1)));
}
textStart = -1;
}
}
@ -1196,6 +1196,16 @@ public class DocCommentParser {
return m.at(pos).See(html);
break;
case '@':
if (newline)
throw new ParseException("dc.no.content");
break;
case EOI:
if (bp == buf.length - 1)
throw new ParseException("dc.no.content");
break;
default:
if (isJavaIdentifierStart(ch) || ch == '#') {
DCReference ref = reference(true);

View File

@ -2364,6 +2364,9 @@ compiler.err.dc.malformed.html=\
compiler.err.dc.missing.semicolon=\
semicolon missing
compiler.err.dc.no.content=\
no content
compiler.err.dc.no.tag.name=\
no tag name after '@'

View File

@ -36,6 +36,8 @@ import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import java.io.IOException;
import java.io.StringWriter;
import javax.tools.JavaFileObject;
/**
@ -65,6 +67,21 @@ public abstract class DCTree implements DocTree {
return new SimpleDiagnosticPosition(dc.comment.getSourcePos(pos));
}
/** Convert a tree to a pretty-printed string. */
@Override
public String toString() {
StringWriter s = new StringWriter();
try {
new DocPretty(s).print(this);
}
catch (IOException e) {
// should never happen, because StringWriter is defined
// never to throw any IOExceptions
throw new AssertionError(e);
}
return s.toString();
}
public static class DCDocComment extends DCTree implements DocCommentTree {
final Comment comment; // required for the implicit source pos table

View File

@ -81,7 +81,7 @@ public class DocPretty implements DocTreeVisitor<Void,Void> {
/**
* Print list.
*/
protected void print(List<? extends DocTree> list) throws IOException {
public void print(List<? extends DocTree> list) throws IOException {
for (DocTree t: list) {
print(t);
}

View File

@ -26,6 +26,7 @@
package com.sun.tools.javac.tree;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.comp.AttrContext;
@ -330,6 +331,13 @@ public class TreeInfo {
return (docComments == null) ? null : docComments.getCommentText(tree);
}
public static DCTree.DCDocComment getCommentTree(Env<?> env, JCTree tree) {
DocCommentTable docComments = (tree.hasTag(JCTree.Tag.TOPLEVEL))
? ((JCCompilationUnit) tree).docComments
: env.toplevel.docComments;
return (docComments == null) ? null : docComments.getCommentTree(tree);
}
/** The position of the first statement in a block, or the position of
* the block itself if it is empty.
*/

View File

@ -0,0 +1,63 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -ref AccessTest.protected.out AccessTest.java
* @run main DocLintTester -Xmsgs -ref AccessTest.private.out AccessTest.java
* @run main DocLintTester -Xmsgs:syntax -ref AccessTest.private.out AccessTest.java
* @run main DocLintTester -Xmsgs:syntax/public -ref AccessTest.public.out AccessTest.java
* @run main DocLintTester -Xmsgs:syntax/protected -ref AccessTest.protected.out AccessTest.java
* @run main DocLintTester -Xmsgs:syntax/package -ref AccessTest.package.out AccessTest.java
* @run main DocLintTester -Xmsgs:syntax/private -ref AccessTest.private.out AccessTest.java
* @run main DocLintTester -Xmsgs:all,-syntax AccessTest.java
* @run main DocLintTester -Xmsgs:all,-syntax/public AccessTest.java
* @run main DocLintTester -Xmsgs:all,-syntax/protected -ref AccessTest.public.out AccessTest.java
* @run main DocLintTester -Xmsgs:all,-syntax/package -ref AccessTest.protected.out AccessTest.java
* @run main DocLintTester -Xmsgs:all,-syntax/private -ref AccessTest.package.out AccessTest.java
*/
/** */
public class AccessTest {
/**
* public a < b
*/
public void public_syntax_error() { }
/**
* protected a < b
*/
protected void protected_syntax_error() { }
/**
* package-private a < b
*/
void syntax_error() { }
/**
* private a < b
*/
private void private_syntax_error() { }
}
/** */
class AccessTest2 {
/**
* public a < b
*/
public void public_syntax_error() { }
/**
* protected a < b
*/
protected void protected_syntax_error() { }
/**
* package-private a < b
*/
void syntax_error() { }
/**
* private a < b
*/
private void private_syntax_error() { }
}

View File

@ -0,0 +1,20 @@
AccessTest.java:21: error: malformed HTML
* public a < b
^
AccessTest.java:26: error: malformed HTML
* protected a < b
^
AccessTest.java:31: error: malformed HTML
* package-private a < b
^
AccessTest.java:44: error: malformed HTML
* public a < b
^
AccessTest.java:49: error: malformed HTML
* protected a < b
^
AccessTest.java:54: error: malformed HTML
* package-private a < b
^
6 errors

View File

@ -0,0 +1,27 @@
AccessTest.java:21: error: malformed HTML
* public a < b
^
AccessTest.java:26: error: malformed HTML
* protected a < b
^
AccessTest.java:31: error: malformed HTML
* package-private a < b
^
AccessTest.java:36: error: malformed HTML
* private a < b
^
AccessTest.java:44: error: malformed HTML
* public a < b
^
AccessTest.java:49: error: malformed HTML
* protected a < b
^
AccessTest.java:54: error: malformed HTML
* package-private a < b
^
AccessTest.java:59: error: malformed HTML
* private a < b
^
8 errors

View File

@ -0,0 +1,8 @@
AccessTest.java:21: error: malformed HTML
* public a < b
^
AccessTest.java:26: error: malformed HTML
* protected a < b
^
2 errors

View File

@ -0,0 +1,5 @@
AccessTest.java:21: error: malformed HTML
* public a < b
^
1 error

View File

@ -0,0 +1,42 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-accessibility AccessibilityTest.java
* @run main DocLintTester -ref AccessibilityTest.out AccessibilityTest.java
*/
/** */
public class AccessibilityTest {
/**
* <h2> ... </h2>
*/
public void missing_h1() { }
/**
* <h1> ... </h1>
* <h3> ... </h3>
*/
public void missing_h2() { }
/**
* <img src="x.jpg">
*/
public void missing_alt() { }
/**
* <table summary="ok"><tr><th>head<tr><td>data</table>
*/
public void table_with_summary() { }
/**
* <table><caption>ok</caption><tr><th>head<tr><td>data</table>
*/
public void table_with_caption() { }
/**
* <table><tr><th>head<tr><td>data</table>
*/
public void table_without_summary_and_caption() { }
}

View File

@ -0,0 +1,13 @@
AccessibilityTest.java:12: error: header used out of sequence: <H2>
* <h2> ... </h2>
^
AccessibilityTest.java:18: error: header used out of sequence: <H3>
* <h3> ... </h3>
^
AccessibilityTest.java:23: error: no "alt" attribute for image
* <img src="x.jpg">
^
AccessibilityTest.java:38: error: no summary or caption for table
* <table><tr><th>head<tr><td>data</table>
^
4 errors

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2012, 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.
*/
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.sun.tools.doclint.DocLint;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DocLintTester {
public static void main(String... args) throws Exception {
new DocLintTester().run(args);
}
public void run(String... args) throws Exception {
String testSrc = System.getProperty("test.src");
File refFile = null;
List<String> opts = new ArrayList<String>();
List<File> files = new ArrayList<File>();
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.equals("-ref")) {
refFile = new File(testSrc, args[++i]);
} else if (arg.startsWith("-Xmsgs")) {
opts.add(arg);
} else
files.add(new File(testSrc, arg));
}
check(opts, files, refFile);
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void check(List<String> opts, List<File> files, File refFile) throws Exception {
List<String> args = new ArrayList<String>();
args.addAll(opts);
for (File file: files)
args.add(file.getPath());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
new DocLint().run(pw, args.toArray(new String[args.size()]));
pw.flush();
String out = normalizeNewlines(removeFileNames(sw.toString())).trim();
if (out != null)
System.err.println("Output:\n" + out);
if (refFile == null) {
if (!out.isEmpty())
error("unexpected output");
} else {
String expect = readFile(refFile);
if (!expect.equals(out)) {
error("expected output not found");
System.err.println("EXPECT>>" + expect + "<<");
System.err.println(" FOUND>>" + out + "<<");
}
}
}
String readFile(File file) throws IOException {
StringBuilder sb = new StringBuilder();
Reader in = new BufferedReader(new FileReader(file));
try {
char[] buf = new char[1024];
int n;
while ((n = in.read(buf)) != -1)
sb.append(buf, 0, n);
} finally {
in.close();
}
return sb.toString().trim();
}
private static final Pattern dirFileLine = Pattern.compile(
"(?m)" // multi-line mode
+ "^([^: ]+?)" // directory part of file name
+ "([A-Za-z0-9.]+:[0-9]+:)"); // file name and line number
String removeFileNames(String s) {
Matcher m = dirFileLine.matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "$2");
}
m.appendTail(sb);
return sb.toString();
}
private static final String nl = System.getProperty("line.separator");
String normalizeNewlines(String s) {
return (nl.equals("\n") ? s : s.replace(nl, "\n"));
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
}

View File

@ -0,0 +1,10 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptyAuthorTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptyAuthorTest.out EmptyAuthorTest.java
*/
/** @author */
public class EmptyAuthorTest {
}

View File

@ -0,0 +1,5 @@
EmptyAuthorTest.java:8: warning: no description for @author
/** @author */
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptyExceptionTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptyExceptionTest.out EmptyExceptionTest.java
*/
/** . */
public class EmptyExceptionTest {
/** @exception NullPointerException */
int emptyException() throws NullPointerException { }
}

View File

@ -0,0 +1,4 @@
EmptyExceptionTest.java:10: warning: no description for @exception
/** @exception NullPointerException */
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptyParamTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptyParamTest.out EmptyParamTest.java
*/
/** . */
public class EmptyParamTest {
/** @param i */
int emptyParam(int i) { }
}

View File

@ -0,0 +1,5 @@
EmptyParamTest.java:10: warning: no description for @param
/** @param i */
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptyReturnTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptyReturnTest.out EmptyReturnTest.java
*/
/** . */
public class EmptyReturnTest {
/** @return */
int emptyReturn() { }
}

View File

@ -0,0 +1,5 @@
EmptyReturnTest.java:10: warning: no description for @return
/** @return */
^
1 warning

View File

@ -0,0 +1,15 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptySerialDataTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptySerialDataTest.out EmptySerialDataTest.java
*/
import java.io.ObjectOutputStream;
import java.io.Serializable;
/** . */
public class EmptySerialDataTest implements Serializable {
/** @serialData */
private void writeObject(ObjectOutputStream s) { }
}

View File

@ -0,0 +1,5 @@
EmptySerialDataTest.java:13: warning: no description for @serialData
/** @serialData */
^
1 warning

View File

@ -0,0 +1,20 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptySerialFieldTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptySerialFieldTest.out EmptySerialFieldTest.java
*/
import java.io.ObjectStreamField;
import java.io.Serializable;
/** . */
public class EmptySerialFieldTest implements Serializable {
/**
* @serialField empty String
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("empty", String.class),
};
}

View File

@ -0,0 +1,5 @@
EmptySerialFieldTest.java:15: warning: no description for @serialField
* @serialField empty String
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptySinceTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptySinceTest.out EmptySinceTest.java
*/
/** . */
public class EmptySinceTest {
/** @since */
int emptySince() { }
}

View File

@ -0,0 +1,5 @@
EmptySinceTest.java:10: warning: no description for @since
/** @since */
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax EmptyVersionTest.java
* @run main DocLintTester -Xmsgs:syntax -ref EmptyVersionTest.out EmptyVersionTest.java
*/
/** . */
public class EmptyVersionTest {
/** @version */
int missingVersion() { }
}

View File

@ -0,0 +1,4 @@
EmptyVersionTest.java:10: warning: no description for @version
/** @version */
^
1 warning

View File

@ -0,0 +1,25 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-html HtmlAttrsTest.java
* @run main DocLintTester -ref HtmlAttrsTest.out HtmlAttrsTest.java
*/
/** */
public class HtmlAttrsTest {
/**
* <p xyz>
*/
public void unknown() { }
/**
* <img name="x" alt="alt">
*/
public void obsolete() { }
/**
* <font size="3"> text </font>
*/
public void obsolete_use_css() { }
}

View File

@ -0,0 +1,12 @@
HtmlAttrsTest.java:11: error: unknown attribute: xyz
* <p xyz>
^
HtmlAttrsTest.java:16: warning: attribute obsolete: name
* <img name="x" alt="alt">
^
HtmlAttrsTest.java:21: warning: attribute obsolete, use CSS instead: size
* <font size="3"> text </font>
^
1 error
2 warnings

View File

@ -0,0 +1,56 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-html HtmlTagsTest.java
* @run main DocLintTester -ref HtmlTagsTest.out HtmlTagsTest.java
*/
/** */
public class HtmlTagsTest {
/**
* <xyz> ... </xyz>
*/
public void unknownTag1() { }
/**
* <div> <xyz> </div>
*/
public void unknownTag2() { }
/**
* <br/>
*/
public void selfClosingTag() { }
/**
* <html>
*/
public void not_allowed() { }
/**
* <span> <p> </span>
*/
public void not_allowed_inline() { }
/**
* {@link java.lang.String <p> }
* {@link java.lang.String <p> }
*/
public void not_allowed_inline_2() { }
/**
* <img src="any.jpg" alt="alt"> </img>
*/
public void end_not_allowed() { }
/**
* <i> <b> </i>
*/
public void start_not_matched() { }
/**
* <i> </b> </i>
*/
public void end_unexpected() { }
}

View File

@ -0,0 +1,38 @@
HtmlTagsTest.java:11: error: unknown tag: xyz
* <xyz> ... </xyz>
^
HtmlTagsTest.java:11: error: unknown tag: xyz
* <xyz> ... </xyz>
^
HtmlTagsTest.java:16: error: unknown tag: xyz
* <div> <xyz> </div>
^
HtmlTagsTest.java:21: error: self-closing element not allowed
* <br/>
^
HtmlTagsTest.java:26: error: element not allowed in documentation comments: <html>
* <html>
^
HtmlTagsTest.java:31: error: block element not allowed within inline element <span>: p
* <span> <p> </span>
^
HtmlTagsTest.java:36: error: block element not allowed within @link: p
* {@link java.lang.String <p> }
^
HtmlTagsTest.java:37: error: block element not allowed within @link: p
* {@link java.lang.String <p> }
^
HtmlTagsTest.java:42: error: invalid end tag: </img>
* <img src="any.jpg" alt="alt"> </img>
^
HtmlTagsTest.java:47: error: end tag missing: </b>
* <i> <b> </i>
^
HtmlTagsTest.java:52: error: unexpected end tag: </b>
* <i> </b> </i>
^
HtmlTagsTest.java:52: warning: empty <i> tag
* <i> </b> </i>
^
11 errors
1 warning

View File

@ -0,0 +1,14 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-missing MissingCommentTest.java
* @run main DocLintTester -Xmsgs:missing -ref MissingCommentTest.out MissingCommentTest.java
*/
public class MissingCommentTest {
MissingCommentTest() { }
int missingComment;
void missingComment() { }
}

View File

@ -0,0 +1,14 @@
MissingCommentTest.java:8: warning: no comment
public class MissingCommentTest {
^
MissingCommentTest.java:9: warning: no comment
MissingCommentTest() { }
^
MissingCommentTest.java:11: warning: no comment
int missingComment;
^
MissingCommentTest.java:13: warning: no comment
void missingComment() { }
^
4 warnings

View File

@ -0,0 +1,21 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-missing MissingParamsTest.java
* @run main DocLintTester -Xmsgs:missing -ref MissingParamsTest.out MissingParamsTest.java
*/
/** . */
public class MissingParamsTest {
/** */
MissingParamsTest(int param) { }
/** */
<T> MissingParamsTest() { }
/** */
void missingParam(int param) { }
/** */
<T> void missingTyparam() { }
}

View File

@ -0,0 +1,14 @@
MissingParamsTest.java:11: warning: no @param for param
MissingParamsTest(int param) { }
^
MissingParamsTest.java:14: warning: no @param for <T>
<T> MissingParamsTest() { }
^
MissingParamsTest.java:17: warning: no @param for param
void missingParam(int param) { }
^
MissingParamsTest.java:20: warning: no @param for <T>
<T> void missingTyparam() { }
^
4 warnings

View File

@ -0,0 +1,21 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-missing MissingReturnTest.java
* @run main DocLintTester -Xmsgs:missing -ref MissingReturnTest.out MissingReturnTest.java
*/
/** . */
public class MissingReturnTest {
/** no return allowed */
MissingReturnTest() { }
/** no return allowed */
void return_void() { }
/** no return required */
Void return_Void() { }
/** */
int missingReturn() { }
}

View File

@ -0,0 +1,5 @@
MissingReturnTest.java:20: warning: no @return
int missingReturn() { }
^
1 warning

View File

@ -0,0 +1,12 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-missing MissingThrowsTest.java
* @run main DocLintTester -Xmsgs:missing -ref MissingThrowsTest.out MissingThrowsTest.java
*/
/** */
public class MissingThrowsTest {
/** */
void missingThrows() throws Exception { }
}

View File

@ -0,0 +1,4 @@
MissingThrowsTest.java:11: warning: no @throws for java.lang.Exception
void missingThrows() throws Exception { }
^
1 warning

View File

@ -0,0 +1,94 @@
import com.sun.tools.doclint.DocLint;
/*
* Copyright (c) 2012, 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
*/
public class OptionTest {
public static void main(String... args) throws Exception {
new OptionTest().run();
}
String[] positiveTests = {
"-Xmsgs",
"-Xmsgs:all",
"-Xmsgs:none",
"-Xmsgs:accessibility",
"-Xmsgs:html",
"-Xmsgs:missing",
"-Xmsgs:reference",
"-Xmsgs:syntax",
"-Xmsgs:html/public",
"-Xmsgs:html/protected",
"-Xmsgs:html/package",
"-Xmsgs:html/private",
"-Xmsgs:-html/public",
"-Xmsgs:-html/protected",
"-Xmsgs:-html/package",
"-Xmsgs:-html/private",
"-Xmsgs:html,syntax",
"-Xmsgs:html,-syntax",
"-Xmsgs:-html,syntax",
"-Xmsgs:-html,-syntax",
"-Xmsgs:html/public,syntax",
"-Xmsgs:html,syntax/public",
"-Xmsgs:-html/public,syntax/public"
};
String[] negativeTests = {
"-typo",
"-Xmsgs:-all",
"-Xmsgs:-none",
"-Xmsgs:typo",
"-Xmsgs:html/typo",
"-Xmsgs:html/public,typo",
"-Xmsgs:html/public,syntax/typo",
};
void run() throws Exception {
test(positiveTests, true);
test(negativeTests, false);
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void test(String[] tests, boolean expect) {
for (String test: tests) {
System.err.println("test: " + test);
boolean found = DocLint.isValidOption(test);
if (found != expect)
error("Unexpected result: " + found + ",expected: " + expect);
}
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2012, 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
* @build DocLintTester
* @run main DocLintTester -Xmsgs:all OverridesTest.java
*/
/*
* This is a test that missing comments on methods may be inherited
* from overridden methods. As such, there should be no errors due
* to missing comments (or any other types of error) in this test.
*/
/** An interface. */
interface I1 {
/**
* A method
* @param p a param
* @throws Exception an exception
* @return an int
*/
int m(int p) throws Exception;
}
/** An extending interface. */
interface I2 extends I1 { }
/** An abstract class. */
abstract class C1 {
/**
* A method
* @param p a param
* @throws Exception an exception
* @return an int
*/
int m(int p) throws Exception;
}
/** An implementing class. */
class C2 implements I1 {
int m(int p) throws Exception { return p; }
}
/** An extending class. */
class C3 extends C1 {
int m(int p) throws Exception { return p; }
}
/** An extending and implementing class. */
class C4 extends C1 implements I1 {
int m(int p) throws Exception { return p; }
}
/** An implementing class using inheritdoc. */
class C5 implements I1 {
/** {@inheritDoc} */
int m(int p) throws Exception { return p; }
}
/** An implementing class with incomplete documentation. */
class C6 implements I1 {
/** Overriding method */
int m(int p) throws Exception { return p; }
}
/** A class implementing an inherited interface. */
class C7 implements I2 {
int m(int p) throws Exception { return p; }
}

View File

@ -0,0 +1,50 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-reference ReferenceTest.java
* @run main DocLintTester -ref ReferenceTest.out ReferenceTest.java
*/
/** */
public class ReferenceTest {
/**
* @param x description
*/
public int invalid_param;
/**
* @param x description
*/
public class InvalidParam { }
/**
* @param x description
*/
public void param_name_not_found(int a) { }
/**
* @param <X> description
*/
public class typaram_name_not_found { }
/**
* @see Object#tooStrong()
*/
public void ref_not_found() { }
/**
* @return x description
*/
public int invalid_return;
/**
* @return x description
*/
public void invalid_return();
/**
* @throws Exception description
*/
public void exception_not_thrown() { }
}

View File

@ -0,0 +1,30 @@
ReferenceTest.java:11: error: invalid use of @param
* @param x description
^
ReferenceTest.java:16: error: invalid use of @param
* @param x description
^
ReferenceTest.java:21: error: @param name not found
* @param x description
^
ReferenceTest.java:23: warning: no @param for a
public void param_name_not_found(int a) { }
^
ReferenceTest.java:26: error: @param name not found
* @param <X> description
^
ReferenceTest.java:31: error: reference not found
* @see Object#tooStrong()
^
ReferenceTest.java:36: error: invalid use of @return
* @return x description
^
ReferenceTest.java:41: error: invalid use of @return
* @return x description
^
ReferenceTest.java:46: error: exception not thrown: java.lang.Exception
* @throws Exception description
^
8 errors
1 warning

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2012, 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 8000103
* @summary Create doclint utility
*/
import com.sun.tools.doclint.DocLint;
import com.sun.tools.doclint.DocLint.BadArgs;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/** javadoc error on toplevel: a & b. */
public class RunTest {
/** javadoc error on member: a < b */
public static void main(String... args) throws Exception {
new RunTest().run();
}
File testSrc = new File(System.getProperty("test.src"));
File thisFile = new File(testSrc, RunTest.class.getSimpleName() + ".java");
void run() throws Exception {
for (Method m: getClass().getDeclaredMethods()) {
Annotation a = m.getAnnotation(Test.class);
if (a != null) {
System.err.println("test: " + m.getName());
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);;
m.invoke(this, new Object[] { pw });
String out = sw.toString();
System.err.println(">>> " + out.replace("\n", "\n>>> "));
if (!out.contains("a < b"))
error("\"a < b\" not found");
if (!out.contains("a & b"))
error("\"a & b\" not found");
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
throw (cause instanceof Exception) ? ((Exception) cause) : e;
}
System.err.println();
}
}
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
/** Marker annotation for test cases. */
@Retention(RetentionPolicy.RUNTIME)
@interface Test { }
@Test
void testMain(PrintWriter pw) throws BadArgs, IOException {
String[] args = { "-Xmsgs", thisFile.getPath() };
DocLint d = new DocLint();
d.run(pw, args);
}
}

View File

@ -0,0 +1,15 @@
/*
* @test /nodynamiccopyright/
* @build DocLintTester
* @run main DocLintTester -Xmsgs:-syntax SyntaxTest.java
* @run main DocLintTester -ref SyntaxTest.out SyntaxTest.java
*/
/** */
public class SyntaxTest {
/**
* a < b
*/
public void syntax_error() { }
}

View File

@ -0,0 +1,5 @@
SyntaxTest.java:11: error: malformed HTML
* a < b
^
1 error

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2012, 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
* @build DocLintTester
* @run main DocLintTester -Xmsgs:all SyntheticTest.java
*/
/**
* This is a test that messages are not generated for synthesized elements
* such as default constructors and enum methods.
*/
public class SyntheticTest {
// No explicit constructor implies a default constructor
/** enum E */
enum E {
/** enum member E1 */
E1,
/** enum member E2 */
E2,
/** enum member E3 */
E3
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2012, 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
* @build DocLintTester
* @run main DocLintTester ValidTest.java
*/
class ValidTest {
/**
* &lt; &gt; &amp; &#40;
*/
void entities() { }
/**
* <h1> ... </h1>
* <h2> ... </h2>
* <h3> ... </h3>
* <h4> ... </h4>
* <h5> ... </h5>
* <h6> ... </h6>
*/
void all_headers() { }
/**
* <h1> ... </h1>
* <h2> ... </h2>
* <h3> ... </h3>
* <h1> ... </h1>
* <h2> ... </h2>
* <h3> ... </h3>
* <h2> ... </h2>
*/
void header_series() { }
/**
* <div> <p> </div>
*/
void autoclose_tags() { }
/**
* @param x
*/
void method_param(int x) { }
/**
* @param <T>
*/
<T> T method_typaram(T t) { return t; }
/**
* @param <T>
*/
class ClassTyparam<T> { }
/**
* @param <T>
*/
interface InterfaceTyparam<T> { }
/**
* @return x
*/
int return_int() { return 0; }
/**
* @exception Exception
*/
void throws_Exception1() throws Exception { }
/**
* @throws Exception
*/
void throws_Exception2() throws Exception { }
class X {
/**
* @param x
*/
X(int x) { } // constructor parameter
/**
* @param <T>
*/
<T> X(T t) { } // constructor type parameter
}
}

View File

@ -0,0 +1,15 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref AnchorAlreadyDefined.out AnchorAlreadyDefined.java
*/
// tidy: Warning: <.*> anchor ".*" already defined
/**
* <a name="here">valid</a>
* <a name="here">duplicate</a>
* <h1 id="here">duplicate</h1>
*/
public class AnchorAlreadyDefined { }

View File

@ -0,0 +1,7 @@
AnchorAlreadyDefined.java:12: error: anchor already defined: here
* <a name="here">duplicate</a>
^
AnchorAlreadyDefined.java:13: error: anchor already defined: here
* <h1 id="here">duplicate</h1>
^
2 errors

View File

@ -0,0 +1,14 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref BadEnd.out BadEnd.java
*/
// tidy: Warning: <.*> is probably intended as </.*>
/**
* <a name="here"> text <a>
* <code> text <code>
*/
public class BadEnd { }

View File

@ -0,0 +1,17 @@
BadEnd.java:12: warning: nested tag not allowed: <code>
* <code> text <code>
^
BadEnd.java:12: error: element not closed: code
* <code> text <code>
^
BadEnd.java:12: error: element not closed: code
* <code> text <code>
^
BadEnd.java:11: error: element not closed: a
* <a name="here"> text <a>
^
BadEnd.java:11: error: element not closed: a
* <a name="here"> text <a>
^
4 errors
1 warning

View File

@ -0,0 +1,14 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref InsertImplicit.out InsertImplicit.java
*/
// tidy: Warning: inserting implicit <.*>
/**
* </p>
* <i> <blockquote> abc </blockquote> </i>
*/
public class InsertImplicit { }

View File

@ -0,0 +1,7 @@
InsertImplicit.java:11: error: unexpected end tag: </p>
* </p>
^
InsertImplicit.java:12: error: block element not allowed within inline element <i>: blockquote
* <i> <blockquote> abc </blockquote> </i>
^
2 errors

View File

@ -0,0 +1,20 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref InvalidEntity.out InvalidEntity.java
*/
// tidy: Warning: replacing invalid numeric character reference .*
// See
// http://www.w3.org/TR/html4/sgml/entities.html
// http://stackoverflow.com/questions/631406/what-is-the-difference-between-em-dash-151-and-8212
/**
* &#01;
* &#x01;
* &splodge;
*
*/
public class InvalidEntity { }

View File

@ -0,0 +1,10 @@
InvalidEntity.java:15: error: invalid entity &#01;
* &#01;
^
InvalidEntity.java:16: error: invalid entity &#x01;
* &#x01;
^
InvalidEntity.java:17: error: invalid entity &splodge;
* &splodge;
^
3 errors

View File

@ -0,0 +1,16 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref InvalidName.out InvalidName.java
*/
// tidy: Warning: <a> cannot copy name attribute to id
/**
* <a name="abc">valid</a>
* <a name="abc123">valid</a>
* <a name="a.1:2-3_4">valid</a>
* <a name="foo()">invalid</a>
*/
public class InvalidName { }

View File

@ -0,0 +1,4 @@
InvalidName.java:14: error: invalid name for anchor: "foo()"
* <a name="foo()">invalid</a>
^
1 error

View File

@ -0,0 +1,13 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref InvalidTag.out InvalidTag.java
*/
// tidy: Error: <.*> is not recognized!
/**
* List<String> list = new ArrayList<>();
*/
public class InvalidTag { }

View File

@ -0,0 +1,10 @@
InvalidTag.java:11: error: unknown tag: String
* List<String> list = new ArrayList<>();
^
InvalidTag.java:11: error: malformed HTML
* List<String> list = new ArrayList<>();
^
InvalidTag.java:11: error: bad use of '>'
* List<String> list = new ArrayList<>();
^
3 errors

View File

@ -0,0 +1,19 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref InvalidURI.out InvalidURI.java
*/
// tidy: Warning: <a> escaping malformed URI reference
// tidy: Warning: <.*> attribute ".*" lacks value
/**
* <a href="abc">valid</a>
* <a href="abc%20def">valid</a>
* <a href="abc def">invalid</a>
* <a href>no value</a>
* <a href= >no value</a>
* <a href="" >no value</a>
*/
public class InvalidURI { }

View File

@ -0,0 +1,13 @@
InvalidURI.java:14: error: invalid uri: "abc def"
* <a href="abc def">invalid</a>
^
InvalidURI.java:15: error: attribute lacks value
* <a href>no value</a>
^
InvalidURI.java:16: error: attribute lacks value
* <a href= >no value</a>
^
InvalidURI.java:17: error: attribute lacks value
* <a href="" >no value</a>
^
4 errors

View File

@ -0,0 +1,14 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref MissingGT.out MissingGT.java
*/
// tidy: Warning: <.*> missing '>' for end of tag
/**
* <img src="image.gif"
* <i> text </i>
*/
public class MissingGT { }

View File

@ -0,0 +1,4 @@
MissingGT.java:11: error: malformed HTML
* <img src="image.gif"
^
1 error

View File

@ -0,0 +1,15 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref MissingTag.out MissingTag.java
*/
// tidy: Warning: missing <.*>
// tidy: Warning: missing </.*> before </.*>
/**
* </p>
* <h1> <b> text </h1>
*/
public class MissingTag { }

View File

@ -0,0 +1,7 @@
MissingTag.java:12: error: unexpected end tag: </p>
* </p>
^
MissingTag.java:13: error: end tag missing: </b>
* <h1> <b> text </h1>
^
2 errors

View File

@ -0,0 +1,14 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref NestedTag.out NestedTag.java
*/
// tidy: Warning: nested emphasis <.*>
/**
* <b><b> text </b></b>
* {@link java.lang.String <code>String</code>}
*/
public class NestedTag { }

View File

@ -0,0 +1,7 @@
NestedTag.java:11: warning: nested tag not allowed: <b>
* <b><b> text </b></b>
^
NestedTag.java:12: warning: nested tag not allowed: <code>
* {@link java.lang.String <code>String</code>}
^
2 warnings

View File

@ -0,0 +1,18 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref ParaInPre.out ParaInPre.java
*/
// tidy: Warning: replacing <p> by <br>
// tidy: Warning: using <br> in place of <p>
/**
* <pre>
* text
* <p>
* more text
* </pre>
*/
public class ParaInPre { }

View File

@ -0,0 +1,4 @@
ParaInPre.java:14: warning: unexpected use of <p> inside <pre> element
* <p>
^
1 warning

View File

@ -0,0 +1,21 @@
The utilities in this directory can be used to determine
common issues in javadoc comments by running the standard
"tidy" program on the output of javadoc, and analysing
the messages that are reported.
tidy.sh is a script that will run the "tidy" program on
the files in a directory, writing the results to a new
directroy.
tidystats.Main is a Java program that can analyze the
files produced by the tidy.sh script to generate a
summary report about the warnings that were found.
The tests is this directory are focussed on verifying
that doclint detects issues in javadoc comments that will
give rise to issues detected by "tidy" in the output
generated by javadoc.
For more information on the "tidy" program, see the HTML Tidy
Library Project page at http://tidy.sourceforge.net/.

View File

@ -0,0 +1,13 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref RepeatedAttr.out RepeatedAttr.java
*/
// tidy: Warning: <.*> dropping value ".*" for repeated attribute ".*"
/**
* <img src="image.gif" alt alt="summary">
*/
public class RepeatedAttr { }

View File

@ -0,0 +1,4 @@
RepeatedAttr.java:11: error: repeated attribute: alt
* <img src="image.gif" alt alt="summary">
^
1 error

View File

@ -0,0 +1,24 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref TextNotAllowed.out TextNotAllowed.java
*/
// tidy: Warning: plain text isn't allowed in <.*> elements
/**
* <table summary=description> abc </table>
* <table summary=description> <tbody> abc </tbody> </table>
* <table summary=description> <tr> abc </tr> </table>
*
* <dl> abc </dl>
* <ol> abc </ol>
* <ul> abc </ul>
*
* <ul>
* <li> item
* <li> item
* </ul>
*/
public class TextNotAllowed { }

View File

@ -0,0 +1,19 @@
TextNotAllowed.java:11: error: text not allowed in <table> element
* <table summary=description> abc </table>
^
TextNotAllowed.java:12: error: text not allowed in <tbody> element
* <table summary=description> <tbody> abc </tbody> </table>
^
TextNotAllowed.java:13: error: text not allowed in <tr> element
* <table summary=description> <tr> abc </tr> </table>
^
TextNotAllowed.java:15: error: text not allowed in <dl> element
* <dl> abc </dl>
^
TextNotAllowed.java:16: error: text not allowed in <ol> element
* <ol> abc </ol>
^
TextNotAllowed.java:17: error: text not allowed in <ul> element
* <ul> abc </ul>
^
6 errors

View File

@ -0,0 +1,27 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref TrimmingEmptyTag.out TrimmingEmptyTag.java
*/
// tidy: Warning: trimming empty <.*>
/**
* <b></b>
* <table summary=description></table>
* <table><caption></caption></table>
* <code></code>
* <dl></dl>
* <dl><dt></dt><dd></dd></dl>
* <font></font>
* <i></i>
* <ol></ol>
* <p></p>
* <pre></pre>
* <span></span>
* <tt></tt>
* <ul></ul>
* <ul><li></li></ul>
*/
public class TrimmingEmptyTag { }

View File

@ -0,0 +1,46 @@
TrimmingEmptyTag.java:11: warning: empty <b> tag
* <b></b>
^
TrimmingEmptyTag.java:12: warning: empty <table> tag
* <table summary=description></table>
^
TrimmingEmptyTag.java:13: warning: empty <caption> tag
* <table><caption></caption></table>
^
TrimmingEmptyTag.java:14: warning: empty <code> tag
* <code></code>
^
TrimmingEmptyTag.java:15: warning: empty <dl> tag
* <dl></dl>
^
TrimmingEmptyTag.java:16: warning: empty <dt> tag
* <dl><dt></dt><dd></dd></dl>
^
TrimmingEmptyTag.java:16: warning: empty <dd> tag
* <dl><dt></dt><dd></dd></dl>
^
TrimmingEmptyTag.java:17: warning: empty <font> tag
* <font></font>
^
TrimmingEmptyTag.java:18: warning: empty <i> tag
* <i></i>
^
TrimmingEmptyTag.java:19: warning: empty <ol> tag
* <ol></ol>
^
TrimmingEmptyTag.java:20: warning: empty <p> tag
* <p></p>
^
TrimmingEmptyTag.java:21: warning: empty <pre> tag
* <pre></pre>
^
TrimmingEmptyTag.java:22: warning: empty <span> tag
* <span></span>
^
TrimmingEmptyTag.java:23: warning: empty <tt> tag
* <tt></tt>
^
TrimmingEmptyTag.java:24: warning: empty <ul> tag
* <ul></ul>
^
15 warnings

View File

@ -0,0 +1,17 @@
/*
* @test /nodynamiccopyright/
* @library ..
* @build DocLintTester
* @run main DocLintTester -ref UnescapedOrUnknownEntity.out UnescapedOrUnknownEntity.java
*/
// tidy: Warning: unescaped & or unknown entity ".*"
// tidy: Warning: unescaped & which should be written as &amp;
// tidy: Warning: entity ".*" doesn't end in ';'
/**
* L&F
* Drag&Drop
* if (a & b);
*/
public class UnescapedOrUnknownEntity { }

View File

@ -0,0 +1,11 @@
UnescapedOrUnknownEntity.java:13: error: semicolon missing
* L&F
^
UnescapedOrUnknownEntity.java:14: error: semicolon missing
* Drag&Drop
^
UnescapedOrUnknownEntity.java:15: error: bad HTML entity
* if (a & b);
^
3 errors

View File

@ -0,0 +1,182 @@
package tidystats;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String... args) throws IOException {
new Main().run(args);
}
void run(String... args) throws IOException {
FileSystem fs = FileSystems.getDefault();
List<Path> paths = new ArrayList<>();
int i;
for (i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.startsWith("-"))
throw new IllegalArgumentException(arg);
else
break;
}
for ( ; i < args.length; i++) {
Path p = fs.getPath(args[i]);
paths.add(p);
}
for (Path p: paths) {
scan(p);
}
print("%6d files read", files);
print("%6d files had no errors or warnings", ok);
print("%6d files reported \"Not all warnings/errors were shown.\"", overflow);
print("%6d errors found", errs);
print("%6d warnings found", warns);
print("%6d recommendations to use CSS", css);
print("");
Map<Integer, Set<String>> sortedCounts = new TreeMap<>(
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
for (Map.Entry<Pattern, Integer> e: counts.entrySet()) {
Pattern p = e.getKey();
Integer n = e.getValue();
Set<String> set = sortedCounts.get(n);
if (set == null)
sortedCounts.put(n, (set = new TreeSet<>()));
set.add(p.toString());
}
for (Map.Entry<Integer, Set<String>> e: sortedCounts.entrySet()) {
for (String p: e.getValue()) {
if (p.startsWith(".*")) p = p.substring(2);
print("%6d: %s", e.getKey(), p);
}
}
}
void scan(Path p) throws IOException {
if (Files.isDirectory(p)) {
for (Path c: Files.newDirectoryStream(p)) {
scan(c);
}
} else if (isTidyFile(p)) {
scan(Files.readAllLines(p, Charset.defaultCharset()));
}
}
boolean isTidyFile(Path p) {
return Files.isRegularFile(p) && p.getFileName().toString().endsWith(".tidy");
}
void scan(List<String> lines) {
Matcher m;
files++;
for (String line: lines) {
if (okPattern.matcher(line).matches()) {
ok++;
} else if ((m = countPattern.matcher(line)).matches()) {
warns += Integer.valueOf(m.group(1));
errs += Integer.valueOf(m.group(2));
if (m.group(3) != null)
overflow++;
} else if ((m = guardPattern.matcher(line)).matches()) {
boolean found = false;
for (Pattern p: patterns) {
if ((m = p.matcher(line)).matches()) {
found = true;
count(p);
break;
}
}
if (!found)
System.err.println("Unrecognized line: " + line);
} else if (cssPattern.matcher(line).matches()) {
css++;
}
}
}
Map<Pattern, Integer> counts = new HashMap<>();
void count(Pattern p) {
Integer i = counts.get(p);
counts.put(p, (i == null) ? 1 : i + 1);
}
void print(String format, Object... args) {
System.out.println(String.format(format, args));
}
Pattern okPattern = Pattern.compile("No warnings or errors were found.");
Pattern countPattern = Pattern.compile("([0-9]+) warnings, ([0-9]+) errors were found!.*?(Not all warnings/errors were shown.)?");
Pattern cssPattern = Pattern.compile("You are recommended to use CSS.*");
Pattern guardPattern = Pattern.compile("line [0-9]+ column [0-9]+ - (Error|Warning):.*");
Pattern[] patterns = {
Pattern.compile(".*Error: <.*> is not recognized!"),
Pattern.compile(".*Error: missing quote mark for attribute value"),
Pattern.compile(".*Warning: <.*> anchor \".*\" already defined"),
Pattern.compile(".*Warning: <.*> attribute \".*\" has invalid value \".*\""),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute \".*\" lacks value"),
Pattern.compile(".*Warning: <.*> attribute with missing trailing quote mark"),
Pattern.compile(".*Warning: <.*> dropping value \".*\" for repeated attribute \".*\""),
Pattern.compile(".*Warning: <.*> inserting \".*\" attribute"),
Pattern.compile(".*Warning: <.*> is probably intended as </.*>"),
Pattern.compile(".*Warning: <.*> isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: <.*> lacks \".*\" attribute"),
Pattern.compile(".*Warning: <.*> missing '>' for end of tag"),
Pattern.compile(".*Warning: <.*> proprietary attribute \".*\""),
Pattern.compile(".*Warning: <.*> unexpected or duplicate quote mark"),
Pattern.compile(".*Warning: <a> cannot copy name attribute to id"),
Pattern.compile(".*Warning: <a> escaping malformed URI reference"),
Pattern.compile(".*Warning: <blockquote> proprietary attribute \"pre\""),
Pattern.compile(".*Warning: discarding unexpected <.*>"),
Pattern.compile(".*Warning: discarding unexpected </.*>"),
Pattern.compile(".*Warning: entity \".*\" doesn't end in ';'"),
Pattern.compile(".*Warning: inserting implicit <.*>"),
Pattern.compile(".*Warning: inserting missing 'title' element"),
Pattern.compile(".*Warning: missing <!DOCTYPE> declaration"),
Pattern.compile(".*Warning: missing <.*>"),
Pattern.compile(".*Warning: missing </.*> before <.*>"),
Pattern.compile(".*Warning: nested emphasis <.*>"),
Pattern.compile(".*Warning: plain text isn't allowed in <.*> elements"),
Pattern.compile(".*Warning: replacing <p> by <br>"),
Pattern.compile(".*Warning: replacing invalid numeric character reference .*"),
Pattern.compile(".*Warning: replacing unexpected .* by </.*>"),
Pattern.compile(".*Warning: trimming empty <.*>"),
Pattern.compile(".*Warning: unescaped & or unknown entity \".*\""),
Pattern.compile(".*Warning: unescaped & which should be written as &amp;"),
Pattern.compile(".*Warning: using <br> in place of <p>")
};
int files;
int ok;
int warns;
int errs;
int css;
int overflow;
}

View File

@ -0,0 +1,52 @@
#!/bin/sh
#
# Copyright (c) 2012, 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.
#
# Run the "tidy" program over the files in a directory.
#
# Usage:
# sh tidy.sh <dir>
#
# The "tidy" program will be run on each HTML file in <dir>,
# and the output placed in the corresponding location in a new
# directory <dir>.tidy. The console output from running "tidy" will
# be saved in a corresponding file with an additional .tidy extension.
#
# Non-HTML files will be copied without modification from <dir> to
# <dir>.tidy, so that relative links within the directory tree are
# unaffected.
dir=$1
odir=$dir.tidy
( cd $dir ; find . -type f ) | \
while read file ; do
mkdir -p $odir/$(dirname $file)
case $file in
*.html )
cat $dir/$file | tidy 1>$odir/$file 2>$odir/$file.tidy
;;
* ) cp $dir/$file $odir/$file
;;
esac
done

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2012, 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.
*/
// key: compiler.err.dc.no.content
// key: compiler.note.note
// key: compiler.note.proc.messager
// run: backdoor
// options: -processor DocCommentProcessor -proc:only
/** @see */
class NoContent {
}