blob: 3860b058baf2a0fae3b140d1cb77ee647ba89d53 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 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.codegen.ecore.genmodel.GenBase;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenOperation;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenParameter;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.NotificationFilter;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.sirius.business.api.session.ModelChangeTrigger;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* a {@link ModelChangeTrigger} which reconcile any gemodel in the editing
* context.
*
* @author Cedric Brun <cedric.brun@obeo.fr>
*
*/
public class GenModelAutoReload implements ModelChangeTrigger {
private Session session;
public GenModelAutoReload(Session newSession) {
this.session = newSession;
}
public static final NotificationFilter IS_TOUCH = new NotificationFilter.Custom() {
public boolean matches(Notification input) {
return input.isTouch();
}
};
public static final NotificationFilter IS_ECORE = new NotificationFilter.Custom() {
public boolean matches(Notification input) {
return (input.getNotifier() instanceof EObject)
&& ((EObject) input.getNotifier()).eClass().getEPackage() == EcorePackage.eINSTANCE;
}
};
public static final NotificationFilter IS_ATTACHMENT = new NotificationFilter.Custom() {
public boolean matches(Notification input) {
return (input.getFeature() instanceof EReference
&& ((EReference) input.getFeature()).isContainment() == true);
}
};
public static final NotificationFilter IS_EREFENCE_CONTAINMENT = new NotificationFilter.Custom() {
public boolean matches(Notification input) {
return (input.getFeature() == EcorePackage.eINSTANCE.getEReference_Containment());
}
};
public static final NotificationFilter SHOULD_RELOAD = IS_TOUCH.negated().and(IS_ECORE.and(IS_ATTACHMENT));
public static final int PRIORITY = -2;
public Option<Command> localChangesAboutToCommit(Collection<Notification> notifications) {
final Collection<GenModel> genModels = Lists.newArrayList();
for (Resource res : session.getTransactionalEditingDomain().getResourceSet().getResources()) {
for (EObject root : res.getContents()) {
if (root instanceof GenModel) {
genModels.add((GenModel) root);
}
}
}
if (genModels.size() > 0) {
Command result = new ProcessGenModels(session, genModels);
return Options.newSome(result);
}
return Options.newNone();
}
public int priority() {
return PRIORITY;
}
}
class ProcessGenModels extends RecordingCommand {
private Session session;
private Collection<GenModel> genmodels;
public ProcessGenModels(Session session, Collection<GenModel> genmodels) {
super(session.getTransactionalEditingDomain());
this.session = session;
this.genmodels = genmodels;
}
@Override
protected void doExecute() {
/*
* The genXXX.reconcile() methods are relying on the fact that the
* ecoreXXX reference is an unresolvable proxy to cleanup the
* corresponding element. This is not the case when the model is being
* edited "in live" as the Java reference still holds.
*/
Set<GenBase> toDelete = Sets.newLinkedHashSet();
for (GenModel genmodel : genmodels) {
for (GenPackage genpackage : genmodel.getGenPackages()) {
cleanupGenPackages(toDelete, genpackage);
cleanupGenClasses(toDelete, genpackage);
}
}
for (GenBase genFeature : toDelete) {
session.getModelAccessor().eDelete(genFeature, session.getSemanticCrossReferencer());
}
for (GenModel genmodel : genmodels) {
genmodel.reconcile();
}
}
private void cleanupGenPackages(Set<GenBase> toDelete, GenPackage genpackage) {
for (GenPackage subpackage : genpackage.getSubGenPackages()) {
if (subpackage.getEcorePackage() != null
&& subpackage.getEcorePackage().getESuperPackage() != genpackage.getEcorePackage()) {
toDelete.add(subpackage);
} else {
cleanupGenClasses(toDelete, subpackage);
}
}
}
private void cleanupGenClasses(Set<GenBase> toDelete, GenPackage genpackage) {
for (GenClass genclass : genpackage.getGenClasses()) {
if (genclass.getEcoreClass() != null
&& genclass.getEcoreClass().getEPackage() != genpackage.getEcorePackage()) {
toDelete.add(genclass);
} else {
/*
* any feature/eclass vs genfeature/genclass mismatch should be
* fixed by deleting the corresponding genfeature.
*/
for (GenFeature feat : genclass.getGenFeatures()) {
if (feat.getEcoreFeature() != null && feat.getEcoreFeature().getEContainingClass() != feat
.getGenClass().getEcoreClassifier()) {
toDelete.add(feat);
}
}
for (GenOperation op : genclass.getGenOperations()) {
if (op.getEcoreOperation() != null
&& op.getEcoreOperation().getEContainingClass() != op.getGenClass().getEcoreClassifier()) {
toDelete.add(op);
}
for (GenParameter param : op.getGenParameters()) {
if (param.getEcoreParameter() != null
&& param.getEcoreParameter().getEOperation() != op.getEcoreOperation()) {
toDelete.add(param);
}
}
}
}
}
}
}