### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: dom/org/eclipse/jdt/internal/core/dom/rewrite/ImportRewriteAnalyzer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ImportRewriteAnalyzer.java,v retrieving revision 1.26 diff -u -r1.26 ImportRewriteAnalyzer.java --- dom/org/eclipse/jdt/internal/core/dom/rewrite/ImportRewriteAnalyzer.java 13 Apr 2010 18:53:21 -0000 1.26 +++ dom/org/eclipse/jdt/internal/core/dom/rewrite/ImportRewriteAnalyzer.java 31 Aug 2011 19:42:04 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,6 +11,9 @@ package org.eclipse.jdt.internal.core.dom.rewrite; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,8 +29,10 @@ import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Comment; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.LineComment; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.core.search.IJavaSearchConstants; @@ -58,6 +63,8 @@ private boolean useContextToFilterImplicitImports; private boolean findAmbiguousImports; + private IRegion[] preserveExistingCommentsRanges; + private int flags= 0; private static final int F_NEEDS_LEADING_DELIM= 2; @@ -89,6 +96,9 @@ this.replaceRange= evaluateReplaceRange(root); if (restoreExistingImports) { addExistingImports(root); + } else { + // collect all existing comments inside imports and concatenate them + this.preserveExistingCommentsRanges = retrieveExistingCommentsInImports(root); } PackageEntry[] order= new PackageEntry[importOrder.length]; @@ -261,12 +271,30 @@ int nextLength= next.getLength(); int nextOffsetLine= root.getLineNumber(nextOffset); + int extendedStart = root.getExtendedStartPosition(next); + int extendedLength = root.getExtendedLength(next); // if next import is on a different line, modify the end position to the next line begin offset if (currEndLine < nextOffsetLine) { currEndLine++; nextOffset= root.getPosition(currEndLine, 0); } - currPackage.add(new ImportDeclEntry(packName.length(), name, isStatic, new Region(currOffset, nextOffset - currOffset))); + // retrieve preceding and trailing comments if any + IRegion rangeBefore = null; + IRegion rangeAfter = null; + if (nextOffset != extendedStart) { + rangeBefore = new Region(extendedStart, extendedStart - nextOffset + 1); + } + if (nextLength != extendedLength) { + rangeAfter = new Region(nextOffset + nextLength, extendedLength - nextLength + 1); + } + currPackage.add( + new ImportDeclEntry( + packName.length(), + name, + isStatic, + new Region(currOffset, nextOffset - currOffset), + rangeBefore, + rangeAfter)); currOffset= nextOffset; curr= next; @@ -294,6 +322,102 @@ currPackage.add(new ImportDeclEntry(packName.length(), name, isStatic, new Region(curr.getStartPosition(), length))); } + private IRegion[] retrieveExistingCommentsInImports(CompilationUnit root) { + List/*ImportDeclaration*/ decls= root.imports(); + if (decls.isEmpty()) { + return null; + } + + List commentList = root.getCommentList(); + int numberOfComments = commentList.size(); + List regions = null; + int currentExtendedEnd = -1; + int currEndLine= -1; + + /* for the first comment, we only take the trailing comment if any and the replace range doesn't + * include the preceding comment + */ + for (int i= 0; i < decls.size(); i++) { + ImportDeclaration next= (ImportDeclaration) decls.get(i); + int nextOffset= next.getStartPosition(); + int nextLength= next.getLength(); + + int extendedStart = root.getExtendedStartPosition(next); + int extendedLength = root.getExtendedLength(next); + int nextOffsetLine= root.getLineNumber(nextOffset); + + if (nextOffset != extendedStart) { + // preceding comment + int lengthOfPrecedingComment = nextOffset - extendedStart; + if (i != 0) { + if (regions == null) { + regions = new ArrayList(); + } + regions.add(new Region(extendedStart, lengthOfPrecedingComment)); + } + + if (extendedLength != (nextLength + lengthOfPrecedingComment)) { + // Preceding and trailing comments + int regionLength = extendedLength - (nextLength + lengthOfPrecedingComment); + if (regions == null) { + regions = new ArrayList(); + } + regions.add(new Region(nextOffset + nextLength, regionLength)); + } + } else if (nextLength != extendedLength) { + // no extended start - only trailing comment + int regionLength = extendedLength - nextLength; + if (regions == null) { + regions = new ArrayList(); + } + regions.add(new Region(nextOffset + nextLength, regionLength)); + } + if (i > 0) { + // record comments between the previous comment and the current one that are not part + // of any comment extended range. + if ((nextOffsetLine - currEndLine) > 1) { + // check for comments between the two imports + LineComment comment = root.getAST().newLineComment(); + comment.setSourceRange(currentExtendedEnd + 1, 0); + int index = Collections.binarySearch(commentList, comment, new Comparator() { + public int compare(Object o1, Object o2) { + return ((Comment) o1).getStartPosition() - ((Comment) o2).getStartPosition(); + } + }); + // index = -(insertion point) - 1. + if (index < 0) { + loop: for (int j = -(index + 1); j < numberOfComments; j++) { + Comment currentComment = (Comment) commentList.get(j); + int commentStartPosition = currentComment.getStartPosition(); + int commentLength = currentComment.getLength(); + if ((commentStartPosition > currentExtendedEnd) + && ((commentStartPosition + commentLength - 1) < extendedStart)) { + if (regions == null) { + regions = new ArrayList(); + } + regions.add(new Region(commentStartPosition, commentLength)); + } else { + break loop; + } + } + } + } + } + currentExtendedEnd = extendedStart + extendedLength - 1; + currEndLine = root.getLineNumber(currentExtendedEnd); + } + if (regions == null) { + return null; + } + // sort regions according to their positions to restore comments in the same order + IRegion[] result = (IRegion[]) regions.toArray(new IRegion[regions.size()]); + Arrays.sort(result, new Comparator() { + public int compare(Object o1, Object o2) { + return ((IRegion) o1).getOffset() - ((IRegion) o2).getOffset(); + } + }); + return result; + } /** * Specifies that implicit imports (for types in java.lang, types in the same package as the rewrite * compilation unit and types in the compilation unit's main type) should not be created, except if necessary to @@ -617,7 +741,7 @@ boolean doStarImport= pack.hasStarImport(threshold, onDemandConflicts); if (doStarImport && (pack.find("*") == null)) { //$NON-NLS-1$ - String[] imports = getNewImportStrings(pack, isStatic, lineDelim); + String[] imports = getNewImportStrings(buffer, pack, isStatic, lineDelim); for (int j = 0, max = imports.length; j < max; j++) { stringsToInsert.add(imports[j]); } @@ -648,7 +772,16 @@ } else if (doStarImport && !currDecl.isOnDemand()) { String simpleName = currDecl.getTypeQualifiedName(); if (simpleName.indexOf('.') != -1) { - String str= getNewImportString(currDecl.getElementName(), isStatic, lineDelim); + IRegion rangeBefore = currDecl.getPrecedingCommentRange(); + if (rangeBefore != null) { + stringsToInsert.add(buffer.getText(rangeBefore.getOffset(), rangeBefore.getLength())); + } + IRegion rangeAfter = currDecl.getTrailingCommentRange(); + String trailingComment = null; + if (rangeAfter != null) { + trailingComment = buffer.getText(rangeAfter.getOffset(), rangeAfter.getLength()); + } + String str= getNewImportString(currDecl.getElementName(), isStatic, trailingComment, lineDelim); if (stringsToInsert.indexOf(str) == -1) { stringsToInsert.add(str); } @@ -657,6 +790,30 @@ } } + // insert back all existing imports comments since existing imports were not preserved + if (this.preserveExistingCommentsRanges != null) { + for (int i = 0, max = this.preserveExistingCommentsRanges.length; i < max; i++) { + IRegion region = this.preserveExistingCommentsRanges[i]; + String text = buffer.getText(region.getOffset(), region.getLength()); + // remove preceding whitespaces + int index = 0; + int length = text.length(); + loop: while (index < length) { + if (Character.isWhitespace(text.charAt(index))) { + index++; + } else { + break loop; + } + } + if (index != 0) { + text = text.substring(index); + } + if (!text.endsWith(lineDelim)) { + text += lineDelim; + } + stringsToInsert.add(text); + } + } int end= importsStart + importsLen; removeAndInsertNew(buffer, currPos, end, stringsToInsert, resEdit); @@ -787,6 +944,10 @@ } private String getNewImportString(String importName, boolean isStatic, String lineDelim) { + return getNewImportString(importName, isStatic, null, lineDelim); + } + + private String getNewImportString(String importName, boolean isStatic, String trailingComment, String lineDelim) { StringBuffer buf= new StringBuffer(); buf.append("import "); //$NON-NLS-1$ if (isStatic) { @@ -795,6 +956,9 @@ buf.append(importName); if (insertSpaceBeforeSemicolon()) buf.append(' '); buf.append(';'); + if (trailingComment != null) { + buf.append(trailingComment); + } buf.append(lineDelim); if (isStatic) { @@ -805,22 +969,51 @@ return buf.toString(); } - private String[] getNewImportStrings(PackageEntry packageEntry, boolean isStatic, String lineDelim) { + private String[] getNewImportStrings(IBuffer buffer, PackageEntry packageEntry, boolean isStatic, String lineDelim) { boolean isStarImportAdded = false; List allImports = new ArrayList(); int nImports = packageEntry.getNumberOfImports(); + StringBuffer allComments = null; for (int i= 0; i < nImports; i++) { ImportDeclEntry curr= packageEntry.getImportAt(i); String simpleName = curr.getTypeQualifiedName(); if (simpleName.indexOf('.') != -1) { // member type imports - we preserve it - allImports.add(getNewImportString(curr.getElementName(), isStatic, lineDelim)); + IRegion rangeBefore = curr.getPrecedingCommentRange(); + if (rangeBefore != null) { + allImports.add(buffer.getText(rangeBefore.getOffset(), rangeBefore.getLength())); + } + IRegion rangeAfter = curr.getTrailingCommentRange(); + String trailingComment = null; + if (rangeAfter != null) { + trailingComment = buffer.getText(rangeAfter.getOffset(), rangeAfter.getLength()); + } + allImports.add(getNewImportString(curr.getElementName(), isStatic, trailingComment, lineDelim)); } else if (!isStarImportAdded) { String starImportString= packageEntry.getName() + ".*"; //$NON-NLS-1$ allImports.add(getNewImportString(starImportString, isStatic, lineDelim)); isStarImportAdded = true; + } else { + // collect all comments + IRegion rangeBefore = curr.getPrecedingCommentRange(); + if (rangeBefore != null) { + if (allComments == null) { + allComments = new StringBuffer(); + } + allComments.append(buffer.getText(rangeBefore.getOffset(), rangeBefore.getLength())).append(lineDelim); + } + IRegion rangeAfter = curr.getTrailingCommentRange(); + if (rangeAfter != null) { + if (allComments == null) { + allComments = new StringBuffer(); + } + allComments.append(buffer.getText(rangeAfter.getOffset(), rangeAfter.getLength())).append(lineDelim); + } } } + if (allComments != null) { + allImports.add(0, String.valueOf(allComments)); + } return (String[]) allImports.toArray(new String[allImports.size()]); } @@ -879,6 +1072,20 @@ private IRegion sourceRange; private final boolean isStatic; private int containerNameLength; + private IRegion precedingCommentRange; + private IRegion trailingCommentRange; + + public ImportDeclEntry( + int containerNameLength, + String elementName, + boolean isStatic, + IRegion sourceRange, + IRegion precedingCommentRange, + IRegion trailingCommentRange) { + this(containerNameLength, elementName, isStatic, sourceRange); + this.precedingCommentRange = precedingCommentRange; + this.trailingCommentRange = trailingCommentRange; + } public ImportDeclEntry(int containerNameLength, String elementName, boolean isStatic, IRegion sourceRange) { this.elementName= elementName; @@ -929,7 +1136,14 @@ public IRegion getSourceRange() { return this.sourceRange; } + + public IRegion getPrecedingCommentRange() { + return this.precedingCommentRange; + } + public IRegion getTrailingCommentRange() { + return this.trailingCommentRange; + } } /* #P org.eclipse.jdt.core.tests.model Index: src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java,v retrieving revision 1.20 diff -u -r1.20 ImportRewriteTest.java --- src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java 13 Apr 2010 18:53:19 -0000 1.20 +++ src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java 31 Aug 2011 19:42:05 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -1307,6 +1307,186 @@ assertEqualString(units[2].getSource(), buf.toString()); } + public void testAddImports_bug24804() throws Exception { + + IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.lang.String;\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System;\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + String[] order= new String[] { "java" }; + + ImportRewrite imports= newImportsRewrite(cu, order, 99, 99, false); + imports.addImport("java.io.Exception"); + + apply(imports); + + buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.io.Exception;\n"); + buf.append("/** comment */\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + assertEqualString(cu.getSource(), buf.toString()); + } + + public void testAddImports_bug24804_2() throws Exception { + + IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.lang.AssertionError;//test\n"); + buf.append("\n"); + buf.append("/** comment2 */\n"); + buf.append("\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System;\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + String[] order= new String[] { "java" }; + + ImportRewrite imports= newImportsRewrite(cu, order, 99, 99, true); + imports.addImport("java.io.Exception"); + + apply(imports); + + buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.io.Exception;\n"); + buf.append("import java.lang.AssertionError;//test\n"); + buf.append("\n"); + buf.append("/** comment2 */\n"); + buf.append("\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System;\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + assertEqualString(cu.getSource(), buf.toString()); + } + + public void testAddImports_bug24804_3() throws Exception { + + IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.lang.String;//test\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System;\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + String[] order= new String[] { "java" }; + + ImportRewrite imports= newImportsRewrite(cu, order, 99, 99, false); + imports.addImport("java.io.Exception"); + + apply(imports); + + buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.io.Exception;\n"); + buf.append("//test\n"); + buf.append("/** comment */\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + assertEqualString(cu.getSource(), buf.toString()); + } + + public void testAddImports_bug24804_4() throws Exception { + + IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.lang.AssertionError;//test\n"); + buf.append("\n"); + buf.append("/** comment2 */\n"); + buf.append("\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System; /** comment3 */\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + String[] order= new String[] { "java" }; + + ImportRewrite imports= newImportsRewrite(cu, order, 99, 99, false); + imports.addImport("java.io.Exception"); + + apply(imports); + + buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.io.Exception;\n"); + buf.append("//test\n"); + buf.append("/** comment2 */\n"); + buf.append("/** comment */\n"); + buf.append("/** comment3 */\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + assertEqualString(cu.getSource(), buf.toString()); + } + + public void testAddImports_bug24804_5() throws Exception { + + IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.lang.AssertionError; //test\n"); + buf.append("\n"); + buf.append("/** comment2 */\n"); + buf.append("\n"); + buf.append("/** comment */\n"); + buf.append("import java.lang.System;\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + String[] order= new String[] { "java" }; + + ImportRewrite imports= newImportsRewrite(cu, order, 1, 1, false); + imports.addImport("java.io.Exception"); + + apply(imports); + + buf= new StringBuffer(); + buf.append("package pack1;\n"); + buf.append("\n"); + buf.append("import java.io.*;\n"); + buf.append("//test\n"); + buf.append("/** comment2 */\n"); + buf.append("/** comment */\n"); + buf.append("\n"); + buf.append("public class C {\n"); + buf.append("}\n"); + assertEqualString(cu.getSource(), buf.toString()); + } + private void assertAddedAndRemoved(ImportRewrite imports, String[] expectedAdded, String[] expectedRemoved, String[] expectedAddedStatic, String[] expectedRemovedStatic) { assertEqualStringsIgnoreOrder(imports.getAddedImports(), expectedAdded); assertEqualStringsIgnoreOrder(imports.getAddedStaticImports(), expectedAddedStatic); #P org.eclipse.jdt.ui.tests Index: ui/org/eclipse/jdt/ui/tests/core/ImportOrganizeTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/ImportOrganizeTest.java,v retrieving revision 1.89 diff -u -r1.89 ImportOrganizeTest.java --- ui/org/eclipse/jdt/ui/tests/core/ImportOrganizeTest.java 27 Apr 2011 08:56:53 -0000 1.89 +++ ui/org/eclipse/jdt/ui/tests/core/ImportOrganizeTest.java 31 Aug 2011 19:42:06 -0000 @@ -578,6 +578,7 @@ buf.append("package pack1;\r\n"); buf.append("\r\n"); buf.append("import java.util.Vector;\r\n"); + buf.append("// comment\r\n"); buf.append("\r\n"); buf.append("public class C {\r\n"); buf.append(" Vector v;\r\n"); @@ -2575,6 +2576,8 @@ buf.append("\n"); buf.append("import java.util.ArrayList;\n"); buf.append("import java.util.HashMap;\n"); + buf.append("// some comment;\n"); + buf.append("/*another comment*/\n"); buf.append("\n"); buf.append("public class C {\n"); buf.append(" public void foo() {\n"); @@ -2625,6 +2628,8 @@ buf.append("import java.util.HashMap;\n"); buf.append("\n"); buf.append("import static java.io.File.pathSeparator;\n"); + buf.append("// some comment;\n"); + buf.append("/*another comment*/\n"); buf.append("\n"); buf.append("public class C {\n"); buf.append(" public void foo() {\n");