/******************************************************************************* * Copyright (c) 2015 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 * * based on org.eclipse.jface.databinding.wizard.WizardPageSupport * *******************************************************************************/ package org.jboss.tools.openshift.internal.common.ui.databinding; import java.util.Collection; import java.util.Iterator; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.ValidationStatusProvider; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.IStaleListener; import org.eclipse.core.databinding.observable.ObservableTracker; import org.eclipse.core.databinding.observable.StaleEvent; 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.list.ListDiff; import org.eclipse.core.databinding.observable.list.ListDiffEntry; import org.eclipse.core.databinding.observable.value.ComputedValue; 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.util.Policy; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.databinding.dialog.IValidationMessageProvider; import org.eclipse.jface.databinding.dialog.ValidationMessageProvider; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; /** * Connects the validation result from the given data binding context to the * given wizard fragment, updating the page's error message accordingly. * * @since 1.3 */ public class FormPresenterSupport { private IFormPresenter formPresenter; private DataBindingContext dbc; private IValidationMessageProvider messageProvider = new ValidationMessageProvider(); private IObservableValue aggregateStatusProvider; private boolean uiChanged = false; private IChangeListener uiChangeListener = new IChangeListener() { @Override public void handleChange(ChangeEvent event) { handleUIChanged(); } }; private IListChangeListener validationStatusProvidersListener = new IListChangeListener() { @Override public void handleListChange(ListChangeEvent event) { ListDiff diff = event.diff; ListDiffEntry[] differences = diff.getDifferences(); for (int i = 0; i < differences.length; i++) { ListDiffEntry listDiffEntry = differences[i]; ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) listDiffEntry .getElement(); IObservableList targets = validationStatusProvider.getTargets(); if (listDiffEntry.isAddition()) { targets .addListChangeListener(validationStatusProviderTargetsListener); for (Iterator it = targets.iterator(); it.hasNext();) { ((IObservable) it.next()) .addChangeListener(uiChangeListener); } } else { targets .removeListChangeListener(validationStatusProviderTargetsListener); for (Iterator it = targets.iterator(); it.hasNext();) { ((IObservable) it.next()) .removeChangeListener(uiChangeListener); } } } } }; private IListChangeListener validationStatusProviderTargetsListener = new IListChangeListener() { @Override public void handleListChange(ListChangeEvent event) { ListDiff diff = event.diff; ListDiffEntry[] differences = diff.getDifferences(); for (int i = 0; i < differences.length; i++) { ListDiffEntry listDiffEntry = differences[i]; IObservable target = (IObservable) listDiffEntry.getElement(); if (listDiffEntry.isAddition()) { target.addChangeListener(uiChangeListener); } else { target.removeChangeListener(uiChangeListener); } } } }; private ValidationStatusProvider currentStatusProvider; protected IStatus currentStatus; protected boolean currentStatusStale; /** * @param formPresenter * @param dbc * @noreference This constructor is not intended to be referenced by * clients. */ public FormPresenterSupport(IFormPresenter formPresenter, DataBindingContext dbc) { this.formPresenter = formPresenter; this.dbc = dbc; init(); } /** * Sets the {@link IValidationMessageProvider} to use for providing the * message text and message type to display on the dialog page. * * @param messageProvider * The {@link IValidationMessageProvider} to use for providing * the message text and message type to display on the dialog * page. * * @since 1.4 */ public void setValidationMessageProvider( IValidationMessageProvider messageProvider) { this.messageProvider = messageProvider; handleStatusChanged(); } /** * @noreference This method is not intended to be referenced by clients. */ protected void init() { ObservableTracker.setIgnore(true); try { aggregateStatusProvider = new MaxSeverityValidationStatusProvider(dbc); } finally { ObservableTracker.setIgnore(false); } aggregateStatusProvider .addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { statusProviderChanged(); } }); formPresenter.getControl().addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { dispose(); } }); aggregateStatusProvider.addStaleListener(new IStaleListener() { @Override public void handleStale(StaleEvent staleEvent) { currentStatusStale = true; handleStatusChanged(); } }); statusProviderChanged(); dbc.getValidationStatusProviders().addListChangeListener( validationStatusProvidersListener); for (Iterator it = dbc.getValidationStatusProviders().iterator(); it .hasNext();) { ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it .next(); IObservableList targets = validationStatusProvider.getTargets(); targets .addListChangeListener(validationStatusProviderTargetsListener); for (Iterator iter = targets.iterator(); iter.hasNext();) { ((IObservable) iter.next()).addChangeListener(uiChangeListener); } } } private void statusProviderChanged() { currentStatusProvider = (ValidationStatusProvider) aggregateStatusProvider .getValue(); if (currentStatusProvider != null) { currentStatus = (IStatus) currentStatusProvider .getValidationStatus().getValue(); } else { currentStatus = null; } currentStatusStale = aggregateStatusProvider.isStale(); handleStatusChanged(); } /** * @noreference This method is not intended to be referenced by clients. */ protected void handleUIChanged() { uiChanged = true; if (currentStatus != null) { handleStatusChanged(); } dbc.getValidationStatusProviders().removeListChangeListener( validationStatusProvidersListener); for (Iterator it = dbc.getValidationStatusProviders().iterator(); it .hasNext();) { ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it .next(); IObservableList targets = validationStatusProvider.getTargets(); targets .removeListChangeListener(validationStatusProviderTargetsListener); for (Iterator iter = targets.iterator(); iter.hasNext();) { ((IObservable) iter.next()) .removeChangeListener(uiChangeListener); } } } /** * @noreference This method is not intended to be referenced by clients. */ protected void handleStatusChanged() { handleMessage(); handleComplete(); } private void handleComplete() { boolean pageComplete = true; if (currentStatusStale) { pageComplete = false; } else if (currentStatus != null) { pageComplete = !currentStatus.matches(IStatus.ERROR | IStatus.CANCEL); } formPresenter.setComplete(pageComplete); } private void handleMessage() { String message = messageProvider.getMessage(currentStatusProvider); int type = messageProvider.getMessageType(currentStatusProvider); if (type == IMessageProvider.ERROR) { formPresenter.setMessage(uiChanged ? message : null, IMessageProvider.ERROR); if (currentStatus != null && currentStatusHasException()) { handleStatusException(); } } else { formPresenter.setMessage(message, type); } } private boolean currentStatusHasException() { boolean hasException = false; if (currentStatus.getException() != null) { hasException = true; } if (currentStatus instanceof MultiStatus) { MultiStatus multiStatus = (MultiStatus) currentStatus; for (int i = 0; i < multiStatus.getChildren().length; i++) { IStatus status = multiStatus.getChildren()[i]; if (status.getException() != null) { hasException = true; break; } } } return hasException; } /** * @noreference This method is not intended to be referenced by clients. */ protected void handleStatusException() { if (currentStatus.getException() != null) { logThrowable(currentStatus.getException()); } else if (currentStatus instanceof MultiStatus) { MultiStatus multiStatus = (MultiStatus) currentStatus; for (int i = 0; i < multiStatus.getChildren().length; i++) { IStatus status = multiStatus.getChildren()[i]; if (status.getException() != null) { logThrowable(status.getException()); } } } } private void logThrowable(Throwable throwable) { Policy .getLog() .log( new Status( IStatus.ERROR, Policy.JFACE_DATABINDING, IStatus.OK, "Unhandled exception: " + throwable.getMessage(), throwable)); //$NON-NLS-1$ } /** * Disposes of this wizard page support object, removing any listeners it * may have attached. */ public void dispose() { if (aggregateStatusProvider != null) aggregateStatusProvider.dispose(); if (dbc != null && !uiChanged) { for (Iterator it = dbc.getValidationStatusProviders().iterator(); it .hasNext();) { ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it .next(); IObservableList targets = validationStatusProvider.getTargets(); targets .removeListChangeListener(validationStatusProviderTargetsListener); for (Iterator iter = targets.iterator(); iter.hasNext();) { ((IObservable) iter.next()) .removeChangeListener(uiChangeListener); } } dbc.getValidationStatusProviders().removeListChangeListener( validationStatusProvidersListener); } aggregateStatusProvider = null; dbc = null; uiChangeListener = null; validationStatusProvidersListener = null; validationStatusProviderTargetsListener = null; formPresenter = null; } public interface IFormPresenter { public void setMessage(String message, int type); public void setComplete(boolean complete); public Control getControl(); } class MaxSeverityValidationStatusProvider extends ComputedValue { private Collection validationStatusProviders; public MaxSeverityValidationStatusProvider(DataBindingContext dbc) { super(ValidationStatusProvider.class); this.validationStatusProviders = dbc.getValidationStatusProviders(); } @Override protected Object calculate() { int maxSeverity = IStatus.OK; ValidationStatusProvider maxSeverityProvider = null; for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) { ValidationStatusProvider provider = (ValidationStatusProvider) it .next(); IStatus status = (IStatus) provider.getValidationStatus() .getValue(); if (status.getSeverity() > maxSeverity) { maxSeverity = status.getSeverity(); maxSeverityProvider = provider; } } return maxSeverityProvider; } @Override public synchronized void dispose() { validationStatusProviders = null; super.dispose(); } } }