### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java,v retrieving revision 1.9 diff -u -r1.9 FormatJavadocBlock.java --- formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java 10 Nov 2009 21:38:04 -0000 1.9 +++ formatter/org/eclipse/jdt/internal/formatter/FormatJavadocBlock.java 11 Feb 2010 12:13:37 -0000 @@ -31,6 +31,7 @@ final static int PARAM_TAG = 0x0020; final static int IN_PARAM_TAG = 0x0040; final static int IN_DESCRIPTION = 0x0080; + final static int IMMUTABLE = 0x0100; // constants final static int MAX_TAG_HIERARCHY = 10; @@ -53,6 +54,11 @@ case TAG_THROWS_VALUE: case TAG_EXCEPTION_VALUE: this.flags |= PARAM_TAG; + break; + case TAG_CODE_VALUE: + case TAG_LITERAL_VALUE: + this.flags |= IMMUTABLE; + break; } } @@ -141,6 +147,9 @@ } } addNode(text); + if (isImmutable()) { + text.immutable = true; + } } void clean() { @@ -322,6 +331,18 @@ return (this.flags & PARAM_TAG) == PARAM_TAG; } +/** + * Returns whether the block is immutable or not. + *
+ * Currently, only @code inline tag block are considered as immutable.
+ *
+ * @return true
if the block is immutable,
+ * false
otherwise.
+ */
+public boolean isImmutable() {
+ return (this.flags & IMMUTABLE) == IMMUTABLE;
+}
+
void setHeaderLine(int javadocLineStart) {
if (javadocLineStart == this.lineStart) {
this.flags |= ON_HEADER_LINE;
@@ -332,9 +353,10 @@
}
protected void toString(StringBuffer buffer) {
- if ((this.flags & INLINED) != 0) buffer.append('{');
+ boolean inlined = (this.flags & INLINED) != 0;
+ if (inlined) buffer.append(" {"); //$NON-NLS-1$
buffer.append('@');
- buffer.append(this.tagValue);
+ buffer.append(TAG_NAMES[this.tagValue]);
super.toString(buffer);
if (this.reference == null) {
buffer.append('\n');
@@ -345,6 +367,7 @@
}
if (this.nodesPtr > -1) {
for (int i = 0; i <= this.nodesPtr; i++) {
+ if (inlined) buffer.append('\t');
this.nodes[i].toString(buffer);
}
}
Index: formatter/org/eclipse/jdt/internal/formatter/FormatJavadocNode.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/FormatJavadocNode.java,v
retrieving revision 1.6
diff -u -r1.6 FormatJavadocNode.java
--- formatter/org/eclipse/jdt/internal/formatter/FormatJavadocNode.java 27 Jun 2008 16:04:07 -0000 1.6
+++ formatter/org/eclipse/jdt/internal/formatter/FormatJavadocNode.java 11 Feb 2010 12:13:37 -0000
@@ -56,6 +56,17 @@
return false;
}
+/**
+ * Returns whether the node is immutable or not. If true
, then
+ * the formatter will leave it contents unchanged.
+ *
+ * @return true
if the node is immutable, false
+ * otherwise.
+ */
+public boolean isImmutable() {
+ return false;
+}
+
public String toString() {
StringBuffer buffer = new StringBuffer();
toString(buffer);
Index: formatter/org/eclipse/jdt/internal/formatter/FormatJavadocText.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/FormatJavadocText.java,v
retrieving revision 1.8
diff -u -r1.8 FormatJavadocText.java
--- formatter/org/eclipse/jdt/internal/formatter/FormatJavadocText.java 27 Jun 2008 16:04:08 -0000 1.8
+++ formatter/org/eclipse/jdt/internal/formatter/FormatJavadocText.java 11 Feb 2010 12:13:37 -0000
@@ -31,6 +31,7 @@
long[] separators;
int separatorsPtr = -1;
private int htmlTagIndex = -1;
+ boolean immutable = false;
FormatJavadocNode[] htmlNodes;
int[] htmlIndexes;
int htmlNodesPtr = -1;
@@ -49,6 +50,7 @@
* child node.
*/
void appendText(FormatJavadocText text) {
+ this.immutable = text.immutable;
if (this.depth == text.depth) {
addSeparator(text);
this.sourceEnd = text.sourceEnd;
@@ -167,6 +169,7 @@
*
* @return true
if the node is an immutable tag,
* false
otherwise.
+ *@deprecated Use {@link #isImmutable()} instead
*/
public boolean isImmutableHtmlTag() {
return this.htmlTagIndex != -1 && (this.htmlTagIndex & JAVADOC_TAGS_ID_MASK) == JAVADOC_IMMUTABLE_TAGS_ID;
@@ -174,6 +177,21 @@
}
/**
+ * Returns whether the text is immutable or not.
+ *
+ * A text in considered as immutable when it belongs to an immutable block + * or when it's an immutable html tag. + *
+ * + * @returntrue
if the node is an immutable tag,
+ * false
otherwise.
+ */
+public boolean isImmutable() {
+ return this.immutable || (this.htmlTagIndex != -1 && (this.htmlTagIndex & JAVADOC_TAGS_ID_MASK) == JAVADOC_IMMUTABLE_TAGS_ID);
+
+}
+
+/**
* Returns whether the text at the given separator index position is after a
* separator tag or not.
*
Index: formatter/org/eclipse/jdt/internal/formatter/FormatterCommentParser.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/FormatterCommentParser.java,v
retrieving revision 1.31
diff -u -r1.31 FormatterCommentParser.java
--- formatter/org/eclipse/jdt/internal/formatter/FormatterCommentParser.java 9 Feb 2010 08:56:38 -0000 1.31
+++ formatter/org/eclipse/jdt/internal/formatter/FormatterCommentParser.java 11 Feb 2010 12:13:37 -0000
@@ -356,6 +356,7 @@
this.scanner.resetTo(this.index, this.javadocEnd);
return true;
}
+ this.tagValue = TAG_OTHERS_VALUE; // tag is invalid, do not keep the parsed tag value
return false;
}
@@ -382,6 +383,7 @@
}
this.scanner.resetTo(this.tagSourceEnd+1, this.javadocEnd);
}
+ this.tagValue = TAG_OTHERS_VALUE; // tag is invalid, do not keep the parsed tag value
}
return valid;
}
@@ -394,6 +396,7 @@
if (!valid) {
this.scanner.resetTo(this.tagSourceEnd+1, this.javadocEnd);
this.index = this.tagSourceEnd+1;
+ this.tagValue = TAG_OTHERS_VALUE; // tag is invalid, do not keep the parsed tag value
}
return valid;
}
@@ -506,7 +509,7 @@
if (length == TAG_LINK_LENGTH && CharOperation.equals(TAG_LINK, tagName)) {
this.tagValue = TAG_LINK_VALUE;
if (this.inlineTagStarted || (this.kind & COMPLETION_PARSER) != 0) {
- valid= parseReference();
+ valid = parseReference();
} else {
// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
// Cannot have @link outside inline comment
@@ -605,7 +608,6 @@
} else if (this.invalidTagName) {
this.textStart = previousPosition;
} else if (this.astPtr == ptr) {
- this.tagValue = TAG_OTHERS_VALUE; // tag is invalid, do not keep the parsed tag value
createTag();
}
return true;
@@ -620,6 +622,7 @@
// If invalid, restart from the end tag position
this.scanner.resetTo(this.tagSourceEnd+1, this.javadocEnd);
this.index = this.tagSourceEnd+1;
+ this.tagValue = TAG_OTHERS_VALUE; // tag is invalid, do not keep the parsed tag value
}
return valid;
}
Index: formatter/org/eclipse/jdt/internal/formatter/Scribe.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Scribe.java,v
retrieving revision 1.190
diff -u -r1.190 Scribe.java
--- formatter/org/eclipse/jdt/internal/formatter/Scribe.java 7 Jan 2010 20:18:49 -0000 1.190
+++ formatter/org/eclipse/jdt/internal/formatter/Scribe.java 11 Feb 2010 12:13:38 -0000
@@ -2866,7 +2866,7 @@
if (newLines > 0) newLines = 1;
}
}
- if (newLines == 0) {
+ if (newLines == 0 && (!node.isImmutable() || block.reference != null)) {
newLines = printJavadocBlockNodesNewLines(block, node, previousEnd);
}
printJavadocGapLines(previousEnd+1, nodeStart-1, newLines, clearBlankLines, false, null);
@@ -2898,18 +2898,16 @@
// Print node
if (node.isText()) {
FormatJavadocText text = (FormatJavadocText) node;
- if (text.isHtmlTag()) {
- if (text.isImmutableHtmlTag()) {
- // Indent if new line was added
- if (newLines > 0 && this.commentIndentation != null) {
- addInsertEdit(node.sourceStart, this.commentIndentation);
- this.column += this.commentIndentation.length();
- }
- printJavadocHtmlImmutableTag(text, block, newLines > 0);
- this.column += getTextLength(block, text);
- } else {
- printJavadocHtmlTag(text, block, newLines>0);
+ if (text.isImmutable()) {
+ // Indent if new line was added
+ if (newLines > 0 && this.commentIndentation != null) {
+ addInsertEdit(node.sourceStart, this.commentIndentation);
+ this.column += this.commentIndentation.length();
}
+ printJavadocImmutableText(text, block, newLines > 0);
+ this.column += getTextLength(block, text);
+ } else if (text.isHtmlTag()) {
+ printJavadocHtmlTag(text, block, newLines>0);
} else {
printJavadocText(text, block, newLines>0);
}
@@ -2934,15 +2932,29 @@
try {
this.scanner.resetTo(nodeStart , node.sourceEnd);
int length = 0;
- int newLines = 0;
boolean newLine = false;
boolean headerLine = block.isHeaderLine() && this.lastNumberOfNewLines == 0;
int firstColumn = 1 + this.indentationLevel + BLOCK_LINE_PREFIX_LENGTH;
if (this.commentIndentation != null) firstColumn += this.commentIndentation.length();
if (headerLine) maxColumn++;
- if (node.isText()) {
- FormatJavadocText text = (FormatJavadocText)node;
- if (text.isImmutableHtmlTag()) {
+ FormatJavadocText text = null;
+ boolean isImmutableNode = node.isImmutable();
+ boolean nodeIsText = node.isText();
+ if (nodeIsText) {
+ text = (FormatJavadocText)node;
+ } else {
+ FormatJavadocBlock inlinedBlock = (FormatJavadocBlock)node;
+ if (isImmutableNode) {
+ text = (FormatJavadocText) inlinedBlock.getLastNode();
+ length += inlinedBlock.tagEnd - inlinedBlock.sourceStart + 1; // tag length
+ if (nodeStart > (previousEnd+1)) {
+ length++; // include space between nodes
+ }
+ this.scanner.resetTo(text.sourceStart , node.sourceEnd);
+ }
+ }
+ if (text != null) {
+ if (isImmutableNode) {
if (nodeStart > (previousEnd+1)) {
length++; // include space between nodes
}
@@ -2952,8 +2964,10 @@
int token = this.scanner.getNextToken();
switch (token) {
case TerminalTokens.TokenNameWHITESPACE:
- if (CharOperation.indexOf('\n', this.scanner.source, this.scanner.startPosition, this.scanner.currentPosition) >= 0) {
- return newLines;
+ if (nodeIsText) {
+ if (CharOperation.indexOf('\n', this.scanner.source, this.scanner.startPosition, this.scanner.currentPosition) >= 0) {
+ return 0;
+ }
}
length = 1;
break;
@@ -2975,15 +2989,10 @@
}
lastColumn += length;
if (lastColumn > maxColumn) {
- newLines++;
- if (headerLine) {
- maxColumn--;
- headerLine = false;
- }
- lastColumn = firstColumn;
+ return 1;
}
}
- return newLines;
+ return 0;
}
if (text.isHtmlTag()) {
if (text.getHtmlTagID() == JAVADOC_SINGLE_BREAK_TAG_ID) {
@@ -3146,7 +3155,7 @@
private int getTextLength(FormatJavadocBlock block, FormatJavadocText text) {
// Special case for immutable tags
- if (text.isImmutableHtmlTag()) {
+ if (text.isImmutable()) {
this.scanner.resetTo(text.sourceStart , text.sourceEnd);
int textLength = 0;
while (!this.scanner.atEnd()) {
@@ -3468,23 +3477,16 @@
}
}
- private void printJavadocHtmlImmutableTag(FormatJavadocText text, FormatJavadocBlock block, boolean textOnNewLine) {
+ private void printJavadocImmutableText(FormatJavadocText text, FormatJavadocBlock block, boolean textOnNewLine) {
try {
// Iterate on text line separators
int lineNumber = text.lineStart;
this.scanner.tokenizeWhiteSpace = false;
StringBuffer buffer = null;
- for (int idx=1, max=text.separatorsPtr; idxnull
\n" +
" */\n" +
@@ -4398,6 +4397,695 @@
}
/**
+ * @bug 260381: [formatter] Javadoc formatter breaks {@code ...} tags.
+ * @test Ensure that the @code tag is similar to HTML tag
+ * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=260381"
+ */
+public void testBug260381() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @version {@code $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $ $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $}\n" +
+ " */\n" +
+ "public class X01 {\n" +
+ "}\n";
+ formatSource(source);
+}
+public void testBug260381b() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @version $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $ $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $
\n" +
+ " */\n" +
+ "public class X01b {\n" +
+ "}\n";
+ formatSource(source);
+}
+public void testBug260381c() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @version\n" +
+ " * 3.0 xxxxxxxxxxxxxx xwxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\n" +
+ " */\n" +
+ "public class X01c {\n" +
+ "}\n";
+ formatSource(source,
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @version 3.0 xxxxxxxxxxxxxx xwxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\n" +
+ " */\n" +
+ "public class X01c {\n" +
+ "}\n"
+ );
+}
+public void testBug260381d() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @see Object $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $ $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $
\n" +
+ " */\n" +
+ "public class X02 {\n" +
+ "}\n";
+ formatSource(source,
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * @author Myself\n" +
+ " * @see Object\n" +
+ " * $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $ $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $
\n" +
+ " */\n" +
+ "public class X02 {\n" +
+ "}\n"
+ );
+}
+public void testBug260381e() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Comments that can be formated in several lines...\n" +
+ " * \n" +
+ " * {@code $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $\n" +
+ " * $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $}\n" +
+ " */\n" +
+ "public class X03 {\n" +
+ "}\n";
+ formatSource(source);
+}
+public void testBug260381f() throws JavaModelException {
+ String source =
+ "/**\n" +
+ " * Literal inline tag should also be untouched by the formatter\n" +
+ " * \n" +
+ " * @version {@literal $Revision: 1.2 $ $Date: 2009/01/07 12:27:50 $ $Author:myself $ $Source: /projects/cvs/module/project/src/com/foo/Main.java,v $}\n" +
+ " */\n" +
+ "public class X04 {\n" +
+ "\n" +
+ "}\n";
+ formatSource(source);
+}
+public void testBug260381_wksp1_01() throws JavaModelException {
+ String source =
+ "package wksp1;\n" +
+ "\n" +
+ "public interface I01 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns all configured content types for the given source viewer. This list\n" +
+ " * tells the caller which content types must be configured for the given source \n" +
+ " * viewer, i.e. for which content types the given source viewer\'s functionalities\n" +
+ " * must be specified. This implementation always returns \n" +
+ " * new String[] { IDocument.DEFAULT_CONTENT_TYPE }
.\n" +
+ " *\n" +
+ " * @param source the source viewer to be configured by this configuration\n" +
+ " * @return the configured content types for the given viewer\n" +
+ " */\n" +
+ " public String[] getConfiguredContentTypes(String source);\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp1;\n" +
+ "\n" +
+ "public interface I01 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns all configured content types for the given source viewer. This\n" +
+ " * list tells the caller which content types must be configured for the\n" +
+ " * given source viewer, i.e. for which content types the given source\n" +
+ " * viewer\'s functionalities must be specified. This implementation always\n" +
+ " * returns \n" +
+ " * new String[] { IDocument.DEFAULT_CONTENT_TYPE }
.\n" +
+ " * \n" +
+ " * @param source\n" +
+ " * the source viewer to be configured by this configuration\n" +
+ " * @return the configured content types for the given viewer\n" +
+ " */\n" +
+ " public String[] getConfiguredContentTypes(String source);\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_01() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "public interface I01 {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For {@code f: A->B} and\n" +
+ " * {@code g: B->C}, composition is defined as the function h such that\n" +
+ " * {@code h(a) == g(f(a))} for each {@code a}.\n" +
+ " *\n" +
+ " * @see \n" +
+ " * function composition\n" +
+ " *\n" +
+ " * @param g the second function to apply\n" +
+ " * @param f the first function to apply\n" +
+ " * @return the composition of {@code f} and {@code g}\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I01 {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For {@code f: A->B} and\n" +
+ " * {@code g: B->C}, composition is defined as the function h such that\n" +
+ " * {@code h(a) == g(f(a))} for each {@code a}.\n" +
+ " * \n" +
+ " * @see function\n" +
+ " * composition\n" +
+ " * \n" +
+ " * @param g\n" +
+ " * the second function to apply\n" +
+ " * @param f\n" +
+ " * the first function to apply\n" +
+ " * @return the composition of {@code f} and {@code g}\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_01b() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "public interface I01b {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For f: A->B
and\n" +
+ " * g: B->C
, composition is defined as the function h such that\n" +
+ " * h(a) == g(f(a))
for each a
.\n" +
+ " *\n" +
+ " * @see \n" +
+ " * function composition\n" +
+ " *\n" +
+ " * @param g the second function to apply\n" +
+ " * @param f the first function to apply\n" +
+ " * @return the composition of f
and g
\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I01b {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For f: A->B
and\n" +
+ " * g: B->C
, composition is defined as the function h such that\n" +
+ " * h(a) == g(f(a))
for each a
.\n" +
+ " * \n" +
+ " * @see function\n" +
+ " * composition\n" +
+ " * \n" +
+ " * @param g\n" +
+ " * the second function to apply\n" +
+ " * @param f\n" +
+ " * the first function to apply\n" +
+ " * @return the composition of f
and g
\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_01c() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "public interface I01c {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For f: A->B
and\n" +
+ " * \n" +
+ " * g: B->C\n" +
+ " *
,\n" +
+ " * composition is defined as the function h such that\n" +
+ " * \n" +
+ " * h(a) == g(f(a))\n" +
+ " *
\n" +
+ " * for each\n" +
+ " * \n" +
+ " * a\n" +
+ " *
.\n" +
+ " *\n" +
+ " * @see \n" +
+ " * function composition\n" +
+ " *\n" +
+ " * @param g the second function to apply\n" +
+ " * @param f the first function to apply\n" +
+ " * @return the composition of f
and g
\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I01c {\n" +
+ " /**\n" +
+ " * Returns the composition of two functions. For f: A->B
and\n" +
+ " * \n" +
+ " * g: B->C\n" +
+ " *
, composition is defined as the function h such that \n" +
+ " * h(a) == g(f(a))\n" +
+ " *
for each \n" +
+ " * a\n" +
+ " *
.\n" +
+ " * \n" +
+ " * @see function\n" +
+ " * composition\n" +
+ " * \n" +
+ " * @param g\n" +
+ " * the second function to apply\n" +
+ " * @param f\n" +
+ " * the first function to apply\n" +
+ " * @return the composition of f
and g
\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_02() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I02 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Implementations of {@code computeNext} must invoke this method when\n" +
+ " * there are no elements left in the iteration.\n" +
+ " *\n" +
+ " * @return {@code null}; a convenience so your {@link #computeNext}\n" +
+ " * implementation can use the simple statement {@code return endOfData();}\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I02 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Implementations of {@code computeNext} must invoke this method\n" +
+ " * when there are no elements left in the iteration.\n" +
+ " * \n" +
+ " * @return {@code null}; a convenience so your {@link #computeNext}\n" +
+ " * implementation can use the simple statement\n" +
+ " * {@code return endOfData();}\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_03() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I03 {\n" +
+ " /**\n" +
+ " * A builder for creating immutable bimap instances, especially {@code public\n" +
+ " * static final} bimaps (\"constant bimaps\"). Example: {@code\n" +
+ " *\n" +
+ " * static final ImmutableBiMap WORD_TO_INT =\n" +
+ " * new ImmutableBiMap.Builder()\n" +
+ " * .put(\"one\", 1)\n" +
+ " * .put(\"two\", 2)\n" +
+ " * .put(\"three\", 3)\n" +
+ " * .build();}
\n" +
+ " *\n" +
+ " * For small immutable bimaps, the {@code ImmutableBiMap.of()} methods\n" +
+ " * are even more convenient.\n" +
+ " *\n" +
+ " * Builder instances can be reused - it is safe to call {@link #build}\n" +
+ " * multiple times to build multiple bimaps in series. Each bimap is a superset\n" +
+ " * of the bimaps created before it.\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I03 {\n" +
+ " /**\n" +
+ " * A builder for creating immutable bimap instances, especially\n" +
+ " * {@code public static final} bimaps (\"constant bimaps\"). Example:\n" +
+ " * \n" +
+ " *
\n" +
+ " * {\n" +
+ " * @code\n" +
+ " * static final ImmutableBiMap<String, Integer> WORD_TO_INT = new ImmutableBiMap.Builder<String, Integer>()\n" +
+ " * .put("one", 1).put("two", 2).put("three", 3).build();\n" +
+ " * }\n" +
+ " *
\n" +
+ " * \n" +
+ " * For small immutable bimaps, the {@code ImmutableBiMap.of()}\n" +
+ " * methods are even more convenient.\n" +
+ " * \n" +
+ " * \n" +
+ " * Builder instances can be reused - it is safe to call {@link #build}\n" +
+ " * multiple times to build multiple bimaps in series. Each bimap is a\n" +
+ " * superset of the bimaps created before it.\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_04() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I04 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns an immutable multiset containing the given elements.\n" +
+ " * \n" +
+ " *
The multiset is ordered by the first occurrence of each element. For\n" +
+ " * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields\n" +
+ " * a multiset with elements in the order {@code 2, 3, 3, 1}.\n" +
+ " *\n" +
+ " *
Note that if {@code c} is a {@code Collection}, then {@code\n" +
+ " * ImmutableMultiset.copyOf(c)} returns an {@code ImmutableMultiset}\n" +
+ " * containing each of the strings in {@code c}, while\n" +
+ " * {@code ImmutableMultiset.of(c)} returns an\n" +
+ " * {@code ImmutableMultiset>} containing one element (the\n" +
+ " * given collection itself).\n" +
+ " *\n" +
+ " * Note: Despite what the method name suggests, if {@code elements}\n" +
+ " * is an {@code ImmutableMultiset}, no copy will actually be performed, and\n" +
+ " * the given multiset itself will be returned.\n" +
+ " *\n" +
+ " * @throws NullPointerException if any of {@code elements} is null\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I04 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns an immutable multiset containing the given elements.\n" +
+ " * \n" +
+ " *
\n" +
+ " * The multiset is ordered by the first occurrence of each element. For\n" +
+ " * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))}\n" +
+ " * yields a multiset with elements in the order {@code 2, 3, 3, 1}.\n" +
+ " * \n" +
+ " *
\n" +
+ " * Note that if {@code c} is a {@code Collection}, then\n" +
+ " * {@code ImmutableMultiset.copyOf(c)} returns an\n" +
+ " * {@code ImmutableMultiset} containing each of the strings in\n" +
+ " * {@code c}, while {@code ImmutableMultiset.of(c)} returns an\n" +
+ " * {@code ImmutableMultiset>} containing one element (the\n" +
+ " * given collection itself).\n" +
+ " * \n" +
+ " * \n" +
+ " * Note: Despite what the method name suggests, if {@code elements}\n" +
+ " * is an {@code ImmutableMultiset}, no copy will actually be performed, and\n" +
+ " * the given multiset itself will be returned.\n" +
+ " * \n" +
+ " * @throws NullPointerException\n" +
+ " * if any of {@code elements} is null\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_05() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I05 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Indexes the specified values into a {@code Multimap} by applying a\n" +
+ " * specified function to each item in an {@code Iterable} of values. Each\n" +
+ " * value will be stored as a value in the specified multimap. The key used to\n" +
+ " * store that value in the multimap will be the result of calling the function\n" +
+ " * on that value. Depending on the multimap implementation, duplicate entries\n" +
+ " * (equal keys and equal values) may be collapsed.\n" +
+ " *\n" +
+ " *
For example,\n" +
+ " *\n" +
+ " *
\n" +
+ " * List<String> badGuys =\n" +
+ " * Arrays.asList(\"Inky\", \"Blinky\", \"Pinky\", \"Pinky\", \"Clyde\");\n" +
+ " * Function<String, Integer> stringLengthFunction = ...;\n" +
+ " * Multimap<Integer, String> index = Multimaps.newHashMultimap();\n" +
+ " * Multimaps.index(badGuys, stringLengthFunction, index);\n" +
+ " * System.out.println(index);
\n" +
+ " *\n" +
+ " * prints\n" +
+ " *\n" +
+ " * \n" +
+ " * {4=[Inky], 5=[Pinky, Clyde], 6=[Blinky]}
\n" +
+ " *\n" +
+ " * The {@link HashMultimap} collapses the duplicate occurrence of\n" +
+ " * {@code (5, \"Pinky\")}.\n" +
+ " *\n" +
+ " * @param values the values to add to the multimap\n" +
+ " * @param keyFunction the function used to produce the key for each value\n" +
+ " * @param multimap the multimap to store the key value pairs\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I05 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Indexes the specified values into a {@code Multimap} by applying a\n" +
+ " * specified function to each item in an {@code Iterable} of values. Each\n" +
+ " * value will be stored as a value in the specified multimap. The key used\n" +
+ " * to store that value in the multimap will be the result of calling the\n" +
+ " * function on that value. Depending on the multimap implementation,\n" +
+ " * duplicate entries (equal keys and equal values) may be collapsed.\n" +
+ " * \n" +
+ " * \n" +
+ " * For example,\n" +
+ " * \n" +
+ " *
\n" +
+ " * List<String> badGuys =\n" +
+ " * Arrays.asList(\"Inky\", \"Blinky\", \"Pinky\", \"Pinky\", \"Clyde\");\n" +
+ " * Function<String, Integer> stringLengthFunction = ...;\n" +
+ " * Multimap<Integer, String> index = Multimaps.newHashMultimap();\n" +
+ " * Multimaps.index(badGuys, stringLengthFunction, index);\n" +
+ " * System.out.println(index);\n" +
+ " *
\n" +
+ " * \n" +
+ " * prints\n" +
+ " * \n" +
+ " * \n" +
+ " * {4=[Inky], 5=[Pinky, Clyde], 6=[Blinky]}\n" +
+ " *
\n" +
+ " * \n" +
+ " * The {@link HashMultimap} collapses the duplicate occurrence of\n" +
+ " * {@code (5, \"Pinky\")}.\n" +
+ " * \n" +
+ " * @param values\n" +
+ " * the values to add to the multimap\n" +
+ " * @param keyFunction\n" +
+ " * the function used to produce the key for each value\n" +
+ " * @param multimap\n" +
+ " * the multimap to store the key value pairs\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_06() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I06 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Adds a number of occurrences of an element to this multiset. Note that if\n" +
+ " * {@code occurrences == 1}, this method has the identical effect to {@link\n" +
+ " * #add(Object)}. This method is functionally equivalent (except in the case\n" +
+ " * of overflow) to the call {@code addAll(Collections.nCopies(element,\n" +
+ " * occurrences))}, which would presumably perform much more poorly.\n" +
+ " *\n" +
+ " * @param element the element to add occurrences of; may be {@code null} only\n" +
+ " * if explicitly allowed by the implementation\n" +
+ " * @param occurrences the number of occurrences of this element to add. May\n" +
+ " * be zero, in which case no change will be made.\n" +
+ " * @return the previous count of this element before the operation; possibly\n" +
+ " * zero - TODO: make this the actual behavior!\n" +
+ " * @throws IllegalArgumentException if {@code occurrences} is negative, or if\n" +
+ " * this operation would result in more than {@link Integer#MAX_VALUE}\n" +
+ " * occurrences of the element \n" +
+ " * @throws NullPointerException if {@code element} is null and this\n" +
+ " * implementation does not permit null elements. Note that if {@code\n" +
+ " * occurrences} is zero, the implementation may opt to return normally.\n" +
+ " */\n" +
+ " boolean /*int*/ add(E element, int occurrences);\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I06 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Adds a number of occurrences of an element to this multiset. Note that if\n" +
+ " * {@code occurrences == 1}, this method has the identical effect to\n" +
+ " * {@link #add(Object)}. This method is functionally equivalent (except in\n" +
+ " * the case of overflow) to the call\n" +
+ " * {@code addAll(Collections.nCopies(element, occurrences))}, which would\n" +
+ " * presumably perform much more poorly.\n" +
+ " * \n" +
+ " * @param element\n" +
+ " * the element to add occurrences of; may be {@code null} only if\n" +
+ " * explicitly allowed by the implementation\n" +
+ " * @param occurrences\n" +
+ " * the number of occurrences of this element to add. May be zero,\n" +
+ " * in which case no change will be made.\n" +
+ " * @return the previous count of this element before the operation; possibly\n" +
+ " * zero - TODO: make this the actual behavior!\n" +
+ " * @throws IllegalArgumentException\n" +
+ " * if {@code occurrences} is negative, or if this operation\n" +
+ " * would result in more than {@link Integer#MAX_VALUE}\n" +
+ " * occurrences of the element\n" +
+ " * @throws NullPointerException\n" +
+ " * if {@code element} is null and this implementation does not\n" +
+ " * permit null elements. Note that if {@code occurrences} is\n" +
+ " * zero, the implementation may opt to return normally.\n" +
+ " */\n" +
+ " boolean /* int */add(E element, int occurrences);\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_07() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I07 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Constructs a new, empty multiset, sorted according to the specified\n" +
+ " * comparator. All elements inserted into the multiset must be mutually\n" +
+ " * comparable by the specified comparator: {@code comparator.compare(e1,\n" +
+ " * e2)} must not throw a {@code ClassCastException} for any elements {@code\n" +
+ " * e1} and {@code e2} in the multiset. If the user attempts to add an element\n" +
+ " * to the multiset that violates this constraint, the {@code add(Object)} call\n" +
+ " * will throw a {@code ClassCastException}.\n" +
+ " *\n" +
+ " * @param comparator the comparator that will be used to sort this multiset. A\n" +
+ " * null value indicates that the elements\' natural ordering should\n" +
+ " * be used.\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I07 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Constructs a new, empty multiset, sorted according to the specified\n" +
+ " * comparator. All elements inserted into the multiset must be mutually\n" +
+ " * comparable by the specified comparator:\n" +
+ " * {@code comparator.compare(e1, e2)} must not throw a\n" +
+ " * {@code ClassCastException} for any elements {@code e1} and {@code e2} in\n" +
+ " * the multiset. If the user attempts to add an element to the multiset that\n" +
+ " * violates this constraint, the {@code add(Object)} call will throw a\n" +
+ " * {@code ClassCastException}.\n" +
+ " * \n" +
+ " * @param comparator\n" +
+ " * the comparator that will be used to sort this multiset. A null\n" +
+ " * value indicates that the elements\' natural ordering\n" +
+ " * should be used.\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_08() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I08 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns the composition of a function and a predicate. For every {@code x},\n" +
+ " * the generated predicate returns {@code predicate(function(x))}.\n" +
+ " *\n" +
+ " * @return the composition of the provided function and predicate\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "public interface I08 {\n" +
+ "\n" +
+ " /**\n" +
+ " * Returns the composition of a function and a predicate. For every\n" +
+ " * {@code x}, the generated predicate returns {@code predicate(function(x))}\n" +
+ " * .\n" +
+ " * \n" +
+ " * @return the composition of the provided function and predicate\n" +
+ " */\n" +
+ " void foo();\n" +
+ "}\n"
+ );
+}
+public void testBug260381_wksp2_09() throws JavaModelException {
+ String source =
+ "package wksp2;\n" +
+ "\n" +
+ "/**\n" +
+ " A Conditional represents an if/then/else block.\n" +
+ " When this is created the code will already have\n" +
+ " the conditional check code. The code is optimized for branch\n" +
+ " offsets that fit in 2 bytes, though will handle 4 byte offsets.\n" +
+ "\n" +
+ " if condition\n" +
+ " then code\n" +
+ " else code\n" +
+ "
\n" +
+ " what actually gets built is\n" +
+ "\n" +
+ " if !condition branch to eb:\n" +
+ " then code\n" +
+ " goto end: // skip else\n" +
+ " eb:\n" +
+ " else code\n" +
+ " end:\n" +
+ "
\n" +
+ "*/\n" +
+ "public class X09 {\n" +
+ "\n" +
+ "}\n";
+ formatSource(source,
+ "package wksp2;\n" +
+ "\n" +
+ "/**\n" +
+ " * A Conditional represents an if/then/else block. When this is created the code\n" +
+ " * will already have the conditional check code. The code is optimized for\n" +
+ " * branch offsets that fit in 2 bytes, though will handle 4 byte offsets. \n" +
+ " if condition\n" +
+ " then code\n" +
+ " else code\n" +
+ "
\n" +
+ " * what actually gets built is \n" +
+ " if !condition branch to eb:\n" +
+ " then code\n" +
+ " goto end: // skip else\n" +
+ " eb:\n" +
+ " else code\n" +
+ " end:\n" +
+ "
\n" +
+ " */\n" +
+ "public class X09 {\n" +
+ "\n" +
+ "}\n"
+ );
+}
+
+/**
* @bug 260798: [formatter] Strange behavior of never join lines
* @test Ensure that the formatter indents lines correctly when never join lines pref is activated
* @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=260798"