blob: d75accd70314733c0e7771432455e3d258ba01f0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Manumitting Technologies Inc - bug 324310
* Simon Scholz <simon.scholz@vogella.com> - Bug 484445
*******************************************************************************/
package org.eclipse.pde.api.tools.ui.internal.wizards;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.HashSet;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.pde.api.tools.internal.model.ApiModelFactory;
import org.eclipse.pde.api.tools.internal.model.SystemLibraryApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
import org.eclipse.pde.api.tools.ui.internal.ApiToolsLabelProvider;
import org.eclipse.pde.api.tools.ui.internal.IApiToolsHelpContextIds;
import org.eclipse.pde.api.tools.ui.internal.SWTFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
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.DirectoryDialog;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.PlatformUI;
/**
* The wizard page allowing a new directory-based API profiles to be created or
* an existing one to be edited.
*
* @since 1.0.0
*/
public class DirectoryBasedApiBaselineWizardPage extends ApiBaselineWizardPage {
public static boolean isApplicable(IApiBaseline profile) {
String loc = profile.getLocation();
return loc != null && new Path(loc).toFile().exists();
}
/**
* Resets the baseline contents based on current settings and a location
* from which to read plug-ins.
*/
class ReloadOperation implements IRunnableWithProgress {
private String location, name;
/**
* Constructor
*
* @param platformPath
*/
public ReloadOperation(String name, String location) {
this.location = location;
this.name = name;
}
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask(WizardMessages.ApiProfileWizardPage_0, 10);
try {
fProfile = ApiModelFactory.newApiBaseline(name, location);
ApiModelFactory.addComponents(fProfile, location, monitor);
DirectoryBasedApiBaselineWizardPage.this.contentchange = true;
} catch (CoreException e) {
ApiPlugin.log(e);
} finally {
monitor.done();
}
}
}
/**
* widgets
*/
private Text nametext = null;
private TreeViewer treeviewer = null;
Combo locationcombo = null;
private Button browsebutton = null, reloadbutton = null;
/**
* We need to know if we are initializing the page to not respond to changed
* events causing validation when the wizard opens.
*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=266597
*/
private boolean initializing = false;
/**
* Constructor
*
* @param profile
*/
protected DirectoryBasedApiBaselineWizardPage(IApiBaseline profile) {
super(profile);
}
@Override
public void createControl(Composite parent) {
Composite comp = SWTFactory.createComposite(parent, 4, 1, GridData.FILL_HORIZONTAL);
SWTFactory.createWrapLabel(comp, WizardMessages.ApiProfileWizardPage_5, 1);
nametext = SWTFactory.createText(comp, SWT.BORDER | SWT.SINGLE, 3, GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL | GridData.BEGINNING);
nametext.addModifyListener(e -> setPageComplete(pageValid()));
SWTFactory.createVerticalSpacer(comp, 1);
SWTFactory.createWrapLabel(comp, WizardMessages.ApiProfileWizardPage_9, 1);
locationcombo = SWTFactory.createCombo(comp, SWT.BORDER | SWT.SINGLE, 1, GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL | GridData.BEGINNING, null);
locationcombo.addModifyListener(e -> {
setPageComplete(pageValid());
updateButtons();
});
browsebutton = SWTFactory.createPushButton(comp, WizardMessages.ApiProfileWizardPage_10, null);
browsebutton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
DirectoryDialog dialog = new DirectoryDialog(getShell());
dialog.setMessage(WizardMessages.ApiProfileWizardPage_11);
String loctext = locationcombo.getText().trim();
if (loctext.length() > 0) {
dialog.setFilterPath(loctext);
}
String newPath = dialog.open();
if (newPath != null
&& (!new Path(loctext).equals(new Path(newPath)) || getCurrentComponents().length == 0)) {
/*
* If the path is identical, but there is no component loaded, we still want to
* reload. This might be the case if the combo is initialized by copy/paste with
* a path that points to a plugin directory
*/
String updatedLocation = getUpdatedLocationForMacOS(newPath);
locationcombo.setText(updatedLocation);
// If current name matches with the last segment of old location then
// update the current name with last segment of new location
if (nametext.getText().equals(new Path(loctext).lastSegment())) {
nametext.setText(new Path(updatedLocation).lastSegment() == null ? "" //$NON-NLS-1$
: new Path(updatedLocation).lastSegment());
nametext.setFocus();
nametext.selectAll();
}
setErrorMessage(null);
doReload();
}
}));
reloadbutton = SWTFactory.createPushButton(comp, WizardMessages.ApiProfileWizardPage_12, null);
reloadbutton.setEnabled(locationcombo.getText().trim().length() > 0);
reloadbutton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> doReload()));
SWTFactory.createWrapLabel(comp, WizardMessages.ApiProfileWizardPage_13, 4);
Tree tree = new Tree(comp, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = 250;
gd.horizontalSpan = 4;
tree.setLayoutData(gd);
treeviewer = new TreeViewer(tree);
treeviewer.setLabelProvider(new ApiToolsLabelProvider());
treeviewer.setContentProvider(new ContentProvider());
treeviewer.setComparator(new ViewerComparator());
treeviewer.setInput(getCurrentComponents());
treeviewer.addSelectionChangedListener(event -> updateButtons());
treeviewer.addFilter(new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (element instanceof IApiComponent) {
IApiComponent component = (IApiComponent) element;
try {
if (component.isSourceComponent() || component.isSystemComponent()) {
return false;
}
} catch (CoreException e) {
ApiPlugin.log(e);
}
return true;
}
return !(element instanceof SystemLibraryApiComponent);
}
});
setControl(comp);
setPageComplete(fProfile != null);
initialize();
PlatformUI.getWorkbench().getHelpSystem().setHelp(comp, IApiToolsHelpContextIds.APIPROFILES_WIZARD_PAGE);
Dialog.applyDialogFont(comp);
}
private String getUpdatedLocationForMacOS(String newPath) {
String newLoc = newPath;
// On Mac OS, if user selects *.app, then location
// should be reset to *.app/Contents/Eclipse if that exists
if (System.getProperty("os.name").startsWith("Mac")) {//$NON-NLS-1$ //$NON-NLS-2$
Path nPath = new Path(newPath);
if (nPath.lastSegment() == null) {
return newLoc;
}
if (nPath.lastSegment().endsWith(".app")) { //$NON-NLS-1$
IPath appendedPath = nPath.append("Contents").append("Eclipse");//$NON-NLS-1$ //$NON-NLS-2$
if (appendedPath.toFile().exists()) {
newLoc = appendedPath.toOSString();
}
}
}
return newLoc;
}
/**
* Initializes the controls of the page if the profile is not
* <code>null</code>
*/
@Override
protected void initialize() {
initializing = true;
try {
super.initialize();
if (fProfile != null) {
nametext.setText(fProfile.getName());
IApiComponent[] components = fProfile.getApiComponents();
HashSet<String> locations = new HashSet<>();
String loc = fProfile.getLocation();
IPath location = null;
if (loc != null) {
location = new Path(loc);
// check if the location is a file
if (location.toFile().isDirectory()) {
locations.add(location.removeTrailingSeparator().toOSString());
}
} else {
for (int i = 0; i < components.length; i++) {
if (!components[i].isSystemComponent()) {
location = new Path(components[i].getLocation()).removeLastSegments(1);
if (location.toFile().isDirectory()) {
locations.add(location.removeTrailingSeparator().toOSString());
}
}
}
}
if (locations.size() > 0) {
locationcombo.setItems(locations.toArray(new String[locations.size()]));
locationcombo.select(0);
}
} else {
// try to set the default location to be the current install
// directory
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=258969
Location location = Platform.getInstallLocation();
if (location != null) {
URL url = location.getURL();
IPath path = new Path(url.getFile()).removeTrailingSeparator();
if (path.toFile().exists()) {
locationcombo.add(path.toOSString());
locationcombo.select(0);
}
}
}
} finally {
initializing = false;
}
}
/**
* Reloads all of the plugins from the location specified in the location
* text field.
*/
protected void doReload() {
String location = locationcombo.getText().trim();
IRunnableWithProgress op = new ReloadOperation(nametext.getText().trim(), location);
try {
getContainer().run(true, true, op);
// If no name has been set, the directories' name is taken as name.
if (nametext.getText().isEmpty()) {
nametext.setText(Paths.get(location).getFileName() == null ? "" //$NON-NLS-1$
: String.valueOf(Paths.get(location).getFileName()));
}
treeviewer.setInput(getCurrentComponents());
treeviewer.refresh();
setPageComplete(pageValid());
nametext.setFocus();
nametext.selectAll();
} catch (InvocationTargetException | InterruptedException ie) {
}
}
@Override
public IApiBaseline finish() throws IOException, CoreException {
if (fProfile != null) {
fProfile.setName(nametext.getText().trim());
}
return fProfile;
}
/**
* @return if the page is valid, such that it is considered complete and can
* be 'finished'
*/
protected boolean pageValid() {
if (initializing) {
return false;
}
setErrorMessage(null);
if (!isNameValid(nametext.getText().trim())) {
return false;
}
String text = locationcombo.getText().trim();
String updatedText = getUpdatedLocationForMacOS(text);
if (!updatedText.equals(text)) {
locationcombo.setText(updatedText);
}
if (text.length() < 1) {
setErrorMessage(WizardMessages.ApiProfileWizardPage_23);
reloadbutton.setEnabled(false);
return false;
}
if (!new Path(text).toFile().exists()) {
setErrorMessage(WizardMessages.ApiProfileWizardPage_24);
reloadbutton.setEnabled(false);
return false;
}
if (fProfile != null) {
if (fProfile.getApiComponents().length == 0) {
setErrorMessage(WizardMessages.ApiProfileWizardPage_2);
return false;
}
IStatus status = fProfile.getExecutionEnvironmentStatus();
if (status.getSeverity() == IStatus.ERROR) {
setErrorMessage(status.getMessage());
return false;
}
if (fProfile.getLocation() != null && !fProfile.getLocation().equals(locationcombo.getText())) {
setErrorMessage(WizardMessages.ApiProfileWizardPage_location_needs_reset);
return false;
}
} else {
setErrorMessage(WizardMessages.ApiProfileWizardPage_location_needs_reset);
return false;
}
return true;
}
/**
* Updates the state of a variety of buttons on this page
*/
protected void updateButtons() {
String loctext = locationcombo.getText().trim();
reloadbutton.setEnabled(loctext.length() > 0);
}
}