blob: 533de9fc277532feff4907d48cc457d7baa23e39 [file] [log] [blame]
package org.eclipse.emf.henshin.variability.wrapper;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.henshin.model.Action;
import org.eclipse.emf.henshin.model.Annotation;
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.MappingList;
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.ParameterMapping;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.Unit;
import org.eclipse.emf.henshin.model.impl.HenshinFactoryImpl;
import org.eclipse.emf.henshin.variability.matcher.FeatureExpression;
import aima.core.logic.common.ParserException;
import aima.core.logic.propositional.parsing.ast.PropositionSymbol;
import aima.core.logic.propositional.parsing.ast.Sentence;
import aima.core.logic.propositional.visitors.SymbolCollector;
/**
* This class wraps an instance of {@link org.eclipse.emf.henshin.model.Rule} and adds variability awareness to it.
* The variability awareness is enabled by adding multiple {@link org.eclipse.emf.henshin.model.Annotation}s to the wrapped object.
*
* @author Stefan Schulz
*
*/
public class VariabilityRule implements Rule {
final Rule rule;
final Annotation featureModel;
final Annotation injectiveMatchingPresenceCondition;
final Annotation features;
static Annotation[] addVariabilityToRule(Rule rule, boolean transactional) {
Annotation[] result = new Annotation[3];
Annotation featModel = null;
Annotation injMatPreCon = null;
Annotation feats = null;
EList<Annotation> annos = rule.getAnnotations();
Iterator<Annotation> it = annos.iterator();
while(it.hasNext()) {
Annotation anno = it.next();
String key = anno.getKey();
if(key.equals(VariabilityConstants.FEATURE_MODEL)) {
featModel = anno;
} else if(key.equals(VariabilityConstants.INJECTIVE_MATCHING_PC)) {
injMatPreCon = anno;
} else if(key.equals(VariabilityConstants.FEATURES)) {
feats = anno;
}
if(featModel != null && injMatPreCon != null && feats != null) {
break;
}
}
if(featModel != null) {
result[0] = featModel;
} else if (transactional) {
result[0] = VariabilityTransactionHelper.addAnnotation(rule, VariabilityConstants.FEATURE_MODEL, "");
} else {
result[0] = VariabilityHelper.addAnnotation(rule, VariabilityConstants.FEATURE_MODEL, "");
}
if(injMatPreCon != null) {
result[1] = injMatPreCon;
} else if (transactional) {
result[1] = VariabilityTransactionHelper.addAnnotation(rule, VariabilityConstants.INJECTIVE_MATCHING_PC, "");
} else {
result[1] = VariabilityHelper.addAnnotation(rule, VariabilityConstants.INJECTIVE_MATCHING_PC, "");
}
if(feats != null) {
result[2] = feats;
} else if (transactional) {
result[2] = VariabilityTransactionHelper.addAnnotation(rule, VariabilityConstants.FEATURES, "");
} else {
result[2] = VariabilityHelper.addAnnotation(rule, VariabilityConstants.FEATURES, "");
}
return result;
}
/**
* Creates a new {@link org.eclipse.emf.henshin.model.Rule} and makes it variability aware.
*/
VariabilityRule() {
this(HenshinFactoryImpl.eINSTANCE.createRule());
}
/**
* Creates a new {@link org.eclipse.emf.henshin.model.Rule} with the given name and makes it variability aware.
* @param name the name of the new henshin rule
*/
VariabilityRule(String name) {
this(HenshinFactoryImpl.eINSTANCE.createRule(name));
}
/**
* Adds multiple {@link org.eclipse.emf.henshin.model.Annotation} to the given {@link org.eclipse.emf.henshin.model.Rule} in order to enable variability awareness.
* @param rule
*/
VariabilityRule(Rule rule) {
this(rule, false);
}
/**
* Adds multiple {@link org.eclipse.emf.henshin.model.Annotation} to the given {@link org.eclipse.emf.henshin.model.Rule} in order to enable variability awareness.
* @param rule
* @param transactional
*/
VariabilityRule(Rule rule, boolean transactional) {
this.rule = rule;
Annotation[] annos = addVariabilityToRule(rule, transactional);
featureModel = annos[0];
injectiveMatchingPresenceCondition = annos[1];
features = annos[2];
}
/**
* Returns the feature model of this Rule.
* @return the feature model of this Rule.
*/
public String getFeatureModel() {
return featureModel.getValue();
}
/**
* Sets this Rule's feature model to the given model.
* @param featureModelString the feature model to be set for this Rule.
*/
public void setFeatureModel(String featureModelString) {
featureModel.setValue(featureModelString);
//TODO: Update list of features
}
/**
* Returns the injective matching presence condition of this Rule.
* @return the injective matching presence condition of this Rule.
*/
public String getInjectiveMatchingPresenceCondition() {
return injectiveMatchingPresenceCondition.getValue();
}
/**
* Returns the injective matching presence condition for this Rule.
* @param condition the injective matching presence condition to be set for this Rule.
*/
public void setInjectiveMatchingPresenceCondition(String condition) {
injectiveMatchingPresenceCondition.setValue(condition);
}
/**
* Returns an unmodifiable list of this Rule's features.
* @return an {@link java.util.List} containing this Rule's variability points.
*/
public List<String> getFeatures() {
if(features.getValue() == null) {
return null;
}
if (!features.getValue().isEmpty()) {
List<String> featureList = Arrays.asList(features.getValue().split(","));
List<String> result = new ArrayList<String>();
for (String string : featureList) {
result.add(string.trim());
}
return Collections.unmodifiableList(result);
} else {
return Collections.unmodifiableList(new ArrayList<String>());
}
}
/**
* Sets the features of this Rule.
* @param featureString the string containing all features
*/
public void setFeatures(String featureString) {
features.setValue(featureString);
}
/**
* Sets the features of this Rule.
* @param featureList the list containing all features
*/
public void setFeatures(List<String> featureList) {
for (String feature : featureList) {
addFeature(feature);
}
}
/**
* Adds the given feature to this Rule.
* @param feature the variability point to be added to this
*/
public void addFeature(String feature) {
if(features.getValue() == null) {
features.setValue("");
}
String featuresString = features.getValue();
featuresString += (featuresString.length() > 0) ? "," + features : features;
features.setValue(featuresString);
}
/**
* Removes the given feature if it is a part of this Rule.
* @param variabilityPoint the variability point to be removed.
*/
public void removeFeature(String feature) {
if(features.getValue() == null) {
return;
}
List<String> featureList = new ArrayList<String>(Arrays.asList(features.getValue().split(",")));
featureList.remove(feature);
features.setValue(String.join(",", featureList));
}
String oldFeatureModel = "";
String oldFeatures = "";
List<String> missingFeatures = new ArrayList<String>();;
public boolean hasMissingFeatures() {
calculateMissingFeatureNames();
return missingFeatures == null || !missingFeatures.isEmpty();
}
public String[] getMissingFeatures() {
calculateMissingFeatureNames();
return missingFeatures.toArray(new String[0]);
}
private void calculateMissingFeatureNames() {
String currentModel = getFeatureModel();
if (!currentModel.trim().equals(oldFeatureModel) || !oldFeatures.equals(features.getValue())) {
try {
Sentence sentence = FeatureExpression.getExpr(currentModel);
missingFeatures.clear();
Set<PropositionSymbol> symbols = SymbolCollector.getSymbolsFrom(sentence);
List<String> definedFeatures = getFeatures();
for (PropositionSymbol symbol : symbols) {
String symbolName = symbol.getSymbol();
if (!definedFeatures.contains(symbolName)) {
missingFeatures.add(symbolName);
}
}
} catch (ParserException e) {} //TODO: Validate model before parsing
oldFeatureModel = currentModel.trim();
oldFeatures = features.getValue();
}
}
/**
* @see org.eclipse.emf.henshin.model.Rule#canCreateEdge(org.eclipse.emf.henshin.model.Node, org.eclipse.emf.henshin.model.Node, org.eclipse.emf.ecore.EReference)
*/
public boolean canCreateEdge(Node arg0, Node arg1, EReference arg2) {
return rule.canCreateEdge(arg0, arg1, arg2);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#createEdge(org.eclipse.emf.henshin.model.Node, org.eclipse.emf.henshin.model.Node, org.eclipse.emf.ecore.EReference)
*/
public Edge createEdge(Node arg0, Node arg1, EReference arg2) {
return rule.createEdge(arg0, arg1, arg2);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#createNode(org.eclipse.emf.ecore.EClass)
*/
public Node createNode(EClass arg0) {
return rule.createNode(arg0);
}
/**
* @see org.eclipse.emf.common.notify.Notifier#eAdapters()
*/
public EList<Adapter> eAdapters() {
return rule.eAdapters();
}
/**
* @see org.eclipse.emf.common.notify.Notifier#eDeliver()
*/
public boolean eDeliver() {
return rule.eDeliver();
}
/**
* @see org.eclipse.emf.common.notify.Notifier#eSetDeliver(boolean)
*/
public void eSetDeliver(boolean deliver) {
rule.eSetDeliver(deliver);
}
/**
* @see org.eclipse.emf.common.notify.Notifier#eNotify(org.eclipse.emf.common.notify.Notification)
*/
public void eNotify(Notification notification) {
rule.eNotify(notification);
}
/**
* @see org.eclipse.emf.ecore.EObject#eClass()
*/
public EClass eClass() {
return rule.eClass();
}
/**
* @see org.eclipse.emf.ecore.EObject#eResource()
*/
public Resource eResource() {
return rule.eResource();
}
/**
* @see org.eclipse.emf.ecore.EObject#eContainer()
*/
public EObject eContainer() {
return rule.eContainer();
}
/**
* @see org.eclipse.emf.ecore.EObject#eContainingFeature()
*/
public EStructuralFeature eContainingFeature() {
return rule.eContainingFeature();
}
/**
* @see org.eclipse.emf.ecore.EObject#eContainmentFeature()
*/
public EReference eContainmentFeature() {
return rule.eContainmentFeature();
}
/**
* @see org.eclipse.emf.ecore.EObject#eContents()
*/
public EList<EObject> eContents() {
return rule.eContents();
}
/**
* @see org.eclipse.emf.ecore.EObject#eAllContents()
*/
public TreeIterator<EObject> eAllContents() {
return rule.eAllContents();
}
/**
* @see org.eclipse.emf.ecore.EObject#eIsProxy()
*/
public boolean eIsProxy() {
return rule.eIsProxy();
}
/**
* @see org.eclipse.emf.ecore.EObject#eCrossReferences()
*/
public EList<EObject> eCrossReferences() {
return rule.eCrossReferences();
}
/**
* @see org.eclipse.emf.ecore.EObject#eGet(org.eclipse.emf.ecore.EStructuralFeature)
*/
public Object eGet(EStructuralFeature feature) {
return rule.eGet(feature);
}
/**
* @see org.eclipse.emf.ecore.EObject#eGet(org.eclipse.emf.ecore.EStructuralFeature, boolean)
*/
public Object eGet(EStructuralFeature feature, boolean resolve) {
return rule.eGet(feature, resolve);
}
/**
* @see org.eclipse.emf.ecore.EObject#eSet(org.eclipse.emf.ecore.EStructuralFeature, java.lang.Object)
*/
public void eSet(EStructuralFeature feature, Object newValue) {
rule.eSet(feature, newValue);
}
/**
* @see org.eclipse.emf.ecore.EObject#eIsSet(org.eclipse.emf.ecore.EStructuralFeature)
*/
public boolean eIsSet(EStructuralFeature feature) {
return rule.eIsSet(feature);
}
/**
* @see org.eclipse.emf.ecore.EObject#eUnset(org.eclipse.emf.ecore.EStructuralFeature)
*/
public void eUnset(EStructuralFeature feature) {
rule.eUnset(feature);
}
/**
* @see org.eclipse.emf.ecore.EObject#eInvoke(org.eclipse.emf.ecore.EOperation, org.eclipse.emf.common.util.EList)
*/
public Object eInvoke(EOperation operation, EList<?> arguments) throws InvocationTargetException {
return rule.eInvoke(operation, arguments);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getActionEdges(org.eclipse.emf.henshin.model.Action)
*/
public EList<Edge> getActionEdges(Action arg0) {
return rule.getActionEdges(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getActionNodes(org.eclipse.emf.henshin.model.Action)
*/
public EList<Node> getActionNodes(Action arg0) {
return rule.getActionNodes(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getAllJavaImports()
*/
public EList<String> getAllJavaImports() {
return rule.getAllJavaImports();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getAllMappings()
*/
public MappingList getAllMappings() {
return rule.getAllMappings();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getAllMultiRules()
*/
public EList<Rule> getAllMultiRules() {
return rule.getAllMultiRules();
}
/**
* @see org.eclipse.emf.henshin.model.ModelElement#getAnnotations()
*/
public EList<Annotation> getAnnotations() {
return rule.getAnnotations();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getAttributeConditions()
*/
public EList<AttributeCondition> getAttributeConditions() {
return rule.getAttributeConditions();
}
/**
* @see org.eclipse.emf.henshin.model.NamedElement#getDescription()
*/
public String getDescription() {
return rule.getDescription();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getJavaImports()
*/
public EList<String> getJavaImports() {
return rule.getJavaImports();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getKernelRule()
*/
public Rule getKernelRule() {
return rule.getKernelRule();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getLhs()
*/
public Graph getLhs() {
return rule.getLhs();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getMappings()
*/
public MappingList getMappings() {
return rule.getMappings();
}
/**
* @see org.eclipse.emf.henshin.model.Unit#getModule()
*/
public Module getModule() {
return rule.getModule();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getMultiMappings()
*/
public MappingList getMultiMappings() {
return rule.getMultiMappings();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getMultiRule(java.lang.String)
*/
public Rule getMultiRule(String arg0) {
return rule.getMultiRule(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getMultiRulePath(org.eclipse.emf.henshin.model.Rule)
*/
public EList<Rule> getMultiRulePath(Rule arg0) {
return rule.getMultiRulePath(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getMultiRules()
*/
public EList<Rule> getMultiRules() {
return rule.getMultiRules();
}
/**
* @see org.eclipse.emf.henshin.model.NamedElement#getName()
*/
public String getName() {
return rule.getName();
}
/**
* @see org.eclipse.emf.henshin.model.Unit#getParameter(java.lang.String)
*/
public Parameter getParameter(String arg0) {
return rule.getParameter(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Unit#getParameterMappings()
*/
public EList<ParameterMapping> getParameterMappings() {
return rule.getParameterMappings();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getParameterNodes()
*/
public EList<Node> getParameterNodes() {
return rule.getParameterNodes();
}
/**
* @see org.eclipse.emf.henshin.model.Unit#getParameters()
*/
public EList<Parameter> getParameters() {
return rule.getParameters();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getRhs()
*/
public Graph getRhs() {
return rule.getRhs();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#getRootRule()
*/
public Rule getRootRule() {
return rule.getRootRule();
}
/**
* @see org.eclipse.emf.henshin.model.Unit#getSubUnits(boolean)
*/
public EList<Unit> getSubUnits(boolean arg0) {
return rule.getSubUnits(arg0);
}
/**
* @return
* @see org.eclipse.emf.henshin.model.Unit#isActivated()
*/
public boolean isActivated() {
return rule.isActivated();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#isCheckDangling()
*/
public boolean isCheckDangling() {
return rule.isCheckDangling();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#isInjectiveMatching()
*/
public boolean isInjectiveMatching() {
return rule.isInjectiveMatching();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#isMultiRule()
*/
public boolean isMultiRule() {
return rule.isMultiRule();
}
/**
* @see org.eclipse.emf.henshin.model.Rule#removeAttribute(org.eclipse.emf.henshin.model.Attribute, boolean)
*/
public boolean removeAttribute(Attribute arg0, boolean arg1) {
return rule.removeAttribute(arg0, arg1);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#removeEdge(org.eclipse.emf.henshin.model.Edge, boolean)
*/
public boolean removeEdge(Edge arg0, boolean arg1) {
return rule.removeEdge(arg0, arg1);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#removeNode(org.eclipse.emf.henshin.model.Node, boolean)
*/
public boolean removeNode(Node arg0, boolean arg1) {
return rule.removeNode(arg0, arg1);
}
/**
* @see org.eclipse.emf.henshin.model.Unit#setActivated(boolean)
*/
public void setActivated(boolean arg0) {
rule.setActivated(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#setCheckDangling(boolean)
*/
public void setCheckDangling(boolean arg0) {
rule.setCheckDangling(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.NamedElement#setDescription(java.lang.String)
*/
public void setDescription(String arg0) {
rule.setDescription(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#setInjectiveMatching(boolean)
*/
public void setInjectiveMatching(boolean arg0) {
rule.setInjectiveMatching(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#setLhs(org.eclipse.emf.henshin.model.Graph)
*/
public void setLhs(Graph arg0) {
rule.setLhs(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.NamedElement#setName(java.lang.String)
*/
public void setName(String arg0) {
rule.setName(arg0);
}
/**
* @see org.eclipse.emf.henshin.model.Rule#setRhs(org.eclipse.emf.henshin.model.Graph)
*/
public void setRhs(Graph arg0) {
rule.setRhs(arg0);
}
@Override
public int hashCode() {
return rule.hashCode();
}
@Override
public boolean equals(Object obj) {
return rule.equals(obj);
}
}