/*******************************************************************************
* Copyright (c) 2014 Mentor Graphics 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:
* Mentor Graphics - initial API and implementation
*******************************************************************************/
package com.codesourcery.internal.installer;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.planner.IPlanner;
import org.eclipse.osgi.util.NLS;
import com.codesourcery.installer.AbstractInstallModule;
import com.codesourcery.installer.IInstallAction;
import com.codesourcery.installer.IInstallData;
import com.codesourcery.installer.IInstallDescription;
import com.codesourcery.installer.IInstallMode;
import com.codesourcery.installer.IInstallDescription.MirrorMode;
import com.codesourcery.installer.IInstallPlatform.ShortcutFolder;
import com.codesourcery.installer.IInstallValues;
import com.codesourcery.installer.IInstallWizardPage;
import com.codesourcery.installer.IInstalledProduct;
import com.codesourcery.installer.IProductRange;
import com.codesourcery.installer.IRepositoryLocation;
import com.codesourcery.installer.Installer;
import com.codesourcery.installer.LicenseDescriptor;
import com.codesourcery.installer.LinkDescription;
import com.codesourcery.installer.UninstallMode;
import com.codesourcery.installer.ui.IInstallPages;
import com.codesourcery.internal.installer.actions.InstallIUAction;
import com.codesourcery.internal.installer.actions.PathAction;
import com.codesourcery.internal.installer.actions.ShortcutAction;
import com.codesourcery.internal.installer.actions.UninstallLinkAction;
import com.codesourcery.internal.installer.actions.UnityShortcutAction;
import com.codesourcery.internal.installer.ui.pages.ComponentsPage;
import com.codesourcery.internal.installer.ui.pages.InformationPage;
import com.codesourcery.internal.installer.ui.pages.InstallFolderPage;
import com.codesourcery.internal.installer.ui.pages.LicensePage;
import com.codesourcery.internal.installer.ui.pages.MirrorPage;
import com.codesourcery.internal.installer.ui.pages.PathPage;
import com.codesourcery.internal.installer.ui.pages.SetupInstalledProductsPage;
import com.codesourcery.internal.installer.ui.pages.SetupPage;
import com.codesourcery.internal.installer.ui.pages.ShortcutsPage;
import com.codesourcery.internal.installer.ui.pages.WelcomePage;
/**
* Install module that provides general wizard pages and actions.
*/
public class GeneralInstallModule extends AbstractInstallModule {
/** Module identifier */
public static final String ID = "com.codesourcery.installer.module.default";
@Override
public String getId() {
return ID;
}
@Override
public void init(IInstallDescription installDescription) {
super.init(installDescription);
Installer.getDefault().getInstallManager().addInstallVerifier(new GeneralInstallVerifier());
}
@Override
public void initAgent(IProvisioningAgent agent) {
// Use ordered planner
if (Installer.getDefault().getInstallManager().getInstallDescription().getOrderPlanner()) {
agent.registerService(IPlanner.SERVICE_NAME, new OrderedPlanner(agent));
}
}
@Override
public void setDataDefaults(IInstallData data) {
// Initialize defaults
data.setProperty(IInstallValues.CREATE_PROGRAM_SHORTCUTS, true);
data.setProperty(IInstallValues.CREATE_DESKTOP_SHORTCUTS, true);
data.setProperty(IInstallValues.CREATE_LAUNCHER_SHORTCUTS, true);
data.setProperty(IInstallValues.SET_PATH, true);
}
@Override
public IInstallAction[] getInstallActions(IProvisioningAgent agent, IInstallData data, IInstallMode installMode) {
ArrayList<IInstallAction> actions = new ArrayList<IInstallAction>();
// P2 IU action
addIUAction(actions, data);
if (!installMode.isUpdate()) {
// Short-cut actions
addShortcutActions(actions, data);
// Add PATH environment action
addPathAction(actions, data);
// Uninstall links
addUninstallLinkAction(actions, data);
}
return actions.toArray(new IInstallAction[actions.size()]);
}
/**
* Adds the P2 IU action.
*
* @param actions Actions
* @param data Install data
*/
protected void addIUAction(List<IInstallAction> actions, IInstallData data) {
// Units to add
ArrayList<IInstallableUnit> unitsToAdd = new ArrayList<IInstallableUnit>();
// Units to remove
ArrayList<IInstallableUnit> unitsToRemove = new ArrayList<IInstallableUnit>();
// Get the install plan
RepositoryManager.getDefault().getInstallUnits(unitsToAdd, unitsToRemove);
// Add P2 provision action
InstallIUAction iuAction = new InstallIUAction(
RepositoryManager.getDefault().getProfileId(),
getInstallDescription().getProfileProperties(),
getInstallDescription().getUpdateSites(),
unitsToAdd.toArray(new IInstallableUnit[unitsToAdd.size()]),
unitsToRemove.toArray(new IInstallableUnit[unitsToRemove.size()])
);
iuAction.setRemoveProfile(getInstallDescription().getRemoveProfile());
actions.add(iuAction);
}
/**
* Adds short-cut actions.
*
* @param actions Actions
* @param data Install data
*/
protected void addShortcutActions(List<IInstallAction> actions, IInstallData data) {
LinkDescription[] links = getInstallDescription().getLinks();
// No short-cuts
if (links == null || links.length == 0)
return;
for (LinkDescription link : links) {
try {
// Add program shortcuts?
if (link.getFolder().equals(ShortcutFolder.PROGRAMS)) {
if (!data.getBooleanProperty(IInstallValues.CREATE_PROGRAM_SHORTCUTS))
continue;
}
// Add desktop shortcuts?
if (link.getFolder().equals(ShortcutFolder.DESKTOP)) {
if (!data.getBooleanProperty(IInstallValues.CREATE_DESKTOP_SHORTCUTS))
continue;
}
// Add launcher shortcuts?
if (link.getFolder().equals(ShortcutFolder.UNITY_DASH)) {
if (!data.getBooleanProperty(IInstallValues.CREATE_LAUNCHER_SHORTCUTS) ||
!Installer.getDefault().getInstallPlatform()
.desktopIsUnity())
continue;
}
// Handle {cmd} variable.
IPath target;
if (link.getTarget().equals("{cmd}")) {
target = getShellPath();
} else {
target = getInstallDescription().getRootLocation().append(link.getTarget());
}
IPath iconPath = null;
// Link includes icon
if ((link.getIconPath() != null) && !link.getIconPath().isEmpty()) {
iconPath = getInstallDescription().getRootLocation().append(link.getIconPath());
String iconExtension = iconPath.getFileExtension();
// Only icon resources allowed on Windows
if (Installer.isWindows() && (!iconExtension.equals("exe") && !iconExtension.equals("ico"))) {
iconPath = null;
}
}
IPath workingDirectory = target.removeLastSegments(1);
IPath shortcutFolder;
IPath removeFolder = null;
IPath linkPath = new Path(link.getPath());
IPath baseFolder = Installer.getDefault().getInstallPlatform().getShortcutFolder(link.getFolder());
// Desktop short-cut
if (link.getFolder() == ShortcutFolder.DESKTOP) {
// Remove top short-cut folder
if (!linkPath.isEmpty()) {
removeFolder = baseFolder.append(getTopFolder(linkPath));
}
shortcutFolder = baseFolder.append(linkPath);
}
// Programs short-cut
else {
// User location available
String value = (String)data.getProperty(IInstallValues.PROGRAM_SHORTCUTS_FOLDER);
if (value != null) {
IPath userPath = new Path(value);
// On Windows, use start menu relative path
if (Installer.isWindows()) {
removeFolder = baseFolder.append(getTopFolder(userPath));
shortcutFolder = baseFolder.append(userPath).append(link.getPath());
}
// On Linux, use full path
else {
removeFolder = getFirstNonExistentPath(userPath);
shortcutFolder = userPath.append(link.getPath());
}
}
// No user location (silent mode) - use default relative to
// install location
else {
IPath folder = getInstallDescription().getLinksLocation().append(linkPath);
IPath installFolder = Installer.getDefault().getInstallManager().getInstallLocation();
shortcutFolder = Installer.isWindows() ? baseFolder.append(folder) : installFolder.append(folder);
removeFolder = getFirstNonExistentPath(shortcutFolder);
}
}
// Ubuntu Unity short-cut
ShortcutAction shortcutAction = null;
if (link.getFolder() == ShortcutFolder.UNITY_DASH
|| (link.getFolder() == ShortcutFolder.DESKTOP && Installer
.getDefault().getInstallPlatform()
.desktopIsUnity())) {
shortcutAction = new UnityShortcutAction(shortcutFolder, removeFolder, link.getName(),
target, iconPath, workingDirectory, link.getArguments(),
link.getFolder() == ShortcutFolder.UNITY_DASH);
} else {
shortcutAction = new ShortcutAction(shortcutFolder, removeFolder, link.getName(), target,
iconPath, workingDirectory, link.getArguments());
}
actions.add(shortcutAction);
}
catch (Exception e) {
Installer.log(e);
}
}
}
/**
* Returns the path to the system shell.
*
* @return shell path
* @throws Exception
*/
protected IPath getShellPath() throws Exception {
if (!Installer.isWindows()) {
throw new Exception("{cmd} property only supported for Windows");
}
String shellPath = System.getenv("ComSpec");
if (shellPath == null) {
throw new Exception("Can't resolve {cmd} property");
}
return new Path(shellPath);
}
/**
* Adds PATH environment action.
*
* @param actions Actions
* @param data Install data
*/
protected void addPathAction(List<IInstallAction> actions, IInstallData data) {
if (data.getBooleanProperty(IInstallValues.SET_PATH)) {
// Add paths
String[] paths = getInstallDescription().getEnvironmentPaths();
if (paths != null) {
String[] resolvedPaths = new String[paths.length];
for (int index = 0; index < paths.length; index ++) {
IPath path = getInstallDescription().getRootLocation().append(paths[index]);
resolvedPaths[index] = path.toOSString();
}
actions.add(new PathAction(resolvedPaths));
}
}
}
/**
* Adds uninstall link action (Windows only).
*
* @param actions Actions
* @param data Install data
*/
protected void addUninstallLinkAction(List<IInstallAction> actions, IInstallData data) {
if (Installer.isWindows()) {
// Install size (in kilobytes)
int installSize = -1;
String size = data.getProperty(IInstallValues.INSTALL_SIZE);
if (size != null) {
try {
long bytes = Long.parseLong(size);
installSize = (int)(bytes / 1024);
}
catch (Exception e) {
Installer.log(e);
}
}
UninstallMode uninstallMode = getInstallDescription().getUninstallMode();
if ((uninstallMode != null) && uninstallMode.getCreateAddRemove() && (getInstallDescription().getUninstallerName() != null)) {
actions.add(new UninstallLinkAction(
getInstallDescription().getRootLocation().append(IInstallConstants.UNINSTALL_DIRECTORY),
getInstallDescription().getProductVendor(),
getInstallDescription().getProductVersionString(),
getInstallDescription().getProductHelp(),
getInstallDescription().getUninstallerName(),
installSize));
}
}
}
/**
* Given a full path, this method returns the path to the first
* directory that does not exist.
*
* @param fullPath Full path
* @return Path to first directory in full path that does
* not exist.
*/
private IPath getFirstNonExistentPath(IPath fullPath) {
IPath path = new Path("").makeAbsolute();
for (String segment : fullPath.segments()) {
path = path.append(segment);
if (!path.toFile().exists())
break;
}
return path;
}
/**
* Returns the top most folder of a path.
*
* @return
*/
private IPath getTopFolder(IPath path) {
if (!path.isEmpty())
return new Path(path.segment(0));
else
return new Path("");
}
@Override
public IInstallWizardPage[] getInstallPages(IInstallMode installMode) {
ArrayList<IInstallWizardPage> pages = new ArrayList<IInstallWizardPage>();
IInstallDescription installDescription = getInstallDescription();
String productName = installDescription.getProductName();
// Setup page
IInstallWizardPage[] setupPages = createSetupPages(installDescription);
for (IInstallWizardPage setupPage : setupPages) {
pages.add(setupPage);
}
// Fixed licenses
LicenseDescriptor[] licenses = installDescription.getLicenses();
// Welcome page
IInstallWizardPage welcomePage = createWelcomePage(installDescription, installDescription.getProductName());
pages.add(welcomePage);
// Determine if any IU licenses should be displayed
boolean includeUnitLicenses = false;
if (licenses != null) {
for (LicenseDescriptor license : licenses) {
if (license.getUnit() != null) {
includeUnitLicenses = true;
break;
}
}
}
// License page
// If IU license information will not be included, order the license page
// after the Welcome page.
if ((licenses !=null) && (licenses.length > 0) && !includeUnitLicenses) {
pages.add(new LicensePage(licenses));
}
// Information page
String informationText = installDescription.getText(IInstallDescription.TEXT_INFORMATION, null);
if (informationText != null) {
InformationPage informationPage = new InformationPage(IInstallPages.INFORMATION_PAGE, InstallMessages.InformationPageTitle, informationText);
informationPage.setScrollable(true);
pages.add(informationPage);
}
// If install location has not already been set
if (Installer.getDefault().getInstallManager().getInstallLocation() == null) {
// Installation folder page
String defaultFolder = null;
IPath installLocation = installDescription.getRootLocation();
if (installLocation != null) {
defaultFolder = installLocation.toOSString();
}
IInstallWizardPage installFolderPage = createInstallFolderPage(installDescription, defaultFolder);
pages.add(installFolderPage);
}
// Components page
IInstallWizardPage componentsPage = createComponentPage(installDescription);
if(componentsPage != null)
pages.add(componentsPage);
// If IU license information will be included, show page after the
// Components page.
if ((licenses !=null) && (licenses.length > 0) && includeUnitLicenses) {
pages.add(new LicensePage(licenses));
}
// Shortcuts page
LinkDescription[] links = installDescription.getLinks();
if (links != null) {
ShortcutsPage shortcutsPage = new ShortcutsPage(IInstallPages.SHORTCUTS_PAGE,
InstallMessages.ShortcutsPageTitle, links);
pages.add(shortcutsPage);
}
// PATH page
String[] paths = installDescription.getEnvironmentPaths();
if ((paths != null) && (paths.length > 0)) {
PathPage pathPage = new PathPage(IInstallPages.PATHS_PAGE, InstallMessages.PathPageTitle,
productName, paths);
pages.add(pathPage);
}
return pages.toArray(new IInstallWizardPage[pages.size()]);
}
/**
* Creates the Setup page if required.
*
* @param installDescription Install description
* @return Setup pages
*/
protected IInstallWizardPage[] createSetupPages(IInstallDescription installDescription) {
ArrayList<IInstallWizardPage> setupPages = new ArrayList<IInstallWizardPage>();
IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode();
// If no mirror repository has been set
if (RepositoryManager.getDefault().getCacheLocation() == null) {
boolean enableMirror;
// Enable mirroring only for remote repositories
if (installDescription.getMirrorMode() == MirrorMode.REMOTE_ONLY) {
enableMirror = false;
List<IRepositoryLocation> repositoryLocations = installDescription.getRepositoryLocations();
for (IRepositoryLocation location : repositoryLocations) {
URI[] metadataLocations = location.getMetadataLocations();
for (URI metadataLocation : metadataLocations) {
// Remote repository
if (metadataLocation.getHost() != null) {
enableMirror = true;
break;
}
}
}
}
// Enable mirroring
else {
enableMirror = installDescription.getMirrorMode() != MirrorMode.NONE;
}
// Add mirror page if required
if (enableMirror) {
MirrorPage mirrorPage = new MirrorPage(IInstallPages.MIRROR_PAGE, InstallMessages.MirrorPage_Name);
setupPages.add(mirrorPage);
}
}
String installedProductsTitle = installDescription.getText(IInstallDescription.TEXT_CATEGORY_INSTALL,
NLS.bind(InstallMessages.SetupPage_Title0, Installer.getDefault().getInstallManager().getInstallDescription().getProductName()));
// Installing
if (mode.isInstall()) {
// Product requirement range
IProductRange[] range = installDescription.getRequires();
// Product category
String category = installDescription.getProductCategory();
// Get existing product if available
String productId = getInstallDescription().getProductId();
IInstalledProduct installedProduct = Installer.getDefault().getInstallManager().getInstalledProduct(productId);
// If patch install
if (mode.isPatch()) {
IInstalledProduct[] products = null;
// If range is not specified, choose from all products
if (range == null) {
products = Installer.getDefault().getInstallManager().getInstalledProducts();
}
// Choose from applicable products
else {
products = Installer.getDefault().getInstallManager().getInstalledProductsByRange(range, true);
}
// Show setup page to pick product for update, otherwise the user
// must browse for the product installation in the wizard
if ((products != null) && (products.length > 0)) {
IInstallWizardPage setupPage = new SetupInstalledProductsPage(IInstallPages.SETUP_PAGE, installedProductsTitle, products, false);
setupPages.add(setupPage);
}
}
// Update or upgrade
else if (installedProduct != null) {
IInstallDescription description = Installer.getDefault().getInstallManager().getInstallDescription();
Version productVersion = description.getProductVersion();
boolean update = (productVersion.equals(installedProduct.getVersion()));
boolean supportsUpdate = description.getSupportsUpdate();
boolean supportsUpgrade = description.getSupportsUpgrade();
String updateTitle = description.getText(IInstallDescription.TEXT_EXISTING_INSTALL, null);
// If no existing version text supplied, use default
if (updateTitle == null) {
updateTitle = NLS.bind(InstallMessages.SetupPrompter_Title1, installedProduct.getName(), installedProduct.getVersionText());
}
// Else replace version property in text
else {
updateTitle = updateTitle.replace("${" + IInstallDescription.PROP_EXISTING_VERSION + "}", installedProduct.getVersionText());
}
// Update
if (update && supportsUpdate) {
IInstallWizardPage setupPage = new SetupPage(IInstallPages.SETUP_PAGE, updateTitle, installedProduct);
setupPages.add(setupPage);
}
// Upgrade
else if (supportsUpgrade){
Version minUpgradeVersion = getInstallDescription().getMinimumUpgradeVersion();
// If the product meets the minimum supported upgrade version
if ((minUpgradeVersion == null) || (installedProduct.getVersion().compareTo(minUpgradeVersion) >= 0)) {
IInstallWizardPage setupPage = new SetupPage(IInstallPages.SETUP_PAGE, updateTitle, installedProduct);
setupPages.add(setupPage);
}
}
}
// Category specified
else if (category != null) {
IInstalledProduct[] products = Installer.getDefault().getInstallManager().getInstalledProductsByCategory(category, true);
// If a requirement on other products is also specified, filter the list
if (range != null) {
IInstalledProduct[] requiredProducts = Installer.getDefault().getInstallManager().getInstalledProductsByRange(range, true);
ArrayList<IInstalledProduct> validProducts = new ArrayList<IInstalledProduct>();
for (IInstalledProduct product : products) {
for (IInstalledProduct requiredProduct : requiredProducts) {
if (requiredProduct.getId().equals(product.getId())) {
validProducts.add(product);
break;
}
}
}
products = validProducts.toArray(new IInstalledProduct[validProducts.size()]);
}
if (products.length > 0) {
IInstallWizardPage setupPage = new SetupInstalledProductsPage(IInstallPages.SETUP_PAGE, installedProductsTitle, products, true);
setupPages.add(setupPage);
}
}
// Else if product range is specified
else if (range != null) {
IInstalledProduct[] products = Installer.getDefault().getInstallManager().getInstalledProductsByRange(range, true);
if (products.length > 0) {
IInstallWizardPage setupPage = new SetupInstalledProductsPage(IInstallPages.SETUP_PAGE, installedProductsTitle, products, false);
setupPages.add(setupPage);
}
}
}
return setupPages.toArray(new IInstallWizardPage[setupPages.size()]);
}
/***
* Creates Welcome page
*
* @param installDescription Install description
* @param productName Product name
* @return Welcome page
*/
protected IInstallWizardPage createWelcomePage(IInstallDescription installDescription, String productName) {
return new WelcomePage(IInstallPages.WELCOME_PAGE, productName, installDescription.getText(IInstallDescription.TEXT_WELCOME, null));
}
/***
* Creates page to specify install folder.
*
* @param installDescription Install description
* @param defaultFolder Default path of installation directory
* @return Install folder page
*/
protected IInstallWizardPage createInstallFolderPage(IInstallDescription installDescription, String defaultFolder) {
return new InstallFolderPage(IInstallPages.INSTALL_FOLDER_PAGE, InstallMessages.InstallFolderPageTitle, defaultFolder, installDescription);
}
/***
* Creates page to list components
*
* @param installDescription Install description
* @return Components page
*/
protected IInstallWizardPage createComponentPage(IInstallDescription installDescription) {
ComponentsPage componentsPage = new ComponentsPage(IInstallPages.COMPONENTS_PAGE,
InstallMessages.Components, installDescription);
return componentsPage;
}
@Override
public void setEnvironmentVariables(Map<String, String> environment) {
// Set up any PATH that may be required to launch programs
String[] paths = getInstallDescription().getEnvironmentPaths();
if (paths != null) {
String pathKey = "PATH";
String pathVar = environment.get(pathKey);
if (pathVar == null) {
pathVar = "";
}
for (String path : paths) {
IPath resolvedPath = getInstallDescription().getRootLocation().append(path);
pathVar = resolvedPath.toString() + File.pathSeparatorChar + pathVar;
}
environment.put(pathKey, pathVar);
}
}
}