/*******************************************************************************
* 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.tggeditor.actions.create.rule;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
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.IndependentUnit;
import org.eclipse.emf.henshin.model.Module;
import org.eclipse.emf.henshin.model.Node;
import org.eclipse.emf.henshin.model.Parameter;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.Unit;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.ui.IWorkbenchPart;
import de.tub.tfs.henshin.tgg.TAttribute;
import de.tub.tfs.henshin.tgg.TEdge;
import de.tub.tfs.henshin.tgg.TGG;
import de.tub.tfs.henshin.tgg.TGGRule;
import de.tub.tfs.henshin.tgg.TNode;
import de.tub.tfs.henshin.tgg.TRule;
import de.tub.tfs.henshin.tgg.TripleComponent;
import de.tub.tfs.henshin.tgg.TripleGraph;
import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateAttributeConditionCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateNACCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateParameterCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateParameterMappingCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateRuleAttributeCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateRuleCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateRuleEdgeCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.CreateRuleNodeCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.InsertConcurrentRuleCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.MarkAttributeCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.MarkCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.MarkEdgeCommand;
import de.tub.tfs.henshin.tggeditor.commands.create.rule.ProcessRuleCommand;
import de.tub.tfs.henshin.tggeditor.commands.delete.rule.DeleteOpRuleCommand;
import de.tub.tfs.henshin.tggeditor.editparts.tree.TransformationSystemTreeEditPart;
import de.tub.tfs.henshin.tggeditor.editparts.tree.rule.RuleFolderTreeEditPart;
import de.tub.tfs.henshin.tggeditor.editparts.tree.rule.RuleTreeEditPart;
import de.tub.tfs.henshin.tggeditor.util.rule.concurrent.ConcurrentRuleComparator;
import de.tub.tfs.henshin.tggeditor.util.GraphicalNodeUtil;
import de.tub.tfs.henshin.tggeditor.util.rule.concurrent.ConcurrentRuleList;
import de.tub.tfs.henshin.tggeditor.util.rule.concurrent.ConcurrentRuleUtil;
import de.tub.tfs.henshin.tggeditor.util.rule.copy.Graph2GraphCopyMappingList;
/**The class GenerateConcurrentRulesAction generates concurrent rules from selcted TGG rules. The action
* is registered in the Contextmenu of the tree editor containing the TGG rules folder.
* This action merges selected rules and creates concurrent rules,
* which are necessary for the integration of the source and target graphs.
*
*
* @author G�rard Kirpach
*/
public class GenerateConcurrentRulesAction extends SelectionAction {
public static boolean calcInProgress = false;
/**
* The fully qualified ID.
*/
public static final String ID_1 = "de.tub.tfs.henshin.tggeditor.actions.GenerateConcurrentRulesAction";
public static final String ID_2 = "de.tub.tfs.henshin.tggeditor.actions.GenerateConcurrentRulesAction.Recursive";
/** The Constant DESC for the description. */
protected String DESC = "Generate Concurrent Rules";
/** The Constant TOOLTIP for the tooltip. */
protected String TOOLTIP = "Generates Concurrent Rules from selected Rules";
protected String DESC_SINGLE = "Generate Concurrent Rule";
protected String TOOLTIP_SINGLE = "Generates Concurrent Rule from selected Rule";
private boolean recursively = false;
/**
* Indicated whether a single Rule is selected
*/
protected boolean singleRule = false;
/**
* The selected rules from which the Concurrent Rules are generated.
*/
protected LinkedHashMap<TGGRule, RuleFolderTreeEditPart> sRule2Folder = new LinkedHashMap<TGGRule, RuleFolderTreeEditPart>();
/**
* The transformatin System module containing the selected objects
*/
protected Module transSystem;
/**
* All TGG rules initially present in the environment.
*/
private List<TGGRule> initialRules = new LinkedList<TGGRule>();
/**
* This attribute is used to memorize the order in which the rules appear in the RuleFolder.
* Based on this order, its helper functions decide the order of the newly generated methods with respect
* to the order of the initial rules.
*/
private ConcurrentRuleComparator ruleComparator;
//there are two insertion methods available that are chosen depending on this flag
// private final static boolean EXPLICIT_INSERTION = false;
/**
* The commands which are used to execute the generation of a concurrent rules from the selected TGG rules
*/
protected CompoundCommand command;
/**
* The constructor of the GenerateConcurrentTGGRulesAction
* @param part
*/
public GenerateConcurrentRulesAction(IWorkbenchPart part, boolean recursively) {
super(part);
setId(ID_1);
if (recursively){
DESC += " (recursively)";
TOOLTIP += " (recursively)";
setId(ID_2);
}
setDescription(DESC);
setText(DESC);
setToolTipText(TOOLTIP);
this.recursively = recursively;
}
/** Is only enabled for the context menu of a rule.
* @see org.eclipse.gef.ui.actions.WorkbenchPartAction#calculateEnabled()
*/
@Override
protected boolean calculateEnabled() {
final List<?> selectedObjects = getSelectedObjects();
sRule2Folder.clear();
initialRules.clear();
transSystem = null;
RuleFolderTreeEditPart sFolderTEP = null;
List<Unit> sSubRules = null;
RuleTreeEditPart sRuleTEP = null;
TGGRule sRule = null;
EditPart editpart = null;
if (selectedObjects == null || selectedObjects.isEmpty()) {
return false;
}
singleRule = (selectedObjects.size() == 1 && (selectedObjects.get(0) instanceof RuleTreeEditPart));
if (singleRule && recursively) return false;
setDescription((singleRule ? DESC_SINGLE : DESC));
setText((singleRule ? DESC_SINGLE : DESC));
setToolTipText((singleRule ? TOOLTIP_SINGLE : TOOLTIP));
for (Object selectedObject : selectedObjects) {
if (selectedObject == null || !(selectedObject instanceof EditPart)) {
continue; // filter out non EditParts
}
editpart = (EditPart) selectedObject;
if (transSystem == null) { // get TransformationSystem
editpart = (EditPart) selectedObject;
while (editpart != editpart.getRoot()
&& editpart != null
&& !(editpart instanceof TransformationSystemTreeEditPart)) {
editpart = editpart.getParent();
}
if (editpart == null) return false;
// editpart is the TransformationSystemTreeEditPart
if (! (editpart.getModel() instanceof Module)) return false; // selection is not within the tree
transSystem = (Module) editpart.getModel();
}
// case 1 : selected object is a rule
if ((selectedObject instanceof RuleTreeEditPart)
&& (((RuleTreeEditPart) selectedObject).getModel() instanceof TGGRule)) {
sRuleTEP = (RuleTreeEditPart) selectedObject;
sRule = (TGGRule) sRuleTEP.getModel();
// check whether selected rule is a transformation rule
if (!RuleUtil.TGG_RULE.equals(sRule.getMarkerType()))
return false;
sRule2Folder.put(sRule,
(RuleFolderTreeEditPart) sRuleTEP.getParent());
// case 2 : selected object is a folder
} else if (selectedObject instanceof RuleFolderTreeEditPart) {
sFolderTEP = (RuleFolderTreeEditPart) selectedObject;
sSubRules = new LinkedList<Unit>();
addAllSubRules(sFolderTEP, sSubRules);
for (Unit subRule : sSubRules) {
sRule = (TGGRule) subRule;
if (!RuleUtil.TGG_RULE.equals(sRule.getMarkerType())) {
return false;
}
}
}
}
if(transSystem==null) return false;
getInitialRules((IndependentUnit) transSystem.getUnit("RuleFolder"));
ruleComparator = new ConcurrentRuleComparator(initialRules);
return true;
}
private void getInitialRules(IndependentUnit folder){
if (folder == null) return;
for (Unit unit : folder.getSubUnits()) {
if (unit instanceof IndependentUnit){
getInitialRules((IndependentUnit) unit);
} else if (unit instanceof TGGRule && RuleUtil.TGG_RULE.equals(((TGGRule) unit).getMarkerType())){
initialRules.add((TGGRule) unit);
}
}
}
// protected boolean setAllRules() {
// allRules = new LinkedList<Rule> ();
// List<Unit> units = transSystem.getUnits();
// if (units == null || tRules == null) {
// return false;
// }
//
// Rule rule = null;
// for (Unit unit : units) {
// if (unit instanceof Rule) {
// rule = (Rule) unit;
// if (!isTRule(rule)) {
// RuleUtil.setLhsCoordinatesAndLayout(rule);
// allRules.add(rule);
// }
// }
// }
// return true;
// }
/**
* Given the RuleFolderTreeEditPart selectedFolderTEP of a root folder, this
* method fills selectedFolderSubRules with containing rules and subfolder
* rules.
*
*/
protected void addAllSubRules(
RuleFolderTreeEditPart selectedFolderTEP,
List<Unit> selectedFolderSubRules) {
TGGRule selectedRule = null;
if (sRule2Folder == null) {
return;
}
for (Object child : selectedFolderTEP.getChildren()) {
if (child instanceof EditPart) {
if (child instanceof RuleFolderTreeEditPart) {
addAllSubRules((RuleFolderTreeEditPart) child,
selectedFolderSubRules);
} else if (child instanceof RuleTreeEditPart) {
Object childRule = ((RuleTreeEditPart) child)
.getModel();
if (childRule instanceof TGGRule) {
selectedRule = (TGGRule) childRule;
selectedFolderSubRules.add(selectedRule);
sRule2Folder.put(selectedRule, selectedFolderTEP);
}
}
}
}
}
/**
* Executes the concurrent rule generation and insertion.
* @see ProcessRuleCommand
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
if ((sRule2Folder == null) || (initialRules == null) ) {
return;
}
calcInProgress = true;
// concurrent rules created in previous round
Map<TGGRule, RuleFolderTreeEditPart> prevConRule2Folder = new LinkedHashMap<TGGRule, RuleFolderTreeEditPart>();
// concurrent rules created in current round
Map<TGGRule, RuleFolderTreeEditPart> currConRule2Folder = new LinkedHashMap<TGGRule, RuleFolderTreeEditPart>();
// newly generated concurrent rules during all the rounds
Map<TGGRule, RuleFolderTreeEditPart> newConRule2Folder = new LinkedHashMap<TGGRule, RuleFolderTreeEditPart>();
// temporarily created concurrent rule that potentially doesn't collide with existing rules
List<TGGRule> candidateConRules = null;
// temporarily created concurrent rules that don't collide with existing rules
List<TGGRule> uniqueConRules = new LinkedList<TGGRule>();
//indicates whether equivalent rule already exists
boolean collision;
//parent folder of new rule
RuleFolderTreeEditPart parentFolder = null;
// flag that defines the sequence order of the rules to be merged
//temporary Rule for swap
TGGRule tmpRule = null;
// ...
boolean firstRound = true;
boolean swapped = true;
for (TGGRule sRule : sRule2Folder.keySet()){ // copy selected rules
currConRule2Folder.put(sRule, sRule2Folder.get(sRule));
}
while (!currConRule2Folder.isEmpty()) { // while new rules generated
prevConRule2Folder = currConRule2Folder;
currConRule2Folder = new HashMap<TGGRule, RuleFolderTreeEditPart>();
for (TGGRule ruleL : sRule2Folder.keySet()) {
for (TGGRule ruleR : prevConRule2Folder.keySet()) {
do { // executed once/ twice in initial/ successive round(s)
if (!singleRule && (ruleL == ruleR) ||
recursively && !firstRound &&
(ruleL.getName().contains(ruleR.getName())
|| ruleR.getName().contains(ruleL.getName()))) {
break;
}
if (ConcurrentRuleUtil.isConcurrent(ruleL, ruleR)) {
candidateConRules = new ConcurrentRuleList(ruleL, ruleR);
uniqueConRules.clear();
for (TGGRule candidate : candidateConRules) { //check uniqueness
collision = false;
for (TGGRule rule : newConRule2Folder.keySet()){
if (ConcurrentRuleUtil.equivalent(candidate, rule)) {
collision = true;
break;
}
}
if (collision) { break; }
for (TGGRule rule : initialRules) {
if (ConcurrentRuleUtil.equivalent(candidate, rule)) {
collision = true;
break;
}
}
if (!collision) {
uniqueConRules.add(candidate);
}
}
parentFolder = (!swapped
? sRule2Folder.get(ruleL)
: prevConRule2Folder.get(ruleL));
for (TGGRule uniqueConRule : uniqueConRules) { // update
currConRule2Folder.put(uniqueConRule, parentFolder);
newConRule2Folder.put(uniqueConRule, parentFolder);
}
}
tmpRule = ruleL; // swap rules
ruleL = ruleR;
ruleR = tmpRule;
swapped = !swapped; // flip
} while (swapped); // false after 1./ 2. loop in round 1/ >1
}
}
if (!recursively) { break; }
firstRound = false;
} // ...
command = new CompoundCommand();
//for (int index = 0; index < newConRule2Folder.keySet().size() - 1; index++) {//;
// for (Entry<Rule, RuleFolderTreeEditPart> nCREntry : newConRule2Folder.entrySet()) {
for (TGGRule newConRule : newConRule2Folder.keySet()) {
//tmpRule = allRules.get(index);
RuleFolderTreeEditPart folder = newConRule2Folder.get(newConRule);
// if (EXPLICIT_INSERTION) insertConcurrentRule(cRule,
// (IndependentUnit) folder.getModel()); else
command.add(new InsertConcurrentRuleCommand(newConRule,
(IndependentUnit) folder.getModel(), transSystem, ruleComparator));
}
// if (!EXPLICIT_INSERTION)
command.execute();
calcInProgress = false;
}
/***
* This method returns deepest common parent folder of both folders.
* @param ruleFolderEditPartLeft
* @param ruleFolderEditPartRight
* @return
*/
private RuleFolderTreeEditPart getCommonParentFolder(RuleFolderTreeEditPart ruleFolderEditPartLeft, RuleFolderTreeEditPart ruleFolderEditPartRight){
if (ruleFolderEditPartLeft==null || ruleFolderEditPartRight==null) return null;
EditPart editPartLeft = ruleFolderEditPartLeft;
EditPart editPartRight = ruleFolderEditPartRight;
RootEditPart root = editPartLeft.getRoot();
EditPart top = root;
boolean found = true;
while (found && containsSubEditPart(top, editPartLeft) && containsSubEditPart(top, editPartRight)){
found = false;
for (Object child : top.getChildren()){
if (child instanceof EditPart
&& containsSubEditPart((EditPart) child, editPartLeft)
&& containsSubEditPart((EditPart) child, editPartRight)){
top = (EditPart) child;
found = true;
break;
}
}
}
if (!(top.getModel() instanceof IndependentUnit)) {
return null;
}
return (RuleFolderTreeEditPart) top;
}
private boolean containsSubEditPart(EditPart top, EditPart toFind){
if (top==toFind) return true;
for (Object child : top.getChildren()){
if (child instanceof EditPart){
if (containsSubEditPart((EditPart) child, toFind)) return true;
}
}
return false;
}
/* unimportant section*/
//constructs and inserts the given rule into the folder
// private void insertConcurrentRule(Rule rule, IndependentUnit folder){
// Rule newRule = executeCreateEmptyRule(rule, folder);
// Graph2GraphCopyMappingList graphR2CopyWithoutEdges = new Graph2GraphCopyMappingList(rule.getRhs());
// HashMap<Node, Node> mappingGraphR2CopyWithoutEdges = new HashMap<Node, Node>();
// Graph graphWithoutEdges = graphR2CopyWithoutEdges.getGraphCopy();
// for (Node nodeR : rule.getRhs().getNodes()){
// TNode tNodeR = (TNode)nodeR;
// TNode nodeWithoutEdges = (TNode)graphR2CopyWithoutEdges.getImage(tNodeR);
// nodeWithoutEdges.getAttributes().clear();
// nodeWithoutEdges.setMarkerType(null);
// mappingGraphR2CopyWithoutEdges.put(tNodeR, nodeWithoutEdges);
// while(graphWithoutEdges.getEdges().size()!=0){
// graphWithoutEdges.removeEdge(graphWithoutEdges.getEdges().get(0));
// }
//
// Point p = new Point();
// p.x = tNodeR.getX();
// p.y = tNodeR.getY();
// TripleComponent tc = NodeUtil.guessTC(tNodeR);
// //TNode newNode =
// this.executeCreateNode(nodeWithoutEdges, p, tc);
//
// if (nodeWithoutEdges.getMarkerType()!=null){
// this.executeMarkNode(nodeWithoutEdges);
// }
//
// do{
// this.executeMarkNode(nodeWithoutEdges);
// }while(nodeWithoutEdges.getMarkerType()!=tNodeR.getMarkerType());
//
//
// for (Attribute oldAttr : tNodeR.getAttributes()){
// TAttribute newAttr = this.executeCreateAttribute(oldAttr);
// if (((TAttribute)oldAttr).getMarkerType()!=((TAttribute)newAttr).getMarkerType()){
// this.executeMarkAttribute(newAttr);
// }
// }
// }
//
// for (int i = 0; i < rule.getRhs().getEdges().size(); i++) {
// TEdge oldEdge = (TEdge) rule.getRhs().getEdges().get(i);
// TEdge newEdge = executeCreateEdge(
// mappingGraphR2CopyWithoutEdges.get(oldEdge.getSource()),
// mappingGraphR2CopyWithoutEdges.get(oldEdge.getTarget()),
// oldEdge.getType());
//
// // mark, if it was marked before
// if (oldEdge.getMarkerType() != newEdge.getMarkerType())
// executeMarkEdge(newEdge);
// }
//
// Iterator<Parameter> paramIterator = rule.getParameters().iterator();
// while (paramIterator.hasNext()){
// Parameter param = paramIterator.next();
// paramIterator.remove();
// param.setUnit(newRule);
// this.executeCreateParameter(param);
// }
// }
//
// /**
// * command for empty rule creation
// */
// private CreateRuleCommand ruleCommand;
// /**
// * command for attributeConditionCreation
// */
// private CreateAttributeConditionCommand attributeConditionCommand;
//
// private CreateNACCommand nacCommand;
// private CreateParameterCommand parameterCommand;
// private CreateParameterMappingCommand parameterMappingCommand;
//
// private CreateRuleAttributeCommand attributeCommand;
// private CreateRuleNodeCommand nodeCommand;
// private CreateRuleEdgeCommand edgeCommand;
//
// private MarkAttributeCommand markAttributeCommand;
// private MarkEdgeCommand markEdgeCommand;
// private MarkCommand markNodeCommand;
//
// //this method is currently not in use because concurrent rules should not be deleted
// //if other concurrent rules are created (otherwise one could not create concurrent rules one by one)
// //in case this featzure is required it is available in the following method
// private void cleanUpOldContainer(IndependentUnit ruleFolder, CompoundCommand cmd) {
// if (ruleFolder == null) return;
// for (Unit unit : ruleFolder.getSubUnits()) {
// if (unit instanceof IndependentUnit) {
// //cleanUpOldContainer((IndependentUnit) unit,cmd);
// } else if (unit instanceof Rule){
// Rule rule = (Rule) unit;
// //only delete concurrent rules from folder
// if (rule.getName().contains("Concurrent")){
// cmd.add(new DeleteOpRuleCommand(rule, ""));
// }
// }
// }
// }
//
// private void executeMarkNode(Node node){
// markNodeCommand = new MarkCommand(node);
// execute(markNodeCommand);
// }
//
// private Rule executeCreateEmptyRule(Rule rule, IndependentUnit folder){
// ruleCommand = new CreateRuleCommand(transSys, rule.getName(), folder);
// execute(ruleCommand);
// ((TripleGraph)ruleCommand.getRhsGraph()).setDividerCT_X(((TripleGraph)rule.getRhs()).getDividerCT_X());
// ((TripleGraph)ruleCommand.getRhsGraph()).setDividerSC_X(((TripleGraph)rule.getRhs()).getDividerSC_X());
// return ruleCommand.getRule();
// }
//
// private TNode executeCreateNode(TNode nodeWithoutEdges, Point p, TripleComponent tc){
// ruleCommand.getRhsGraph().getNodes().add(nodeWithoutEdges);
// nodeWithoutEdges.setGraph(ruleCommand.getRhsGraph());
// nodeCommand = new CreateRuleNodeCommand(nodeWithoutEdges, ruleCommand.getRhsGraph(), p, tc);
// execute(nodeCommand);
// return ((TNode)nodeCommand.getNode());
// }
//
// private TEdge executeCreateEdge(Node source, Node target, EReference type){
// edgeCommand = new CreateRuleEdgeCommand(ruleCommand.getRhsGraph(), source, target, type);
// execute(edgeCommand);
// return (TEdge)edgeCommand.getEdge();
// }
//
// private void executeMarkEdge(Edge edge){
// markEdgeCommand = new MarkEdgeCommand(edge);
// execute(markEdgeCommand);
// }
//
// private TAttribute executeCreateAttribute(Attribute attribute){
// attributeCommand = new CreateRuleAttributeCommand(nodeCommand.getNode(), attribute.getType().getName());
// attributeCommand.setAttributeType(attribute.getType());
// attributeCommand.setValue(attribute.getValue());
// attributeCommand.execute();
// return (TAttribute)attributeCommand.getAttribute();
// }
//
// private void executeMarkAttribute(Attribute attribute){
// markAttributeCommand = new MarkAttributeCommand(attribute);//attributeCommand.getAttribute());
// execute(markAttributeCommand);
// }
//
// private void executeCreateParameter(Parameter parameter){
// this.parameterCommand = new CreateParameterCommand(parameter.getUnit(), parameter);//attributeCommand.getAttribute());
// execute(parameterCommand);
// }
}