/******************************************************************************* * 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.util.rule.concurrent; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.henshin.model.Attribute; import org.eclipse.emf.henshin.model.AttributeCondition; import org.eclipse.emf.henshin.model.Edge; import org.eclipse.emf.henshin.model.Graph; import org.eclipse.emf.henshin.model.GraphElement; 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.impl.AttributeConditionImpl; import org.eclipse.emf.henshin.model.impl.MappingListImpl; import de.tub.tfs.henshin.tgg.TAttribute; import de.tub.tfs.henshin.tgg.TNode; import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil; import de.tub.tfs.henshin.tggeditor.util.GraphicalNodeUtil; import de.tub.tfs.henshin.tggeditor.util.rule.copy.Graph2GraphCopyMappingList; public class GraphIntersectionHandler extends MappingListImpl{ private static final long serialVersionUID = 6055470032042933242L; private HashMap<String, String> noneValue2Value; private HashMap<String, String> oldNoneValue2Value; private Set<Set<String>> noneValueCosets; private Set<Set<String>> oldNoneValueCosets; private Set<String> noneValuesBelongingToCoset; private AttributeCondition attributeCondition; private boolean consistent = true; private HashMap<String, String> hash2Value; public GraphIntersectionHandler(Graph leftGraph, Graph rightGraph){ super(); this.noneValue2Value = new HashMap<String, String>(); this.oldNoneValue2Value = new HashMap<String, String>(); this.noneValueCosets = new HashSet<Set<String>>(); this.oldNoneValueCosets = new HashSet<Set<String>>(); this.noneValuesBelongingToCoset = new HashSet<String>(); this.hash2Value = new HashMap<String, String>(); this.graphL = leftGraph; this.graphR = rightGraph; this.graphL2GraphCopy = new Graph2GraphCopyMappingList(this.graphL); this.graphR2GraphCopy = new Graph2GraphCopyMappingList(this.graphR); this.edges = new HashSet<Edge>(); } public boolean isConsistent(){ return consistent; } public AttributeCondition getAttributeCondition(){ if (this.attributeCondition!=null) return this.attributeCondition; this.attributeCondition = new AttributeConditionImpl(); String conditionText = ""; for (Set<String> coset : noneValueCosets){ String prev = null; String value = null; for (String param : coset){ if (prev!=null){ if (!conditionText.equals("")){ conditionText+=" && "; } conditionText+="("+prev+")==("+param+")"; //value = noneValue2Value.remove(prev); } prev = param; } value = noneValue2Value.get(prev); if (value!=null){ conditionText += "&& ("+prev+")==("+value+")"; } } for (String noneValue : this.noneValue2Value.keySet()){ if (!this.noneValuesBelongingToCoset.contains(noneValue)){ String value = this.noneValue2Value.get(noneValue); if (!conditionText.equals("")){ conditionText+=" && "; } conditionText+="("+noneValue+"=="+value+")"; } } this.attributeCondition.setConditionText(conditionText); return this.attributeCondition; } public void persistTemporaryChanges(){ //copy paramCosets; boolean empty = this.noneValueCosets.isEmpty(); if (!changed) return; this.oldNoneValueCosets.clear(); this.oldNoneValueCosets = new HashSet<Set<String>>(); for (Set<String> coset : this.noneValueCosets){ this.oldNoneValueCosets.add(new HashSet<String>(coset)); } Test.check(this.noneValueCosets.isEmpty()==empty); empty = this.noneValue2Value.isEmpty(); this.oldNoneValue2Value.clear(); this.oldNoneValue2Value = new HashMap<String, String>(this.noneValue2Value); Test.check(this.noneValue2Value.isEmpty()==empty); this.changed = false; this.consistent = true; } //private boolean unChanged = false; private boolean changed = false; public boolean undoTemporaryChanges(){ if (!changed) return false; this.noneValueCosets.clear(); this.noneValueCosets = new HashSet<Set<String>>(); for (Set<String> coset : this.oldNoneValueCosets){ this.noneValueCosets.add(new HashSet<String>(coset)); } this.noneValue2Value.clear(); this.noneValue2Value = new HashMap<String, String>(this.oldNoneValue2Value); this.changed = false; this.consistent = true; return true; } private boolean isIntersectingNodes(Node nodeLRuleL, Node nodeLRuleR, boolean add){ if (this.getImage(nodeLRuleL, nodeLRuleR.getGraph())==nodeLRuleR) return true; Node nodeRRuleR = RuleUtil.getRHSNode(nodeLRuleR); if (!ConcurrentRuleUtil.sameNodeType(nodeLRuleL, nodeLRuleR)) return false; if (nodeLRuleL instanceof TNode && nodeLRuleR instanceof TNode && !GraphicalNodeUtil.guessTC((TNode)nodeLRuleL).equals(GraphicalNodeUtil.guessTC((TNode)nodeLRuleR))) return false; if (nodeLRuleL instanceof TNode && nodeLRuleR instanceof TNode && GraphicalNodeUtil.isCorrNode((TNode)nodeLRuleL) && GraphicalNodeUtil.isCorrNode((TNode)nodeLRuleR)) return true; EList<Attribute> attributesNodeLRuleL = nodeLRuleL.getAttributes(); EList<Attribute> attributesNodeLRuleR = nodeLRuleR.getAttributes(); //persistTemporaryChanges(); for (Attribute attributeNodeLRuleR : attributesNodeLRuleR){ boolean attributeNodeLRuleLFound = false; for (Attribute attributeNodeLRuleL : attributesNodeLRuleL){ if (attributeNodeLRuleL.getType().getName().equals(attributeNodeLRuleR.getType().getName())){ String valueL = attributeNodeLRuleL.getValue(); String valueR = attributeNodeLRuleR.getValue(); attributeNodeLRuleLFound = (valueL==null ? valueR==null : valueL.equals(valueR)); if (!attributeNodeLRuleLFound){ if (isValue(valueL) && !isValue(valueR)){ attributeNodeLRuleLFound = addValues(valueL, valueR, add); }else if (isValue(valueR) && !isValue(valueL)){ attributeNodeLRuleLFound = addValues(valueR, valueL, add); }else if (!isValue(valueL) && !isValue(valueR)){ attributeNodeLRuleLFound = addNoneValues(valueL, valueR, add); } } if (attributeNodeLRuleLFound) break; } } if (!attributeNodeLRuleLFound) { return false; } } //attributes from NodeLRuleL that are not in NodeLRuleR are only valid if they are not created by ruleR in case they are unique //if not they would be overwritten by the attribute creation of the rule for (Attribute attributeNodeLRuleL : attributesNodeLRuleL){ if (!attributeNodeLRuleL.getType().isUnique()) continue; boolean attributeNodeLRuleRFound = false; for (Attribute attributeNodeLRuleR : attributesNodeLRuleR){ if (attributeNodeLRuleL.getType().getName().equals(attributeNodeLRuleR.getType().getName())){ String valueL = attributeNodeLRuleL.getValue(); String valueR = attributeNodeLRuleR.getValue(); attributeNodeLRuleRFound = (valueL==null ? valueR==null : valueL.equals(valueR)); if (!attributeNodeLRuleRFound && !(isValue(valueL) && isValue(valueR))) attributeNodeLRuleRFound=true; if (attributeNodeLRuleRFound) break; } } //if new attribute if (!attributeNodeLRuleRFound && nodeRRuleR!=null){ EList<Attribute> attributesNodeRRuleR = nodeRRuleR.getAttributes(); for (Attribute attributeNodeRRuleR : attributesNodeRRuleR){ if (attributeNodeLRuleL.getType().getName().equals(attributeNodeRRuleR.getType().getName())){ return false; } } } } return true; } private boolean addValues(String value, String noneValue, boolean add){ if (!isValue(value)) throw new IllegalArgumentException(value+" is not a valid value"); if (!addValue(value, noneValue, add)) { return false; } Set<String> coSet = getCoset(noneValue); if (coSet!=null){ boolean inConsistent = false; for (String nValue : coSet){ if (!addValue(value, nValue, add)){ if (inConsistent && add) consistent = false; return false; } inConsistent = true; } } if (add) this.changed = true; return true; } private boolean addValue(String value, String noneValue, boolean add){ if (this.noneValue2Value.containsKey(noneValue)){ String val = this.noneValue2Value.get(noneValue); return val.equals(value); } if (!add) return true; this.noneValue2Value.put(noneValue, value); this.changed = true; return true; } private String getValue(String noneValue){ return noneValue2Value.get(noneValue); } private Set<String> getCoset(String noneValue){ for (Set<String> noneValueCoset : noneValueCosets){ if (noneValueCoset.contains(noneValue)) return noneValueCoset; } return null; } private boolean addNoneValues(String noneValue1, String noneValue2, boolean add){ String value1 = getValue(noneValue1); String value2 = getValue(noneValue2); if (value1!=null && value2!=null && !value1.equals(value2)) return false; if (!add) return true; Set<String> coset1 = getCoset(noneValue1); Set<String> coset2 = getCoset(noneValue2); if (coset1!=null && coset2==null){ /*if (value2!=null ){ if (!addValues(value2, noneValue1)) Test.check(false); }else if (value1!=null){ if (!addValue(value1, noneValue2)) Test.check(false); }*/ coset1.add(noneValue2); }else if (coset2!=null && coset1==null){ /*if (value1!=null ){ if (!addValues(value1, noneValue2)) Test.check(false); }else if (value2!=null){ if (!addValue(value2, noneValue1)) Test.check(false); }*/ coset2.add(noneValue1); }else if (coset2==null && coset1==null){ Set<String> set = new HashSet<String>(); set.add(noneValue1); set.add(noneValue2); this.noneValuesBelongingToCoset.add(noneValue1); this.noneValuesBelongingToCoset.add(noneValue2); this.noneValueCosets.add(set); /* if (value1!=null){ if (!addValue(value1,noneValue2)) Test.check(false); }else if (value2!=null){ if (!addValue(value2, noneValue1)) Test.check(false); }*/ }else{ if (coset1 != coset2){ coset1.addAll(coset2); noneValueCosets.remove(coset2); } } changed = true; if (value1==null && value2!=null && !addValues(value2, noneValue1, add)) Test.check(false); if (value2==null && value1!=null && !addValues(value1, noneValue2, add)) Test.check(false); return true; } //NEW public boolean isValue(String value){ if (value==null) return true; if (graphL.getRule()!=null) for (Parameter p : graphL.getRule().getParameters()){ if (value.equals(p.getName())) return false; } if (graphR.getRule()!=null) for (Parameter p : graphR.getRule().getParameters()){ if (value.equals(p.getName())) return false; } boolean result = ( ( ((value.charAt(0)=='"' && value.charAt(value.length()-1)=='"') || (value.charAt(0)=='\'' && value.charAt(value.length()-1)=='\'') ) && (!value.substring(1, value.length()-1).contains("\"")) && (!value.substring(1, value.length()-1).contains("\'")) ) ||isNumberValue(value) || !(value.contains("+") || value.contains("-") || value.contains("/") || value.contains("*")) ); return result; } //NEW public static boolean isNumberValue(String number){ try{ Integer.valueOf(number); }catch(NumberFormatException e){ try{ Double.valueOf(number); }catch(NumberFormatException exception){ return false; } } return true; } //NEW public static boolean isParam(String param){ boolean specialChar = //param.contains("\"") || param.contains("\'") || param.contains("+") || param.contains("-") || param.contains("\\") || param.contains("/") || param.contains("%") || param.contains("*") || isNumberValue(param); return !specialChar; } public boolean corresponds(Attribute att1, Attribute att2){ if (!ConcurrentRuleUtil.equivalent(att1, att2)) { return false; }; String attribute1 = att1.getValue(); String attribute2 = att2.getValue(); if (attribute1 == null && attribute2 == null) { return true; } for (String noneValue : this.noneValue2Value.keySet()){ String value = this.noneValue2Value.get(noneValue); if (attribute1.equals(value)){ return attribute2.equals(noneValue); } if (attribute2.equals(value)){ return attribute1.equals(noneValue); } } for(Set<String> coset : this.noneValueCosets){ boolean found = false; for (String noneValue : coset){ if (noneValue.equals(attribute1)){ found = true; break; } } if (found){ for (String noneValue : coset){ if (noneValue.equals(attribute2)){ return true; } } } } return false; } //NEW private String getValueFromExpression(Attribute a){ String value = a.getValue(); if (!isValue(value)){ value = a.getValue().hashCode()+""; } return value; } private void updateRuleNodeAttributeMarkers(Rule rule){ //MappingListImpl attributeMapping = new MappingListImpl(); for (Node nodeR : rule.getRhs().getNodes()){ Node nodeL = rule.getMappings().getOrigin(nodeR); if (nodeL==null){ for(Attribute aR: nodeR.getAttributes()){ ((TAttribute)aR).setMarkerType(RuleUtil.NEW); } }else{ for (Attribute aR : nodeR.getAttributes()){ boolean aLFound = false; for (Attribute aL : nodeL.getAttributes()){ if (aL.getType().getName().equals(aR.getType().getName())){ if (this.corresponds(aL, aR)){ ((TAttribute)aR).setMarkerType(null); aLFound = true; break; } } } if (!aLFound){ ((TAttribute)aR).setMarkerType(RuleUtil.NEW); } } } } } public void replaceAttributeHashValuesWithInitialExpressionsAndSetMarkers(Rule rule, HashMap<String, String> id2Value){ for(Node nodeR : rule.getRhs().getNodes()){ for (Attribute attribute : nodeR.getAttributes()){ String id = attribute.getValue(); String value = id2Value.get(id); if(value==null){ id = "\""+id+"\""; value = id2Value.get(id); } attribute.setValue(value); } } for(Node nodeL : rule.getLhs().getNodes()){ for (Attribute attribute : nodeL.getAttributes()){ String id = attribute.getValue(); String value = id2Value.get(id); if(value==null){ id = "\""+id+"\""; value = id2Value.get(id); } if (value==null){ System.out.println(attribute.getType().getName()+"with "+id+" has no value for "+value); } attribute.setValue(value); } } this.updateRuleNodeAttributeMarkers(rule); } private boolean correspondingHash(String hashValue, String attributeValue){ try{ int hash = Integer.valueOf(hashValue); if (hash==attributeValue.hashCode()){ return true; } }catch(Exception e){ } return (hashValue.equals("\""+attributeValue.hashCode()+"\"") || hashValue.equals("\'"+attributeValue.hashCode()+"\'")); } private boolean replaceHashValueWithInitialValue(Attribute attribute){ String hashCode = attribute.getValue(); /** for (Set<String> coSet : this.noneValueCosets){ for (String noneValue : coSet){ System.out.println("None Value "+noneValue); if (correspondingHash(hashCode, noneValue)){ System.out.println(hashCode+ "corresponds to "+noneValue); attribute.setValue(noneValue); return true; } } } for (String noneValue : this.noneValue2Value.keySet()){ String value = this.noneValue2Value.get(noneValue); if (correspondingHash(hashCode, noneValue)){ System.out.println(hashCode+ "corresponds to "+noneValue); attribute.setValue(noneValue); return true; } if (correspondingHash(hashCode, value)){ System.out.println(hashCode+ "corresponds to "+value); attribute.setValue(value); return true; } }**/ String value= this.hash2Value.get(hashCode); if (value!=null){ attribute.setValue(value); return true; }else return false; } /*********************************************************************************/ private Graph2GraphCopyMappingList graphL2GraphCopy; private Graph2GraphCopyMappingList graphR2GraphCopy; private Graph graphL; private Graph graphR; private Set<Edge> edges; //private Set<Node> nodes; public Graph2GraphCopyMappingList getGraphL2GraphLCopy() { return graphL2GraphCopy; } public Graph2GraphCopyMappingList getGraphR2GraphRCopy() { return graphR2GraphCopy; } public Graph getGraphL() { return graphL; } public Graph getGraphR() { return graphR; } //return either isConsitently Present, is present but not consistent(oronly partially present)or isnot present at all (unknown) private Boolean isConsistent(Node origin, Node image){ if (containsOrigin(origin) && containsImage(image)){ return (getImage(origin, image.getGraph())==getOrigin(image)); }else if (containsOrigin(origin) || containsImage(image)){ return false; }else return null; } public boolean addIntersection(Node nodeL, Node nodeR){ if (!validIntersection(nodeL, nodeR, true)){ return false; } this.add(nodeL, nodeR); return true; } public boolean potentialIntersection(Node nodeL, Node nodeR){ return validIntersection(nodeL, nodeR, false); } private boolean validIntersection(Node nodeL, Node nodeR, boolean add){ if (this.getImage(nodeL, nodeR.getGraph())==nodeR) return true; if (!ConcurrentRuleUtil.sameNodeType(nodeL, nodeR)) return false; Boolean isConsistent = isConsistent(nodeL, nodeR); if (isConsistent!=null && !isConsistent) return false; //if (!checkBijectivity(nodeL, nodeR)) return false; return isIntersectingNodes(nodeL, nodeR, add); } private boolean checkBiject( Node iNodeGraphCopyL, Node iNodeGraphCopyR){ boolean inComingEdges = false; boolean nodesSwaped = false; do{ EList<Edge> edgesiNodeGraphCopyL = (inComingEdges ? iNodeGraphCopyL.getIncoming() : iNodeGraphCopyL.getOutgoing()); for (Edge edgeiNodeGraphCopyL : edgesiNodeGraphCopyL){ Node iNodeGraphCopyLAssociate = (inComingEdges ? edgeiNodeGraphCopyL.getSource() : edgeiNodeGraphCopyL.getTarget()); //skip each associated node that is not part of the intersection boolean partOfIntersection = (nodesSwaped ? this.containsImage(iNodeGraphCopyLAssociate) : this.containsOrigin(iNodeGraphCopyLAssociate)); if (!partOfIntersection) continue; boolean iNodeGraphCopyRAssociateFound = false; EList<Edge> edgesiNodeGraphCopyR = (inComingEdges ? iNodeGraphCopyR.getIncoming() : iNodeGraphCopyR.getOutgoing()); for (Edge edgeiNodeGraphCopyR : edgesiNodeGraphCopyR){ Node iNodeGraphCopyRAssociate = (inComingEdges ? edgeiNodeGraphCopyR.getSource() : edgeiNodeGraphCopyR.getTarget()); boolean intersectingNodes = (nodesSwaped ? isIntersectingNodes(iNodeGraphCopyRAssociate, iNodeGraphCopyLAssociate, false) : isIntersectingNodes(iNodeGraphCopyLAssociate, iNodeGraphCopyRAssociate, false)); if (intersectingNodes){ boolean consistent = (nodesSwaped ? this.getImage(iNodeGraphCopyRAssociate)!=iNodeGraphCopyLAssociate || this.getOrigin(iNodeGraphCopyLAssociate)!=iNodeGraphCopyRAssociate : this.getImage(iNodeGraphCopyLAssociate)!=iNodeGraphCopyRAssociate || this.getOrigin(iNodeGraphCopyRAssociate)!=iNodeGraphCopyLAssociate); if (!consistent) continue; iNodeGraphCopyRAssociateFound = true; } } if (!iNodeGraphCopyRAssociateFound) { System.out.println("Nonbijective, edge missing in GraphR"); return false; } } if (inComingEdges && nodesSwaped) break; //false -> true -> false -> true inComingEdges=!inComingEdges; //true -> false -> true if (!inComingEdges){//false -> true-> false Node tmp = iNodeGraphCopyL; iNodeGraphCopyL = iNodeGraphCopyR; iNodeGraphCopyR = tmp; //false -> true -> true nodesSwaped = true; } } while(true); return true; } public Attribute getImage(Attribute origin) { return super.getImage(origin, graphR2GraphCopy.getGraphCopy()); } public <E extends GraphElement> E getImage(E origin) { return super.getImage(origin, graphR2GraphCopy.getGraphCopy()); } public boolean containsOrigin(Node node){ return this.getImage(node)!=null; } public boolean containsImage(Node node){ return this.getOrigin(node)!=null; } }