001: package org.drools.util;
002:
003: import java.lang.reflect.Field;
004: import java.util.ArrayList;
005: import java.util.List;
006:
007: import junit.framework.TestCase;
008:
009: import org.drools.Cheese;
010: import org.drools.base.ClassFieldExtractor;
011: import org.drools.base.ClassFieldExtractorCache;
012: import org.drools.base.ClassObjectType;
013: import org.drools.base.evaluators.ObjectFactory;
014: import org.drools.base.evaluators.Operator;
015: import org.drools.base.evaluators.StringFactory;
016: import org.drools.common.DefaultFactHandle;
017: import org.drools.common.InternalFactHandle;
018: import org.drools.reteoo.ReteTuple;
019: import org.drools.rule.Pattern;
020: import org.drools.rule.Declaration;
021: import org.drools.spi.FieldExtractor;
022: import org.drools.util.AbstractHashTable.FactEntryImpl;
023: import org.drools.util.AbstractHashTable.FieldIndex;
024: import org.drools.util.FactHandleIndexHashTable.FieldIndexEntry;
025: import org.drools.util.ObjectHashMap.ObjectEntry;
026:
027: public class FieldIndexHashTableTest extends TestCase {
028:
029: public void testSingleEntry() throws Exception {
030: final FieldExtractor extractor = ClassFieldExtractorCache
031: .getExtractor(Cheese.class, "type", getClass()
032: .getClassLoader());
033:
034: final Pattern pattern = new Pattern(0, new ClassObjectType(
035: Cheese.class));
036:
037: final Declaration declaration = new Declaration("typeOfCheese",
038: extractor, pattern);
039:
040: final FieldIndex fieldIndex = new FieldIndex(extractor,
041: declaration, StringFactory.getInstance().getEvaluator(
042: Operator.EQUAL));
043:
044: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
045: new FieldIndex[] { fieldIndex });
046:
047: final Cheese cheddar = new Cheese("cheddar", 10);
048: final InternalFactHandle cheddarHandle1 = new DefaultFactHandle(
049: 0, cheddar);
050:
051: assertEquals(0, map.size());
052: assertNull(map.get(new ReteTuple(cheddarHandle1)));
053:
054: final Cheese stilton1 = new Cheese("stilton", 35);
055: final InternalFactHandle stiltonHandle1 = new DefaultFactHandle(
056: 1, stilton1);
057: map.add(stiltonHandle1);
058:
059: assertEquals(1, map.size());
060: assertEquals(1, tablePopulationSize(map));
061:
062: final Cheese stilton2 = new Cheese("stilton", 80);
063: final InternalFactHandle stiltonHandle2 = new DefaultFactHandle(
064: 2, stilton2);
065:
066: final FieldIndexEntry stiltonEntry = map.get(new ReteTuple(
067: stiltonHandle2));
068: assertSame(stiltonHandle1, stiltonEntry.getFirst()
069: .getFactHandle());
070: assertNull(stiltonEntry.getFirst().getNext());
071: }
072:
073: public void testTwoDifferentEntries() throws Exception {
074: final FieldExtractor extractor = ClassFieldExtractorCache
075: .getExtractor(Cheese.class, "type", getClass()
076: .getClassLoader());
077:
078: final Pattern pattern = new Pattern(0, new ClassObjectType(
079: Cheese.class));
080:
081: final Declaration declaration = new Declaration("typeOfCheese",
082: extractor, pattern);
083:
084: final FieldIndex fieldIndex = new FieldIndex(extractor,
085: declaration, StringFactory.getInstance().getEvaluator(
086: Operator.EQUAL));
087:
088: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
089: new FieldIndex[] { fieldIndex });
090:
091: assertEquals(0, map.size());
092:
093: final Cheese stilton1 = new Cheese("stilton", 35);
094: final InternalFactHandle stiltonHandle1 = new DefaultFactHandle(
095: 1, stilton1);
096: map.add(stiltonHandle1);
097:
098: final Cheese cheddar1 = new Cheese("cheddar", 35);
099: final InternalFactHandle cheddarHandle1 = new DefaultFactHandle(
100: 2, cheddar1);
101: map.add(cheddarHandle1);
102:
103: assertEquals(2, map.size());
104: assertEquals(2, tablePopulationSize(map));
105:
106: final Cheese stilton2 = new Cheese("stilton", 77);
107: final InternalFactHandle stiltonHandle2 = new DefaultFactHandle(
108: 2, stilton2);
109: final FieldIndexEntry stiltonEntry = map.get(new ReteTuple(
110: stiltonHandle2));
111: assertSame(stiltonHandle1, stiltonEntry.getFirst()
112: .getFactHandle());
113: assertNull(stiltonEntry.getFirst().getNext());
114:
115: final Cheese cheddar2 = new Cheese("cheddar", 5);
116: final InternalFactHandle cheddarHandle2 = new DefaultFactHandle(
117: 2, cheddar2);
118: final FieldIndexEntry cheddarEntry = map.get(new ReteTuple(
119: cheddarHandle2));
120: assertSame(cheddarHandle1, cheddarEntry.getFirst()
121: .getFactHandle());
122: assertNull(cheddarEntry.getFirst().getNext());
123: }
124:
125: public void testTwoEqualEntries() throws Exception {
126: final FieldExtractor extractor = ClassFieldExtractorCache
127: .getExtractor(Cheese.class, "type", getClass()
128: .getClassLoader());
129:
130: final Pattern pattern = new Pattern(0, new ClassObjectType(
131: Cheese.class));
132:
133: final Declaration declaration = new Declaration("typeOfCheese",
134: extractor, pattern);
135:
136: final FieldIndex fieldIndex = new FieldIndex(extractor,
137: declaration, StringFactory.getInstance().getEvaluator(
138: Operator.EQUAL));
139:
140: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
141: new FieldIndex[] { fieldIndex });
142:
143: assertEquals(0, map.size());
144:
145: final Cheese stilton1 = new Cheese("stilton", 35);
146: final InternalFactHandle stiltonHandle1 = new DefaultFactHandle(
147: 1, stilton1);
148: map.add(stiltonHandle1);
149:
150: final Cheese cheddar1 = new Cheese("cheddar", 35);
151: final InternalFactHandle cheddarHandle1 = new DefaultFactHandle(
152: 2, cheddar1);
153: map.add(cheddarHandle1);
154:
155: final Cheese stilton2 = new Cheese("stilton", 81);
156: final InternalFactHandle stiltonHandle2 = new DefaultFactHandle(
157: 3, stilton2);
158: map.add(stiltonHandle2);
159:
160: assertEquals(3, map.size());
161: assertEquals(2, tablePopulationSize(map));
162:
163: // Check they are correctly chained to the same FieldIndexEntry
164: final Cheese stilton3 = new Cheese("stilton", 89);
165: final InternalFactHandle stiltonHandle3 = new DefaultFactHandle(
166: 4, stilton2);
167:
168: final FieldIndexEntry stiltonEntry = map.get(new ReteTuple(
169: stiltonHandle3));
170: assertSame(stiltonHandle2, stiltonEntry.getFirst()
171: .getFactHandle());
172: assertSame(stiltonHandle1, ((FactEntryImpl) stiltonEntry
173: .getFirst().getNext()).getFactHandle());
174: }
175:
176: public void testTwoDifferentEntriesSameHashCode() throws Exception {
177: final FieldExtractor extractor = ClassFieldExtractorCache
178: .getExtractor(TestClass.class, "object", getClass()
179: .getClassLoader());
180:
181: final Pattern pattern = new Pattern(0, new ClassObjectType(
182: TestClass.class));
183:
184: final Declaration declaration = new Declaration("theObject",
185: extractor, pattern);
186:
187: final FieldIndex fieldIndex = new FieldIndex(extractor,
188: declaration, ObjectFactory.getInstance().getEvaluator(
189: Operator.EQUAL));
190:
191: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
192: new FieldIndex[] { fieldIndex });
193:
194: final TestClass c1 = new TestClass(0, new TestClass(20,
195: "stilton"));
196:
197: final InternalFactHandle ch1 = new DefaultFactHandle(1, c1);
198:
199: map.add(ch1);
200:
201: final TestClass c2 = new TestClass(0, new TestClass(20,
202: "cheddar"));
203: final InternalFactHandle ch2 = new DefaultFactHandle(2, c2);
204: map.add(ch2);
205:
206: // same hashcode, but different values, so it should result in a size of 2
207: assertEquals(2, map.size());
208:
209: // however both are in the same table bucket
210: assertEquals(1, tablePopulationSize(map));
211:
212: // this table bucket will have two FieldIndexEntries, as they are actually two different values
213: final FieldIndexEntry entry = (FieldIndexEntry) getEntries(map)[0];
214:
215: }
216:
217: public void testRemove() throws Exception {
218: final FieldExtractor extractor = ClassFieldExtractorCache
219: .getExtractor(Cheese.class, "type", getClass()
220: .getClassLoader());
221:
222: final Pattern pattern = new Pattern(0, new ClassObjectType(
223: Cheese.class));
224:
225: final Declaration declaration = new Declaration("typeOfCheese",
226: extractor, pattern);
227:
228: final FieldIndex fieldIndex = new FieldIndex(extractor,
229: declaration, StringFactory.getInstance().getEvaluator(
230: Operator.EQUAL));
231:
232: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
233: new FieldIndex[] { fieldIndex });
234:
235: assertEquals(0, map.size());
236:
237: final Cheese stilton1 = new Cheese("stilton", 35);
238: final InternalFactHandle stiltonHandle1 = new DefaultFactHandle(
239: 1, stilton1);
240: map.add(stiltonHandle1);
241:
242: final Cheese cheddar1 = new Cheese("cheddar", 35);
243: final InternalFactHandle cheddarHandle1 = new DefaultFactHandle(
244: 2, cheddar1);
245: map.add(cheddarHandle1);
246:
247: final Cheese stilton2 = new Cheese("stilton", 81);
248: final InternalFactHandle stiltonHandle2 = new DefaultFactHandle(
249: 3, stilton2);
250: map.add(stiltonHandle2);
251:
252: assertEquals(3, map.size());
253: assertEquals(2, tablePopulationSize(map));
254:
255: // cheddar is in its own buccket, which should be removed once empty. We cannot have
256: // empty FieldIndexEntries in the Map, as they get their value from the first FactEntry.
257: map.remove(cheddarHandle1);
258: assertEquals(2, map.size());
259: assertEquals(1, tablePopulationSize(map));
260:
261: // We remove t he stiltonHandle2, but there is still one more stilton, so size should be the same
262: map.remove(stiltonHandle2);
263: assertEquals(1, map.size());
264: assertEquals(1, tablePopulationSize(map));
265:
266: // No more stiltons, so the table should be empty
267: map.remove(stiltonHandle1);
268: assertEquals(0, map.size());
269: assertEquals(0, tablePopulationSize(map));
270: }
271:
272: public void testResize() throws Exception {
273: final FieldExtractor extractor = ClassFieldExtractorCache
274: .getExtractor(Cheese.class, "type", getClass()
275: .getClassLoader());
276:
277: final Pattern pattern = new Pattern(0, new ClassObjectType(
278: Cheese.class));
279:
280: final Declaration declaration = new Declaration("typeOfCheese",
281: extractor, pattern);
282:
283: final FieldIndex fieldIndex = new FieldIndex(extractor,
284: declaration, StringFactory.getInstance().getEvaluator(
285: Operator.EQUAL));
286:
287: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
288: new FieldIndex[] { fieldIndex });
289:
290: assertEquals(0, map.size());
291:
292: final Cheese stilton1 = new Cheese("stilton", 35);
293: map.add(new DefaultFactHandle(1, stilton1));
294:
295: final Cheese stilton2 = new Cheese("stilton", 81);
296: map.add(new DefaultFactHandle(2, stilton2));
297:
298: final Cheese cheddar1 = new Cheese("cheddar", 35);
299: map.add(new DefaultFactHandle(3, cheddar1));
300:
301: final Cheese cheddar2 = new Cheese("cheddar", 38);
302: map.add(new DefaultFactHandle(4, cheddar2));
303:
304: final Cheese brie = new Cheese("brie", 293);
305: map.add(new DefaultFactHandle(5, brie));
306:
307: final Cheese mozerella = new Cheese("mozerella", 15);
308: map.add(new DefaultFactHandle(6, mozerella));
309:
310: final Cheese dolcelatte = new Cheese("dolcelatte", 284);
311: map.add(new DefaultFactHandle(7, dolcelatte));
312:
313: final Cheese camembert1 = new Cheese("camembert", 924);
314: map.add(new DefaultFactHandle(8, camembert1));
315:
316: final Cheese camembert2 = new Cheese("camembert", 765);
317: map.add(new DefaultFactHandle(9, camembert2));
318:
319: final Cheese redLeicestor = new Cheese("red leicestor", 23);
320: map.add(new DefaultFactHandle(10, redLeicestor));
321:
322: final Cheese wensleydale = new Cheese("wensleydale", 20);
323: map.add(new DefaultFactHandle(11, wensleydale));
324:
325: final Cheese edam = new Cheese("edam", 12);
326: map.add(new DefaultFactHandle(12, edam));
327:
328: final Cheese goude1 = new Cheese("goude", 93);
329: map.add(new DefaultFactHandle(13, goude1));
330:
331: final Cheese goude2 = new Cheese("goude", 88);
332: map.add(new DefaultFactHandle(14, goude2));
333:
334: final Cheese gruyere = new Cheese("gruyere", 82);
335: map.add(new DefaultFactHandle(15, gruyere));
336:
337: final Cheese emmental = new Cheese("emmental", 98);
338: map.add(new DefaultFactHandle(16, emmental));
339:
340: // At this point we have 16 facts but only 12 different types of cheeses
341: // so no table resize and thus its size is 16
342:
343: assertEquals(16, map.size());
344:
345: Entry[] table = map.getTable();
346: assertEquals(16, table.length);
347:
348: final Cheese feta = new Cheese("feta", 48);
349: map.add(new DefaultFactHandle(2, feta));
350:
351: // This adds our 13th type of cheese. The map is set with an initial capacity of 16 and
352: // a threshold of 75%, that after 12 it should resize the map to 32.
353: assertEquals(17, map.size());
354:
355: table = map.getTable();
356: assertEquals(32, table.length);
357:
358: final Cheese haloumi = new Cheese("haloumi", 48);
359: map.add(new DefaultFactHandle(2, haloumi));
360:
361: final Cheese chevre = new Cheese("chevre", 48);
362: map.add(new DefaultFactHandle(2, chevre));
363:
364: }
365:
366: public static class TestClass {
367: private int hashCode;
368: private Object object;
369:
370: public TestClass() {
371:
372: }
373:
374: public TestClass(final int hashCode, final Object object) {
375: this .hashCode = hashCode;
376: this .object = object;
377: }
378:
379: public Object getObject() {
380: return this .object;
381: }
382:
383: public void setObject(final Object object) {
384: this .object = object;
385: }
386:
387: public void setHashCode(final int hashCode) {
388: this .hashCode = hashCode;
389: }
390:
391: public int hashCode() {
392: return this .hashCode;
393: }
394:
395: public boolean equals(final Object obj) {
396: if (this == obj) {
397: return true;
398: }
399: if (obj == null) {
400: return false;
401: }
402: if (getClass() != obj.getClass()) {
403: return false;
404: }
405: final TestClass other = (TestClass) obj;
406:
407: if (this .object == null) {
408: if (other.object != null) {
409: return false;
410: }
411: } else if (!this .object.equals(other.object)) {
412: return false;
413: }
414: return true;
415: }
416: }
417:
418: private int tablePopulationSize(final AbstractHashTable map)
419: throws Exception {
420: final Field field = AbstractHashTable.class
421: .getDeclaredField("table");
422: field.setAccessible(true);
423: final Entry[] array = (Entry[]) field.get(map);
424: int size = 0;
425: for (int i = 0, length = array.length; i < length; i++) {
426: if (array[i] != null) {
427: size++;
428: }
429: }
430: return size;
431: }
432:
433: private Entry[] getEntries(final AbstractHashTable map)
434: throws Exception {
435: final Field field = AbstractHashTable.class
436: .getDeclaredField("table");
437: field.setAccessible(true);
438: final List list = new ArrayList();
439:
440: final Entry[] array = (Entry[]) field.get(map);
441: for (int i = 0, length = array.length; i < length; i++) {
442: if (array[i] != null) {
443: list.add(array[i]);
444: }
445: }
446: return (Entry[]) list.toArray(new Entry[list.size()]);
447: }
448:
449: public void testEmptyIterator() {
450: final FieldExtractor extractor = ClassFieldExtractorCache
451: .getExtractor(Cheese.class, "type", getClass()
452: .getClassLoader());
453:
454: final Pattern pattern = new Pattern(0, new ClassObjectType(
455: Cheese.class));
456:
457: final Declaration declaration = new Declaration("typeOfCheese",
458: extractor, pattern);
459:
460: final FieldIndex fieldIndex = new FieldIndex(extractor,
461: declaration, StringFactory.getInstance().getEvaluator(
462: Operator.EQUAL));
463:
464: final FactHandleIndexHashTable map = new FactHandleIndexHashTable(
465: new FieldIndex[] { fieldIndex });
466:
467: final Cheese stilton = new Cheese("stilton", 55);
468: final InternalFactHandle stiltonHandle = new DefaultFactHandle(
469: 2, stilton);
470:
471: final Iterator it = map.iterator(new ReteTuple(stiltonHandle));
472: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
473: .next()) {
474: fail("Map is empty, there should be no iteration");
475: }
476: }
477:
478: }
|