/*******************************************************************************
* Copyright (c) 2012, 2014 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License,
* Version 2.0 (the "License�); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
********************************************************************************/
package org.cloudfoundry.ide.eclipse.server.ui.internal.wizards;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.cloudfoundry.client.lib.domain.CloudService;
import org.cloudfoundry.client.lib.domain.CloudServiceOffering;
import org.cloudfoundry.client.lib.domain.CloudServicePlan;
import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryServer;
import org.cloudfoundry.ide.eclipse.server.core.internal.client.LocalCloudService;
import org.cloudfoundry.ide.eclipse.server.ui.internal.CloudFoundryImages;
import org.cloudfoundry.ide.eclipse.server.ui.internal.Messages;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.Observables;
import org.eclipse.core.databinding.observable.map.WritableMap;
import org.eclipse.core.databinding.observable.value.WritableValue;
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.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.wizard.WizardPage;
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.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.statushandlers.StatusManager;
public class CloudFoundryServicePlanWizardPage extends WizardPage {
protected DataBindingContext bindingContext;
private final CloudFoundryServer cloudServer;
private List<CloudServiceOffering> serviceOfferings;
protected WritableMap map;
private Text nameText;
/**
* The data model.
*/
protected LocalCloudService service;
private Composite planDetailsComposite;
protected Group planGroup;
private PageBook pageBook;
private WritableValue planObservable = new WritableValue();
private Combo typeCombo;
private Pattern VALID_CHARS = Pattern.compile("[A-Za-z\\$_0-9\\-]+"); //$NON-NLS-1$
protected CloudFoundryServicePlanWizardPage(CloudFoundryServer cloudServer) {
super(Messages.CloudFoundryServicePlanWizardPage_TEXT_SERVICE_PAGE);
this.cloudServer = cloudServer;
setTitle(Messages.CloudFoundryServicePlanWizardPage_TITLE_SERVICE_CONFIG);
setDescription(Messages.CloudFoundryServicePlanWizardPage_TEXT_FINISH_ADD);
ImageDescriptor banner = CloudFoundryImages.getWizardBanner(cloudServer.getServer().getServerType().getId());
if (banner != null) {
setImageDescriptor(banner);
}
this.service = createService();
}
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(2).spacing(10, LayoutConstants.getSpacing().y).applyTo(composite);
Label label = new Label(composite, SWT.NONE);
label.setText(Messages.COMMONTXT_NAME_WITH_COLON);
nameText = new Text(composite, SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).applyTo(nameText);
nameText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
service.setName(nameText.getText());
}
});
bindingContext = new DataBindingContext();
map = new WritableMap();
WizardPageSupport.create(this, bindingContext);
bindingContext.bindValue(SWTObservables.observeText(nameText, SWT.Modify),
Observables.observeMapEntry(map, "name"), //$NON-NLS-1$
new UpdateValueStrategy().setAfterConvertValidator(new StringValidator()), null);
label = new Label(composite, SWT.NONE);
label.setText(Messages.CloudFoundryServicePlanWizardPage_LABEL_TYPE);
typeCombo = new Combo(composite, SWT.READ_ONLY | SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).applyTo(typeCombo);
typeCombo.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
int index = typeCombo.getSelectionIndex();
if (index != -1) {
CloudServiceOffering configuration = serviceOfferings.get(index);
setCloudService(service, configuration);
}
refreshPlan();
}
});
bindingContext.bindValue(SWTObservables.observeSelection(typeCombo), Observables.observeMapEntry(map, "type"), //$NON-NLS-1$
new UpdateValueStrategy().setAfterConvertValidator(new ComboValidator(Messages.CloudFoundryServicePlanWizardPage_TEXT_SELECT_TYPE)), null);
pageBook = new PageBook(composite, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(pageBook);
planGroup = new Group(pageBook, SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, true).applyTo(planGroup);
planGroup.setLayout(new GridLayout());
planGroup.setVisible(false);
planGroup.setText(getPlanLabel());
MultiValidator validator = new MultiValidator() {
protected IStatus validate() {
// access plan value to bind validator
if (planObservable.getValue() == null) {
return ValidationStatus.cancel(getValidationErrorMessage());
}
return ValidationStatus.ok();
}
};
bindingContext.addValidationStatusProvider(validator);
Dialog.applyDialogFont(composite);
setControl(composite);
}
public LocalCloudService getService() {
return service;
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible && serviceOfferings == null) {
// delay until dialog is actually visible
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (!getControl().isDisposed()) {
refresh();
}
}
});
}
}
protected boolean supportsSpaces() {
return cloudServer != null && cloudServer.hasCloudSpace();
}
protected void refresh() {
if (updateConfiguration()) {
typeCombo.removeAll();
for (CloudServiceOffering offering : serviceOfferings) {
String label = offering.getLabel() != null ? offering.getLabel() + " - " //$NON-NLS-1$
+ offering.getDescription() : offering.getDescription();
typeCombo.add(label);
}
refreshPlan();
}
}
protected void refreshPlan() {
int index = typeCombo.getSelectionIndex();
if (index == -1) {
pageBook.setVisible(false);
planGroup.setVisible(false);
// re-validate
planObservable.setValue(null);
}
else {
pageBook.setVisible(true);
for (Control control : planGroup.getChildren()) {
control.dispose();
}
CloudServiceOffering configuration = serviceOfferings.get(index);
List<CloudServicePlan> servicePlans = getPlans(configuration);
if (servicePlans.size() > 1) {
pageBook.showPage(planGroup);
planGroup.setVisible(true);
Button defaultPlanControl = null;
for (CloudServicePlan plan : servicePlans) {
String planLabelText = plan.getName();
Button planButton = new Button(planGroup, SWT.RADIO);
if (defaultPlanControl == null) {
defaultPlanControl = planButton;
}
planButton.setText(planLabelText);
planButton.setData(plan);
planButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
Button button = (Button) event.widget;
if (button.getSelection()) {
CloudServicePlan plan = (CloudServicePlan) button.getData();
setPlan(plan);
}
}
});
}
planDetailsComposite = new Composite(planGroup, SWT.NONE);
GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 10, 0).numColumns(2)
.applyTo(planDetailsComposite);
// Set a default plan, if one exists
if (defaultPlanControl != null) {
defaultPlanControl.setSelection(true);
CloudServicePlan plan = (CloudServicePlan) defaultPlanControl.getData();
setPlan(plan);
}
}
else if (servicePlans.size() == 1) {
planGroup.setVisible(false);
CloudServicePlan plan = servicePlans.get(0);
setPlan(plan);
}
else {
pageBook.setVisible(false);
}
}
((Composite) getControl()).layout(true, true);
}
protected void setPlan(CloudServicePlan plan) {
getService().setPlan(plan.getName());
// re-validate
planObservable.setValue(plan);
}
protected boolean updateConfiguration() {
try {
getContainer().run(true, false, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
serviceOfferings = cloudServer.getBehaviour().getServiceOfferings(monitor);
Collections.sort(serviceOfferings, new Comparator<CloudServiceOffering>() {
public int compare(CloudServiceOffering o1, CloudServiceOffering o2) {
return o1.getDescription().compareTo(o2.getDescription());
}
});
sortServicePlans(serviceOfferings);
}
catch (CoreException e) {
throw new InvocationTargetException(e);
}
catch (OperationCanceledException e) {
throw new InterruptedException();
}
finally {
monitor.done();
}
}
});
return true;
}
catch (InvocationTargetException e) {
IStatus status = cloudServer.error(
NLS.bind(Messages.CloudFoundryServicePlanWizardPage_ERROR_CONFIG_RETRIVE, e.getCause().getMessage()), e);
StatusManager.getManager().handle(status, StatusManager.LOG);
setMessage(status.getMessage(), IMessageProvider.ERROR);
}
catch (InterruptedException e) {
// ignore
}
return false;
}
protected class ComboValidator implements IValidator {
private final String message;
public ComboValidator(String message) {
this.message = message;
}
public IStatus validate(Object value) {
if (value instanceof String && ((String) value).length() > 0) {
return Status.OK_STATUS;
}
return ValidationStatus.cancel(message);
}
}
protected class StringValidator implements IValidator {
public IStatus validate(Object value) {
if (value instanceof String) {
if (((String) value).length() == 0) {
return ValidationStatus.cancel(Messages.CloudFoundryServicePlanWizardPage_TEXT_ENTER_NAME);
}
Matcher matcher = VALID_CHARS.matcher((String) value);
if (!matcher.matches()) {
return ValidationStatus.error(Messages.CloudFoundryServicePlanWizardPage_ERROR_INVALID_CHAR);
}
}
return Status.OK_STATUS;
}
}
protected void sortServicePlans(List<CloudServiceOffering> configurations) {
for (CloudServiceOffering offering : configurations) {
Collections.sort(offering.getCloudServicePlans(), new Comparator<CloudServicePlan>() {
public int compare(CloudServicePlan o1, CloudServicePlan o2) {
return o1.getName().compareTo(o2.getName());
}
});
}
}
protected List<CloudServicePlan> getPlans(CloudServiceOffering offering) {
List<CloudServicePlan> plans = new ArrayList<CloudServicePlan>();
List<CloudServicePlan> cloudPlans = offering.getCloudServicePlans();
if (cloudPlans != null) {
for (CloudServicePlan plan : cloudPlans) {
plans.add(plan);
}
}
return plans;
}
protected String getValidationErrorMessage() {
return Messages.CloudFoundryServicePlanWizardPage_ERROR_SELECT_PLAN;
}
protected String getPlanLabel() {
return Messages.CloudFoundryServicePlanWizardPage_LABEL_PLAN;
}
protected void setCloudService(CloudService service, CloudServiceOffering offering) {
service.setVersion(offering.getVersion());
service.setLabel(offering.getLabel());
service.setProvider(offering.getProvider());
}
protected LocalCloudService createService() {
LocalCloudService service = new LocalCloudService(""); //$NON-NLS-1$
return service;
}
}