@@ -230,53 +245,22 @@
if (t == null) {
env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
} else {
+ for (TagStackItem tsi: tagStack) {
+ if (tsi.tag.accepts(t)) {
+ while (tagStack.peek() != tsi) tagStack.pop();
+ break;
+ } else if (tsi.tag.endKind != HtmlTag.EndKind.OPTIONAL)
+ break;
+ }
+
+ checkStructure(tree, t);
+
// tag specific checks
switch (t) {
// check for out of sequence headers, such as ...
...
case H1: case H2: case H3: case H4: case H5: case H6:
checkHeader(tree, t);
break;
- // inside
- 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)) {
@@ -324,6 +308,58 @@
}
}
+ private void checkStructure(StartElementTree tree, HtmlTag t) {
+ Name treeName = tree.getName();
+ TagStackItem top = tagStack.peek();
+ switch (t.blockType) {
+ case BLOCK:
+ if (top == null || top.tag.accepts(t))
+ return;
+
+ switch (top.tree.getKind()) {
+ case START_ELEMENT: {
+ if (top.tag.blockType == HtmlTag.BlockType.INLINE) {
+ Name name = ((StartElementTree) top.tree).getName();
+ env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.element",
+ treeName, name);
+ return;
+ }
+ }
+ 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);
+ return;
+ }
+ }
+ break;
+
+ case INLINE:
+ if (top == null || top.tag.accepts(t))
+ return;
+ break;
+
+ case LIST_ITEM:
+ case TABLE_ITEM:
+ if (top != null) {
+ // reset this flag so subsequent bad inline content gets reported
+ top.flags.remove(Flag.REPORTED_BAD_INLINE);
+ if (top.tag.accepts(t))
+ return;
+ }
+ break;
+
+ case OTHER:
+ env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
+ return;
+ }
+
+ env.messages.error(HTML, tree, "dc.tag.not.allowed.here", treeName);
+ }
+
private void checkHeader(StartElementTree tree, HtmlTag tag) {
// verify the new tag
if (getHeaderLevel(tag) > getHeaderLevel(currHeaderTag) + 1) {
@@ -378,10 +414,6 @@
&& !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();
done = true;
break;
@@ -763,7 +795,7 @@
for (DocTree d: list) {
switch (d.getKind()) {
case TEXT:
- if (!((TextTree) d).getBody().trim().isEmpty())
+ if (hasNonWhitespace((TextTree) d))
return;
break;
default:
@@ -772,6 +804,16 @@
}
env.messages.warning(SYNTAX, tree, "dc.empty", tree.getKind().tagName);
}
+
+ boolean hasNonWhitespace(TextTree tree) {
+ String s = tree.getBody();
+ for (int i = 0; i < s.length(); i++) {
+ if (!Character.isWhitespace(s.charAt(i)))
+ return true;
+ }
+ return false;
+ }
+
//
}
diff -r c1c9a9e11b26 -r 8f719dc43e1f langtools/src/share/classes/com/sun/tools/doclint/HtmlTag.java
--- a/langtools/src/share/classes/com/sun/tools/doclint/HtmlTag.java Mon Jan 21 10:00:46 2013 -0800
+++ b/langtools/src/share/classes/com/sun/tools/doclint/HtmlTag.java Mon Jan 21 10:07:37 2013 -0800
@@ -57,16 +57,22 @@
B(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
- BLOCKQUOTE,
+ BIG(BlockType.INLINE, EndKind.REQUIRED,
+ EnumSet.of(Flag.EXPECT_CONTENT)),
+
+ BLOCKQUOTE(BlockType.BLOCK, EndKind.REQUIRED,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
BODY(BlockType.OTHER, EndKind.REQUIRED),
BR(BlockType.INLINE, EndKind.NONE,
attrs(AttrKind.USE_CSS, CLEAR)),
- CAPTION(EnumSet.of(Flag.EXPECT_CONTENT)),
+ CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED,
+ EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
- CENTER,
+ CENTER(BlockType.BLOCK, EndKind.REQUIRED,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
CITE(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
@@ -74,17 +80,23 @@
CODE(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
- DD(BlockType.BLOCK, EndKind.OPTIONAL,
- EnumSet.of(Flag.EXPECT_CONTENT)),
+ DD(BlockType.LIST_ITEM, EndKind.OPTIONAL,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
- DIV,
+ DIV(BlockType.BLOCK, EndKind.REQUIRED,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
DL(BlockType.BLOCK, EndKind.REQUIRED,
- EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
- attrs(AttrKind.USE_CSS, COMPACT)),
+ EnumSet.of(Flag.EXPECT_CONTENT),
+ attrs(AttrKind.USE_CSS, COMPACT)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == DT) || (t == DD);
+ }
+ },
- DT(BlockType.BLOCK, EndKind.OPTIONAL,
- EnumSet.of(Flag.EXPECT_CONTENT)),
+ DT(BlockType.LIST_ITEM, EndKind.OPTIONAL,
+ EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
EM(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.NO_NEST)),
@@ -97,12 +109,12 @@
FRAMESET(BlockType.OTHER, EndKind.REQUIRED),
- H1,
- H2,
- H3,
- H4,
- H5,
- H6,
+ H1(BlockType.BLOCK, EndKind.REQUIRED),
+ H2(BlockType.BLOCK, EndKind.REQUIRED),
+ H3(BlockType.BLOCK, EndKind.REQUIRED),
+ H4(BlockType.BLOCK, EndKind.REQUIRED),
+ H5(BlockType.BLOCK, EndKind.REQUIRED),
+ H6(BlockType.BLOCK, EndKind.REQUIRED),
HEAD(BlockType.OTHER, EndKind.REQUIRED),
@@ -118,31 +130,54 @@
attrs(AttrKind.OBSOLETE, NAME),
attrs(AttrKind.USE_CSS, ALIGN, HSPACE, VSPACE, BORDER)),
- LI(BlockType.BLOCK, EndKind.OPTIONAL),
+ LI(BlockType.LIST_ITEM, EndKind.OPTIONAL,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
LINK(BlockType.OTHER, EndKind.NONE),
- MENU,
+ MENU(BlockType.BLOCK, EndKind.REQUIRED) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == LI);
+ }
+ },
META(BlockType.OTHER, EndKind.NONE),
NOFRAMES(BlockType.OTHER, EndKind.REQUIRED),
- NOSCRIPT(BlockType.OTHER, EndKind.REQUIRED),
+ NOSCRIPT(BlockType.BLOCK, EndKind.REQUIRED),
OL(BlockType.BLOCK, EndKind.REQUIRED,
- EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
- attrs(AttrKind.USE_CSS, START, TYPE)),
+ EnumSet.of(Flag.EXPECT_CONTENT),
+ attrs(AttrKind.USE_CSS, START, TYPE)){
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == LI);
+ }
+ },
P(BlockType.BLOCK, EndKind.OPTIONAL,
EnumSet.of(Flag.EXPECT_CONTENT),
attrs(AttrKind.USE_CSS, ALIGN)),
- PRE(EnumSet.of(Flag.EXPECT_CONTENT)),
+ PRE(BlockType.BLOCK, EndKind.REQUIRED,
+ EnumSet.of(Flag.EXPECT_CONTENT)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ switch (t) {
+ case IMG: case BIG: case SMALL: case SUB: case SUP:
+ return false;
+ default:
+ return (t.blockType == BlockType.INLINE);
+ }
+ }
+ },
SCRIPT(BlockType.OTHER, EndKind.REQUIRED),
- SMALL(BlockType.INLINE, EndKind.REQUIRED),
+ SMALL(BlockType.INLINE, EndKind.REQUIRED,
+ EnumSet.of(Flag.EXPECT_CONTENT)),
SPAN(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT)),
@@ -157,37 +192,70 @@
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
TABLE(BlockType.BLOCK, EndKind.REQUIRED,
- EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
+ EnumSet.of(Flag.EXPECT_CONTENT),
attrs(AttrKind.OK, SUMMARY, Attr.FRAME, RULES, BORDER,
CELLPADDING, CELLSPACING),
- attrs(AttrKind.USE_CSS, ALIGN, WIDTH, BGCOLOR)),
+ attrs(AttrKind.USE_CSS, ALIGN, WIDTH, BGCOLOR)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ switch (t) {
+ case CAPTION:
+ case THEAD: case TBODY: case TFOOT:
+ case TR: // HTML 3.2
+ return true;
+ default:
+ return false;
+ }
+ }
+ },
- TBODY(BlockType.BLOCK, EndKind.REQUIRED,
- EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_TEXT),
- attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)),
+ TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED,
+ EnumSet.of(Flag.EXPECT_CONTENT),
+ attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == TR);
+ }
+ },
- TD(BlockType.BLOCK, EndKind.OPTIONAL,
+ TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
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)),
+ TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED,
+ attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == TR);
+ }
+ },
- TH(BlockType.BLOCK, EndKind.OPTIONAL,
+ TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
+ EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
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)),
+ THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED,
+ attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == TR);
+ }
+ },
TITLE(BlockType.OTHER, EndKind.REQUIRED),
- TR(BlockType.BLOCK, EndKind.OPTIONAL,
- EnumSet.of(Flag.NO_TEXT),
+ TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN),
- attrs(AttrKind.USE_CSS, BGCOLOR)),
+ attrs(AttrKind.USE_CSS, BGCOLOR)) {
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == TH) || (t == TD);
+ }
+ },
TT(BlockType.INLINE, EndKind.REQUIRED,
EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
@@ -196,8 +264,13 @@
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)),
+ EnumSet.of(Flag.EXPECT_CONTENT),
+ attrs(AttrKind.USE_CSS, COMPACT, TYPE)){
+ @Override
+ public boolean accepts(HtmlTag t) {
+ return (t == LI);
+ }
+ },
VAR(BlockType.INLINE, EndKind.REQUIRED);
@@ -207,6 +280,8 @@
public static enum BlockType {
BLOCK,
INLINE,
+ LIST_ITEM,
+ TABLE_ITEM,
OTHER;
}
@@ -220,9 +295,10 @@
}
public static enum Flag {
+ ACCEPTS_BLOCK,
+ ACCEPTS_INLINE,
EXPECT_CONTENT,
- NO_NEST,
- NO_TEXT
+ NO_NEST
}
public static enum Attr {
@@ -300,22 +376,14 @@
public final Set