/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.core.rule.constraint;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import org.drools.core.base.ClassFieldReader;
import org.drools.core.base.DroolsQuery;
import org.drools.core.base.EvaluatorWrapper;
import org.drools.core.base.extractors.ArrayElementReader;
import org.drools.core.base.extractors.MVELObjectClassFieldReader;
import org.drools.core.base.mvel.MVELCompilationUnit;
import org.drools.core.common.DroolsObjectInputStream;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.definitions.InternalKnowledgePackage;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.reteoo.PropertySpecificUtil;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.rule.ContextEntry;
import org.drools.core.rule.Declaration;
import org.drools.core.rule.IndexEvaluator;
import org.drools.core.rule.IndexableConstraint;
import org.drools.core.rule.MVELDialectRuntimeData;
import org.drools.core.rule.MutableTypeConstraint;
import org.drools.core.rule.constraint.ConditionAnalyzer.CombinedCondition;
import org.drools.core.rule.constraint.ConditionAnalyzer.Condition;
import org.drools.core.rule.constraint.ConditionAnalyzer.EvaluatedExpression;
import org.drools.core.rule.constraint.ConditionAnalyzer.Expression;
import org.drools.core.rule.constraint.ConditionAnalyzer.FieldAccessInvocation;
import org.drools.core.rule.constraint.ConditionAnalyzer.Invocation;
import org.drools.core.rule.constraint.ConditionAnalyzer.MethodInvocation;
import org.drools.core.rule.constraint.ConditionAnalyzer.SingleCondition;
import org.drools.core.spi.AcceptsReadAccessor;
import org.drools.core.spi.FieldValue;
import org.drools.core.spi.InternalReadAccessor;
import org.drools.core.spi.Tuple;
import org.drools.core.util.AbstractHashTable.FieldIndex;
import org.drools.core.util.MemoryUtil;
import org.drools.core.util.bitmask.BitMask;
import org.drools.core.util.index.IndexUtil;
import org.kie.api.runtime.rule.Variable;
import org.kie.internal.concurrent.ExecutorProviderFactory;
import org.mvel2.ParserConfiguration;
import org.mvel2.compiler.CompiledExpression;
import org.mvel2.compiler.ExecutableStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.drools.core.reteoo.PropertySpecificUtil.*;
import static org.drools.core.util.ClassUtils.areNullSafeEquals;
import static org.drools.core.util.ClassUtils.getter2property;
import static org.drools.core.util.Drools.isJmxAvailable;
import static org.drools.core.util.StringUtils.*;
public class MvelConstraint extends MutableTypeConstraint implements IndexableConstraint, AcceptsReadAccessor {
protected static final boolean TEST_JITTING = false;
private static final Logger logger = LoggerFactory.getLogger(MvelConstraint.class);
protected final transient AtomicInteger invocationCounter = new AtomicInteger(1);
protected transient boolean jitted = false;
private Set<String> packageNames;
protected String expression;
private IndexUtil.ConstraintType constraintType = IndexUtil.ConstraintType.UNKNOWN;
private Declaration[] declarations;
private EvaluatorWrapper[] operators;
private Declaration indexingDeclaration;
private InternalReadAccessor extractor;
private boolean isUnification;
protected boolean isDynamic;
private FieldValue fieldValue;
protected MVELCompilationUnit compilationUnit;
private EvaluationContext evaluationContext = new EvaluationContext();
protected transient volatile ConditionEvaluator conditionEvaluator;
private transient volatile Condition analyzedCondition;
private static final Declaration[] EMPTY_DECLARATIONS = new Declaration[0];
private static final EvaluatorWrapper[] EMPTY_OPERATORS = new EvaluatorWrapper[0];
public MvelConstraint() {}
public MvelConstraint(final String packageName,
String expression,
MVELCompilationUnit compilationUnit,
IndexUtil.ConstraintType constraintType,
FieldValue fieldValue,
InternalReadAccessor extractor,
EvaluatorWrapper[] operators) {
this.packageNames = new HashSet<String>() {{ add(packageName); }};
this.expression = expression;
this.compilationUnit = compilationUnit;
this.constraintType = constraintType;
this.declarations = EMPTY_DECLARATIONS;
this.operators = operators == null ? EMPTY_OPERATORS : operators;
this.fieldValue = fieldValue;
this.extractor = extractor;
}
public MvelConstraint(final String packageName,
String expression,
Declaration[] declarations,
EvaluatorWrapper[] operators,
MVELCompilationUnit compilationUnit,
boolean isDynamic) {
this.packageNames = new HashSet<String>() {{ add(packageName); }};
this.expression = expression;
this.declarations = declarations == null ? EMPTY_DECLARATIONS : declarations;
this.operators = operators == null ? EMPTY_OPERATORS : operators;
this.compilationUnit = compilationUnit;
this.isDynamic = isDynamic;
}
public MvelConstraint(Collection<String> packageNames,
String expression,
Declaration[] declarations,
EvaluatorWrapper[] operators,
MVELCompilationUnit compilationUnit,
IndexUtil.ConstraintType constraintType,
Declaration indexingDeclaration,
InternalReadAccessor extractor,
boolean isUnification) {
this.packageNames = new HashSet<String>(packageNames);
this.expression = expression;
this.compilationUnit = compilationUnit;
this.constraintType = indexingDeclaration != null ? constraintType : IndexUtil.ConstraintType.UNKNOWN;
this.declarations = declarations == null ? EMPTY_DECLARATIONS : declarations;
this.operators = operators == null ? EMPTY_OPERATORS : operators;
this.indexingDeclaration = indexingDeclaration;
this.extractor = extractor;
this.isUnification = isUnification;
}
protected String getAccessedClass() {
return extractor instanceof ClassFieldReader ?
((ClassFieldReader)extractor).getClassName() :
extractor instanceof MVELObjectClassFieldReader ?
((MVELObjectClassFieldReader)extractor).getClassName() :
null;
}
public void setReadAccessor(InternalReadAccessor readAccessor) {
this.extractor = readAccessor;
}
public Collection<String> getPackageNames() {
return packageNames;
}
public void addPackageNames(Collection<String> otherPkgs) {
packageNames.addAll(otherPkgs);
}
public String getExpression() {
return expression;
}
public boolean isDynamic() {
return isDynamic;
}
public boolean isUnification() {
return isUnification;
}
public void unsetUnification() {
isUnification = false;
}
public boolean isIndexable(short nodeType) {
return getConstraintType().isIndexableForNode(nodeType);
}
public IndexUtil.ConstraintType getConstraintType() {
return constraintType;
}
public FieldValue getField() {
return fieldValue;
}
public boolean isAllowed(InternalFactHandle handle, InternalWorkingMemory workingMemory) {
if (isUnification) {
throw new UnsupportedOperationException( "Should not be called" );
}
return evaluate(handle, workingMemory, null);
}
public boolean isAllowedCachedLeft(ContextEntry context, InternalFactHandle handle) {
if (isUnification) {
if (((UnificationContextEntry)context).getVariable() != null) {
return true;
}
context = ((UnificationContextEntry)context).getContextEntry();
}
MvelContextEntry mvelContextEntry = (MvelContextEntry)context;
return evaluate(handle, mvelContextEntry.workingMemory, mvelContextEntry.tuple);
}
public boolean isAllowedCachedRight(Tuple tuple, ContextEntry context) {
if (isUnification) {
DroolsQuery query = ( DroolsQuery ) tuple.get( 0 ).getObject();
Variable v = query.getVariables()[ ((UnificationContextEntry)context).getReader().getIndex() ];
if (v != null) {
return true;
}
context = ((UnificationContextEntry)context).getContextEntry();
}
MvelContextEntry mvelContextEntry = (MvelContextEntry)context;
return evaluate(mvelContextEntry.rightHandle, mvelContextEntry.workingMemory, tuple);
}
protected boolean evaluate(InternalFactHandle handle, InternalWorkingMemory workingMemory, Tuple tuple) {
if (!jitted) {
int jittingThreshold = TEST_JITTING ? 0 : workingMemory.getKnowledgeBase().getConfiguration().getJittingThreshold();
if (conditionEvaluator == null) {
createMvelConditionEvaluator(workingMemory);
if (jittingThreshold == 0 && !isDynamic) { // Only for test purposes
forceJitEvaluator(handle, workingMemory, tuple);
}
}
if (!TEST_JITTING && !isDynamic && invocationCounter.getAndIncrement() == jittingThreshold) {
jitEvaluator(handle, workingMemory, tuple);
}
}
try {
return conditionEvaluator.evaluate( handle, workingMemory, tuple );
} catch (Exception e) {
throw new RuntimeException( "Error evaluating constraint '" + expression + "' in " + evaluationContext, e );
}
}
protected void createMvelConditionEvaluator(InternalWorkingMemory workingMemory) {
if (compilationUnit != null) {
MVELDialectRuntimeData data = getMVELDialectRuntimeData(workingMemory);
ExecutableStatement statement = (ExecutableStatement)compilationUnit.getCompiledExpression(data, evaluationContext);
ParserConfiguration configuration = statement instanceof CompiledExpression ?
((CompiledExpression)statement).getParserConfiguration() :
data.getParserConfiguration();
conditionEvaluator = new MvelConditionEvaluator(compilationUnit, configuration, statement, declarations, operators, getAccessedClass());
} else {
conditionEvaluator = new MvelConditionEvaluator(getParserConfiguration(workingMemory), expression, declarations, operators, getAccessedClass());
}
}
protected boolean forceJitEvaluator(InternalFactHandle handle, InternalWorkingMemory workingMemory, Tuple tuple) {
boolean mvelValue;
try {
mvelValue = conditionEvaluator.evaluate(handle, workingMemory, tuple);
} catch (ClassCastException cce) {
mvelValue = false;
}
jitEvaluator(handle, workingMemory, tuple);
return mvelValue;
}
protected void jitEvaluator(InternalFactHandle handle, InternalWorkingMemory workingMemory, Tuple tuple) {
jitted = true;
if (TEST_JITTING) {
executeJitting(handle, workingMemory, tuple);
} else {
ExecutorHolder.executor.execute(new ConditionJitter(this, handle, workingMemory, tuple));
}
}
private static class ConditionJitter implements Runnable {
private MvelConstraint mvelConstraint;
private InternalFactHandle rightHandle;
private InternalWorkingMemory workingMemory;
private Tuple tuple;
private ConditionJitter(MvelConstraint mvelConstraint, InternalFactHandle rightHandle, InternalWorkingMemory workingMemory, Tuple tuple) {
this.mvelConstraint = mvelConstraint;
this.rightHandle = rightHandle;
this.workingMemory = workingMemory;
this.tuple = tuple;
}
public void run() {
mvelConstraint.executeJitting(rightHandle, workingMemory, tuple);
mvelConstraint = null;
rightHandle = null;
workingMemory = null;
tuple = null;
}
}
private static class ExecutorHolder {
private static final Executor executor = ExecutorProviderFactory.getExecutorProvider().getExecutor();
}
private void executeJitting(InternalFactHandle handle, InternalWorkingMemory workingMemory, Tuple tuple) {
InternalKnowledgeBase kBase = workingMemory.getKnowledgeBase();
if ( !isJmxAvailable() && MemoryUtil.permGenStats.isUsageThresholdExceeded(kBase.getConfiguration().getPermGenThreshold()) ) {
return;
}
try {
if (analyzedCondition == null) {
analyzedCondition = ((MvelConditionEvaluator) conditionEvaluator).getAnalyzedCondition(handle, workingMemory, tuple);
}
conditionEvaluator = ASMConditionEvaluatorJitter.jitEvaluator(expression, analyzedCondition, declarations, operators, kBase.getRootClassLoader(), tuple);
} catch (Throwable t) {
if (TEST_JITTING) {
if (analyzedCondition == null) {
logger.error("Unable to analize condition for expression: " + expression, t);
} else {
throw new RuntimeException("Unable to analize condition for expression: " + expression, t);
}
} else {
logger.warn( "Exception jitting: " + expression +
" This is NOT an error and NOT prevent the correct execution since the constraint will be evaluated in intrepreted mode" );
}
}
}
public ContextEntry createContextEntry() {
if (declarations.length == 0) return null;
ContextEntry contextEntry = new MvelContextEntry(declarations);
if (isUnification) {
contextEntry = new UnificationContextEntry(contextEntry, declarations[0]);
}
return contextEntry;
}
public FieldIndex getFieldIndex() {
// declaration's offset can be modified by the reteoo's PatternBuilder so modify the indexingDeclaration accordingly
indexingDeclaration.getPattern().setOffset(declarations[0].getPattern().getOffset());
return new FieldIndex(extractor, indexingDeclaration, INDEX_EVALUATOR);
}
public InternalReadAccessor getFieldExtractor() {
return extractor;
}
public Declaration[] getRequiredDeclarations() {
return declarations;
}
public EvaluatorWrapper[] getOperators() {
return operators;
}
public void replaceDeclaration(Declaration oldDecl, Declaration newDecl) {
for (int i = 0; i < declarations.length; i++) {
if (declarations[i].equals(oldDecl)) {
if (compilationUnit != null) {
compilationUnit.replaceDeclaration(declarations[i], newDecl);
}
declarations[i] = newDecl;
break;
}
}
if (indexingDeclaration != null && indexingDeclaration.equals(oldDecl)) {
indexingDeclaration = newDecl;
}
}
// Slot specific
public BitMask getListenedPropertyMask(List<String> settableProperties) {
return analyzedCondition != null ?
calculateMask(analyzedCondition, settableProperties) :
calculateMaskFromExpression(settableProperties);
}
private BitMask calculateMaskFromExpression(List<String> settableProperties) {
BitMask mask = getEmptyPropertyReactiveMask(settableProperties.size());
String[] simpleExpressions = expression.split("\\Q&&\\E|\\Q||\\E");
for (String simpleExpression : simpleExpressions) {
String propertyName = getPropertyNameFromSimpleExpression(simpleExpression);
if (propertyName == null || propertyName.equals("this") || propertyName.length() == 0) {
return allSetButTraitBitMask();
}
int pos = settableProperties.indexOf(propertyName);
if (pos < 0 && Character.isUpperCase(propertyName.charAt(0))) {
propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
pos = settableProperties.indexOf(propertyName);
}
if (pos >= 0) { // Ignore not settable properties
mask = mask.set(pos + PropertySpecificUtil.CUSTOM_BITS_OFFSET);
}
}
return mask;
}
private String getPropertyNameFromSimpleExpression(String simpleExpression) {
StringBuilder propertyNameBuilder = new StringBuilder();
int cursor = extractFirstIdentifier(simpleExpression, propertyNameBuilder, 0);
String propertyName = propertyNameBuilder.toString();
if (propertyName.equals("this")) {
cursor = skipBlanks(simpleExpression, cursor);
if (simpleExpression.charAt(cursor) != '.') {
return "this";
}
propertyNameBuilder = new StringBuilder();
extractFirstIdentifier(simpleExpression, propertyNameBuilder, cursor);
propertyName = propertyNameBuilder.toString();
}
if (propertyName.startsWith("is") || propertyName.startsWith("get")) {
int exprPos = simpleExpression.indexOf(propertyName);
int propNameEnd = exprPos + propertyName.length();
if (simpleExpression.length() > propNameEnd + 2 && simpleExpression.charAt(propNameEnd) == '(') {
propertyName = getter2property(propertyName);
}
}
return propertyName;
}
private BitMask calculateMask(Condition condition, List<String> settableProperties) {
BitMask mask = getEmptyPropertyReactiveMask(settableProperties.size());
for (Condition c : ((CombinedCondition)condition).getConditions()) {
String propertyName = getFirstInvokedPropertyName(((SingleCondition) c).getLeft());
if (propertyName != null) {
mask = setPropertyOnMask(mask, settableProperties, propertyName);
}
}
return mask;
}
private String getFirstInvokedPropertyName(Expression expression) {
if (!(expression instanceof EvaluatedExpression)) {
return null;
}
List<Invocation> invocations = ((EvaluatedExpression)expression).invocations;
Invocation invocation = invocations.get(0);
if (invocation instanceof MethodInvocation) {
Method method = ((MethodInvocation)invocation).getMethod();
if (method == null) {
if (invocations.size() > 1) {
invocation = invocations.get(1);
if (invocation instanceof MethodInvocation) {
method = ((MethodInvocation)invocation).getMethod();
} else if (invocation instanceof FieldAccessInvocation) {
return ((FieldAccessInvocation)invocation).getField().getName();
}
} else {
return null;
}
}
return method != null ? getter2property(method.getName()) : null;
}
if (invocation instanceof FieldAccessInvocation) {
return ((FieldAccessInvocation)invocation).getField().getName();
}
return null;
}
// Externalizable
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeObject(packageNames);
out.writeObject(expression);
if (extractor instanceof ClassFieldReader) {
out.writeObject( ( (ClassFieldReader) extractor ).getAccessorKey() );
} else {
out.writeObject( extractor );
}
out.writeObject(indexingDeclaration);
out.writeObject(declarations);
out.writeObject(constraintType);
out.writeBoolean(isUnification);
out.writeBoolean(isDynamic);
out.writeObject(fieldValue);
out.writeObject(compilationUnit);
out.writeObject(evaluationContext);
out.writeObject(operators);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
packageNames = (Set<String>)in.readObject();
expression = (String)in.readObject();
( (DroolsObjectInputStream) in ).readExtractor( this::setReadAccessor );
indexingDeclaration = (Declaration) in.readObject();
declarations = (Declaration[]) in.readObject();
constraintType = (IndexUtil.ConstraintType) in.readObject();
isUnification = in.readBoolean();
isDynamic = in.readBoolean();
fieldValue = (FieldValue) in.readObject();
compilationUnit = (MVELCompilationUnit) in.readObject();
evaluationContext = (EvaluationContext) in.readObject();
operators = (EvaluatorWrapper[]) in.readObject();
}
public boolean isTemporal() {
return false;
}
@Override
public MvelConstraint cloneIfInUse() {
MvelConstraint clone = (MvelConstraint)super.cloneIfInUse();
if ( clone != this) {
clone.conditionEvaluator = null;
}
return clone;
}
public MvelConstraint clone() {
Declaration[] clonedDeclarations = new Declaration[declarations.length];
System.arraycopy(declarations, 0, clonedDeclarations, 0, declarations.length);
MvelConstraint clone = new MvelConstraint();
clone.setType(getType());
clone.packageNames = packageNames;
clone.expression = expression;
clone.fieldValue = fieldValue;
clone.constraintType = constraintType;
clone.declarations = clonedDeclarations;
clone.operators = operators;
clone.indexingDeclaration = indexingDeclaration;
clone.extractor = extractor;
clone.isUnification = isUnification;
clone.isDynamic = isDynamic;
clone.conditionEvaluator = conditionEvaluator;
clone.compilationUnit = compilationUnit != null ? compilationUnit.clone() : null;
return clone;
}
public int hashCode() {
if (isAlphaHashable()) {
return 29 * getLeftInExpression(IndexUtil.ConstraintType.EQUAL).hashCode() + 31 * fieldValue.hashCode();
}
return expression.hashCode();
}
private String getLeftInExpression(IndexUtil.ConstraintType constraint) {
return expression.substring(0, expression.indexOf(constraint.getOperator())).trim();
}
private boolean isAlphaHashable() {
return fieldValue != null && constraintType == IndexUtil.ConstraintType.EQUAL && getType() == ConstraintType.ALPHA;
}
public boolean equals(final Object object) {
if ( this == object ) {
return true;
}
if ( object == null || object.getClass() != MvelConstraint.class ) {
return false;
}
MvelConstraint other = (MvelConstraint) object;
if (isAlphaHashable()) {
if ( !other.isAlphaHashable() ||
!getLeftInExpression(IndexUtil.ConstraintType.EQUAL).equals(other.getLeftInExpression(IndexUtil.ConstraintType.EQUAL)) ||
!fieldValue.equals(other.fieldValue) ) {
return false;
}
} else {
if (!equalsIgnoreSpaces( expression, other.expression )) {
return false;
}
}
if (declarations.length != other.declarations.length) {
return false;
}
for (int i = 0; i < declarations.length; i++) {
if ( !declarations[i].getExtractor().equals( other.declarations[i].getExtractor() ) ) {
return false;
}
}
return true;
}
public boolean equals(Object object, InternalKnowledgeBase kbase) {
if ( !equals( object ) ) {
return false;
}
String thisPkg = packageNames.iterator().next();
String otherPkg = ((MvelConstraint) object).packageNames.iterator().next();
if (thisPkg.equals( otherPkg )) {
return true;
}
Map<String, Object> thisImports = ((MVELDialectRuntimeData) kbase.getPackage( thisPkg ).getDialectRuntimeRegistry().getDialectData("mvel")).getImports();
Map<String, Object> otherImports = ((MVELDialectRuntimeData) kbase.getPackage( otherPkg ).getDialectRuntimeRegistry().getDialectData("mvel")).getImports();
if (fieldValue != null && constraintType.getOperator() != null) {
return equalsExpressionTokensInBothImports(getLeftInExpression(constraintType), thisImports, otherImports);
} else {
return equalsExpressionTokensInBothImports(expression, thisImports, otherImports);
}
}
private boolean equalsExpressionTokensInBothImports(String expression, Map<String, Object> thisImports, Map<String, Object> otherImports) {
for (String token : splitExpression(expression)) {
if ( !areNullSafeEquals(thisImports.get(token), otherImports.get(token)) ) {
return false;
}
}
return true;
}
/**
* Splits the expression in token (words) ignoring everything that is between quotes
*/
private static List<String> splitExpression(String expression) {
List<String> tokens = new ArrayList<String>();
int lastStart = -1;
boolean isQuoted = false;
for (int i = 0; i < expression.length(); i++) {
if ( lastStart == -1 ) {
if ( !isQuoted && Character.isJavaIdentifierStart( expression.charAt(i) ) ) {
lastStart = i;
}
} else if ( !Character.isJavaIdentifierPart( expression.charAt(i) ) ) {
tokens.add(expression.subSequence(lastStart, i).toString());
lastStart = -1;
}
if (expression.charAt(i) == '"' || expression.charAt(i) == '\'') {
if (i == 0 || expression.charAt(i-1) != '\\') {
isQuoted = !isQuoted;
}
if (isQuoted) {
lastStart = -1;
}
}
}
if (lastStart != -1) {
tokens.add( expression.subSequence( lastStart, expression.length() ).toString() );
}
return tokens;
}
@Override
public String toString() {
return expression;
}
protected ParserConfiguration getParserConfiguration(InternalWorkingMemory workingMemory) {
return getMVELDialectRuntimeData(workingMemory).getParserConfiguration();
}
protected MVELDialectRuntimeData getMVELDialectRuntimeData(InternalWorkingMemory workingMemory) {
return getMVELDialectRuntimeData(workingMemory.getKnowledgeBase());
}
protected MVELDialectRuntimeData getMVELDialectRuntimeData(InternalKnowledgeBase kbase) {
for (String packageName : packageNames) {
InternalKnowledgePackage pkg = kbase.getPackage( packageName );
if (pkg != null) {
return ((MVELDialectRuntimeData) pkg.getDialectRuntimeRegistry().getDialectData("mvel"));
}
}
return null;
}
// MvelArrayContextEntry
public static class MvelContextEntry implements ContextEntry {
protected ContextEntry next;
protected Tuple tuple;
protected InternalFactHandle rightHandle;
protected Declaration[] declarations;
protected transient InternalWorkingMemory workingMemory;
public MvelContextEntry() { }
public MvelContextEntry(Declaration[] declarations) {
this.declarations = declarations;
}
public ContextEntry getNext() {
return this.next;
}
public void setNext(final ContextEntry entry) {
this.next = entry;
}
public void updateFromTuple(InternalWorkingMemory workingMemory, Tuple tuple) {
this.tuple = tuple;
this.workingMemory = workingMemory;
}
public void updateFromFactHandle(InternalWorkingMemory workingMemory, InternalFactHandle handle) {
this.workingMemory = workingMemory;
rightHandle = handle;
}
public void resetTuple() {
tuple = null;
}
public void resetFactHandle() {
workingMemory = null;
rightHandle = null;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(tuple);
out.writeObject(rightHandle);
out.writeObject(declarations);
out.writeObject(next);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
tuple = (Tuple)in.readObject();
rightHandle = (InternalFactHandle) in.readObject();
declarations = (Declaration[])in.readObject();
next = (ContextEntry)in.readObject();
}
public InternalFactHandle getRight() {
return rightHandle;
}
public Declaration[] getDeclarations() {
return declarations;
}
public InternalWorkingMemory getWorkingMemory() {
return workingMemory;
}
}
public static class UnificationContextEntry implements ContextEntry {
private ContextEntry contextEntry;
private Declaration declaration;
private Variable variable;
private ArrayElementReader reader;
public UnificationContextEntry() { }
public UnificationContextEntry(ContextEntry contextEntry,
Declaration declaration) {
this.contextEntry = contextEntry;
this.declaration = declaration;
reader = ( ArrayElementReader ) this.declaration.getExtractor();
}
public ContextEntry getContextEntry() {
return this.contextEntry;
}
public ArrayElementReader getReader() {
return reader;
}
public ContextEntry getNext() {
return this.contextEntry.getNext();
}
public void resetFactHandle() {
this.contextEntry.resetFactHandle();
}
public void resetTuple() {
this.contextEntry.resetTuple();
this.variable = null;
}
public void setNext(ContextEntry entry) {
this.contextEntry.setNext( entry );
}
public void updateFromFactHandle(InternalWorkingMemory workingMemory,
InternalFactHandle handle) {
this.contextEntry.updateFromFactHandle( workingMemory, handle );
}
public void updateFromTuple(InternalWorkingMemory workingMemory,
Tuple tuple) {
DroolsQuery query = ( DroolsQuery ) tuple.getObject( 0 );
this.variable = query.getVariables()[ this.reader.getIndex() ];
if ( this.variable == null ) {
// if there is no Variable, handle it as a normal constraint
this.contextEntry.updateFromTuple( workingMemory, tuple );
}
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.contextEntry = (ContextEntry) in.readObject();
this.declaration = ( Declaration ) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( this.contextEntry );
out.writeObject( this.declaration );
}
public Variable getVariable() {
return this.variable;
}
}
public static final IndexEvaluator INDEX_EVALUATOR = new PlainIndexEvaluator();
public static class PlainIndexEvaluator implements IndexEvaluator {
public boolean evaluate(InternalWorkingMemory workingMemory,
final InternalReadAccessor extractor1,
final Object object1,
final InternalReadAccessor extractor2,
final Object object2) {
return evaluate(workingMemory, extractor1.getValue( workingMemory, object1 ), extractor2, object2);
}
public boolean evaluate(InternalWorkingMemory workingMemory,
final Object value1,
final InternalReadAccessor extractor2,
final Object object2) {
final Object value2 = extractor2.getValue( workingMemory, object2 );
if (value1 == null) {
return value2 == null;
}
if (value1 instanceof String) {
return value2 != null && value1.equals(value2.toString());
}
if (value2 instanceof String) {
return value1 != null && value2.equals(value1.toString());
}
return value1.equals( value2 );
}
}
public void registerEvaluationContext(BuildContext buildContext) {
evaluationContext.addContext(buildContext);
}
public static class EvaluationContext implements Externalizable {
private Collection<String> evaluatedRules = new HashSet<String>();
public void addContext(BuildContext buildContext) {
evaluatedRules.add(buildContext.getRule().toRuleNameAndPathString());
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(evaluatedRules);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
evaluatedRules = (Collection<String>)in.readObject();
}
@Override
public String toString() {
return evaluatedRules.toString();
}
}
}