001: /**
002: *
003: */package org.drools.util;
004:
005: import org.drools.common.InternalFactHandle;
006: import org.drools.reteoo.FactHandleMemory;
007: import org.drools.reteoo.ReteTuple;
008: import org.drools.util.TupleIndexHashTable.FieldIndexEntry;
009:
010: public class FactHandleIndexHashTable extends AbstractHashTable
011: implements FactHandleMemory {
012:
013: private static final long serialVersionUID = 400L;
014:
015: public static final int PRIME = 31;
016:
017: private int startResult;
018:
019: private FieldIndexHashTableIterator tupleValueIterator;
020:
021: private int factSize;
022:
023: private Index index;
024:
025: public FactHandleIndexHashTable(final FieldIndex[] index) {
026: this (16, 0.75f, index);
027: }
028:
029: public FactHandleIndexHashTable(final int capacity,
030: final float loadFactor, final FieldIndex[] index) {
031: super (capacity, loadFactor);
032:
033: this .startResult = FactHandleIndexHashTable.PRIME;
034: for (int i = 0, length = index.length; i < length; i++) {
035: this .startResult = FactHandleIndexHashTable.PRIME
036: * this .startResult
037: + index[i].getExtractor().getIndex();
038: }
039:
040: switch (index.length) {
041: case 0:
042: throw new IllegalArgumentException(
043: "FieldIndexHashTable cannot use an index[] of length 0");
044: case 1:
045: this .index = new SingleIndex(index, this .startResult);
046: break;
047: case 2:
048: this .index = new DoubleCompositeIndex(index,
049: this .startResult);
050: break;
051: case 3:
052: this .index = new TripleCompositeIndex(index,
053: this .startResult);
054: break;
055: default:
056: throw new IllegalArgumentException(
057: "FieldIndexHashTable cannot use an index[] of length great than 3");
058: }
059: }
060:
061: public Iterator iterator() {
062: throw new UnsupportedOperationException(
063: "FieldIndexHashTable does not support iterator()");
064: }
065:
066: public Iterator iterator(final ReteTuple tuple) {
067: if (this .tupleValueIterator == null) {
068: this .tupleValueIterator = new FieldIndexHashTableIterator();
069: }
070: final FieldIndexEntry entry = get(tuple);
071: this .tupleValueIterator.reset((entry != null) ? entry.first
072: : null);
073: return this .tupleValueIterator;
074: }
075:
076: public boolean isIndexed() {
077: return true;
078: }
079:
080: public Index getIndex() {
081: return this .index;
082: }
083:
084: public Entry getBucket(final Object object) {
085: final int hashCode = this .index.hashCodeOf(object);
086: final int index = indexOf(hashCode, this .table.length);
087:
088: return this .table[index];
089: }
090:
091: /**
092: * Fast re-usable iterator
093: *
094: */
095: public static class FieldIndexHashTableIterator implements Iterator {
096: private Entry entry;
097:
098: public FieldIndexHashTableIterator() {
099:
100: }
101:
102: /* (non-Javadoc)
103: * @see org.drools.util.Iterator#next()
104: */
105: public Object next() {
106: final Entry current = this .entry;
107: this .entry = (this .entry != null) ? this .entry.getNext()
108: : null;
109: return current;
110: }
111:
112: /* (non-Javadoc)
113: * @see org.drools.util.Iterator#reset()
114: */
115: public void reset(final Entry entry) {
116: this .entry = entry;
117: }
118: }
119:
120: public Entry[] toArray() {
121: Entry[] result = new Entry[this .factSize];
122: int index = 0;
123: for (int i = 0; i < this .table.length; i++) {
124: FieldIndexEntry fieldIndexEntry = (FieldIndexEntry) this .table[i];
125: while (fieldIndexEntry != null) {
126: Entry entry = fieldIndexEntry.getFirst();
127: while (entry != null) {
128: result[index++] = entry;
129: entry = entry.getNext();
130: }
131: fieldIndexEntry = (FieldIndexEntry) fieldIndexEntry
132: .getNext();
133: }
134: }
135: return result;
136: }
137:
138: public boolean add(final InternalFactHandle handle) {
139: final FieldIndexEntry entry = getOrCreate(handle.getObject());
140: entry.add(handle);
141: this .factSize++;
142: return true;
143: }
144:
145: public boolean add(final InternalFactHandle handle,
146: final boolean checkExists) {
147: throw new UnsupportedOperationException(
148: "FieldIndexHashTable does not support add(InternalFactHandle handle, boolean checkExists)");
149: }
150:
151: public boolean remove(final InternalFactHandle handle) {
152: final Object object = handle.getObject();
153: //this.index.setCachedValue( object );
154: final int hashCode = this .index.hashCodeOf(object);
155:
156: final int index = indexOf(hashCode, this .table.length);
157:
158: // search the table for the Entry, we need to track previous and next, so if the
159: // Entry is empty after its had the FactEntry removed, we must remove it from the table
160: FieldIndexEntry previous = (FieldIndexEntry) this .table[index];
161: FieldIndexEntry current = previous;
162: while (current != null) {
163: final FieldIndexEntry next = (FieldIndexEntry) current.next;
164: if (current.matches(object, hashCode)) {
165: current.remove(handle);
166: this .factSize--;
167: // If the FactEntryIndex is empty, then remove it from the hash table
168: if (current.first == null) {
169: if (previous == current) {
170: this .table[index] = next;
171: } else {
172: previous.next = next;
173: }
174: current.next = null;
175: this .size--;
176: }
177: return true;
178: }
179: previous = current;
180: current = next;
181: }
182: return false;
183: }
184:
185: public boolean contains(final InternalFactHandle handle) {
186: final Object object = handle.getObject();
187: //this.index.setCachedValue( object );
188:
189: final int hashCode = this .index.hashCodeOf(object);
190:
191: final int index = indexOf(hashCode, this .table.length);
192:
193: FieldIndexEntry current = (FieldIndexEntry) this .table[index];
194: while (current != null) {
195: if (current.matches(object, hashCode)) {
196: return true;
197: }
198: current = (FieldIndexEntry) current.next;
199: }
200: return false;
201: }
202:
203: public FieldIndexEntry get(final ReteTuple tuple) {
204: //this.index.setCachedValue( tuple );
205:
206: final int hashCode = this .index.hashCodeOf(tuple);
207:
208: final int index = indexOf(hashCode, this .table.length);
209: FieldIndexEntry entry = (FieldIndexEntry) this .table[index];
210:
211: while (entry != null) {
212: if (entry.matches(tuple, hashCode)) {
213: return entry;
214: }
215: entry = (FieldIndexEntry) entry.getNext();
216: }
217:
218: return entry;
219: }
220:
221: /**
222: * We use this method to aviod to table lookups for the same hashcode; which is what we would have to do if we did
223: * a get and then a create if the value is null.
224: *
225: * @param value
226: * @return
227: */
228: private FieldIndexEntry getOrCreate(final Object object) {
229: //this.index.setCachedValue( object );
230:
231: final int hashCode = this .index.hashCodeOf(object);
232:
233: final int index = indexOf(hashCode, this .table.length);
234: FieldIndexEntry entry = (FieldIndexEntry) this .table[index];
235:
236: while (entry != null) {
237: if (entry.matches(object, hashCode)) {
238: return entry;
239: }
240: entry = (FieldIndexEntry) entry.next;
241: }
242:
243: if (entry == null) {
244: entry = new FieldIndexEntry(this .index, hashCode);
245: entry.next = this .table[index];
246: this .table[index] = entry;
247:
248: if (this .size++ >= this .threshold) {
249: resize(2 * this .table.length);
250: }
251: }
252: return entry;
253: }
254:
255: public int size() {
256: return this .factSize;
257: }
258:
259: public static class FieldIndexEntry implements Entry {
260:
261: private static final long serialVersionUID = 400L;
262: private Entry next;
263: private FactEntryImpl first;
264: private final int hashCode;
265: private Index index;
266:
267: public FieldIndexEntry(final Index index, final int hashCode) {
268: this .index = index;
269: this .hashCode = hashCode;
270: }
271:
272: public Entry getNext() {
273: return this .next;
274: }
275:
276: public void setNext(final Entry next) {
277: this .next = next;
278: }
279:
280: public FactEntryImpl getFirst() {
281: return this .first;
282: }
283:
284: public void add(final InternalFactHandle handle) {
285: final FactEntryImpl entry = new FactEntryImpl(handle);
286: entry.next = this .first;
287: this .first = entry;
288: }
289:
290: public FactEntryImpl get(final InternalFactHandle handle) {
291: final long id = handle.getId();
292: FactEntryImpl current = this .first;
293: while (current != null) {
294: if (current.handle.getId() == id) {
295: return current;
296: }
297: current = (FactEntryImpl) current.next;
298: }
299: return null;
300: }
301:
302: public FactEntryImpl remove(final InternalFactHandle handle) {
303: final long id = handle.getId();
304:
305: FactEntryImpl previous = this .first;
306: FactEntryImpl current = previous;
307: while (current != null) {
308: final FactEntryImpl next = (FactEntryImpl) current.next;
309: if (current.handle.getId() == id) {
310: if (this .first == current) {
311: this .first = next;
312: } else {
313: previous.next = next;
314: }
315: current.next = null;
316: return current;
317: }
318: previous = current;
319: current = next;
320: }
321: return current;
322: }
323:
324: // public boolean matches(int otherHashCode) {
325: // return this.hashCode == otherHashCode && this.index.equal( this.first.getFactHandle().getObject() );
326: // }
327:
328: public boolean matches(final Object object,
329: final int objectHashCode) {
330: return this .hashCode == objectHashCode
331: && this .index.equal(this .first.getFactHandle()
332: .getObject(), object);
333: }
334:
335: public boolean matches(final ReteTuple tuple,
336: final int tupleHashCode) {
337: return this .hashCode == tupleHashCode
338: && this .index.equal(this .first.getFactHandle()
339: .getObject(), tuple);
340: }
341:
342: public int hashCode() {
343: return this .hashCode;
344: }
345:
346: public boolean equals(final Object object) {
347: final FieldIndexEntry other = (FieldIndexEntry) object;
348: return this .hashCode == other.hashCode
349: && this .index == other.index;
350: }
351:
352: public String toString() {
353: return "FieldIndexEntry( hashCode=" + this .hashCode
354: + " first=" + this .first + " )";
355: }
356: }
357: }
|