/******************************************************************************* * Copyright (c) 2010-2015 Henshin developers. 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: * TU Berlin, University of Luxembourg, SES S.A. *******************************************************************************/ package de.tub.tfs.henshin.tgg.interpreter.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.henshin.interpreter.impl.EGraphImpl; import org.eclipse.emf.henshin.model.Attribute; import org.eclipse.emf.henshin.model.Edge; import org.eclipse.emf.henshin.model.Graph; import org.eclipse.emf.henshin.model.Node; import de.tub.tfs.henshin.tgg.TggFactory; public class TggHenshinEGraph extends EGraphImpl implements Adapter { // Generated serial ID private static final long serialVersionUID = -2408288653525326829L; // The Henshin graph: protected final Graph henshinGraph; // Correspondence maps: protected Map<Node, EObject> node2object; protected Map<EObject, Node> object2node; /** * Default constructor. * @param graph The Henshin graph. */ public TggHenshinEGraph(Graph graph) { henshinGraph = graph; node2object = new HashMap<Node, EObject>(); object2node = new HashMap<EObject, Node>(); henshin2emfGraph(); } /** * Translates the Henshin {@link Graph} in {@link #henshinGraph} into an * ordinary EMF graph with {@link EObject}s, {@link EReference}s and so on. * Afterwards, corresponding {@link EObject}s can be found in * <code>eObjects</code> and correspondences in <code>eObject2nodeMap</code> * and <code>node2eObjectMap</code>. * <p> * <strong>Remark:</strong> Currently, edges typed over <i>derived</i> * {@link EReference}s (see {@link EReference#isDerived()}) are omitted * during translation. Furthermore, if a typing {@link EReference}s is * non-changeable (see {@link EReference#isChangeable()}) but its opposite * (see {@link EReference#getEOpposite()}) is changeable, the reference is * translated into its opposite. */ @SuppressWarnings("unchecked") private void henshin2emfGraph() { clear(); // henshinGraph.eAdapters().clear(); object2node.clear(); node2object.clear(); for (Node node : henshinGraph.getNodes()) { EObject eObject = node2object.get(node); try { if (eObject == null) { EClass nodeType = node.getType(); EFactory factory = nodeType.getEPackage().getEFactoryInstance(); eObject = factory.create(nodeType); addSynchronizedPair(node, eObject); // add eObject: after the pair is added to the hash maps, // because the node is recreated otherwise add(eObject); } } catch (Exception ex){ ex.printStackTrace(); } for (Attribute attr : node.getAttributes()) { try { // don't notify me about changes that I made eObject.eAdapters().remove(this); EAttribute attrType = attr.getType(); String attrValue = attr.getValue(); attrValue = attrValue.replaceAll("\"", ""); if (attrType.isMany()) { List<Object> attrValues = (List<Object>) eObject.eGet(attrType); try { attrValues.add(attrValue); } catch (Exception ex){ } } else { try { eObject.eSet(attrType, EcoreUtil.createFromString(attrType.getEAttributeType(), attrValue)); } catch (Exception ex){ if (ex instanceof IllegalArgumentException) System.out.println("Error in graph: attribute value is invalid. Trying to instantiate attribute "+ attrType.getName() + ":" + attrType.getEAttributeType().getName() + " with value " + attrValue + "."); ex.printStackTrace(); } } eObject.eAdapters().add(this); } catch (Exception ex){ } } } for (Edge edge : new ArrayList<Edge>(henshinGraph.getEdges())) { try { EReference edgeType = edge.getType(); /* * If reference <code>edgeType</code> is derived it is available * implicitly and does not need to be set. Furthermore, if a * reference is not changeable it is omitted as well. */ // Do not ignore derived features they might be required by some grammars! //if (edgeType.isDerived()) // continue; EObject ownerObject = node2object.get(edge.getSource()); EObject targetObject = node2object.get(edge.getTarget()); /* * If the edgeType is not changeable but its opposite is, then we * switch to the opposite. */ if (!edgeType.isChangeable()) { if (edgeType.getEOpposite() != null && edgeType.getEOpposite().isChangeable()) { edgeType = edgeType.getEOpposite(); // switch source and target EObject temp = ownerObject; ownerObject = targetObject; targetObject = temp; } else /* * Otherwise we cannot handle the edge and omit it (or * better: shall throw an exception) */ continue; }// if // don't notify me about changes that I made ownerObject.eAdapters().remove(this); if (edgeType.isMany()) { List<Object> edgeValues = (List<Object>) ownerObject.eGet(edgeType); edgeValues.add(targetObject); } else { ownerObject.eSet(edgeType, targetObject); } ownerObject.eAdapters().add(this); } catch (Exception ex){ } } } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.impl.EGraphImpl#add(org.eclipse.emf.ecore.EObject) */ @Override public boolean add(EObject eObject) { boolean isNew = super.add(eObject); if (isNew) { Node node = object2node.get(eObject); if (node == null) { node = createNode(); node.setType(eObject.eClass()); henshinGraph.getNodes().add(node); addSynchronizedPair(node, eObject); } else { if (!henshinGraph.getNodes().contains(node)) henshinGraph.getNodes().add(node); } } return isNew; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.impl.EGraphImpl#remove(java.lang.Object) */ @Override public boolean remove(Object object) { boolean removed = super.remove(object); if (removed) { Node node = object2node.get(object); if (node != null) { henshinGraph.getNodes().remove(node); List<Edge> list = new ArrayList<Edge>(node.getIncoming()); list.addAll(node.getOutgoing()); for (Edge edge : list) { edge.setSource(null); edge.setTarget(null); edge.setGraph(null); } removeSynchronizedPair(node, (EObject) object); } } return removed; } /* * Add a synchronized pair of a node and an object. */ private void addSynchronizedPair(Node node, EObject eObject) { node2object.put(node, eObject); object2node.put(eObject, node); eObject.eAdapters().add(this); } /* * Remove a synchronized pair of a node and an object. */ private void removeSynchronizedPair(Node node, EObject eObject) { // node2eObjectMap.remove(node); // eObject2nodeMap.remove(eObject); // eObject.eAdapters().remove(this); } /* * (non-Javadoc) * @see org.eclipse.emf.common.notify.Adapter#getTarget() */ @Override public Notifier getTarget() { return null; } /* * (non-Javadoc) * @see org.eclipse.emf.common.notify.Adapter#isAdapterForType(java.lang.Object) */ @Override public boolean isAdapterForType(Object type) { return false; } /* * (non-Javadoc) * @see org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common.notify.Notification) */ @Override public void notifyChanged(Notification notification) { EObject owner = (EObject) notification.getNotifier(); Node ownerNode = object2node.get(owner); Object feature = notification.getFeature(); Object oldValue = notification.getOldValue(); Object newValue = notification.getNewValue(); if (feature instanceof EStructuralFeature && ownerNode != null) { // remove all deleted structures from the henshin graph if (oldValue != null && newValue != oldValue) { removeFromHenshinGraph(ownerNode, (EStructuralFeature) feature, oldValue); } // add new structures to henshin graph if (newValue != null) { addToHenshinGraph(owner, (EStructuralFeature) feature, newValue); } } } /* * (non-Javadoc) * @see org.eclipse.emf.common.notify.Adapter#setTarget(org.eclipse.emf.common.notify.Notifier) */ @Override public void setTarget(Notifier newTarget) { } /* * Remove an object from the Henshin graph. */ private void removeFromHenshinGraph(Node owner, EStructuralFeature feature, Object value) { if (feature instanceof EAttribute) { Attribute attribute = null; for (Attribute nodeAttribute : owner.getAttributes()) { if (nodeAttribute.getType() == feature) { attribute = nodeAttribute; break; } } if (attribute != null) { attribute.setNode(null); } } else if (feature instanceof EReference) { Edge edge = null; if (value instanceof EObject) { Node targetNode = object2node.get(value); for (Edge outgoingEdge : owner.getOutgoing()) { if (outgoingEdge.getTarget() == targetNode && outgoingEdge.getType() == feature) { edge = outgoingEdge; break; } } if (edge != null) { edge.setSource(null); edge.setTarget(null); edge.setGraph(null); } } } } /* * Add an object to the Henshin graph. */ private void addToHenshinGraph(EObject owner, EStructuralFeature feature, Object value) { Node node = object2node.get(owner); if (node != null && value != null) { if (feature instanceof EAttribute) { Attribute attribute = null; for (Attribute nodeAttribute : node.getAttributes()) { if (nodeAttribute.getType() == feature) { attribute = nodeAttribute; break; } } if (attribute == null) { attribute = createAttribute(); attribute.setType((EAttribute) feature); attribute.setNode(node); } attribute.setValue(value.toString()); } else if (feature instanceof EReference) { Edge edge = null; if (value instanceof EObject) { Node targetNode = object2node.get(value); for (Edge outgoingEdge : node.getOutgoing()) { if (outgoingEdge.getTarget() == targetNode && outgoingEdge.getType() == feature) { edge = outgoingEdge; break; } } if (edge == null) { edge = createEdge(); edge.setSource(node); edge.setTarget(targetNode); edge.setGraph(henshinGraph); edge.setType((EReference) feature); } } } } } public Map<Node, EObject> getNode2ObjectMap() { return node2object; } public void setNode2ObjectMap(Map<Node,EObject> node2object) { this.node2object = node2object; } public Map<EObject, Node> getObject2NodeMap() { return object2node; } public void setObject2NodeMap(Map<EObject, Node> object2node) { this.object2node = object2node; } /** * creates a new node of a triple graph with layout and marker information * returns the created TNode */ protected Node createNode() { return TggFactory.eINSTANCE.createTNode(); } protected Attribute createAttribute() { return TggFactory.eINSTANCE.createTAttribute(); } protected Edge createEdge() { return TggFactory.eINSTANCE.createTEdge(); } public Graph getHenshinGraph(){ return henshinGraph; } }