blob: 0086f511a474f1411ffccc06d954a0314e0ea1ab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
*
* 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:
* Eugen - initial API and implementation
* Christian W. Damus - bugs 533522, 543160
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.validation;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.common.spi.UniqueSetting;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeAddRemoveListener;
import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
import org.eclipse.emf.ecp.view.spi.model.VControl;
import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReferenceSegment;
import org.eclipse.emf.ecp.view.spi.model.VElement;
import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
import org.eclipse.emf.ecp.view.spi.validation.ValidationProvider;
import org.eclipse.emf.ecp.view.spi.validation.ValidationService;
import org.eclipse.emf.ecp.view.spi.validation.ViewValidationListener;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emfforms.common.internal.validation.DiagnosticHelper;
import org.eclipse.emfforms.common.spi.validation.ValidationResultListener;
import org.eclipse.emfforms.common.spi.validation.filter.AbstractSimpleFilter;
import org.eclipse.emfforms.spi.common.report.AbstractReport;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.controlmapper.EMFFormsSettingToControlMapper;
import org.eclipse.emfforms.spi.core.services.controlmapper.SubControlMapper;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
import org.eclipse.emfforms.spi.core.services.mappingprovider.EMFFormsMappingProviderManager;
import org.eclipse.emfforms.spi.core.services.view.EMFFormsContextListener;
import org.eclipse.emfforms.spi.core.services.view.EMFFormsViewContext;
/**
* Validation service that, once instantiated, synchronizes the validation result of a model element with its
* Renderable.
*
* @author Eugen Neufeld
*
*/
public class ValidationServiceImpl implements ValidationService, EMFFormsContextListener {
/**
* The {@link ValidationDomainModelChangeListener} for the view model.
*
*/
private class ViewModelChangeListener implements ModelChangeAddRemoveListener {
@Override
public void notifyChange(ModelChangeNotification notification) {
if (VViewPackage.eINSTANCE.getElement_Enabled() == notification.getRawNotification()
.getFeature()
|| VViewPackage.eINSTANCE.getElement_Visible() == notification.getRawNotification()
.getFeature()) {
if (VViewPackage.eINSTANCE.getControl().isInstance(notification.getNotifier())) {
final VControl control = (VControl) notification.getNotifier();
final VDomainModelReference domainModelReference = control.getDomainModelReference();
if (domainModelReference == null) {
return;
}
try {
handleControlNotification(notification, control, domainModelReference);
} catch (final DatabindingFailedException ex) {
reportService.report(new DatabindingFailedReport(ex));
return;
}
}
}
if (!VElement.class.isInstance(notification.getNotifier())) {
return;
}
switch (notification.getRawNotification().getEventType()) {
case Notification.REMOVE:
case Notification.REMOVE_MANY:
final Map<VElement, VDiagnostic> map = Collections.emptyMap();
reevaluateToTop(notification.getNotifier(), map);
break;
default:
break;
}
}
/**
* @param notification
* @param control
* @param domainModelReference
* @throws DatabindingFailedException
*/
private void handleControlNotification(ModelChangeNotification notification, VControl control,
VDomainModelReference domainModelReference) throws DatabindingFailedException {
if (VViewPackage.eINSTANCE.getElement_Enabled() == notification.getRawNotification().getFeature()) {
control.setDiagnostic(null);
}
final Set<UniqueSetting> settingsForControl = controlMapper.getSettingsForControl(control);
final Set<EObject> eObjectsToValidate = new LinkedHashSet<EObject>();
for (final UniqueSetting setting : settingsForControl) {
eObjectsToValidate.add(setting.getEObject());
}
validate(eObjectsToValidate);
}
@Override
public void notifyAdd(Notifier notifier) {
if (VDomainModelReference.class.isInstance(notifier)
&& !VDomainModelReference.class.isInstance(EObject.class.cast(notifier).eContainer())
&& !VDomainModelReferenceSegment.class.isInstance(EObject.class.cast(notifier).eContainer())) {
final VDomainModelReference domainModelReference = VDomainModelReference.class.cast(notifier);
if (domainModelReference == null) {
return;
}
final Set<EObject> eObjectsToValidate = new LinkedHashSet<EObject>();
if (VControl.class.isInstance(domainModelReference.eContainer())) {
final Set<UniqueSetting> settings = mappingProviderManager.getAllSettingsFor(domainModelReference,
context.getDomainModel());
for (final UniqueSetting setting : settings) {
final EObject object = setting.getEObject();
if (object != null) {
eObjectsToValidate.add(object);
}
}
} else {
@SuppressWarnings("rawtypes")
IObservableValue observableValue;
try {
observableValue = context.getService(EMFFormsDatabinding.class)
.getObservableValue(domainModelReference, context.getDomainModel());
} catch (final DatabindingFailedException ex) {
reportService.report(new DatabindingFailedReport(ex));
return;
}
final EObject observed = (EObject) ((IObserving) observableValue).getObserved();
observableValue.dispose();
if (observed != null) {
eObjectsToValidate.add(observed);
}
}
validate(eObjectsToValidate);
}
}
@Override
public void notifyRemove(Notifier notifier) {
// do nothing
}
}
/**
* The {@link ValidationDomainModelChangeListener} for the domain model.
*
*/
private class ValidationDomainModelChangeListener implements ModelChangeAddRemoveListener {
@Override
public void notifyChange(ModelChangeNotification notification) {
if (ValidationNotification.class.isInstance(notification.getRawNotification())) {
validate(notification.getNotifier());
return;
}
if (isIgnore(notification)) {
return;
}
final Notification rawNotification = notification.getRawNotification();
final int eventType = rawNotification.getEventType();
// Special cases
if (eventType == Notification.ADD || eventType == Notification.ADD_MANY) {
handleAdd(notification);
return;
} else if (eventType == Notification.REMOVE || eventType == Notification.REMOVE_MANY) {
handleRemove(notification);
return;
}
// Default case
validate(notification.getNotifier());
if (EReference.class.isInstance(notification.getStructuralFeature())) {
if (notification.getRawNotification().getNewValue() != null) {
final Object newValue = notification.getRawNotification().getNewValue();
/*
* unset on a list has a boolean as a new value. therefore we need to check if new value is an
* EObject
*/
if (EObject.class.isInstance(newValue)) {
validate((EObject) newValue);
}
}
}
}
/**
* Indicates whether the given {@link ModelChangeNotification} should be ignored.
*
* @param notification the {@link ModelChangeNotification} to check.
* @return {@code true} if the given notification should be ignored, {@code false} otherwise.
*/
private boolean isIgnore(ModelChangeNotification notification) {
if (notification.getRawNotification().isTouch()) {
return true;
}
final int eventType = notification.getRawNotification().getEventType();
if (eventType == Notification.REMOVING_ADAPTER) {
return true;
}
if (eventType == Notification.SET) {
if (EReference.class.isInstance(notification.getStructuralFeature())) {
final EReference eReference = EReference.class.cast(notification.getStructuralFeature());
if (eReference.isContainer() && notification.getRawNotification().getNewValue() == null) {
return true;
}
}
}
return false;
}
/**
* Handles the case when the given {@link ModelChangeNotification} originates from an ADD.
*
* @param notification
* the {@link ModelChangeNotification} to handle.
*/
@SuppressWarnings("unchecked")
private void handleAdd(ModelChangeNotification notification) {
final Set<EObject> toValidate = new LinkedHashSet<EObject>();
toValidate.add(notification.getNotifier());
// in case of not containment references
if (EReference.class.isInstance(notification.getStructuralFeature())) {
if (Notification.ADD == notification.getRawNotification().getEventType()) {
toValidate.addAll(getAllEObjects((EObject) notification.getRawNotification().getNewValue()));
} else {
toValidate.addAll((Collection<EObject>) notification.getRawNotification().getNewValue());
}
}
validate(toValidate);
}
/**
* Handles the case when the given {@link ModelChangeNotification} originates from a REMOVE.
*
* @param notification
* the {@link ModelChangeNotification} to handle.
*/
private void handleRemove(ModelChangeNotification notification) {
final Notification rawNotification = notification.getRawNotification();
if (rawNotification.getEventType() == Notification.REMOVE
&& EReference.class.isInstance(rawNotification.getFeature())) {
cleanControlDiagnostics(EObject.class.cast(notification.getNotifier()),
EReference.class.cast(rawNotification.getFeature()),
EObject.class.cast(rawNotification.getOldValue()));
}
// TODO JF since we now have an indexed dmr, this should clean diagnostics, too, doesn't it?
validate(notification.getNotifier());
}
@Override
public void notifyAdd(Notifier notifier) {
if (notifier == context.getDomainModel()) {
validate(getAllEObjectsToValidate(context));
}
}
@Override
public void notifyRemove(Notifier notifier) {
}
}
private org.eclipse.emfforms.common.spi.validation.ValidationService validationService;
private ValidationDomainModelChangeListener domainChangeListener;
private ViewModelChangeListener viewChangeListener;
private ViewModelContext context;
private final Queue<EObject> validationQueue = new ConcurrentLinkedSetQueue<EObject>();
private final Set<EObject> validated = Collections.newSetFromMap(new ConcurrentHashMap<EObject, Boolean>());
private final AtomicBoolean validationRunning = new AtomicBoolean(false);
// In a typical application, these lists will usually have zero or one element. In
// any case, uniqueness is ensured by the validation algorithm and so needs not be
// enforced by the collection, so just use simple lists
private final Map<UniqueSetting, List<Diagnostic>> currentUpdates = new ConcurrentHashMap<UniqueSetting, List<Diagnostic>>();
private final Function<Object, List<Diagnostic>> diagnosticListFactory = __ -> new ArrayList<>(3);
private ComposedAdapterFactory adapterFactory;
private ReportService reportService;
@Override
public void instantiate(ViewModelContext context) {
this.context = context;
reportService = context.getService(ReportService.class);
mappingProviderManager = context.getService(EMFFormsMappingProviderManager.class);
controlMapper = context.getService(EMFFormsSettingToControlMapper.class);
final VElement renderable = context.getViewModel();
if (renderable == null) {
throw new IllegalStateException("View model must not be null"); //$NON-NLS-1$
}
final EObject domainModel = context.getDomainModel();
if (domainModel == null) {
throw new IllegalStateException("Domain model must not be null"); //$NON-NLS-1$
}
validationService = new org.eclipse.emfforms.common.internal.validation.ValidationServiceImpl();
validationService.registerValidationFilter(new AbstractSimpleFilter() {
@Override
public boolean skipValidation(EObject eObject) {
return validated.contains(eObject);
}
@Override
public boolean ignoreDiagnostic(EObject eObject, Diagnostic diagnostic) {
return !controlMapper.hasControlsFor(eObject);
}
});
validationService.registerValidationResultListener(new ValidationResultListener() {
@Override
public void onValidate(EObject eObject, Diagnostic diagnostic) {
validated.add(eObject);
}
@Override
public void afterValidate(EObject eObject, Diagnostic diagnostic) {
// nothing to do here
}
});
adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
new ReflectiveItemProviderAdapterFactory(),
new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
final ViewSubstitutionLabelProviderFactory substitutionLabelProviderFactory = getSubstitutionLabelProviderFactory();
SubstitutionLabelProvider substitutionLabelProvider;
if (substitutionLabelProviderFactory != null) {
substitutionLabelProvider = substitutionLabelProviderFactory
.createSubstitutionLabelProvider(adapterFactory);
} else {
substitutionLabelProvider = new ECPSubstitutionLabelProvider(adapterFactory);
}
validationService.setSubstitutionLabelProvider(substitutionLabelProvider);
registerValidationProviders();
domainChangeListener = new ValidationDomainModelChangeListener();
viewChangeListener = new ViewModelChangeListener();
context.registerDomainChangeListener(domainChangeListener);
context.registerViewChangeListener(viewChangeListener);
context.registerEMFFormsContextListener(this);
}
private void cleanControlDiagnostics(EObject parent, EReference parentReference, EObject removedEObject) {
final Set<VElement> controls = controlMapper
.getControlsFor(UniqueSetting.createSetting(parent, parentReference));
if (controls == null) {
return;
}
for (final VElement vControl : controls) {
if (vControl == null) {
continue;
}
if (vControl.getDiagnostic() == null) {
continue;
}
final Set<Object> diagnosticsToRemove = new LinkedHashSet<Object>();
for (final Object diagnosticObject : vControl.getDiagnostic().getDiagnostics()) {
final Diagnostic diagnostic = Diagnostic.class.cast(diagnosticObject);
if (diagnostic.getData().size() < 1) {
continue;
}
if (removedEObject.equals(DiagnosticHelper.getFirstInternalEObject(diagnostic.getData()))) {
diagnosticsToRemove.add(diagnostic);
}
}
vControl.getDiagnostic().getDiagnostics().removeAll(diagnosticsToRemove);
}
}
private void registerValidationProviders() {
for (final ValidationProvider provider : ValidationProviderHelper.fetchValidationProviders()) {
validationService.addValidator(provider);
}
}
@Override
public void dispose() {
context.unregisterEMFFormsContextListener(this);
context.unregisterDomainChangeListener(domainChangeListener);
context.unregisterViewChangeListener(viewChangeListener);
adapterFactory.dispose();
}
@Override
public int getPriority() {
return 1;
}
/**
* Returns a collection of all direct and indirect child-EObjects including the parent.
*
* @param eObject the parent
* @return all eobjects
*/
private Collection<EObject> getAllEObjects(EObject eObject) {
final List<EObject> result = new ArrayList<EObject>();
result.add(eObject);
final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(eObject, false);
while (iterator.hasNext()) {
result.add(iterator.next());
}
return result;
}
private Collection<EObject> getAllEObjectsToValidate(ViewModelContext context) {
return getAllEObjectsToValidate(context, controlMapper);
}
private static Collection<EObject> getAllEObjectsToValidate(ViewModelContext context,
EMFFormsSettingToControlMapper controlMapper) {
if (context.getParentContext() == null || !(controlMapper instanceof SubControlMapper)) {
// Easy: validate the whole model
return controlMapper.getEObjectsWithSettings();
}
final SubControlMapper subMapper = (SubControlMapper) controlMapper;
final Collection<EObject> result = subMapper.getEObjectsWithSettings(context.getViewModel());
// And all container objects that have settings
for (ViewModelContext parent = context.getParentContext(); parent != null; parent = parent
.getParentContext()) {
final EObject parentObject = parent.getDomainModel();
if (controlMapper.hasControlsFor(parentObject)) {
result.add(parentObject);
}
}
return result;
}
@Override
public void validate(Collection<EObject> eObjects) {
validationQueue.addAll(eObjects);
processValidationQueue();
}
/**
* Validate the given eObject.
*
* @param eObject the eObject to validate
*/
public void validate(EObject eObject) {
/**
* We are using a queue here to allow validators to add additional eObjects
* to the current validation run. This is because we actually want a diagnostics aggregate,
* otherwise consecutive runs would replace already existing diagnostics on the UI.
* This is probably not the best way to solve this problem, but it will do for now.
*/
validationQueue.offer(eObject);
processValidationQueue();
}
private void processValidationQueue() {
if (!initialized) {
return;
}
// prohibit re-entry in recursion
if (!validationRunning.compareAndSet(false, true)) {
return;
}
EObject toValidate;
while ((toValidate = validationQueue.poll()) != null) {
validateAndCollectSettings(toValidate);
}
update();
notifyListeners();
currentUpdates.clear();
validated.clear();
validationRunning.compareAndSet(true, false);
}
/**
* Notifies all listeners.
*/
public void notifyListeners() {
if (validationListeners.size() > 0) {
final Set<Diagnostic> result = getDiagnosticResult();
for (final ViewValidationListener l : validationListeners) {
l.onNewValidation(result);
}
}
}
private void update() {
// prepare Map
final Map<VElement, Set<UniqueSetting>> vElementToSettingMap = prepareVElementToSettingMap();
final Map<VElement, VDiagnostic> controlDiagnosticMap = new LinkedHashMap<VElement, VDiagnostic>();
for (final VElement control : vElementToSettingMap.keySet()) {
updateControlDiagnostics(control, vElementToSettingMap, controlDiagnosticMap);
// add all diagnostics of control which are not in the currentUpdates
if (control.getDiagnostic() == null) {
continue;
}
for (final Object diagnosticObject : control.getDiagnostic().getDiagnostics()) {
final Diagnostic diagnostic = Diagnostic.class.cast(diagnosticObject);
if (diagnostic.getData().size() < 2) {
continue;
}
final EObject diagnosticEobject = DiagnosticHelper.getFirstInternalEObject(diagnostic.getData());
final EStructuralFeature eStructuralFeature = DiagnosticHelper
.getEStructuralFeature(diagnostic.getData());
if (diagnosticEobject == null || eStructuralFeature == null) {
continue;
}
// TODO performance
if (!isObjectStillValid(diagnosticEobject)) {
continue;
}
final UniqueSetting uniqueSetting2 = UniqueSetting.createSetting(
diagnosticEobject, eStructuralFeature);
if (!currentUpdates.containsKey(uniqueSetting2)) {
controlDiagnosticMap.get(control).getDiagnostics().add(diagnosticObject);
}
}
}
updateAndPropagate(controlDiagnosticMap);
}
private void updateControlDiagnostics(final VElement control,
final Map<VElement, Set<UniqueSetting>> vElementToSettingMap,
final Map<VElement, VDiagnostic> controlDiagnosticMap) {
if (!controlDiagnosticMap.containsKey(control)) {
controlDiagnosticMap.put(control, VViewFactory.eINSTANCE.createDiagnostic());
}
for (final UniqueSetting uniqueSetting : vElementToSettingMap.get(control)) {
final List<Diagnostic> diagnostics = currentUpdates.get(uniqueSetting);
if (!diagnostics.isEmpty()) {
controlDiagnosticMap.get(control).getDiagnostics().addAll(diagnostics);
}
}
}
private Map<VElement, Set<UniqueSetting>> prepareVElementToSettingMap() {
final Map<VElement, Set<UniqueSetting>> result = new LinkedHashMap<VElement, Set<UniqueSetting>>();
final Function<VElement, Set<UniqueSetting>> setFactory = __ -> new LinkedHashSet<>();
for (final UniqueSetting uniqueSetting : currentUpdates.keySet()) {
final Set<VElement> controls = controlMapper.getControlsFor(uniqueSetting);
if (controls == null || controls.isEmpty()) {
continue;
}
for (final VElement control : controls) {
result.computeIfAbsent(control, setFactory).add(uniqueSetting);
}
}
return result;
}
private boolean isObjectStillValid(EObject diagnosticEobject) {
return controlMapper.hasControlsFor(diagnosticEobject);
}
private void updateAndPropagate(Map<VElement, VDiagnostic> controlDiagnosticMap) {
for (final Map.Entry<VElement, VDiagnostic> next : controlDiagnosticMap.entrySet()) {
next.getKey().setDiagnostic(next.getValue());
reevaluateToTop(next.getKey().eContainer(), controlDiagnosticMap);
}
}
private void reevaluateToTop(EObject parent, Map<VElement, VDiagnostic> controlDiagnosticMap) {
while (parent != null) {
final EObject newParent = parent.eContainer();
if (!VElement.class.isInstance(parent)) {
parent = newParent;
continue;
}
final VElement vElement = (VElement) parent;
final VDiagnostic vDiagnostic = VViewFactory.eINSTANCE.createDiagnostic();
if (controlDiagnosticMap.containsKey(vElement)) {
vDiagnostic.getDiagnostics().addAll(controlDiagnosticMap.get(vElement).getDiagnostics());
}
for (final EObject eObject : vElement.eContents()) {
if (!VElement.class.isInstance(eObject)) {
continue;
}
final VElement childElement = (VElement) eObject;
// check that the child is visible and enabled
if (childElement.getDiagnostic() != null && childElement.isEffectivelyEnabled()
&& childElement.isVisible()) {
vDiagnostic.getDiagnostics().addAll(childElement.getDiagnostic().getDiagnostics());
}
}
vElement.setDiagnostic(vDiagnostic);
parent = newParent;
}
}
private void validateAndCollectSettings(EObject eObject) {
final long start = System.nanoTime();
try {
final Diagnostic diagnostic = validationService.validate(eObject);
if (diagnostic == null) { // happens if the eObject is being filtered
return;
}
for (final EStructuralFeature feature : eObject.eClass().getEAllStructuralFeatures()) {
final UniqueSetting uniqueSetting = UniqueSetting.createSetting(eObject, feature);
currentUpdates.computeIfAbsent(uniqueSetting, diagnosticListFactory);
}
analyzeDiagnostic(diagnostic);
} finally {
if (System.nanoTime() - start > 1000L * 1000L * 1000L) {
reportService.report(new AbstractReport(MessageFormat.format(
"Validation took longer than expected for EObject {0}", eObject, //$NON-NLS-1$
IStatus.INFO)));
}
}
}
private void analyzeDiagnostic(Diagnostic diagnostic) {
if (diagnostic.getData().size() > 1) {
final InternalEObject internalEObject = DiagnosticHelper.getFirstInternalEObject(diagnostic.getData());
final EStructuralFeature eStructuralFeature = DiagnosticHelper.getEStructuralFeature(diagnostic.getData());
if (internalEObject == null || eStructuralFeature == null) {
return;
}
if (!internalEObject.eClass().getEAllStructuralFeatures().contains(eStructuralFeature)) {
reportService.report(new AbstractReport(
MessageFormat.format(
"No Setting can be created for Diagnostic {0} since the EObject's EClass does not contain the Feature.", //$NON-NLS-1$
diagnostic),
IStatus.INFO));
return;
}
final Setting setting = internalEObject.eSetting(eStructuralFeature);
final UniqueSetting uniqueSetting = UniqueSetting.createSetting(setting);
currentUpdates.computeIfAbsent(uniqueSetting, diagnosticListFactory).add(diagnostic);
} else {
for (final Diagnostic childDiagnostic : diagnostic.getChildren()) {
analyzeDiagnostic(childDiagnostic);
}
}
}
@Override
public void addValidationProvider(ValidationProvider validationProvider) {
addValidationProvider(validationProvider, true);
}
@Override
public void addValidationProvider(ValidationProvider validationProvider, boolean revalidate) {
validationService.addValidator(validationProvider);
if (revalidate && context != null) {
validate(getAllEObjectsToValidate(context));
}
}
@Override
public void removeValidationProvider(ValidationProvider validationProvider) {
removeValidationProvider(validationProvider, true);
}
@Override
public void removeValidationProvider(ValidationProvider validationProvider, boolean revalidate) {
validationService.removeValidator(validationProvider);
if (revalidate && context != null) {
validate(getAllEObjectsToValidate(context));
}
}
private final Set<ViewValidationListener> validationListeners = new LinkedHashSet<ViewValidationListener>();
private EMFFormsMappingProviderManager mappingProviderManager;
private EMFFormsSettingToControlMapper controlMapper;
private boolean initialized;
@Override
public void registerValidationListener(ViewValidationListener listener) {
validationListeners.add(listener);
listener.onNewValidation(getDiagnosticResult());
}
private Set<Diagnostic> getDiagnosticResult() {
final Set<Diagnostic> result = new LinkedHashSet<Diagnostic>();
final VDiagnostic diagnostic = context.getViewModel().getDiagnostic();
if (diagnostic != null) {
for (final Object diagObject : diagnostic.getDiagnostics()) {
result.add((Diagnostic) diagObject);
}
}
return result;
}
@Override
public void deregisterValidationListener(ViewValidationListener listener) {
validationListeners.remove(listener);
}
@Override
public void childViewModelContextAdded(ViewModelContext childContext) {
// do nothing
}
@Override
public void childContextAdded(VElement parentElement, EMFFormsViewContext childContext) {
// We are getting this from a parent content that is a view-model context, so the
// child really ought to be one, also
if (childContext instanceof ViewModelContext) {
validate(getAllEObjectsToValidate((ViewModelContext) childContext));
}
childContext.registerViewChangeListener(viewChangeListener);
}
@Override
public void childContextDisposed(EMFFormsViewContext childContext) {
// TODO Auto-generated method stub
}
@Override
public void contextInitialised() {
initialized = true;
validate(getAllEObjectsToValidate(context));
}
@Override
public void contextDispose() {
// do nothing
}
/**
* Returns a {@link ViewSubstitutionLabelProviderFactory}, if any is registered.
*
* @return an instance of a {@link ViewSubstitutionLabelProviderFactory}, if any is available,
* {@code null} otherwise
*/
protected ViewSubstitutionLabelProviderFactory getSubstitutionLabelProviderFactory() {
if (context.hasService(ViewSubstitutionLabelProviderFactory.class)) {
return context.getService(ViewSubstitutionLabelProviderFactory.class);
}
return null;
}
}