/**
* <copyright>
* </copyright>
*
* $Id$
*/
package tefkat.engine.runtime.impl;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.util.EObjectContainmentEList;
import org.eclipse.emf.ecore.util.InternalEList;
import tefkat.engine.runtime.Binding;
import tefkat.engine.runtime.BindingPair;
import tefkat.engine.runtime.Condition;
import tefkat.engine.runtime.Context;
import tefkat.engine.runtime.DynamicObject;
import tefkat.engine.runtime.Expression;
import tefkat.engine.runtime.FeatureExpr;
import tefkat.engine.runtime.NotGroundException;
import tefkat.engine.runtime.ResolutionException;
import tefkat.engine.runtime.RuntimePackage;
import tefkat.engine.runtime.Var;
import tefkat.engine.runtime.WrappedVar;
import tefkat.model.internal.ModelUtils;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Condition</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link tefkat.engine.runtime.impl.ConditionImpl#getArg <em>Arg</em>}</li>
* <li>{@link tefkat.engine.runtime.impl.ConditionImpl#getRelation <em>Relation</em>}</li>
* </ul>
* </p>
*
* @generated
*/
public class ConditionImpl extends SimpleTermImpl implements Condition {
final private static String[] relOpArray = {
"<", "<=", ">", ">=", "!="
};
final private static List relOpList = Arrays.asList(relOpArray);
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public static final String copyright = "Copyright michael lawley 2004";
/**
* The cached value of the '{@link #getArg() <em>Arg</em>}' containment reference list.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getArg()
* @generated
* @ordered
*/
protected EList arg = null;
/**
* The default value of the '{@link #getRelation() <em>Relation</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getRelation()
* @generated
* @ordered
*/
protected static final String RELATION_EDEFAULT = null;
/**
* The cached value of the '{@link #getRelation() <em>Relation</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getRelation()
* @generated
* @ordered
*/
protected String relation = RELATION_EDEFAULT;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
protected ConditionImpl() {
super();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
protected EClass eStaticClass() {
return RuntimePackage.Literals.CONDITION;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public EList getArg() {
if (arg == null) {
arg = new EObjectContainmentEList(Expression.class, this, RuntimePackage.CONDITION__ARG);
}
return arg;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getRelation() {
return relation;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setRelation(String newRelation) {
String oldRelation = relation;
relation = newRelation;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, RuntimePackage.CONDITION__RELATION, oldRelation, relation));
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
switch (featureID) {
case RuntimePackage.CONDITION__ARG:
return ((InternalEList)getArg()).basicRemove(otherEnd, msgs);
}
return super.eInverseRemove(otherEnd, featureID, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case RuntimePackage.CONDITION__ARG:
return getArg();
case RuntimePackage.CONDITION__RELATION:
return getRelation();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case RuntimePackage.CONDITION__ARG:
getArg().clear();
getArg().addAll((Collection)newValue);
return;
case RuntimePackage.CONDITION__RELATION:
setRelation((String)newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void eUnset(int featureID) {
switch (featureID) {
case RuntimePackage.CONDITION__ARG:
getArg().clear();
return;
case RuntimePackage.CONDITION__RELATION:
setRelation(RELATION_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public boolean eIsSet(int featureID) {
switch (featureID) {
case RuntimePackage.CONDITION__ARG:
return arg != null && !arg.isEmpty();
case RuntimePackage.CONDITION__RELATION:
return RELATION_EDEFAULT == null ? relation != null : !RELATION_EDEFAULT.equals(relation);
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public String toString() {
final List args = getArg();
switch (args.size()) {
case 0:
return getRelation();
case 1:
return getRelation() + " " + args.get(0);
case 2:
return args.get(0) + " " + getRelation() + " " + args.get(1);
default:
return getRelation() + " " + args;
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public void match(Context context) throws ResolutionException, NotGroundException {
boolean result = false;
String relation = getRelation();
List args = getArg();
if ("=".equals(relation)) {
Collection vals1 = ((Expression) args.get(0)).eval(context);
Collection vals2 = ((Expression) args.get(1)).eval(context);
// TODO need to check handling of all the possible cases
// Eg X = Y, X = 3, 3 = X, 2 = 3, 3 = 3,
// (Z = 1, X = Z), (Z = 1, 2 = Z), (X = Y, Z = X), etc
for (final Iterator itr1 = vals1.iterator(); itr1.hasNext(); ) {
Object val1 = itr1.next();
for (final Iterator itr2 = vals2.iterator(); itr2.hasNext(); ) {
Object val2 = itr2.next();
Binding unifier = Binding.createBinding(val1, val2);
if (null != unifier) {
result = true;
context.createBranch(unifier);
}
}
}
} else if (relOpList.contains(relation)) {
Collection vals1 = ((Expression) args.get(0)).eval(context);
Collection vals2 = ((Expression) args.get(1)).eval(context);
for (Iterator itr1 = vals1.iterator(); itr1.hasNext(); ) {
Object val1 = itr1.next();
// System.err.println("** " + val1);
for (Iterator itr2 = vals2.iterator(); itr2.hasNext(); ) {
Object val2 = itr2.next();
// System.err.println("**** " + val2);
if (val1 instanceof WrappedVar) {
context.delay("Unbound Var, " + val1 + ", not allowed in Condition.");
} else if (val2 instanceof WrappedVar) {
context.delay("Unbound Var, " + val2 + ", not allowed in Condition.");
} else {
Binding unifier = new Binding();
if (val1 instanceof BindingPair) {
unifier.composeRight((BindingPair) val1);
val1 = ((BindingPair) val1).getValue();
}
if (val2 instanceof BindingPair) {
unifier.composeRight((BindingPair) val2);
val2 = ((BindingPair) val2).getValue();
}
if (compare(context, relation, val1, val2)) {
result = true;
context.createBranch(unifier);
// } else {
// no match - result unchanged
}
}
}
}
} else if (relation.equals("boolean")) {
Collection vals = ((Expression) args.get(0)).eval(context);
List bindings = new ArrayList();
for (Iterator itr = vals.iterator(); itr.hasNext(); ) {
Object val = itr.next();
if (val instanceof BindingPair) {
Object bVal = ((BindingPair) val).getValue();
if (Boolean.TRUE.equals(bVal)) {
result = true;
bindings.add(val);
} else if (Boolean.FALSE.equals(bVal)) {
// do nothing
} else if (bVal instanceof WrappedVar) {
context.delay("Unbound Var, " + bVal + ", not allowed in Condition.");
} else {
context.error("Condition did not reference a boolean valued Expression.");
}
}
if (Boolean.TRUE.equals(val)) {
result = true;
} else if (Boolean.FALSE.equals(val)) {
// do nothing
} else if (val instanceof WrappedVar) {
context.delay("Unbound Var, " + val + ", not allowed in Condition.");
} else {
context.error("Condition did not reference a boolean valued Expression.");
}
}
// This is outside the loop since there's no point in creating
// multiple branches for the same (new) goal and empty Binding.
if (result) {
if (bindings.size() > 0) {
for (Iterator itr = bindings.iterator(); itr.hasNext(); ) {
context.createBranch((Binding) itr.next());
}
} else {
context.createBranch();
}
}
} else {
context.error("Unknown relation '" + relation + "' in Condition");
}
if (!result) {
context.fail();
}
}
private boolean compare(Context context, String relation, Object val1, Object val2)
throws ResolutionException {
long cmp;
if (val1 instanceof Number && val2 instanceof Number) {
if (val1 instanceof Float || val1 instanceof Double ||
val2 instanceof Float || val2 instanceof Double) {
double dval1 = ((Number) val1).doubleValue();
double dval2 = ((Number) val2).doubleValue();
cmp = (long) (dval1 - dval2);
} else if (val1 instanceof BigInteger || val1 instanceof BigDecimal ||
val2 instanceof BigInteger || val2 instanceof BigDecimal) {
try {
cmp = ((Comparable) val1).compareTo(val2);
} catch (ClassCastException e) {
context.error(val1 + " and " + val2 + " are not comparable.", e);
cmp = 0; // notreached
}
} else {
long lval1 = ((Number) val1).longValue();
long lval2 = ((Number) val2).longValue();
cmp = lval1 - lval2;
}
} else if (val1 instanceof Comparable) {
try {
cmp = ((Comparable) val1).compareTo(val2);
} catch (ClassCastException e) {
context.error(val1 + " and " + val2 + " are not comparable.", e);
cmp = 0; // notreached
}
} else if ("!=".equals(relation)) {
return !val1.equals(val2);
} else {
context.error(val1 + " and " + val2 + " are not comparable.");
cmp = 0; // notreached
}
if (cmp < 0 && relation.charAt(0) == '<') { // "<".equals(relation) || "<=".equals(relation)
return true;
} else if (cmp > 0 && relation.charAt(0) == '>') { // ">".equals(relation) || ">=".equals(relation)
return true;
} else if (cmp == 0 && ("<=".equals(relation) || ">=".equals(relation))) {
return true;
} else if (cmp != 0 && "!=".equals(relation)) {
return true;
} else {
return false;
}
}
public void ensure(Context context) throws ResolutionException, NotGroundException {
String relation = getRelation();
if (relation.equals("=")) {
handleBindingCondition(context);
} else if (relation.equals("boolean")) {
handleBooleanCondition(context);
} else {
context.error("Target condition containing "
+ relation + " is Not Yet Implemented");
}
}
private void handleBindingCondition(final Context context)
throws ResolutionException, NotGroundException {
List args = getArg();
// The following possibilities exist:
// LHS & RHS unbound
// LHS unbound & RHS bound
// LHS bound & RHS unbound
// LHS & RHS bound
// For now, implement the following:
// <expr>.y = <valExpr>;
Expression valExpr = (Expression) args.get(1);
List vals = valExpr.eval(context);
if (args.get(0) instanceof FeatureExpr) {
Binding unifier = null;
FeatureExpr featExpr = (FeatureExpr) args.get(0);
Collection featureNames = featExpr.getFeature().eval(context);
Collection objs = ((Expression) featExpr.getArg().get(0)).eval(context);
for (Iterator fItr = featureNames.iterator(); fItr.hasNext(); ) {
Object fObj = fItr.next();
EStructuralFeature featureObj = null;
String featureName = null;
if (fObj instanceof WrappedVar) {
Var var = ((WrappedVar) fObj).getVar();
context.delay(
"Unsupported mode (unbound '" + var.getName() + "') for FeatureExpr: " + var.getName() + "." + featExpr.getFeature());
} else if (fObj instanceof EStructuralFeature) {
featureObj = (EStructuralFeature) fObj;
} else if (!(fObj instanceof String)) {
context.error("The Feature Expression " + featExpr + " must evaluate to a feature name of type String, not " + fObj.getClass());
} else {
featureName = (String) fObj;
}
for (Iterator itr = objs.iterator(); itr.hasNext(); ) {
Object obj = itr.next();
if (obj instanceof WrappedVar) {
Var var = ((WrappedVar) obj).getVar();
context.delay(
"Unsupported mode (unbound '" + var.getName() + "') for FeatureExpr: " + var.getName() + "." + featureName);
} else if (!(obj instanceof EObject)) {
context.error("Target object is not an EObject (i.e., not a valid model instance)" + featExpr.getArg().get(0));
}
EObject instance = (EObject) obj;
EStructuralFeature theFeature;
if (null == featureObj) {
theFeature = context.getFeature(instance.eClass(), featureName);
} else {
EClass objClass = instance.eClass();
EClass featureClass = featureObj.getEContainingClass();
if (objClass.equals(featureClass) || objClass.getEAllSuperTypes().contains(featureClass)) {
theFeature = featureObj;
} else {
context.error("The target feature " + featureObj + " does not belong to the object " + instance);
theFeature = null; // notreached
}
}
if (theFeature.isMany()) {
// Adding multiple feature values: featureName = vals
if (vals.size() == 1 && vals.get(0) instanceof WrappedVar) {
Object newVal = vals.get(0);
// ruleEval.fireInfo(newVal + DELAYING_MESSAGE);
context.delay(newVal + NOT_BOUND_MESSAGE);
}
// values are always added for multi-valued features
List featureValues = (List) instance.eGet(theFeature);
List newVals = Util.coerceTypes(vals, theFeature);
try {
// Insert at beginning so that we have a chance of preserving
// the order from the source model (the Node-tree traversal
// would otherwise naturally invert the order).
if (featExpr.isCollect()) {
for (final Iterator nvItr = newVals.iterator(); nvItr.hasNext(); ) {
final List valList = (List) nvItr.next();
if (theFeature.isUnique()) {
// This is normally done by the EMF code, but not in the
// case where the theFeature is backed by a FeatureMap.
// Hence, we do it ourselves
valList.removeAll(featureValues);
}
featureValues.addAll(0, valList);
recordMultiReference(instance, theFeature, valList);
}
} else {
if (theFeature.isUnique()) {
// This is normally done by the EMF code, but not in the
// case where the theFeature is backed by a FeatureMap.
// Hence, we do it ourselves
newVals.removeAll(featureValues);
}
featureValues.addAll(0, newVals);
recordMultiReference(instance, theFeature, newVals);
}
} catch (ArrayStoreException e) {
context.error("Couldn't add values to feature (type mismatch?): " + ModelUtils.getFullyQualifiedName(theFeature) + " <- " + newVals, e);
}
} else if (vals.size() > 1) {
context.error("Too many values for, " + ModelUtils.getFullyQualifiedName(theFeature));
} else if (vals.size() == 1) {
Object newVal = Util.coerceType(vals.get(0), theFeature);
if (instance.eIsSet(theFeature)) {
Object curVal = instance.eGet(theFeature);
if (newVal instanceof WrappedVar) {
context.info(newVal + NOT_BOUND_MESSAGE + " Attempting to bind and continue.");
unifier = new Binding();
unifier.add(((WrappedVar) newVal).getVar(), curVal);
} else if (!curVal.equals(newVal)) {
context.error("Conflicting value: " + newVal + " found for feature, " + ModelUtils.getFullyQualifiedName(theFeature) + ", which is already set to: " + curVal);
}
} else {
//System.err.println("Setting " + theFeature + " to " + newVal);
// System.err.println("O **** " + instance);
// System.err.println(instance.eClass());
// System.err.println("F **** " + theFeature);
// System.err.println(theFeature.getEType());
// System.err.println("V **** " + newVal);
// System.err.println(newVal.getClass());
if (newVal instanceof WrappedVar) {
// ruleEval.fireInfo(newVal + DELAYING_MESSAGE);
context.delay(newVal + NOT_BOUND_MESSAGE);
}
instance.eSet(theFeature, newVal);
if (newVal instanceof DynamicObject) {
((DynamicObject) newVal).addReferenceFrom(instance, theFeature);
}
}
} else {
if (theFeature.getLowerBound() > 0) {
context.error("No value for " + ModelUtils.getFullyQualifiedName(theFeature) + " but lower bound is " + theFeature.getLowerBound());
}
context.warn("No value for " + ModelUtils.getFullyQualifiedName(theFeature));
}
context.createBranch(unifier);
}
}
} else {
context.error("Non FeatureExpr LHS, " + args.get(0) + ", Not Yet Implemented");
}
}
private void recordMultiReference(EObject instance, EStructuralFeature theFeature, List newVals) {
for (Iterator newValsItr = newVals.iterator(); newValsItr.hasNext(); ) {
Object newVal = newValsItr.next();
if (newVal instanceof DynamicObject) {
((DynamicObject) newVal).addMultiReferenceFrom(instance, theFeature);
}
}
}
/**
* @param context
* @param term
* @throws ResolutionException
* @throws NotGroundException
*/
private void handleBooleanCondition(final Context context)
throws ResolutionException, NotGroundException {
List args = getArg();
Collection vals = ((Expression) args.get(0)).eval(context);
for (Iterator itr = vals.iterator(); itr.hasNext(); ) {
Object val = itr.next();
if (Boolean.TRUE.equals(val)) {
// do nothing
} else if (Boolean.FALSE.equals(val)) {
context.error("Stopping on target-side FALSE.");
} else if (val instanceof WrappedVar) {
context.delay("Unbound Var, " + val + ", not allowed in Condition.");
} else {
context.error("Condition did not reference a boolean valued Expression.");
}
}
// This is outside the loop since there's no point in creating
// multiple branches for the same (new) goal and empty Binding.
context.createBranch();
}
} //ConditionImpl