/** * 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. */ /* * Author: atotic * Created: Sep 8, 2003 */ package org.python.pydev.ui.pythonpathconf; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.File; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; 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.Properties; import java.util.Set; import java.util.zip.ZipFile; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.ui.EnvironmentTab; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; import org.python.copiedfromeclipsesrc.JDTNotAvailableException; import org.python.copiedfromeclipsesrc.PythonListEditor; import org.python.pydev.core.IInterpreterInfo; import org.python.pydev.core.IInterpreterManager; import org.python.pydev.core.PropertiesHelper; import org.python.pydev.core.bundle.ImageCache; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.core.log.Log; import org.python.pydev.core.uiutils.AsynchronousProgressMonitorDialog; import org.python.pydev.jython.IPythonInterpreter; import org.python.pydev.jython.JythonPlugin; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.runners.SimpleJythonRunner; import org.python.pydev.ui.TabVariables; import org.python.pydev.ui.UIConstants; import org.python.pydev.ui.dialogs.InterpreterInputDialog; import org.python.pydev.ui.dialogs.PyDialogHelpers; import org.python.pydev.ui.filetypes.FileTypesPreferencesPage; import com.aptana.shared_core.io.FileUtils; import com.aptana.shared_core.structure.Tuple; /** * Field editor for a list of python interpreter with executable verifier. * * <p> * heavily inspired by org.eclipse.jface.preference.PathEditor * <p> * Tries to run python binary to make sure it exists * * Subclasses must implement :<code>parseString</code>,<code>createList</code>,<code>getNewInputObject</code> */ public abstract class AbstractInterpreterEditor extends PythonListEditor { /** * Interpreter manager we are using (given at init) */ private IInterpreterManager interpreterManager; /** * Tree to add libs. */ private Tree treeWithLibs; /** * This is the control where the interpreters are shown */ /*default*/Tree treeWithInterpreters; /** * Images */ private Image imageSystemLibRoot; private Image imageSystemLib; private Image environmentImage; private Composite boxSystem; private Button addBtSystemFolder; private Button removeBtSystemFolder; private Button addBtSystemJar; private SelectionListener selectionListenerSystem; private Map<String, IInterpreterInfo> nameToInfo = new HashMap<String, IInterpreterInfo>(); private Set<String> exeOrJarOfInterpretersToRestore = new HashSet<String>(); private Set<String> exeOrJarOfInterpretersWithBuiltinsChanged = new HashSet<String>(); private Set<String> exeOrJarOfInterpretersWithPredefinedChanged = new HashSet<String>(); private Set<String> exeOrJarOfInterpretersWithStringSubstitutionChanged = new HashSet<String>(); private void clearInfos() { nameToInfo.clear(); exeOrJarOfInterpretersToRestore.clear(); exeOrJarOfInterpretersWithBuiltinsChanged.clear(); exeOrJarOfInterpretersWithPredefinedChanged.clear(); exeOrJarOfInterpretersWithStringSubstitutionChanged.clear(); } public Set<String> getInterpreterExeOrJarToRestoreAndClear() { HashSet<String> set = new HashSet<String>(); set.addAll(exeOrJarOfInterpretersToRestore); set.addAll(exeOrJarOfInterpretersWithBuiltinsChanged); set.addAll(exeOrJarOfInterpretersWithPredefinedChanged); set.addAll(exeOrJarOfInterpretersWithStringSubstitutionChanged); exeOrJarOfInterpretersToRestore.clear(); exeOrJarOfInterpretersWithBuiltinsChanged.clear(); exeOrJarOfInterpretersWithPredefinedChanged.clear(); exeOrJarOfInterpretersWithStringSubstitutionChanged.clear(); return set; } public IInterpreterInfo[] getExesList() { TreeItem[] items = treeWithInterpreters.getItems(); ArrayList<IInterpreterInfo> infos = new ArrayList<IInterpreterInfo>(); for (TreeItem exe : items) { IInterpreterInfo info = this.nameToInfo.get(getNameFromTreeItem(exe)); if (info == null) { Log.log("Didn't expect interpreter info to be null in the memory: " + exe); } else { infos.add(info); } } return infos.toArray(new IInterpreterInfo[infos.size()]); } protected String getNameFromTreeItem(TreeItem treeItem) { return treeItem.getText(0); } /*default*/InterpreterInfo getSelectedInfo() { if (treeWithInterpreters.getSelectionCount() == 1) { TreeItem[] selection = treeWithInterpreters.getSelection(); return (InterpreterInfo) this.nameToInfo.get(getNameFromTreeItem(selection[0])); } return null; } /** * Creates a path field editor linked to the preference name passed * * @param labelText the label text of the field editor * @param parent the parent of the field editor's control */ protected AbstractInterpreterEditor(String preferenceName, String labelText, Composite parent, IInterpreterManager interpreterManager) { init(preferenceName, labelText); this.interpreterManager = interpreterManager; IInterpreterInfo[] interpreters = this.interpreterManager.getInterpreterInfos(); clearInfos(); for (IInterpreterInfo interpreterInfo : interpreters) { if (interpreterInfo != null) { nameToInfo.put(interpreterInfo.getName(), interpreterInfo.makeCopy()); } } if (USE_ICONS) { ImageCache imageCache = PydevPlugin.getImageCache(); imageSystemLibRoot = imageCache.get(UIConstants.LIB_SYSTEM_ROOT); imageSystemLib = imageCache.get(UIConstants.LIB_SYSTEM); environmentImage = imageCache.get(UIConstants.ENVIRONMENT_ICON); } createControl(parent); updateTree(); } /** * @see org.eclipse.jface.preference.FieldEditor#createControl(org.eclipse.swt.widgets.Composite) */ protected void createControl(Composite parent) { super.createControl(parent); treeWithInterpreters = getListControl(parent); treeWithInterpreters.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { updateTree(); } public void widgetDefaultSelected(SelectionEvent e) { updateTree(); } }); treeWithInterpreters.addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.F2) { renameSelection(); } } }); treeWithInterpreters.addMouseListener(new MouseListener() { public void mouseUp(MouseEvent e) { } public void mouseDown(MouseEvent e) { } public void mouseDoubleClick(MouseEvent e) { renameSelection(); } }); } private void renameSelection() { int index = getSelectionIndex(); if (index >= 0) { TreeItem curr = treeWithInterpreters.getItem(index); final String initialName = getNameFromTreeItem(curr); InputDialog d = new InputDialog(this.getShell(), "New name", "Please specify the new name of the interpreter.", initialName, new IInputValidator() { public String isValid(String newText) { if (newText == null || newText.trim().equals("")) { return "Please specify a non-empty name."; } newText = newText.trim(); if (newText.equals(initialName)) { return null; } return getDuplicatedMessageError(newText, null); } }); int retCode = d.open(); if (retCode == InputDialog.OK) { String newName = d.getValue().trim(); if (!newName.equals(initialName)) { IInterpreterInfo info = this.nameToInfo.get(initialName); info.setName(newName); curr.setText(0, newName); this.nameToInfo.remove(initialName); this.nameToInfo.put(newName, info); this.exeOrJarOfInterpretersToRestore.add(info.getExecutableOrJar()); } } } } /** * @param parent * @return */ private Tree getTreeLibsControl(Composite parent) { if (treeWithLibs == null) { treeWithLibs = new Tree(parent, SWT.BORDER | SWT.MULTI); treeWithLibs.setFont(parent.getFont()); treeWithLibs.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent event) { treeWithLibs = null; } }); } return treeWithLibs; } @Override protected void disposeOfTreeItem(TreeItem t) { String nameFromTreeItem = this.getNameFromTreeItem(t); this.nameToInfo.remove(nameFromTreeItem); super.disposeOfTreeItem(t); } protected void adjustForNumColumns(int numColumns) { super.adjustForNumColumns(numColumns); ((GridData) tabFolder.getLayoutData()).horizontalSpan = numColumns; } protected TabFolder tabFolder; private EnvironmentTab environmentTab; private MyEnvWorkingCopy workingCopy = new MyEnvWorkingCopy(); private TabVariables tabVariables; private AbstractListWithNewRemoveControl forcedBuiltins; private AbstractListWithNewRemoveControl predefinedCompletions; /** * @see org.eclipse.jface.preference.ListEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite, int) */ protected void doFillIntoGrid(Composite parent, int numColumns) { super.doFillIntoGrid(parent, numColumns); GridData gd = new GridData(); tabFolder = new TabFolder(parent, SWT.None); gd = new GridData(); gd.horizontalAlignment = SWT.FILL; gd.verticalAlignment = SWT.FILL; gd.grabExcessVerticalSpace = true; gd.horizontalSpan = numColumns; tabFolder.setLayoutData(gd); createTreeLibsControlTab(); //----------------------- FORCED BUILTINS forcedBuiltins = new AbstractListWithNewRemoveControl(this) { protected List<String> getStringsFromInfo(InterpreterInfo info) { ArrayList<String> ret = new ArrayList<String>(); for (Iterator<String> iter = info.forcedLibsIterator(); iter.hasNext();) { ret.add(iter.next()); } return ret; } protected void removeSelectedFrominfo(InterpreterInfo info, String[] builtins) { for (String builtin : builtins) { info.removeForcedLib(builtin); } exeOrJarOfInterpretersWithBuiltinsChanged.add(info.getExecutableOrJar()); } protected String getInput() { IInputValidator validator = new IInputValidator() { public String isValid(String newText) { for (char c : newText.toCharArray()) { if (!Character.isJavaIdentifierPart(c) && c != ' ' && c != ',' && c != '.') { return "Can only accept valid python module names (char: '" + c + "' not accepted)"; } } return null; } }; InputDialog d = new InputDialog(getShell(), "Builtin to add", "Builtin to add (comma separated)", "", validator); int retCode = d.open(); String builtins = null; if (retCode == InputDialog.OK) { builtins = d.getValue(); } return builtins; } protected void addInputToInfo(InterpreterInfo info, String builtins) { java.util.List<String> split = StringUtils.splitAndRemoveEmptyTrimmed(builtins, ','); for (String string : split) { String trimmed = string.trim(); if (trimmed.length() > 0) { info.addForcedLib(trimmed); } } exeOrJarOfInterpretersWithBuiltinsChanged.add(info.getExecutableOrJar()); } }; forcedBuiltins.createTab("Forced Builtins", "Forced Builtins (check <a>Manual</a> for more info)."); //----------------------- PREDEFINED COMPLETIONS predefinedCompletions = new AbstractListWithNewRemoveControl(this) { private Button addAPIBt; protected List<String> getStringsFromInfo(InterpreterInfo info) { return info.getPredefinedCompletionsPath(); } protected void removeSelectedFrominfo(InterpreterInfo info, String[] items) { for (String item : items) { info.removePredefinedCompletionPath(item); } exeOrJarOfInterpretersWithPredefinedChanged.add(info.getExecutableOrJar()); } protected String getInput() { DirectoryDialog dialog = new DirectoryDialog(getShell()); dialog.setFilterPath(lastDirectoryDialogPath); String filePath = dialog.open(); if (filePath != null) { lastDirectoryDialogPath = filePath; } return filePath; } protected void addInputToInfo(InterpreterInfo info, String item) { info.addPredefinedCompletionsPath(item); exeOrJarOfInterpretersWithPredefinedChanged.add(info.getExecutableOrJar()); } protected void createButtons(AbstractInterpreterEditor interpreterEditor) { super.createButtons(interpreterEditor); addAPIBt = interpreterEditor.createBt(box, "Add from QScintilla api file", this);//$NON-NLS-1$ } public void widgetDisposed(DisposeEvent event) { super.widgetDisposed(event); if (addAPIBt != null) { addAPIBt.dispose(); addAPIBt = null; } } public void widgetSelected(SelectionEvent event) { super.widgetSelected(event); Widget widget = event.widget; if (widget == addAPIBt) { addAPIBt(); } } private void addAPIBt() { final AbstractInterpreterEditor interpreterEditor = this.container.get(); Assert.isNotNull(interpreterEditor); final InterpreterInfo info = interpreterEditor.getSelectedInfo(); if (info != null) { FileDialog dialog = new FileDialog(getShell(), SWT.PRIMARY_MODAL | SWT.MULTI); dialog.setFilterExtensions(new String[] { "*.api" }); dialog.setText("Select .api file to be converted to .pypredef."); dialog.setFilterPath(lastFileDialogPath); final String filePath = dialog.open(); if (filePath != null) { lastFileDialogPath = filePath; File filePath1 = new File(filePath); final String dir = filePath1.getParent(); IInputValidator validator = new IInputValidator() { public String isValid(String newText) { if (newText.length() == 0) { return "Number not provided."; } try { Integer.parseInt(newText); } catch (NumberFormatException e) { return "The string: " + newText + " is not a valid integer."; } return null; } }; final InputDialog d = new InputDialog( getShell(), "Number of tokens to consider module", "Please specify the number of tokens to consider a module from the .api file\n\n" + "i.e.: if there's a PyQt4.QtCore.QObject and PyQt4.QtCore is a module and QtObject " + "is the first class, the number of tokens to consider a module would be 2 (one for " + "PyQt4 and another for QtCore).", "", validator); int retCode = d.open(); final ByteArrayOutputStream output = new ByteArrayOutputStream(); if (retCode == InputDialog.OK) { ProgressMonitorDialog monitorDialog = new AsynchronousProgressMonitorDialog(getShell()); monitorDialog.setBlockOnOpen(false); final Exception[] exception = new Exception[1]; try { IRunnableWithProgress operation = new IRunnableWithProgress() { public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Restoring PYTHONPATH", IProgressMonitor.UNKNOWN); IPythonInterpreter interpreter = JythonPlugin .newPythonInterpreter(false, false); interpreter.setErr(output); interpreter.setOut(output); HashMap<String, Object> locals = new HashMap<String, Object>(); locals.put("api_file", filePath); locals.put("parts_for_module", d.getValue()); locals.put("cancel_monitor", monitor); try { JythonPlugin.exec(locals, "convert_api_to_pypredef.py", interpreter); } catch (Exception e) { Log.log(e + "\n\n" + output.toString()); exception[0] = e; } monitor.done(); } }; monitorDialog.run(true, true, operation); } catch (Exception e) { Log.log(e); } Exception e = exception[0]; String contents = output.toString(); if (e == null && contents.indexOf("SUCCESS") != -1) { addInputToInfo(info, dir); interpreterEditor.updateTree(); } else { if (e != null) { MessageDialog.openError(getShell(), "Error creating .pypredef files", e.getMessage() + "\n\n" + contents); } else { MessageDialog.openError(getShell(), "Error creating .pypredef files", contents); } } } } } } }; predefinedCompletions.createTab("Predefined", "Predefined completions (check <a>Manual</a> for more info)."); createEnvironmentVariablesTab(); createStringSubstitutionTab(); } /** * Creates tab to show the string substitution variables. */ private void createStringSubstitutionTab() { Map<String, String> initialVariables = new HashMap<String, String>(); tabVariables = new TabVariables(tabFolder, initialVariables); } /** * Creates tab to show the environment variables. */ private void createEnvironmentVariablesTab() { Composite parent; TabItem tabItem = new TabItem(tabFolder, SWT.None); tabItem.setText("Environment"); tabItem.setImage(environmentImage); Composite composite = new Composite(tabFolder, SWT.None); parent = composite; composite.setLayout(new GridLayout(1, false)); environmentTab = new EnvironmentTab() { protected void createAppendReplace(Composite parent) { super.createAppendReplace(parent); appendEnvironment.setVisible(false); replaceEnvironment.setVisible(false); } }; environmentTab.createControl(parent); GridData gd = new GridData(); gd.horizontalAlignment = SWT.FILL; gd.verticalAlignment = SWT.FILL; gd.grabExcessHorizontalSpace = true; gd.grabExcessVerticalSpace = true; environmentTab.getControl().setLayoutData(gd); tabItem.setControl(composite); } /** * Creates tab to show the pythonpath (libraries) */ private void createTreeLibsControlTab() { Composite parent; GridData gd; TabItem tabItem = new TabItem(tabFolder, SWT.None); tabItem.setText("Libraries"); tabItem.setImage(imageSystemLibRoot); Composite composite = new Composite(tabFolder, SWT.None); parent = composite; composite.setLayout(new GridLayout(2, false)); Label l1 = new Label(parent, SWT.None); l1.setText("System PYTHONPATH"); gd = new GridData(); gd.horizontalSpan = 2; l1.setLayoutData(gd); //the tree treeWithLibs = getTreeLibsControl(parent); gd = new GridData(); gd.horizontalAlignment = SWT.FILL; gd.verticalAlignment = SWT.FILL; gd.grabExcessHorizontalSpace = true; gd.grabExcessVerticalSpace = true; gd.heightHint = 200; treeWithLibs.setLayoutData(gd); //buttons at the side of the tree Composite control = getButtonBoxControlSystem(parent); gd = new GridData(); gd.verticalAlignment = GridData.BEGINNING; control.setLayoutData(gd); tabItem.setControl(composite); } /** * Returns this field editor's button box containing the Add Source Folder, Add Jar and Remove * * @param parent the parent control * @return the button box */ public Composite getButtonBoxControlSystem(Composite parent) { if (boxSystem == null) { boxSystem = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.marginWidth = 0; boxSystem.setLayout(layout); addBtSystemFolder = createBt(boxSystem, "New Folder", getSelectionListenerSystem());//$NON-NLS-1$ switch (this.interpreterManager.getInterpreterType()) { case IInterpreterManager.INTERPRETER_TYPE_JYTHON: addBtSystemJar = createBt(boxSystem, "New Jar/Zip(s)", getSelectionListenerSystem());//$NON-NLS-1$ break; default: addBtSystemJar = createBt(boxSystem, "New Egg/Zip(s)", getSelectionListenerSystem());//$NON-NLS-1$ } removeBtSystemFolder = createBt(boxSystem, "ListEditor.remove", getSelectionListenerSystem());//$NON-NLS-1$ boxSystem.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent event) { addBtSystemJar = null; addBtSystemFolder = null; removeBtSystemFolder = null; boxSystem = null; } }); } else { checkParent(boxSystem, parent); } return boxSystem; } private static String lastDirectoryDialogPath = null; private static String lastFileDialogPath = null; /** * Returns this field editor's selection listener. The listener is created if necessary. * * @return the selection listener */ private SelectionListener getSelectionListenerSystem() { if (selectionListenerSystem == null) { selectionListenerSystem = new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (treeWithInterpreters.getSelectionCount() == 1) { TreeItem[] selection = treeWithInterpreters.getSelection(); String nameFromTreeItem = getNameFromTreeItem(selection[0]); InterpreterInfo info = (InterpreterInfo) nameToInfo.get(nameFromTreeItem); exeOrJarOfInterpretersToRestore.add(info.getExecutableOrJar()); Widget widget = event.widget; if (widget == addBtSystemFolder) { DirectoryDialog dialog = new DirectoryDialog(getShell()); dialog.setFilterPath(lastDirectoryDialogPath); String filePath = dialog.open(); if (filePath != null) { lastDirectoryDialogPath = filePath; info.libs.add(filePath); } } else if (widget == addBtSystemJar) { FileDialog dialog = new FileDialog(getShell(), SWT.PRIMARY_MODAL | SWT.MULTI); switch (AbstractInterpreterEditor.this.interpreterManager.getInterpreterType()) { case IInterpreterManager.INTERPRETER_TYPE_JYTHON: dialog.setFilterExtensions(FileTypesPreferencesPage .getWildcardJythonValidZipFiles()); break; default: dialog.setFilterExtensions(FileTypesPreferencesPage .getWildcardPythonValidZipFiles()); } dialog.setFilterPath(lastFileDialogPath); String filePath = dialog.open(); if (filePath != null) { lastFileDialogPath = filePath; File filePath1 = new File(filePath); String dir = filePath1.getParent(); String[] fileNames = dialog.getFileNames(); for (String f : fileNames) { f = dir + File.separatorChar + f; if (!info.libs.contains(f)) { info.libs.add(f); } } } } else if (widget == removeBtSystemFolder) { TreeItem[] libSelection = treeWithLibs.getSelection(); for (int i = 0; i < libSelection.length; i++) { TreeItem s = libSelection[i]; String text = s.getText(); info.libs.remove(text); // changed = true; } } updateTree(); } } }; } return selectionListenerSystem; } /** * Helper method to create a push button. * * @param parent the parent control * @param key the resource name used to supply the button's label text * @param listenerToAdd * @return Button */ /*default*/Button createBt(Composite parent, String key, SelectionListener listenerToAdd) { Button button = new Button(parent, SWT.PUSH); button.setText(JFaceResources.getString(key)); button.setFont(parent.getFont()); GridData data = new GridData(GridData.FILL_HORIZONTAL); // data.heightHint = convertVerticalDLUsToPixels(button, IDialogConstants.BUTTON_HEIGHT); int widthHint = convertHorizontalDLUsToPixels(button, IDialogConstants.BUTTON_WIDTH); data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); button.setLayoutData(data); button.addSelectionListener(listenerToAdd); return button; } /** * @param listControl */ public void updateTree() { int index = this.getSelectionIndex(); if (index >= 0) { TreeItem item = treeWithInterpreters.getItem(index); fillPathItemsFromName(getNameFromTreeItem(item)); } else { fillPathItemsFromName(null); if (treeWithInterpreters.getItemCount() > 0) { treeWithInterpreters.setSelection(treeWithInterpreters.getItem(0)); selectionChanged(); fillPathItemsFromName(getNameFromTreeItem(treeWithInterpreters.getItem(0))); } } } public Shell getShell() { return super.getShell(); } /** * @param s * */ private void fillPathItemsFromName(String name) { treeWithLibs.removeAll(); this.forcedBuiltins.removeAllFromList(); this.predefinedCompletions.removeAllFromList(); //before any change, apply the changes in the previous info (if not set, that's ok) InterpreterInfo workingCopyInfo = workingCopy.getInfo(); if (workingCopyInfo != null) { environmentTab.performApply(workingCopy); Properties propertiesFromMap = PropertiesHelper.createPropertiesFromMap(this.tabVariables .getTreeItemsAsMap()); Properties stringSubstitutionVariables = workingCopyInfo.getStringSubstitutionVariables(); boolean equals = false; if (stringSubstitutionVariables == null) { if (propertiesFromMap == null || propertiesFromMap.size() == 0) { equals = true; } } else { equals = stringSubstitutionVariables.equals(propertiesFromMap); } if (!equals) { exeOrJarOfInterpretersWithStringSubstitutionChanged.add(workingCopyInfo.getExecutableOrJar()); workingCopyInfo.setStringSubstitutionVariables(propertiesFromMap); } } if (name != null) { TreeItem item = new TreeItem(treeWithLibs, SWT.NONE); item.setText("System libs"); item.setImage(imageSystemLibRoot); InterpreterInfo info = (InterpreterInfo) this.nameToInfo.get(name); if (info == null) { Log.log("Didn't expect interpreter info to be null in the memory: " + name); } else { for (Iterator<String> iter = info.libs.iterator(); iter.hasNext();) { TreeItem subItem = new TreeItem(item, SWT.NONE); subItem.setText(iter.next()); subItem.setImage(imageSystemLib); } item.setExpanded(true); this.forcedBuiltins.update(info); this.predefinedCompletions.update(info); workingCopy.setInfo(info); } environmentTab.initializeFrom(workingCopy); Properties stringSubstitutionVariables = info.getStringSubstitutionVariables(); if (stringSubstitutionVariables != null) { this.tabVariables.setTreeItemsFromMap(PropertiesHelper .createMapFromProperties(stringSubstitutionVariables)); } else { this.tabVariables.setTreeItemsFromMap(new HashMap<String, String>()); } } } /** * @return a string with the extensions that are accepted for the interpreter */ public abstract String[] getInterpreterFilterExtensions(); @Override protected Tuple<String, String> getNewInputObject(boolean autoConfig) { CharArrayWriter charWriter = new CharArrayWriter(); PrintWriter logger = new PrintWriter(charWriter); logger.println("Information about process of adding new interpreter:"); try { Tuple<String, String> interpreterNameAndExecutable = null; if (autoConfig) { try { interpreterNameAndExecutable = getAutoNewInput(); } catch (CancelException e) { //user canceled. return null; } if (interpreterNameAndExecutable == null) { reportAutoConfigProblem(null); return null; } } else { InterpreterInputDialog dialog = new InterpreterInputDialog(getShell(), "Select interpreter", "Enter the name and executable of your interpreter", this); logger.println("- Opening dialog to request executable (or jar)."); int result = dialog.open(); if (result == Window.OK) { interpreterNameAndExecutable = dialog.getKeyAndValueEntered(); } else { return null; } } boolean foundError = checkInterpreterNameAndExecutable(interpreterNameAndExecutable, logger, "Error getting info on interpreter"); if (foundError) { return null; } logger.println("- Chosen interpreter (name and file):'" + interpreterNameAndExecutable); if (interpreterNameAndExecutable != null && interpreterNameAndExecutable.o2 != null) { //ok, now that we got the file, let's see if it is valid and get the library info. logger.println("- Ok, file is non-null. Getting info on:" + interpreterNameAndExecutable.o2); ProgressMonitorDialog monitorDialog = new AsynchronousProgressMonitorDialog(this.getShell()); monitorDialog.setBlockOnOpen(false); ObtainInterpreterInfoOperation operation; while (true) { operation = new ObtainInterpreterInfoOperation(interpreterNameAndExecutable.o2, logger, interpreterManager); monitorDialog.run(true, false, operation); if (operation.e != null) { logger.println("- Some error happened while getting info on the interpreter:"); operation.e.printStackTrace(logger); if (operation.e instanceof SimpleJythonRunner.JavaNotConfiguredException) { SimpleJythonRunner.JavaNotConfiguredException javaNotConfiguredException = (SimpleJythonRunner.JavaNotConfiguredException) operation.e; ErrorDialog.openError(this.getShell(), "Error getting info on interpreter", javaNotConfiguredException.getMessage(), PydevPlugin.makeStatus(IStatus.ERROR, "Java vm not configured.\n", javaNotConfiguredException)); } else if (operation.e instanceof JDTNotAvailableException) { JDTNotAvailableException noJdtException = (JDTNotAvailableException) operation.e; ErrorDialog.openError(this.getShell(), "Error getting info on interpreter", noJdtException.getMessage(), PydevPlugin.makeStatus(IStatus.ERROR, "JDT not available.\n", noJdtException)); } else { if (autoConfig) { reportAutoConfigProblem(operation.e); } else { String errorMsg = "Error getting info on interpreter.\n\n" + "Common reasons include:\n\n" + "- Using an unsupported version\n" + " (Python and Jython require at least version 2.1 and Iron Python 2.6).\n" + "\n" + "- Specifying an invalid interpreter\n" + " (usually a link to the actual interpreter on Mac or Linux)" + ""; //show the user a message (so that it does not fail silently)... ErrorDialog.openError(this.getShell(), "Unable to get info on the interpreter.", errorMsg, PydevPlugin.makeStatus(IStatus.ERROR, "See error log for details.", operation.e)); } } throw operation.e; } else { if (operation.result != null) { foundError = checkInterpreterNameAndExecutable(new Tuple<String, String>( interpreterNameAndExecutable.o1, operation.result.executableOrJar), logger, "Error adding interpreter"); if (foundError) { return null; } try { //Ok, we got the result, so, let's check if things are correct (i.e.: do we have threading.py, traceback.py?) HashSet<String> hashSet = new HashSet<String>(); hashSet.add("threading"); hashSet.add("traceback"); String[] validSourceFiles = FileTypesPreferencesPage.getValidSourceFiles(); Set<String> extensions = new HashSet<String>(Arrays.asList(validSourceFiles)); for (String s : operation.result.libs) { File file = new File(s); if (file.isDirectory()) { String[] directoryFiles = file.list(); if (directoryFiles != null) { for (String found : directoryFiles) { List<String> split = StringUtils.split(found, '.'); if (split.size() == 2) { if (extensions.contains(split.get(1))) { hashSet.remove(split.get(0)); } } } } else { logger.append("Warning: unable to get contents of directory: " + file + " (permission not available, it's not a dir or dir does not exist)."); } } else if (file.isFile()) { //Zip file? try { ZipFile zipFile = new ZipFile(file); for (String extension : validSourceFiles) { if (zipFile.getEntry("threading." + extension) != null) { hashSet.remove("threading"); } if (zipFile.getEntry("traceback." + extension) != null) { hashSet.remove("traceback"); } } } catch (Exception e) { //ignore (not zip file) } } } if (hashSet.size() > 0) { //The /Lib folder wasn't there (or at least threading.py and traceback.py weren't found) int choice = PyDialogHelpers .openCriticalWithChoices( "Error: Python stdlib source files not found.", "Error: Python stdlib not found or stdlib found without .py files.\n" + "\n" + "It seems that the Python /Lib folder (which contains the standard library) " + "was not found/selected during the install process or the stdlib does not contain " + "the required .py files (i.e.: only has .pyc files).\n" + "\n" + "This folder (which contains files such as threading.py and traceback.py) is " + "required for PyDev to function properly, and it must contain the actual source files, not " + "only .pyc files. if you don't have the .py files in your install, please use an install from " + "python.org or grab the standard library for your install from there.\n" + "\n" + "If this is a virtualenv install, the /Lib folder from the base install needs to be selected " + "(unlike the site-packages which is optional).\n" + "\n" + "What do you want to do?\n\n" + "Note: if you choose to proceed, the /Lib with the standard library .py source files must " + "be added later on, otherwise PyDev may not function properly.", new String[] { "Re-select folders", "Cancel", "Proceed anyways" }); if (choice == 0) { //Keep on with outer while(true) continue; } if (choice != 2) { return null; } } } catch (Exception e) { ErrorDialog.openError(this.getShell(), "Problem checking if the interpreter paths are correct.", e.getMessage(), PydevPlugin.makeStatus(IStatus.ERROR, "See error log for details.", e)); throw e; } operation.result.setName(interpreterNameAndExecutable.o1); logger.println("- Success getting the info. Result:" + operation.result); String newName = operation.result.getName(); this.nameToInfo.put(newName, operation.result.makeCopy()); exeOrJarOfInterpretersToRestore.add(operation.result.executableOrJar); return new Tuple<String, String>(operation.result.getName(), operation.result.executableOrJar); } else { return null; } } } } } catch (Exception e) { Log.log(e); return null; } finally { Log.logInfo(charWriter.toString()); } return null; } private boolean checkInterpreterNameAndExecutable(Tuple<String, String> interpreterNameAndExecutable, PrintWriter logger, String errorMsg) { boolean foundError = false; //Check auto config or dialog return. if (interpreterNameAndExecutable == null) { logger.println("- When trimmed, the chosen file was null (returning null)."); ErrorDialog.openError(this.getShell(), errorMsg, "interpreterNameAndExecutable == null", PydevPlugin .makeStatus(IStatus.ERROR, "interpreterNameAndExecutable == null", new RuntimeException())); foundError = true; } if (!foundError) { if (interpreterNameAndExecutable.o2.trim().length() == 0) { logger.println("- When trimmed, the chosen file was empty (returning null)."); ErrorDialog.openError(this.getShell(), errorMsg, "interpreterNameAndExecutable size == empty", PydevPlugin.makeStatus(IStatus.ERROR, "interpreterNameAndExecutable size == empty", new RuntimeException())); foundError = true; } } if (!foundError) { String error = getDuplicatedMessageError(interpreterNameAndExecutable.o1, interpreterNameAndExecutable.o2); if (error != null) { logger.println("- Duplicated interpreter found."); ErrorDialog.openError(this.getShell(), errorMsg, error, PydevPlugin.makeStatus(IStatus.ERROR, "Duplicated interpreter information", new RuntimeException())); foundError = true; } } return foundError; } /** * Gets a unique name for the interpreter based on an initial expected name. */ public String getUniqueInterpreterName(final String expectedName) { String additional = ""; int i = 0; while (getDuplicatedMessageError(expectedName + additional, null) != null) { i++; additional = String.valueOf(i); } return expectedName + additional; } /** * Uses the passed name and executable to see if it'll match against one of the existing * * The null parameters are ignored. */ public String getDuplicatedMessageError(String interpreterName, String executableOrJar) { String error = null; if (interpreterName != null) { interpreterName = interpreterName.trim(); if (this.nameToInfo.containsKey(interpreterName)) { error = "An interpreter is already configured with the name: " + interpreterName; } } if (executableOrJar != null) { executableOrJar = executableOrJar.trim(); for (IInterpreterInfo info : this.nameToInfo.values()) { if (info.getExecutableOrJar().trim().equals(executableOrJar)) { error = "An interpreter is already configured with the path: " + executableOrJar; } } } return error; } private void reportAutoConfigProblem(Exception e) { String errorMsg = "Unable to auto-configure the interpreter.\n" + "Please create a new interpreter using the 'New' button."; ErrorDialog.openError(this.getShell(), "Unable to auto-configure.", errorMsg, PydevPlugin.makeStatus(IStatus.ERROR, "Unable to gather the needed info from the system.\n" + "\n" + "This usually means that your interpreter is not in\n" + "the system PATH.", e)); } public static final class CancelException extends Exception { private static final long serialVersionUID = 1L; } public final CancelException cancelException = new CancelException(); /** * @return a tuple with the name of the interpreter and the string with the file to be executed * (for python could be just python.exe) and for jython the jython.jar location. * * This is also be platform-dependent (so, it could be python.exe or just python) * * If it cannot be determined, the return should be null (and not a tuple with empty values) */ protected abstract Tuple<String, String> getAutoNewInput() throws CancelException; @Override protected void doStore() { //Do nothing (all handled in the preferences page regarding the store (no longer in this editor) } @Override protected void doLoad() { if (treeWithInterpreters != null) { //Work with a copy of the interpreters actually configured. String s = interpreterManager.getPersistedString(); IInterpreterInfo[] array = interpreterManager.getInterpretersFromPersistedString(s); clearInfos(); for (int i = 0; i < array.length; i++) { IInterpreterInfo interpreterInfo = array[i]; createInterpreterItem(interpreterInfo.getName(), interpreterInfo.getExecutableOrJar()); this.nameToInfo.put(interpreterInfo.getName(), interpreterInfo.makeCopy()); } } updateTree(); } public String getPreferenceName() { throw new RuntimeException( "The preferences should be stored/gotten from the IInterpreterManager, and not directly."); } /** * @see org.python.copiedfromeclipsesrc.PythonListEditor#doLoadDefault() */ protected void doLoadDefault() { //do nothing } public Tuple<String, String> getAutoNewInputFromPaths(java.util.List<String> pathsToSearch, String expectedFilename, String nameForUser) { for (String s : pathsToSearch) { if (s.trim().length() > 0) { File file = new File(s.trim()); if (file.isDirectory()) { String[] available = file.list(); if (available != null) { for (String jar : available) { if (jar.toLowerCase().equals(expectedFilename)) { return new Tuple<String, String>(getUniqueInterpreterName(nameForUser), FileUtils.getFileAbsolutePath(new File(file, jar))); } } } } } } return null; } }