blob: 94a6fd918954bd386e6de7729e1d614d7d5f5778 [file] [log] [blame]
/*
* Copyright (c) 2015-2018 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Yatta Solutions - [466264] Enhance UX in simple installer
*/
package org.eclipse.oomph.setup.internal.installer;
import org.eclipse.oomph.base.Annotation;
import org.eclipse.oomph.base.util.BaseUtil;
import org.eclipse.oomph.internal.setup.SetupPrompter;
import org.eclipse.oomph.internal.setup.SetupProperties;
import org.eclipse.oomph.internal.ui.FlatButton;
import org.eclipse.oomph.internal.ui.ImageCheckbox;
import org.eclipse.oomph.internal.ui.ImageHoverButton;
import org.eclipse.oomph.jreinfo.JRE;
import org.eclipse.oomph.jreinfo.JREManager;
import org.eclipse.oomph.jreinfo.ui.JREController;
import org.eclipse.oomph.p2.core.AgentManager;
import org.eclipse.oomph.p2.core.P2Util;
import org.eclipse.oomph.p2.internal.core.DownloadArtifactEvent;
import org.eclipse.oomph.setup.AnnotationConstants;
import org.eclipse.oomph.setup.AttributeRule;
import org.eclipse.oomph.setup.Installation;
import org.eclipse.oomph.setup.LicenseInfo;
import org.eclipse.oomph.setup.Product;
import org.eclipse.oomph.setup.ProductCatalog;
import org.eclipse.oomph.setup.ProductVersion;
import org.eclipse.oomph.setup.Scope;
import org.eclipse.oomph.setup.SetupFactory;
import org.eclipse.oomph.setup.SetupPackage;
import org.eclipse.oomph.setup.SetupTask;
import org.eclipse.oomph.setup.SetupTaskContext;
import org.eclipse.oomph.setup.Trigger;
import org.eclipse.oomph.setup.User;
import org.eclipse.oomph.setup.VariableTask;
import org.eclipse.oomph.setup.internal.core.AbstractSetupTaskContext;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.SetupTaskPerformer;
import org.eclipse.oomph.setup.internal.core.util.SetupCoreUtil;
import org.eclipse.oomph.setup.internal.installer.SimpleInstallLaunchButton.State;
import org.eclipse.oomph.setup.internal.installer.SimpleMessageOverlay.Type;
import org.eclipse.oomph.setup.internal.installer.SimpleProductPage.ProductComposite;
import org.eclipse.oomph.setup.log.ProgressLog;
import org.eclipse.oomph.setup.ui.EnablementComposite;
import org.eclipse.oomph.setup.ui.EnablementDialog;
import org.eclipse.oomph.setup.ui.JREDownloadHandler;
import org.eclipse.oomph.setup.ui.LicensePrePrompter;
import org.eclipse.oomph.setup.ui.SetupUIPlugin;
import org.eclipse.oomph.setup.ui.UnsignedContentDialog;
import org.eclipse.oomph.setup.ui.wizards.ProgressPage;
import org.eclipse.oomph.setup.ui.wizards.SetupWizard.SelectionMemento;
import org.eclipse.oomph.ui.StackComposite;
import org.eclipse.oomph.ui.UIUtil;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.OS;
import org.eclipse.oomph.util.ObjectUtil;
import org.eclipse.oomph.util.OomphPlugin.Preference;
import org.eclipse.oomph.util.Pair;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.oomph.util.UserCallback;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.equinox.p2.core.UIServices;
import org.eclipse.equinox.p2.metadata.ILicense;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
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.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import java.io.File;
import java.io.FileFilter;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
public class SimpleVariablePage extends SimpleInstallerPage
{
public static final int PROGRESS_WATCHDOG_TIMEOUT = PropertiesUtil.getProperty("oomph.progress.watchdog.timeout", 15);
private static final boolean EXIT_AFTER_LAUNCH = !PropertiesUtil.isProperty("oomph.no.exit.after.launch");
private static final Preference PREF_STARTMENU_ENTRY = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("startMenuEntry");
private static final Preference PREF_DESKTOP_SHORTCUT = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("desktopShortcut");
private static final String PROP_INSTALL_ROOT = "oomph.setup.install.root";
private static final Preference PREF_INSTALL_ROOT = SetupInstallerPlugin.INSTANCE.getConfigurationPreference("installRoot");
private static final File FILE_INSTALL_ROOT = new File(SetupInstallerPlugin.INSTANCE.getUserLocation().toFile(), PREF_INSTALL_ROOT.key() + ".txt");
private static final String TEXT_README = "show readme file";
private static final String TEXT_SYSTEM_EXPLOROR = "open in system explorer";
private static final String TEXT_KEEP = "keep installer";
private static final String MESSAGE_SUCCESS = "Installation completed successfully.";
private static final String MESSAGE_FAILURE = "Installation failed with an error.";
protected static final boolean JRE_CHOICE = Boolean.valueOf(PropertiesUtil.getProperty(SetupProperties.PROP_SETUP_JRE_CHOICE, "true"));
protected static final boolean INSTALL_CHOICE = Boolean.valueOf(PropertiesUtil.getProperty(SetupProperties.PROP_SETUP_INSTALL_CHOICE, "true"));
private final SelectionMemento selectionMemento;
private final Map<String, ProductVersion> productVersions = new HashMap<String, ProductVersion>();
private Product product;
private ProductVersion selectedProductVersion;
private String readmePath;
private Browser detailBrowser;
private ProductComposite detailComposite;
private CCombo versionCombo;
private Label versionLabel;
private Control versionSpacer;
private ImageCheckbox bitness32Button;
private ImageCheckbox bitness64Button;
private Label bitness32Spacer;
private Label bitness64Spacer;
private JREController javaController;
private Label javaLabel;
private Control javaSpacer;
private ComboViewer javaViewer;
private FlatButton javaButton;
private Control javaTrailingSpacer;
private Text folderText;
private Label installFolderLabel;
private Control installFolderSpacer;
private FlatButton folderButton;
private String installRoot;
private String installFolder;
private Thread installThread;
private StackComposite installStack;
private SimpleInstallLaunchButton installButton;
private String installError;
private boolean installed;
private SetupTaskPerformer performer;
private SimpleProgress progress;
private FlatButton cancelButton;
private FlatButton openInSystemExplorerButton;
private FlatButton showReadmeButton;
private FlatButton keepInstallerButton;
private Composite afterInstallComposite;
private Composite errorComposite;
private Composite container;
private SimpleCheckbox createStartMenuEntryButton;
private SimpleCheckbox createDesktopShortcutButton;
private Runnable preInstallAction;
public SimpleVariablePage(final Composite parent, final SimpleInstallerDialog dialog, SelectionMemento selectionMemento)
{
super(parent, dialog, true);
this.selectionMemento = selectionMemento;
}
@Override
protected void createContent(Composite container)
{
this.container = container;
container.setBackgroundMode(SWT.INHERIT_FORCE);
container.setBackground(AbstractSimpleDialog.COLOR_WHITE);
String powerShell = KeepInstallerUtil.getPowerShell();
// Row 1
GridData browserLayoutData = GridDataFactory.fillDefaults().indent(0, 13).grab(true, false).create();
Point defaultSize = SimpleInstallerDialog.getDefaultSize(container);
browserLayoutData.heightHint = defaultSize.y * (powerShell != null ? 20 : 34) / 100;
Composite detailArea = new Composite(container, SWT.NONE);
detailArea.setLayoutData(browserLayoutData);
detailArea.setLayout(new FillLayout());
if (UIUtil.isBrowserAvailable())
{
detailBrowser = new Browser(detailArea, SWT.NO_SCROLL);
detailBrowser.addLocationListener(new LocationAdapter()
{
@Override
public void changing(LocationEvent event)
{
String url = event.location;
if (!"about:blank".equals(url))
{
OS.INSTANCE.openSystemBrowser(url);
event.doit = false;
}
}
});
}
else
{
detailBrowser = null;
detailComposite = new ProductComposite(detailArea, null, null);
}
Composite variablesComposite = new Composite(container, SWT.NONE);
GridLayout variablesLayout = new GridLayout(5, false);
variablesLayout.horizontalSpacing = 8;
variablesLayout.verticalSpacing = 3;
variablesLayout.marginLeft = 14;
variablesLayout.marginRight = 30;
variablesLayout.marginTop = 24;
variablesLayout.marginBottom = 0;
variablesLayout.marginHeight = 0;
variablesComposite.setLayout(variablesLayout);
variablesComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
// Row 3
versionLabel = createLabel(variablesComposite, "Product Version");
// Spacer to get a little bit more distance between labels and input fields
versionSpacer = spacer(variablesComposite);
versionSpacer.setLayoutData(GridDataFactory.swtDefaults().hint(17, SWT.DEFAULT).create());
versionCombo = createComboBox(variablesComposite, SWT.READ_ONLY);
versionCombo.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
String label = versionCombo.getItem(versionCombo.getSelectionIndex());
productVersionSelected(productVersions.get(label));
UIUtil.clearTextSelection(versionCombo);
}
});
if (JREManager.BITNESS_CHANGEABLE)
{
bitness32Button = new ImageCheckbox(variablesComposite, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/32bit_enabled.png"),
SetupInstallerPlugin.INSTANCE.getSWTImage("simple/32bit_selected.png"));
bitness32Button.setDisabledImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/32bit_disabled.png"));
bitness32Button.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).indent(4, 0).hint(SWT.DEFAULT, 30).create());
bitness32Button.setChecked(false);
bitness32Button.setVisible(JREManager.BITNESS_CHANGEABLE);
bitness32Button.setToolTipText("Create a 32 bit installation");
bitness32Button.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
bitness32Button.setChecked(true);
bitness64Button.setChecked(false);
javaController.setBitness(32);
}
});
bitness64Button = new ImageCheckbox(variablesComposite, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/64bit_enabled.png"),
SetupInstallerPlugin.INSTANCE.getSWTImage("simple/64bit_selected.png"));
bitness64Button.setDisabledImage(SetupInstallerPlugin.INSTANCE.getSWTImage("simple/64bit_disabled.png"));
bitness64Button.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).hint(SWT.DEFAULT, 30).create());
bitness64Button.setChecked(true);
bitness64Button.setVisible(JREManager.BITNESS_CHANGEABLE);
bitness64Button.setToolTipText("Create a 64 bit installation");
bitness64Button.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
bitness32Button.setChecked(false);
bitness64Button.setChecked(true);
javaController.setBitness(64);
}
});
}
else
{
bitness32Spacer = new Label(variablesComposite, SWT.NONE);
bitness64Spacer = new Label(variablesComposite, SWT.NONE);
}
// Row 4
javaLabel = createLabel(variablesComposite, "Java VM");
javaSpacer = spacer(variablesComposite);
final CCombo javaCombo = createComboBox(variablesComposite, SWT.READ_ONLY);
javaViewer = new ComboViewer(javaCombo);
javaViewer.setContentProvider(new ArrayContentProvider());
javaViewer.setLabelProvider(new LabelProvider());
javaViewer.addPostSelectionChangedListener(new ISelectionChangedListener()
{
public void selectionChanged(SelectionChangedEvent event)
{
UIUtil.setSelectionToEnd(javaCombo);
}
});
javaButton = new ImageHoverButton(variablesComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"),
SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_hover.png"), SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_disabled.png"));
javaButton.setLayoutData(GridDataFactory.swtDefaults().indent(4, 0).create());
javaButton.setToolTipText("Select a Java VM");
javaButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
javaController.configureJREs();
}
});
JREDownloadHandler downloadHandler = new JREDownloadHandler()
{
@Override
protected Product getProduct()
{
return product;
}
};
javaController = new JREController(javaLabel, javaViewer, downloadHandler)
{
@Override
protected void modelEmpty(boolean empty)
{
super.modelEmpty(empty);
validatePage();
}
@Override
protected void jreChanged(JRE jre)
{
super.jreChanged(jre);
validatePage();
javaCombo.setToolTipText(jre == null ? null : jre.getJavaHome().toString());
}
@Override
protected void setLabel(String text)
{
super.setLabel(text + " ");
}
};
javaTrailingSpacer = spacer(variablesComposite);
// Row 5
installFolderLabel = createLabel(variablesComposite, "Installation Folder");
installFolderSpacer = spacer(variablesComposite);
folderText = createTextField(variablesComposite);
folderText.addModifyListener(new ModifyListener()
{
/**
* A class for delayed validation of the install folder.
* @author Ed Merks
*/
class Validator extends UIUtil.DelayedRunnable
{
public Validator()
{
super(folderText, 350);
}
@Override
protected void perform()
{
validatePage();
}
@Override
protected void prepareForDispatch()
{
// Immediately disable the install button if the install folder is currently invalid.
if (validateInstallFolder() != null)
{
installButton.setEnabled(false);
}
}
}
private final Validator validator = new Validator();
public void modifyText(ModifyEvent e)
{
final FocusSelectionAdapter focusSelectionAdapter = (FocusSelectionAdapter)folderText.getData(FocusSelectionAdapter.ADAPTER_KEY);
focusSelectionAdapter.setNextSelectionRange(folderText.getSelection());
String folder = folderText.getText().trim();
installFolder = folder;
folderText.setToolTipText(installFolder);
validator.schedule();
String productInstallFolder = getProductInstallFolder().toString();
installFolderLabel.setToolTipText("Install into the folder '" + productInstallFolder + "'");
installButton.setToolTipText("Start installing the product into '" + productInstallFolder + "'");
}
});
new FileCompletionSupport(folderText)
{
@Override
protected void setFolderTextAndSelect(String folder)
{
SimpleVariablePage.this.setFolderText(folder);
}
};
folderButton = new ImageHoverButton(variablesComposite, SWT.PUSH, SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder.png"),
SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_hover.png"), SetupInstallerPlugin.INSTANCE.getSWTImage("simple/folder_disabled.png"));
folderButton.setLayoutData(GridDataFactory.swtDefaults().indent(4, 0).create());
folderButton.setToolTipText("Select an installation folder");
folderButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
DirectoryDialog dialog = new DirectoryDialog(getShell());
dialog.setText(PropertiesUtil.getProductName());
dialog.setMessage("Select an installation folder:");
if (!StringUtil.isEmpty(installFolder))
{
try
{
File existingFolder = IOUtil.getExistingFolder(getEffectiveInstallFolder());
if (existingFolder != null)
{
dialog.setFilterPath(existingFolder.getAbsolutePath());
}
}
catch (Exception ex)
{
SetupInstallerPlugin.INSTANCE.log(ex, IStatus.WARNING);
}
}
String dir = dialog.open();
if (dir != null)
{
validatePage();
setFolderText(dir);
UIUtil.getDisplay().asyncExec(new Runnable()
{
public void run()
{
folderText.setFocus();
}
});
}
}
});
// Pin rows
if (powerShell != null)
{
{
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
}
{
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
createStartMenuEntryButton = createCheckbox(variablesComposite, "create start menu entry");
createStartMenuEntryButton.setChecked(PREF_STARTMENU_ENTRY.get(true));
createStartMenuEntryButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
PREF_STARTMENU_ENTRY.set(createStartMenuEntryButton.isChecked());
}
});
spacer(variablesComposite);
}
{
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
createDesktopShortcutButton = createCheckbox(variablesComposite, "create desktop shortcut");
createDesktopShortcutButton.setChecked(PREF_DESKTOP_SHORTCUT.get(true));
createDesktopShortcutButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
PREF_DESKTOP_SHORTCUT.set(createDesktopShortcutButton.isChecked());
}
});
spacer(variablesComposite);
}
}
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
installButton = new SimpleInstallLaunchButton(variablesComposite);
Point defaultInstallButtonSize = installButton.computeSize(SWT.DEFAULT, SWT.DEFAULT);
installButton
.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING).hint(SWT.DEFAULT, defaultInstallButtonSize.y + 3).indent(0, 32).create());
installButton.setCurrentState(SimpleInstallLaunchButton.State.INSTALL);
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
spacer(variablesComposite);
installStack = new StackComposite(variablesComposite, SWT.NONE);
installStack.setLayoutData(GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 72).create());
final Composite duringInstallContainer = new Composite(installStack, SWT.NONE);
duringInstallContainer.setLayout(UIUtil.createGridLayout(1));
// During installation.
cancelButton = createButton(duringInstallContainer, "Cancel Installation", "Cancel", SetupInstallerPlugin.INSTANCE.getSWTImage("simple/delete.png"));
cancelButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
if (progress != null)
{
progress.setCanceled(true);
}
}
});
GridLayout afterInstallLayout = UIUtil.createGridLayout(1);
afterInstallLayout.verticalSpacing = 3;
afterInstallComposite = new Composite(installStack, SWT.NONE);
afterInstallComposite.setLayout(afterInstallLayout);
showReadmeButton = createButton(afterInstallComposite, TEXT_README, null, null);
showReadmeButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
if (readmePath != null)
{
String relativeProductFolder = performer.getRelativeProductFolder();
java.net.URI readmeURI = new File(installFolder, relativeProductFolder + "/" + readmePath).toURI();
dialog.showReadme(readmeURI);
}
}
});
showReadmeButton.setToolTipText("Show the readme file of the installed product");
openInSystemExplorerButton = createButton(afterInstallComposite, TEXT_SYSTEM_EXPLOROR, null, null);
openInSystemExplorerButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
String launchLocation = getProductInstallFolder().toURI().toString();
OS.INSTANCE.openSystemBrowser(launchLocation);
}
});
keepInstallerButton = createButton(afterInstallComposite, TEXT_KEEP, null, null);
keepInstallerButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
dialog.showKeepInstaller();
}
});
keepInstallerButton.setToolTipText(KeepInstallerUtil.KEEP_INSTALLER_DESCRIPTION);
installButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
if (installed)
{
launchProduct();
}
else
{
if (preInstallAction != null)
{
preInstallAction.run();
}
dialog.clearMessage();
dialog.setButtonsEnabled(false);
setEnabled(false);
installButton.setCurrentState(State.INSTALLING);
installButton.setProgress(0);
installStack.setTopControl(duringInstallContainer);
installStack.setVisible(true);
layout(true, true);
forceFocus();
install();
}
}
});
GridLayout errorLayout = UIUtil.createGridLayout(1);
errorLayout.verticalSpacing = 0;
errorComposite = new Composite(installStack, SWT.NONE);
errorComposite.setLayout(errorLayout);
// Exclude browser from focus traversal because otherwise we get
// a strange traversal behavior
List<Control> tabList = new ArrayList<Control>(Arrays.asList(container.getTabList()));
tabList.remove(detailArea);
container.setTabList(tabList.toArray(new Control[tabList.size()]));
// Just for debugging
// installStack.setVisible(true);
// installStack.setTopControl(errorComposite);
// installButton.setProgress(0.98f);
// installButton.setCurrentState(InstallLaunchButton.State.INSTALLING);
// installButton.setEnabled(false);
}
@Override
protected void backSelected()
{
if (promptLaunchProduct("You're about to install another product"))
{
super.backSelected();
}
}
public boolean promptLaunchProduct(String currentAction)
{
if (installButton != null && installButton.getCurrentState() == State.LAUNCH)
{
MessageDialog prompt = new MessageDialog(dialog, "Launch Product", null,
currentAction + " without having launched the installed product.\n\nDo you want to launch it now?", MessageDialog.QUESTION,
new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
int result = prompt.open();
if (result == 0)
{
// Yes: Do launch; then continue.
launchProduct(false);
return true;
}
if (result == 1)
{
// No: Do not launch; but continue.
return true;
}
// Cancel: Do not launch; and do not continue.
return false;
}
// Nothing to launch; just continue.
return true;
}
private FlatButton createButton(Composite parent, String text, String toolTip, Image icon)
{
FlatButton button = new FlatButton(parent, SWT.PUSH);
button.setBackground(AbstractSimpleDialog.COLOR_LIGHTEST_GRAY);
button.setFocusForegroundColor(AbstractSimpleDialog.COLOR_LIGHTEST_GRAY);
button.setText(text);
button.setCornerWidth(10);
button.setAlignment(SWT.CENTER);
button.setFont(SimpleInstallerDialog.getFont(1, "normal"));
button.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 22).create());
button.setForeground(AbstractSimpleDialog.COLOR_LABEL_FOREGROUND);
if (icon != null)
{
button.setImage(icon);
}
if (toolTip != null)
{
button.setToolTipText(toolTip);
}
return button;
}
protected void productVersionSelected(ProductVersion productVersion)
{
if (selectedProductVersion != productVersion)
{
selectedProductVersion = productVersion;
String requiredJavaVersion = productVersion.getRequiredJavaVersion();
javaController.setJavaVersion(requiredJavaVersion);
ProductPage.saveProductVersionSelection(installer.getCatalogManager(), selectedProductVersion);
String toolTipText = ProductPage.getToolTipText(selectedProductVersion);
versionLabel.setToolTipText(toolTipText);
versionCombo.setToolTipText(toolTipText);
}
setFolderText(getDefaultInstallationFolder());
}
@Override
public boolean setFocus()
{
boolean focused = folderText.setFocus();
// Set the cursor to the end
UIUtil.setSelectionToEnd(folderText);
return focused;
}
public void setProduct(Product product)
{
ProductVersion defaultProductVersion = ProductPage.getDefaultProductVersion(installer.getCatalogManager(), product);
List<ProductVersion> validProductVersions = ProductPage.getValidProductVersions(product, PRODUCT_VERSION_FILTER);
for (ProductVersion productVersion : validProductVersions)
{
if (defaultProductVersion == null || !validProductVersions.contains(defaultProductVersion))
{
defaultProductVersion = productVersion;
}
}
if (defaultProductVersion != null)
{
setProductVersion(defaultProductVersion);
}
}
public void setProductVersion(ProductVersion productVersion)
{
product = productVersion.getProduct();
if (detailBrowser != null)
{
String html = SimpleInstallerDialog.getPageTemplate();
html = html.replace("%CONTENT%", SimpleProductPage.renderProduct(product, true));
detailBrowser.setText(html, true);
}
else
{
detailComposite.setProduct(product);
}
productVersions.clear();
versionCombo.removeAll();
int i = 0;
int selection = -1;
List<ProductVersion> validProductVersions = ProductPage.getValidProductVersions(product, PRODUCT_VERSION_FILTER);
for (ProductVersion version : validProductVersions)
{
String label = SetupCoreUtil.getLabel(version);
productVersions.put(label, version);
versionCombo.add(label);
if (version == productVersion)
{
selection = i;
}
++i;
}
// If the product version is filtered out of the valid available versions, add a choice for it anyway.
if (selection == -1)
{
String label = SetupCoreUtil.getLabel(productVersion);
productVersions.put(label, productVersion);
versionCombo.add(label);
selection = i;
}
versionCombo.select(selection);
productVersionSelected(productVersion);
UIUtil.setSelectionToEnd(versionCombo);
installButton.setCurrentState(State.INSTALL);
installStack.setVisible(false);
installed = false;
UIUtil.asyncExec(container, new Runnable()
{
public void run()
{
setEnabled(true);
}
});
}
private void setVisible(Control control, boolean visible)
{
if (control != null)
{
control.setVisible(visible);
Object layoutData = control.getLayoutData();
if (layoutData == null)
{
layoutData = new GridData();
control.setLayoutData(layoutData);
}
if (layoutData instanceof GridData)
{
GridData gridData = (GridData)layoutData;
gridData.exclude = !visible;
}
}
}
@Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
versionCombo.setEnabled(enabled);
javaViewer.getControl().setEnabled(enabled);
javaButton.setEnabled(enabled);
folderText.setEnabled(enabled);
folderButton.setEnabled(enabled);
if (createStartMenuEntryButton != null)
{
createStartMenuEntryButton.setEnabled(enabled);
}
if (createDesktopShortcutButton != null)
{
createDesktopShortcutButton.setEnabled(enabled);
}
boolean versionVisible = versionCombo.getItemCount() != 1;
setVisible(versionLabel, versionVisible);
setVisible(versionSpacer, versionVisible);
setVisible(versionCombo.getParent(), versionVisible);
setVisible(bitness32Button, versionVisible);
setVisible(bitness32Spacer, versionVisible);
setVisible(bitness64Button, versionVisible);
setVisible(bitness64Spacer, versionVisible);
if (JREManager.BITNESS_CHANGEABLE)
{
int bitness = javaController.getBitness();
bitness32Button.setEnabled(enabled);
bitness32Button.setVisible(versionVisible && (enabled || bitness == 32));
bitness64Button.setEnabled(enabled);
bitness64Button.setVisible(versionVisible && (enabled || bitness == 64));
}
IStructuredSelection structuredSelection = (IStructuredSelection)javaViewer.getSelection();
boolean javaVisible = JRE_CHOICE || !(structuredSelection.getFirstElement() instanceof JRE);
setVisible(javaLabel, javaVisible);
setVisible(javaSpacer, javaVisible);
setVisible(javaViewer.getCCombo().getParent(), javaVisible);
setVisible(javaButton, javaVisible);
setVisible(javaTrailingSpacer, javaVisible);
setVisible(folderText, INSTALL_CHOICE);
setVisible(folderText.getParent(), INSTALL_CHOICE);
setVisible(installFolderLabel, INSTALL_CHOICE);
setVisible(installFolderSpacer, INSTALL_CHOICE);
setVisible(folderButton, INSTALL_CHOICE);
container.layout(true, true);
}
public boolean refreshJREs()
{
if (javaController != null)
{
javaController.refresh();
return true;
}
return false;
}
private File getProductInstallFolder()
{
String productFolderName = SetupTaskPerformer.getProductFolderName(selectedProductVersion, OS.INSTANCE);
String relativeProductFolderName = OS.INSTANCE.getRelativeProductFolder(productFolderName);
File result = new File(getEffectiveInstallFolder(), relativeProductFolderName);
return result;
}
private String getDefaultInstallationFolder()
{
String name = product.getName();
int lastDot = name.lastIndexOf('.');
if (lastDot != -1)
{
name = name.substring(lastDot + 1);
}
name += "-" + selectedProductVersion.getName().replace('.', '-');
if (installRoot == null)
{
installRoot = PropertiesUtil.getProperty(PROP_INSTALL_ROOT);
}
if (installRoot == null)
{
if (FILE_INSTALL_ROOT.isFile())
{
List<String> lines = IOUtil.readLines(FILE_INSTALL_ROOT, "UTF-8");
if (lines != null && !lines.isEmpty())
{
installRoot = lines.get(0);
if (installRoot.length() == 0)
{
installRoot = null;
}
}
}
if (installRoot == null)
{
// Default to ${user.home}/eclipse, unless there is a file at that location, or what looks like an existing Eclipse installation.
// In that case default just to ${user.home}.
String defaultValue = PropertiesUtil.getUserHome();
File defaultInstallRoot = new File(defaultValue, "eclipse");
if (!defaultInstallRoot.exists())
{
defaultValue = defaultInstallRoot.toString();
}
else if (defaultInstallRoot.isDirectory())
{
boolean isEclipseInstallation = false;
for (File file : defaultInstallRoot.listFiles())
{
if ("eclipse.ini".equals(file.getName()))
{
isEclipseInstallation = true;
}
}
if (!isEclipseInstallation)
{
defaultValue = defaultInstallRoot.toString();
}
}
installRoot = PREF_INSTALL_ROOT.get(defaultValue);
}
}
for (int i = 1; i < 1000; i++)
{
String filename = name;
if (i != 1)
{
filename += i;
}
File folder = new File(installRoot, filename);
if (!folder.exists())
{
return folder.getAbsolutePath();
}
}
throw new IllegalStateException("User home is full");
}
private void install()
{
installThread = new Thread()
{
@Override
public void run()
{
installError = null;
performer = null;
progress = new SimpleProgress();
try
{
installPerform();
}
catch (InterruptedException ex)
{
installCancel();
}
catch (OperationCanceledException ex)
{
installCancel();
}
catch (UnloggedException ex)
{
installError = ex.getMessage();
}
catch (Exception ex)
{
if (!progress.isCanceled())
{
SetupInstallerPlugin.INSTANCE.log(ex);
installError = MESSAGE_FAILURE;
}
}
finally
{
if (performer != null)
{
IOUtil.close(performer.getLogStream());
}
if (!progress.isCanceled())
{
UIUtil.syncExec(new Runnable()
{
public void run()
{
try
{
installFinished();
}
catch (SWTException ex)
{
//$FALL-THROUGH$
}
}
});
}
}
}
};
installThread.setDaemon(true);
installThread.start();
}
@SuppressWarnings("restriction")
private void installPerform() throws Exception
{
if (dialog.getPool() != null)
{
P2Util.getAgentManager().setDefaultBundlePool(SetupUIPlugin.INSTANCE.getSymbolicName(), dialog.getPool());
System.setProperty(AgentManager.PROP_BUNDLE_POOL_LOCATION, dialog.getPool().getLocation().getAbsolutePath());
}
else
{
System.setProperty(AgentManager.PROP_BUNDLE_POOL_LOCATION, AgentManager.BUNDLE_POOL_LOCATION_NONE);
}
JRE jre = javaController.getJRE();
String vmPath = ProductPage.getVMOption(jre);
ResourceSet resourceSet = installer.getResourceSet();
URIConverter uriConverter = resourceSet.getURIConverter();
SetupContext setupContext = SetupContext.create(resourceSet, selectedProductVersion);
Installation installation = setupContext.getInstallation();
final User user = setupContext.getUser();
if (UIUtil.isBrowserAvailable())
{
UIUtil.syncExec(new Runnable()
{
public void run()
{
EList<LicenseInfo> licenses = LicensePrePrompter.execute(getShell(), user);
if (licenses != null)
{
user.getAcceptedLicenses().addAll(licenses);
BaseUtil.saveEObject(user);
}
}
});
}
UserAdjuster userAdjuster = new UserAdjuster();
userAdjuster.adjust(user, installFolder);
try
{
IOUtil.writeLines(FILE_INSTALL_ROOT, "UTF-8", Collections.singletonList(installRoot));
SimplePrompter prompter = new SimplePrompter(OS.INSTANCE.getForBitness(javaController.getBitness()), vmPath);
performer = SetupTaskPerformer.create(uriConverter, prompter, Trigger.BOOTSTRAP, setupContext, false);
final List<VariableTask> unresolvedVariables = prompter.getUnresolvedVariables();
if (!unresolvedVariables.isEmpty())
{
StringBuilder builder = new StringBuilder();
for (VariableTask variable : unresolvedVariables)
{
if (builder.length() != 0)
{
builder.append(", ");
}
builder.append(variable.getName());
}
final String variables = builder.toString();
final boolean multi = unresolvedVariables.size() > 1;
UIUtil.syncExec(new Runnable()
{
public void run()
{
String message = "The variable" + (multi ? "s" : "") + " " + variables + " " + (multi ? "are" : "is") + " undefined. You likely declared "
+ (multi ? "them" : "it")
+ " in your 'user.setup' file without appropriate restrictions, so that you can't use the installer's simple mode anymore."
+ "\n\nDo you want to switch to the advanced mode now?";
if (MessageDialog.openQuestion(getShell(), "Undefined Variables", message))
{
unresolvedVariables.clear();
dialog.switchToAdvancedMode();
}
}
});
if (unresolvedVariables.isEmpty())
{
return;
}
throw new UnloggedException("Undefined variable" + (multi ? "s" : "") + ": " + variables);
}
EList<SetupTask> triggeredSetupTasks = performer.getTriggeredSetupTasks();
final Map<EClass, EList<SetupTask>> enablementTasks = EnablementComposite.getEnablementTasks(triggeredSetupTasks);
if (!enablementTasks.isEmpty())
{
UIUtil.syncExec(
getDisplay(), new Runnable()
{
public void run()
{
EnablementDialog enablementDialog = new EnablementDialog(getShell(), "the installer", enablementTasks);
if (enablementDialog.open() == EnablementDialog.OK)
{
selectionMemento.setProductVersion(EcoreUtil.getURI(selectedProductVersion));
dialog.restart();
}
installCancel();
}
});
throw new OperationCanceledException();
}
performer.getUnresolvedVariables().clear();
performer.put(UIServices.class, Installer.SERVICE_UI);
if (performer.get(ILicense.class) == null)
{
performer.put(ILicense.class, ProgressPage.LICENSE_CONFIRMER);
}
if (performer.get(Certificate.class) == null)
{
performer.put(Certificate.class, UnsignedContentDialog.createUnsignedContentConfirmer(user, false));
}
performer.setOffline(PropertiesUtil.isProperty(SetupProperties.PROP_SETUP_OFFLINE_STARTUP));
performer.setMirrors(true);
performer.setProgress(progress);
performer.log("Executing " + performer.getTrigger().toString().toLowerCase() + " tasks");
performer.put(org.eclipse.equinox.internal.provisional.p2.core.eventbus.ProvisioningListener.class, new DownloadArtifactLister());
performer.perform(progress);
performer.recordVariables(installation, null, user);
performer.savePasswords();
File configurationLocation = performer.getProductConfigurationLocation();
installation.eResource().setURI(URI.createFileURI(new File(configurationLocation, "org.eclipse.oomph.setup/installation.setup").toString()));
BaseUtil.saveEObject(installation);
}
finally
{
userAdjuster.undo();
BaseUtil.saveEObject(user);
}
}
private void installCancel()
{
if (progress != null)
{
progress.setCanceled(true);
}
if (installThread != null)
{
installThread.interrupt();
}
UIUtil.syncExec(getDisplay(), new Runnable()
{
public void run()
{
dialog.setButtonsEnabled(true);
setEnabled(true);
installButton.setCurrentState(State.INSTALL);
installStack.setVisible(false);
dialog.clearMessage();
}
});
}
private void installFinished()
{
readmePath = null;
boolean success = installError == null;
if (performer != null)
{
Installation installation = performer.getInstallation();
if (installation != null)
{
ProductVersion productVersion = installation.getProductVersion();
if (productVersion != null)
{
OS os = performer.getOS();
SetupCoreUtil.sendStats(success, productVersion, os);
}
}
}
if (success)
{
installed = true;
installButton.setCurrentState(State.LAUNCH);
File launchLocation = performer.getExecutableInfo().getLaunchLocation();
installButton.setToolTipText("Launch '" + launchLocation + "'");
keepInstallerButton.setVisible(KeepInstallerUtil.canKeepInstaller());
Scope scope = selectedProductVersion;
while (scope != null)
{
Annotation annotation = scope.getAnnotation(AnnotationConstants.ANNOTATION_BRANDING_INFO);
if (annotation != null)
{
readmePath = annotation.getDetails().get(AnnotationConstants.KEY_README_PATH);
if (readmePath != null)
{
break;
}
}
scope = scope.getParentScope();
}
showReadmeButton.setVisible(readmePath != null);
openInSystemExplorerButton.setToolTipText("Open the folder '" + getProductInstallFolder() + "' in the system explorer");
if (createStartMenuEntryButton != null || createDesktopShortcutButton != null)
{
File executable = performer.getExecutableInfo().getExecutable();
// TODO The entire naming of the shortcuts should be revisited and made more flexible with BrandingInfo annotations.
ProductCatalog productCatalog = product.getProductCatalog();
String catalogName = "user.products".equals(productCatalog.getName()) ? "" : productCatalog.getLabel();
int firstDot = catalogName.indexOf('.');
if (firstDot != -1)
{
catalogName = catalogName.substring(0, firstDot);
}
String productName = product.getName();
if (productName.startsWith("epp.package."))
{
productName = productName.substring("epp.package.".length());
}
productName = productName.replace('.', ' ');
String qualifiedProductName = productName + " " + selectedProductVersion.getName().replace('.', ' ');
String shortCutName = StringUtil.capAll(StringUtil.isEmpty(catalogName) ? qualifiedProductName : catalogName + " " + qualifiedProductName);
if (createStartMenuEntryButton != null && createStartMenuEntryButton.isChecked())
{
KeepInstallerUtil.createShortCut("Programs", "Eclipse", executable.getAbsolutePath(), shortCutName);
}
if (createDesktopShortcutButton != null && createDesktopShortcutButton.isChecked())
{
KeepInstallerUtil.createShortCut("Desktop", null, executable.getAbsolutePath(), shortCutName);
}
}
showSuccessMessage();
backButton.setEnabled(true);
}
else
{
setEnabled(true);
installButton.setCurrentState(State.INSTALL);
showErrorMessage();
}
dialog.setButtonsEnabled(true);
}
private void showSuccessMessage()
{
dialog.showMessage(MESSAGE_SUCCESS, Type.SUCCESS, true);
installStack.setTopControl(afterInstallComposite);
installStack.setVisible(true);
layout(true, true);
}
private void showErrorMessage()
{
Runnable action = null;
String errorMessage = installError;
if (isInstallLogAvailable())
{
action = new SimpleMessageOverlay.RunnableWithLabel()
{
public void run()
{
openInstallLog();
}
public String getLabel()
{
return "Show log.";
}
};
}
dialog.showMessage(errorMessage, Type.ERROR, false, action);
installStack.setTopControl(errorComposite);
installStack.setVisible(true);
layout(true, true);
}
private void launchProduct()
{
launchProduct(EXIT_AFTER_LAUNCH);
}
private void launchProduct(boolean exitAfterLaunch)
{
try
{
ProgressPage.launchProduct(performer);
}
catch (Exception ex)
{
SetupInstallerPlugin.INSTANCE.log(ex);
}
if (exitAfterLaunch)
{
dialog.exitQuiet();
}
}
private void setFolderText(String folder)
{
folderText.setText(folder);
final FocusSelectionAdapter focusSelectionAdapter = (FocusSelectionAdapter)folderText.getData(FocusSelectionAdapter.ADAPTER_KEY);
int index = folder.length();
Point selection = new Point(index, index);
folderText.setSelection(selection);
focusSelectionAdapter.setNextSelectionRange(selection);
}
private void validatePage()
{
preInstallAction = null;
String errorMessage = null;
Type type = Type.ERROR;
errorMessage = validateJREs();
if (errorMessage == null && installFolder != null)
{
Pair<Type, String> folderValidationMesage = validateInstallFolder();
if (folderValidationMesage != null)
{
type = folderValidationMesage.getElement1();
errorMessage = folderValidationMesage.getElement2();
}
if (errorMessage == null || type == Type.WARNING)
{
OS os = OS.INSTANCE.getForBitness(javaController.getBitness());
String productFolderName = AbstractSetupTaskContext.getProductFolderName(selectedProductVersion, os);
String relativeProductFolderName = OS.INSTANCE.getRelativeProductFolder(productFolderName);
folderValidationMesage = validateChildFolder(IOUtil.getCanonicalFile(getEffectiveInstallFolder()), relativeProductFolderName, true);
if (folderValidationMesage != null)
{
type = folderValidationMesage.getElement1();
errorMessage = folderValidationMesage.getElement2();
}
}
}
if (isTop())
{
if (errorMessage != null)
{
if (type == Type.WARNING)
{
preInstallAction = new Runnable()
{
public void run()
{
preInstallAction = null;
String folder = IOUtil.getCanonicalFile(getEffectiveInstallFolder()).toString();
setFolderText(folder);
}
};
}
dialog.showMessage(errorMessage, type, false, preInstallAction, preInstallAction);
}
else
{
dialog.clearMessage();
}
}
installButton.setEnabled(errorMessage == null || type == Type.WARNING);
}
@Override
public void aboutToShow()
{
super.aboutToShow();
keepInstallerButton.setVisible(KeepInstallerUtil.canKeepInstaller());
if (!installed)
{
validatePage();
}
}
private String validateJREs()
{
Object input = javaViewer.getInput();
if (input instanceof Collection<?> && ((Collection<?>)input).isEmpty())
{
return "Configuration of JRE failed.";
}
return null;
}
private File getEffectiveInstallFolder()
{
File folder = new File(installFolder.trim());
if (!folder.isAbsolute())
{
folder = new File(PropertiesUtil.getUserHome(), "eclipse/" + installFolder.trim());
}
return folder;
}
private Pair<Type, String> validateInstallFolder()
{
File canonicalFolder = IOUtil.getCanonicalFile(getEffectiveInstallFolder());
File parentFolder = canonicalFolder.getParentFile();
Pair<Type, String> errorMessage = validateFolder(canonicalFolder.getParentFile(), canonicalFolder.getName());
if (errorMessage != null)
{
return errorMessage;
}
if (!canonicalFolder.toString().equals(installFolder.trim()))
{
if (StringUtil.isEmpty(installFolder.trim()))
{
return Pair.create(Type.WARNING, "The folder is unspecified so '" + canonicalFolder + "' will be used.");
}
return Pair.create(Type.WARNING, "The folder is specified as '" + installFolder + "' but '" + canonicalFolder + "' will be used.");
}
installRoot = parentFolder == null ? canonicalFolder.getAbsolutePath() : parentFolder.getAbsolutePath();
return errorMessage;
}
private Pair<Type, String> validateFolder(File parentFolder, String childFolder)
{
if (parentFolder == null)
{
return Pair.create(Type.ERROR, "The product cannot be installed in the root of the file system.");
}
if (parentFolder.exists())
{
if (parentFolder.isDirectory())
{
if (IOUtil.canWriteFolder(parentFolder))
{
try
{
new File(parentFolder, childFolder).getCanonicalFile();
Pair<Type, String> result = validateChildFolder(parentFolder, childFolder, false);
// The folder exists, but that's not a problem we want to report in this case.
if (result == null || result.getElement1() == Type.WARNING)
{
return null;
}
return result;
}
catch (Exception ex)
{
return Pair.create(Type.ERROR, "Folder '" + childFolder + "' cannot be created in the folder '" + parentFolder + "'.");
}
}
Pair<Type, String> result = validateChildFolder(parentFolder, childFolder, false);
if (result != null)
{
// The folder exists, but that's not a problem we want to report in this case.
if (result.getElement1() == Type.WARNING)
{
return null;
}
return result;
}
return Pair.create(Type.ERROR, "Folder '" + childFolder + "' cannot be created in the read-only folder '" + parentFolder + "'.");
}
return Pair.create(Type.ERROR,
"Folder '" + childFolder + "' cannot be created in the folder '" + parentFolder + "' because '" + parentFolder.getName() + "' exists as a file.");
}
return validateFolder(parentFolder.getParentFile(), parentFolder.getName());
}
private Pair<Type, String> validateChildFolder(File parentFolder, String childFolder, boolean isProductLocation)
{
String prefix = isProductLocation ? "Product folder '" : "Folder '";
File child = new File(parentFolder, childFolder);
if (child.exists())
{
if (child.isDirectory())
{
if (IOUtil.canWriteFolder(child))
{
return Pair.create(Type.WARNING, prefix + child + " already exists.");
}
return Pair.create(Type.ERROR, prefix + child + "' cannot be used because it is read-only.");
}
return Pair.create(Type.ERROR, prefix + child + "' cannot be used because it exists as file.");
}
try
{
new File(parentFolder, childFolder).getCanonicalFile();
}
catch (Exception ex)
{
return Pair.create(Type.ERROR, prefix + childFolder + "' cannot be created.");
}
return null;
}
private File getLogFile()
{
return performer.getLogFile();
}
private void openInstallLog()
{
File installationLogFile = getLogFile();
dialog.showInstallationLog(installationLogFile);
}
private boolean isInstallLogAvailable()
{
return performer != null && getLogFile().exists();
}
private SimpleCheckbox createCheckbox(Composite parent, String text)
{
final SimpleCheckbox checkbox = new SimpleCheckbox(parent);
checkbox.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
checkbox.setText(text);
checkbox.setChecked(true);
return checkbox;
}
/**
* @author Ed Merks
*/
@SuppressWarnings("restriction")
private class DownloadArtifactLister implements org.eclipse.equinox.internal.provisional.p2.core.eventbus.SynchronousProvisioningListener
{
public synchronized void notify(EventObject event)
{
if (event instanceof DownloadArtifactEvent)
{
DownloadArtifactEvent downloadArtifactEvent = (DownloadArtifactEvent)event;
URI artifactURI = URI.createURI(downloadArtifactEvent.getArtifactURI().toString());
if (downloadArtifactEvent.isCompleted())
{
progress.removeArtifactURI(artifactURI);
}
else
{
progress.addArtifactURI(artifactURI);
}
}
}
}
/**
* @author Eike Stepper
*/
private static final class UnloggedException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public UnloggedException(String message)
{
super(message);
}
}
/**
* @author Eike Stepper
*/
private static class UserAdjuster
{
private static final URI INSTALLATION_LOCATION_ATTRIBUTE_URI = SetupTaskPerformer.getAttributeURI(SetupPackage.Literals.INSTALLATION_TASK__LOCATION);
private EList<AttributeRule> attributeRules;
private String oldValue;
private void adjust(User user, String installFolder)
{
attributeRules = user.getAttributeRules();
for (AttributeRule attributeRule : attributeRules)
{
if (INSTALLATION_LOCATION_ATTRIBUTE_URI.equals(attributeRule.getAttributeURI()))
{
oldValue = attributeRule.getValue();
attributeRule.setValue(installFolder);
return;
}
}
AttributeRule attributeRule = SetupFactory.eINSTANCE.createAttributeRule();
attributeRule.setAttributeURI(INSTALLATION_LOCATION_ATTRIBUTE_URI);
attributeRule.setValue(installFolder);
attributeRules.add(attributeRule);
}
public void undo()
{
for (Iterator<AttributeRule> it = attributeRules.iterator(); it.hasNext();)
{
AttributeRule attributeRule = it.next();
if (INSTALLATION_LOCATION_ATTRIBUTE_URI.equals(attributeRule.getAttributeURI()))
{
if (oldValue == null)
{
it.remove();
}
else
{
attributeRule.setValue(oldValue);
}
return;
}
}
}
}
/**
* @author Eike Stepper
*/
private final class SimplePrompter extends HashMap<String, String> implements SetupPrompter
{
private static final long serialVersionUID = 1L;
private final List<VariableTask> unresolvedVariables = new ArrayList<VariableTask>();
private final OS os;
private final String vmPath;
public OS getOS()
{
return os;
}
public SimplePrompter(OS os, String vmPath)
{
this.os = os;
this.vmPath = vmPath;
}
public String getVMPath()
{
return vmPath;
}
public List<VariableTask> getUnresolvedVariables()
{
return unresolvedVariables;
}
public boolean promptVariables(List<? extends SetupTaskContext> performers)
{
for (SetupTaskContext performer : performers)
{
List<VariableTask> unresolvedVariables = ((SetupTaskPerformer)performer).getUnresolvedVariables();
for (VariableTask variable : unresolvedVariables)
{
String value = getValue(variable);
if (value == null)
{
this.unresolvedVariables.add(variable);
}
}
}
return unresolvedVariables.isEmpty();
}
public String getValue(VariableTask variable)
{
String name = variable.getName();
return get(name);
}
public UserCallback getUserCallback()
{
return null;
}
}
/**
* @author Eike Stepper
*/
private final class SimpleProgress implements ProgressLog, IProgressMonitor, Runnable
{
private volatile String name;
private volatile double totalWork;
private volatile double work;
private volatile double lastWork;
private volatile long reportWarningTimeout;
private volatile boolean canceled;
private volatile boolean done;
private String lastName;
private final Set<URI> artifactURIs = new HashSet<URI>();
private Set<URI> lastArtifactURIs;
public synchronized void addArtifactURI(URI uri)
{
artifactURIs.add(uri);
}
public synchronized void removeArtifactURI(URI uri)
{
artifactURIs.remove(uri);
}
public void setTerminating()
{
}
public void log(String line)
{
log(line, true, Severity.OK);
}
public void log(String line, Severity severity)
{
log(line, true, severity);
}
public void log(String line, boolean filter)
{
log(line, filter, Severity.OK);
}
public synchronized void log(String line, boolean filter, Severity severity)
{
name = line;
}
public void log(IStatus status)
{
String string = SetupInstallerPlugin.toString(status);
log(string, false);
}
public void log(Throwable t)
{
String string = SetupInstallerPlugin.toString(t);
log(string, false);
}
public synchronized void task(SetupTask setupTask)
{
if (setupTask != null)
{
name = "Performing setup task " + setupTask.eClass().getName();
}
else
{
name = null;
}
}
public synchronized boolean isCanceled()
{
return canceled;
}
public synchronized void setCanceled(boolean canceled)
{
this.canceled = canceled;
}
public synchronized void beginTask(String name, int totalWork)
{
performer.log(name);
if (this.totalWork == 0)
{
this.totalWork = totalWork;
schedule();
}
}
public synchronized void done()
{
work = totalWork;
done = true;
}
public synchronized void setTaskName(String name)
{
performer.log(name);
}
public synchronized void subTask(String name)
{
performer.log(name);
}
public synchronized void internalWorked(double work)
{
this.work += work;
}
public void worked(int work)
{
internalWorked(work);
}
public void run()
{
String name;
double totalWork;
double work;
boolean canceled;
boolean done;
Set<URI> artifactURIs;
synchronized (this)
{
name = this.name;
totalWork = this.totalWork;
work = this.work;
canceled = this.canceled;
done = this.done;
artifactURIs = new HashSet<URI>(this.artifactURIs);
}
if (!canceled && !done)
{
String safeName = StringUtil.safe(name);
if (PROGRESS_WATCHDOG_TIMEOUT != 0)
{
long now = System.currentTimeMillis();
boolean slowArtifactDownload = !artifactURIs.isEmpty() && artifactURIs.equals(lastArtifactURIs);
if ((lastWork == work || slowArtifactDownload) && !isModalShellInForeground())
{
if (now >= reportWarningTimeout)
{
if (slowArtifactDownload)
{
StringBuilder message = new StringBuilder();
Set<URI> hosts = new HashSet<URI>();
for (URI artifactURI : artifactURIs)
{
URI hostURI = URI.createHierarchicalURI(artifactURI.scheme(), artifactURI.authority(), null, null, null);
if (hosts.add(hostURI))
{
if (message.length() != 0)
{
message.append(", ");
}
message.append(hostURI);
}
}
if (hosts.size() > 1)
{
message.insert(0, "the following hosts: ");
}
message.insert(0, "Artifact download is progressing very slowly from ");
dialog.showMessage(message.toString().replace(' ', '\u00a0'), Type.WARNING, false);
}
else
{
dialog.showMessage(("The installation process is taking longer than usual: " + safeName).replace(' ', '\u00a0'), Type.WARNING, false);
}
installButton.setProgressAnimationSpeed(0.4f);
resetWatchdogTimer(now);
}
}
else
{
installButton.startProgressAnimation();
installButton.setProgressAnimationSpeed(1);
dialog.clearMessage();
resetWatchdogTimer(now);
}
lastWork = work;
lastArtifactURIs = artifactURIs;
}
double progress = work / totalWork;
try
{
installButton.setProgress((float)progress);
if (!ObjectUtil.equals(name, lastName))
{
lastName = name;
installButton.setToolTipText(safeName);
}
}
catch (SWTException ex)
{
return;
}
reschedule();
}
}
private boolean isModalShellInForeground()
{
Shell mainShell = getShell();
Shell[] shells = getDisplay().getShells();
for (Shell shell : shells)
{
if (shell != mainShell)
{
int style = shell.getStyle();
if ((style & SWT.APPLICATION_MODAL) != 0 || (style & SWT.PRIMARY_MODAL) != 0 || (style & SWT.SYSTEM_MODAL) != 0)
{
return true;
}
}
}
return false;
}
private void resetWatchdogTimer(long now)
{
reportWarningTimeout = now + PROGRESS_WATCHDOG_TIMEOUT * 1000;
}
private void reschedule()
{
UIUtil.timerExec(100, getDisplay(), this);
}
private void schedule()
{
resetWatchdogTimer(System.currentTimeMillis());
UIUtil.asyncExec(getDisplay(), this);
}
}
/**
* @author Ed Merks
*/
private static class FileCompletionSupport extends KeyAdapter implements VerifyListener
{
private final Text folderText;
private Shell shell;
public FileCompletionSupport(Text text)
{
folderText = text;
if (OS.INSTANCE.isMac())
{
folderText.addKeyListener(this);
}
else
{
folderText.addVerifyListener(this);
}
}
protected void setFolderTextAndSelect(String folder)
{
folderText.setText(folder);
int length = folder.length();
folderText.setSelection(new Point(length, length));
}
@Override
public void keyReleased(KeyEvent e)
{
if (e.keyCode == ' ' && e.stateMask == SWT.CTRL)
{
e.doit = false;
open();
}
}
public void verifyText(VerifyEvent e)
{
if (e.keyCode == ' ' && e.stateMask == SWT.CTRL)
{
e.doit = false;
open();
}
}
private void open()
{
if (shell == null)
{
new Dialog(folderText.getShell())
{
private org.eclipse.swt.widgets.List list;
private ModifyListener modifyListener = new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
updater.schedule();
}
};
private FocusListener focusListener = new FocusAdapter()
{
@Override
public void focusLost(FocusEvent e)
{
checkFocus();
}
};
private MouseListener mouseListener = new MouseAdapter()
{
@Override
public void mouseDown(MouseEvent e)
{
checkFocus();
}
};
private ControlListener controlListener = new ControlListener()
{
public void controlResized(ControlEvent e)
{
shell.dispose();
}
public void controlMoved(ControlEvent e)
{
shell.dispose();
}
};
private KeyListener keyListener = new KeyAdapter()
{
@Override
public void keyReleased(KeyEvent e)
{
if (e.keyCode == SWT.ARROW_UP)
{
int itemCount = list.getItemCount();
if (itemCount > 0)
{
shell.setFocus();
list.select(itemCount - 1);
list.showSelection();
}
}
else if (e.keyCode == SWT.ARROW_DOWN)
{
int itemCount = list.getItemCount();
if (itemCount > 0)
{
shell.setFocus();
int selectionIndex = list.getSelectionIndex();
list.select(selectionIndex + 1);
}
}
else if (e.keyCode == '\r')
{
int selectionIndex = list.getSelectionIndex();
if (selectionIndex != -1)
{
String folder = list.getItem(selectionIndex);
File file = new File(folder);
if (!folder.endsWith(File.separator) && file.isDirectory() && !IOUtil.canWriteFolder(file))
{
folder += File.separator;
}
setFolderTextAndSelect(folder);
}
}
}
};
/**
* A class for delayed update of the list.
* @author Ed Merks
*/
class Updater extends UIUtil.DelayedRunnable
{
public Updater(Shell shell)
{
super(shell, 350);
}
@Override
protected void perform()
{
update();
}
}
private Updater updater;
private void checkFocus()
{
UIUtil.asyncExec(list, new Runnable()
{
public void run()
{
if (shell.getDisplay().getActiveShell() != shell)
{
shell.dispose();
}
}
});
}
public void open()
{
Shell parent = getParent();
shell = new Shell(parent, SWT.MODELESS | SWT.NO_TRIM);
Control wrapper = folderText.getParent();
Rectangle folderTextBounds = wrapper.getBounds();
Point position = wrapper.getParent().toDisplay(folderTextBounds.x, folderTextBounds.y + folderTextBounds.height);
shell.setBounds(position.x, position.y, folderTextBounds.width, 100);
shell.setLayout(new FillLayout());
updater = new Updater(shell);
list = new org.eclipse.swt.widgets.List(shell, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
list.addMouseListener(new MouseAdapter()
{
@Override
public void mouseUp(MouseEvent e)
{
int selectionIndex = list.getSelectionIndex();
if (selectionIndex != -1)
{
apply(list.getItem(selectionIndex));
}
}
});
list.addKeyListener(new KeyAdapter()
{
@Override
public void keyReleased(KeyEvent e)
{
if (e.keyCode == '\r')
{
int selectionIndex = list.getSelectionIndex();
if (selectionIndex != -1)
{
apply(list.getItem(selectionIndex));
}
}
}
});
shell.addShellListener(new ShellAdapter()
{
@Override
public void shellDeactivated(ShellEvent e)
{
shell.dispose();
}
});
shell.addDisposeListener(new DisposeListener()
{
public void widgetDisposed(DisposeEvent e)
{
folderText.removeModifyListener(modifyListener);
folderText.removeFocusListener(focusListener);
folderText.removeMouseListener(mouseListener);
folderText.removeKeyListener(keyListener);
if (!folderText.getShell().isDisposed())
{
folderText.getShell().removeControlListener(controlListener);
}
shell = null;
}
});
folderText.addModifyListener(modifyListener);
folderText.addFocusListener(focusListener);
folderText.addMouseListener(mouseListener);
folderText.addKeyListener(keyListener);
folderText.getShell().addControlListener(controlListener);
update();
shell.setVisible(true);
}
private void update()
{
if (!list.isDisposed())
{
list.removeAll();
Point selection = folderText.getSelection();
String folderLiteral = StringUtil.trimLeft(folderText.getText().substring(0, selection.x));
File folder = IOUtil.getCanonicalFile(new File(folderLiteral));
final String name = folder.getName().toLowerCase();
if (folderLiteral.length() == 0)
{
File[] roots = File.listRoots();
if (roots != null)
{
for (File root : roots)
{
list.add(root.getPath());
}
}
}
else
{
boolean isExplicitFolder = folderLiteral.endsWith("/") || folderLiteral.endsWith("\\");
final String nameFilter = isExplicitFolder ? "" : name;
File searchFolder = isExplicitFolder ? folder : folder.getParentFile();
if (searchFolder != null)
{
File[] folders = searchFolder.listFiles(new FileFilter()
{
public boolean accept(File file)
{
return file.isDirectory() && file.getName().toLowerCase().startsWith(nameFilter);
}
});
if (folders != null)
{
for (File root : folders)
{
list.add(root.getPath());
}
}
}
else
{
File[] roots = File.listRoots();
if (roots != null)
{
String lowerCaseFolderLiteral = folderLiteral.toLowerCase();
for (File root : roots)
{
String path = root.getPath();
if (path.length() == 1)
{
File[] folders = root.listFiles(new FileFilter()
{
public boolean accept(File file)
{
return file.isDirectory() && file.getName().toLowerCase().startsWith(nameFilter);
}
});
if (folders != null)
{
for (File child : folders)
{
list.add(child.getPath());
}
}
}
else
{
if (path.toLowerCase().startsWith(lowerCaseFolderLiteral))
{
list.add(root.getPath());
}
}
}
}
}
}
}
if (list.getItemCount() != 0)
{
list.select(0);
}
int width = list.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
Rectangle clientArea = shell.getClientArea();
Rectangle newClientArea = shell.computeTrim(clientArea.x, clientArea.y, width, clientArea.height);
Rectangle bounds = shell.getBounds();
if (bounds.width < newClientArea.width)
{
bounds.width = newClientArea.width;
shell.setBounds(bounds);
}
}
private void apply(String folder)
{
File file = new File(folder);
if (!folder.endsWith(File.separator) && file.isDirectory() && !IOUtil.canWriteFolder(file))
{
folder += File.separator;
}
setFolderTextAndSelect(folder);
shell.dispose();
}
}.open();
}
}
}
}