/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Dec 10, 2006 * @author Fabio */ package com.python.pydev.refactoring.wizards.rename; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.python.pydev.core.IModule; import org.python.pydev.core.IProjectModulesManager; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.ModulesKey; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.editor.codecompletion.revisited.CompletionCache; import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule; import org.python.pydev.editor.codecompletion.revisited.visitors.Definition; import org.python.pydev.editor.refactoring.RefactoringRequest; import org.python.pydev.parser.visitors.scope.ASTEntry; import org.python.pydev.plugin.nature.PythonNature; import com.aptana.shared_core.structure.Tuple; import com.python.pydev.refactoring.actions.PyFindAllOccurrences; import com.python.pydev.refactoring.refactorer.AstEntryRefactorerRequestConstants; /** * This class provides helper methods for finding things in the workspace. * * The user is only required to implement {@link #getEntryOccurrences(String, SourceModule)} to * return the available references in the given module. * * @author Fabio */ public abstract class AbstractRenameWorkspaceRefactorProcess extends AbstractRenameRefactorProcess { public static final boolean DEBUG_FILTERED_MODULES = false || PyFindAllOccurrences.DEBUG_FIND_REFERENCES; /** * May be used by subclasses */ protected AbstractRenameWorkspaceRefactorProcess() { } public AbstractRenameWorkspaceRefactorProcess(Definition definition) { super(definition); } /** * Gets and returns only the occurrences that point to what we're looking for, meaning that * we have to filter out references that may be pointing to some other definition, * and not the one we're actually refering to. * * @param initialName the name we're looking for * @param module the module we're analyzing right now * @return a list with the references that point to the definition we're renaming. */ protected List<ASTEntry> getOccurrencesInOtherModule(RefactoringStatus status, String initialName, SourceModule module, PythonNature nature) { CompletionCache completionCache = new CompletionCache(); List<ASTEntry> entryOccurrences = findReferencesOnOtherModule(status, initialName, module); //Removed this check: it made subclasses work badly, also, in Python because of duck-typing, many of those //matches are actually wanted. // // if(getRecheckWhereDefinitionWasFound()){ // for (Iterator<ASTEntry> iter = entryOccurrences.iterator(); iter.hasNext();) { // ASTEntry entry = iter.next(); // int line = entry.node.beginLine; // int col = entry.node.beginColumn; // try { // ArrayList<IDefinition> definitions = new ArrayList<IDefinition>(); // PyRefactoringFindDefinition.findActualDefinition(request, module, initialName, definitions, line, col, nature, completionCache); // //Definition[] definitions = module.findDefinition(new CompletionState(line-1, col-1, initialName, nature, ""), line, col, nature, null); // for (IDefinition def : definitions) { // if(def instanceof Definition){ // Definition localDefinition = (Definition) def; // //if within one module any of the definitions pointed to some class in some other module, // //that means that the tokens in this module actually point to some other class // //(with the same name), and we can't actually rename them. // String foundModName = localDefinition.module.getName(); // if(foundModName != null && !foundModName.equals(this.definition.module.getName())){ // if(DEBUG_FILTERED_MODULES){ // System.out.println("The entries found on module:"+module.getName()+" had the definition found on module:"+ // foundModName+" and were removed from the elements to be renamed."); // // } // return new ArrayList<ASTEntry>(); // } // } // } // } catch (Exception e) { // throw new RuntimeException(e); // } // // } // } return entryOccurrences; } /** * @return true if the definitions found should be re-checked for the module where it was defined * and false otherwise (the visitor already got correct matches) */ protected abstract boolean getRecheckWhereDefinitionWasFound(); /** * Default implementation for checking the tokens in the workspace. */ @Override protected void findReferencesToRenameOnWorkspace(RefactoringRequest request, RefactoringStatus status) { request.getMonitor().beginTask("Find references on workspace", 100); try { try { request.pushMonitor(new SubProgressMonitor(request.getMonitor(), 20)); findReferencesToRenameOnLocalScope(request, status); } finally { request.popMonitor().done(); } //if the user has set that we should only find references in the local scope in the checkInitialOnLocalScope //we should not try to find other references in the workspace. boolean onlyInLocalScope = (Boolean) request.getAdditionalInfo( AstEntryRefactorerRequestConstants.FIND_REFERENCES_ONLY_IN_LOCAL_SCOPE, false); if (!onlyInLocalScope && !status.hasFatalError()) { request.pushMonitor(new SubProgressMonitor(request.getMonitor(), 80)); try { doCheckInitialOnWorkspace(status, request); } finally { request.popMonitor().done(); } } } finally { request.getMonitor().done(); } } /** * This method is made to be used in the checkInitialOnWorkspace implementation. * * It will find files with possible references in the workspace (from the token * name we're searching) and for each file that maps to a module it will * call getOccurrencesInOtherModule, and will add those occurrences to * the map with the file pointing to the entries. * * @param status used to add some error status to the refactoring * @param request the request used for the refactoring */ protected void doCheckInitialOnWorkspace(RefactoringStatus status, RefactoringRequest request) { try { request.getMonitor().beginTask("Check references on workspace", 100); ArrayList<Tuple<List<ModulesKey>, IPythonNature>> references; try { request.pushMonitor(new SubProgressMonitor(request.getMonitor(), 90)); references = findFilesWithPossibleReferences(request); } finally { request.popMonitor().done(); } int total = references.size(); try { request.pushMonitor(new SubProgressMonitor(request.getMonitor(), 10)); request.getMonitor().beginTask("Analyzing references found", total); int i = 0; for (Tuple<List<ModulesKey>, IPythonNature> file : references) { i++; request.communicateWork(com.aptana.shared_core.string.StringUtils.format("Analyzing %s (%s of %s)", file.o2.getProject(), i, total)); PythonNature nature = (PythonNature) file.o2; if (nature != null) { if (!nature.startRequests()) { continue; } try { for (ModulesKey key : file.o1) { IProjectModulesManager modulesManager = (IProjectModulesManager) nature.getAstManager() .getModulesManager(); request.checkCancelled(); String modName = key.name; if (modName != null) { if (!request.moduleName.equals(modName)) { //we've already checked the module from the request... request.checkCancelled(); IModule module = modulesManager .getModuleInDirectManager(modName, nature, false); if (module instanceof SourceModule) { request.checkCancelled(); List<ASTEntry> entryOccurrences = getOccurrencesInOtherModule(status, request.initialName, (SourceModule) module, nature); if (entryOccurrences.size() > 0) { addOccurrences(entryOccurrences, key.file, modName); } } } } } } finally { nature.endRequests(); } } } } finally { request.popMonitor().done(); } } catch (OperationCanceledException e) { //that's ok } catch (Exception e) { throw new RuntimeException(e); } finally { request.getMonitor().done(); } } /** * This method is called for each module that may have some reference to the definition * we're looking for. * * It will be called for all the modules but the one in the request (for that one * the findReferencesToRenameOnLocalScope is called). * * @param initialName this is the name of the token we're looking for * @param module this is the module that may contain references to that module * @return a list of entries that are references to the given module. */ protected abstract List<ASTEntry> findReferencesOnOtherModule(RefactoringStatus status, String initialName, SourceModule module); }