/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.jres; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants; import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; import org.eclipse.jdt.internal.debug.ui.actions.ControlAccessibleListener; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.IVMInstallType; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.VMStandin; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; /** * A composite that displays installed JREs in a combo box, with a 'manage...' * button to modify installed JREs. * <p> * This block implements ISelectionProvider - it sends selection change events * when the checked JRE in the table changes, or when the "use default" button * check state changes. * </p> */ public class JREsComboBlock { public static final String PROPERTY_JRE = "PROPERTY_JRE"; //$NON-NLS-1$ /** * This block's control */ private Composite fControl; /** * VMs being displayed */ private List<Object> fVMs = new ArrayList<>(); /** * The main control */ private Combo fCombo; // Action buttons private Button fManageButton; /** * JRE change listeners */ private ListenerList<IPropertyChangeListener> fListeners = new ListenerList<>(); /** * Whether the default JRE should be in first position (if <code>false</code>, it becomes last). */ private boolean fDefaultFirst; /** * Default JRE descriptor or <code>null</code> if none. */ private JREDescriptor fDefaultDescriptor = null; /** * Specific JRE descriptor or <code>null</code> if none. */ private JREDescriptor fSpecificDescriptor = null; /** * Default JRE radio button or <code>null</code> if none */ private Button fDefaultButton = null; /** * Selected JRE radio button */ private Button fSpecificButton = null; /** * The title used for the JRE block */ private String fTitle = null; /** * Selected JRE profile radio button */ private Button fEnvironmentsButton = null; /** * Combo box of JRE profiles */ private Combo fEnvironmentsCombo = null; private Button fManageEnvironmentsButton = null; // a path to an unavailable JRE private IPath fErrorPath; /** * List of execution environments */ private List<Object> fEnvironments = new ArrayList<>(); private IStatus fStatus = OK_STATUS; private static IStatus OK_STATUS = new Status(IStatus.OK, JDIDebugUIPlugin.getUniqueIdentifier(), 0, "", null); //$NON-NLS-1$ /** * Creates a JREs combo block. * * @param defaultFirst whether the default JRE should be in first position (if <code>false</code>, it becomes last) */ public JREsComboBlock(boolean defaultFirst) { fDefaultFirst= defaultFirst; } public void addPropertyChangeListener(IPropertyChangeListener listener) { fListeners.add(listener); } public void removePropertyChangeListener(IPropertyChangeListener listener) { fListeners.remove(listener); } private void firePropertyChange() { PropertyChangeEvent event = new PropertyChangeEvent(this, PROPERTY_JRE, null, getPath()); for (IPropertyChangeListener listener : fListeners) { listener.propertyChange(event); } } /** * Creates this block's control in the given control. * * @param anscestor containing control */ public void createControl(Composite ancestor) { fControl = SWTFactory.createComposite(ancestor, 1, 1, GridData.FILL_BOTH); if (fTitle == null) { fTitle = JREMessages.JREsComboBlock_3; } Group group = SWTFactory.createGroup(fControl, fTitle, 1, 1, GridData.FILL_HORIZONTAL); Composite comp = SWTFactory.createComposite(group, group.getFont(), 3, 1, GridData.FILL_BOTH, 0, 0); if (fDefaultFirst) { createDefaultJREControls(comp); } createEEControls(comp); createAlternateJREControls(comp); if (!fDefaultFirst) { createDefaultJREControls(comp); } } private void createEEControls(Composite comp) { fEnvironmentsButton = SWTFactory.createRadioButton(comp, JREMessages.JREsComboBlock_4); fEnvironmentsButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (fEnvironmentsButton.getSelection()) { fCombo.setEnabled(false); if (fEnvironmentsCombo.getText().length() == 0 && !fEnvironments.isEmpty()) { fEnvironmentsCombo.select(0); } fEnvironmentsCombo.setEnabled(true); if (fEnvironments.isEmpty()) { setError(JREMessages.JREsComboBlock_5); } else { setStatus(OK_STATUS); } firePropertyChange(); } } }); fEnvironmentsCombo = SWTFactory.createCombo(comp, SWT.DROP_DOWN | SWT.READ_ONLY, 1, null); fEnvironmentsCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setPath(JavaRuntime.newJREContainerPath(getEnvironment())); firePropertyChange(); } }); fManageEnvironmentsButton = SWTFactory.createPushButton(comp, JREMessages.JREsComboBlock_14, null); fManageEnvironmentsButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { showPrefPage(ExecutionEnvironmentsPreferencePage.ID); } }); fillWithWorkspaceProfiles(); } private void createAlternateJREControls(Composite comp) { String text = JREMessages.JREsComboBlock_1; if (fSpecificDescriptor != null) { text = fSpecificDescriptor.getDescription(); } fSpecificButton = SWTFactory.createRadioButton(comp, text, 1); fSpecificButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (fSpecificButton.getSelection()) { fCombo.setEnabled(true); if (fCombo.getText().length() == 0 && !fVMs.isEmpty()) { fCombo.select(0); } if (fVMs.isEmpty()) { setError(JREMessages.JREsComboBlock_0); } else { setStatus(OK_STATUS); } fEnvironmentsCombo.setEnabled(false); firePropertyChange(); } } }); fCombo = SWTFactory.createCombo(comp, SWT.DROP_DOWN | SWT.READ_ONLY, 1, null); ControlAccessibleListener.addListener(fCombo, fSpecificButton.getText()); fCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setStatus(OK_STATUS); firePropertyChange(); } }); fManageButton = SWTFactory.createPushButton(comp, JREMessages.JREsComboBlock_2, null); fManageButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { showPrefPage(JREsPreferencePage.ID); } }); fillWithWorkspaceJREs(); } private void createDefaultJREControls(Composite comp) { if (fDefaultDescriptor != null) { fDefaultButton = SWTFactory.createRadioButton(comp, fDefaultDescriptor.getDescription(), 3); fDefaultButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (fDefaultButton.getSelection()) { setUseDefaultJRE(); setStatus(OK_STATUS); firePropertyChange(); } } }); } } /** * Opens the given preference page, and updates when closed. * * @param id pref page id * @param page pref page */ private void showPrefPage(String id/*, IPreferencePage page*/) { IVMInstall prevJRE = getJRE(); IExecutionEnvironment prevEnv = getEnvironment(); JDIDebugUIPlugin.showPreferencePage(id); fillWithWorkspaceJREs(); fillWithWorkspaceProfiles(); restoreCombo(fVMs, prevJRE, fCombo); restoreCombo(fEnvironments, prevEnv, fEnvironmentsCombo); // update text setDefaultJREDescriptor(fDefaultDescriptor); if (isDefaultJRE()) { // reset in case default has changed setUseDefaultJRE(); } setPath(getPath()); firePropertyChange(); } private void restoreCombo(List<Object> elements, Object element, Combo combo) { int index = -1; if (element != null) { index = elements.indexOf(element); } if (index >= 0) { combo.select(index); } else { combo.select(0); } } /** * Returns this block's control * * @return control */ public Control getControl() { return fControl; } /** * Sets the JREs to be displayed in this block * * @param vms JREs to be displayed */ protected void setJREs(List<VMStandin> jres) { fVMs.clear(); fVMs.addAll(jres); // sort by name Collections.sort(fVMs, new Comparator<Object>() { @Override public int compare(Object o1, Object o2) { IVMInstall left = (IVMInstall)o1; IVMInstall right = (IVMInstall)o2; return left.getName().compareToIgnoreCase(right.getName()); } @Override public boolean equals(Object obj) { return obj == this; } }); // now make an array of names String[] names = new String[fVMs.size()]; Iterator<Object> iter = fVMs.iterator(); int i = 0; while (iter.hasNext()) { IVMInstall vm = (IVMInstall)iter.next(); names[i] = vm.getName(); i++; } fCombo.setItems(names); fCombo.setVisibleItemCount(Math.min(names.length, 20)); } protected Shell getShell() { return getControl().getShell(); } /** * Selects a specific JRE based on type/name. * * @param vm JRE or <code>null</code> */ private void selectJRE(IVMInstall vm) { fSpecificButton.setSelection(true); fDefaultButton.setSelection(false); fEnvironmentsButton.setSelection(false); fCombo.setEnabled(true); fEnvironmentsCombo.setEnabled(false); if (vm != null) { int index = fVMs.indexOf(vm); if (index >= 0) { fCombo.select(index); } } firePropertyChange(); } /** * Selects a JRE based environment. * * @param env environment or <code>null</code> */ private void selectEnvironment(IExecutionEnvironment env) { fSpecificButton.setSelection(false); fDefaultButton.setSelection(false); fCombo.setEnabled(false); fEnvironmentsButton.setSelection(true); fEnvironmentsCombo.setEnabled(true); if (env != null) { int index = fEnvironments.indexOf(env); if (index >= 0) { fEnvironmentsCombo.select(index); } } firePropertyChange(); } /** * Returns the selected JRE or <code>null</code> if none. * * @return the selected JRE or <code>null</code> if none */ public IVMInstall getJRE() { int index = fCombo.getSelectionIndex(); if (index >= 0) { return (IVMInstall)fVMs.get(index); } return null; } /** * Returns the selected Environment or <code>null</code> if none. * * @return the selected Environment or <code>null</code> if none */ private IExecutionEnvironment getEnvironment() { int index = fEnvironmentsCombo.getSelectionIndex(); if (index >= 0) { return (IExecutionEnvironment)fEnvironments.get(index); } return null; } /** * Populates the JRE table with existing JREs defined in the workspace. */ protected void fillWithWorkspaceJREs() { // fill with JREs List<VMStandin> standins = new ArrayList<>(); IVMInstallType[] types = JavaRuntime.getVMInstallTypes(); for (int i = 0; i < types.length; i++) { IVMInstallType type = types[i]; IVMInstall[] installs = type.getVMInstalls(); for (int j = 0; j < installs.length; j++) { IVMInstall install = installs[j]; standins.add(new VMStandin(install)); } } setJREs(standins); } /** * Populates the JRE profile combo with profiles defined in the workspace. */ protected void fillWithWorkspaceProfiles() { fEnvironments.clear(); IExecutionEnvironment[] environments = JavaRuntime.getExecutionEnvironmentsManager().getExecutionEnvironments(); for (int i = 0; i < environments.length; i++) { fEnvironments.add(environments[i]); } String[] names = new String[fEnvironments.size()]; Iterator<Object> iter = fEnvironments.iterator(); int i = 0; while (iter.hasNext()) { IExecutionEnvironment env = (IExecutionEnvironment)iter.next(); IPath path = JavaRuntime.newJREContainerPath(env); IVMInstall install = JavaRuntime.getVMInstall(path); if (install != null) { names[i] = NLS.bind(JREMessages.JREsComboBlock_15, new String[]{env.getId(), install.getName()}); } else { names[i] = NLS.bind(JREMessages.JREsComboBlock_16, new String[]{env.getId()}); } i++; } fEnvironmentsCombo.setItems(names); fEnvironmentsCombo.setVisibleItemCount(Math.min(names.length, 20)); } /** * Sets the Default JRE Descriptor for this block. * * @param descriptor default JRE descriptor */ public void setDefaultJREDescriptor(JREDescriptor descriptor) { fDefaultDescriptor = descriptor; setButtonTextFromDescriptor(fDefaultButton, descriptor); } private void setButtonTextFromDescriptor(Button button, JREDescriptor descriptor) { if (button != null) { //update the description & JRE in case it has changed String currentText = button.getText(); String newText = descriptor.getDescription(); if (!newText.equals(currentText)) { button.setText(newText); fControl.layout(); } } } /** * Sets the specific JRE Descriptor for this block. * * @param descriptor specific JRE descriptor */ public void setSpecificJREDescriptor(JREDescriptor descriptor) { fSpecificDescriptor = descriptor; setButtonTextFromDescriptor(fSpecificButton, descriptor); } /** * Returns whether the 'use default JRE' button is checked. * * @return whether the 'use default JRE' button is checked */ public boolean isDefaultJRE() { if (fDefaultButton != null) { return fDefaultButton.getSelection(); } return false; } /** * Sets this control to use the 'default' JRE. */ private void setUseDefaultJRE() { if (fDefaultDescriptor != null) { fDefaultButton.setSelection(true); fSpecificButton.setSelection(false); fEnvironmentsButton.setSelection(false); fCombo.setEnabled(false); fEnvironmentsCombo.setEnabled(false); firePropertyChange(); } } /** * Sets the title used for this JRE block * * @param title title for this JRE block */ public void setTitle(String title) { fTitle = title; } /** * Refresh the default JRE description. */ public void refresh() { setDefaultJREDescriptor(fDefaultDescriptor); } /** * Returns a classpath container path identifying the selected JRE. * * @return classpath container path or <code>null</code> * @since 3.2 */ public IPath getPath() { if (!getStatus().isOK() && fErrorPath != null) { return fErrorPath; } if (fEnvironmentsButton.getSelection()) { int index = fEnvironmentsCombo.getSelectionIndex(); if (index >= 0) { IExecutionEnvironment env = (IExecutionEnvironment) fEnvironments.get(index); return JavaRuntime.newJREContainerPath(env); } return null; } if (fSpecificButton.getSelection()) { int index = fCombo.getSelectionIndex(); if (index >= 0) { IVMInstall vm = (IVMInstall) fVMs.get(index); return JavaRuntime.newJREContainerPath(vm); } return null; } return JavaRuntime.newDefaultJREContainerPath(); } /** * Sets the selection based on the given container path and returns * a status indicating if the selection was successful. * * @param containerPath * @return status */ public void setPath(IPath containerPath) { fErrorPath = null; setStatus(OK_STATUS); if (JavaRuntime.newDefaultJREContainerPath().equals(containerPath)) { setUseDefaultJRE(); } else { String envId = JavaRuntime.getExecutionEnvironmentId(containerPath); if (envId != null) { IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); IExecutionEnvironment environment = manager.getEnvironment(envId); if (environment == null) { fErrorPath = containerPath; selectEnvironment(environment); setError(NLS.bind(JREMessages.JREsComboBlock_6, new String[]{envId})); } else { selectEnvironment(environment); IVMInstall[] installs = environment.getCompatibleVMs(); if (installs.length == 0) { setError(NLS.bind(JREMessages.JREsComboBlock_7, new String[]{environment.getId()})); } } } else { IVMInstall install = JavaRuntime.getVMInstall(containerPath); if (install == null) { selectJRE(install); fErrorPath = containerPath; String installTypeId = JavaRuntime.getVMInstallTypeId(containerPath); if (installTypeId == null) { setError(JREMessages.JREsComboBlock_8); } else { IVMInstallType installType = JavaRuntime.getVMInstallType(installTypeId); if (installType == null) { setError(NLS.bind(JREMessages.JREsComboBlock_9, new String[]{installTypeId})); } else { String installName = JavaRuntime.getVMInstallName(containerPath); if (installName == null) { setError(NLS.bind(JREMessages.JREsComboBlock_10, new String[]{installType.getName()})); } else { setError(NLS.bind(JREMessages.JREsComboBlock_11, new String[]{installName, installType.getName()})); } } } } else { selectJRE(install); File location = install.getInstallLocation(); if (location == null) { setError(JREMessages.JREsComboBlock_12); } else if (!location.exists()) { setError(JREMessages.JREsComboBlock_13); } } } } } private void setError(String message) { setStatus(new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, message, null)); } /** * Returns the status of the JRE selection. * * @return status */ public IStatus getStatus() { return fStatus; } private void setStatus(IStatus status) { fStatus = status; } }