001: package org.drools.rule;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.beans.IntrospectionException;
020:
021: import junit.framework.TestCase;
022:
023: import org.drools.Cheese;
024: import org.drools.FactHandle;
025: import org.drools.RuleBaseFactory;
026: import org.drools.WorkingMemory;
027: import org.drools.base.ClassFieldExtractor;
028: import org.drools.base.ClassFieldExtractorCache;
029: import org.drools.base.ClassObjectType;
030: import org.drools.base.FieldFactory;
031: import org.drools.base.ShadowProxy;
032: import org.drools.base.ValueType;
033: import org.drools.base.evaluators.Operator;
034: import org.drools.common.InternalFactHandle;
035: import org.drools.common.InternalWorkingMemory;
036: import org.drools.reteoo.InstrumentedReteTuple;
037: import org.drools.reteoo.ReteooRuleBase;
038: import org.drools.rule.PredicateConstraint.PredicateContextEntry;
039: import org.drools.rule.ReturnValueRestriction.ReturnValueContextEntry;
040: import org.drools.spi.Evaluator;
041: import org.drools.spi.FieldExtractor;
042: import org.drools.spi.FieldValue;
043: import org.drools.spi.PredicateExpression;
044: import org.drools.spi.ReturnValueExpression;
045: import org.drools.spi.Tuple;
046:
047: public class FieldConstraintTest extends TestCase {
048: public FieldConstraintTest() {
049: super ();
050: }
051:
052: /**
053: * <pre>
054: *
055: *
056: * ( Cheese (type "cheddar") )
057: *
058: *
059: * </pre>
060: *
061: * This is currently the same as using a ReturnValueConstraint just that it
062: * doesn't need any requiredDeclarations
063: *
064: * @throws IntrospectionException
065: */
066: public void testLiteralConstraint() throws IntrospectionException {
067: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
068: .newRuleBase();
069: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
070: .newStatefulSession();
071:
072: final ClassFieldExtractor extractor = ClassFieldExtractorCache
073: .getExtractor(Cheese.class, "type", getClass()
074: .getClassLoader());
075:
076: final FieldValue field = FieldFactory.getFieldValue("cheddar");
077:
078: final Evaluator evaluator = ValueType.STRING_TYPE
079: .getEvaluator(Operator.EQUAL);
080:
081: final LiteralConstraint constraint = new LiteralConstraint(
082: extractor, evaluator, field);
083:
084: final Cheese cheddar = new Cheese("cheddar", 5);
085:
086: final InternalFactHandle cheddarHandle = (InternalFactHandle) workingMemory
087: .insert(cheddar);
088:
089: // check constraint
090: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
091: workingMemory));
092:
093: final Cheese stilton = new Cheese("stilton", 5);
094:
095: final InternalFactHandle stiltonHandle = (InternalFactHandle) workingMemory
096: .insert(stilton);
097:
098: // check constraint
099: assertFalse(constraint.isAllowed(stiltonHandle.getObject(),
100: workingMemory));
101: }
102:
103: /**
104: * <pre>
105: *
106: *
107: * Cheese( price == 5 )
108: *
109: *
110: * </pre>
111: *
112: * @throws IntrospectionException
113: */
114: public void testPrimitiveLiteralConstraint()
115: throws IntrospectionException {
116: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
117: .newRuleBase();
118: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
119: .newStatefulSession();
120:
121: final ClassFieldExtractor extractor = ClassFieldExtractorCache
122: .getExtractor(Cheese.class, "price", getClass()
123: .getClassLoader());
124:
125: final FieldValue field = FieldFactory.getFieldValue(5);
126:
127: final Evaluator evaluator = ValueType.PINTEGER_TYPE
128: .getEvaluator(Operator.EQUAL);
129:
130: final LiteralConstraint constraint = new LiteralConstraint(
131: extractor, evaluator, field);
132:
133: final Cheese cheddar = new Cheese("cheddar", 5);
134:
135: final InternalFactHandle cheddarHandle = (InternalFactHandle) workingMemory
136: .insert(cheddar);
137:
138: // check constraint
139: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
140: workingMemory));
141:
142: final Cheese stilton = new Cheese("stilton", 10);
143:
144: final InternalFactHandle stiltonHandle = (InternalFactHandle) workingMemory
145: .insert(stilton);
146:
147: // check constraint
148: assertFalse(constraint.isAllowed(stiltonHandle.getObject(),
149: workingMemory));
150: }
151:
152: /**
153: * <pre>
154: *
155: *
156: * (Cheese (price ?price1 )
157: * (Cheese (price ?price2&:(= ?price2 (* 2 ?price1) )
158: *
159: *
160: * </pre>
161: *
162: * @throws IntrospectionException
163: */
164: public void testPredicateConstraint() throws IntrospectionException {
165: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
166: .newRuleBase();
167: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
168: .newStatefulSession();
169:
170: final FieldExtractor priceExtractor = ClassFieldExtractorCache
171: .getExtractor(Cheese.class, "price", getClass()
172: .getClassLoader());
173:
174: Pattern pattern = new Pattern(0, new ClassObjectType(
175: Cheese.class));
176:
177: // Bind the extractor to a decleration
178: // Declarations know the pattern they derive their value form
179: final Declaration price1Declaration = new Declaration("price1",
180: priceExtractor, pattern);
181:
182: pattern = new Pattern(1, new ClassObjectType(Cheese.class));
183:
184: // Bind the extractor to a decleration
185: // Declarations know the pattern they derive their value form
186: final Declaration price2Declaration = new Declaration("price2",
187: priceExtractor, pattern);
188:
189: final PredicateExpression evaluator = new PredicateExpression() {
190:
191: /**
192: *
193: */
194: private static final long serialVersionUID = 400L;
195:
196: public boolean evaluate(Object object, Tuple tuple,
197: Declaration[] previousDeclarations,
198: Declaration[] localDeclarations,
199: WorkingMemory workingMemory) {
200: int price1 = previousDeclarations[0].getIntValue(
201: (InternalWorkingMemory) workingMemory,
202: workingMemory.getObject(tuple
203: .get(previousDeclarations[0])));
204: int price2 = localDeclarations[0].getIntValue(
205: (InternalWorkingMemory) workingMemory, object);
206:
207: return (price2 == (price1 * 2));
208:
209: }
210: };
211:
212: final PredicateConstraint constraint1 = new PredicateConstraint(
213: evaluator, new Declaration[] { price1Declaration },
214: new Declaration[] { price2Declaration },
215: new String[] {});
216:
217: final Cheese cheddar0 = new Cheese("cheddar", 5);
218: final FactHandle f0 = workingMemory.insert(cheddar0);
219: InstrumentedReteTuple tuple = new InstrumentedReteTuple(f0);
220:
221: final Cheese cheddar1 = new Cheese("cheddar", 10);
222: final InternalFactHandle f1 = (InternalFactHandle) workingMemory
223: .insert(cheddar1);
224:
225: tuple = new InstrumentedReteTuple(tuple, f1);
226:
227: final PredicateContextEntry context = (PredicateContextEntry) constraint1
228: .getContextEntry();
229: context.updateFromTuple(workingMemory, tuple);
230: assertTrue(constraint1.isAllowedCachedLeft(context, f1
231: .getObject()));
232: }
233:
234: /**
235: * <pre>
236: *
237: *
238: * (Cheese (price ?price )
239: * (Cheese (price =(* 2 ?price) )
240: * (Cheese (price >(* 2 ?price) )
241: *
242: *
243: * </pre>
244: *
245: * @throws IntrospectionException
246: */
247: public void testReturnValueConstraint()
248: throws IntrospectionException {
249: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
250: .newRuleBase();
251: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
252: .newStatefulSession();
253:
254: final FieldExtractor priceExtractor = ClassFieldExtractorCache
255: .getExtractor(Cheese.class, "price", getClass()
256: .getClassLoader());
257:
258: final Pattern pattern = new Pattern(0, new ClassObjectType(
259: Cheese.class));
260:
261: // Bind the extractor to a decleration
262: // Declarations know the pattern they derive their value form
263: final Declaration priceDeclaration = new Declaration("price1",
264: priceExtractor, pattern);
265:
266: final ReturnValueExpression isDoubleThePrice = new ReturnValueExpression() {
267: /**
268: *
269: */
270: private static final long serialVersionUID = 400L;
271:
272: public FieldValue evaluate(
273: Object object,
274: Tuple tuple, // ?price
275: Declaration[] previousDeclarations,
276: Declaration[] localDeclarations,
277: WorkingMemory workingMemory) {
278: int price = ((Number) previousDeclarations[0].getValue(
279: (InternalWorkingMemory) workingMemory,
280: workingMemory.getObject(tuple
281: .get(previousDeclarations[0]))))
282: .intValue();
283: return FieldFactory.getFieldValue(2 * price);
284:
285: }
286: };
287:
288: final ReturnValueRestriction restriction1 = new ReturnValueRestriction(
289: priceExtractor, isDoubleThePrice,
290: new Declaration[] { priceDeclaration },
291: new Declaration[0], new String[0],
292: ValueType.INTEGER_TYPE.getEvaluator(Operator.EQUAL));
293:
294: final ReturnValueConstraint constraint1 = new ReturnValueConstraint(
295: priceExtractor, restriction1);
296:
297: final ReturnValueRestriction restriction2 = new ReturnValueRestriction(
298: priceExtractor, isDoubleThePrice,
299: new Declaration[] { priceDeclaration },
300: new Declaration[0], new String[0],
301: ValueType.INTEGER_TYPE.getEvaluator(Operator.GREATER));
302:
303: final ReturnValueConstraint constraint2 = new ReturnValueConstraint(
304: priceExtractor, restriction2);
305:
306: final Cheese cheddar0 = new Cheese("cheddar", 5);
307: final FactHandle f0 = workingMemory.insert(cheddar0);
308:
309: InstrumentedReteTuple tuple = new InstrumentedReteTuple(f0);
310:
311: final Cheese cheddar1 = new Cheese("cheddar", 10);
312: final InternalFactHandle f1 = (InternalFactHandle) workingMemory
313: .insert(cheddar1);
314: tuple = new InstrumentedReteTuple(tuple, f1);
315:
316: final ReturnValueContextEntry context1 = (ReturnValueContextEntry) constraint1
317: .getContextEntry();
318: context1.updateFromTuple(workingMemory, tuple);
319: assertTrue(constraint1.isAllowedCachedLeft(context1, f1
320: .getObject()));
321:
322: final ReturnValueContextEntry context2 = (ReturnValueContextEntry) constraint2
323: .getContextEntry();
324: context2.updateFromTuple(workingMemory, tuple);
325: assertFalse(constraint2.isAllowedCachedLeft(context2, f1
326: .getObject()));
327:
328: final Cheese cheddar2 = new Cheese("cheddar", 11);
329:
330: final InternalFactHandle f2 = (InternalFactHandle) workingMemory
331: .insert(cheddar2);
332:
333: assertTrue(constraint2.isAllowedCachedLeft(context2, f2
334: .getObject()));
335: }
336:
337: /**
338: * <pre>
339: *
340: *
341: * type == "cheddar" && price > 10
342: *
343: *
344: * </pre>
345: *
346: * Test the use of the composite AND constraint. Composite AND constraints are only
347: * used when nested inside other field constraints, as the top level AND is implicit
348: *
349: * @throws IntrospectionException
350: */
351: public void testCompositeAndConstraint() {
352: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
353: .newRuleBase();
354: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
355: .newStatefulSession();
356:
357: final ClassFieldExtractor extractor = ClassFieldExtractorCache
358: .getExtractor(Cheese.class, "type", getClass()
359: .getClassLoader());
360:
361: final FieldValue field = FieldFactory.getFieldValue("cheddar");
362:
363: final Evaluator evaluator = ValueType.STRING_TYPE
364: .getEvaluator(Operator.EQUAL);
365:
366: final LiteralConstraint constraint1 = new LiteralConstraint(
367: extractor, evaluator, field);
368:
369: final ClassFieldExtractor priceExtractor = ClassFieldExtractorCache
370: .getExtractor(Cheese.class, "price", getClass()
371: .getClassLoader());
372:
373: final FieldValue priceField = FieldFactory.getFieldValue(10);
374:
375: final Evaluator priceEvaluator = ValueType.INTEGER_TYPE
376: .getEvaluator(Operator.GREATER);
377:
378: final LiteralConstraint constraint2 = new LiteralConstraint(
379: priceExtractor, priceEvaluator, priceField);
380:
381: final Cheese cheddar = new Cheese("cheddar", 15);
382:
383: final AndConstraint constraint = new AndConstraint();
384: constraint.addAlphaConstraint(constraint1);
385: constraint.addAlphaConstraint(constraint2);
386:
387: final InternalFactHandle cheddarHandle = (InternalFactHandle) workingMemory
388: .insert(cheddar);
389:
390: // check constraint
391: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
392: workingMemory));
393:
394: cheddar.setPrice(5);
395: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
396: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
397: workingMemory));
398:
399: cheddar.setType("stilton");
400: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
401: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
402: workingMemory));
403:
404: cheddar.setPrice(15);
405: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
406: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
407: workingMemory));
408: }
409:
410: /**
411: * <pre>
412: *
413: *
414: * Cheese( type == "cheddar" || price > 10 )
415: *
416: *
417: * </pre>
418: *
419: * Test the use of the composite OR constraint.
420: *
421: * @throws IntrospectionException
422: */
423: public void testCompositeOrConstraint() {
424: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
425: .newRuleBase();
426: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
427: .newStatefulSession();
428:
429: final ClassFieldExtractor extractor = ClassFieldExtractorCache
430: .getExtractor(Cheese.class, "type", getClass()
431: .getClassLoader());
432:
433: final FieldValue field = FieldFactory.getFieldValue("cheddar");
434:
435: final Evaluator evaluator = ValueType.STRING_TYPE
436: .getEvaluator(Operator.EQUAL);
437:
438: final LiteralConstraint constraint1 = new LiteralConstraint(
439: extractor, evaluator, field);
440:
441: final ClassFieldExtractor priceExtractor = ClassFieldExtractorCache
442: .getExtractor(Cheese.class, "price", getClass()
443: .getClassLoader());
444:
445: final FieldValue priceField = FieldFactory.getFieldValue(10);
446:
447: final Evaluator priceEvaluator = ValueType.INTEGER_TYPE
448: .getEvaluator(Operator.GREATER);
449:
450: final LiteralConstraint constraint2 = new LiteralConstraint(
451: priceExtractor, priceEvaluator, priceField);
452:
453: final Cheese cheddar = new Cheese("cheddar", 15);
454:
455: final OrConstraint constraint = new OrConstraint();
456: constraint.addAlphaConstraint(constraint1);
457: constraint.addAlphaConstraint(constraint2);
458:
459: final InternalFactHandle cheddarHandle = (InternalFactHandle) workingMemory
460: .insert(cheddar);
461:
462: // check constraint
463: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
464: workingMemory));
465:
466: cheddar.setPrice(5);
467: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
468: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
469: workingMemory));
470:
471: cheddar.setType("stilton");
472: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
473: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
474: workingMemory));
475:
476: cheddar.setPrice(15);
477: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
478: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
479: workingMemory));
480: }
481:
482: /**
483: * <pre>
484: *
485: *
486: * Cheese( ( type == "cheddar" && price > 10) || ( type == "e;stilton"e; && price < 10 ) )
487: *
488: *
489: * </pre>
490: *
491: * Test the use of the composite OR constraint.
492: *
493: * @throws IntrospectionException
494: */
495: public void testNestedCompositeConstraints() {
496: final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory
497: .newRuleBase();
498: final InternalWorkingMemory workingMemory = (InternalWorkingMemory) ruleBase
499: .newStatefulSession();
500:
501: final ClassFieldExtractor typeExtractor = ClassFieldExtractorCache
502: .getExtractor(Cheese.class, "type", getClass()
503: .getClassLoader());
504:
505: final FieldValue cheddarField = FieldFactory
506: .getFieldValue("cheddar");
507:
508: final Evaluator stringEqual = ValueType.STRING_TYPE
509: .getEvaluator(Operator.EQUAL);
510:
511: // type == 'cheddar'
512: final LiteralConstraint constraint1 = new LiteralConstraint(
513: typeExtractor, stringEqual, cheddarField);
514:
515: final ClassFieldExtractor priceExtractor = ClassFieldExtractorCache
516: .getExtractor(Cheese.class, "price", getClass()
517: .getClassLoader());
518:
519: final FieldValue field10 = FieldFactory.getFieldValue(10);
520:
521: final Evaluator integerGreater = ValueType.INTEGER_TYPE
522: .getEvaluator(Operator.GREATER);
523:
524: // price > 10
525: final LiteralConstraint constraint2 = new LiteralConstraint(
526: priceExtractor, integerGreater, field10);
527:
528: // type == 'cheddar' && price > 10
529: final AndConstraint and1 = new AndConstraint();
530: and1.addAlphaConstraint(constraint1);
531: and1.addAlphaConstraint(constraint2);
532:
533: final FieldValue stiltonField = FieldFactory
534: .getFieldValue("stilton");
535: // type == 'stilton'
536: final LiteralConstraint constraint3 = new LiteralConstraint(
537: typeExtractor, stringEqual, stiltonField);
538:
539: final Evaluator integerLess = ValueType.INTEGER_TYPE
540: .getEvaluator(Operator.LESS);
541:
542: // price < 10
543: final LiteralConstraint constraint4 = new LiteralConstraint(
544: priceExtractor, integerLess, field10);
545:
546: // type == 'stilton' && price < 10
547: final AndConstraint and2 = new AndConstraint();
548: and2.addAlphaConstraint(constraint3);
549: and2.addAlphaConstraint(constraint4);
550:
551: // ( type == 'cheddar' && price > 10 ) || ( type == 'stilton' && price < 10 )
552: final OrConstraint constraint = new OrConstraint();
553: constraint.addAlphaConstraint(and1);
554: constraint.addAlphaConstraint(and2);
555:
556: final Cheese cheddar = new Cheese("cheddar", 15);
557:
558: final InternalFactHandle cheddarHandle = (InternalFactHandle) workingMemory
559: .insert(cheddar);
560:
561: // check constraint
562: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
563: workingMemory));
564:
565: cheddar.setPrice(5);
566: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
567: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
568: workingMemory));
569:
570: cheddar.setType("stilton");
571: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
572: assertTrue(constraint.isAllowed(cheddarHandle.getObject(),
573: workingMemory));
574:
575: cheddar.setPrice(15);
576: ((ShadowProxy) cheddarHandle.getObject()).updateProxy();
577: assertFalse(constraint.isAllowed(cheddarHandle.getObject(),
578: workingMemory));
579: }
580:
581: }
|