blob: c6394db38154c9d0e2d062f3f862acf1e46ba720 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.reorg;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.ISearchPattern;
import org.eclipse.jdt.internal.corext.Assert;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.ImportEdit;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
import org.eclipse.jdt.internal.corext.refactoring.SearchResult;
import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
import org.eclipse.jdt.internal.corext.refactoring.rename.RefactoringScopeFactory;
import org.eclipse.jdt.internal.corext.refactoring.structure.ReferenceFinderUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
import org.eclipse.jdt.internal.corext.textmanipulation.SimpleTextEdit;
import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer;
import org.eclipse.jdt.internal.corext.textmanipulation.TextEdit;
import org.eclipse.jdt.internal.corext.textmanipulation.TextEditCopier;
import org.eclipse.jdt.internal.corext.textmanipulation.TextRange;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
class MoveCuUpdateCreator {
private ICompilationUnit[] fCus;
private IPackageFragment fDestination;
private CodeGenerationSettings fSettings;
private Map fImportEdits; //ICompilationUnit -> ImportEdit
public MoveCuUpdateCreator(ICompilationUnit cu, IPackageFragment pack, CodeGenerationSettings settings){
this(new ICompilationUnit[]{cu}, pack, settings);
}
public MoveCuUpdateCreator(ICompilationUnit[] cus, IPackageFragment pack, CodeGenerationSettings settings){
Assert.isNotNull(cus);
Assert.isNotNull(pack);
Assert.isNotNull(settings);
fCus= convertToOriginals(cus);
fDestination= pack;
fSettings= settings;
fImportEdits= new HashMap();
}
private static ICompilationUnit[] convertToOriginals(ICompilationUnit[] cus){
ICompilationUnit[] result= new ICompilationUnit[cus.length];
for (int i= 0; i < cus.length; i++) {
result[i]= convertToOriginal(cus[i]);
}
return result;
}
private static ICompilationUnit convertToOriginal(ICompilationUnit cu){
if (! cu.isWorkingCopy())
return cu;
return (ICompilationUnit)cu.getOriginalElement();
}
public TextChangeManager createChangeManager(IProgressMonitor pm) throws JavaModelException{
pm.beginTask("", 2); //$NON-NLS-1$
try{
TextChangeManager changeManager= new TextChangeManager();
addUpdates(changeManager, new SubProgressMonitor(pm, 1));
addImportsToDestinationPackage(new SubProgressMonitor(pm, 1));
for (Iterator iter= fImportEdits.keySet().iterator(); iter.hasNext();) {
ICompilationUnit cu= (ICompilationUnit)iter.next();
ImportEdit importEdit= (ImportEdit)fImportEdits.get(cu);
if (importEdit != null && ! importEdit.isEmpty())
changeManager.get(cu).addTextEdit(RefactoringCoreMessages.getString("MoveCuUpdateCreator.update_imports"), importEdit); //$NON-NLS-1$
}
return changeManager;
} catch (JavaModelException e){
throw e;
} catch (CoreException e){
throw new JavaModelException(e);
} finally{
pm.done();
}
}
private void addUpdates(TextChangeManager changeManager, IProgressMonitor pm) throws CoreException {
pm.beginTask("", fCus.length); //$NON-NLS-1$
for (int i= 0; i < fCus.length; i++){
if (pm.isCanceled())
throw new OperationCanceledException();
addUpdates(changeManager, fCus[i], new SubProgressMonitor(pm, 1));
}
}
private void addUpdates(TextChangeManager changeManager, ICompilationUnit movedUnit, IProgressMonitor pm) throws CoreException{
try{
pm.beginTask("", 3); //$NON-NLS-1$
pm.subTask(RefactoringCoreMessages.getFormattedString("MoveCuUpdateCreator.searching", movedUnit.getElementName())); //$NON-NLS-1$
if (isDestinationAnotherFragmentOfSamePackage(movedUnit)){
pm.worked(3);
return;
}
addImportToSourcePackageTypes(movedUnit, new SubProgressMonitor(pm, 1));
removeImportsToDestinationPackageTypes(movedUnit);
addReferenceUpdates(changeManager, movedUnit, new SubProgressMonitor(pm, 1));
} finally{
pm.done();
}
}
private boolean isDestinationAnotherFragmentOfSamePackage(ICompilationUnit movedUnit) {
if (! (movedUnit.getParent() instanceof IPackageFragment))
return false;
IPackageFragment sourcePackage= (IPackageFragment)movedUnit.getParent();
return ! fDestination.equals(sourcePackage) &&
fDestination.getJavaProject().equals(sourcePackage.getJavaProject()) &&
fDestination.getElementName().equals(sourcePackage.getElementName());
}
private void addReferenceUpdates(TextChangeManager changeManager, ICompilationUnit movedUnit, IProgressMonitor pm) throws JavaModelException, CoreException {
SearchResultGroup[] references = getReferences(movedUnit, pm);
for (int i= 0; i < references.length; i++){
ICompilationUnit referencingCu= references[i].getCompilationUnit();
if (referencingCu == null)
continue;
SearchResult[] results= references[i].getSearchResults();
for (int j= 0; j < results.length; j++){
if (isQualifiedReference(results[j])){
TextEdit edit= new UpdateTypeReferenceEdit(results[j], getPackage(movedUnit), fDestination);
changeManager.get(referencingCu).addTextEdit(RefactoringCoreMessages.getString("MoveCuUpdateCreator.update_references"), edit); //$NON-NLS-1$
}
if (results[j].getEnclosingElement().getElementType() == IJavaElement.IMPORT_DECLARATION){
ImportEdit importEdit= getImportEdit(referencingCu);
IImportDeclaration importDecl= (IImportDeclaration)results[j].getEnclosingElement();
importEdit.removeImport(importDecl.getElementName());
importEdit.addImport(createStringForNewImport(movedUnit, importDecl));
}
}
}
}
private String createStringForNewImport(ICompilationUnit movedUnit, IImportDeclaration importDecl) {
return new StringBuffer(importDecl.getElementName()).replace(0, movedUnit.getParent().getElementName().length(), fDestination.getElementName()).toString();
}
private void removeImportsToDestinationPackageTypes(ICompilationUnit movedUnit) throws JavaModelException{
ImportEdit importEdit= getImportEdit(movedUnit);
IType[] destinationTypes= getDestinationPackageTypes();
for (int i= 0; i < destinationTypes.length; i++) {
importEdit.removeImport(JavaModelUtil.getFullyQualifiedName(destinationTypes[i]));
}
}
private IType[] getDestinationPackageTypes() throws JavaModelException{
List types= new ArrayList();
ICompilationUnit[] cus= fDestination.getCompilationUnits();
for (int i= 0; i < cus.length; i++) {
types.addAll(Arrays.asList(cus[i].getAllTypes()));
}
return (IType[]) types.toArray(new IType[types.size()]);
}
private void addImportToSourcePackageTypes(ICompilationUnit movedUnit, IProgressMonitor pm) throws JavaModelException{
List cuList= Arrays.asList(fCus);
IType[] allCuTypes= movedUnit.getAllTypes();
IType[] referencedTypes= ReferenceFinderUtil.getTypesReferencedIn(allCuTypes, pm);
ImportEdit importEdit= getImportEdit(movedUnit);
importEdit.setFilterImplicitImports(false);
IPackageFragment srcPack= (IPackageFragment)movedUnit.getParent();
for (int i= 0; i < referencedTypes.length; i++) {
IType iType= referencedTypes[i];
if (! iType.exists())
continue;
if (! iType.getPackageFragment().equals(srcPack))
continue;
if (cuList.contains(iType.getCompilationUnit()))
continue;
importEdit.addImport(JavaModelUtil.getFullyQualifiedName(iType));
}
}
private void addImportsToDestinationPackage(IProgressMonitor pm) throws CoreException{
pm.beginTask("", fCus.length); //$NON-NLS-1$
ICompilationUnit[] cusThatNeedIt= collectCusThatWillImportDestinationPackage(pm);
for (int i= 0; i < cusThatNeedIt.length; i++) {
if (pm.isCanceled())
throw new OperationCanceledException();
ICompilationUnit iCompilationUnit= cusThatNeedIt[i];
addImport(false, fDestination, iCompilationUnit);
pm.worked(1);
}
}
private boolean addImport(boolean force, IPackageFragment pack, ICompilationUnit cu) throws JavaModelException {
if (pack.isDefaultPackage())
return false;
if (cu.getImport(pack.getElementName() + ".*").exists()) //$NON-NLS-1$
return false;
ImportEdit importEdit= getImportEdit(cu);
importEdit.setFilterImplicitImports(!force);
importEdit.addImport(pack.getElementName() + ".*"); //$NON-NLS-1$
return true;
}
private ImportEdit getImportEdit(ICompilationUnit cu) throws JavaModelException{
if (fImportEdits.containsKey(cu))
return (ImportEdit)fImportEdits.get(cu);
ImportEdit importEdit= new ImportEdit(cu, fSettings);
fImportEdits.put(cu, importEdit);
return importEdit;
}
private ICompilationUnit[] collectCusThatWillImportDestinationPackage(IProgressMonitor pm) throws JavaModelException{
Set collected= new HashSet(); //use set to remove dups
pm.beginTask("", fCus.length); //$NON-NLS-1$
for (int i= 0; i < fCus.length; i++) {
collected.addAll(collectCusThatWillImportDestinationPackage(fCus[i], new SubProgressMonitor(pm, 1)));
}
return (ICompilationUnit[]) collected.toArray(new ICompilationUnit[collected.size()]);
}
private Set collectCusThatWillImportDestinationPackage(ICompilationUnit movedUnit, IProgressMonitor pm) throws JavaModelException{
SearchResultGroup[] references= getReferences(movedUnit, pm);
Set result= new HashSet();
List cuList= Arrays.asList(fCus);
for (int i= 0; i < references.length; i++) {
SearchResultGroup searchResultGroup= references[i];
ICompilationUnit referencingCu= references[i].getCompilationUnit();
if (referencingCu == null)
continue;
if (needsImportToDestinationPackage(movedUnit, cuList, searchResultGroup, referencingCu))
result.add(referencingCu);
}
return result;
}
private boolean needsImportToDestinationPackage(ICompilationUnit movedUnit, List cuList, SearchResultGroup searchResultGroup, ICompilationUnit referencingCu) throws JavaModelException {
if (! hasSimpleReference(searchResultGroup))
return false;
if (referencingCu.equals(movedUnit))
return false;
if (cuList.contains(referencingCu))
return false;
if (isDestinationAnotherFragmentOfSamePackage(movedUnit))
return false;
//heuristic
if (referencingCu.getImport(movedUnit.getParent().getElementName() + ".*").exists()) //$NON-NLS-1$
return true;
if (! referencingCu.getParent().equals(movedUnit.getParent()))
return false;
return true;
}
private static boolean hasSimpleReference(SearchResultGroup searchResultGroup) throws JavaModelException{
SearchResult[] results= searchResultGroup.getSearchResults();
for (int i= 0; i < results.length; i++) {
if (! isQualifiedReference(results[i]))
return true;
}
return false;
}
private static SearchResultGroup[] getReferences(ICompilationUnit unit, IProgressMonitor pm) throws org.eclipse.jdt.core.JavaModelException {
IJavaSearchScope scope= RefactoringScopeFactory.create(unit);
ISearchPattern pattern= createSearchPattern(unit);
return RefactoringSearchEngine.search(new SubProgressMonitor(pm, 1), scope, pattern);
}
private static boolean isQualifiedReference(SearchResult searchResult) throws JavaModelException{
if (searchResult.getEnclosingElement() instanceof IImportDeclaration)
return false;
//XXX needs improvement
ICompilationUnit cu= searchResult.getCompilationUnit();
if (cu == null)
return false;
int offset= searchResult.getStart();
int end= searchResult.getEnd();
String source= cu.getBuffer().getText(offset, end - offset);
if (source.indexOf(".") != -1) //$NON-NLS-1$
return true;
return false;
}
private static IPackageFragment getPackage(ICompilationUnit cu){
return (IPackageFragment)cu.getParent();
}
private static ISearchPattern createSearchPattern(ICompilationUnit cu) throws JavaModelException{
return RefactoringSearchEngine.createSearchPattern(cu.getTypes(), IJavaSearchConstants.REFERENCES);
}
private final static class UpdateTypeReferenceEdit extends SimpleTextEdit {
private SearchResult fSearchResult;
private IPackageFragment fDestination;
private IPackageFragment fSource;
UpdateTypeReferenceEdit(SearchResult searchResult, IPackageFragment source, IPackageFragment destination) {
fSearchResult= searchResult;
fDestination= destination;
fSource= source;
}
protected TextEdit copy0(TextEditCopier copier) {
return new UpdateTypeReferenceEdit(fSearchResult, fSource, fDestination);
}
public void connect(TextBuffer buffer) throws CoreException {
int length= fSearchResult.getEnd() - fSearchResult.getStart();
int offset= fSearchResult.getStart();
String newText= fDestination.getElementName();
String oldText= buffer.getContent(offset, length);
String currectPackageName= fSource.getElementName();
if (fSource.isDefaultPackage()){
length= 0;
} else if (! oldText.startsWith(currectPackageName)){
//no action - simple reference
length= 0;
newText= ""; //$NON-NLS-1$
} else{
length= currectPackageName.length();
}
setText(newText);
setTextRange(new TextRange(offset, length));
super.connect(buffer);
}
}
}