### 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 18:25:06 -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,8 @@ package org.eclipse.jdt.internal.core.dom.rewrite; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,8 +28,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 +62,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 +95,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]; @@ -245,7 +254,11 @@ ImportDeclaration curr= (ImportDeclaration) decls.get(0); int currOffset= curr.getStartPosition(); int currLength= curr.getLength(); + int currentExtendedEnd = root.getExtendedStartPosition(curr) + root.getExtendedLength(curr) - 1; int currEndLine= root.getLineNumber(currOffset + currLength); + List commentList = root.getCommentList(); + int numberOfComments = commentList.size(); + List commentRegions = null; for (int i= 1; i < decls.size(); i++) { boolean isStatic= curr.isStatic(); @@ -261,14 +274,61 @@ 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); + } + // retrieve comments not part of the 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 (commentRegions == null) { + commentRegions = new ArrayList(); + } + commentRegions.add(new Region(commentStartPosition, commentLength)); + } else { + break loop; + } + } + } + } + currPackage.add( + new ImportDeclEntry( + packName.length(), + name, + isStatic, + new Region(currOffset, nextOffset - currOffset), + rangeBefore, + rangeAfter)); currOffset= nextOffset; curr= next; + currentExtendedEnd = extendedLength + extendedStart - 1; // add a comment entry for spacing between imports if (currEndLine < nextOffsetLine) { @@ -292,8 +352,101 @@ } int length= this.replaceRange.getOffset() + this.replaceRange.getLength() - curr.getStartPosition(); currPackage.add(new ImportDeclEntry(packName.length(), name, isStatic, new Region(curr.getStartPosition(), length))); + + if (commentRegions != null) { + this.preserveExistingCommentsRanges = (IRegion[]) commentRegions.toArray(new IRegion[commentRegions.size()]); + } } + 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; + } + return (IRegion[]) regions.toArray(new IRegion[regions.size()]); + } /** * 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 @@ -550,6 +703,8 @@ } } } + // concatenate existing comments inside the imports + return new Region(startPos, endPos - startPos); } else { int start= getPackageStatementEndPos(root); @@ -617,7 +772,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 +803,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 +821,17 @@ } } + // 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()); + if (!text.endsWith(lineDelim)) { + text += lineDelim; + } + stringsToInsert.add(text); + } + } int end= importsStart + importsLen; removeAndInsertNew(buffer, currPos, end, stringsToInsert, resEdit); @@ -787,6 +962,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 +974,9 @@ buf.append(importName); if (insertSpaceBeforeSemicolon()) buf.append(' '); buf.append(';'); + if (trailingComment != null) { + buf.append(trailingComment); + } buf.append(lineDelim); if (isStatic) { @@ -805,22 +987,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 +1090,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 +1154,14 @@ public IRegion getSourceRange() { return this.sourceRange; } + + public IRegion getPrecedingCommentRange() { + return this.precedingCommentRange; + } + public IRegion getTrailingCommentRange() { + return this.trailingCommentRange; + } } /*