/** * 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.editorinput; import java.io.File; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IPathEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.part.FileEditorInput; import org.python.pydev.core.IPyStackFrame; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.core.log.Log; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.codecompletion.revisited.PythonPathHelper; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.ui.filetypes.FileTypesPreferencesPage; import com.aptana.shared_core.io.FileUtils; import com.aptana.shared_core.structure.Tuple; /** * Refactored from the PydevPlugin: helpers to find some IFile / IEditorInput * from a Path (or java.io.File) * * @author fabioz */ public class PySourceLocatorBase { /** * This method will try to find the most likely file that matches the given path, * considering: * - The workspace files * - The open editors * * and if all fails, it'll still ask the user which path should be used. * * * @param path * @return */ public IEditorInput createEditorInput(IPath path) { return createEditorInput(path, true, null); } /** * @param file the file we want to get in the workspace * @return a workspace file that matches the given file. */ public IFile getWorkspaceFile(File file) { IFile[] files = getWorkspaceFiles(file); return selectWorkspaceFile(files); } /** * @param file the file we want to get in the workspace * @return a workspace file that matches the given file. */ public IFile[] getWorkspaceFiles(File file) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IFile[] files = workspace.getRoot().findFilesForLocationURI(file.toURI()); files = filterNonExistentFiles(files); if (files == null || files.length == 0) { return null; } return files; } public IContainer getWorkspaceContainer(File file) { IContainer[] containers = getWorkspaceContainers(file); if (containers == null || containers.length < 1) { return null; } return containers[0]; } public IContainer[] getWorkspaceContainers(File file) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IContainer[] containers = workspace.getRoot().findContainersForLocationURI(file.toURI()); containers = filterNonExistentContainers(containers); if (containers == null || containers.length == 0) { return null; } return containers; } //---------------------------------- PRIVATE API BELOW -------------------------------------------- /** * Creates the editor input from a given path. * * @param path the path for the editor input we're looking * @param askIfDoesNotExist if true, it'll try to ask the user/check existing editors and look * in the workspace for matches given the name * * @return the editor input found or none if None was available for the given path */ public IEditorInput createEditorInput(IPath path, boolean askIfDoesNotExist, IPyStackFrame pyStackFrame) { int onSourceNotFound = PySourceLocatorPrefs.getOnSourceNotFound(); IEditorInput edInput = null; String pathTranslation = PySourceLocatorPrefs.getPathTranslation(path); if (pathTranslation != null) { if (!pathTranslation.equals(PySourceLocatorPrefs.DONTASK)) { //change it for the registered translation path = Path.fromOSString(pathTranslation); } else { //DONTASK!! askIfDoesNotExist = false; } } IWorkspace w = ResourcesPlugin.getWorkspace(); //let's start with the 'easy' way IFile fileForLocation = w.getRoot().getFileForLocation(path); if (fileForLocation != null && fileForLocation.exists()) { return new FileEditorInput(fileForLocation); } IFile files[] = w.getRoot().findFilesForLocation(path); if (files == null || files.length == 0 || !files[0].exists()) { //it is probably an external file File systemFile = path.toFile(); if (systemFile.exists()) { edInput = createEditorInput(systemFile); } if (edInput == null) { //here we can do one more thing: if the file matches some opened editor, let's use it... //(this is done because when debugging, we don't want to be asked over and over //for the same file) IEditorInput input = getEditorInputFromExistingEditors(systemFile.getName()); if (input != null) { return input; } if (askIfDoesNotExist && (onSourceNotFound == PySourceLocatorPrefs.ASK_FOR_FILE || onSourceNotFound == PySourceLocatorPrefs.ASK_FOR_FILE_GET_FROM_SERVER)) { //this is the last resort... First we'll try to check for a 'good' match, //and if there's more than one we'll ask it to the user List<IFile> likelyFiles = getLikelyFiles(path, w); IFile iFile = selectWorkspaceFile(likelyFiles.toArray(new IFile[0])); if (iFile != null) { PySourceLocatorPrefs.addPathTranslation(path, iFile.getLocation()); return new FileEditorInput(iFile); } //ok, ask the user for any file in the computer IEditorInput pydevFileEditorInput = selectFilesystemFileForPath(path); input = pydevFileEditorInput; if (input != null) { File file = PydevFileEditorInput.getFile(pydevFileEditorInput); if (file != null) { PySourceLocatorPrefs.addPathTranslation(path, Path.fromOSString(FileUtils.getFileAbsolutePath(file))); return input; } } PySourceLocatorPrefs.setIgnorePathTranslation(path); } } } else { //file exists IFile workspaceFile = selectWorkspaceFile(files); if (workspaceFile != null) { edInput = new FileEditorInput(workspaceFile); } } if (edInput == null && (onSourceNotFound == PySourceLocatorPrefs.ASK_FOR_FILE_GET_FROM_SERVER || onSourceNotFound == PySourceLocatorPrefs.GET_FROM_SERVER)) { if (pyStackFrame != null) { try { String fileContents = pyStackFrame.getFileContents(); if (fileContents != null && fileContents.length() > 0) { String lastSegment = path.lastSegment(); File workspaceMetadataFile = PydevPlugin.getWorkspaceMetadataFile("temporary_files"); if (!workspaceMetadataFile.exists()) { workspaceMetadataFile.mkdirs(); } File file = new File(workspaceMetadataFile, lastSegment); try { if (file.exists()) { file.delete(); } } catch (Exception e) { } FileUtils.writeStrToFile(fileContents, file); try { file.setReadOnly(); } catch (Exception e) { } edInput = PydevFileEditorInput.create(file, true); } } catch (Exception e) { Log.log(e); } } } return edInput; } /** * @param matchName the name to match in the editor * @return an editor input from an existing editor available */ private IEditorInput getEditorInputFromExistingEditors(final String matchName) { final Tuple<IWorkbenchWindow, IEditorInput> workbenchAndReturn = new Tuple<IWorkbenchWindow, IEditorInput>( PlatformUI.getWorkbench().getActiveWorkbenchWindow(), null); Runnable r = new Runnable() { public void run() { IWorkbenchWindow workbenchWindow = workbenchAndReturn.o1; if (workbenchWindow == null) { workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); } if (workbenchWindow == null) { return; } IWorkbenchPage activePage = workbenchWindow.getActivePage(); if (activePage == null) { return; } IEditorReference[] editorReferences = activePage.getEditorReferences(); for (IEditorReference editorReference : editorReferences) { IEditorPart editor = editorReference.getEditor(false); if (editor != null) { if (editor instanceof PyEdit) { PyEdit pyEdit = (PyEdit) editor; IEditorInput editorInput = pyEdit.getEditorInput(); if (editorInput instanceof IPathEditorInput) { IPathEditorInput pathEditorInput = (IPathEditorInput) editorInput; IPath localPath = pathEditorInput.getPath(); if (localPath != null) { String considerName = localPath.segment(localPath.segmentCount() - 1); if (matchName.equals(considerName)) { workbenchAndReturn.o2 = editorInput; return; } } } else { File editorFile = pyEdit.getEditorFile(); if (editorFile != null) { if (editorFile.getName().equals(matchName)) { workbenchAndReturn.o2 = editorInput; return; } } } } } } } }; if (workbenchAndReturn.o1 == null) { //not ui-thread Display.getDefault().syncExec(r); } else { r.run(); } return workbenchAndReturn.o2; } /** * This is the last resort... pointing to some filesystem file to get the editor for some path. */ protected IEditorInput selectFilesystemFileForPath(final IPath path) { final List<String> l = new ArrayList<String>(); Runnable r = new Runnable() { public void run() { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); FileDialog dialog = new FileDialog(shell); dialog.setText(path + " - select correspondent filesystem file."); String[] wildcardValidSourceFiles = FileTypesPreferencesPage.getWildcardValidSourceFiles(); wildcardValidSourceFiles = StringUtils.addString(wildcardValidSourceFiles, "*"); dialog.setFilterExtensions(wildcardValidSourceFiles); String string = dialog.open(); if (string != null) { l.add(string); } } }; if (Display.getCurrent() == null) { //not ui-thread Display.getDefault().syncExec(r); } else { r.run(); } if (l.size() > 0) { String fileAbsolutePath = FileUtils.getFileAbsolutePath(l.get(0)); return PydevFileEditorInput.create(new File(fileAbsolutePath), true); } return null; } /** * This method will pass all the files in the workspace and check if there's a file that might * be a match to some path (use only as an almost 'last-resort'). */ private List<IFile> getLikelyFiles(IPath path, IWorkspace w) { List<IFile> ret = new ArrayList<IFile>(); try { IResource[] resources = w.getRoot().members(); getLikelyFiles(path, ret, resources); } catch (CoreException e) { Log.log(e); } return ret; } /** * Used to recursively get the likely files given the first set of containers */ private void getLikelyFiles(IPath path, List<IFile> ret, IResource[] resources) throws CoreException { String strPath = path.removeFileExtension().lastSegment().toLowerCase(); //this will return something as 'foo' for (IResource resource : resources) { if (resource instanceof IFile) { IFile f = (IFile) resource; if (PythonPathHelper.isValidSourceFile(f)) { if (resource.getFullPath().removeFileExtension().lastSegment().toLowerCase().equals(strPath)) { ret.add((IFile) resource); } } } else if (resource instanceof IContainer) { getLikelyFiles(path, ret, ((IContainer) resource).members()); } } } /** * Creates some editor input for the passed file * @param file the file for which an editor input should be created * @return the editor input that'll open the passed file. */ private IEditorInput createEditorInput(File file) { IFile[] workspaceFile = getWorkspaceFiles(file); if (workspaceFile != null && workspaceFile.length > 0) { IFile file2 = selectWorkspaceFile(workspaceFile); if (file2 != null) { return new FileEditorInput(file2); } else { return new FileEditorInput(workspaceFile[0]); } } return PydevFileEditorInput.create(file, true); } /** * @param files the files that should be filtered * @return a new array of IFile with only the files that actually exist. */ private IFile[] filterNonExistentFiles(IFile[] files) { if (files == null) return null; int length = files.length; ArrayList<IFile> existentFiles = new ArrayList<IFile>(length); for (int i = 0; i < length; i++) { if (files[i].exists()) existentFiles.add(files[i]); } return (IFile[]) existentFiles.toArray(new IFile[existentFiles.size()]); } /** * @param containers the containers that should be filtered * @return a new array of IContainer with only the containers that actually exist. */ public IContainer[] filterNonExistentContainers(IContainer[] containers) { if (containers == null) return null; int length = containers.length; ArrayList<IContainer> existentFiles = new ArrayList<IContainer>(length); for (int i = 0; i < length; i++) { if (containers[i].exists()) existentFiles.add(containers[i]); } return (IContainer[]) existentFiles.toArray(new IContainer[existentFiles.size()]); } /** * Ask the user to select one file of the given list of files (if some is available) * * @param files the files available for selection. * @return the selected file (from the files passed) or null if there was no file available for * selection or if the user canceled it. */ private IFile selectWorkspaceFile(final IFile[] files) { if (files == null || files.length == 0) { return null; } if (files.length == 1) { return files[0]; } final List<IFile> selected = new ArrayList<IFile>(); Runnable r = new Runnable() { public void run() { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell, new PyFileLabelProvider()); dialog.setElements(files); dialog.setTitle("Select Workspace File"); dialog.setMessage("File may be matched to multiple files in the workspace."); if (dialog.open() == Window.OK) { selected.add((IFile) dialog.getFirstResult()); } } }; if (Display.getCurrent() == null) { //not ui-thread Display.getDefault().syncExec(r); } else { r.run(); } if (selected.size() > 0) { return selected.get(0); } return null; } }