blob: a5dfce31cd1c320d4f02a1434a116c24b4413e28 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Obeo
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.ecoretools.design.service;
import java.util.Collection;
import java.util.Set;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.sirius.business.api.session.ModelChangeTrigger;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
import com.google.common.collect.Sets;
public class LiveValidationTrigger implements ModelChangeTrigger {
public static final NotificationFilter IS_ECORE_CHANGE = new NotificationFilter.Custom() {
public boolean matches(Notification notification) {
return (!notification.isTouch() && notification.getFeature() instanceof EStructuralFeature && ((EStructuralFeature) notification.getFeature()).getEContainingClass().getEPackage() == EcorePackage.eINSTANCE);
}
};
private TransactionalEditingDomain domain;
/**
* We need to be triggered before the refresh mechanism takes place so that
* the diagnostic attachements are up-to date when computing colors.
*/
public static final int PRIORITY = 0;
public LiveValidationTrigger(TransactionalEditingDomain domain) {
this.domain = domain;
}
public Option<Command> localChangesAboutToCommit(Collection<Notification> notifications) {
final Set<EObject> changedEcoreObjects = Sets.newLinkedHashSet();
for (Notification notif : notifications) {
Object obj = notif.getNotifier();
if (obj instanceof EObject && ((EObject) obj).eClass() != null && ((EObject) obj).eClass().getEPackage() == EcorePackage.eINSTANCE) {
changedEcoreObjects.add((EObject) obj);
}
}
if (changedEcoreObjects.size() > 0) {
Command revalidateEObjects = new RecordingCommand(domain) {
@Override
protected void doExecute() {
Set<EObject> containers = Sets.newLinkedHashSet();
for (EObject eObj : changedEcoreObjects) {
revalidate(eObj);
EObject container = eObj.eContainer();
if (container != null) {
containers.add(container);
}
}
/*
* When an Ecore object changes it is likely the container
* might have a new validation error (or an error might be
* gone) even if it has suffered no change itself. Example :
* two EStructural features having the same name and
* contained in the same EClass will trigger an error on
* such EClass. This state should be updated when one of the
* EStructuralFeature got renamed.
*/
for (EObject container : containers) {
revalidate(container);
}
}
protected void revalidate(EObject eObj) {
DiagnosticAttachment diag = DiagnosticAttachment.getAttachment(eObj);
/*
* If the EObject had a validation marker, then we need to
* update its state, otherwise nobody cared about its
* validation status, no need to pre-compute it.
*/
if (diag != null) {
try {
/*
* anything could happen within the validate. We
* make sure we won't fail the whole post-process in
* this case.
*/
Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObj);
/*
* We attach the result of the validation on the
* EObject through the eAdapters list. It might be
* consumed by some modelers to change colors or
* update tooltips.
*/
diag.setDiagnostic(diagnostic);
} catch (Throwable e) {
/*
* Anything which happens here might not be a
* concern.
*/
}
}
}
};
return Options.newSome(revalidateEObjects);
}
return Options.newNone();
}
public int priority() {
return PRIORITY;
}
}