/*******************************************************************************
* 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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.henshin.interpreter.EGraph;
import org.eclipse.emf.henshin.interpreter.Match;
import org.eclipse.emf.henshin.interpreter.impl.EGraphImpl;
import org.eclipse.emf.henshin.interpreter.impl.MatchImpl;
import org.eclipse.emf.henshin.interpreter.impl.RuleApplicationImpl;
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 org.eclipse.emf.henshin.model.Rule;
import de.tub.tfs.henshin.tgg.TAttribute;
import de.tub.tfs.henshin.tgg.TEdge;
import de.tub.tfs.henshin.tgg.TNode;
import de.tub.tfs.henshin.tgg.TripleComponent;
import de.tub.tfs.henshin.tgg.interpreter.TggTransformation;
import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil;
import de.tub.tfs.henshin.tgg.interpreter.util.TggUtil;
public class TggTransformationImpl implements TggTransformation {
/**
* the maps for the marked elements
*/
protected TranslationMaps translationMaps = new TranslationMaps();
/**
* the map for the marked node objects
*/
protected HashMap<EObject, Boolean> isTranslatedNodeMap = translationMaps.getIsTranslatedNodeMap();
/**
* the map for the marked attributes
*/
protected HashMap<EObject, HashMap<EAttribute, Boolean>> isTranslatedAttributeMap = translationMaps.getIsTranslatedAttributeMap();
/**
* the map for the marked references
*/
protected HashMap<EObject, HashMap<EReference, HashMap<EObject, Boolean>>> isTranslatedEdgeMap = translationMaps.getIsTranslatedEdgeMap();
/**
* the map storing the triple components for each node
*/
protected HashMap<EObject, TripleComponent> tripleComponentNodeMap = new HashMap<EObject, TripleComponent>();
/**
*
*/
private TggEngineImpl emfEngine;
/**
*
*/
private EGraph eGraph;
/**
* flag, whether attribute values can be matched to null values
*/
public Boolean nullValueMatching = true;
/**
*
*/
public List<Rule> opRulesList = new Vector<Rule>();
/**
*
*/
protected ArrayList<RuleApplicationImpl> ruleApplicationList= new ArrayList<RuleApplicationImpl>();
private long startTime;
@Override
public HashMap<EObject, TripleComponent> getTripleComponentNodeMap() {
return tripleComponentNodeMap;
}
@Override
public ArrayList<RuleApplicationImpl> getRuleApplicationList() {
return ruleApplicationList;
}
@Override
public EGraph getGraph() {
return eGraph;
}
@Override
public void setGraph(EGraph graph) {
this.eGraph = graph;
}
@Override
public List<Rule> getOpRuleList() {
return opRulesList;
}
@Override
public void setOpRuleList(List<Rule> opRuleList) {
this.opRulesList = opRuleList;
}
@Override
public void addOpRuleList(List<Rule> opRuleList) {
this.opRulesList.addAll(opRuleList);
}
@Override
public TggEngineImpl getEmfEngine() {
return emfEngine;
}
@Override
public void setEmfEngine(TggEngineImpl emfEngine) {
this.emfEngine = emfEngine;
}
public TggTransformationImpl() {
}
@Override
public TranslationMaps getTranslationMaps() {
return translationMaps;
}
@Override
public void setTranslationMaps(TranslationMaps translationMaps) {
this.translationMaps = translationMaps;
}
@Override
public void setInput(EObject root) {
ArrayList<EObject> inputRootEObjects = new ArrayList<EObject>();
inputRootEObjects.add(root);
setInput(inputRootEObjects);
}
@Override
public void setInput(List<EObject> inputRootEObjects) {
createInputGraph(inputRootEObjects);
fillTranslatedMaps(inputRootEObjects,false);
registerUserConstraints();
}
@Override
public void setInput(TggHenshinEGraph eGraph) {
this.eGraph=eGraph;
fillTranslatedMapsFromGraph(eGraph);
registerUserConstraints();
}
private void fillTranslatedMapsFromGraph(TggHenshinEGraph eGraph) {
Graph graph = eGraph.getHenshinGraph();
Map<Node, EObject> node2eObject = eGraph.getNode2ObjectMap();
// input graph has to be marked initially to avoid confusion if source and target meta model coincide
// select marked nodes and put them in list of input eObjects
EObject o=null;
EAttribute a=null;
EReference eRef=null;
EObject source=null;
EObject target=null;
Boolean translationMarker=null;
for(Node n:graph.getNodes()){
if(n instanceof TNode)
translationMarker = TggUtil.getIsTranslated(((TNode)n));
if(translationMarker!=null){
o = node2eObject.get(n);
isTranslatedNodeMap.put(o,translationMarker);
translationMarker=null;
}
for(Attribute att:n.getAttributes()){
if(att instanceof TAttribute)
translationMarker =TggUtil.getIsTranslated((TAttribute)att);
if( translationMarker !=null){
a= att.getType();
addToTranslatedAttributeMap(o, a, translationMarker);
translationMarker=null;
}
}
}
for(Edge e:graph.getEdges()){
if(e instanceof TEdge)
translationMarker = TggUtil.getIsTranslated(((TEdge)e));
if (translationMarker!=null){
source=node2eObject.get(e.getSource());
eRef=e.getType();
target= node2eObject.get(e.getTarget());
addToTranslatedEdgeMap(source, eRef, target,translationMarker);
translationMarker=null;
}
}
}
private void createInputGraph(List<EObject> inputEObjects) {
eGraph = new EGraphImpl();
for(EObject o: inputEObjects){
eGraph.addTree(o); // matching via depth first search - as in editor
//eGraph.addGraph(o); // matching via breath first search
}
}
private void registerUserConstraints() {
emfEngine = new TggEngineOperational(eGraph,this);
}
@Override
public void applyRules() {
applyRules(null,null);
}
@Override
public boolean applyRules(boolean debug) {
return applyRules(null,null,debug);
}
@Override
public boolean applyRules(IProgressMonitor monitor, String msg) {
return applyRules(monitor,msg,false);
}
private String getTime(){
long duration = (System.nanoTime() - getStartTime() + 500000) / 1000000;
long durationSeconds = (duration + 500) / 1000;
long durationS = durationSeconds;
long durationH = durationSeconds / 3600;
durationS -= durationH * 3600;
long durationM = durationS / 60;
durationS -= durationM * 60;
return durationH + ":" + durationM + ":" + durationS + " ";
}
public long getStartTime(){
return startTime;
}
public void setStartTime(long startTime){
this.startTime = startTime;
}
@Override
public boolean applyRules(IProgressMonitor monitor, String msg, boolean debug) {
// check if any rule can be applied
ruleApplicationList.clear();
long startTimeOneStep=System.nanoTime();
long endTimeOneStep=System.nanoTime();
double duration=0;
double maxDuration=0;
String maxDurationRuleName=null;
boolean foundApplication = false;
boolean continueRuleApplications = true;
while (continueRuleApplications) {
boolean foundApplicationForRuleSet = false;
// apply all rules on graph
Rule currentRule = null;
// printOpRuleList();
try {
for (Rule rule : opRulesList) {
if (debug){
System.out.println();
System.out.print(String.format("%1$15s", eGraph.size()+" "));
System.out.print(String.format("%1$65s", rule.getName()+" "));
}
startTimeOneStep=System.nanoTime();
if (monitor!=null)
monitor.subTask(getTime() + " " + msg + " (" + rule.getName() + ")");
currentRule=rule;
/*
* Apply a rule as long as it's possible and add each successful
* application to ruleApplicationlist. Then fill the
* isTranslatedTable Start with a fresh match.
*/
Boolean matchesToCheck = true;
while (matchesToCheck) {
Iterator<Match> matchesIterator = emfEngine
.findMatches(rule, eGraph, new MatchImpl(rule))
.iterator();
boolean foundApplicationForRule = false;
while (matchesIterator.hasNext()) {
// refresh rule application to be used for layout and debugging
// create new rule application for each match
RuleApplicationImpl ruleApplication = new RuleApplicationImpl(emfEngine);
ruleApplication.setEGraph(eGraph);
ruleApplication.setRule(rule);
ruleApplication.setPartialMatch(matchesIterator
.next());
try {
foundApplicationForRule = executeOneStep(ruleApplication,
false, rule);
foundApplicationForRuleSet = foundApplicationForRuleSet || foundApplicationForRule;
// show one bar for each successful rule application
if(foundApplicationForRule && debug){System.out.print("|");}
} catch (RuntimeException e){
e.printStackTrace();
matchesToCheck = false;
}
}
// continue with this rule, if one application was successful
if (foundApplicationForRule)
matchesToCheck = true;
else matchesToCheck = false;
}
if(debug){
endTimeOneStep=System.nanoTime();
duration=(endTimeOneStep-startTimeOneStep)/(1E6);
if(maxDuration<duration){
maxDuration=duration;
maxDurationRuleName=rule.getName();
}
if(duration>10)
System.out.print(//"Rule " + rule.getName() + ":" +
duration + "ms");
}
// startTimeOneStep=System.nanoTime();
}
} catch (RuntimeException e) {
System.out.println("Rule "
+ currentRule.getName()
+ " caused a runtime exception. Check input parameter settings: "
+ e.getMessage());
}
if (debug)System.out.println("");
continueRuleApplications = foundApplicationForRuleSet;
foundApplication = foundApplication || foundApplicationForRuleSet;
}
if(debug){ System.out.println("### Rule " + maxDurationRuleName + " had the highest execution time of:" +
maxDuration + "ms");
}
return foundApplication;
}
private boolean executeOneStep(RuleApplicationImpl ruleApplication,
boolean foundApplication, Rule rule) {
if (ruleApplication.execute(null)) {
foundApplication = true;
// fill isTranslatedNodeMap
List<Node> rhsNodes = rule.getRhs().getNodes();
Match resultMatch = ruleApplication.getResultMatch();
for (Node n: rhsNodes) {
TNode ruleNodeRHS = (TNode) n;
EObject nodeEObject = resultMatch.getNodeTarget(ruleNodeRHS);
tripleComponentNodeMap.put(nodeEObject,ruleNodeRHS.getComponent());
if (RuleUtil.Translated.equals(ruleNodeRHS.getMarkerType())) {
isTranslatedNodeMap.put(nodeEObject, true);
updateTranslatedAttributeMap(ruleNodeRHS, nodeEObject);
updateTranslatedEdgeMap(ruleNodeRHS, nodeEObject, resultMatch);
} else {
// context node, thus check whether the edges
// and attributes are translated
updateTranslatedAttributeMap(ruleNodeRHS, nodeEObject);
updateTranslatedEdgeMap(ruleNodeRHS, nodeEObject, resultMatch);
}
}
emfEngine.postProcess(resultMatch);
// everything successful, add the rule application
ruleApplicationList.add(ruleApplication);
} else {
// Match is not applicable, e.g. because it became invalid by a previous step - TODO: possible to improve efficiency
}
return foundApplication;
}
private void addToTranslatedAttributeMap(EObject graphNodeEObject, EAttribute eAttr, Boolean translationMarker) {
if(!isTranslatedAttributeMap.containsKey(graphNodeEObject)) {
isTranslatedAttributeMap.put(graphNodeEObject,new HashMap<EAttribute,Boolean>());
}
putAttributeInMap(graphNodeEObject, eAttr, translationMarker);
}
private void addToTranslatedEdgeMap(EObject source, EReference eRef, EObject target, Boolean translationMarker) {
if(!isTranslatedEdgeMap.containsKey(source)) {
isTranslatedEdgeMap.put(source,new HashMap<EReference,HashMap<EObject, Boolean>>());
}
if(!isTranslatedEdgeMap.get(source).containsKey(eRef)) {
isTranslatedEdgeMap.get(source).put(eRef,new HashMap<EObject, Boolean>());
}
putEdgeInMap(source, eRef, target, translationMarker);
}
private void updateTranslatedAttributeMap(Node ruleNodeRHS, EObject graphNodeEObject) {
// fill isTranslatedAttributeMap
// scan the contained attributes for <tr>
for (Attribute ruleAttribute : ruleNodeRHS.getAttributes()) {
String isMarked=((TAttribute) ruleAttribute).getMarkerType();
if (RuleUtil.Translated.equals(isMarked)) {
//mark this attribute to be translated
putAttributeInMap(graphNodeEObject,ruleAttribute.getType(),true);
}
}
}
private void putAttributeInMap(EObject graphNodeEObject, EAttribute eAttr, Boolean value) {
HashMap<EAttribute,Boolean> attrMap = isTranslatedAttributeMap.get(graphNodeEObject);
if(attrMap==null) {
System.out.println("!WARNING: Translated attribute map is missing node entry.");
return;
}
attrMap.put(eAttr, value);
}
private void updateTranslatedEdgeMap(Node ruleNode, EObject sourceNodeEObject, Match resultMatch) {
// fill isTranslatedEdgeMap
EObject targetNodeeObject;
// scan the outgoing edges for <tr>
for (Edge ruleEdge : ruleNode.getOutgoing()) {
if (RuleUtil.Translated.equals( (((TEdge) ruleEdge).getMarkerType()))) {
Node ruleTarget = ruleEdge.getTarget();
targetNodeeObject = resultMatch.getNodeTarget(ruleTarget);
// put edge in hashmap
putEdgeInMap(sourceNodeEObject,ruleEdge.getType(),targetNodeeObject,true);
}
}
}
private void putEdgeInMap(
EObject sourceNodeEObject, EReference eRef, EObject targetNodeEObject, Boolean value) {
HashMap<EReference,HashMap<EObject,Boolean>> edgeMap = isTranslatedEdgeMap.get(sourceNodeEObject);
if(edgeMap==null) {
System.out.println("Translated edge map is missing node entry.");
return;
}
if(!edgeMap.containsKey(eRef))
edgeMap.put(eRef,new HashMap<EObject,Boolean>());
edgeMap.get(eRef).put(targetNodeEObject, value);
}
@Override
public void fillTranslatedMaps(List<EObject> eObjects, Boolean markerValue) {
for (EObject o: eObjects){
fillTranslatedMaps(o, markerValue);
}
}
@Override
public void fillTranslatedMaps(EObject eObject, Boolean markerValue) {
// fills translated maps with all given elements of the graph
// component(s) that shall be marked (all of inputEObjects)
// first, mark the given eObject
isTranslatedNodeMap.put(eObject, markerValue);
// mark all children
TreeIterator<EObject> it = null;
EObject obj = null;
it = eObject.eAllContents();
while (it.hasNext()) {
obj = it.next();
isTranslatedNodeMap.put(obj, markerValue);
fillTranslatedMapsForFeatures(obj, markerValue);
}
// now, mark all features - edge map requires that source and target nodes are marked before
it = eObject.eAllContents();
while (it.hasNext()) {
obj = it.next();
fillTranslatedMapsForFeatures(obj, markerValue);
}
fillTranslatedMapsForFeatures(eObject, markerValue);
}
private void fillTranslatedMapsForFeatures(EObject o, Boolean markerValue) {
final EList<EStructuralFeature> allEStructFeats = o.eClass().getEAllStructuralFeatures();
for(EStructuralFeature esf : allEStructFeats)
{
// all attributes
if (esf instanceof EAttribute){
// if (currentEObject.eIsSet(esf)) // attribute is set // FIXME: check whether necessary
addToTranslatedAttributeMap(o,(EAttribute)esf,markerValue);
}
// all edges
if (esf instanceof EReference){
EReference ref = (EReference) esf;
Object referenceValue = o.eGet(esf);
if (referenceValue == null){
// reference is not set, nothing to do
}
else if (referenceValue instanceof List) {
List<Object> references = (List<Object>) referenceValue;
for (Object targetObj: references){
if (targetObj instanceof EObject && isTranslatedNodeMap.containsKey(targetObj)) // target eObject has to be marked as well
addToTranslatedEdgeMap(o,ref,(EObject) targetObj,markerValue);
}
}
else if (referenceValue instanceof EObject){
if (isTranslatedNodeMap.containsKey(referenceValue)) // target eObject has to be marked as well
addToTranslatedEdgeMap(o,ref,(EObject) referenceValue,markerValue);
}
else{
System.out.println("!WARNING: transformation initialisation error, references are not a list nor a plain object reference");
}
}
}
}
@Override
public void setNullValueMatching(boolean matchNullValues) {
this.nullValueMatching=matchNullValues;
}
@Override
public Boolean getNullValueMatching() {
return nullValueMatching;
}
}