/** * Copyright (c) 2005-2013 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 java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; 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.IPathEditorInput; 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.IPythonPathNature; 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.plugin.nature.PythonNature; import org.python.pydev.shared_core.callbacks.ICallback; import org.python.pydev.shared_core.io.FileUtils; import org.python.pydev.shared_core.locator.GetContainers; import org.python.pydev.shared_core.locator.GetFiles; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_ui.editor_input.EditorInputUtils; import org.python.pydev.ui.filetypes.FileTypesPreferencesPage; /** * Refactored from the PydevPlugin: helpers to find some IFile / IEditorInput * from a Path (or java.io.File) * * @author fabioz */ public class PySourceLocatorBase { /** * Helper class. */ private abstract static class FindFromExistingEditors { private final Object matchName; protected FindFromExistingEditors(Object matchName) { this.matchName = matchName; } public IEditorInput findFromOpenedPyEdits() { Object ret = PyEdit.iterOpenEditorsUntilFirstReturn(new ICallback<Object, PyEdit>() { @Override public Object call(PyEdit pyEdit) { IEditorInput editorInput = pyEdit.getEditorInput(); if (editorInput instanceof IPathEditorInput) { IPathEditorInput pathEditorInput = (IPathEditorInput) editorInput; IPath localPath = pathEditorInput.getPath(); if (localPath != null) { if (matchesPath(matchName, editorInput, localPath)) { return editorInput; } } } else { File editorFile = pyEdit.getEditorFile(); if (editorFile != null) { if (matchesFile(matchName, editorInput, editorFile)) { return editorInput; } } } return null; } }); return (IEditorInput) ret; } protected abstract boolean matchesFile(final Object match, IEditorInput editorInput, File editorFile); protected abstract boolean matchesPath(final Object match, IEditorInput editorInput, IPath localPath); } private static class FindFromExistingEditorsName extends PySourceLocatorBase.FindFromExistingEditors { protected FindFromExistingEditorsName(String matchName) { super(matchName); } @Override protected boolean matchesFile(final Object match, IEditorInput editorInput, File editorFile) { String matchName = (String) match; if (editorFile.getName().equals(matchName)) { return true; } return false; } @Override protected boolean matchesPath(final Object match, IEditorInput editorInput, IPath localPath) { String matchName = (String) match; String considerName = localPath.segment(localPath.segmentCount() - 1); if (matchName.equals(considerName)) { return true; } return false; } } private static class FindFromExistingEditorsFile extends PySourceLocatorBase.FindFromExistingEditors { protected FindFromExistingEditorsFile(File matchFile) { super(matchFile); } @Override protected boolean matchesFile(final Object match, IEditorInput editorInput, File editorFile) { File matchFile = (File) match; if (editorFile.equals(matchFile)) { return true; } return false; } @Override protected boolean matchesPath(final Object match, IEditorInput editorInput, IPath localPath) { File matchFile = (File) match; if (Path.fromOSString(FileUtils.getFileAbsolutePath(matchFile)).equals(localPath)) { return true; } return false; } } private final static GetFiles getFiles = new GetFiles() { @Override protected IFile getFileInProject(IPath location, IProject project) { IFile file = super.getFileInProject(location, project); if (file != null) { return file; } PythonNature nature = PythonNature.getPythonNature(project); if (nature != null) { IPythonPathNature pythonPathNature = nature.getPythonPathNature(); try { //Paths Set<IResource> projectSourcePathSet = pythonPathNature.getProjectSourcePathFolderSet(); for (IResource iResource : projectSourcePathSet) { if (iResource instanceof IContainer) { //I.e.: don't consider zip files IContainer iContainer = (IContainer) iResource; file = getFileInContainer(location, iContainer); if (file != null) { return file; } } } } catch (CoreException e) { Log.log(e); } } return null; }; }; private final static GetContainers getContainers = new GetContainers() { @Override protected IContainer getContainerInProject(IPath location, IProject project) { IContainer file = super.getContainerInProject(location, project); if (file != null) { return file; } PythonNature nature = PythonNature.getPythonNature(project); if (nature != null) { IPythonPathNature pythonPathNature = nature.getPythonPathNature(); try { //Paths Set<IResource> projectSourcePathSet = pythonPathNature.getProjectSourcePathFolderSet(); for (IResource iResource : projectSourcePathSet) { if (iResource instanceof IContainer) { //I.e.: don't consider zip files IContainer iContainer = (IContainer) iResource; file = getContainerInContainer(location, iContainer); if (file != null) { return file; } } } } catch (CoreException e) { Log.log(e); } } return null; }; }; /** * 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, IProject project) { return createEditorInput(path, true, null, project); } public IEditorInput createEditorInput(IPath path) { return createEditorInput(path, true, null, null); } public IFile[] getFilesForLocation(IPath location, IProject project, boolean stopOnFirst) { return getFiles.getFilesForLocation(location, project, stopOnFirst); } public IFile getFileForLocation(IPath location, IProject project) { return getFiles.getFileForLocation(location, project); } /** * @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, IProject project) { return getFileForLocation(Path.fromOSString(file.getAbsolutePath()), project); } /** * @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) { boolean stopOnFirst = false; IFile[] files = getFilesForLocation(Path.fromOSString(file.getAbsolutePath()), null, stopOnFirst); if (files == null || files.length == 0) { return null; } return files; } public IContainer getContainerForLocation(IPath location, IProject project) { return getContainers.getContainerForLocation(location, project); } public IContainer[] getContainersForLocation(IPath location) { boolean stopOnFirst = false; return getContainers.getContainersForLocation(location, null, stopOnFirst); } //---------------------------------- 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 * @param project if provided, and if a matching file is found in this project, that file will be * opened before asking the user to select from a list of all matches * * @return the editor input found or none if None was available for the given path */ public IEditorInput createEditorInput(IPath path, boolean askIfDoesNotExist, IPyStackFrame pyStackFrame, IProject project) { int onSourceNotFound = PySourceLocatorPrefs.getOnSourceNotFound(); IEditorInput edInput = null; File systemFile = path.toFile(); IEditorInput input = getEditorInputFromExistingEditors(systemFile); if (input != null) { return input; } 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; } } IFile fileForLocation = getFileForLocation(path, project); if (fileForLocation != null && fileForLocation.exists()) { //if a project was specified, make sure the file found comes from that project return new FileEditorInput(fileForLocation); } //getFileForLocation() will search all projects starting with the one we pass and references, //so, if not found there, it is probably an external file if (systemFile.exists()) { edInput = EditorInputFactory.create(systemFile, true); } 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) 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 IWorkspace w = ResourcesPlugin.getWorkspace(); List<IFile> likelyFiles = getLikelyFiles(path, w); IFile iFile = selectWorkspaceFile(likelyFiles.toArray(new IFile[0])); if (iFile != null) { IPath location = iFile.getLocation(); if (location != null) { PySourceLocatorPrefs.addPathTranslation(path, location); 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 = EditorInputUtils.getFile(pydevFileEditorInput); if (file != null) { PySourceLocatorPrefs.addPathTranslation(path, Path.fromOSString(FileUtils.getFileAbsolutePath(file))); return input; } } PySourceLocatorPrefs.setIgnorePathTranslation(path); } } 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 = EditorInputFactory.create(file, true); } } catch (Exception e) { Log.log(e); } } } return edInput; } /** * @param matchFile the file to match in the editor * @return an editor input from an existing editor available */ private IEditorInput getEditorInputFromExistingEditors(final File matchFile) { return new FindFromExistingEditorsFile(matchFile).findFromOpenedPyEdits(); } /** * @param matchName the name to match in the editor * @return an editor input from an existing editor available */ private IEditorInput getEditorInputFromExistingEditors(final String matchName) { return new FindFromExistingEditorsName(matchName).findFromOpenedPyEdits(); } /** * 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() { @Override 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 EditorInputFactory.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()); } } } /** * 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() { @Override 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; } }