ImportRewriteAnalyzer.java :  » IDE » IntelliJ » org » eclipse » jdt » internal » core » dom » rewrite » Java Open Source

Java Open Source » IDE » IntelliJ 
IntelliJ » org » eclipse » jdt » internal » core » dom » rewrite » ImportRewriteAnalyzer.java
/*******************************************************************************
 * Copyright (c) 2000, 2008 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 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 ? staticImportOnDemandThreshold : 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(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);
    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 {
    
    public static PackageEntry createOnPlaceholderEntry(String preferenceOrder) {
      if (preferenceOrder.length() > 0 && preferenceOrder.charAt(0) == '#') {
        String curr= preferenceOrder.substring(1);
        return new PackageEntry(curr, curr, true);
      }
      return new PackageEntry(preferenceOrder, preferenceOrder, false);
    }
    
    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 ImportDeclEntry getLast() {
      int nImports= getNumberOfImports();
      if (nImports > 0) {
        return getImportAt(nImports - 1);
      }
      return null;
    }
    
    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()]);
  }
  
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.