/* * Copyright (c) 2006- michael lawley and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation * which accompanies this distribution, and is available by writing to * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Contributors: * michael lawley */ package tefkat.engine; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.FeatureMap; import tefkat.model.TRule; import tefkat.model.Term; import tefkat.model.Var; import tefkat.model.internal.ModelUtils; final class Context { final private RuleEvaluator ruleEval; final private Evaluator exprEval; final Tree tree; final Node node; Context(RuleEvaluator ruleEval, Evaluator exprEval, Tree tree, Node node) { this.ruleEval = ruleEval; this.exprEval = exprEval; this.tree = tree; this.node = node; } void createBranch() { List newGoal = newGoal(); tree.createBranch(node, null, newGoal); } void createBranch(Term term) { createBranch(term, null); } void createBranch(Binding unifier) { List newGoal = newGoal(); tree.createBranch(node, unifier, newGoal); } void createBranch(Term term, Binding unifier) { List newGoal = newGoal(); newGoal.add(term); tree.createBranch(node, unifier, newGoal); } void createBranch(Collection terms) { List newGoal = newGoal(); newGoal.addAll(terms); tree.createBranch(node, null, newGoal); } void delay(String message) throws NotGroundException { throw new NotGroundException(node, message); } void error(String message) throws ResolutionException { throw new ResolutionException(node, message); } void error(String message, Exception e) throws ResolutionException { throw new ResolutionException(node, message, e); } private List newGoal() { List newGoal = new ArrayList(node.goal()); newGoal.remove(node.selectedLiteral()); return newGoal; } void fail() { node.setIsFailure(true); } /** * Find a binding for the variable in the current context. * * @param var The var to lookup in this context * @return The value that var is bound to in the context of this node or null */ Object lookup(Var var) { return node.lookup(var); } Tree createTree(Collection goal, Binding unifier, boolean isNegation, boolean subTree) { return createTree(new Node(goal, unifier), isNegation, subTree); } Tree createTree(Node newRoot, boolean isNegation, boolean subTree) { Tree result = new Tree(this, newRoot, tree.getContext(), tree.getTrackingExtent(), isNegation); if (subTree) { result.setLevel(tree.getLevel()-1); } else { result.setLevel(tree.getLevel()); } // if (ruleEval.INCREMENTAL) { ruleEval.addUnresolvedTree(result); // } return result; } Binding getBindings() { return node.getBindings(); } List expand(WrappedVar var) throws NotGroundException { return exprEval.expand(this, var); } Function getFunction(String name) { return (Function) exprEval.funcMap.get(name); } EObject lookup(List keys, TRule rule) { return ruleEval.injections.lookup(tree.getTrackingExtent(), keys, rule); } void warn(String string) { ruleEval.fireWarning(string);// + "\n " + node); } Map getNameMap() { return ruleEval.nameMap; } Object fetchFeature(String featureName, Object obj) throws ResolutionException { Object valuesObject = null; if (obj instanceof DynamicObject) { throw new ResolutionException(node, "Illegal attempt to retrieve feature value from target object instance: " + obj); } if (obj instanceof EObject) { EObject instance = (EObject) obj; // If instance is a DynamicObject or it's containing eResource is a target Extent // then we're querying a target object which is an error (until we update the stratification // as outlined by David Hearnden) try { EStructuralFeature eFeature = AbstractResolver.getFeature(this, instance.eClass(), featureName); valuesObject = instance.eGet(eFeature); if (valuesObject != null || instance.eIsSet(eFeature) || !eFeature.isRequired()) { ExtentUtil.highlightEdge(instance, valuesObject, ExtentUtil.FEATURE_LOOKUP); } else { warn(ModelUtils.getFullyQualifiedName(eFeature) + " is not set and no default value"); } return valuesObject; // This was a valid feature - don't want to fall through } catch (ResolutionException e) { // EFeature not found, so try other ways to get a value for featureName } } if (obj instanceof FeatureMap.Entry) { FeatureMap.Entry entry = (FeatureMap.Entry) obj; EStructuralFeature eFeature = entry.getEStructuralFeature(); if (eFeature.getName().equals(featureName)) { valuesObject = entry.getValue(); return valuesObject; // This was a valid feature - don't want to fall through } } String methName = "get" + featureName.substring(0, 1).toUpperCase() + featureName.substring(1, featureName.length()); try { try { valuesObject = obj.getClass().getMethod(methName, null).invoke(obj, null); } catch (NoSuchMethodException e) { if (null == valuesObject) { valuesObject = obj.getClass().getField(featureName).get(obj); } } } catch (Exception e) { warn("Could not find a source of values for '" + featureName + "' in '" + obj + "' " + e.getMessage()); } return valuesObject; } void addPartialOrder(Object inst, Object feat, Object lesser, Object greater) { ruleEval.addPartialOrder(inst, feat, lesser, greater); } void fireInfo(String mesg) { ruleEval.fireInfo(mesg); } Tree getResultTree(final Term term, final Binding unifier) { final Map cache = ruleEval.getPatternCache(term); final Binding parameterContext; if (null == unifier) { parameterContext = tree.getContext(); } else { parameterContext = unifier; parameterContext.composeRight(tree.getContext()); } Tree resultTree = (Tree) cache.get(parameterContext); if (null == resultTree) { final Collection goal = new ArrayList(); goal.add(term); // Collection patGoal = new ArrayList(); // Term pDefTerm = pDefn.getTerm(); // patGoal.add(pDefTerm); // System.err.println("resolving " + patGoal + "\n " + newContext); // TODO delete Node patternNode = new Node(goal, parameterContext); // Maybe Tree (via context) should construct the new tree? resultTree = createTree(patternNode, false, false); cache.put(parameterContext, resultTree); } if (!resultTree.isCompleted()) { // Register listener for floundering (and remove from cache) resultTree.addTreeListener(new TreeListener() { public void solution(Binding answer) { } public void completed(Tree theTree) { theTree.removeTreeListener(this); } public void floundered(Tree theTree) { cache.remove(parameterContext); } }); } return resultTree; } }