/**
* <copyright>
* Copyright (c) 2010-2014 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
* </copyright>
*/
package org.eclipse.emf.henshin.interpreter.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.henshin.interpreter.EGraph;
import org.eclipse.emf.henshin.interpreter.matching.conditions.ConditionHandler;
import org.eclipse.emf.henshin.interpreter.matching.conditions.IFormula;
import org.eclipse.emf.henshin.interpreter.matching.constraints.AttributeConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot;
import org.eclipse.emf.henshin.interpreter.matching.constraints.ReferenceConstraint;
import org.eclipse.emf.henshin.interpreter.matching.constraints.SolutionFinder;
import org.eclipse.emf.henshin.interpreter.matching.constraints.Variable;
/**
* A graph isomorphy checker for {@link EGraph}s.
* @author Christian Krause
*/
public class EGraphIsomorphyChecker {
// Attribute condition handles (used internally for the match finding):
private static final ConditionHandler ATTRIBUTE_CONDITION_HANDLER;
// Initialize static members:
static {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
ATTRIBUTE_CONDITION_HANDLER = new ConditionHandler(new HashMap<String, Collection<String>>(), engine);
}
// The source graph:
private final EGraph source;
// Number of links in the source graph:
private int linkCount;
// Ignored attributes:
private final List<EAttribute> ignoredAttributes;
// Object variables map:
private Map<EObject, Variable> variablesMap;
// Variables as a list:
private List<Variable> variablesList;
/**
* Default constructor.
* @param source Source graph.
* @param useAttributes Flag indicating whether attribute values should be used.
*/
public EGraphIsomorphyChecker(final EGraph source, List<EAttribute> ignoredAttributes) {
this.source = source;
this.ignoredAttributes = ignoredAttributes;
this.linkCount = InterpreterUtil.countEdges(source);
initVariables();
}
/*
* Initialize variables.
*/
private void initVariables() {
// Instantiate variables map and list:
int objectCount = source.size();
variablesMap = new HashMap<EObject, Variable>(objectCount);
variablesList = new ArrayList<Variable>(objectCount);
// Create a variable for every object:
for (EObject object : source) {
Variable variable = new Variable(object.eClass(), true);
variablesMap.put(object, variable);
variablesList.add(variable);
}
// Create constraints:
for (Map.Entry<EObject, Variable> entry : variablesMap.entrySet()) {
EObject object = entry.getKey();
Variable variable = entry.getValue();
// Create attribute constraints if necessary:
for (EAttribute attr : object.eClass().getEAllAttributes()) {
if (ignoredAttributes==null || !ignoredAttributes.contains(attr)) {
variable.attributeConstraints.add(new AttributeConstraint(attr, object.eGet(attr), true));
}
}
// Create reference constraints:
for (EReference ref : object.eClass().getEAllReferences()) {
if (ref.isMany()) {
@SuppressWarnings("unchecked")
EList<EObject> targets = (EList<EObject>) object.eGet(ref);
for (EObject target : targets) {
variable.referenceConstraints.add(new ReferenceConstraint(variablesMap.get(target), ref));
}
} else {
EObject target = (EObject) object.eGet(ref);
if (target!=null) {
variable.referenceConstraints.add(new ReferenceConstraint(variablesMap.get(target), ref));
}
}
}
}
}
/**
* Check whether the argument graph is isomorphic to the source graph.
* @param graph Graph for which you want to check isomorphy.
* @param partialMatch Optional partial match from source to the argument graph.
* @return <code>true</code> if the source and the graph are isomorphic.
*/
public boolean isIsomorphicTo(EGraph graph, Map<EObject,EObject> partialMatch) {
// We do a quick comparison of the object count first:
if (source.size()!=graph.size()) {
return false;
}
// Create the domain map:
Map<Variable, DomainSlot> domainMap = new HashMap<Variable, DomainSlot>();
// Create the domain slots:
for (Map.Entry<EObject, Variable> entry : variablesMap.entrySet()) {
DomainSlot domainSlot = new DomainSlot(ATTRIBUTE_CONDITION_HANDLER,
new HashSet<EObject>(), true, false, true, true);
if (partialMatch!=null) {
EObject match = partialMatch.get(entry.getKey());
if (match!=null) {
domainSlot.fixInstantiation(match);
}
}
domainMap.put(entry.getValue(), domainSlot);
}
// Create the match finder:
SolutionFinder matchFinder = new SolutionFinder(graph, domainMap, ATTRIBUTE_CONDITION_HANDLER);
matchFinder.variables = variablesList;
matchFinder.formula = IFormula.TRUE;
// Try to find a match:
if (!matchFinder.findSolution()) {
return false;
}
// We still need to verify that the link count is the same:
if (linkCount!=InterpreterUtil.countEdges(graph)) {
return false;
}
// No reason why they shouldn't be isomorphic:
return true;
}
}