/*******************************************************************************
* Copyright (c) 2015-2016 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.openshift.internal.ui.wizard.newapp;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.apache.commons.lang.StringUtils;
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.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
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.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
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.WidgetProperties;
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.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.PageChangingEvent;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
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.jface.wizard.IWizard;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
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.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.progress.UIJob;
import org.jboss.tools.common.ui.WizardUtils;
import org.jboss.tools.common.ui.databinding.DataBindingUtils;
import org.jboss.tools.common.ui.databinding.ParametrizableWizardPageSupport;
import org.jboss.tools.common.ui.databinding.ValueBindingBuilder;
import org.jboss.tools.openshift.common.core.utils.ProjectUtils;
import org.jboss.tools.openshift.common.core.utils.VariablesHelper;
import org.jboss.tools.openshift.core.server.adapter.ProjectBuilderTypeDetector;
import org.jboss.tools.openshift.egit.core.EGitUtils;
import org.jboss.tools.openshift.egit.ui.util.EGitUIUtils;
import org.jboss.tools.openshift.internal.common.core.job.JobChainBuilder;
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.RequiredControlDecorationUpdater;
import org.jboss.tools.openshift.internal.common.ui.databinding.TabFolderSelectionProperty;
import org.jboss.tools.openshift.internal.common.ui.utils.UIUtils;
import org.jboss.tools.openshift.internal.core.util.ResourceUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
import org.jboss.tools.openshift.internal.ui.OpenshiftUIConstants;
import org.jboss.tools.openshift.internal.ui.dialog.ResourceSummaryDialog;
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.treeitem.ObservableTreeItemStyledCellLabelProvider;
import org.jboss.tools.openshift.internal.ui.wizard.common.AbstractProjectPage;
import com.openshift.restclient.OpenShiftException;
import com.openshift.restclient.ResourceKind;
import com.openshift.restclient.UnsupportedVersionException;
import com.openshift.restclient.model.template.ITemplate;
/**
* A page that offers a list of builder images or templates to a user
*
* @author jeff.cantrill
* @author Andre Dietisheim
* @author Jeff Maury
*
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class ApplicationSourceListPage extends AbstractProjectPage<IApplicationSourceListPageModel> {
private static final int LOCAL_TEMPLATE_TAB_INDEX = 1;
public static final String PAGE_NAME = "appSourceList";
private TreeViewer templatesViewer;
public ApplicationSourceListPage(IWizard wizard, IApplicationSourceListPageModel model) {
super(wizard, model, "Select template",
"Server template choices may be filtered by typing the name of a tag in the text field.",
"templateList");
}
@Override
protected void doCreateControls(Composite parent, DataBindingContext dbc) {
super.doCreateControls(parent, dbc);
IObservableValue selectedEclipseProject = createEclipseProjectControls(parent, dbc);
TabFolder tabContainer= new TabFolder(parent, SWT.NONE);
GridDataFactory.fillDefaults()
.span(3, 1)
.align(SWT.FILL, SWT.CENTER)
.grab(true, false)
.applyTo(tabContainer);
tabContainer.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
// JBIDE-21072: force re-layout of the parent upon tab switching
parent.layout(true, false);
}
});
IObservableValue useLocalTemplateObservable =
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_USE_LOCAL_APP_SOURCE).observe(model);
ValueBindingBuilder
.bind(new TabFolderSelectionProperty().observe(tabContainer))
.converting(new Converter(Integer.class, Boolean.class) {
@Override
public Object convert(Object fromObject) {
return Integer.valueOf(LOCAL_TEMPLATE_TAB_INDEX).equals(fromObject);
}
})
.to(useLocalTemplateObservable).converting(new Converter(Boolean.class, Integer.class) {
@Override
public Object convert(Object fromObject) {
return (fromObject != null && (Boolean) fromObject) ? LOCAL_TEMPLATE_TAB_INDEX : 0;
}
})
.in(dbc);
TabFolderTraverseListener tabFolderTraverseListener = new TabFolderTraverseListener(tabContainer);
IObservableValue serverTemplate =
createServerTemplateControls(tabContainer, tabFolderTraverseListener, useLocalTemplateObservable, dbc);
IObservableValue localTemplateFilename =
createLocalTemplateControls(tabContainer, tabFolderTraverseListener, useLocalTemplateObservable, dbc);
dbc.addValidationStatusProvider(new MultiValidator() {
@Override
protected IStatus validate() {
return (IStatus) BeanProperties.value(
IApplicationSourceListPageModel.PROPERTY_APP_SOURCE_STATUS, IStatus.class).observe(model).getValue();
}
});
createDetailsGroup(parent, dbc);
model.setUseLocalAppSource(false);
// validate required template
IObservableValue selectedTemplate =
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_SELECTED_APP_SOURCE).observe(model);
TemplateListPageValidator pageValidator =
new TemplateListPageValidator(useLocalTemplateObservable, localTemplateFilename, serverTemplate, selectedTemplate,
selectedEclipseProject, parent);
dbc.addValidationStatusProvider(pageValidator );
ControlDecorationSupport.create(pageValidator, SWT.LEFT | SWT.TOP, null, new RequiredControlDecorationUpdater(true));
ProjectNameValidator projectNameValidator = new ProjectNameValidator(selectedEclipseProject, parent);
dbc.addValidationStatusProvider(projectNameValidator);
ControlDecorationSupport.create(projectNameValidator, SWT.LEFT | SWT.TOP, null, new RequiredControlDecorationUpdater(true));
}
private IObservableValue createEclipseProjectControls(Composite parent, DataBindingContext dbc) {
IObservableValue eclipseProjectObservable =
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_ECLIPSE_PROJECT).observe(model);
SelectProjectComponentBuilder builder = new SelectProjectComponentBuilder();
builder
.setTextLabel("Use existing workspace project:")
.setHorisontalSpan(1)
.setRequired(false)
.setEclipseProjectObservable(eclipseProjectObservable)
.setSelectionListener(onBrowseProjects())
.build(parent, dbc);
Link gitLabel = new Link(parent, SWT.NONE);
gitLabel.setText("The project needs to be <a>shared with Git</a> and have a remote repository accessible by OpenShift");
gitLabel.addSelectionListener(onClickEGitLink());
GridDataFactory.fillDefaults().span(3, 1).applyTo(gitLabel);
DataBindingUtils.addDisposableValueChangeListener(
new IValueChangeListener() {
@Override
public void handleValueChange(ValueChangeEvent event) {
org.eclipse.core.resources.IProject p =
(org.eclipse.core.resources.IProject) event.getObservableValue().getValue();
toggleEgitLink(gitLabel, p);
}
}, eclipseProjectObservable, gitLabel);
toggleEgitLink(gitLabel, model.getEclipseProject());
return builder.getProjectNameTextObservable();
}
private SelectionAdapter onClickEGitLink() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Display.getCurrent().asyncExec(new Runnable() {
@Override
public void run() {
org.eclipse.core.resources.IProject p = model.getEclipseProject();
model.setEclipseProject(null);
EGitUIUtils.openGitSharingWizard(Display.getCurrent().getActiveShell(), p);
model.setEclipseProject(p);//force re-validation
}
});
}
};
}
/**
* Open a dialog box to select an open project when clicking on the 'Browse' button.
*
* @return
*/
private SelectionListener onBrowseProjects() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
SelectExistingProjectDialog dialog =
new SelectExistingProjectDialog(model.getEclipseProject() == null?
"Select an existing workspace project from the list below."
: NLS.bind("Currently project {0} is selected, you may pick a different one from the list below.",
model.getEclipseProject().getName())
, getShell());
if (model.getEclipseProject() != null) {
dialog.setInitialSelections(new Object[]{model.getEclipseProject()});
}
if (dialog.open() == Dialog.OK) {
Object selectedProject = dialog.getFirstResult();
model.setEclipseProject(((org.eclipse.core.resources.IProject) selectedProject));
}
}
};
}
private IObservableValue createLocalTemplateControls(TabFolder tabContainer, TabFolderTraverseListener tabFolderTraverseListener, IObservableValue useLocalTemplate, DataBindingContext dbc) {
TabItem localTemplatesTab = new TabItem(tabContainer, SWT.NONE);
localTemplatesTab.setText("Custom template");
Composite parent = new Composite(tabContainer, SWT.NONE);
GridLayoutFactory.fillDefaults()
.numColumns(3).margins(10, 10).spacing(6, 2)
.applyTo(parent);
Label lbl = new Label(parent, SWT.NONE);
lbl.setText("Select a local template file or a full URL:");
GridDataFactory.fillDefaults().span(3,1).applyTo(lbl);
// local template file name
Text txtLocalTemplateFileName = new Text(parent, SWT.BORDER);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER).grab(true, false)
.span(3, 1)
.applyTo(txtLocalTemplateFileName);
IObservableValue localTemplateFilename = WidgetProperties.text(SWT.Modify).observe(txtLocalTemplateFileName);
IValidator validator = (o -> {
IStatus status = ValidationStatus.ok();
if (!OpenshiftUIConstants.URL_VALIDATOR.isValid(o.toString())
&& StringUtils.isNotBlank(o.toString())
&& !isFile(o.toString())) {
status = ValidationStatus.error(o +" is not a file");
}
return status;
});
ValueBindingBuilder
.bind(localTemplateFilename)
.validatingBeforeSet(validator)
.to(BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_LOCAL_APP_SOURCE_FILENAME).observe(model))
.in(dbc);
// browse button
Button btnBrowseFiles = new Button(parent, SWT.NONE);
btnBrowseFiles.setText("Browse File System...");
GridDataFactory.fillDefaults()
.align(SWT.END, SWT.CENTER)
.span(2,1)
.grab(true, false)
.applyTo(btnBrowseFiles);
btnBrowseFiles.addSelectionListener(onFileSystemBrowseClicked());
// browse button
Button btnBrowseWorkspaceFiles = new Button(parent, SWT.NONE);
btnBrowseWorkspaceFiles.setText("Browse Workspace...");
GridDataFactory.fillDefaults()
.align(SWT.END, SWT.CENTER)
.applyTo(btnBrowseWorkspaceFiles);
btnBrowseWorkspaceFiles.addSelectionListener(onBrowseWorkspaceClicked());
UIUtils.setEqualButtonWidth(btnBrowseFiles, btnBrowseWorkspaceFiles);
localTemplatesTab.setControl(parent);
tabFolderTraverseListener.bindTabControls(tabContainer.getItemCount() - 1,
txtLocalTemplateFileName, btnBrowseFiles, btnBrowseWorkspaceFiles);
return localTemplateFilename;
}
private SelectionListener onBrowseWorkspaceClicked() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ElementTreeSelectionDialog dialog = UIUtils.createFileDialog(model.getLocalAppSourceFileName(),
"Select an OpenShift template",
"Select an OpenShift template (*.json)",
"json",
model.getEclipseProject());
if (dialog.open() == IDialogConstants.OK_ID && dialog.getFirstResult() instanceof IFile) {
String path = ((IFile)dialog.getFirstResult()).getFullPath().toString();
String file = VariablesHelper.addWorkspacePrefix(path);
setLocalTemplate(file);
}
}
};
}
private void setLocalTemplate(String file) {
if (file == null || !isFile(file)) {
return;
}
try {
model.setLocalAppSourceFileName(file);
return;
} catch (NotATemplateException ex) {
MessageDialog.openWarning(getShell(), "Template Error",
NLS.bind("The file \"{0}\" is not an OpenShift template. It contains a resource of type {1} instead.",
file, ex.getResourceKind()));
} catch (ClassCastException ex) {
//should not happen due to NotATemplateException.
IStatus status = ValidationStatus.error(ex.getMessage(), ex);
OpenShiftUIActivator.getDefault().getLogger().logStatus(status);
ErrorDialog.openError(getShell(), "Template Error",
NLS.bind("The file \"{0}\" is not an OpenShift template.",
file),
status);
} catch (UnsupportedVersionException ex) {
IStatus status = ValidationStatus.error(ex.getMessage(), ex);
OpenShiftUIActivator.getDefault().getLogger().logStatus(status);
ErrorDialog.openError(getShell(), "Template Error",
NLS.bind("The file \"{0}\" is a template in a version that we do not support.", file),
status);
} catch (OpenShiftException ex) {
IStatus status = ValidationStatus.error(ex.getMessage(), ex);
OpenShiftUIActivator.getDefault().getLogger().logStatus(status);
ErrorDialog.openError(getShell(), "Template Error",
NLS.bind("Unable to read and/or parse the file \"{0}\" as a template.", file),
status);
}
}
private IObservableValue createServerTemplateControls(TabFolder tabFolder, TabFolderTraverseListener tabFolderTraverseListener, IObservableValue uploadTemplate, DataBindingContext dbc) {
TabItem serverTemplatesTab = new TabItem(tabFolder, SWT.NONE);
serverTemplatesTab.setText("Server application source");
Composite parent = new Composite(tabFolder, SWT.NONE);
GridLayoutFactory.fillDefaults()
.margins(10, 6).spacing(2, 2)
.applyTo(parent);
serverTemplatesTab.setControl(parent);
// filter text
final Text txtTemplateFilter = UIUtils.createSearchText(parent);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.applyTo(txtTemplateFilter);
IObservableValue eclipseProjectObservable =
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_ECLIPSE_PROJECT).observe(model);
DataBindingUtils.addDisposableValueChangeListener(new IValueChangeListener() {
@Override
public void handleValueChange(ValueChangeEvent event) {
filterTemplates(txtTemplateFilter, (org.eclipse.core.resources.IProject) event.getObservableValue().getValue());
}
}, eclipseProjectObservable, txtTemplateFilter);
filterTemplates(txtTemplateFilter, model.getEclipseProject());
// the list of templates
this.templatesViewer = createServerTemplatesViewer(parent, txtTemplateFilter);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL).grab(true, true).hint(400, 180)
.applyTo(templatesViewer.getControl());
IObservableValue selectedViewerServerTemplate =
ViewerProperties.singleSelection().observe(templatesViewer);
ValueBindingBuilder
.bind(selectedViewerServerTemplate)
.converting(new ObservableTreeItem2ModelConverter(IApplicationSource.class))
.to(BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_SERVER_APP_SOURCE).observe(model))
.converting(new Model2ObservableTreeItemConverter(ApplicationSourceTreeItems.INSTANCE))
.in(dbc);
templatesViewer.addDoubleClickListener(onServerTemplateDoubleClicked());
txtTemplateFilter.addModifyListener(onFilterTextTyped(templatesViewer));
tabFolderTraverseListener.bindTabControls(
tabFolder.getItemCount() - 1, txtTemplateFilter, templatesViewer.getTree());
return selectedViewerServerTemplate;
}
protected String findMatchingTags(org.eclipse.core.resources.IProject project) {
return new ProjectBuilderTypeDetector().findTemplateFilter(project);
}
private void createDetailsGroup(Composite parent, DataBindingContext dbc) {
// details
Group detailsGroup = new Group(parent, SWT.NONE);
detailsGroup.setText("Details");
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL).grab(true, false).span(3, 1).hint(SWT.DEFAULT, 106)
.applyTo(detailsGroup);
GridLayoutFactory.fillDefaults()
.margins(10, 6).spacing(2, 2) //TODO fix margins
.applyTo(detailsGroup);
Composite detailsContainer = new Composite(detailsGroup, SWT.NONE);
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.FILL).grab(true, true)
.applyTo(detailsContainer);
new ApplicationSourceDetailViews(
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_SELECTED_APP_SOURCE).observe(model), null, detailsContainer, dbc)
.createControls();
// detail resources button
Button btnDetails = new Button(detailsGroup, SWT.NONE);
btnDetails.setText("Defined Resources...");
GridDataFactory.fillDefaults()
.align(SWT.RIGHT, SWT.CENTER)
.applyTo(btnDetails);
IObservableValue selectedTemplate =
BeanProperties.value(IApplicationSourceListPageModel.PROPERTY_SELECTED_APP_SOURCE).observe(model);
ValueBindingBuilder
.bind(WidgetProperties.visible().observe(btnDetails))
.notUpdatingParticipant()
.to(selectedTemplate)
.converting(new Converter(Object.class, Boolean.class) {
@Override
public Object convert(Object fromObject) {
return fromObject != null
&& ResourceKind.TEMPLATE.equals(((IApplicationSource)fromObject).getSource().getKind());
}
})
.in(dbc);
btnDetails.addSelectionListener(onDefinedResourcesClicked());
}
private IDoubleClickListener onServerTemplateDoubleClicked() {
return new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
if (hasApplicationSource(selection.getFirstElement())
&& canFlipToNextPage()) {
getContainer().showPage(getNextPage());
}
}
};
}
private TreeViewer createServerTemplatesViewer(Composite parent, final Text templateFilterText) {
TreeViewer viewer = new TreeViewer(parent, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
IListProperty childrenProperty = new MultiListProperty(
new IListProperty[] {
BeanProperties.list(IApplicationSourceListPageModel.PROPERTY_APP_SOURCES),
BeanProperties.list(ObservableTreeItem.PROPERTY_CHILDREN) });
ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
childrenProperty.listFactory(), null);
viewer.setContentProvider(contentProvider);
viewer.setLabelProvider(new ObservableTreeItemStyledCellLabelProvider());
viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
viewer.setComparator(new ApplicationSourceComparator());
viewer.addFilter(new AppSourceViewerFilter(templateFilterText));
templateFilterText.addKeyListener(onFilterKeyPressed(viewer));
viewer.setInput(model);
return viewer;
}
private static class AppSourceViewerFilter extends ViewerFilter {
private Text filterText;
public AppSourceViewerFilter(Text filterText) {
this.filterText = filterText;
}
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (!(element instanceof ObservableTreeItem)
|| !(((ObservableTreeItem) element).getModel() instanceof IApplicationSource)) {
return true;
}
IApplicationSource appSource = (IApplicationSource) ((ObservableTreeItem) element).getModel();
return ResourceUtils.isMatching(filterText.getText(), appSource.getName(), appSource.getTags());
}
}
private SelectionAdapter onFileSystemBrowseClicked() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
FileDialog dialog = createFileDialog(model.getLocalAppSourceFileName());
String file = dialog.open();
setLocalTemplate(file);
}
private FileDialog createFileDialog(String selectedFile) {
FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
dialog.setText("Select an OpenShift template");
if(isFile(selectedFile)) {
File file = new File(selectedFile);
dialog.setFilterPath(file.getParentFile().getAbsolutePath());
}
return dialog;
}
};
}
private SelectionAdapter onDefinedResourcesClicked() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ITemplate template = (ITemplate) model.getSelectedAppSource().getSource();
new ResourceSummaryDialog(getShell(),
template.getObjects(),
"Template Details",
NLS.bind("The following resources will be created by using template\n\"{0}\":", template.getName()),
new ResourceDetailsLabelProvider(), new ResourceDetailsContentProvider()).open();
}
};
}
private ModifyListener onFilterTextTyped(final TreeViewer viewer) {
return new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
viewer.refresh();
viewer.expandAll();
}
};
}
/**
* Navigates templates with arrows up/down while focus remains on the filter text.
*
* @param viewer
* @return
*/
private KeyListener onFilterKeyPressed(final TreeViewer viewer) {
return new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.ARROW_UP) {
TreeItem[] items = viewer.getTree().getItems();
if (items == null
|| items.length == 0) {
return;
}
TreeItem[] s = viewer.getTree().getSelection();
TreeItem next = null;
if (s == null || s.length == 0) {
next = (e.keyCode == SWT.ARROW_DOWN)? items[0] : items[items.length - 1];
} else {
for (int i = 0; i < items.length && next == null; i++) {
if (items[i] == s[0]) {
if (e.keyCode == SWT.ARROW_DOWN) {
if (i + 1 < items.length) {
next = items[i + 1];
}
} else {
if (i > 0) {
next = items[i - 1];
}
}
}
}
}
if (next != null) {
viewer.setSelection(new StructuredSelection(next.getData()), true);
}
e.doit = false;
}
}
};
}
@Override
protected JobChainBuilder getLoadResourcesJobBuilder(boolean[] closeAfter, boolean closeOnCancel) {
JobChainBuilder builder = super.getLoadResourcesJobBuilder(closeAfter, closeOnCancel);
builder.runWhenSuccessfullyDone(new UIJob("Expanding resource tree...") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
templatesViewer.expandAll();
return Status.OK_STATUS;
}
});
return builder;
}
public static class ApplicationSourceComparator extends ViewerComparator {
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
if (hasApplicationSource(e1) && hasApplicationSource(e2)) {
String t1 = ((IApplicationSource)((ObservableTreeItem)e1).getModel()).getName();
String t2 = ((IApplicationSource)((ObservableTreeItem)e2).getModel()).getName();
return t1.compareTo(t2);
}
return super.compare(viewer, e1, e2);
}
}
private static boolean hasApplicationSource(Object item) {
return item instanceof IApplicationSource
|| item instanceof ObservableTreeItem
&& ((ObservableTreeItem)item).getModel() instanceof IApplicationSource;
}
/**
* A validator that validates this page based on the choice to use a local
* or server template and the settings that are required therefore.
*/
private class TemplateListPageValidator extends MultiValidator {
private IObservableValue useLocalTemplateObservable;
private IObservableValue localTemplateFilenameObservable;
private IObservableValue serverTemplateObservable;
private IObservableValue selectedTemplateObservable;
private IObservableValue projectNameObservable;
private IObservableList mutableTargets = new WritableList<>();
private Composite composite;
public TemplateListPageValidator(IObservableValue useLocalTemplate, IObservableValue localTemplateFilename,
IObservableValue serverTemplate, IObservableValue selectedTemplate, IObservableValue projectNameObservable, Composite composite) {
this.useLocalTemplateObservable = useLocalTemplate;
useLocalTemplate.getValue();
this.localTemplateFilenameObservable = localTemplateFilename;
this.serverTemplateObservable = serverTemplate;
this.selectedTemplateObservable = selectedTemplate;
this.projectNameObservable = projectNameObservable;
this.composite = composite;
}
@Override
protected IStatus validate() {
IStatus status = ValidationStatus.ok();
mutableTargets.clear();
projectNameObservable.getValue();
Object useLocalTemplate = useLocalTemplateObservable.getValue();
Object localTemplateFilename = localTemplateFilenameObservable.getValue();
serverTemplateObservable.getValue();
Object selectedTemplate = selectedTemplateObservable.getValue();
if(status.getSeverity() < IStatus.ERROR) {
if (Boolean.TRUE.equals(useLocalTemplate)) {
String localTemplate = (String)localTemplateFilename;
if (StringUtils.isNotBlank(localTemplate)) {
if (!OpenshiftUIConstants.URL_VALIDATOR.isValid(localTemplate) && !isFile(localTemplate)) {
status = ValidationStatus.error(NLS.bind("{0} is not a valid file.", localTemplate));
mutableTargets.add(localTemplateFilenameObservable);
}
} else {
status = ValidationStatus.cancel("Please select a local template file or URL.");
mutableTargets.add(localTemplateFilenameObservable);
}
} else {
if (selectedTemplate == null){
status = ValidationStatus.cancel("Please select an image or template.");
mutableTargets.add(serverTemplateObservable);
}
}
}
// force redraw since removed decorations somehow stay visible, GTK3 bug?
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=478618
composite.redraw();
return status;
}
@Override
public IObservableList getTargets() {
return mutableTargets;
}
}
class ProjectNameValidator extends MultiValidator {
private IObservableValue projectNameObservable;
private IObservableList mutableTargets = new WritableList<>();
private Composite composite;
public ProjectNameValidator(IObservableValue projectNameObservable, Composite composite) {
this.projectNameObservable = projectNameObservable;
this.composite = composite;
}
protected IStatus validate() {
IStatus status = ValidationStatus.ok();
final String projectName = (String) projectNameObservable.getValue();
if (!StringUtils.isEmpty(projectName)) {
final org.eclipse.core.resources.IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (!ProjectUtils.exists(project)) {
status = ValidationStatus.error(
NLS.bind("The project {0} does not exist in your workspace.", projectName));
} else if (!ProjectUtils.isAccessible(project)) {
status = ValidationStatus.error(
NLS.bind("The project {0} is not open.", projectName));
} else if (EGitUtils.isSharedWithGit(project)){
try {
List<String> repos = EGitUtils.getRemoteGitRepos(project);
if (repos == null || repos.isEmpty()) {
status = ValidationStatus.warning(
NLS.bind("A remote Git repository using the HTTP(S) protocol must be defined on project {0}", projectName));
}
} catch (CoreException e) {
status = ValidationStatus.error(
NLS.bind("Can not read Git config on project {0} : {1}", projectName, e.getMessage()));
}
} else {
status = ValidationStatus.error(
NLS.bind("The project {0} is not shared with Git.", projectName));
}
}
if (!status.isOK()) {
mutableTargets.add(projectNameObservable);
}
composite.redraw();
return status;
}
}
private void filterTemplates(Text text, org.eclipse.core.resources.IProject project) {
String tags = findMatchingTags(project);
if (tags != null && !text.isDisposed()) {
text.setText(tags);
}
}
private void toggleEgitLink(Link gitLabel, org.eclipse.core.resources.IProject p) {
if (gitLabel.isDisposed()) {
return;
}
boolean showLink = p != null && !EGitUtils.isSharedWithGit(p);
UIUtils.setVisibleAndExclude(showLink, gitLabel);
}
@Override
protected void onPageWillGetDeactivated(Direction progress, PageChangingEvent event, DataBindingContext dbc) {
if (model.isUseLocalAppSource()) {
String filename = model.getLocalAppSourceFileName();
if (StringUtils.isNotBlank(filename)) {
Job job = new Job(NLS.bind("Loading application source", filename)) {
@Override
protected IStatus run(IProgressMonitor monitor) {
model.loadAppSource(monitor);
return Status.OK_STATUS;
}
};
try {
WizardUtils.runInWizard(job, getContainer(), getDatabindingContext());
event.doit = model.getAppSourceStatus().isOK();
} catch (InvocationTargetException | InterruptedException e) {
OpenShiftUIActivator.getDefault().getLogger().logError(e);
event.doit = false;
}
} else {
model.resetLocalAppSource();
event.doit = false;
}
}
}
@Override
protected void setupWizardPageSupport(DataBindingContext dbc) {
ParametrizableWizardPageSupport.create(
IStatus.ERROR | IStatus.INFO | IStatus.CANCEL, this,
dbc);
}
}