blob: fbfc1256f724f621e8ef25f49ad9c2f7f80a9950 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2019 Chalmers | University of Gothenburg, rt-labs and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Chalmers | University of Gothenburg and rt-labs - initial API and implementation and/or initial documentation
* Chalmers | University of Gothenburg - additional features, updated API
*******************************************************************************/
package org.eclipse.capra.handler.emf.notification;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.eclipse.capra.core.adapters.Connection;
import org.eclipse.capra.core.adapters.TraceMetaModelAdapter;
import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
import org.eclipse.capra.core.helpers.ExtensionPointHelper;
import org.eclipse.capra.ui.notification.CapraNotificationHelper;
import org.eclipse.capra.ui.notification.CapraNotificationHelper.IssueType;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.EMFCompare;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.scope.DefaultComparisonScope;
import org.eclipse.emf.compare.scope.IComparisonScope;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* Listens to changes in the target model and produces a notification, if they
* affect the Capra trace model.
*
* @author Dusan Kalanj
*
*/
public class ModelChangeListener extends EContentAdapter {
List<Integer> eventsToProcess = Arrays.asList(new Integer[] { 1, 3, 4 });
@Override
public void notifyChanged(Notification notification) {
Object notifier = notification.getNotifier();
// Only process the notification if it signifies a set, add or remove
// operation on an EObject.
if (!eventsToProcess.contains(notification.getEventType()) || !(notifier instanceof EObject))
return;
Resource changedResource = ((EObject) notifier).eResource();
if (changedResource != null)
// Compare the current state of the model (changedResource) with the
// previously saved state of the same model.
compareTracedItems(changedResource);
}
private void compareTracedItems(Resource changedResource) {
TracePersistenceAdapter persistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
ResourceSetImpl newResourceSet = new ResourceSetImpl();
EObject traceModel = persistenceAdapter.getTraceModel(newResourceSet);
//check that atleast one trace link exists
if (traceModel!=null && traceModel.eContents().size() > 0) {
IPath path = new Path(EcoreUtil.getURI(traceModel).toPlatformString(false));
IFile traceContainer = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
TraceMetaModelAdapter traceMetamodelAdapter = ExtensionPointHelper.getTraceMetamodelAdapter().get();
List<Connection> connections = traceMetamodelAdapter
.getAllTraceLinks(persistenceAdapter.getTraceModel(new ResourceSetImpl()));
if (!connections.isEmpty()) {
for (Connection c : connections) {
List<EObject> tempList = new ArrayList<>();
tempList.add(c.getOrigin());
tempList.addAll(c.getTargets());
for (EObject tracedItem : tempList) {
Resource oldResource = tracedItem.eResource();
if (oldResource == null)
oldResource = newResourceSet.getResource(EcoreUtil.getURI(tracedItem).trimFragment(), true);
if (oldResource != null) {
if (CapraNotificationHelper.getFileUri(oldResource)
.equals(CapraNotificationHelper.getFileUri(changedResource))) {
IComparisonScope scope = new DefaultComparisonScope(changedResource, oldResource, null);
// Build a comparison object with EMFCompare and
// recursively resolve the Match elements.
Comparison comparison = EMFCompare.builder().build().compare(scope);
for (Match match : comparison.getMatches())
resolveMatch(tracedItem, match, null, traceContainer);
}
}
}
}
}
}
}
private void resolveMatch(EObject tracedItem, Match match, DifferenceKind diffKind, IFile traceContainer) {
EList<Diff> differences = match.getDifferences();
if (!differences.isEmpty())
diffKind = differences.get(0).getKind();
for (Match subMatch : match.getSubmatches())
// Recursively resolve the subMatches, if they exist.
resolveMatch(tracedItem, subMatch, diffKind, traceContainer);
EObject newObject = match.getLeft();
if (newObject != null) {
// Potentially deletes an existing marker, if the state of the
// newObject is the same as the state of the same object in the
// previously saved resource.
URI oldUri = CapraNotificationHelper.getFileUri(tracedItem);
if (CapraNotificationHelper.getFileUri(newObject).equals(oldUri))
CapraNotificationHelper.deleteCapraMarker(oldUri.toString(), null, traceContainer);
}
if (diffKind != null) {
EObject oldObject = match.getRight();
if (oldObject != null && oldObject.equals(tracedItem)) {
// If the match or a parent match contains a difference that
// affects the tracedItem, produce a marker.
IssueType issueType = null;
if (diffKind.equals(DifferenceKind.DELETE))
issueType = IssueType.DELETED;
else if (diffKind.equals(DifferenceKind.MOVE))
issueType = IssueType.MOVED;
else if (diffKind.equals(DifferenceKind.ADD))
issueType = IssueType.ADDED;
else if (diffKind.equals(DifferenceKind.CHANGE))
issueType = IssueType.RENAMED;
if (issueType != null)
createCapraMarker(tracedItem, match, issueType, traceContainer);
}
}
}
/**
* Generates the info for the to-be-created marker which notifies the user
* that the tracedItem has been affected - and calls the method from the
* CapraNotificationHelper class that actually creates the marker.
*
* @param tracedItem
* the item from the Capra trace model that was affected by the
* change
* @param match
* the Match element generated by EMF Compare from which the
* marker info will be extracted
* @param diffKind
* the difference type that describes the change that happened to
* the tracedItem
* @param file
* the file to which the marker will be attached
*/
private void createCapraMarker(EObject tracedItem, Match match, IssueType issueType, IFile file) {
String oldUri = CapraNotificationHelper.getFileUri(tracedItem).toString();
String oldName = (String) tracedItem.eGet(tracedItem.eClass().getEStructuralFeature("name"));
EObject changedObject = match.getLeft();
String newUri = "";
String newName = "";
if (changedObject != null) {
newUri = CapraNotificationHelper.getFileUri(changedObject).toString();
newName = (String) changedObject.eGet(changedObject.eClass().getEStructuralFeature("name"));
}
String message = "";
switch (issueType) {
case DELETED:
if (changedObject == null)
message = oldUri + " has been deleted.";
break;
case MOVED:
if (!oldUri.equals(newUri))
message = oldName + " has been moved to " + newUri + ".";
break;
case RENAMED:
if (!newName.equals(oldName))
message = oldUri + " has been renamed to " + newName + ".";
else
message = "An ancestor of " + oldName + " has been renamed." + " The new URI of the element is "
+ newUri;
break;
case ADDED:
break;
case CHANGED:
break;
}
if (issueType != null) {
HashMap<String, String> markerInfo = new HashMap<String, String>();
markerInfo.put(CapraNotificationHelper.ISSUE_TYPE, issueType.getValue());
markerInfo.put(CapraNotificationHelper.MESSAGE, message);
markerInfo.put(CapraNotificationHelper.OLD_URI, oldUri);
markerInfo.put(CapraNotificationHelper.NEW_URI, newUri);
markerInfo.put(CapraNotificationHelper.NEW_NAME, newName);
CapraNotificationHelper.createCapraMarker(markerInfo, file);
}
}
}