/*******************************************************************************
* Copyright (c) 2015-2017 Red Hat Inc..
* 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:
* Red Hat Incorporated - initial API and implementation
*******************************************************************************/
package org.jboss.tools.openshift.internal.ui.server;
import static org.jboss.tools.openshift.core.preferences.IOpenShiftCoreConstants.DOWNLOAD_INSTRUCTIONS_URL;
import static org.jboss.tools.openshift.core.preferences.IOpenShiftCoreConstants.OPEN_SHIFT_PREFERENCE_PAGE_ID;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.BooleanUtils;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.property.list.IListProperty;
import org.eclipse.core.databinding.property.list.MultiListProperty;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.MultiValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ViewerProperties;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.IExpansionListener;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerWorkingCopy;
import org.jboss.ide.eclipse.as.ui.editor.DeploymentTypeUIUtil.ICompletable;
import org.jboss.tools.common.ui.WizardUtils;
import org.jboss.tools.common.ui.databinding.InvertingBooleanConverter;
import org.jboss.tools.common.ui.databinding.ValueBindingBuilder;
import org.jboss.tools.foundation.ui.util.BrowserUtility;
import org.jboss.tools.openshift.common.core.OpenShiftCoreException;
import org.jboss.tools.openshift.common.core.utils.ProjectUtils;
import org.jboss.tools.openshift.common.core.utils.StringUtils;
import org.jboss.tools.openshift.common.core.utils.VariablesHelper;
import org.jboss.tools.openshift.core.connection.Connection;
import org.jboss.tools.openshift.core.server.OpenShiftServerUtils;
import org.jboss.tools.openshift.internal.common.ui.OpenShiftCommonUIMessages;
import org.jboss.tools.openshift.internal.common.ui.SelectExistingProjectDialog;
import org.jboss.tools.openshift.internal.common.ui.SelectProjectComponentBuilder;
import org.jboss.tools.openshift.internal.common.ui.databinding.FormPresenterSupport;
import org.jboss.tools.openshift.internal.common.ui.databinding.FormPresenterSupport.IFormPresenter;
import org.jboss.tools.openshift.internal.common.ui.databinding.RequiredControlDecorationUpdater;
import org.jboss.tools.openshift.internal.common.ui.utils.DataBindingUtils;
import org.jboss.tools.openshift.internal.common.ui.utils.DialogAdvancedPart;
import org.jboss.tools.openshift.internal.common.ui.utils.UIUtils;
import org.jboss.tools.openshift.internal.common.ui.wizard.AbstractOpenShiftWizardPage;
import org.jboss.tools.openshift.internal.core.preferences.OCBinary;
import org.jboss.tools.openshift.internal.core.util.ResourceUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIMessages;
import org.jboss.tools.openshift.internal.ui.comparators.ProjectViewerComparator;
import org.jboss.tools.openshift.internal.ui.dialog.SelectRouteDialog.RouteLabelProvider;
import org.jboss.tools.openshift.internal.ui.treeitem.Model2ObservableTreeItemConverter;
import org.jboss.tools.openshift.internal.ui.treeitem.ObservableTreeItem;
import org.jboss.tools.openshift.internal.ui.treeitem.ObservableTreeItem2ModelConverter;
import org.jboss.tools.openshift.internal.ui.wizard.importapp.ImportApplicationWizard;
import com.openshift.restclient.OpenShiftException;
import com.openshift.restclient.model.IBuildConfig;
import com.openshift.restclient.model.IResource;
import com.openshift.restclient.model.route.IRoute;
/**
* @author Andre Dietisheim
* @author Jeff Maury
*/
public class ServerSettingsWizardPage extends AbstractOpenShiftWizardPage implements ICompletable {
static final String IS_LOADING_SERVICES = "isLoadingServices";
protected ServerSettingsWizardPageModel model;
protected boolean needsLoadingResources = true;
protected boolean isLoadingResources = false;
protected Control uiHook = null;
/**
* Invoked from new server wizard (servers view, main menu)
*
* @param wizard the parent {@link IWizard}
* @param connection the current OpenShift {@link Connection}
*/
public ServerSettingsWizardPage(final IWizard wizard, final IServerWorkingCopy server, final Connection connection, IProject deployProject) {
this(wizard, server, connection, null, null, deployProject);
}
/**
* Invoked from OpenShift explorer
*
* @param wizard the parent {@link IWizard}
* @param server the working copy of the {@link IServer} to create
* @param connection the current OpenShift {@link Connection}
* @param resource the selected resource
*/
protected ServerSettingsWizardPage(final IWizard wizard, final IServerWorkingCopy server, final Connection connection,
final IResource resource, final IRoute route) {
this(wizard, server, connection, resource, route, null);
}
protected ServerSettingsWizardPage(final IWizard wizard, final IServerWorkingCopy server, final Connection connection,
final IResource resource, final IRoute route, final IProject deployProject) {
super("Server Settings",
"Create an OpenShift 3 Server Adapter by selecting the project, resource and folders used for file synchronization.",
"Create an OpenShift 3 Server Adapter",
wizard);
OCBinary ocBinary = OCBinary.getInstance();
boolean valid = ocBinary.isCompatibleForPublishing(new NullProgressMonitor());
this.model = new ServerSettingsWizardPageModel(resource, route, deployProject, connection, server, getOCBinaryStatus(valid, ocBinary.getLocation()));
}
/**
* @return the {@link ServerSettingsWizardPageModel} associated with this page.
*/
ServerSettingsWizardPageModel getModel() {
return model;
}
void updateServer() throws OpenShiftException {
model.updateServer();
}
/**
* Compute the error message for the OCBinary state and path.
*
* @param valid if the oc binary is valid or not
* @param location the location of the oc binary
* @return the error message (may be null)
*/
IStatus getOCBinaryStatus(boolean valid, String location) {
IStatus status = Status.OK_STATUS;
if (!valid) {
if (location == null) {
status = OpenShiftUIActivator.statusFactory().errorStatus(OpenShiftUIMessages.NoOCBinaryLocationErrorMessage);
} else if (!new File(location).exists()) {
status = OpenShiftUIActivator.statusFactory().errorStatus(NLS.bind(OpenShiftUIMessages.OCBinaryLocationDontExistsErrorMessage, location));
} else {
status = OpenShiftUIActivator.statusFactory().warningStatus(OpenShiftUIMessages.OCBinaryLocationIncompatibleErrorMessage);
}
}
return status;
}
/**
* @return a boolean flag to indicate if this page needs to load resources from OpenShift.
*/
public boolean isNeedsLoadingResources() {
return needsLoadingResources;
}
/**
* @return a boolean flag to serverSettingsWizardPageindicate if this page is currently loading resources from OpenShift.
*/
public boolean isLoadingResources() {
return isLoadingResources;
}
@Override
public void setComplete(final boolean complete) {
setPageComplete(complete);
}
@Override
protected void doCreateControls(final Composite parent, final DataBindingContext dbc) {
GridLayoutFactory.fillDefaults().numColumns(3).margins(0, 0).applyTo(parent);
createControls(parent, model, dbc);
isLoadingResources = false; //Since wizard fragment is cached and reused, this precaution is needed.
new FormPresenterSupport(
new IFormPresenter() {
@Override
public void setMessage(String message, int type) {
if (getContainer() != null) {
ServerSettingsWizardPage.this.setMessage(message, type);
}
}
@Override
public void setComplete(boolean complete) {
if (getContainer() != null) {
ServerSettingsWizardPage.this.setComplete(complete);
}
}
@Override
public Control getControl() {
return parent;
}
},
dbc);
// assuming that the wizard may be complete upon initialization
setComplete(true);
loadResources(getContainer());
}
/**
* Loads the resources for this view, does it in a blocking way.
* @param model
* @param container
*/
private void loadResources(final IWizardContainer container) {
try {
WizardUtils.runInWizard(new Job("Loading projects and services...") {
@Override
protected IStatus run(IProgressMonitor monitor) {
ServerSettingsWizardPage.this.model.loadResources();
ServerSettingsWizardPage.this.needsLoadingResources = false;
return Status.OK_STATUS;
}
}, container);
} catch (InvocationTargetException | InterruptedException e) {
// swallow intentionally
}
}
/**
* Sets the default deployment project in the wizard page, unless it is <code>null</code>
* @param project the project to select
*/
void setDeploymentProject(final IProject project) {
if (project != null) {
model.setDeployProject(project);
}
}
private final int numColumns = 4;
private Composite createControls(Composite parent, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
final Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults()
.numColumns(numColumns)
.margins(6, 6)
.applyTo(container);
GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
createInfoControls(container, model, dbc);
createProjectControls(container, model, dbc);
createResourceControls(container, model, dbc);
createAdvancedGroup(container, dbc);
uiHook = container;
return container;
}
private void createAdvancedGroup(Composite parent, DataBindingContext dbc) {
DialogAdvancedPart advancedPart = new DialogAdvancedPart() {
@Override
protected void createAdvancedContent(Composite advancedComposite) {
createSourcePathControls(advancedComposite, model, dbc);
createDeploymentControls(advancedComposite, model, dbc);
createRouteControls(advancedComposite, model, dbc);
}
@Override
protected GridLayoutFactory adjustAdvancedCompositeLayout(GridLayoutFactory gridLayoutFactory) {
return gridLayoutFactory.numColumns(numColumns).margins(0, 0);
}
};
advancedPart.createAdvancedGroup(parent, numColumns);
}
private void createProjectControls(Composite container, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
IObservableValue<IProject> eclipseProjectObservable = BeanProperties.value(
ServerSettingsWizardPageModel.PROPERTY_DEPLOYPROJECT).observe(model);
new SelectProjectComponentBuilder()
.setTextLabel("Eclipse Project: ")
.setHorisontalSpan(1)
.setEclipseProjectObservable(eclipseProjectObservable)
.setSelectionListener(onBrowseProjects(model, container.getShell()))
.build(container, dbc);
Button importButton = new Button(container, SWT.PUSH);
importButton.setText(OpenShiftCommonUIMessages.ImportButtonLabel);
GridDataFactory.fillDefaults()
.align(SWT.LEFT, SWT.CENTER)
.grab(false, false)
.applyTo(importButton);
UIUtils.setDefaultButtonWidth(importButton);
importButton.addSelectionListener(onImportProject(model, container.getShell()));
}
private void createInfoControls(Composite container, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
Composite composite = new Composite(container, SWT.NONE);
GridDataFactory.fillDefaults().span(4, 1).applyTo(composite);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
ValueBindingBuilder
.bind(WidgetProperties.visible().observe(composite))
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_OC_BINARY_STATUS).observe(model))
.converting(new Converter(IStatus.class, Boolean.class) {
@Override
public Object convert(Object fromObject) {
return !((IStatus)fromObject).isOK();
}
})
.in(dbc);
Label label = new Label(composite, SWT.NONE);
ValueBindingBuilder
.bind(WidgetProperties.image().observe(label))
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_OC_BINARY_STATUS).observe(model))
.converting(new Converter(IStatus.class, Image.class) {
@Override
public Object convert(Object fromObject) {
switch (((IStatus)fromObject).getSeverity()) {
case IStatus.WARNING:
return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
case IStatus.ERROR:
return JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR);
}
return null;
}
})
.in(dbc);
Link link = new Link(composite, SWT.WRAP);
ValueBindingBuilder
.bind(WidgetProperties.text().observe(link))
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_OC_BINARY_STATUS).observe(model))
.converting(new Converter(IStatus.class, String.class) {
@Override
public Object convert(Object fromObject) {
return ((IStatus)fromObject).getMessage();
}
})
.in(dbc);
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if ("download".equals(e.text)) {
new BrowserUtility().checkedCreateExternalBrowser(DOWNLOAD_INSTRUCTIONS_URL,
OpenShiftUIActivator.PLUGIN_ID,
OpenShiftUIActivator.getDefault().getLog());
} else {
int rc = PreferencesUtil.createPreferenceDialogOn(getShell(),
OPEN_SHIFT_PREFERENCE_PAGE_ID,
new String[] {OPEN_SHIFT_PREFERENCE_PAGE_ID},
null).open();
if (rc == Dialog.OK) {
new Job("Checking oc binary") {
@Override
protected IStatus run(IProgressMonitor monitor) {
OCBinary ocBinary = OCBinary.getInstance();
boolean valid = ocBinary.isCompatibleForPublishing(monitor);
ServerSettingsWizardPage.this.model.setOCBinaryStatus(getOCBinaryStatus(valid, ocBinary.getLocation()));
return Status.OK_STATUS;
}
}.schedule();
}
}
}
});
GridDataFactory.fillDefaults().hint(600, SWT.DEFAULT).applyTo(link);
MultiValidator validator = new MultiValidator() {
@Override
protected IStatus validate() {
IObservableValue<IStatus> observable = BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_OC_BINARY_STATUS).observe(model);
Status status = (Status)observable.getValue();
switch (status.getSeverity()) {
case IStatus.ERROR:
return OpenShiftUIActivator.statusFactory().errorStatus(OpenShiftUIMessages.OCBinaryErrorMessage);
case IStatus.WARNING:
return OpenShiftUIActivator.statusFactory().warningStatus(OpenShiftUIMessages.OCBinaryWarningMessage);
}
return status;
}
};
dbc.addValidationStatusProvider(validator);
}
private IDoubleClickListener onDoubleClickService() {
return new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
if (getWizard().canFinish()) {
Button finishButton = getShell().getDefaultButton();
UIUtils.clickButton(finishButton);
}
}
};
}
/**
* Open a dialog box to select an open project when clicking on the 'Browse' button.
*
* @return
*/
private SelectionListener onBrowseProjects(ServerSettingsWizardPageModel model, final Shell shell) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
SelectExistingProjectDialog dialog =
new SelectExistingProjectDialog("Select a project to deploy", shell);
if(model.getDeployProject() != null) {
dialog.setInitialSelections(new Object[]{model.getDeployProject()});
}
if (dialog.open() == Dialog.OK) {
Object selectedProject = dialog.getFirstResult();
if (selectedProject instanceof IProject) {
model.setDeployProject((org.eclipse.core.resources.IProject) selectedProject);
}
}
}
};
}
/**
* Open a dialog box to import an Eclipse project when clicking on the 'Import' button.
*
* @return
*/
private SelectionListener onImportProject(ServerSettingsWizardPageModel model, final Shell shell) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Map<com.openshift.restclient.model.IProject, Collection<IBuildConfig>> projectsAndBuildConfigs = new HashMap<>();
List<IBuildConfig> buildConfigs = new ArrayList<>();
ResourceUtils.getBuildConfigFor(model.getResource(), buildConfigs);
projectsAndBuildConfigs.put(model.getResource().getProject(), buildConfigs);
ImportApplicationWizard wizard = new ImportApplicationWizard(projectsAndBuildConfigs);
final boolean done = WizardUtils.openWizardDialog(wizard, shell);
if (done) {
model.setDeployProject(ResourcesPlugin.getWorkspace().getRoot().getProject(wizard.getModel().getRepoName()));
}
}
};
}
private void createSourcePathControls(Composite container, ServerSettingsWizardPageModel model,
DataBindingContext dbc) {
Label sourcePathLabel = new Label(container, SWT.NONE);
sourcePathLabel.setText("Source Path: ");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(sourcePathLabel);
Text sourcePathText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER).grab(true, false)
.applyTo(sourcePathText);
Binding sourcePathBinding = ValueBindingBuilder
.bind(WidgetProperties.text(SWT.Modify).observe(sourcePathText))
.validatingAfterConvert(new IValidator() {
@Override
public IStatus validate(Object value) {
String path = (String) value;
if (StringUtils.isEmpty(path)) {
return ValidationStatus.cancel("Please provide a local path to deploy from.");
}
String provideValidPathMessage = "Please provide a valid local path to deploy from.";
try {
path = VariablesHelper.replaceVariables(path);
} catch (OpenShiftCoreException e) {
String message = org.apache.commons.lang.StringUtils.substringAfter(e.getMessage(), "Exception:");
return ValidationStatus.error(provideValidPathMessage + "\nError: " + message);
}
if (!isReadableFile(path)) {
return ValidationStatus.error(provideValidPathMessage);
}
return ValidationStatus.ok();
}
private boolean isReadableFile(String path) {
return new File(path).canRead();
}
})
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_SOURCE_PATH).observe(model))
.in(dbc);
ControlDecorationSupport.create(
sourcePathBinding, SWT.LEFT | SWT.TOP, null, new RequiredControlDecorationUpdater(true));
Button browseSourceButton = new Button(container, SWT.PUSH);
browseSourceButton.setText("Browse...");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(browseSourceButton);
browseSourceButton.addSelectionListener(onBrowseSource(browseSourceButton.getShell()));
Button browseWorkspaceSourceButton = new Button(container, SWT.PUSH | SWT.READ_ONLY);
browseWorkspaceSourceButton.setText("Workspace...");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(browseWorkspaceSourceButton);
browseWorkspaceSourceButton.addSelectionListener(onBrowseWorkspace(browseWorkspaceSourceButton.getShell()));
UIUtils.setEqualButtonWidth(browseSourceButton, browseWorkspaceSourceButton);
}
private SelectionAdapter onBrowseSource(final Shell shell) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DirectoryDialog dialog = new DirectoryDialog(shell, SWT.OPEN);
dialog.setText("Choose the source path to sync");
String sourcePath = VariablesHelper.replaceVariables(model.getSourcePath(), true);
dialog.setFilterPath(sourcePath);
String filepath = dialog.open();
if (!StringUtils.isEmpty(filepath)) {
model.setSourcePath(filepath);
}
}
};
}
private SelectionAdapter onBrowseWorkspace(final Shell shell) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String sourcePath = getWorkspaceRelativePath(model.getSourcePath());
ElementTreeSelectionDialog dialog = createWorkspaceFolderDialog(shell, sourcePath);
if (dialog.open() == IDialogConstants.OK_ID
&& dialog.getFirstResult() instanceof IContainer) {
String path = ((IContainer) dialog.getFirstResult()).getFullPath().toString();
String folderPath = VariablesHelper.addWorkspacePrefix(path);
model.setSourcePath(folderPath);
}
}
};
}
private String getWorkspaceRelativePath(String sourcePath) {
if (org.apache.commons.lang.StringUtils.isBlank(sourcePath) || sourcePath.contains("{")) {
return sourcePath;
}
IPath absolutePath = new Path(sourcePath);
IContainer container = ResourcesPlugin.getWorkspace().getRoot().getContainerForLocation(absolutePath);
if (container != null) {
return container.getFullPath().toString();
}
return null;
}
private ElementTreeSelectionDialog createWorkspaceFolderDialog(Shell shell, String selectedFile) {
ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(
shell,
new WorkbenchLabelProvider(),
new WorkbenchContentProvider()
);
dialog.setTitle("Select a workspace folder");
dialog.setMessage("Select a workspace folder to deploy");
dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
dialog.addFilter(new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (!(element instanceof IContainer)) {
return false;
}
IContainer container = (IContainer) element;
return container.isAccessible()
&& !ProjectUtils.isInternalPde(container.getName())
&& !ProjectUtils.isInternalRSE(container.getName());
}
});
dialog.setAllowMultiple(false);
org.eclipse.core.resources.IResource res = model.getDeployProject();
if (org.apache.commons.lang.StringUtils.isNotBlank(selectedFile)) {
String path = VariablesHelper.getWorkspacePath(selectedFile);
org.eclipse.core.resources.IResource member = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (member != null) {
res = member;
}
}
if (res != null) {
dialog.setInitialSelection(res);
}
return dialog;
}
private void createDeploymentControls(Composite container, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
Button useInferredPodPathButton = new Button(container, SWT.CHECK);
useInferredPodPathButton.setText("&Use inferred Pod Deployment Path");
GridDataFactory.fillDefaults()
.span(4,1).align(SWT.FILL, SWT.CENTER)
.applyTo(useInferredPodPathButton);
ISWTObservableValue useInferredPodPathObservable = WidgetProperties.selection().observe(useInferredPodPathButton);
ValueBindingBuilder
.bind(useInferredPodPathObservable)
.to(BeanProperties.value(OpenShiftServerEditorModel.PROPERTY_USE_INFERRED_POD_PATH).observe(model))
.in(dbc);
Label podPathLabel = new Label(container, SWT.NONE);
podPathLabel.setText("Pod Deployment Path: ");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(podPathLabel);
Text podPathText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults()
.span(3, 1).align(SWT.FILL, SWT.CENTER).grab(true, false)
.applyTo(podPathText);
ISWTObservableValue podPathObservable = WidgetProperties.text(SWT.Modify).observe(podPathText);
ValueBindingBuilder
.bind(WidgetProperties.enabled().observe(podPathText))
.notUpdatingParticipant()
.to(useInferredPodPathObservable)
.converting(new InvertingBooleanConverter())
.in(dbc);
ValueBindingBuilder
.bind(WidgetProperties.enabled().observe(podPathLabel))
.notUpdatingParticipant()
.to(useInferredPodPathObservable)
.converting(new InvertingBooleanConverter())
.in(dbc);
ValueBindingBuilder
.bind(podPathObservable)
.to(BeanProperties.value(OpenShiftServerEditorModel.PROPERTY_POD_PATH).observe(model))
.in(dbc);
PodPathValidator podPathValidator = new PodPathValidator(useInferredPodPathObservable, podPathObservable);
ControlDecorationSupport.create(
podPathValidator, SWT.LEFT | SWT.TOP, null, new RequiredControlDecorationUpdater(true));
dbc.addValidationStatusProvider(podPathValidator);
}
private static class PodPathValidator extends MultiValidator {
private IObservableValue<Boolean> useDefaultPodPath;
private IObservableValue<String> podPath;
public PodPathValidator(IObservableValue<Boolean> useDefaultPodPath, IObservableValue<String> podPath) {
this.useDefaultPodPath = useDefaultPodPath;
this.podPath = podPath;
}
@Override
protected IStatus validate() {
if (BooleanUtils.isFalse((Boolean) useDefaultPodPath.getValue())) {
if (StringUtils.isEmpty(podPath.getValue())) {
return ValidationStatus.cancel("Please provide a path to deploy to on the pod.");
}
if (!Path.isValidPosixPath((String) podPath.getValue())) {
return ValidationStatus.error("You have to choose a path on the pod that route that will be used for this server adapter.");
}
}
return ValidationStatus.ok();
}
}
private void createResourceControls(Composite container, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
Group resourcesGroup = new Group(container, SWT.NONE);
resourcesGroup.setText("Resources");
GridDataFactory.fillDefaults()
.span(4, 1).align(SWT.FILL, SWT.FILL).grab(true, true)
.applyTo(resourcesGroup);
GridLayoutFactory.fillDefaults()
.numColumns(2).margins(10,10)
.applyTo(resourcesGroup);
Label selectorLabel = new Label(resourcesGroup, SWT.NONE);
selectorLabel.setText("Selector:");
Text selectorText = UIUtils.createSearchText(resourcesGroup);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(selectorText);
final TreeViewer resourcesViewer = createResourcesTreeViewer(resourcesGroup, model, selectorText);
resourcesViewer.addDoubleClickListener(onDoubleClickService());
IObservableList resourceItemsObservable = BeanProperties.list(ServerSettingsWizardPageModel.PROPERTY_RESOURCE_ITEMS).observe(model);
DataBindingUtils.addDisposableListChangeListener(
onResourceItemsChanged(resourcesViewer), resourceItemsObservable, resourcesViewer.getTree());
GridDataFactory.fillDefaults()
.span(2, 1).align(SWT.FILL, SWT.FILL).hint(SWT.DEFAULT, 160).grab(true, true)
.applyTo(resourcesViewer.getControl());
selectorText.addModifyListener(onFilterTextModified(resourcesViewer));
IViewerObservableValue selectedResourceTreeItem = ViewerProperties.singleSelection().observe(resourcesViewer);
ValueBindingBuilder
.bind(selectedResourceTreeItem)
.converting(new ObservableTreeItem2ModelConverter(IResource.class))
.validatingAfterConvert(new IValidator() {
@Override
public IStatus validate(Object value) {
if ((value instanceof IResource) && OpenShiftServerUtils.isAllowedForServerAdapter((IResource) value)) {
return ValidationStatus.ok();
}
return ValidationStatus.cancel("Please select a resource that this adapter will be bound to.");
}
})
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_RESOURCE).observe(model))
.converting(new Model2ObservableTreeItemConverter(new ServerSettingsWizardPageModel.ResourceTreeItemsFactory()))
.in(dbc);
// details
ExpandableComposite expandable = new ExpandableComposite(resourcesGroup, SWT.None);
GridDataFactory.fillDefaults()
.span(2, 1).align(SWT.FILL, SWT.FILL).grab(true, false).hint(SWT.DEFAULT, 150)
.applyTo(expandable);
expandable.setText("Resource Details");
expandable.setExpanded(true);
GridLayoutFactory.fillDefaults().numColumns(2).margins(0, 0).spacing(0, 0).applyTo(expandable);
GridDataFactory.fillDefaults()
.span(2, 1).align(SWT.FILL, SWT.FILL).grab(true, false).hint(SWT.DEFAULT, 150)
.applyTo(expandable);
Composite detailsContainer = new Composite(expandable, SWT.NONE);
GridDataFactory.fillDefaults()
.span(2, 1).align(SWT.FILL, SWT.FILL).grab(true, false).hint(SWT.DEFAULT, 150)
.applyTo(detailsContainer);
IObservableValue<IResource> selectedResource = new WritableValue<IResource>();
ValueBindingBuilder
.bind(selectedResourceTreeItem)
.converting(new ObservableTreeItem2ModelConverter())
.to(selectedResource)
.notUpdatingParticipant()
.in(dbc);
new ResourceDetailViews(selectedResource, detailsContainer, dbc).createControls();
expandable.setClient(detailsContainer);
expandable.addExpansionListener(new IExpansionListener() {
@Override
public void expansionStateChanging(ExpansionEvent e) {
}
@Override
public void expansionStateChanged(ExpansionEvent e) {
resourcesGroup.update();
resourcesGroup.layout(true);
}
});
}
private void createRouteControls(Composite container, ServerSettingsWizardPageModel model, DataBindingContext dbc) {
Group routeGroup = new Group(container, SWT.NONE);
routeGroup.setText("Route");
GridDataFactory.fillDefaults()
.span(4, 1).align(SWT.FILL, SWT.FILL).grab(true, false)
.applyTo(routeGroup);
GridLayoutFactory.fillDefaults()
.applyTo(routeGroup);
// additional nesting required because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=478618
Composite routeContainer = new Composite(routeGroup, SWT.None);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL).grab(true, true)
.applyTo(routeContainer);
GridLayoutFactory.fillDefaults()
.margins(10,10).numColumns(2)
.applyTo(routeContainer);
Button promptRouteButton = new Button(routeContainer, SWT.CHECK);
promptRouteButton.setSelection(true);
promptRouteButton.setText("Prompt for route when multiple routes available to show in browser");
GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).span(2, 1).applyTo(promptRouteButton);
Label routeLabel = new Label(routeContainer, SWT.NONE);
routeLabel.setText("Use Route: ");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(routeLabel);
StructuredViewer routesViewer = new ComboViewer(routeContainer);
GridDataFactory.fillDefaults()
.span(1,1).align(SWT.FILL, SWT.CENTER).grab(true, false)
.applyTo(routesViewer.getControl());
routesViewer.setContentProvider(new ObservableListContentProvider());
routesViewer.setLabelProvider(new RouteLabelProvider());
routesViewer.setInput(
BeanProperties.list(ServerSettingsWizardPageModel.PROPERTY_ROUTES).observe(model));
// routesViewer.setComparer(new IElementComparer() {
//
// @Override
// public boolean equals(Object object1, Object object2) {
// if (object1 instanceof IRoute) {
// if (!(object2 instanceof IRoute)) {
// return false;
// }
//
// IRoute route1 = (IRoute) object1;
// IRoute route2 = (IRoute) object2;
//
// return Objects.equals(route1.getServiceName(), route2.getServiceName())
// && Objects.equals(route1.getURL(), route2.getURL());
// } else if (object2 instanceof IRoute) {
// return false;
// } else {
// return Objects.equals(object1, object2);
// }
// }
//
// @Override
// public int hashCode(Object element) {
// if (element instanceof IRoute) {
// IRoute route = (IRoute) element;
// return new HashCodeBuilder()
// .append(route.getServiceName())
// .append(route.getURL())
// .toHashCode();
// }
// return element.hashCode();
// }
// });
IObservableValue<IResource> selectedRouteObservable = ViewerProperties.singleSelection().observe(routesViewer);
ValueBindingBuilder
.bind(selectedRouteObservable)
.to(BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_ROUTE).observe(model))
.in(dbc);
final IObservableValue<Boolean> isSelectDefaultRouteObservable =
WidgetProperties.selection().observe(promptRouteButton);
final IObservableValue<Boolean> selectDefaultRouteModelObservable =
BeanProperties.value(ServerSettingsWizardPageModel.PROPERTY_SELECT_DEFAULT_ROUTE).observe(model);
ValueBindingBuilder
.bind(isSelectDefaultRouteObservable)
.converting(new InvertingBooleanConverter())
.to(selectDefaultRouteModelObservable)
.converting(new InvertingBooleanConverter())
.in(dbc);
ValueBindingBuilder
.bind(WidgetProperties.enabled().observe(routesViewer.getControl()))
.notUpdating(selectDefaultRouteModelObservable)
.in(dbc);
ValueBindingBuilder
.bind(WidgetProperties.enabled().observe(routeLabel))
.notUpdating(selectDefaultRouteModelObservable)
.in(dbc);
RouteValidator routeValidator = new RouteValidator(isSelectDefaultRouteObservable, selectedRouteObservable);
dbc.addValidationStatusProvider(routeValidator);
ControlDecorationSupport.create(routeValidator, SWT.LEFT | SWT.TOP, null, new RequiredControlDecorationUpdater(true));
}
private IListChangeListener<ObservableTreeItem> onResourceItemsChanged(final TreeViewer resourcesViewer) {
return new IListChangeListener<ObservableTreeItem>() {
@Override
public void handleListChange(ListChangeEvent<? extends ObservableTreeItem> event) {
resourcesViewer.expandAll();
}
};
}
private TreeViewer createResourcesTreeViewer(Composite parent, ServerSettingsWizardPageModel model, Text selectorText) {
TreeViewer applicationTemplatesViewer =
new TreeViewer(parent, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
IListProperty<ServerSettingsWizardPageModel, ObservableTreeItem> childrenProperty = new MultiListProperty<ServerSettingsWizardPageModel, ObservableTreeItem>(
new IListProperty[] {
BeanProperties.list(ServerSettingsWizardPageModel.PROPERTY_RESOURCE_ITEMS),
BeanProperties.list(ObservableTreeItem.PROPERTY_CHILDREN) });
ObservableListTreeContentProvider contentProvider =
new ObservableListTreeContentProvider(childrenProperty.listFactory(), null);
applicationTemplatesViewer.setContentProvider(contentProvider);
applicationTemplatesViewer.setLabelProvider(new ResourcesViewLabelProvider());
applicationTemplatesViewer.addFilter(new ServiceViewerFilter(selectorText));
applicationTemplatesViewer.setComparator(ProjectViewerComparator.createProjectTreeSorter());
applicationTemplatesViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
applicationTemplatesViewer.setInput(model);
return applicationTemplatesViewer;
}
protected ModifyListener onFilterTextModified(final TreeViewer applicationTemplatesViewer) {
return new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
applicationTemplatesViewer.refresh();
applicationTemplatesViewer.expandAll();
}
};
}
@Override
public void dispose() {
super.dispose();
uiHook = null;
model.dispose();
}
@Override
public boolean isPageComplete() {
return !isLoadingResources
&& uiHook != null && !uiHook.isDisposed()
&& !needsLoadingResources
&& model != null && model.getResource() != null
&& super.isPageComplete();
}
public IServer saveServer(IProgressMonitor monitor) throws CoreException {
model.updateServer();
return model.saveServer(monitor);
}
class RouteValidator extends MultiValidator {
private IObservableValue<Boolean> useDefaultRoute;
private IObservableValue<IResource> selectedRoute;
public RouteValidator(IObservableValue<Boolean> useDefaultRoute, IObservableValue<IResource> selectedRoute) {
this.useDefaultRoute = useDefaultRoute;
this.selectedRoute = selectedRoute;
}
@Override
protected IStatus validate() {
if (BooleanUtils.isFalse((Boolean) useDefaultRoute.getValue())) {
if (selectedRoute.getValue() == null) {
return ValidationStatus.cancel("You have to choose a route that will be used for this server adapter.");
}
}
return ValidationStatus.ok();
}
}
}