blob: 3a3d0c40e32186952ecd77e3fb5ae7c83912ac44 [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.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.impl.EStringToStringMapEntryImpl;
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);
/*
* in the case of a changed EParameter or annotationwe really want to include
* the parent EClass in the revalidation.
*/
if ((eObj instanceof EParameter || eObj instanceof EStringToStringMapEntryImpl)
&& container.eContainer() instanceof EClass) {
containers.add(container.eContainer());
}
}
}
/*
* 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;
}
}