| /******************************************************************************* |
| * Copyright (c) 2000, 2009 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 |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.dom.rewrite; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ImportDeclaration; |
| import org.eclipse.jdt.core.dom.PackageDeclaration; |
| import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.core.search.TypeNameRequestor; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.text.edits.DeleteEdit; |
| import org.eclipse.text.edits.InsertEdit; |
| import org.eclipse.text.edits.MultiTextEdit; |
| |
| public final class ImportRewriteAnalyzer { |
| |
| private final ICompilationUnit compilationUnit; |
| private final ArrayList packageEntries; |
| |
| private final List importsCreated; |
| private final List staticImportsCreated; |
| |
| private final IRegion replaceRange; |
| |
| private final int importOnDemandThreshold; |
| private final int staticImportOnDemandThreshold; |
| |
| private boolean filterImplicitImports; |
| private boolean findAmbiguousImports; |
| |
| private int flags= 0; |
| |
| private static final int F_NEEDS_LEADING_DELIM= 2; |
| private static final int F_NEEDS_TRAILING_DELIM= 4; |
| |
| private static final String JAVA_LANG= "java.lang"; //$NON-NLS-1$ |
| |
| public ImportRewriteAnalyzer(ICompilationUnit cu, CompilationUnit root, String[] importOrder, int threshold, int staticThreshold, boolean restoreExistingImports) { |
| this.compilationUnit= cu; |
| this.importOnDemandThreshold= threshold; |
| this.staticImportOnDemandThreshold= staticThreshold; |
| |
| this.filterImplicitImports= true; |
| this.findAmbiguousImports= true; //!restoreExistingImports; |
| |
| this.packageEntries= new ArrayList(20); |
| this.importsCreated= new ArrayList(); |
| this.staticImportsCreated= new ArrayList(); |
| this.flags= 0; |
| |
| this.replaceRange= evaluateReplaceRange(root); |
| if (restoreExistingImports) { |
| addExistingImports(root); |
| } |
| |
| PackageEntry[] order= new PackageEntry[importOrder.length]; |
| for (int i= 0; i < order.length; i++) { |
| String curr= importOrder[i]; |
| if (curr.length() > 0 && curr.charAt(0) == '#') { |
| curr= curr.substring(1); |
| order[i]= new PackageEntry(curr, curr, true); // static import group |
| } else { |
| order[i]= new PackageEntry(curr, curr, false); // normal import group |
| } |
| } |
| |
| addPreferenceOrderHolders(order); |
| } |
| |
| private int getSpacesBetweenImportGroups() { |
| try { |
| int num= Integer.parseInt(this.compilationUnit.getJavaProject().getOption(DefaultCodeFormatterConstants.FORMATTER_BLANK_LINES_BETWEEN_IMPORT_GROUPS, true)); |
| if (num >= 0) |
| return num; |
| } catch (NumberFormatException e) { |
| // fall through |
| } |
| return 1; |
| } |
| |
| private boolean insertSpaceBeforeSemicolon() { |
| return JavaCore.INSERT.equals(this.compilationUnit.getJavaProject().getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_SEMICOLON, true)); |
| } |
| |
| private void addPreferenceOrderHolders(PackageEntry[] preferenceOrder) { |
| if (this.packageEntries.isEmpty()) { |
| // all new: copy the elements |
| for (int i= 0; i < preferenceOrder.length; i++) { |
| this.packageEntries.add(preferenceOrder[i]); |
| } |
| } else { |
| // match the preference order entries to existing imports |
| // entries not found are appended after the last successfully matched entry |
| |
| PackageEntry[] lastAssigned= new PackageEntry[preferenceOrder.length]; |
| |
| // find an existing package entry that matches most |
| for (int k= 0; k < this.packageEntries.size(); k++) { |
| PackageEntry entry= (PackageEntry) this.packageEntries.get(k); |
| if (!entry.isComment()) { |
| String currName= entry.getName(); |
| int currNameLen= currName.length(); |
| int bestGroupIndex= -1; |
| int bestGroupLen= -1; |
| for (int i= 0; i < preferenceOrder.length; i++) { |
| boolean currPrevStatic= preferenceOrder[i].isStatic(); |
| if (currPrevStatic == entry.isStatic()) { |
| String currPrefEntry= preferenceOrder[i].getName(); |
| int currPrefLen= currPrefEntry.length(); |
| if (currName.startsWith(currPrefEntry) && currPrefLen >= bestGroupLen) { |
| if (currPrefLen == currNameLen || currName.charAt(currPrefLen) == '.') { |
| if (bestGroupIndex == -1 || currPrefLen > bestGroupLen) { |
| bestGroupLen= currPrefLen; |
| bestGroupIndex= i; |
| } |
| } |
| } |
| } |
| } |
| if (bestGroupIndex != -1) { |
| entry.setGroupID(preferenceOrder[bestGroupIndex].getName()); |
| lastAssigned[bestGroupIndex]= entry; // remember last entry |
| } |
| } |
| } |
| // fill in not-assigned categories, keep partial order |
| int currAppendIndex= 0; |
| for (int i= 0; i < lastAssigned.length; i++) { |
| PackageEntry entry= lastAssigned[i]; |
| if (entry == null) { |
| PackageEntry newEntry= preferenceOrder[i]; |
| if (currAppendIndex == 0 && !newEntry.isStatic()) { |
| currAppendIndex= getIndexAfterStatics(); |
| } |
| this.packageEntries.add(currAppendIndex, newEntry); |
| currAppendIndex++; |
| } else { |
| currAppendIndex= this.packageEntries.indexOf(entry) + 1; |
| } |
| } |
| } |
| } |
| |
| private static String getQualifier(ImportDeclaration decl) { |
| String name= decl.getName().getFullyQualifiedName(); |
| return decl.isOnDemand() ? name : Signature.getQualifier(name); |
| } |
| |
| private static String getFullName(ImportDeclaration decl) { |
| String name= decl.getName().getFullyQualifiedName(); |
| return decl.isOnDemand() ? name + ".*": name; //$NON-NLS-1$ |
| } |
| |
| private void addExistingImports(CompilationUnit root) { |
| List/*ImportDeclaration*/ decls= root.imports(); |
| if (decls.isEmpty()) { |
| return; |
| } |
| PackageEntry currPackage= null; |
| |
| ImportDeclaration curr= (ImportDeclaration) decls.get(0); |
| int currOffset= curr.getStartPosition(); |
| int currLength= curr.getLength(); |
| int currEndLine= root.getLineNumber(currOffset + currLength); |
| |
| for (int i= 1; i < decls.size(); i++) { |
| boolean isStatic= curr.isStatic(); |
| String name= getFullName(curr); |
| String packName= getQualifier(curr); |
| if (currPackage == null || currPackage.compareTo(packName, isStatic) != 0) { |
| currPackage= new PackageEntry(packName, null, isStatic); |
| this.packageEntries.add(currPackage); |
| } |
| |
| ImportDeclaration next= (ImportDeclaration) decls.get(i); |
| int nextOffset= next.getStartPosition(); |
| int nextLength= next.getLength(); |
| int nextOffsetLine= root.getLineNumber(nextOffset); |
| |
| // 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(name, isStatic, new Region(currOffset, nextOffset - currOffset))); |
| currOffset= nextOffset; |
| curr= next; |
| |
| // add a comment entry for spacing between imports |
| if (currEndLine < nextOffsetLine) { |
| nextOffset= root.getPosition(nextOffsetLine, 0); |
| |
| currPackage= new PackageEntry(); // create a comment package entry for this |
| this.packageEntries.add(currPackage); |
| currPackage.add(new ImportDeclEntry(null, false, new Region(currOffset, nextOffset - currOffset))); |
| |
| currOffset= nextOffset; |
| } |
| currEndLine= root.getLineNumber(nextOffset + nextLength); |
| } |
| |
| boolean isStatic= curr.isStatic(); |
| String name= getFullName(curr); |
| String packName= getQualifier(curr); |
| if (currPackage == null || currPackage.compareTo(packName, isStatic) != 0) { |
| currPackage= new PackageEntry(packName, null, isStatic); |
| this.packageEntries.add(currPackage); |
| } |
| int length= this.replaceRange.getOffset() + this.replaceRange.getLength() - curr.getStartPosition(); |
| currPackage.add(new ImportDeclEntry(name, isStatic, new Region(curr.getStartPosition(), length))); |
| } |
| |
| /** |
| * Sets that implicit imports (types in default package, CU- package and |
| * 'java.lang') should not be created. Note that this is a heuristic filter and can |
| * lead to missing imports, e.g. in cases where a type is forced to be specified |
| * due to a name conflict. |
| * By default, the filter is enabled. |
| * @param filterImplicitImports The filterImplicitImports to set |
| */ |
| public void setFilterImplicitImports(boolean filterImplicitImports) { |
| this.filterImplicitImports= filterImplicitImports; |
| } |
| |
| /** |
| * When set searches for imports that can not be folded into on-demand |
| * imports but must be specified explicitly |
| * @param findAmbiguousImports The new value |
| */ |
| public void setFindAmbiguousImports(boolean findAmbiguousImports) { |
| this.findAmbiguousImports= findAmbiguousImports; |
| } |
| |
| private static class PackageMatcher { |
| private String newName; |
| private String bestName; |
| private int bestMatchLen; |
| |
| public PackageMatcher() { |
| // initialization in 'initialize' |
| } |
| |
| public void initialize(String newImportName, String bestImportName) { |
| this.newName= newImportName; |
| this.bestName= bestImportName; |
| this.bestMatchLen= getCommonPrefixLength(bestImportName, newImportName); |
| } |
| |
| public boolean isBetterMatch(String currName, boolean preferCurr) { |
| boolean isBetter; |
| int currMatchLen= getCommonPrefixLength(currName, this.newName); |
| int matchDiff= currMatchLen - this.bestMatchLen; |
| if (matchDiff == 0) { |
| if (currMatchLen == this.newName.length() && currMatchLen == currName.length() && currMatchLen == this.bestName.length()) { |
| // duplicate entry and complete match |
| isBetter= preferCurr; |
| } else { |
| isBetter= sameMatchLenTest(currName); |
| } |
| } else { |
| isBetter= (matchDiff > 0); // curr has longer match |
| } |
| if (isBetter) { |
| this.bestName= currName; |
| this.bestMatchLen= currMatchLen; |
| } |
| return isBetter; |
| } |
| |
| private boolean sameMatchLenTest(String currName) { |
| int matchLen= this.bestMatchLen; |
| // known: bestName and currName differ from newName at position 'matchLen' |
| // currName and bestName don't have to differ at position 'matchLen' |
| |
| // determine the order and return true if currName is closer to newName |
| char newChar= getCharAt(this.newName, matchLen); |
| char currChar= getCharAt(currName, matchLen); |
| char bestChar= getCharAt(this.bestName, matchLen); |
| |
| if (newChar < currChar) { |
| if (bestChar < newChar) { // b < n < c |
| return (currChar - newChar) < (newChar - bestChar); // -> (c - n) < (n - b) |
| } else { // n < b && n < c |
| if (currChar == bestChar) { // longer match between curr and best |
| return false; // keep curr and best together, new should be before both |
| } else { |
| return currChar < bestChar; // -> (c < b) |
| } |
| } |
| } else { |
| if (bestChar > newChar) { // c < n < b |
| return (newChar - currChar) < (bestChar - newChar); // -> (n - c) < (b - n) |
| } else { // n > b && n > c |
| if (currChar == bestChar) { // longer match between curr and best |
| return true; // keep curr and best together, new should be ahead of both |
| } else { |
| return currChar > bestChar; // -> (c > b) |
| } |
| } |
| } |
| } |
| } |
| |
| /* package */ static int getCommonPrefixLength(String s, String t) { |
| int len= Math.min(s.length(), t.length()); |
| for (int i= 0; i < len; i++) { |
| if (s.charAt(i) != t.charAt(i)) { |
| return i; |
| } |
| } |
| return len; |
| } |
| |
| /* package */ static char getCharAt(String str, int index) { |
| if (str.length() > index) { |
| return str.charAt(index); |
| } |
| return 0; |
| } |
| |
| private PackageEntry findBestMatch(String newName, boolean isStatic) { |
| if (this.packageEntries.isEmpty()) { |
| return null; |
| } |
| String groupId= null; |
| int longestPrefix= -1; |
| // find the matching group |
| for (int i= 0; i < this.packageEntries.size(); i++) { |
| PackageEntry curr= (PackageEntry) this.packageEntries.get(i); |
| if (isStatic == curr.isStatic()) { |
| String currGroup= curr.getGroupID(); |
| if (currGroup != null && newName.startsWith(currGroup)) { |
| int prefixLen= currGroup.length(); |
| if (prefixLen == newName.length()) { |
| return curr; // perfect fit, use entry |
| } |
| if ((newName.charAt(prefixLen) == '.' || prefixLen == 0) && prefixLen > longestPrefix) { |
| longestPrefix= prefixLen; |
| groupId= currGroup; |
| } |
| } |
| } |
| } |
| PackageEntry bestMatch= null; |
| PackageMatcher matcher= new PackageMatcher(); |
| matcher.initialize(newName, ""); //$NON-NLS-1$ |
| for (int i= 0; i < this.packageEntries.size(); i++) { // find the best match with the same group |
| PackageEntry curr= (PackageEntry) this.packageEntries.get(i); |
| if (!curr.isComment() && curr.isStatic() == isStatic) { |
| if (groupId == null || groupId.equals(curr.getGroupID())) { |
| boolean preferrCurr= (bestMatch == null) || (curr.getNumberOfImports() > bestMatch.getNumberOfImports()); |
| if (matcher.isBetterMatch(curr.getName(), preferrCurr)) { |
| bestMatch= curr; |
| } |
| } |
| } |
| } |
| return bestMatch; |
| } |
| |
| private static boolean isImplicitImport(String qualifier, ICompilationUnit cu) { |
| if (JAVA_LANG.equals(qualifier)) { |
| return true; |
| } |
| String packageName= cu.getParent().getElementName(); |
| if (qualifier.equals(packageName)) { |
| return true; |
| } |
| String mainTypeName= JavaCore.removeJavaLikeExtension(cu.getElementName()); |
| if (packageName.length() == 0) { |
| return qualifier.equals(mainTypeName); |
| } |
| return qualifier.equals(packageName +'.' + mainTypeName); |
| } |
| |
| public void addImport(String fullTypeName, boolean isStatic) { |
| String typeContainerName= Signature.getQualifier(fullTypeName); |
| ImportDeclEntry decl= new ImportDeclEntry(fullTypeName, isStatic, null); |
| sortIn(typeContainerName, decl, isStatic); |
| } |
| |
| public boolean removeImport(String qualifiedName, boolean isStatic) { |
| String containerName= Signature.getQualifier(qualifiedName); |
| |
| int nPackages= this.packageEntries.size(); |
| for (int i= 0; i < nPackages; i++) { |
| PackageEntry entry= (PackageEntry) this.packageEntries.get(i); |
| if (entry.compareTo(containerName, isStatic) == 0) { |
| if (entry.remove(qualifiedName, isStatic)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private int getIndexAfterStatics() { |
| for (int i= 0; i < this.packageEntries.size(); i++) { |
| if (!((PackageEntry) this.packageEntries.get(i)).isStatic()) { |
| return i; |
| } |
| } |
| return this.packageEntries.size(); |
| } |
| |
| |
| private void sortIn(String typeContainerName, ImportDeclEntry decl, boolean isStatic) { |
| PackageEntry bestMatch= findBestMatch(typeContainerName, isStatic); |
| if (bestMatch == null) { |
| PackageEntry packEntry= new PackageEntry(typeContainerName, null, isStatic); |
| packEntry.add(decl); |
| int insertPos= packEntry.isStatic() ? 0 : getIndexAfterStatics(); |
| this.packageEntries.add(insertPos, packEntry); |
| } else { |
| int cmp= typeContainerName.compareTo(bestMatch.getName()); |
| if (cmp == 0) { |
| bestMatch.sortIn(decl); |
| } else { |
| // create a new package entry |
| String group= bestMatch.getGroupID(); |
| if (group != null) { |
| if (!typeContainerName.startsWith(group)) { |
| group= null; |
| } |
| } |
| PackageEntry packEntry= new PackageEntry(typeContainerName, group, isStatic); |
| packEntry.add(decl); |
| int index= this.packageEntries.indexOf(bestMatch); |
| if (cmp < 0) { // insert ahead of best match |
| this.packageEntries.add(index, packEntry); |
| } else { // insert after best match |
| this.packageEntries.add(index + 1, packEntry); |
| } |
| } |
| } |
| } |
| |
| private IRegion evaluateReplaceRange(CompilationUnit root) { |
| List imports= root.imports(); |
| if (!imports.isEmpty()) { |
| ImportDeclaration first= (ImportDeclaration) imports.get(0); |
| ImportDeclaration last= (ImportDeclaration) imports.get(imports.size() - 1); |
| |
| int startPos= first.getStartPosition(); // no extended range for first: bug 121428 |
| int endPos= root.getExtendedStartPosition(last) + root.getExtendedLength(last); |
| int endLine= root.getLineNumber(endPos); |
| if (endLine > 0) { |
| int nextLinePos= root.getPosition(endLine + 1, 0); |
| if (nextLinePos >= 0) { |
| int firstTypePos= getFirstTypeBeginPos(root); |
| if (firstTypePos != -1 && firstTypePos < nextLinePos) { |
| endPos= firstTypePos; |
| } else { |
| endPos= nextLinePos; |
| } |
| } |
| } |
| return new Region(startPos, endPos - startPos); |
| } else { |
| int start= getPackageStatementEndPos(root); |
| return new Region(start, 0); |
| } |
| } |
| |
| public MultiTextEdit getResultingEdits(IProgressMonitor monitor) throws JavaModelException { |
| if (monitor == null) { |
| monitor= new NullProgressMonitor(); |
| } |
| try { |
| int importsStart= this.replaceRange.getOffset(); |
| int importsLen= this.replaceRange.getLength(); |
| |
| String lineDelim= this.compilationUnit.findRecommendedLineSeparator(); |
| IBuffer buffer= this.compilationUnit.getBuffer(); |
| |
| int currPos= importsStart; |
| MultiTextEdit resEdit= new MultiTextEdit(); |
| |
| if ((this.flags & F_NEEDS_LEADING_DELIM) != 0) { |
| // new import container |
| resEdit.addChild(new InsertEdit(currPos, lineDelim)); |
| } |
| |
| PackageEntry lastPackage= null; |
| |
| Set onDemandConflicts= null; |
| if (this.findAmbiguousImports) { |
| onDemandConflicts= evaluateStarImportConflicts(monitor); |
| } |
| |
| int spacesBetweenGroups= getSpacesBetweenImportGroups(); |
| |
| ArrayList stringsToInsert= new ArrayList(); |
| |
| int nPackageEntries= this.packageEntries.size(); |
| for (int i= 0; i < nPackageEntries; i++) { |
| PackageEntry pack= (PackageEntry) this.packageEntries.get(i); |
| int nImports= pack.getNumberOfImports(); |
| |
| if (this.filterImplicitImports && !pack.isStatic() && isImplicitImport(pack.getName(), this.compilationUnit)) { |
| pack.removeAllNew(onDemandConflicts); |
| nImports= pack.getNumberOfImports(); |
| } |
| if (nImports == 0) { |
| continue; |
| } |
| |
| |
| if (spacesBetweenGroups > 0) { |
| // add a space between two different groups by looking at the two adjacent imports |
| if (lastPackage != null && !pack.isComment() && !pack.isSameGroup(lastPackage)) { |
| ImportDeclEntry last= lastPackage.getImportAt(lastPackage.getNumberOfImports() - 1); |
| ImportDeclEntry first= pack.getImportAt(0); |
| if (!lastPackage.isComment() && (last.isNew() || first.isNew())) { |
| for (int k= spacesBetweenGroups; k > 0; k--) { |
| stringsToInsert.add(lineDelim); |
| } |
| } |
| } |
| } |
| lastPackage= pack; |
| |
| boolean isStatic= pack.isStatic(); |
| int threshold= isStatic ? this.staticImportOnDemandThreshold : this.importOnDemandThreshold; |
| |
| boolean doStarImport= pack.hasStarImport(threshold, onDemandConflicts); |
| if (doStarImport && (pack.find("*") == null)) { //$NON-NLS-1$ |
| String starImportString= pack.getName() + ".*"; //$NON-NLS-1$ |
| String str= getNewImportString(starImportString, isStatic, lineDelim); |
| stringsToInsert.add(str); |
| } |
| |
| for (int k= 0; k < nImports; k++) { |
| ImportDeclEntry currDecl= pack.getImportAt(k); |
| IRegion region= currDecl.getSourceRange(); |
| |
| if (region == null) { // new entry |
| if (!doStarImport || currDecl.isOnDemand() || (onDemandConflicts != null && onDemandConflicts.contains(currDecl.getSimpleName()))) { |
| String str= getNewImportString(currDecl.getElementName(), isStatic, lineDelim); |
| stringsToInsert.add(str); |
| } |
| } else { |
| if (!doStarImport || currDecl.isOnDemand() || onDemandConflicts == null || onDemandConflicts.contains(currDecl.getSimpleName())) { |
| int offset= region.getOffset(); |
| removeAndInsertNew(buffer, currPos, offset, stringsToInsert, resEdit); |
| stringsToInsert.clear(); |
| currPos= offset + region.getLength(); |
| } |
| } |
| } |
| } |
| |
| int end= importsStart + importsLen; |
| removeAndInsertNew(buffer, currPos, end, stringsToInsert, resEdit); |
| |
| if (importsLen == 0) { |
| if (!this.importsCreated.isEmpty() || !this.staticImportsCreated.isEmpty()) { // new import container |
| if ((this.flags & F_NEEDS_TRAILING_DELIM) != 0) { |
| resEdit.addChild(new InsertEdit(currPos, lineDelim)); |
| } |
| } else { |
| return new MultiTextEdit(); // no changes |
| } |
| } |
| return resEdit; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private void removeAndInsertNew(IBuffer buffer, int contentOffset, int contentEnd, ArrayList stringsToInsert, MultiTextEdit resEdit) { |
| int pos= contentOffset; |
| for (int i= 0; i < stringsToInsert.size(); i++) { |
| String curr= (String) stringsToInsert.get(i); |
| int idx= findInBuffer(buffer, curr, pos, contentEnd); |
| if (idx != -1) { |
| if (idx != pos) { |
| resEdit.addChild(new DeleteEdit(pos, idx - pos)); |
| } |
| pos= idx + curr.length(); |
| } else { |
| resEdit.addChild(new InsertEdit(pos, curr)); |
| } |
| } |
| if (pos < contentEnd) { |
| resEdit.addChild(new DeleteEdit(pos, contentEnd - pos)); |
| } |
| } |
| |
| private int findInBuffer(IBuffer buffer, String str, int start, int end) { |
| int pos= start; |
| int len= str.length(); |
| if (pos + len > end || str.length() == 0) { |
| return -1; |
| } |
| char first= str.charAt(0); |
| int step= str.indexOf(first, 1); |
| if (step == -1) { |
| step= len; |
| } |
| while (pos + len <= end) { |
| if (buffer.getChar(pos) == first) { |
| int k= 1; |
| while (k < len && buffer.getChar(pos + k) == str.charAt(k)) { |
| k++; |
| } |
| if (k == len) { |
| return pos; // found |
| } |
| if (k < step) { |
| pos+= k; |
| } else { |
| pos+= step; |
| } |
| } else { |
| pos++; |
| } |
| } |
| return -1; |
| } |
| |
| private Set evaluateStarImportConflicts(IProgressMonitor monitor) throws JavaModelException { |
| //long start= System.currentTimeMillis(); |
| |
| final HashSet/*String*/ onDemandConflicts= new HashSet(); |
| |
| IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { this.compilationUnit.getJavaProject() }); |
| |
| ArrayList/*<char[][]>*/ starImportPackages= new ArrayList(); |
| ArrayList/*<char[][]>*/ simpleTypeNames= new ArrayList(); |
| int nPackageEntries= this.packageEntries.size(); |
| for (int i= 0; i < nPackageEntries; i++) { |
| PackageEntry pack= (PackageEntry) this.packageEntries.get(i); |
| if (!pack.isStatic() && pack.hasStarImport(this.importOnDemandThreshold, null)) { |
| starImportPackages.add(pack.getName().toCharArray()); |
| for (int k= 0; k < pack.getNumberOfImports(); k++) { |
| ImportDeclEntry curr= pack.getImportAt(k); |
| if (!curr.isOnDemand() && !curr.isComment()) { |
| simpleTypeNames.add(curr.getSimpleName().toCharArray()); |
| } |
| } |
| } |
| } |
| if (starImportPackages.isEmpty()) { |
| return null; |
| } |
| |
| starImportPackages.add(this.compilationUnit.getParent().getElementName().toCharArray()); |
| starImportPackages.add(JAVA_LANG.toCharArray()); |
| |
| char[][] allPackages= (char[][]) starImportPackages.toArray(new char[starImportPackages.size()][]); |
| char[][] allTypes= (char[][]) simpleTypeNames.toArray(new char[simpleTypeNames.size()][]); |
| |
| TypeNameRequestor requestor= new TypeNameRequestor() { |
| HashMap foundTypes= new HashMap(); |
| |
| private String getTypeContainerName(char[] packageName, char[][] enclosingTypeNames) { |
| StringBuffer buf= new StringBuffer(); |
| buf.append(packageName); |
| for (int i= 0; i < enclosingTypeNames.length; i++) { |
| if (buf.length() > 0) |
| buf.append('.'); |
| buf.append(enclosingTypeNames[i]); |
| } |
| return buf.toString(); |
| } |
| |
| public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) { |
| String name= new String(simpleTypeName); |
| String containerName= getTypeContainerName(packageName, enclosingTypeNames); |
| |
| String oldContainer= (String) this.foundTypes.put(name, containerName); |
| if (oldContainer != null && !oldContainer.equals(containerName)) { |
| onDemandConflicts.add(name); |
| } |
| } |
| }; |
| new SearchEngine().searchAllTypeNames(allPackages, allTypes, scope, requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor); |
| return onDemandConflicts; |
| } |
| |
| private String getNewImportString(String importName, boolean isStatic, String lineDelim) { |
| StringBuffer buf= new StringBuffer(); |
| buf.append("import "); //$NON-NLS-1$ |
| if (isStatic) { |
| buf.append("static "); //$NON-NLS-1$ |
| } |
| buf.append(importName); |
| if (insertSpaceBeforeSemicolon()) buf.append(' '); |
| buf.append(';'); |
| buf.append(lineDelim); |
| |
| if (isStatic) { |
| this.staticImportsCreated.add(importName); |
| } else { |
| this.importsCreated.add(importName); |
| } |
| return buf.toString(); |
| } |
| |
| private static int getFirstTypeBeginPos(CompilationUnit root) { |
| List types= root.types(); |
| if (!types.isEmpty()) { |
| return root.getExtendedStartPosition(((ASTNode) types.get(0))); |
| } |
| return -1; |
| } |
| |
| private int getPackageStatementEndPos(CompilationUnit root) { |
| PackageDeclaration packDecl= root.getPackage(); |
| if (packDecl != null) { |
| int afterPackageStatementPos= -1; |
| int lineNumber= root.getLineNumber(packDecl.getStartPosition() + packDecl.getLength()); |
| if (lineNumber >= 0) { |
| int lineAfterPackage= lineNumber + 1; |
| afterPackageStatementPos= root.getPosition(lineAfterPackage, 0); |
| } |
| if (afterPackageStatementPos < 0) { |
| this.flags|= F_NEEDS_LEADING_DELIM; |
| return packDecl.getStartPosition() + packDecl.getLength(); |
| } |
| int firstTypePos= getFirstTypeBeginPos(root); |
| if (firstTypePos != -1 && firstTypePos <= afterPackageStatementPos) { |
| if (firstTypePos <= afterPackageStatementPos) { |
| this.flags|= F_NEEDS_TRAILING_DELIM; |
| if (firstTypePos == afterPackageStatementPos) { |
| this.flags|= F_NEEDS_LEADING_DELIM; |
| } |
| return firstTypePos; |
| } |
| } |
| this.flags|= F_NEEDS_LEADING_DELIM; |
| return afterPackageStatementPos; // insert a line after after package statement |
| } |
| this.flags |= F_NEEDS_TRAILING_DELIM; |
| return 0; |
| } |
| |
| public String toString() { |
| int nPackages= this.packageEntries.size(); |
| StringBuffer buf= new StringBuffer("\n-----------------------\n"); //$NON-NLS-1$ |
| for (int i= 0; i < nPackages; i++) { |
| PackageEntry entry= (PackageEntry) this.packageEntries.get(i); |
| if (entry.isStatic()) { |
| buf.append("static "); //$NON-NLS-1$ |
| } |
| buf.append(entry.toString()); |
| } |
| return buf.toString(); |
| } |
| |
| private static final class ImportDeclEntry { |
| |
| private String elementName; |
| private IRegion sourceRange; |
| private final boolean isStatic; |
| |
| public ImportDeclEntry(String elementName, boolean isStatic, IRegion sourceRange) { |
| this.elementName= elementName; |
| this.sourceRange= sourceRange; |
| this.isStatic= isStatic; |
| } |
| |
| public String getElementName() { |
| return this.elementName; |
| } |
| |
| public int compareTo(String fullName, boolean isStaticImport) { |
| int cmp= this.elementName.compareTo(fullName); |
| if (cmp == 0) { |
| if (this.isStatic == isStaticImport) { |
| return 0; |
| } |
| return this.isStatic ? -1 : 1; |
| } |
| return cmp; |
| } |
| |
| public String getSimpleName() { |
| return Signature.getSimpleName(this.elementName); |
| } |
| |
| public boolean isOnDemand() { |
| return this.elementName != null && this.elementName.endsWith(".*"); //$NON-NLS-1$ |
| } |
| |
| public boolean isStatic() { |
| return this.isStatic; |
| } |
| |
| public boolean isNew() { |
| return this.sourceRange == null; |
| } |
| |
| public boolean isComment() { |
| return this.elementName == null; |
| } |
| |
| public IRegion getSourceRange() { |
| return this.sourceRange; |
| } |
| |
| } |
| |
| /* |
| * Internal element for the import structure: A container for imports |
| * of all types from the same package |
| */ |
| private final static class PackageEntry { |
| private String name; |
| private ArrayList importEntries; |
| private String group; |
| private boolean isStatic; |
| |
| /** |
| * Comment package entry |
| */ |
| public PackageEntry() { |
| this("!", null, false); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @param name Name of the package entry. e.g. org.eclipse.jdt.ui, containing imports like |
| * org.eclipse.jdt.ui.JavaUI. |
| * @param group The index of the preference order entry assigned |
| * different group id's will result in spacers between the entries |
| */ |
| public PackageEntry(String name, String group, boolean isStatic) { |
| this.name= name; |
| this.importEntries= new ArrayList(5); |
| this.group= group; |
| this.isStatic= isStatic; |
| } |
| |
| public boolean isStatic() { |
| return this.isStatic; |
| } |
| |
| public int compareTo(String otherName, boolean isOtherStatic) { |
| int cmp= this.name.compareTo(otherName); |
| if (cmp == 0) { |
| if (this.isStatic == isOtherStatic) { |
| return 0; |
| } |
| return this.isStatic ? -1 : 1; |
| } |
| return cmp; |
| } |
| |
| public void sortIn(ImportDeclEntry imp) { |
| String fullImportName= imp.getElementName(); |
| int insertPosition= -1; |
| int nInports= this.importEntries.size(); |
| for (int i= 0; i < nInports; i++) { |
| ImportDeclEntry curr= getImportAt(i); |
| if (!curr.isComment()) { |
| int cmp= curr.compareTo(fullImportName, imp.isStatic()); |
| if (cmp == 0) { |
| return; // exists already |
| } else if (cmp > 0 && insertPosition == -1) { |
| insertPosition= i; |
| } |
| } |
| } |
| if (insertPosition == -1) { |
| this.importEntries.add(imp); |
| } else { |
| this.importEntries.add(insertPosition, imp); |
| } |
| } |
| |
| |
| public void add(ImportDeclEntry imp) { |
| this.importEntries.add(imp); |
| } |
| |
| public ImportDeclEntry find(String simpleName) { |
| int nInports= this.importEntries.size(); |
| for (int i= 0; i < nInports; i++) { |
| ImportDeclEntry curr= getImportAt(i); |
| if (!curr.isComment()) { |
| String currName= curr.getElementName(); |
| if (currName.endsWith(simpleName)) { |
| int dotPos= currName.length() - simpleName.length() - 1; |
| if ((dotPos == -1) || (dotPos > 0 && currName.charAt(dotPos) == '.')) { |
| return curr; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public boolean remove(String fullName, boolean isStaticImport) { |
| int nInports= this.importEntries.size(); |
| for (int i= 0; i < nInports; i++) { |
| ImportDeclEntry curr= getImportAt(i); |
| if (!curr.isComment() && curr.compareTo(fullName, isStaticImport) == 0) { |
| this.importEntries.remove(i); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void removeAllNew(Set onDemandConflicts) { |
| int nInports= this.importEntries.size(); |
| for (int i= nInports - 1; i >= 0; i--) { |
| ImportDeclEntry curr= getImportAt(i); |
| if (curr.isNew() /*&& (onDemandConflicts == null || onDemandConflicts.contains(curr.getSimpleName()))*/) { |
| this.importEntries.remove(i); |
| } |
| } |
| } |
| |
| public ImportDeclEntry getImportAt(int index) { |
| return (ImportDeclEntry) this.importEntries.get(index); |
| } |
| |
| public boolean hasStarImport(int threshold, Set explicitImports) { |
| if (isComment() || isDefaultPackage()) { // can not star import default package |
| return false; |
| } |
| int nImports= getNumberOfImports(); |
| int count= 0; |
| boolean containsNew= false; |
| for (int i= 0; i < nImports; i++) { |
| ImportDeclEntry curr= getImportAt(i); |
| if (curr.isOnDemand()) { |
| return true; |
| } |
| if (!curr.isComment()) { |
| count++; |
| boolean isExplicit= !curr.isStatic() && (explicitImports != null) && explicitImports.contains(curr.getSimpleName()); |
| containsNew |= curr.isNew() && !isExplicit; |
| } |
| } |
| return (count >= threshold) && containsNew; |
| } |
| |
| public int getNumberOfImports() { |
| return this.importEntries.size(); |
| } |
| |
| public String getName() { |
| return this.name; |
| } |
| |
| public String getGroupID() { |
| return this.group; |
| } |
| |
| public void setGroupID(String groupID) { |
| this.group= groupID; |
| } |
| |
| public boolean isSameGroup(PackageEntry other) { |
| if (this.group == null) { |
| return other.getGroupID() == null; |
| } else { |
| return this.group.equals(other.getGroupID()) && (this.isStatic == other.isStatic()); |
| } |
| } |
| |
| public boolean isComment() { |
| return "!".equals(this.name); //$NON-NLS-1$ |
| } |
| |
| public boolean isDefaultPackage() { |
| return this.name.length() == 0; |
| } |
| |
| public String toString() { |
| StringBuffer buf= new StringBuffer(); |
| if (isComment()) { |
| buf.append("comment\n"); //$NON-NLS-1$ |
| } else { |
| buf.append(this.name); buf.append(", groupId: "); buf.append(this.group); buf.append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| int nImports= getNumberOfImports(); |
| for (int i= 0; i < nImports; i++) { |
| ImportDeclEntry curr= getImportAt(i); |
| buf.append(" "); //$NON-NLS-1$ |
| if (curr.isStatic()) { |
| buf.append("static "); //$NON-NLS-1$ |
| } |
| buf.append(curr.getSimpleName()); |
| if (curr.isNew()) { |
| buf.append(" (new)"); //$NON-NLS-1$ |
| } |
| buf.append("\n"); //$NON-NLS-1$ |
| } |
| } |
| return buf.toString(); |
| } |
| } |
| |
| public String[] getCreatedImports() { |
| return (String[]) this.importsCreated.toArray(new String[this.importsCreated.size()]); |
| } |
| |
| public String[] getCreatedStaticImports() { |
| return (String[]) this.staticImportsCreated.toArray(new String[this.staticImportsCreated.size()]); |
| } |
| |
| } |