001: package org.drools.reteoo;
002:
003: import java.io.Serializable;
004: import java.util.ArrayList;
005: import java.util.List;
006:
007: import org.drools.base.ValueType;
008: import org.drools.base.evaluators.Operator;
009: import org.drools.common.InternalFactHandle;
010: import org.drools.common.InternalWorkingMemory;
011: import org.drools.rule.LiteralConstraint;
012: import org.drools.spi.AlphaNodeFieldConstraint;
013: import org.drools.spi.Evaluator;
014: import org.drools.spi.Extractor;
015: import org.drools.spi.FieldExtractor;
016: import org.drools.spi.FieldValue;
017: import org.drools.spi.PropagationContext;
018: import org.drools.util.Iterator;
019: import org.drools.util.LinkedList;
020: import org.drools.util.LinkedListNode;
021: import org.drools.util.ObjectHashMap;
022: import org.drools.util.ObjectHashMap.ObjectEntry;
023:
024: public class CompositeObjectSinkAdapter implements ObjectSinkPropagator {
025:
026: // /** You can override this property via a system property (eg -Ddrools.hashThreshold=4) */
027: // public static final String HASH_THRESHOLD_SYSTEM_PROPERTY = "drools.hashThreshold";
028: //
029: // /** The threshold for when hashing kicks in */
030: // public static final int THRESHOLD_TO_HASH = Integer.parseInt( System.getProperty( HASH_THRESHOLD_SYSTEM_PROPERTY,
031: // "3" ) );
032:
033: private static final long serialVersionUID = 400L;
034: ObjectSinkNodeList otherSinks;
035: ObjectSinkNodeList hashableSinks;
036:
037: LinkedList hashedFieldIndexes;
038:
039: ObjectHashMap hashedSinkMap;
040:
041: private HashKey hashKey;
042:
043: private final int alphaNodeHashingThreshold;
044:
045: public CompositeObjectSinkAdapter() {
046: this (3);
047: }
048:
049: public CompositeObjectSinkAdapter(
050: final int alphaNodeHashingThreshold) {
051: this .hashKey = new HashKey();
052: this .alphaNodeHashingThreshold = alphaNodeHashingThreshold;
053: }
054:
055: public void addObjectSink(final ObjectSink sink) {
056: if (sink instanceof AlphaNode) {
057: final AlphaNode alphaNode = (AlphaNode) sink;
058: final AlphaNodeFieldConstraint fieldConstraint = alphaNode
059: .getConstraint();
060:
061: if (fieldConstraint instanceof LiteralConstraint) {
062: final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
063: final Evaluator evaluator = literalConstraint
064: .getEvaluator();
065:
066: if (evaluator.getOperator() == Operator.EQUAL) {
067: final int index = literalConstraint
068: .getFieldExtractor().getIndex();
069: final FieldIndex fieldIndex = registerFieldIndex(
070: index, literalConstraint
071: .getFieldExtractor());
072:
073: if (fieldIndex.getCount() >= this .alphaNodeHashingThreshold) {
074: if (!fieldIndex.isHashed()) {
075: hashSinks(fieldIndex);
076: }
077: final FieldValue value = literalConstraint
078: .getField();
079: // no need to check, we know the sink does not exist
080: this .hashedSinkMap.put(
081: new HashKey(index, value), sink, false);
082: } else {
083: if (this .hashableSinks == null) {
084: this .hashableSinks = new ObjectSinkNodeList();
085: }
086: this .hashableSinks.add((ObjectSinkNode) sink);
087: }
088: return;
089: }
090:
091: }
092: }
093:
094: if (this .otherSinks == null) {
095: this .otherSinks = new ObjectSinkNodeList();
096: }
097:
098: this .otherSinks.add((ObjectSinkNode) sink);
099: }
100:
101: public void removeObjectSink(final ObjectSink sink) {
102: if (sink instanceof AlphaNode) {
103: final AlphaNode alphaNode = (AlphaNode) sink;
104: final AlphaNodeFieldConstraint fieldConstraint = alphaNode
105: .getConstraint();
106:
107: if (fieldConstraint instanceof LiteralConstraint) {
108: final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
109: final Evaluator evaluator = literalConstraint
110: .getEvaluator();
111: final FieldValue value = literalConstraint.getField();
112:
113: if (evaluator.getOperator() == Operator.EQUAL) {
114: final int index = literalConstraint
115: .getFieldExtractor().getIndex();
116: final FieldIndex fieldIndex = unregisterFieldIndex(index);
117:
118: if (fieldIndex.isHashed()) {
119: this .hashKey.setValue(index, value);
120: this .hashedSinkMap.remove(this .hashKey);
121: if (fieldIndex.getCount() <= this .alphaNodeHashingThreshold - 1) {
122: // we have less than three so unhash
123: unHashSinks(fieldIndex);
124: }
125: } else {
126: this .hashableSinks
127: .remove((ObjectSinkNode) sink);
128: }
129:
130: if (this .hashableSinks != null
131: && this .hashableSinks.isEmpty()) {
132: this .hashableSinks = null;
133: }
134:
135: return;
136: }
137: }
138: }
139:
140: this .otherSinks.remove((ObjectSinkNode) sink);
141:
142: if (this .otherSinks.isEmpty()) {
143: this .otherSinks = null;
144: }
145: }
146:
147: public void hashSinks(final FieldIndex fieldIndex) {
148: final int index = fieldIndex.getIndex();
149:
150: final List list = new ArrayList();
151:
152: if (this .hashedSinkMap == null) {
153: this .hashedSinkMap = new ObjectHashMap();
154: }
155:
156: for (ObjectSinkNode sink = this .hashableSinks.getFirst(); sink != null; sink = sink
157: .getNextObjectSinkNode()) {
158: final AlphaNode alphaNode = (AlphaNode) sink;
159: final AlphaNodeFieldConstraint fieldConstraint = alphaNode
160: .getConstraint();
161: final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
162: final Evaluator evaluator = literalConstraint
163: .getEvaluator();
164: if (evaluator.getOperator() == Operator.EQUAL
165: && index == literalConstraint.getFieldExtractor()
166: .getIndex()) {
167: final FieldValue value = literalConstraint.getField();
168: list.add(sink);
169: this .hashedSinkMap.put(new HashKey(index, value), sink);
170: }
171: }
172:
173: for (final java.util.Iterator it = list.iterator(); it
174: .hasNext();) {
175: final ObjectSinkNode sink = (ObjectSinkNode) it.next();
176: this .hashableSinks.remove(sink);
177: }
178:
179: if (this .hashableSinks.isEmpty()) {
180: this .hashableSinks = null;
181: }
182:
183: fieldIndex.setHashed(true);
184: }
185:
186: public void unHashSinks(final FieldIndex fieldIndex) {
187: final int index = fieldIndex.getIndex();
188:
189: final List sinks = new ArrayList();
190:
191: //iterate twice as custom iterator is immutable
192: final Iterator mapIt = this .hashedSinkMap.newIterator();
193: for (ObjectHashMap.ObjectEntry e = (ObjectHashMap.ObjectEntry) mapIt
194: .next(); e != null;) {
195:
196: sinks.add(e.getValue());
197: e = (ObjectHashMap.ObjectEntry) mapIt.next();
198: }
199:
200: for (final java.util.Iterator iter = sinks.iterator(); iter
201: .hasNext();) {
202: final AlphaNode sink = (AlphaNode) iter.next();
203: final AlphaNode alphaNode = sink;
204: final AlphaNodeFieldConstraint fieldConstraint = alphaNode
205: .getConstraint();
206: final LiteralConstraint literalConstraint = (LiteralConstraint) fieldConstraint;
207: final Evaluator evaluator = literalConstraint
208: .getEvaluator();
209: if (evaluator.getOperator() == Operator.EQUAL
210: && index == literalConstraint.getFieldExtractor()
211: .getIndex()) {
212: final FieldValue value = literalConstraint.getField();
213: if (this .hashableSinks == null) {
214: this .hashableSinks = new ObjectSinkNodeList();
215: }
216: this .hashableSinks.add(sink);
217: this .hashedSinkMap.remove(new HashKey(index, value));
218: }
219: ;
220: }
221:
222: if (this .hashedSinkMap.isEmpty()) {
223: this .hashedSinkMap = null;
224: }
225:
226: fieldIndex.setHashed(false);
227: }
228:
229: /**
230: * Returns a FieldIndex which Keeps a count on how many times a particular field is used with an equality check in the sinks.
231: * @param index
232: * @param fieldExtractor
233: * @return
234: */
235: private FieldIndex registerFieldIndex(final int index,
236: final FieldExtractor fieldExtractor) {
237: FieldIndex fieldIndex = null;
238:
239: // is linkedlist null, if so create and add
240: if (this .hashedFieldIndexes == null) {
241: this .hashedFieldIndexes = new LinkedList();
242: fieldIndex = new FieldIndex(index, fieldExtractor);
243: this .hashedFieldIndexes.add(fieldIndex);
244: }
245:
246: // still null, so see if it already exists
247: if (fieldIndex == null) {
248: fieldIndex = findFieldIndex(index);
249: }
250:
251: // doesn't exist so create it
252: if (fieldIndex == null) {
253: fieldIndex = new FieldIndex(index, fieldExtractor);
254: this .hashedFieldIndexes.add(fieldIndex);
255: }
256:
257: fieldIndex.increaseCounter();
258:
259: return fieldIndex;
260: }
261:
262: private FieldIndex unregisterFieldIndex(final int index) {
263: final FieldIndex fieldIndex = findFieldIndex(index);
264: fieldIndex.decreaseCounter();
265:
266: // if the fieldcount is 0 then remove it from the linkedlist
267: if (fieldIndex.getCount() == 0) {
268: this .hashedFieldIndexes.remove(fieldIndex);
269:
270: // if the linkedlist is empty then null it
271: if (this .hashedFieldIndexes.isEmpty()) {
272: this .hashedFieldIndexes = null;
273: }
274: }
275:
276: return fieldIndex;
277: }
278:
279: private FieldIndex findFieldIndex(final int index) {
280: for (FieldIndex node = (FieldIndex) this .hashedFieldIndexes
281: .getFirst(); node != null; node = (FieldIndex) node
282: .getNext()) {
283: if (node.getIndex() == index) {
284: return node;
285: }
286: }
287:
288: return null;
289: }
290:
291: public void propagateAssertObject(final InternalFactHandle handle,
292: final PropagationContext context,
293: final InternalWorkingMemory workingMemory) {
294: final Object object = handle.getObject();
295:
296: // Iterates t he FieldIndex collection, which tells you if particularly field is hashed or not
297: // if the field is hashed then it builds the hashkey to return the correct sink for the current objects slot's
298: // value, one object may have multiple fields indexed.
299: if (this .hashedFieldIndexes != null) {
300: // Iterate the FieldIndexes to see if any are hashed
301: for (FieldIndex fieldIndex = (FieldIndex) this .hashedFieldIndexes
302: .getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex
303: .getNext()) {
304: if (!fieldIndex.isHashed()) {
305: continue;
306: }
307: // this field is hashed so set the existing hashKey and see if there is a sink for it
308: final int index = fieldIndex.getIndex();
309: final FieldExtractor extractor = fieldIndex
310: .getFieldExtactor();
311: this .hashKey.setValue(index, object, extractor);
312: final ObjectSink sink = (ObjectSink) this .hashedSinkMap
313: .get(this .hashKey);
314: if (sink != null) {
315: // The sink exists so propagate
316: sink.assertObject(handle, context, workingMemory);
317: }
318: }
319: }
320:
321: // propagate unhashed
322: if (this .hashableSinks != null) {
323: for (ObjectSinkNode sink = this .hashableSinks.getFirst(); sink != null; sink = sink
324: .getNextObjectSinkNode()) {
325: sink.assertObject(handle, context, workingMemory);
326: }
327: }
328:
329: if (this .otherSinks != null) {
330: // propagate others
331: for (ObjectSinkNode sink = this .otherSinks.getFirst(); sink != null; sink = sink
332: .getNextObjectSinkNode()) {
333: sink.assertObject(handle, context, workingMemory);
334: }
335: }
336:
337: }
338:
339: public void propagateRetractObject(final InternalFactHandle handle,
340: final PropagationContext context,
341: final InternalWorkingMemory workingMemory,
342: final boolean useHash) {
343: if (this .hashedFieldIndexes != null) {
344: if (useHash && this .hashedSinkMap != null) {
345: final Object object = handle.getObject();
346: // Iterate the FieldIndexes to see if any are hashed
347: for (FieldIndex fieldIndex = (FieldIndex) this .hashedFieldIndexes
348: .getFirst(); fieldIndex != null; fieldIndex = (FieldIndex) fieldIndex
349: .getNext()) {
350: // this field is hashed so set the existing hashKey and see if there is a sink for it
351: if (!fieldIndex.isHashed()) {
352: continue;
353: }
354:
355: final int index = fieldIndex.getIndex();
356: final FieldExtractor extractor = fieldIndex
357: .getFieldExtactor();
358: this .hashKey.setValue(index, object, extractor);
359: final ObjectSink sink = (ObjectSink) this .hashedSinkMap
360: .get(this .hashKey);
361: if (sink != null) {
362: // The sink exists so propagate
363: sink.retractObject(handle, context,
364: workingMemory);
365: }
366: }
367: } else if (this .hashedSinkMap != null) {
368: final Iterator it = this .hashedSinkMap.newIterator();
369: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
370: .next()) {
371: final ObjectSink sink = (ObjectSink) entry
372: .getValue();
373: sink.retractObject(handle, context, workingMemory);
374: }
375: }
376: }
377:
378: if (this .hashableSinks != null) {
379: // we can't retrieve hashed sinks, as the field value might have changed, so we have to iterate and propagate to all hashed sinks
380: for (ObjectSinkNode sink = this .hashableSinks.getFirst(); sink != null; sink = sink
381: .getNextObjectSinkNode()) {
382: sink.retractObject(handle, context, workingMemory);
383: }
384: }
385:
386: if (this .otherSinks != null) {
387: // propagate others
388: for (ObjectSinkNode sink = this .otherSinks.getFirst(); sink != null; sink = sink
389: .getNextObjectSinkNode()) {
390: sink.retractObject(handle, context, workingMemory);
391: }
392: }
393: }
394:
395: public ObjectSink[] getSinks() {
396: final List list = new ArrayList();
397:
398: if (this .otherSinks != null) {
399: for (ObjectSinkNode sink = this .otherSinks.getFirst(); sink != null; sink = sink
400: .getNextObjectSinkNode()) {
401: list.add(sink);
402: }
403: }
404:
405: if (this .hashableSinks != null) {
406: for (ObjectSinkNode sink = this .hashableSinks.getFirst(); sink != null; sink = sink
407: .getNextObjectSinkNode()) {
408: list.add(sink);
409: }
410: }
411:
412: if (this .hashedSinkMap != null) {
413: final Iterator it = this .hashedSinkMap.newIterator();
414: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
415: .next()) {
416: final ObjectSink sink = (ObjectSink) entry.getValue();
417: list.add(sink);
418: }
419: }
420:
421: return (ObjectSink[]) list.toArray(new ObjectSink[list.size()]);
422: }
423:
424: public int size() {
425: int size = 0;
426: size += ((this .otherSinks != null) ? this .otherSinks.size() : 0);
427: size += ((this .hashableSinks != null) ? this .hashableSinks
428: .size() : 0);
429: size += ((this .hashedSinkMap != null) ? this .hashedSinkMap
430: .size() : 0);
431: return size;
432: }
433:
434: public static class HashKey implements Serializable {
435: private static final long serialVersionUID = 400L;
436:
437: private static final byte OBJECT = 1;
438: private static final byte LONG = 2;
439: private static final byte DOUBLE = 3;
440: private static final byte BOOL = 4;
441:
442: private int index;
443:
444: private byte type;
445: private Object ovalue;
446: private long lvalue;
447: private boolean bvalue;
448: private double dvalue;
449:
450: private int hashCode;
451:
452: public HashKey() {
453: }
454:
455: public HashKey(final int index, final FieldValue value) {
456: this .setValue(index, value);
457: }
458:
459: public HashKey(final int index, final Object value,
460: final Extractor extractor) {
461: this .setValue(index, value, extractor);
462: }
463:
464: public int getIndex() {
465: return this .index;
466: }
467:
468: public void setValue(final int index, final Object value,
469: final Extractor extractor) {
470: this .index = index;
471: final ValueType vtype = extractor.getValueType();
472: if (vtype.isBoolean()) {
473: this .bvalue = extractor.getBooleanValue(null, value);
474: this .type = BOOL;
475: this .setHashCode(this .bvalue ? 1231 : 1237);
476: } else if (vtype.isIntegerNumber()) {
477: this .lvalue = extractor.getLongValue(null, value);
478: this .type = LONG;
479: this
480: .setHashCode((int) (this .lvalue ^ (this .lvalue >>> 32)));
481: } else if (vtype.isFloatNumber()) {
482: this .dvalue = extractor.getDoubleValue(null, value);
483: this .type = DOUBLE;
484: final long temp = Double.doubleToLongBits(this .dvalue);
485: this .setHashCode((int) (temp ^ (temp >>> 32)));
486: } else {
487: this .ovalue = extractor.getValue(null, value);
488: this .type = OBJECT;
489: this .setHashCode(this .ovalue != null ? this .ovalue
490: .hashCode() : 0);
491: }
492: }
493:
494: public void setValue(final int index, final FieldValue value) {
495: this .index = index;
496: if (value.isBooleanField()) {
497: this .bvalue = value.getBooleanValue();
498: this .type = BOOL;
499: this .setHashCode(this .bvalue ? 1231 : 1237);
500: } else if (value.isIntegerNumberField()) {
501: this .lvalue = value.getLongValue();
502: this .type = LONG;
503: this
504: .setHashCode((int) (this .lvalue ^ (this .lvalue >>> 32)));
505: } else if (value.isFloatNumberField()) {
506: this .dvalue = value.getDoubleValue();
507: this .type = DOUBLE;
508: final long temp = Double.doubleToLongBits(this .dvalue);
509: this .setHashCode((int) (temp ^ (temp >>> 32)));
510: } else {
511: this .ovalue = value.getValue();
512: this .type = OBJECT;
513: this .setHashCode(this .ovalue != null ? this .ovalue
514: .hashCode() : 0);
515: }
516: }
517:
518: private void setHashCode(final int hashSeed) {
519: final int PRIME = 31;
520: int result = 1;
521: result = PRIME * result + hashSeed;
522: result = PRIME * result + this .index;
523: this .hashCode = result;
524: }
525:
526: public boolean getBooleanValue() {
527: switch (this .type) {
528: case BOOL:
529: return this .bvalue;
530: case OBJECT:
531: if (this .ovalue == null) {
532: return false;
533: } else if (this .ovalue instanceof Boolean) {
534: return ((Boolean) this .ovalue).booleanValue();
535: } else if (this .ovalue instanceof String) {
536: return Boolean.valueOf((String) this .ovalue)
537: .booleanValue();
538: } else {
539: throw new ClassCastException("Can't convert "
540: + this .ovalue.getClass()
541: + " to a boolean value.");
542: }
543: case LONG:
544: throw new ClassCastException(
545: "Can't convert long to a boolean value.");
546: case DOUBLE:
547: throw new ClassCastException(
548: "Can't convert double to a boolean value.");
549:
550: }
551: return false;
552: }
553:
554: public long getLongValue() {
555: switch (this .type) {
556: case BOOL:
557: return this .bvalue ? 1 : 0;
558: case OBJECT:
559: if (this .ovalue == null) {
560: return 0;
561: } else if (this .ovalue instanceof Number) {
562: return ((Number) this .ovalue).longValue();
563: } else if (this .ovalue instanceof String) {
564: return Long.parseLong((String) this .ovalue);
565: } else {
566: throw new ClassCastException("Can't convert "
567: + this .ovalue.getClass()
568: + " to a long value.");
569: }
570: case LONG:
571: return this .lvalue;
572: case DOUBLE:
573: return (long) this .dvalue;
574:
575: }
576: return 0;
577: }
578:
579: public double getDoubleValue() {
580: switch (this .type) {
581: case BOOL:
582: return this .bvalue ? 1 : 0;
583: case OBJECT:
584: if (this .ovalue == null) {
585: return 0;
586: } else if (this .ovalue instanceof Number) {
587: return ((Number) this .ovalue).doubleValue();
588: } else if (this .ovalue instanceof String) {
589: return Double.parseDouble((String) this .ovalue);
590: } else {
591: throw new ClassCastException("Can't convert "
592: + this .ovalue.getClass()
593: + " to a double value.");
594: }
595: case LONG:
596: return this .lvalue;
597: case DOUBLE:
598: return this .dvalue;
599: }
600: return 0;
601: }
602:
603: public Object getObjectValue() {
604: switch (this .type) {
605: case BOOL:
606: return this .bvalue ? Boolean.TRUE : Boolean.FALSE;
607: case OBJECT:
608: return this .ovalue;
609: case LONG:
610: return new Long(this .lvalue);
611: case DOUBLE:
612: return new Double(this .dvalue);
613: }
614: return null;
615: }
616:
617: public int hashCode() {
618: return this .hashCode;
619: }
620:
621: public boolean equals(final Object object) {
622: final HashKey other = (HashKey) object;
623:
624: switch (this .type) {
625: case BOOL:
626: return (this .index == other.index)
627: && (this .bvalue == other.getBooleanValue());
628: case LONG:
629: return (this .index == other.index)
630: && (this .lvalue == other.getLongValue());
631: case DOUBLE:
632: return (this .index == other.index)
633: && (this .dvalue == other.getDoubleValue());
634: case OBJECT:
635: final Object otherValue = other.getObjectValue();
636: if ((this .ovalue != null)
637: && (this .ovalue instanceof Number)
638: && (otherValue instanceof Number)) {
639: return (this .index == other.index)
640: && (((Number) this .ovalue).doubleValue() == ((Number) otherValue)
641: .doubleValue());
642: }
643: return (this .index == other.index)
644: && (this .ovalue == null ? otherValue == null
645: : this .ovalue.equals(otherValue));
646: }
647: return false;
648: }
649:
650: }
651:
652: public static class FieldIndex implements LinkedListNode {
653: private static final long serialVersionUID = 400L;
654: private final int index;
655: private FieldExtractor fieldExtactor;
656:
657: private int count;
658:
659: private boolean hashed;
660:
661: private LinkedListNode previous;
662: private LinkedListNode next;
663:
664: public FieldIndex(final int index,
665: final FieldExtractor fieldExtractor) {
666: this .index = index;
667: this .fieldExtactor = fieldExtractor;
668: }
669:
670: public FieldExtractor getFieldExtractor() {
671: return this .fieldExtactor;
672: }
673:
674: public int getIndex() {
675: return this .index;
676: }
677:
678: public int getCount() {
679: return this .count;
680: }
681:
682: public FieldExtractor getFieldExtactor() {
683: return this .fieldExtactor;
684: }
685:
686: public boolean isHashed() {
687: return this .hashed;
688: }
689:
690: public void setHashed(final boolean hashed) {
691: this .hashed = hashed;
692: }
693:
694: public void increaseCounter() {
695: this .count++;
696: }
697:
698: public void decreaseCounter() {
699: this .count--;
700: }
701:
702: public LinkedListNode getNext() {
703: return this .next;
704: }
705:
706: public LinkedListNode getPrevious() {
707: return this .previous;
708: }
709:
710: public void setNext(final LinkedListNode next) {
711: this .next = next;
712:
713: }
714:
715: public void setPrevious(final LinkedListNode previous) {
716: this.previous = previous;
717: }
718: }
719: }
|