/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company 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: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.validators; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EValidator; import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider; import org.eclipse.emf.ecore.util.Diagnostician; import org.eclipse.emf.ecore.util.EObjectValidator; import org.eclipse.emf.ecore.util.EcoreUtil; import com.rcpcompany.uibindings.BindingMessageSeverity; import com.rcpcompany.uibindings.IBindingMessage; import com.rcpcompany.uibindings.bindingMessages.AbstractBindingMessage; import com.rcpcompany.uibindings.model.utils.BasicUtils; import com.rcpcompany.uibindings.utils.IBindingObjectInformation; import com.rcpcompany.utils.basic.ToStringUtils; import com.rcpcompany.utils.logging.LogUtils; /** * This class provides an adapter interface between the {@link EValidator} interface of Ecore and * XXX * <p> * The validator is based on {@link Diagnostician} and will only run after a certain time of * inactivity. * * @author Tonny Madsen, The RCP Company */ public class EValidatorAdapter extends AbstractValidatorAdapter { /** * The diagnostian used for the validation. */ private final Diagnostician myDiagnostician = new Diagnostician(); private final Map<Object, Object> myContextEntries = new HashMap<Object, Object>(); /** * Constructs and returns a new adapter for {@link Diagnostician} based validation. */ public EValidatorAdapter() { myContextEntries.put(SubstitutionLabelProvider.class, new MySubstitutionLabelProvider()); } @Override public void validateObjectTree(EObject root, IObservableList messages) { final Diagnostic diagnostic = myDiagnostician.validate(root, myContextEntries); final List<Message> toRemoveList = new ArrayList<Message>(messages); final List<Message> toAddList = new ArrayList<Message>(); for (final Diagnostic d : diagnostic.getChildren()) { boolean old = false; for (final Object o : messages) { final Message f = (Message) o; if (BasicUtils.equals(f.getDiagnostic(), d)) { old = true; toRemoveList.remove(f); } } if (old) { continue; } final Message m = new Message(d); if (m.getTargets().isEmpty()) { continue; } toAddList.add(m); } messages.removeAll(toRemoveList); messages.addAll(toAddList); } /** * The {@link IBindingMessage} used for {@link Diagnostic} based messages. */ private static class Message extends AbstractBindingMessage { private final Diagnostic myDiagnostic; private Message(Diagnostic diagnostic) { super(null); myDiagnostic = diagnostic; final List<?> data = diagnostic.getData(); /* * General case: * * 1) two elements, * * 2) first is EObject, and * * 3) second is EStructuralFeature */ if (data.size() == 2 && data.get(0) instanceof EObject && (data.get(1) == null || data.get(1) instanceof EStructuralFeature)) { addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), null); return; } /* * General case: * * 1) four elements, * * 2) first and third are EObject, and * * 3) second and fourth are EStructuralFeature */ if (data.size() == 4 && data.get(0) instanceof EObject && (data.get(1) == null || data.get(1) instanceof EStructuralFeature) && data.get(2) instanceof EObject && (data.get(3) == null || data.get(3) instanceof EStructuralFeature)) { addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), null); addTarget((EObject) data.get(2), (EStructuralFeature) data.get(3), null); return; } /* * Another General Case: here there are nothing to add * * 1) one element, and * * 2) first is Object (not EObject) */ if (data.size() >= 1 && !(data.get(0) instanceof EObject) && !data.get(0).getClass().isArray()) return; /* * Special cases from EObjectValidator */ if (diagnostic.getSource() == EObjectValidator.DIAGNOSTIC_SOURCE) { /* * Special Case: no data! */ if (data.size() == 0) return; /* * Special case: * * 1) two or three elements, * * 2) first is EObject, and * * 3) second is EStructuralFeature */ if ((data.size() == 2 || data.size() == 3) && data.get(0) instanceof EObject && (data.get(1) == null || data.get(1) instanceof EStructuralFeature)) { addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), null); return; } /* * Another Special case: here there are nothing to add * * 1) two or more elements, * * 2) first is Object (not EObject), and * * 3) second is EDataType */ if (data.size() >= 2 && !(data.get(0) instanceof EObject) && data.get(1) instanceof EDataType) return; switch (diagnostic.getCode()) { case EObjectValidator.EOBJECT__UNIQUE_ID: // eObject, otherEObject, id addTarget((EObject) data.get(0), null, null); break; case EObjectValidator.EOBJECT__EVERY_KEY_UNIQUE: // eObject, eReference, value, // otherValue addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), null); break; case EObjectValidator.EOBJECT__EVERY_BIDIRECTIONAL_REFERENCE_IS_PAIRED: // eObject, // eReference, // oppositeEObject, // eOpposite addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), null); addTarget((EObject) data.get(2), (EStructuralFeature) data.get(3), null); break; case EObjectValidator.EOBJECT__EVERY_MAP_ENTRY_UNIQUE: // eObject, eReference, // entry, eMap.get(index) addTarget((EObject) data.get(0), (EStructuralFeature) data.get(1), data.get(2)); break; case EObjectValidator.DATA_VALUE__VALUE_IN_RANGE: // value, ... case EObjectValidator.DATA_VALUE__LENGTH_IN_RANGE: // value, ... case EObjectValidator.DATA_VALUE__TOTAL_DIGITS_IN_RANGE: // value, ... case EObjectValidator.DATA_VALUE__VALUE_IN_ENUMERATION: // value, ... case EObjectValidator.DATA_VALUE__MATCHES_PATTERN: // value, ... case EObjectValidator.DATA_VALUE__TYPE_CORRECT: // value, ... break; } return; } /* * The normal case */ for (final Object o : data) { if (o == null) { LogUtils.error(data, "The data elements must be either EObject or [EObject EStructuralFeature]"); continue; } if (o instanceof EObject) { addTarget((EObject) o, null, null); continue; } if (!(o instanceof Object[])) { LogUtils.error(data, "The data elements must be either EObject or [EObject EStructuralFeature]"); continue; } final Object[] a = (Object[]) o; if (a.length < 1 || 3 < a.length) { LogUtils.error(data, "The data elements must be either EObject or [EObject EStructuralFeature]"); continue; } if (!(a[0] instanceof EObject)) { LogUtils.error(data, "The data elements must be either EObject or [EObject EStructuralFeature]"); continue; } if (a.length > 1 && !(a[1] == null || a[1] instanceof EStructuralFeature)) { LogUtils.error(data, "The data elements must be either EObject or [EObject EStructuralFeature]"); continue; } final EObject obj = (EObject) a[0]; final EStructuralFeature feature = a.length > 1 ? (EStructuralFeature) a[1] : null; final Object key = a.length > 2 ? a[2] : null; if (feature != null && !obj.eClass().getEAllStructuralFeatures().contains(feature)) { LogUtils.DEBUG_STRACK_LEVELS = 4; LogUtils.error(obj, "Reported problem with feature " + feature.getContainerClass().getSimpleName() + "." + feature.getName() + " which is not a feature of " + obj.getClass().getSimpleName()); LogUtils.DEBUG_STRACK_LEVELS = 0; continue; } addTarget(obj, feature, key); } } @Override public String getSource() { return getDiagnostic().getSource(); } @Override public int getCode() { return getDiagnostic().getCode(); } @Override public Object getData() { return getDiagnostic(); } @Override public String getMessage() { return getDiagnostic().getMessage(); } @Override public BindingMessageSeverity getSeverity() { switch (getDiagnostic().getSeverity()) { case Diagnostic.OK: return BindingMessageSeverity.NONE; case Diagnostic.INFO: return BindingMessageSeverity.INFORMATION; case Diagnostic.WARNING: return BindingMessageSeverity.WARNING; case Diagnostic.ERROR: return BindingMessageSeverity.ERROR; default: LogUtils.error(this, "Unknown severity: " + getDiagnostic().getSeverity()); break; } return BindingMessageSeverity.NONE; } public Diagnostic getDiagnostic() { return myDiagnostic; } @Override public boolean supersedes(IBindingMessage otherMessage) { if (otherMessage instanceof Message) { final Message m = (Message) otherMessage; if (m.getDiagnostic() == getDiagnostic()) return true; } return super.supersedes(otherMessage); } } /** * {@link SubstitutionLabelProvider} based on {@link IBindingObjectInformation}. */ public class MySubstitutionLabelProvider implements SubstitutionLabelProvider { @Override public String getObjectLabel(EObject eObject) { return IBindingObjectInformation.Factory.getQualifiedName(eObject); } @Override public String getFeatureLabel(EStructuralFeature sf) { return ToStringUtils.formatHumanReadable(sf.getName()); } @Override public String getValueLabel(EDataType eDataType, Object value) { return EcoreUtil.convertToString(eDataType, value); } } }