From d840d3255832b48af4c5723f691d3a5811fff2e4 Mon Sep 17 00:00:00 2001
From: Andreas Bayha <bayha@fortiss.org>
Date: Mon, 3 Jul 2023 09:28:30 +0200
Subject: [PATCH] PLA: Started optimized implementation of product-line
 analysis

Started optimized implementation with collection of relevant objects.

Issue-ref: 4240
Issue-URL: https://git.fortiss.org/af3/af3/-/issues/4240

Signed-off-by: Andreas Bayha <bayha@fortiss.org>
---
 .../analysis/EMFProductLineTranslation.java   | 892 ++++++++++++++++++
 .../util/VariabilityUtilsInternal.java        |  12 +
 2 files changed, 904 insertions(+)
 create mode 100644 org.fortiss.variability/src/org/fortiss/variability/analysis/EMFProductLineTranslation.java

diff --git a/org.fortiss.variability/src/org/fortiss/variability/analysis/EMFProductLineTranslation.java b/org.fortiss.variability/src/org/fortiss/variability/analysis/EMFProductLineTranslation.java
new file mode 100644
index 000000000..a52e72050
--- /dev/null
+++ b/org.fortiss.variability/src/org/fortiss/variability/analysis/EMFProductLineTranslation.java
@@ -0,0 +1,892 @@
+package org.fortiss.variability.analysis;
+
+import static java.util.stream.Collectors.toList;
+import static org.fortiss.variability.util.VariabilityUtilsInternal.calculateGlobalPresenceCondition;
+import static org.fortiss.variability.util.VariabilityUtilsInternal.getChildrenWithType;
+import static org.fortiss.variability.util.VariabilityUtilsInternal.getContainedSuperClass;
+
+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 java.util.Set;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EEnumLiteral;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.fortiss.variability.model.IVariationPoint;
+import org.fortiss.variability.model.features.AbstractAlternativeFeature;
+import org.fortiss.variability.model.features.AbstractCompositionalFeature;
+import org.fortiss.variability.model.features.AbstractCrossFeatureConstraint;
+import org.fortiss.variability.model.features.AbstractFeature;
+import org.fortiss.variability.model.features.AbstractFeatureModel;
+import org.fortiss.variability.model.presence.AndPC;
+import org.fortiss.variability.model.presence.DefaultPC;
+import org.fortiss.variability.model.presence.ILiteralReferencable;
+import org.fortiss.variability.model.presence.LiteralPC;
+import org.fortiss.variability.model.presence.NotPC;
+import org.fortiss.variability.model.presence.OrPC;
+import org.fortiss.variability.model.presence.PresenceCondition;
+import org.fortiss.variability.model.presence.PresenceConditionTerm;
+
+import com.microsoft.z3.BoolExpr;
+import com.microsoft.z3.BoolSort;
+import com.microsoft.z3.Context;
+import com.microsoft.z3.EnumSort;
+import com.microsoft.z3.Expr;
+import com.microsoft.z3.FuncDecl;
+import com.microsoft.z3.IntSort;
+import com.microsoft.z3.SeqExpr;
+import com.microsoft.z3.SeqSort;
+import com.microsoft.z3.Solver;
+import com.microsoft.z3.Sort;
+import com.microsoft.z3.StringSymbol;
+import com.microsoft.z3.Symbol;
+
+/**
+ * Base class that implements the translation of model product-lines to SMT in order to perform
+ * product-line analysis via constraint lifting.
+ * 
+ * @author bayha
+ */
+public class EMFProductLineTranslation {
+
+	/** Z3 sort for base type integer. */
+	protected IntSort intSort;
+	/** Z3 sort for base type string. */
+	protected SeqSort stringSort;
+	/** Z3 sort for base type boolean. */
+	protected BoolSort boolSort;
+
+	/** Mapping from classes to all their objects */
+	protected BucketSetMap<EClass, EObject> allObjects = new BucketSetMap<EClass, EObject>();
+	/** Mapping from classes to all their subclasses */
+	protected BucketSetMap<EClass, EClass> allSubclasses = new BucketSetMap<EClass, EClass>();
+
+	/** Classes which are not supposed to be translated (usually for optimization). */
+	protected Set<EClass> excludedClasses = new HashSet<EClass>();
+	/** References which are not supposed to be translated (usually for optimization). */
+	protected Set<EReference> excludedReferences = new HashSet<EReference>();
+	/** Mapping from classes to all attributes of the respective type. */
+	protected BucketSetMap<EClass, EAttribute> translatedAttributes =
+			new BucketSetMap<EClass, EAttribute>();
+	/** Mapping from classes to all references of the respective type. */
+	protected BucketSetMap<EClass, EReference> translatedReferences =
+			new BucketSetMap<EClass, EReference>();
+	/** Mapping of attributes to their translated Z3 function. */
+	protected DualKeyMap<EAttribute, EClass, FuncDecl> eAttribute2FunDecl =
+			new DualKeyMap<EAttribute, EClass, FuncDecl>();
+	/** Mapping of references to their translated Z3 function. */
+	protected DualKeyMap<EReference, EClass, FuncDecl> eReference2FunDecl =
+			new DualKeyMap<EReference, EClass, FuncDecl>();
+
+	/** Mapping of references to the respective translated Z3 type. */
+	protected Map<EReference, Sort> eReference2Sort = new HashMap<EReference, Sort>();
+
+	/** Mapping of EDataTypes (e.g. EInt) to their translated Z3 type. */
+	protected Map<EDataType, Sort> eDataType2Sort = new HashMap<EDataType, Sort>();
+	/** Mapping for classes to their respective Z3 type. */
+	protected Map<EClass, EnumSort> eClass2Datatype = new HashMap<EClass, EnumSort>();
+
+	/** Mapping of class.reference to the respective z3 function. */
+	protected DualKeyMap<EClass, String, FuncDecl> attRefNames2FuncDecl =
+			new DualKeyMap<EClass, String, FuncDecl>();
+
+	/**
+	 * Mapping of objects with types to their translated Z3 expressions. (EClass is necessary as an
+	 * object is translated for all classes in the inheritance hierarchy).
+	 */
+	protected DualKeyMap<EObject, EClass, Expr> eObject2Expr =
+			new DualKeyMap<EObject, EClass, Expr>();
+
+	protected Map<Expr, EObject> expr2EObject = new HashMap<Expr, EObject>();
+
+	/** Mapping of enumeration literals to the translated Z3 expression. */
+	protected Map<Enum<?>, Expr> eLiteral2Expr = new HashMap<Enum<?>, Expr>();
+	/** Mapping of classes to their respective z3 expression for 'null' */
+	protected Map<EClass, Expr> eClass2NullElement = new HashMap<EClass, Expr>();
+
+	/** Mapping of enumerations to their respective z3 expression for 'null' */
+	protected Map<EEnum, Expr> eEnum2NullElement = new HashMap<EEnum, Expr>();
+
+	/** Mapping of feature names to corresponding Z3 Expr. */
+	protected Map<String, BoolExpr> featureName2BoolExpr = new HashMap<String, BoolExpr>();
+	/** Mapping of classes to the selection function (lambda) for this type. */
+	protected Map<EClass, FuncDecl> eClass2SelectionFunction = new HashMap<EClass, FuncDecl>();
+	/** Mapping of objects to their presence conditions. */
+	protected Map<EObject, BoolExpr> eObject2Selection = new HashMap<EObject, BoolExpr>();
+
+	/** {@link Context} to capture the translated model. */
+	protected Context ctx = new Context();
+
+	/** List of all translated assertions for the model. */
+	protected List<BoolExpr> modelAssertions = new ArrayList<BoolExpr>();
+	/** List of all assertions for the feature model. */
+	protected List<BoolExpr> featureModelAssertions = new ArrayList<BoolExpr>();
+	/** List of all assertions which encode the presence conditions. */
+	protected List<BoolExpr> presenceConditionAssertions = new ArrayList<BoolExpr>();
+
+	/* Refacting */
+
+	protected Set<Class<? extends EObject>> translatedClasses;
+	protected Set<EReference> translatedRefernces;
+	protected Set<EObject> translatedObjects;
+	protected BucketSetMap<Class<? extends EObject>, EObject> class2TranslatedEObjects;
+
+	/**
+	 * Constructor.
+	 */
+	protected EMFProductLineTranslation() {
+		initialize();
+	}
+
+	/**
+	 * Initialization of analysis and base types.
+	 */
+	private void initialize() {
+		intSort = ctx.mkIntSort();
+		stringSort = ctx.mkStringSort();
+		boolSort = ctx.mkBoolSort();
+
+		eDataType2Sort.put(EcorePackage.Literals.EINT, intSort);
+		eDataType2Sort.put(EcorePackage.Literals.ESTRING, stringSort);
+		eDataType2Sort.put(EcorePackage.Literals.EBOOLEAN, boolSort);
+
+		class2TranslatedEObjects = new BucketSetMap<Class<? extends EObject>, EObject>();
+	}
+
+	/** Performs the complete translation of the product-line to Z3. */
+	/* package */ void doTranslate(EObject model) {
+		// Prepare the core translation
+		setTranslatedClassesAndReferences();
+
+		translateFeatureModel(model);
+		collectTranslatedObjects(model);
+
+		// Metamodel translation
+		translateClassesMetamodel();
+		translateAttributesAndReferencesMetamodel();
+
+		// Translate variability
+		translatePresenceConditions();
+
+		// Model translation
+		translateObjects();
+	}
+
+	/**
+	 * TODO
+	 * 
+	 * @param model
+	 */
+	private void collectTranslatedObjects(EObject model) {
+
+		EList<? extends EObject> varModelElements = getVariableModelElements(model);
+		for(EObject eo : varModelElements) {
+			Class<? extends EObject> eoClass = eo.getClass();
+			if(translatedClasses.contains(eoClass)) {
+				collectEObject(eo, eoClass);
+
+				continue;
+			}
+
+			for(Class<? extends EObject> t : translatedClasses) {
+				if(t.isAssignableFrom(eoClass)) {
+					collectEObject(eo, t);
+				}
+			}
+		}
+	}
+
+	/**
+	 * @param eo
+	 */
+	private void collectEObject(EObject eo, Class<? extends EObject> cls) {
+		class2TranslatedEObjects.add(cls, eo);
+
+		for(EReference ref : eo.eClass().getEAllReferences()) {
+			if(translatedRefernces.contains(ref)) {
+				Object refTarget = eo.eGet(ref);
+				if(refTarget instanceof EObject) {
+					EObject refEObj = (EObject)refTarget;
+
+					Class<? extends EObject> refClass = refEObj.getClass();
+					if(translatedClasses.contains(refClass)) {
+						collectEObject(refEObj, refClass);
+
+						continue;
+					}
+
+					Class<? extends EObject> translRefCls =
+							getContainedSuperClass(refClass, translatedClasses);
+					if(translRefCls != null) {
+						collectEObject(eo, translRefCls);
+					} else {
+						throw new RuntimeException("Invalid reference target class found. Class " +
+								refClass.getCanonicalName() + " is not translated.");
+					}
+				}
+
+			}
+		}
+	}
+
+	/**
+	 * @param model
+	 */
+	protected EList<? extends EObject> getVariableModelElements(EObject model) {
+		return getChildrenWithType(model, IVariationPoint.class);
+	}
+
+	/**
+	 * TODO
+	 */
+	protected void setTranslatedClassesAndReferences() {
+		// EMPTY
+	}
+
+	/** Translates the presence conditions to Z3. */
+	protected void translatePresenceConditions() {
+		for(EClass ec : allObjects.keySet()) {
+			EnumSort ecSort = eClass2Datatype.get(ec);
+			FuncDecl selFun = ctx.mkFuncDecl("SELECTED_" + ec.getName(), ecSort, boolSort);
+			eClass2SelectionFunction.put(ec, selFun);
+
+			for(EObject eo : allObjects.get(ec)) {
+				BoolExpr eoSelection = eObject2Selection.get(eo);
+
+				// If there is no PC term yet, create one
+				if(eoSelection == null) {
+					final PresenceConditionTerm globalPC = getGlobalPresenceConditionForEObject(eo);
+					eoSelection = translatePresenceCondition(globalPC);
+					if(eoSelection == null) {
+						eoSelection = ctx.mkTrue();
+					}
+					eObject2Selection.put(eo, eoSelection);
+				}
+
+				Expr eoExpr = eObject2Expr.get(eo, ec);
+				presenceConditionAssertions.add(ctx.mkEq(ctx.mkApp(selFun, eoExpr), eoSelection));
+			}
+		}
+	}
+
+	/**
+	 * Calculates the global presence condition for any given {@link EObject}.
+	 * 
+	 * This method might be overwritten if necessary for custom variation point implementations.
+	 */
+	protected PresenceConditionTerm getGlobalPresenceConditionForEObject(EObject eo) {
+		return calculateGlobalPresenceCondition(eo);
+	}
+
+	/** Translates a given {@link PresenceCondition} to Z3. */
+	private BoolExpr translatePresenceCondition(PresenceCondition pc) {
+		if(pc == null) {
+			return null;
+		}
+
+		if(pc instanceof LiteralPC) {
+			ILiteralReferencable literal = ((LiteralPC)pc).getLiteralReference();
+			if(literal == null || literal.getName().length() == 0) {
+				// Illegal literals are translated as true.
+				return ctx.mkTrue();
+			}
+			return featureName2BoolExpr.get(literal.getName());
+		}
+
+		if(pc instanceof DefaultPC) {
+			return translatePresenceCondition(pc.resolveToFeatureLiterals());
+		}
+
+		if(pc instanceof OrPC) {
+			PresenceConditionTerm op1 = ((OrPC)pc).getOperand1();
+			PresenceConditionTerm op2 = ((OrPC)pc).getOperand2();
+			return ctx.mkOr(translatePresenceCondition(op1), translatePresenceCondition(op2));
+		}
+
+		if(pc instanceof AndPC) {
+			PresenceConditionTerm op1 = ((AndPC)pc).getOperand1();
+			PresenceConditionTerm op2 = ((AndPC)pc).getOperand2();
+			return ctx.mkAnd(translatePresenceCondition(op1), translatePresenceCondition(op2));
+		}
+
+		if(pc instanceof NotPC) {
+			return ctx.mkNot(translatePresenceCondition(((NotPC)pc).getOperand()));
+		}
+
+		logError("Unknown PresenceCondition: " + pc);
+		return null;
+	}
+
+	/** Overwrite to exclude classes from the translation for optimization purposes. */
+	protected void excludeClassesAndReferences() {
+		// EMPTY
+	}
+
+	/** Translates the feature model to Z3. */
+	protected void translateFeatureModel(EObject model) {
+		// FIXME: Optimize!
+		EList<AbstractFeatureModel> featureModels =
+				getChildrenWithType(model, AbstractFeatureModel.class);
+
+		if(featureModels.size() == 0) {
+			return;
+		}
+
+		// There can only be one FeatureModel in any project.
+		AbstractFeatureModel fm = featureModels.get(0);
+
+		BoolExpr fmExpr = ctx.mkBoolConst("FEATURE_" + fm.getName());
+		featureName2BoolExpr.put(fm.getName(), fmExpr);
+
+		featureModelAssertions.add(fmExpr);
+
+		// Translate all features (including alternatives)
+		for(AbstractFeature f : getChildrenWithType(fm, AbstractFeature.class)) {
+			String name = f.getName();
+			String constName = "FEATURE_" + name;
+			final BoolExpr fExpr = ctx.mkBoolConst(constName.replaceAll("@", "AT"));
+			featureName2BoolExpr.put(name, fExpr);
+
+			// If f is child of another feature, the selection criteria have to be encoded.
+			final EObject fContainer = f.eContainer();
+			if(fContainer instanceof AbstractFeature) {
+				AbstractFeature parentF = (AbstractFeature)fContainer;
+				final BoolExpr parentExpr = featureName2BoolExpr.get(parentF.getName());
+
+				if(f.isOptional()) {
+					featureModelAssertions.add(ctx.mkImplies(fExpr, parentExpr));
+				} else {
+					if(fContainer instanceof AbstractCompositionalFeature) {
+						featureModelAssertions.add(ctx.mkImplies(parentExpr, fExpr));
+					}
+				}
+			}
+		}
+
+		// For alternatives also translate the alternative selection criteria
+		for(AbstractAlternativeFeature af : getChildrenWithType(fm,
+				AbstractAlternativeFeature.class)) {
+			BoolExpr afExpr = featureName2BoolExpr.get(af.getName());
+			EList<AbstractFeature> as = af.getAlternatives();
+
+			featureModelAssertions.add(ctx.mkImplies(afExpr,
+					ctx.mkOr(as.stream().map(alt -> featureName2BoolExpr.get(alt.getName()))
+							.collect(toList()).toArray(new BoolExpr[0]))));
+
+			for(AbstractFeature a : as) {
+				BoolExpr aExpr = featureName2BoolExpr.get(a.getName());
+				featureModelAssertions.add(ctx.mkImplies(aExpr, afExpr));
+				featureModelAssertions.add(ctx.mkImplies(aExpr,
+						ctx.mkAnd(as.stream().filter(alt -> alt != a)
+								.map(alt -> ctx.mkNot(featureName2BoolExpr.get(alt.getName())))
+								.collect(toList()).toArray(new BoolExpr[0]))));
+			}
+		}
+
+		// Translate all constraints.
+		for(AbstractCrossFeatureConstraint c : getChildrenWithType(fm,
+				AbstractCrossFeatureConstraint.class)) {
+			AbstractFeature target = c.getTarget();
+			AbstractFeature source = (AbstractFeature)c.eContainer();
+
+			BoolExpr srcExpr = featureName2BoolExpr.get(source.getName());
+			BoolExpr trgExpr = featureName2BoolExpr.get(target.getName());
+
+			switch(c.getType()) {
+				case REQUIRES:
+					featureModelAssertions.add(ctx.mkImplies(srcExpr, trgExpr));
+					break;
+				case EXCLUDES:
+					featureModelAssertions.add(ctx.mkImplies(srcExpr, ctx.mkNot(trgExpr)));
+					break;
+				default: // Suggestions are not translated.
+			}
+		}
+	}
+
+	/** Mapping from tracking variables to z3 expression (used for counterexample extraction). */
+	// protected Map<BoolExpr, BoolExpr> tracToExpr = new HashMap<BoolExpr, BoolExpr>();
+
+	/** Creates and initializes the Z3 solver to be used for analyis. */
+	protected Solver createSolver(Map<BoolExpr, BoolExpr> tracToExpr) {
+		Solver solver = ctx.mkSolver();// ctx.mkTactic("psmt"));
+
+		solver.add(featureModelAssertions.toArray(new BoolExpr[featureModelAssertions.size()]));
+		solver.add(presenceConditionAssertions
+				.toArray(new BoolExpr[presenceConditionAssertions.size()]));
+		solver.add(modelAssertions.toArray(new BoolExpr[modelAssertions.size()]));
+
+		//
+		// List<BoolExpr> tracFeaturesList = featureModelAssertions.stream()
+		// .map(a -> ctx.mkBoolConst("tracFeatureVar" + a.getId())).collect(toList());
+		//
+		// solver.assertAndTrack(
+		// featureModelAssertions.toArray(new BoolExpr[featureModelAssertions.size()]),
+		// tracFeaturesList.toArray(new BoolExpr[featureModelAssertions.size()]));
+
+		// for(int i = 0; i < featureModelAssertions.size(); i++) {
+		// tracToExpr.put(tracFeaturesList.get(i), featureModelAssertions.get(i));
+		// }
+		//
+		// List<BoolExpr> tracPresenceList = presenceConditionAssertions.stream()
+		// .map(a -> ctx.mkBoolConst("tracPresenceVar" + a.getId())).collect(toList());
+		//
+		// solver.assertAndTrack(
+		// presenceConditionAssertions
+		// .toArray(new BoolExpr[presenceConditionAssertions.size()]),
+		// tracPresenceList.toArray(new BoolExpr[presenceConditionAssertions.size()]));
+		//
+		// for(int i = 0; i < presenceConditionAssertions.size(); i++) {
+		// tracToExpr.put(tracPresenceList.get(i), presenceConditionAssertions.get(i));
+		// }
+
+		// BoolExpr[] assertionsArray = modelAssertions.toArray(new
+		// BoolExpr[modelAssertions.size()]);
+		// List<BoolExpr> tracList = modelAssertions.stream()
+		// .map(a -> ctx.mkBoolConst("tracModelVar" + a.getId())).collect(toList());
+		// BoolExpr[] assertionsTracArray = tracList.toArray(new BoolExpr[modelAssertions.size()]);
+		//
+		// for(int i = 0; i < modelAssertions.size(); i++) {
+		// tracToExpr.put(tracList.get(i), modelAssertions.get(i));
+		// }
+		//
+		// solver.assertAndTrack(assertionsArray, assertionsTracArray);
+
+		return solver;
+	}
+
+	/** Translates all objects in the model. */
+	private void translateObjects() {
+		// Translate objects by class.
+		for(EClass ec : allObjects.keySet()) {
+			Set<EObject> Objs = allObjects.get(ec);
+
+			Set<EAttribute> attributes = translatedAttributes.get(ec);
+			if(attributes != null) {
+				for(EAttribute ea : attributes) {
+					FuncDecl attFunc = eAttribute2FunDecl.get(ea, ec);
+
+					for(EObject eo : Objs) {
+						Expr attVal = translateAttributeValue(eo, ea);
+						BoolExpr attValAssertion =
+								ctx.mkEq(ctx.mkApp(attFunc, eObject2Expr.get(eo, ec)), attVal);
+						modelAssertions.add(attValAssertion);
+					}
+				}
+			}
+
+			Set<EReference> references = translatedReferences.get(ec);
+			if(references != null) {
+				for(EReference er : references) {
+					FuncDecl refFunc = eReference2FunDecl.get(er, ec);
+					for(EObject eo : Objs) {
+						Expr eoExpr = eObject2Expr.get(eo, ec);
+						Expr refVal = translateReferenceValue(eo, er);
+						BoolExpr rafValAssertion = ctx.mkEq(ctx.mkApp(refFunc, eoExpr), refVal);
+						modelAssertions.add(rafValAssertion);
+					}
+
+					// Null must always only point to null....
+					EClass refType = er.getEReferenceType();
+					Expr nullElement = eClass2NullElement.get(ec);
+					if(er.getUpperBound() > 0) {
+						modelAssertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement),
+								eClass2NullElement.get(refType)));
+					} else {
+						modelAssertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement),
+								ctx.mkEmptySeq(eReference2Sort.get(er))));
+					}
+				}
+			}
+		}
+	}
+
+	/** Translates the given instance of a relation between individual objects in the model. */
+	private Expr translateReferenceValue(EObject eo, EReference er) {
+		Object value = eo.eGet(er);
+		EClass refType = er.getEReferenceType();
+		Expr refNullValue = eClass2NullElement.get(refType);
+
+		FuncDecl refSelFun = eClass2SelectionFunction.get(er.getEType());
+
+		if(er.getUpperBound() < 0) {
+			// Case for lists
+			List<?> values = (List<?>)value;
+
+			if(values.size() == 0) {
+				return ctx.mkEmptySeq(eReference2Sort.get(er));
+			}
+
+			// Remove objects of untranslated objects.
+			values = values.stream().filter(o -> !excludedClasses.contains(((EObject)o).eClass()))
+					.collect(toList());
+
+			SeqExpr[] seqElems = new SeqExpr[values.size()];
+			for(int i = 0; i < values.size(); i++) {
+				Object o = values.get(i);
+
+				Expr elemExpr = eObject2Expr.get((EObject)o, refType);
+
+				SeqExpr selectedExpr = ctx.mkUnit(elemExpr);
+				if(refSelFun != null) {
+					SeqExpr notSelectedExpr = ctx.mkEmptySeq(eReference2Sort.get(er));
+					seqElems[i] = (SeqExpr)ctx.mkITE((BoolExpr)ctx.mkApp(refSelFun, elemExpr),
+							selectedExpr, notSelectedExpr);
+				} else {
+					seqElems[i] = selectedExpr;
+				}
+			}
+
+			if(seqElems.length == 0) {
+				return ctx.mkEmptySeq(eReference2Sort.get(er));
+			}
+			if(seqElems.length == 1) {
+				return seqElems[0];
+			}
+			return ctx.mkConcat(seqElems);
+		}
+
+		if(value == null) {
+			return refNullValue;
+		}
+
+		Expr singleValExpr = eObject2Expr.get((EObject)value, refType);
+
+		if(refSelFun != null) {
+			// Case with variable target
+			BoolExpr eoSel = (BoolExpr)ctx.mkApp(refSelFun, singleValExpr);
+
+			return ctx.mkITE(eoSel, singleValExpr, refNullValue);
+		}
+
+		// Default case with non variable target.
+		return singleValExpr;
+	}
+
+	/** Creates a function call with the given expression as a parameter. */
+	protected Expr createFunctionCallOnExpr(Expr startExpr, String funName, EClass startExprType) {
+		FuncDecl fun = attRefNames2FuncDecl.get(startExprType, funName);
+		return ctx.mkApp(fun, startExpr);
+	}
+
+	/**
+	 * Creates a containment reference between the given expressions, analogly to the containment in
+	 * EMF.
+	 */
+	protected BoolExpr createContainmentReference(Expr contained, Expr container,
+			EClass containerType, String refName) {
+		SeqExpr contRefExpr = (SeqExpr)createFunctionCallOnExpr(container, refName, containerType);
+		// return ctx.mkAnd(ctx.mkNot(ctx.mkEq(ctx.mkLength(contRefExpr), ctx.mkInt(0))),
+		// ctx.mkContains(contRefExpr, ctx.mkUnit(contained)));
+		return ctx.mkContains(contRefExpr, ctx.mkUnit(contained));
+	}
+
+	/** Translates the given EAttribute value for the given object to Z3. */
+	private Expr translateAttributeValue(EObject eo, EAttribute ea) {
+		Object value = eo.eGet(ea);
+		EDataType attType = ea.getEAttributeType();
+
+		if(ea.getUpperBound() < 0 && value instanceof List<?>) {
+			// Case for lists.
+			List<?> values = (List<?>)value;
+
+			SeqExpr[] seqElem = new SeqExpr[values.size()];
+			for(int i = 0; i < values.size(); i++) {
+				Object o = values.get(i);
+				Expr elemExpr = traslatePrimitiveObject(o, attType);
+
+				seqElem[i] = ctx.mkUnit(elemExpr);
+			}
+
+			return ctx.mkConcat(seqElem);
+		}
+
+		return traslatePrimitiveObject(value, attType);
+	}
+
+	/** Translates the given object for a primitive type (Enums, Integer, String, Boolean) to Z3. */
+	private Expr traslatePrimitiveObject(Object value, EDataType attType) {
+		// Single value
+		if(attType.equals(EcorePackage.Literals.EINT)) {
+			if(value == null) {
+				logError("null int found");
+				return ctx.mkInt(0);
+			}
+			return ctx.mkInt((int)value);
+		}
+		if(attType.equals(EcorePackage.Literals.ESTRING)) {
+			if(value == null) {
+				return ctx.mkString("");
+			}
+			return ctx.mkString((String)value);
+		}
+		if(attType.equals(EcorePackage.Literals.EBOOLEAN)) {
+			if(value == null) {
+				logError("null bool found");
+				return ctx.mkBool(false);
+			}
+			return ctx.mkBool((boolean)value);
+		}
+		if(attType instanceof EEnum) {
+			if(value == null) {
+				return eEnum2NullElement.get(attType);
+			}
+			return eLiteral2Expr.get(value);
+		}
+
+		return null;
+	}
+
+	/** Creates functions for all attributes and references of the meta model in Z3. */
+	private void translateAttributesAndReferencesMetamodel() {
+		for(EClass ec : allObjects.keySet()) {
+			for(EAttribute ea : ec.getEAllAttributes()) {
+				String funId = ec.getName() + "_" + ea.getName();
+
+				EDataType attDataType = ea.getEAttributeType();
+				Sort rangeSort = eDataType2Sort.get(attDataType);
+				if(attDataType instanceof EEnum) {
+					if(rangeSort == null) {
+						rangeSort = translateEnum((EEnum)attDataType);
+						eDataType2Sort.put(attDataType, rangeSort);
+					}
+				}
+
+				if(rangeSort == null) {
+					// Attribute is not translated, as all attribute types except enums are known.
+					continue;
+				}
+
+				FuncDecl funDecl = ctx.mkFuncDecl(funId, eClass2Datatype.get(ec), rangeSort);
+
+				eAttribute2FunDecl.put(ea, ec, funDecl);
+				translatedAttributes.add(ec, ea);
+				attRefNames2FuncDecl.put(ec, ea.getName(), funDecl);
+			}
+			for(EReference er : ec.getEAllReferences()) {
+				String funId = ec.getName() + "_" + er.getName();
+
+				EClass refType = er.getEReferenceType();
+
+				Sort refSort = eClass2Datatype.get(refType);
+				if(refSort == null) {
+					if(!isReferenceTranslated(er)) {
+						// This is the case in which a custom instance class is specified - those
+						// cannot
+						// be translated.
+						continue;
+					}
+
+					refSort = translateSuperType(refType);
+					if(refSort == null) {
+						// There are no instances of this type...
+						continue;
+					}
+				}
+
+				FuncDecl funDecl;
+
+				if(er.getUpperBound() < 0) {
+					// This is the case of unlimited multiplicity
+					refSort = ctx.mkSeqSort(refSort);
+				}
+
+				funDecl = ctx.mkFuncDecl(funId, eClass2Datatype.get(ec), refSort);
+
+				eReference2Sort.put(er, refSort);
+				eReference2FunDecl.put(er, ec, funDecl);
+				translatedReferences.add(ec, er);
+				attRefNames2FuncDecl.put(ec, er.getName(), funDecl);
+			}
+		}
+	}
+
+	/** Checks whether the given reference is supposed to be translated. */
+	private boolean isReferenceTranslated(EReference ref) {
+		if(excludedReferences.contains(ref)) {
+			return false;
+		}
+
+		EClass refType = ref.getEReferenceType();
+		if(excludedClasses.contains(refType)) {
+			excludedReferences.add(ref);
+			// References to excluded classes not translated.
+			return false;
+		}
+
+		// Only EObjects are translated.
+		boolean isTranslated = EObject.class.isAssignableFrom(refType.getInstanceClass());
+		return isTranslated;
+	}
+
+	/**
+	 * Translates the given superType by adding all objects to it, that belong to
+	 * subtypes.
+	 */
+	private EnumSort translateSuperType(EClass refType) {
+		Set<EClass> subClasses = allSubclasses.get(refType);
+		if(subClasses == null) {
+			// There are no objects of this class in this case, hence it is not relevant.
+			return null;
+		}
+
+		String typeName = refType.getName();
+
+		List<EObject> subClassObjects = new ArrayList<EObject>();
+		for(EClass subCls : subClasses) {
+			if(excludedClasses.contains(subCls)) {
+				// If subclass is excluded, it must not be added here, either.
+				continue;
+			}
+			subClassObjects.addAll(allObjects.get(subCls));
+		}
+
+		Symbol[] allSymbols = new Symbol[subClassObjects.size()];
+		for(int i = 0; i < subClassObjects.size(); i++) {
+			StringSymbol symbol = ctx.mkSymbol(typeName + i);
+
+			allSymbols[i] = symbol;
+		}
+
+		EnumSort sort = ctx.mkEnumSort(ctx.mkSymbol(typeName), allSymbols);
+		eClass2Datatype.put(refType, sort);
+
+		return sort;
+	}
+
+	/** Translates the give enumeration */
+	private Sort translateEnum(EEnum en) {
+		String name = en.getName();
+		EList<EEnumLiteral> enumLiterals = en.getELiterals();
+
+		// Null needs to be encoded as a dedicated literal
+		int numLiterals = enumLiterals.size();
+		String[] literals = new String[numLiterals + 1];
+		for(int i = 0; i < numLiterals; i++) {
+			literals[i] = name + "_" + enumLiterals.get(i).getName();
+		}
+		literals[numLiterals] = name + "_NONE_LITERAL";
+
+		EnumSort enumSort = ctx.mkEnumSort(name, literals);
+
+		// Add regular literals and null literal to the respective mappings.
+		for(int i = 0; i < numLiterals; i++) {
+			eLiteral2Expr.put((Enum<?>)enumLiterals.get(i).getInstance(), enumSort.getConst(i));
+		}
+		eEnum2NullElement.put(en, enumSort.getConst(numLiterals));
+
+		return enumSort;
+	}
+
+	/** Creates the types for all classes and objects in Z3. */
+	private void translateClassesMetamodel() {
+		for(EClass cls : allObjects.keySet()) {
+			List<EObject> objs = new ArrayList<EObject>(allObjects.get(cls));
+
+			// The last index is added for the null element.
+			Symbol[] objSymbols = new Symbol[objs.size() + 1];
+			String clsName = cls.getName();
+			for(int i = 0; i < objs.size(); i++) {
+				String id = clsName + i;
+
+				StringSymbol objSymbol = ctx.mkSymbol(id);
+				objSymbols[i] = objSymbol;
+			}
+			objSymbols[objs.size()] = ctx.mkSymbol(clsName + "NONE");
+
+			// Create EnumSort for class
+			EnumSort enumSort = ctx.mkEnumSort(ctx.mkSymbol(clsName), objSymbols);
+			eClass2Datatype.put(cls, enumSort);
+
+			// Map objects to z3 enum literals
+			Expr[] consts = enumSort.getConsts();
+			for(int i = 0; i < objs.size(); i++) {
+				eObject2Expr.put(objs.get(i), cls, consts[i]);
+				expr2EObject.put(consts[i], objs.get(i));
+			}
+			Expr noneExpr = consts[consts.length - 1];
+			eClass2NullElement.put(cls, noneExpr);
+		}
+	}
+
+	/** Logs an error in the translation. */
+	private void logError(String message) {
+		// FIXME //error(getDefault(), "Error in product-line translation: " + message);
+	}
+
+	/**
+	 * Checks whether any of the elements in the given {@link Collection} is contained in the given
+	 * set of elements.
+	 * 
+	 * @param set
+	 *            The {@link Set} to check for containment.
+	 * @param elements
+	 *            The {@link Collection} of elements to be checked for containment in the set.
+	 * 
+	 * @return 'true' if at least one of the elements on the given {@link Collection} is contained
+	 *         in the given {@link Set}.
+	 */
+	public <E> boolean containsAny(Set<E> set, Collection<E> elements) {
+		for(E e : elements) {
+			if(set.contains(e)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @param clss
+	 */
+	public void addExcludedClasses(Collection<EClass> clss) {
+		this.excludedClasses.addAll(clss);
+	}
+
+	/** Returns ctx. */
+	public Context getContext() {
+		return ctx;
+	}
+
+	/** Returns eClass2NullElement. */
+	public Map<EClass, Expr> getEClass2NullElement() {
+		return eClass2NullElement;
+	}
+
+	/** Traverses the model to collect all classes and objects to be translated. */
+	private void collectClassesAndObjectsRecrsively(EObject model) {
+		EClass eClass = model.eClass();
+		EList<EClass> superTypes = eClass.getEAllSuperTypes();
+
+		if(excludedClasses.contains(eClass)) {
+			return;
+		}
+
+		if(containsAny(excludedClasses, superTypes)) {
+			excludedClasses.add(eClass);
+			return;
+		}
+
+		for(EClass sprCls : superTypes) {
+			if(!sprCls.equals(EcorePackage.Literals.EOBJECT)) {
+				allObjects.add(sprCls, model);
+				allSubclasses.add(sprCls, eClass);
+			}
+		}
+
+		allObjects.add(eClass, model);
+
+		for(EObject c : model.eContents()) {
+			collectClassesAndObjectsRecrsively(c);
+		}
+	}
+}
diff --git a/org.fortiss.variability/src/org/fortiss/variability/util/VariabilityUtilsInternal.java b/org.fortiss.variability/src/org/fortiss/variability/util/VariabilityUtilsInternal.java
index 60efb90f7..3db6af80e 100644
--- a/org.fortiss.variability/src/org/fortiss/variability/util/VariabilityUtilsInternal.java
+++ b/org.fortiss.variability/src/org/fortiss/variability/util/VariabilityUtilsInternal.java
@@ -26,6 +26,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.Set;
 import java.util.function.Predicate;
 
 import org.eclipse.emf.common.util.BasicEList;
@@ -261,4 +262,15 @@ public class VariabilityUtilsInternal {
 
 		return rest;
 	}
+
+	public static Class<? extends EObject> getContainedSuperClass(Class<?> subCls,
+			Set<Class<? extends EObject>> clss) {
+		for(Class<? extends EObject> t : clss) {
+			if(t.isAssignableFrom(subCls)) {
+				return t;
+			}
+		}
+
+		return null;
+	}
 }
-- 
GitLab