/** * 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. */ package org.python.pydev.editor.refactoring; import java.io.File; import java.util.Stack; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.python.pydev.core.IModule; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.MisconfigurationException; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.structure.DecoratableObject; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule; import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.plugin.nature.PythonNature; import org.python.pydev.plugin.nature.SystemPythonNature; import com.aptana.shared_core.structure.Tuple; /** * This class encapsulates all the info needed in order to do a refactoring * As we'd have a getter/setter without any side-effects, let's leave them all public... * * It is a Decoratable Object, so that clients can add additional information to this * object at runtime. */ public class RefactoringRequest extends DecoratableObject { /** * The file associated with the editor where the refactoring is being requested */ public final File file; /** * The current selection when the refactoring was requested */ public PySelection ps; /** * The progress monitor to give feedback to the user (may be checked in another thread) * May be null * * Note that this is the monitor for the initial request, but, clients may use it in othe */ private final Stack<IProgressMonitor> monitors = new Stack<IProgressMonitor>(); /** * The nature used */ public IPythonNature nature; /** * The python editor. May be null (especially on tests) */ public final PyEdit pyEdit; /** * The module for the passed document. Has a getter that caches the result here. */ private IModule module; /** * The module name (may be null) */ public String moduleName; /** * The new name in a refactoring (may be null if not applicable) */ public String inputName; /** * The initial representation of the selected name */ public String initialName; /** * If the file is passed, we also set the document automatically * @param file the file correspondent to this request */ public RefactoringRequest(File file, PySelection selection, PythonNature nature) { this(file, selection, null, nature, null); } /** * If the file is passed, we also set the document automatically * @param file the file correspondent to this request * @throws MisconfigurationException */ public RefactoringRequest(PyEdit pyEdit, PySelection ps) throws MisconfigurationException { this(pyEdit.getEditorFile(), ps, null, pyEdit.getPythonNature(), pyEdit); } /** * Assigns parameters to attributes (tries to resolve the module name and create a SystemPythonNature if the * nature is not specified) */ public RefactoringRequest(File file, PySelection ps, IProgressMonitor monitor, IPythonNature nature, PyEdit pyEdit) { this.file = file; this.ps = ps; this.pushMonitor(monitor); if (nature == null) { Tuple<IPythonNature, String> infoForFile = PydevPlugin.getInfoForFile(file); if (infoForFile != null) { this.nature = infoForFile.o1; this.moduleName = infoForFile.o2; } else { this.nature = null; } } else { this.nature = nature; if (file != null) { this.moduleName = resolveModule(); } } this.pyEdit = pyEdit; } /** * Used to make the work communication (also checks to see if it has been cancelled) * @param desc Some string to be shown in the progress */ public synchronized void communicateWork(String desc) throws OperationCanceledException { IProgressMonitor monitor = getMonitor(); if (monitor != null) { monitor.setTaskName(desc); monitor.worked(1); checkCancelled(); } } /** * Checks if the process was cancelled (throws CancelledException in this case) */ public void checkCancelled() throws OperationCanceledException { if (getMonitor().isCanceled()) { throw new OperationCanceledException(); } } /** * @return the module name or null if it is not possible to determine the module name */ public String resolveModule() { if (moduleName == null) { if (file != null && nature != null) { try { moduleName = nature.resolveModule(file); } catch (MisconfigurationException e) { throw new RuntimeException(e); } } } return moduleName; } // Some shortcuts to the PySelection /** * @return the initial column selected (starting at 0) */ public int getBeginCol() { return ps.getAbsoluteCursorOffset() - ps.getStartLine().getOffset(); } /** * @return the final column selected (starting at 0) */ public int getEndCol() { return ps.getAbsoluteCursorOffset() + ps.getSelLength() - ps.getEndLine().getOffset(); } /** * @return the last line selected (starting at 1) */ public int getEndLine() { return ps.getEndLineIndex() + 1; } /** * @return the initial line selected (starting at 1) */ public int getBeginLine() { return ps.getStartLineIndex() + 1; } /** * @return the module for the document (may return the ast from the pyedit if it is available). */ public IModule getModule() { if (module == null) { if (pyEdit != null) { SimpleNode ast = pyEdit.getAST(); if (ast != null) { IDocument doc = ps.getDoc(); long astModificationTimeStamp = pyEdit.getAstModificationTimeStamp(); if (astModificationTimeStamp != -1 && astModificationTimeStamp == (((IDocumentExtension4) doc).getModificationStamp())) { //Matched time stamp -- so, we can use the ast without fear of being unsynched. module = AbstractModule.createModule(ast, file, resolveModule()); } else { //Did not match time stamp!! We'll reparse the document later on to get a synched version. } } } if (module == null) { try { module = AbstractModule.createModuleFromDoc(resolveModule(), file, ps.getDoc(), nature, false); } catch (MisconfigurationException e) { throw new RuntimeException(e); } } } return module; } /** * @return the ast for the current module */ public SimpleNode getAST() { IModule mod = getModule(); if (mod instanceof SourceModule) { return ((SourceModule) mod).getAst(); } return null; } /** * Fills the initial name and initial offset from the PySelection */ public void fillInitialNameAndOffset() { try { Tuple<String, Integer> currToken = ps.getCurrToken(); initialName = currToken.o1; } catch (Exception e) { throw new RuntimeException(e); } } /** * @return the current document where this refactoring request was asked */ public IDocument getDoc() { return ps.getDoc(); } /** * Sets the monitor to be used (because it may change depending on the current step we're in) * @param monitor the monitor to be used */ public void pushMonitor(IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); } this.monitors.push(monitor); } /** * Removes and returns the current top-most progress monitor */ public IProgressMonitor popMonitor() { return this.monitors.pop(); } /** * Clients using the RefactoringRequest are expected to receive it as a parameter, then: * * getMonitor().beginTask("my task", total) * * * try{ * //Calling another function * req.pushMonitor(new SubProgressMonitor(monitor, 10)); * callIt(req); * finally{ * req.popMonitor().done(); * } * * try{ * //Calling another function * req.pushMonitor(new SubProgressMonitor(monitor, 90)); * callIt(req); * finally{ * req.popMonitor().done(); * } * * * getMonitor().done(); * * * * * * @return the current progress monitor */ public IProgressMonitor getMonitor() { return this.monitors.peek(); } public IFile getIFile() { if (this.pyEdit == null) { return null; } return this.pyEdit.getIFile(); } }