Below is the full story of "How I Implemented My First Custom Operator
in Drools". (I gladly admit that I stole much of the code from the
implementations of Drools' own operators, and that I still do not
understand all the subtleties, especially why a negated operator has
to be implemented all over again.)
Summary: You must write an evaluator definition (ED) to notify the
knowledge builder's (KB) parser about the operator keyword and provide
the operator's implementation, for runtime. The KB may then be
instantiated with a KB configuration where this new ED is given as a
KB option. - The operator "disjoint" returns true iff both Set<?>
operandrs have no element in common. It crashes ungracefully (CCE: XXX
cannot be cast to java.util.Set), if either operand is not a set.
Code snippets of a use case:
public class Element {
private String name;
private Set<Integer> numbers;
public Element( String name, Integer... numbers ){ ... }
// getters...
}
// ... create some facts
for( Element element: new Element[]{
new Element( "primes", 2, 3, 5, 7, 11, 13, 17, 19 ),
new Element( "even", 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 ),
new Element( "powers of 2", 1, 2, 4, 8, 16 ),
new Element( "odd", 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 ),
new Element( "esoteric", 7, 13 ), };
elements ){ session.insert( element );
}
// rule
rule "find disjoint pairs"
when
e1: Element( n1 : name, s1 : numbers )
e2: Element( this != e1, n2 : name, numbers disjoint s1 )
then
System.out.println( n1 + " and " + n2 + " are disjoint.");
end
// results
powers of 2 and esoteric are disjoint.
even and esoteric are disjoint.
esoteric and powers of 2 are disjoint.
esoteric and even are disjoint.
even and odd are disjoint.
odd and even are disjoint.
Code snippet for the KB instantiation (imports are from
org.drools.builder. and org.drools.builder.conf):
kBase = KnowledgeBaseFactory.newKnowledgeBase();
EvaluatorDefinition evDef = new appl.DisjointEvaluatorsDefinition();
MultiValueKnowledgeBuilderOption evOption = EvaluatorOption.get(
"setop", evDef );
KnowledgeBuilderConfiguration kbConfig
=KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration();
kbConfig.setOption( evOption );
KnowledgeBuilder kbuilder =
KnowledgeBuilderFactory.newKnowledgeBuilder( kbConfig );
Complete code of the ED for "disjoint" and its negation. A Set == null
is treated as the empty set.
package appl;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashSet;
import java.util.Set;
import org.drools.base.BaseEvaluator;
import org.drools.base.ValueType;
import org.drools.common.InternalWorkingMemory;
import org.drools.rule.VariableRestriction.ObjectVariableContextEntry;
import org.drools.rule.VariableRestriction.VariableContextEntry;
import org.drools.spi.Evaluator;
import org.drools.spi.FieldValue;
import org.drools.spi.InternalReadAccessor;
import org.drools.base.evaluators.EvaluatorCache;
import org.drools.base.evaluators.EvaluatorDefinition;
import org.drools.base.evaluators.Operator;
public class DisjointEvaluatorsDefinition implements EvaluatorDefinition {
public static final Operator DISJOINT =
Operator.addOperatorToRegistry( "disjoint", false );
public static final Operator NOT_DISJOINT =
Operator.addOperatorToRegistry( "disjoint", true );
private static final String[] SUPPORTED_IDS = {
DISJOINT.getOperatorString() };
private EvaluatorCache evaluators = new EvaluatorCache() {
private static final long serialVersionUID = 510l;
{
addEvaluator( ValueType.OBJECT_TYPE, DISJOINT,
DisjointEvaluator.INSTANCE );
addEvaluator( ValueType.OBJECT_TYPE, NOT_DISJOINT,
NotDisjointEvaluator.INSTANCE );
}
};
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
evaluators = (EvaluatorCache) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( evaluators );
}
public Evaluator getEvaluator(ValueType type,
Operator operator) {
return this.evaluators.getEvaluator( type,
operator );
}
public Evaluator getEvaluator(ValueType type,
Operator operator,
String parameterText) {
return this.evaluators.getEvaluator( type,
operator );
}
public Evaluator getEvaluator(final ValueType type,
final String operatorId,
final boolean isNegated,
final String parameterText) {
return this.getEvaluator( type,
operatorId,
isNegated,
parameterText,
Target.FACT,
Target.FACT );
}
public Evaluator getEvaluator(final ValueType type,
final String operatorId,
final boolean isNegated,
final String parameterText,
final Target left,
final Target right ) {
return this.evaluators.getEvaluator( type,
Operator.determineOperator( operatorId, isNegated ) );
}
public String[] getEvaluatorIds() {
return SUPPORTED_IDS;
}
public boolean isNegatable() {
return true;
}
public Target getTarget() {
return Target.FACT;
}
public boolean supportsType(ValueType type) {
return this.evaluators.supportsType( type );
}
public static class DisjointEvaluator extends BaseEvaluator {
private static boolean isIntersectionEmpty( Set<?> a, Set<?> b ){
if( a == null || b == null ) return true;
Set<?> h = new HashSet<Object>( a );
h.retainAll( b );
return h.isEmpty();
}
private static final long serialVersionUID = 510l;
public final static Evaluator INSTANCE = new
DisjointEvaluator();
public DisjointEvaluator() {
super( ValueType.OBJECT_TYPE, DISJOINT );
}
public boolean evaluate(InternalWorkingMemory workingMemory,
final InternalReadAccessor extractor,
final Object object1,
final FieldValue object2) {
final Set<?> set2 = (Set<?>) object2.getValue();
final Set<?> set1 = (Set<?>) extractor.getValue(
workingMemory, object1 );
return isIntersectionEmpty( set1, set2 );
}
public boolean evaluateCachedRight(InternalWorkingMemory workingMemory,
final VariableContextEntry context,
final Object left) {
final Set<?> set2 = (Set<?>)
context.declaration.getExtractor().getValue( workingMemory, left );
final Set<?> set1 = (Set<?>) ((ObjectVariableContextEntry)
context).right;
return isIntersectionEmpty( set1, set2 );
}
public boolean evaluateCachedLeft(InternalWorkingMemory workingMemory,
final VariableContextEntry context,
final Object right) {
final Set<?> set2 = (Set<?>)((ObjectVariableContextEntry)
context).left;
final Set<?> set1 = (Set<?>) context.extractor.getValue(
workingMemory, right );
return isIntersectionEmpty( set1, set2 );
}
public boolean evaluate(InternalWorkingMemory workingMemory,
final InternalReadAccessor extractor1,
final Object object1,
final InternalReadAccessor extractor2,
final Object object2) {
final Set<?> set2 = (Set<?>)extractor2.getValue(
workingMemory, object2 );
final Set<?> set1 = (Set<?>) extractor1.getValue( workingMemory,
object1 );
return isIntersectionEmpty( set1, set2 );
}
public String toString() {
return "Set disjoint";
}
}
public static class NotDisjointEvaluator extends BaseEvaluator {
private static boolean isIntersectionNotEmpty( Set<?> a, Set<?> b ){
if( a == null || b == null ) return false;
Set<?> h = new HashSet<Object>( a );
h.retainAll( b );
return ! h.isEmpty();
}
private static final long serialVersionUID = 510l;
public final static Evaluator INSTANCE = new
NotDisjointEvaluator();
public NotDisjointEvaluator() {
super( ValueType.OBJECT_TYPE, NOT_DISJOINT );
}
public boolean evaluate(InternalWorkingMemory workingMemory,
final InternalReadAccessor extractor,
final Object object1,
final FieldValue object2) {
final Set<?> set2 = (Set<?>) object2.getValue();
final Set<?> set1 = (Set<?>) extractor.getValue(
workingMemory, object1 );
return isIntersectionNotEmpty( set1, set2 );
}
public boolean evaluateCachedRight(InternalWorkingMemory workingMemory,
final VariableContextEntry context,
final Object left) {
final Set<?> set2 = (Set<?>)
context.declaration.getExtractor().getValue( workingMemory, left );
final Set<?> set1 = (Set<?>) ((ObjectVariableContextEntry)
context).right;
return isIntersectionNotEmpty( set1, set2 );
}
public boolean evaluateCachedLeft(InternalWorkingMemory workingMemory,
final VariableContextEntry context,
final Object right) {
final Set<?> set2 = (Set<?>)((ObjectVariableContextEntry)
context).left;
final Set<?> set1 = (Set<?>) context.extractor.getValue(
workingMemory, right );
return isIntersectionNotEmpty( set1, set2 );
}
public boolean evaluate(InternalWorkingMemory workingMemory,
final InternalReadAccessor extractor1,
final Object object1,
final InternalReadAccessor extractor2,
final Object object2) {
final Set<?> set2 = (Set<?>)extractor2.getValue(
workingMemory, object2 );
final Set<?> set1 = (Set<?>) extractor1.getValue( workingMemory,
object1 );
return isIntersectionNotEmpty( set1, set2 );
}
public String toString() {
return "Set not disjoint";
}
}
}
On 25 August 2010 16:55, mechlife <gaiamech(a)gmail.com> wrote:
Thanks for your response.
I need to use it like 1-4 times depending on what the legacy rules are setup
on one rule but i do have thousands of rules.
Also, a future state might be to get these imported into a tool for business
users to start writing these and would definitely be more pleasing if they
had an operator that they could use.
Would you be able to point me to some resources on how to do the custom
operator
if the need arise.
thanks again for your time.
--
View this message in context:
http://drools-java-rules-engine.46999.n3.nabble.com/comparing-an-intersec...
Sent from the Drools - User mailing list archive at
Nabble.com.
_______________________________________________
rules-users mailing list
rules-users(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/rules-users