Changeset 1733 for branches/autoquest-core-tasktrees-alignment/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TaskSymbolBucketedMap.java
- Timestamp:
- 09/05/14 19:33:12 (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/autoquest-core-tasktrees-alignment/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TaskSymbolBucketedMap.java
r1408 r1733 36 36 /** 37 37 * <p> 38 * This class is a data structure for holding task instances in form of a symbol map. It is more 39 * efficient than a simple list. This data structure can be used with a comparator to adapt the 40 * effective list behavior and to define the equals strategy for comparing task instances. After a 41 * certain size ({@link #MAX_LIST_SIZE}), the map creates an index consisting of buckets of similar 42 * task instances. This allows searching for task instances in a more efficient order as the search 43 * can start in the most appropriate of the internal buckets. 38 * This class is a data structure for holding task instances in form of a symbol 39 * map. It is more efficient than a simple list. This data structure can be used 40 * with a comparator to adapt the effective list behavior and to define the 41 * equals strategy for comparing task instances. After a certain size ( 42 * {@link #MAX_LIST_SIZE}), the map creates an index consisting of buckets of 43 * similar task instances. This allows searching for task instances in a more 44 * efficient order as the search can start in the most appropriate of the 45 * internal buckets. 44 46 * </p> 45 47 * <p> 46 * The class is called a map, although it is not. It may contain the same task instances as 47 * separate keys. This implementation is done for performance improvements. If it is required to 48 * really assure, that a key exists only once, then each call to the 49 * {@link #addSymbol(Object, Object)} method should be done only, if the 50 * {@link #containsSymbol(Object)} method for the same symbol returns false. 48 * The class is called a map, although it is not. It may contain the same task 49 * instances as separate keys. This implementation is done for performance 50 * improvements. If it is required to really assure, that a key exists only 51 * once, then each call to the {@link #addSymbol(Object, Object)} method should 52 * be done only, if the {@link #containsSymbol(Object)} method for the same 53 * symbol returns false. 51 54 * </p> 52 55 * … … 56 59 * @param <V> 57 60 */ 58 class TaskSymbolBucketedMap<V> implements SymbolMap<ITaskInstance, V>, Serializable { 59 60 /** 61 * <p> 62 * default serial version UID 63 * </p> 64 */ 65 private static final long serialVersionUID = 1L; 66 67 /** 68 * <p> 69 * the maximum number of task instances in this map which is still only treated as list 70 * instead of using buckets. 71 * </p> 72 */ 73 private static final int MAX_LIST_SIZE = 15; 74 75 /** 76 * <p> 77 * Comparator to be used for comparing the task instances with each other 78 * </p> 79 */ 80 private TaskInstanceComparator comparator; 81 82 /** 83 * <p> 84 * Internally maintained plain list of task instances and associated values 85 * </p> 86 */ 87 private List<Map.Entry<ITaskInstance, V>> symbolList; 88 89 /** 90 * <p> 91 * If the size of the map exceeds {@link #MAX_LIST_SIZE}, this is the index using buckets 92 * for optimizing the search order. 93 * </p> 94 */ 95 private Map<Integer, List<Map.Entry<ITaskInstance, V>>> symbolBuckets; 96 97 /** 98 * <p> 99 * When using buckets, not any task instance may be associated a correct bucket. Therefore, we 100 * set a default bucket for all such task instances. This may change if the same bucket for a 101 * specific symbol is selected. 102 * </p> 103 */ 104 private int defaultBucket = 0; 105 106 /** 107 * <p> 108 * Instantiates a task instance map with a comparator 109 * </p> 110 * 111 * @param comparator the comparator to use for comparing task instances 112 * 113 * @throws IllegalArgumentException if the provided comparator is null 114 */ 115 public TaskSymbolBucketedMap(TaskInstanceComparator comparator) { 116 if (comparator == null) { 117 throw new IllegalArgumentException("comparator must not be null"); 118 } 119 120 this.comparator = comparator; 121 this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>(); 122 } 123 124 /** 125 * <p> 126 * Copy constructor 127 * </p> 128 * 129 * @param otherMap the other map to be copied including its comparator 130 * 131 * @throws IllegalArgumentException if the provided other map is null 132 */ 133 public TaskSymbolBucketedMap(TaskSymbolBucketedMap<V> otherMap) { 134 if (otherMap == null) { 135 throw new IllegalArgumentException("otherMap must not be null"); 136 } 137 138 this.comparator = otherMap.comparator; 139 this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>(otherMap.symbolList); 140 141 if (this.symbolList.size() > MAX_LIST_SIZE) { 142 createSymbolBuckets(); 143 } 144 } 145 146 /** 147 * <p> 148 * Returns the size of the map, i.e. the number of task instance entries 149 * </p> 150 * 151 * @return as described 152 */ 153 public int size() { 154 return symbolList.size(); 155 } 156 157 /** 158 * <p> 159 * Returns true if this map is empty, i.e. if {@link #size()} returns 0 160 * </p> 161 * 162 * @return as described 163 */ 164 public boolean isEmpty() { 165 return symbolList.isEmpty(); 166 } 167 168 /** 169 * <p> 170 * Returns true if the provided task instance was stored in this map. 171 * </p> 172 * 173 * @param symbol the task instance to check if it was stored in this map 174 * 175 * @return as described 176 * 177 * @throws IllegalArgumentException if the provided task instance is null 178 */ 179 public boolean containsSymbol(ITaskInstance symbol) { 180 if (symbol == null) { 181 throw new IllegalArgumentException("symbol must not be null"); 182 } 183 184 return getEntry(symbol) != null; 185 } 186 187 /** 188 * <p> 189 * Returns the value associated to the provided task instance in this map. If there is no value 190 * associated to the given task instance or if the task instance is not stored in this map, 191 * the method returns null. 192 * </p> 193 * 194 * @param symbol the task instance to return the value for 195 * 196 * @return as described 197 * 198 * @throws IllegalArgumentException if the provided task instance is null 199 */ 200 public V getValue(ITaskInstance symbol) { 201 if (symbol == null) { 202 throw new IllegalArgumentException("symbol must not be null"); 203 } 204 205 Map.Entry<ITaskInstance, V> entry = getEntry(symbol); 206 207 if (entry != null) { 208 return entry.getValue(); 209 } 210 else { 211 return null; 212 } 213 } 214 215 /** 216 * <p> 217 * Adds a task instance and an associated value to the map. If the value is null, the task 218 * instance is added, anyway and {@link #containsSymbol(Object)} will return true for that 219 * task instance. Adding the same task instance twice will produce two entries. This is 220 * contradictory to typical map implementations. To prevent this, the 221 * {@link #containsSymbol(Object)} and {@link #removeSymbol(Object)} methods should be used to 222 * ensure map behavior. 223 * </p> 224 * 225 * @param symbol the task instance to add to the map 226 * @param value the value to associate to the task instance in this map 227 * 228 * @return as described 229 * 230 * @throws IllegalArgumentException if the provided task instance is null 231 */ 232 public void addSymbol(ITaskInstance symbol, V value) { 233 if (symbol == null) { 234 throw new IllegalArgumentException("symbol must not be null"); 235 } 236 237 Map.Entry<ITaskInstance, V> entry = new SymbolMapEntry(symbol, value); 238 239 symbolList.add(entry); 240 241 if (symbolList.size() > MAX_LIST_SIZE) { 242 if (symbolBuckets == null) { 243 createSymbolBuckets(); 244 } 245 else { 246 addToSymbolBucket(entry); 247 } 248 } 249 } 250 251 /** 252 * <p> 253 * Removes a task instance and its associated value from the map. If the task instance is 254 * stored several times, only the first of its occurrences is removed. 255 * </p> 256 * 257 * @param symbol the task instance to be removed from the map 258 * 259 * @return as described 260 * 261 * @throws IllegalArgumentException if the provided task instance is null 262 */ 263 public V removeSymbol(ITaskInstance symbol) { 264 if (symbol == null) { 265 throw new IllegalArgumentException("symbol must not be null"); 266 } 267 268 for (int i = 0; i < symbolList.size(); i++) { 269 if (comparator.equals(symbolList.get(i).getKey(), symbol)) { 270 // found the symbol. Remove it from the list, and if required, also from the map. 271 V value = symbolList.remove(i).getValue(); 272 273 if (symbolList.size() > MAX_LIST_SIZE) { 274 removeFromSymbolBuckets(symbol); 275 } 276 277 return value; 278 } 279 } 280 281 return null; 282 } 283 284 /** 285 * <p> 286 * Returns a collection of all task instances in this map. 287 * </p> 288 * 289 * @return as described 290 */ 291 public Collection<ITaskInstance> getSymbols() { 292 return new ReadOnlyCollectionFacade<ITaskInstance>(symbolList, new SymbolFacade()); 293 } 294 295 /** 296 * <p> 297 * Returns a collection of all values associated to task instances in this map. May contain 298 * null values, if some of the task instances are mapped to null. The length of the returned 299 * collection is in any case the same as the size of the map. 300 * </p> 301 * 302 * @return as described 303 */ 304 public Collection<V> getValues() { 305 return new ReadOnlyCollectionFacade<V>(symbolList, new ValueFacade()); 306 } 307 308 /** 309 * <p> 310 * Removes all task instances and associated values from the map. 311 * </p> 312 */ 313 public void clear() { 314 symbolList.clear(); 315 symbolBuckets = null; 316 } 317 318 /* (non-Javadoc) 319 * @see java.lang.Object#hashCode() 320 */ 321 @Override 322 public int hashCode() { 323 return symbolList.size(); 324 } 325 326 /* (non-Javadoc) 327 * @see java.lang.Object#equals(java.lang.Object) 328 */ 329 @SuppressWarnings("unchecked") 330 @Override 331 public boolean equals(Object obj) { 332 if (this == obj) { 333 return true; 334 } 335 else if (this.getClass().isInstance(obj)) { 336 TaskSymbolBucketedMap<V> other = (TaskSymbolBucketedMap<V>) obj; 337 338 return (symbolList.size() == other.symbolList.size()) && 339 (symbolList.containsAll(other.symbolList)); 340 } 341 else { 342 return false; 343 } 344 } 345 346 /** 347 * <p> 348 * Internally used to create task instance buckets in case the number of stored task instances 349 * increased above {@link #MAX_LIST_SIZE}. 350 * </p> 351 */ 352 private void createSymbolBuckets() { 353 //System.out.println("creating symbol buckets"); 354 symbolBuckets = new HashMap<Integer, List<Map.Entry<ITaskInstance, V>>>(); 355 356 for (Map.Entry<ITaskInstance, V> symbol : symbolList) { 357 addToSymbolBucket(symbol); 358 } 359 } 360 361 /** 362 * <p> 363 * Adds a task instance and its value to its corresponding bucket. The corresponding bucket is 364 * the first element of the array returned by the method 365 * {@link #getBucketSearchOrder(ITaskInstance)}. If no search order for a task instance is 366 * defined, the entry is added to the default bucket. If the bucket id identical to the default 367 * bucket id, the default bucket id is shifted to another value. 368 * </p> 369 */ 370 private void addToSymbolBucket(Map.Entry<ITaskInstance, V> symbolEntry) { 371 int bucketId = defaultBucket; 372 int[] bucketSearchOrder = getBucketSearchOrder(symbolEntry.getKey()); 373 374 if ((bucketSearchOrder != null) && (bucketSearchOrder.length > 0)) { 375 bucketId = bucketSearchOrder[0]; 376 377 if (bucketId == defaultBucket) { 378 setNewDefaultBucketId(); 379 } 380 } 381 382 List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId); 383 384 if (list == null) { 385 list = new LinkedList<Map.Entry<ITaskInstance, V>>(); 386 symbolBuckets.put(bucketId, list); 387 } 388 389 list.add(symbolEntry); 390 } 391 392 /** 393 * <p> 394 * returns the search order for a task instance which is different for event task instances, 395 * sequence instances, selection instances, and iteration instances 396 * </p> 397 */ 398 private int[] getBucketSearchOrder(ITaskInstance taskInstance) { 399 // 0 = sequence; 1 = selection; 2 = iteration; 3 = optional; 4 = event task in general; 400 // other = hashCode of name of event type 401 402 if (taskInstance instanceof IEventTaskInstance) { 403 // event tasks are most likely equal to those of the same type happening on the same 404 // target. Afterwards, they should be equal to those of the event type with the same 405 // name. Afterwards, they may be equal to iterations, optionals, other event tasks, 406 // selections, and finally the rest. 407 Event event = ((IEventTaskInstance) taskInstance).getEvent(); 408 return new int[] { event.getTarget().hashCode() + event.getType().getName().hashCode(), 409 event.getType().getName().hashCode(), 2, 3, 4, 1 }; 410 } 411 else if (taskInstance instanceof ISequenceInstance) { 412 return new int[] { 0, 2, 3, 1 }; 413 } 414 else if (taskInstance instanceof ISelectionInstance) { 415 return new int[] { 1, 4, 2, 3 }; 416 } 417 else if (taskInstance instanceof IIterationInstance) { 418 return new int[] { 2, 1, 4 }; 419 } 420 421 return null; 422 } 423 424 /** 425 * <p> 426 * Removes the entry for a given task instance from the buckets. It uses the bucket search order 427 * defined to find the task instance as fast as possible. 428 * </p> 429 */ 430 private Map.Entry<ITaskInstance, V> removeFromSymbolBuckets(ITaskInstance symbol) { 431 int bucketId = defaultBucket; 432 int[] bucketSearchOrder = getBucketSearchOrder(symbol); 433 434 if ((bucketSearchOrder != null) && (bucketSearchOrder.length > 0)) { 435 bucketId = bucketSearchOrder[0]; 436 } 437 438 List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId); 439 Map.Entry<ITaskInstance, V> result = null; 440 441 if (list != null) { 442 for (int i = 0; i < list.size(); i++) { 443 if (comparator.equals(list.get(i).getKey(), symbol)) { 444 result = list.remove(i); 445 break; 446 } 447 } 448 449 if (list.isEmpty()) { 450 symbolBuckets.remove(bucketId); 451 } 452 } 453 454 return result; 455 } 456 457 /** 458 * <p> 459 * Updates the default bucket id to a new one 460 * </p> 461 */ 462 private void setNewDefaultBucketId() { 463 int oldDefaultBucket = defaultBucket; 464 do { 465 defaultBucket += 1; 466 } 467 while (symbolBuckets.containsKey(defaultBucket)); 468 469 symbolBuckets.put(defaultBucket, symbolBuckets.remove(oldDefaultBucket)); 470 } 471 472 /** 473 * <p> 474 * searches for the entry belonging to the given task instance. The method either uses the 475 * list if buckets are not used yet, or it uses the buckets and searches them in the order 476 * defined by the method {@link #getBucketSearchOrder(ITaskInstance)}. If the task instances 477 * isn't found and the bucket search order does not refer all buckets, then also the other 478 * buckets are searched for the task instance. 479 * </p> 480 */ 481 private Map.Entry<ITaskInstance, V> getEntry(ITaskInstance symbol) { 482 Map.Entry<ITaskInstance, V> entry = null; 483 if (symbolBuckets == null) { 484 entry = lookup(symbol, symbolList); 485 } 486 else { 487 int[] bucketSearchOrder = getBucketSearchOrder(symbol); 488 for (int bucketId : bucketSearchOrder) { 489 List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId); 490 if (list != null) { 491 entry = lookup(symbol, list); 492 if (entry != null) { 493 break; 494 } 495 } 496 } 497 498 // try to search the other buckets 499 if (entry == null) { 500 Arrays.sort(bucketSearchOrder); 501 for (Map.Entry<Integer, List<Map.Entry<ITaskInstance, V>>> bucket : symbolBuckets.entrySet()) { 502 if (Arrays.binarySearch(bucketSearchOrder, bucket.getKey()) < 0) { 503 List<Map.Entry<ITaskInstance, V>> list = bucket.getValue(); 504 if (list != null) { 505 entry = lookup(symbol, list); 506 if (entry != null) { 507 break; 508 } 509 } 510 } 511 } 512 } 513 } 514 515 return entry; 516 } 517 518 /** 519 * <p> 520 * Convenience method to look up a symbol in a list of entries using the comparator. 521 * </p> 522 */ 523 private Map.Entry<ITaskInstance, V> lookup(ITaskInstance symbol, List<Map.Entry<ITaskInstance, V>> list) { 524 for (Map.Entry<ITaskInstance, V> candidate : list) { 525 if (comparator.equals(candidate.getKey(), symbol)) { 526 return candidate; 527 } 528 } 529 530 return null; 531 } 532 533 /** 534 * <p> 535 * Internally used data structure for storing task instance - value pairs 536 * </p> 537 * 538 * @author Patrick Harms 539 */ 540 private class SymbolMapEntry implements Map.Entry<ITaskInstance, V> { 541 542 /** 543 * the task instance to map to a value 544 */ 545 private ITaskInstance symbol; 546 547 /** 548 * the value associated with the symbol 549 */ 550 private V value; 551 552 /** 553 * <p> 554 * Simple constructor for initializing the entry with a task instance and its associated 555 * value. 556 * </p> 557 */ 558 private SymbolMapEntry(ITaskInstance symbol, V value) { 559 super(); 560 this.symbol = symbol; 561 this.value = value; 562 } 563 564 /* (non-Javadoc) 565 * @see java.util.Map.Entry#getKey() 566 */ 567 @Override 568 public ITaskInstance getKey() { 569 return symbol; 570 } 571 572 /* (non-Javadoc) 573 * @see java.util.Map.Entry#getValue() 574 */ 575 @Override 576 public V getValue() { 577 return value; 578 } 579 580 /* (non-Javadoc) 581 * @see java.util.Map.Entry#setValue(java.lang.Object) 582 */ 583 @Override 584 public V setValue(V value) { 585 V oldValue = this.value; 586 this.value = value; 587 return oldValue; 588 } 589 590 /* (non-Javadoc) 591 * @see java.lang.Object#hashCode() 592 */ 593 @Override 594 public int hashCode() { 595 return symbol.hashCode(); 596 } 597 598 /* (non-Javadoc) 599 * @see java.lang.Object#equals(java.lang.Object) 600 */ 601 @SuppressWarnings("unchecked") 602 @Override 603 public boolean equals(Object obj) { 604 if (this == obj) { 605 return true; 606 } 607 else if (this.getClass().isInstance(obj)) { 608 SymbolMapEntry other = (SymbolMapEntry) obj; 609 return (symbol.equals(other.symbol) && 610 (value == null ? other.value == null : value.equals(other.value))); 611 } 612 else { 613 return false; 614 } 615 } 616 617 /* (non-Javadoc) 618 * @see java.lang.Object#toString() 619 */ 620 @Override 621 public String toString() { 622 return symbol + "=" + value; 623 } 624 625 } 626 627 /** 628 * <p> 629 * Used to create an efficient facade for accessing the internal list of entries either only 630 * for the task instances or only for the values. It is a default implementation of the 631 * collection interface. The entry facade provided to the constructor decides, if either the 632 * list accesses only the task instances or only the values. 633 * </p> 634 * 635 * @author Patrick Harms 636 */ 637 private class ReadOnlyCollectionFacade<TYPE> implements Collection<TYPE> { 638 639 /** 640 * the list facaded by this facade 641 */ 642 private List<Map.Entry<ITaskInstance, V>> list; 643 644 /** 645 * the facade to be used for the entries 646 */ 647 private EntryFacade<TYPE> entryFacade; 648 649 /** 650 * <p> 651 * Initializes the facade with the facaded list and the facade to be used for the entries 652 * </p> 653 */ 654 private ReadOnlyCollectionFacade(List<Map.Entry<ITaskInstance, V>> list, 655 EntryFacade<TYPE> entryFacade) 656 { 657 this.list = list; 658 this.entryFacade = entryFacade; 659 } 660 661 /* (non-Javadoc) 662 * @see java.util.Collection#size() 663 */ 664 @Override 665 public int size() { 666 return list.size(); 667 } 668 669 /* (non-Javadoc) 670 * @see java.util.Collection#isEmpty() 671 */ 672 @Override 673 public boolean isEmpty() { 674 return list.isEmpty(); 675 } 676 677 /* (non-Javadoc) 678 * @see java.util.Collection#contains(java.lang.Object) 679 */ 680 @Override 681 public boolean contains(Object o) { 682 if (o == null) { 683 for (Map.Entry<ITaskInstance, V> entry : list) { 684 if (entryFacade.getFacadedElement(entry) == null) { 685 return true; 686 } 687 } 688 } 689 else { 690 for (Map.Entry<ITaskInstance, V> entry : list) { 691 if (o.equals(entryFacade.getFacadedElement(entry))) { 692 return true; 693 } 694 } 695 } 696 697 return false; 698 } 699 700 /* (non-Javadoc) 701 * @see java.util.Collection#toArray() 702 */ 703 @Override 704 public Object[] toArray() { 705 Object[] result = new Object[list.size()]; 706 707 for (int i = 0; i < list.size(); i++) { 708 result[i] = entryFacade.getFacadedElement(list.get(i)); 709 } 710 711 return result; 712 } 713 714 /* (non-Javadoc) 715 * @see java.util.Collection#toArray(T[]) 716 */ 717 @SuppressWarnings("unchecked") 718 @Override 719 public <T> T[] toArray(T[] a) { 720 T[] result = a; 721 722 for (int i = 0; i < list.size(); i++) { 723 result[i] = (T) entryFacade.getFacadedElement(list.get(i)); 724 } 725 726 return result; 727 } 728 729 /* (non-Javadoc) 730 * @see java.util.Collection#add(java.lang.Object) 731 */ 732 @Override 733 public boolean add(TYPE e) { 734 throw new UnsupportedOperationException("this collection is read only"); 735 } 736 737 /* (non-Javadoc) 738 * @see java.util.Collection#remove(java.lang.Object) 739 */ 740 @Override 741 public boolean remove(Object o) { 742 throw new UnsupportedOperationException("this collection is read only"); 743 } 744 745 /* (non-Javadoc) 746 * @see java.util.Collection#containsAll(java.util.Collection) 747 */ 748 @Override 749 public boolean containsAll(Collection<?> c) { 750 for (Object candidate : c) { 751 if (!contains(candidate)) { 752 return false; 753 } 754 } 755 756 return true; 757 } 758 759 /* (non-Javadoc) 760 * @see java.util.Collection#addAll(java.util.Collection) 761 */ 762 @Override 763 public boolean addAll(Collection<? extends TYPE> c) { 764 throw new UnsupportedOperationException("this collection is read only"); 765 } 766 767 /* (non-Javadoc) 768 * @see java.util.Collection#removeAll(java.util.Collection) 769 */ 770 @Override 771 public boolean removeAll(Collection<?> c) { 772 throw new UnsupportedOperationException("this collection is read only"); 773 } 774 775 /* (non-Javadoc) 776 * @see java.util.Collection#retainAll(java.util.Collection) 777 */ 778 @Override 779 public boolean retainAll(Collection<?> c) { 780 throw new UnsupportedOperationException("this collection is read only"); 781 } 782 783 /* (non-Javadoc) 784 * @see java.util.Collection#clear() 785 */ 786 @Override 787 public void clear() { 788 throw new UnsupportedOperationException("this collection is read only"); 789 } 790 791 /* (non-Javadoc) 792 * @see java.util.Collection#iterator() 793 */ 794 @Override 795 public Iterator<TYPE> iterator() { 796 return new ReadOnlyCollectionIteratorFacade<TYPE>(list.iterator(), entryFacade); 797 } 798 799 } 800 801 /** 802 * <p> 803 * Implementation of an iterator to facade an iterator on the internal list of task instance 804 * entries. 805 * </p> 806 * 807 * @author Patrick Harms 808 */ 809 private class ReadOnlyCollectionIteratorFacade<TYPE> implements Iterator<TYPE> { 810 811 /** 812 * the facaded iterator 813 */ 814 private Iterator<Map.Entry<ITaskInstance, V>> iterator; 815 816 /** 817 * the facade for the entries provided by the facaded iterator 818 */ 819 private EntryFacade<TYPE> entryFacade; 820 821 /** 822 * <p> 823 * initialized this facade with the facaded iterator and the entry facade to be used for 824 * the entries. 825 * </p> 826 */ 827 private ReadOnlyCollectionIteratorFacade(Iterator<Map.Entry<ITaskInstance, V>> iterator, 828 EntryFacade<TYPE> entryFacade) 829 { 830 this.iterator = iterator; 831 this.entryFacade = entryFacade; 832 } 833 834 /* (non-Javadoc) 835 * @see java.util.Iterator#hasNext() 836 */ 837 @Override 838 public boolean hasNext() { 839 return iterator.hasNext(); 840 } 841 842 /* (non-Javadoc) 843 * @see java.util.Iterator#next() 844 */ 845 @Override 846 public TYPE next() { 847 return entryFacade.getFacadedElement(iterator.next()); 848 } 849 850 /* (non-Javadoc) 851 * @see java.util.Iterator#remove() 852 */ 853 @Override 854 public void remove() { 855 throw new UnsupportedOperationException("this iterator is read only"); 856 } 857 858 } 859 860 /** 861 * <p> 862 * Used to facade task instance entries and to return only this part of an entry, that is 863 * relevant. 864 * </p> 865 * 866 * @author Patrick Harms 867 */ 868 private abstract class EntryFacade<T> { 869 870 /** 871 * <p> 872 * Returns only the part of an entry that is relevant or required. 873 * </p> 874 * 875 * @param entry of which the part shall be returned 876 * 877 * @return the part of the entry to be returned 878 */ 879 protected abstract T getFacadedElement(Entry<ITaskInstance, V> entry); 880 881 } 882 883 /** 884 * <p> 885 * Implementation of the entry facade returning the entries key, i.e. the symbol. 886 * </p> 887 * 888 * @author Patrick Harms 889 */ 890 private class SymbolFacade extends EntryFacade<ITaskInstance> { 891 892 /* (non-Javadoc) 893 * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry) 894 */ 895 @Override 896 protected ITaskInstance getFacadedElement(Entry<ITaskInstance, V> entry) { 897 return entry.getKey(); 898 } 899 } 900 901 /** 902 * <p> 903 * Implementation of the entry facade returning the entries value, i.e. the value associated to 904 * the symbol. 905 * </p> 906 * 907 * @author Patrick Harms 908 */ 909 private class ValueFacade extends EntryFacade<V> { 910 911 /* (non-Javadoc) 912 * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry) 913 */ 914 @Override 915 protected V getFacadedElement(Entry<ITaskInstance, V> entry) { 916 return entry.getValue(); 917 } 918 } 61 class TaskSymbolBucketedMap<V> implements SymbolMap<ITaskInstance, V>, 62 Serializable { 63 64 /** 65 * <p> 66 * Used to facade task instance entries and to return only this part of an 67 * entry, that is relevant. 68 * </p> 69 * 70 * @author Patrick Harms 71 */ 72 private abstract class EntryFacade<T> { 73 74 /** 75 * <p> 76 * Returns only the part of an entry that is relevant or required. 77 * </p> 78 * 79 * @param entry 80 * of which the part shall be returned 81 * 82 * @return the part of the entry to be returned 83 */ 84 protected abstract T getFacadedElement(Entry<ITaskInstance, V> entry); 85 86 } 87 88 /** 89 * <p> 90 * Used to create an efficient facade for accessing the internal list of 91 * entries either only for the task instances or only for the values. It is 92 * a default implementation of the collection interface. The entry facade 93 * provided to the constructor decides, if either the list accesses only the 94 * task instances or only the values. 95 * </p> 96 * 97 * @author Patrick Harms 98 */ 99 private class ReadOnlyCollectionFacade<TYPE> implements Collection<TYPE> { 100 101 /** 102 * the list facaded by this facade 103 */ 104 private final List<Map.Entry<ITaskInstance, V>> list; 105 106 /** 107 * the facade to be used for the entries 108 */ 109 private final EntryFacade<TYPE> entryFacade; 110 111 /** 112 * <p> 113 * Initializes the facade with the facaded list and the facade to be 114 * used for the entries 115 * </p> 116 */ 117 private ReadOnlyCollectionFacade( 118 List<Map.Entry<ITaskInstance, V>> list, 119 EntryFacade<TYPE> entryFacade) { 120 this.list = list; 121 this.entryFacade = entryFacade; 122 } 123 124 /* 125 * (non-Javadoc) 126 * 127 * @see java.util.Collection#add(java.lang.Object) 128 */ 129 @Override 130 public boolean add(TYPE e) { 131 throw new UnsupportedOperationException( 132 "this collection is read only"); 133 } 134 135 /* 136 * (non-Javadoc) 137 * 138 * @see java.util.Collection#addAll(java.util.Collection) 139 */ 140 @Override 141 public boolean addAll(Collection<? extends TYPE> c) { 142 throw new UnsupportedOperationException( 143 "this collection is read only"); 144 } 145 146 /* 147 * (non-Javadoc) 148 * 149 * @see java.util.Collection#clear() 150 */ 151 @Override 152 public void clear() { 153 throw new UnsupportedOperationException( 154 "this collection is read only"); 155 } 156 157 /* 158 * (non-Javadoc) 159 * 160 * @see java.util.Collection#contains(java.lang.Object) 161 */ 162 @Override 163 public boolean contains(Object o) { 164 if (o == null) { 165 for (final Map.Entry<ITaskInstance, V> entry : list) { 166 if (entryFacade.getFacadedElement(entry) == null) { 167 return true; 168 } 169 } 170 } else { 171 for (final Map.Entry<ITaskInstance, V> entry : list) { 172 if (o.equals(entryFacade.getFacadedElement(entry))) { 173 return true; 174 } 175 } 176 } 177 178 return false; 179 } 180 181 /* 182 * (non-Javadoc) 183 * 184 * @see java.util.Collection#containsAll(java.util.Collection) 185 */ 186 @Override 187 public boolean containsAll(Collection<?> c) { 188 for (final Object candidate : c) { 189 if (!contains(candidate)) { 190 return false; 191 } 192 } 193 194 return true; 195 } 196 197 /* 198 * (non-Javadoc) 199 * 200 * @see java.util.Collection#isEmpty() 201 */ 202 @Override 203 public boolean isEmpty() { 204 return list.isEmpty(); 205 } 206 207 /* 208 * (non-Javadoc) 209 * 210 * @see java.util.Collection#iterator() 211 */ 212 @Override 213 public Iterator<TYPE> iterator() { 214 return new ReadOnlyCollectionIteratorFacade<TYPE>(list.iterator(), 215 entryFacade); 216 } 217 218 /* 219 * (non-Javadoc) 220 * 221 * @see java.util.Collection#remove(java.lang.Object) 222 */ 223 @Override 224 public boolean remove(Object o) { 225 throw new UnsupportedOperationException( 226 "this collection is read only"); 227 } 228 229 /* 230 * (non-Javadoc) 231 * 232 * @see java.util.Collection#removeAll(java.util.Collection) 233 */ 234 @Override 235 public boolean removeAll(Collection<?> c) { 236 throw new UnsupportedOperationException( 237 "this collection is read only"); 238 } 239 240 /* 241 * (non-Javadoc) 242 * 243 * @see java.util.Collection#retainAll(java.util.Collection) 244 */ 245 @Override 246 public boolean retainAll(Collection<?> c) { 247 throw new UnsupportedOperationException( 248 "this collection is read only"); 249 } 250 251 /* 252 * (non-Javadoc) 253 * 254 * @see java.util.Collection#size() 255 */ 256 @Override 257 public int size() { 258 return list.size(); 259 } 260 261 /* 262 * (non-Javadoc) 263 * 264 * @see java.util.Collection#toArray() 265 */ 266 @Override 267 public Object[] toArray() { 268 final Object[] result = new Object[list.size()]; 269 270 for (int i = 0; i < list.size(); i++) { 271 result[i] = entryFacade.getFacadedElement(list.get(i)); 272 } 273 274 return result; 275 } 276 277 /* 278 * (non-Javadoc) 279 * 280 * @see java.util.Collection#toArray(T[]) 281 */ 282 @SuppressWarnings("unchecked") 283 @Override 284 public <T> T[] toArray(T[] a) { 285 final T[] result = a; 286 287 for (int i = 0; i < list.size(); i++) { 288 result[i] = (T) entryFacade.getFacadedElement(list.get(i)); 289 } 290 291 return result; 292 } 293 294 } 295 296 /** 297 * <p> 298 * Implementation of an iterator to facade an iterator on the internal list 299 * of task instance entries. 300 * </p> 301 * 302 * @author Patrick Harms 303 */ 304 private class ReadOnlyCollectionIteratorFacade<TYPE> implements 305 Iterator<TYPE> { 306 307 /** 308 * the facaded iterator 309 */ 310 private final Iterator<Map.Entry<ITaskInstance, V>> iterator; 311 312 /** 313 * the facade for the entries provided by the facaded iterator 314 */ 315 private final EntryFacade<TYPE> entryFacade; 316 317 /** 318 * <p> 319 * initialized this facade with the facaded iterator and the entry 320 * facade to be used for the entries. 321 * </p> 322 */ 323 private ReadOnlyCollectionIteratorFacade( 324 Iterator<Map.Entry<ITaskInstance, V>> iterator, 325 EntryFacade<TYPE> entryFacade) { 326 this.iterator = iterator; 327 this.entryFacade = entryFacade; 328 } 329 330 /* 331 * (non-Javadoc) 332 * 333 * @see java.util.Iterator#hasNext() 334 */ 335 @Override 336 public boolean hasNext() { 337 return iterator.hasNext(); 338 } 339 340 /* 341 * (non-Javadoc) 342 * 343 * @see java.util.Iterator#next() 344 */ 345 @Override 346 public TYPE next() { 347 return entryFacade.getFacadedElement(iterator.next()); 348 } 349 350 /* 351 * (non-Javadoc) 352 * 353 * @see java.util.Iterator#remove() 354 */ 355 @Override 356 public void remove() { 357 throw new UnsupportedOperationException( 358 "this iterator is read only"); 359 } 360 361 } 362 363 /** 364 * <p> 365 * Implementation of the entry facade returning the entries key, i.e. the 366 * symbol. 367 * </p> 368 * 369 * @author Patrick Harms 370 */ 371 private class SymbolFacade extends EntryFacade<ITaskInstance> { 372 373 /* 374 * (non-Javadoc) 375 * 376 * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry) 377 */ 378 @Override 379 protected ITaskInstance getFacadedElement(Entry<ITaskInstance, V> entry) { 380 return entry.getKey(); 381 } 382 } 383 384 /** 385 * <p> 386 * Internally used data structure for storing task instance - value pairs 387 * </p> 388 * 389 * @author Patrick Harms 390 */ 391 private class SymbolMapEntry implements Map.Entry<ITaskInstance, V> { 392 393 /** 394 * the task instance to map to a value 395 */ 396 private final ITaskInstance symbol; 397 398 /** 399 * the value associated with the symbol 400 */ 401 private V value; 402 403 /** 404 * <p> 405 * Simple constructor for initializing the entry with a task instance 406 * and its associated value. 407 * </p> 408 */ 409 private SymbolMapEntry(ITaskInstance symbol, V value) { 410 super(); 411 this.symbol = symbol; 412 this.value = value; 413 } 414 415 /* 416 * (non-Javadoc) 417 * 418 * @see java.lang.Object#equals(java.lang.Object) 419 */ 420 @SuppressWarnings("unchecked") 421 @Override 422 public boolean equals(Object obj) { 423 if (this == obj) { 424 return true; 425 } else if (this.getClass().isInstance(obj)) { 426 final SymbolMapEntry other = (SymbolMapEntry) obj; 427 return (symbol.equals(other.symbol) && (value == null ? other.value == null 428 : value.equals(other.value))); 429 } else { 430 return false; 431 } 432 } 433 434 /* 435 * (non-Javadoc) 436 * 437 * @see java.util.Map.Entry#getKey() 438 */ 439 @Override 440 public ITaskInstance getKey() { 441 return symbol; 442 } 443 444 /* 445 * (non-Javadoc) 446 * 447 * @see java.util.Map.Entry#getValue() 448 */ 449 @Override 450 public V getValue() { 451 return value; 452 } 453 454 /* 455 * (non-Javadoc) 456 * 457 * @see java.lang.Object#hashCode() 458 */ 459 @Override 460 public int hashCode() { 461 return symbol.hashCode(); 462 } 463 464 /* 465 * (non-Javadoc) 466 * 467 * @see java.util.Map.Entry#setValue(java.lang.Object) 468 */ 469 @Override 470 public V setValue(V value) { 471 final V oldValue = this.value; 472 this.value = value; 473 return oldValue; 474 } 475 476 /* 477 * (non-Javadoc) 478 * 479 * @see java.lang.Object#toString() 480 */ 481 @Override 482 public String toString() { 483 return symbol + "=" + value; 484 } 485 486 } 487 488 /** 489 * <p> 490 * Implementation of the entry facade returning the entries value, i.e. the 491 * value associated to the symbol. 492 * </p> 493 * 494 * @author Patrick Harms 495 */ 496 private class ValueFacade extends EntryFacade<V> { 497 498 /* 499 * (non-Javadoc) 500 * 501 * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry) 502 */ 503 @Override 504 protected V getFacadedElement(Entry<ITaskInstance, V> entry) { 505 return entry.getValue(); 506 } 507 } 508 509 /** 510 * <p> 511 * default serial version UID 512 * </p> 513 */ 514 private static final long serialVersionUID = 1L; 515 516 /** 517 * <p> 518 * the maximum number of task instances in this map which is still only 519 * treated as list instead of using buckets. 520 * </p> 521 */ 522 private static final int MAX_LIST_SIZE = 15; 523 524 /** 525 * <p> 526 * Comparator to be used for comparing the task instances with each other 527 * </p> 528 */ 529 private final TaskInstanceComparator comparator; 530 531 /** 532 * <p> 533 * Internally maintained plain list of task instances and associated values 534 * </p> 535 */ 536 private final List<Map.Entry<ITaskInstance, V>> symbolList; 537 538 /** 539 * <p> 540 * If the size of the map exceeds {@link #MAX_LIST_SIZE}, this is the index 541 * using buckets for optimizing the search order. 542 * </p> 543 */ 544 private Map<Integer, List<Map.Entry<ITaskInstance, V>>> symbolBuckets; 545 546 /** 547 * <p> 548 * When using buckets, not any task instance may be associated a correct 549 * bucket. Therefore, we set a default bucket for all such task instances. 550 * This may change if the same bucket for a specific symbol is selected. 551 * </p> 552 */ 553 private int defaultBucket = 0; 554 555 /** 556 * <p> 557 * Instantiates a task instance map with a comparator 558 * </p> 559 * 560 * @param comparator 561 * the comparator to use for comparing task instances 562 * 563 * @throws IllegalArgumentException 564 * if the provided comparator is null 565 */ 566 public TaskSymbolBucketedMap(TaskInstanceComparator comparator) { 567 if (comparator == null) { 568 throw new IllegalArgumentException("comparator must not be null"); 569 } 570 571 this.comparator = comparator; 572 this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>(); 573 } 574 575 /** 576 * <p> 577 * Copy constructor 578 * </p> 579 * 580 * @param otherMap 581 * the other map to be copied including its comparator 582 * 583 * @throws IllegalArgumentException 584 * if the provided other map is null 585 */ 586 public TaskSymbolBucketedMap(TaskSymbolBucketedMap<V> otherMap) { 587 if (otherMap == null) { 588 throw new IllegalArgumentException("otherMap must not be null"); 589 } 590 591 this.comparator = otherMap.comparator; 592 this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>( 593 otherMap.symbolList); 594 595 if (this.symbolList.size() > MAX_LIST_SIZE) { 596 createSymbolBuckets(); 597 } 598 } 599 600 /** 601 * <p> 602 * Adds a task instance and an associated value to the map. If the value is 603 * null, the task instance is added, anyway and 604 * {@link #containsSymbol(Object)} will return true for that task instance. 605 * Adding the same task instance twice will produce two entries. This is 606 * contradictory to typical map implementations. To prevent this, the 607 * {@link #containsSymbol(Object)} and {@link #removeSymbol(Object)} methods 608 * should be used to ensure map behavior. 609 * </p> 610 * 611 * @param symbol 612 * the task instance to add to the map 613 * @param value 614 * the value to associate to the task instance in this map 615 * 616 * @return as described 617 * 618 * @throws IllegalArgumentException 619 * if the provided task instance is null 620 */ 621 @Override 622 public void addSymbol(ITaskInstance symbol, V value) { 623 if (symbol == null) { 624 throw new IllegalArgumentException("symbol must not be null"); 625 } 626 627 final Map.Entry<ITaskInstance, V> entry = new SymbolMapEntry(symbol, 628 value); 629 630 symbolList.add(entry); 631 632 if (symbolList.size() > MAX_LIST_SIZE) { 633 if (symbolBuckets == null) { 634 createSymbolBuckets(); 635 } else { 636 addToSymbolBucket(entry); 637 } 638 } 639 } 640 641 /** 642 * <p> 643 * Adds a task instance and its value to its corresponding bucket. The 644 * corresponding bucket is the first element of the array returned by the 645 * method {@link #getBucketSearchOrder(ITaskInstance)}. If no search order 646 * for a task instance is defined, the entry is added to the default bucket. 647 * If the bucket id identical to the default bucket id, the default bucket 648 * id is shifted to another value. 649 * </p> 650 */ 651 private void addToSymbolBucket(Map.Entry<ITaskInstance, V> symbolEntry) { 652 int bucketId = defaultBucket; 653 final int[] bucketSearchOrder = getBucketSearchOrder(symbolEntry 654 .getKey()); 655 656 if ((bucketSearchOrder != null) && (bucketSearchOrder.length > 0)) { 657 bucketId = bucketSearchOrder[0]; 658 659 if (bucketId == defaultBucket) { 660 setNewDefaultBucketId(); 661 } 662 } 663 664 List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId); 665 666 if (list == null) { 667 list = new LinkedList<Map.Entry<ITaskInstance, V>>(); 668 symbolBuckets.put(bucketId, list); 669 } 670 671 list.add(symbolEntry); 672 } 673 674 /** 675 * <p> 676 * Removes all task instances and associated values from the map. 677 * </p> 678 */ 679 @Override 680 public void clear() { 681 symbolList.clear(); 682 symbolBuckets = null; 683 } 684 685 /** 686 * <p> 687 * Returns true if the provided task instance was stored in this map. 688 * </p> 689 * 690 * @param symbol 691 * the task instance to check if it was stored in this map 692 * 693 * @return as described 694 * 695 * @throws IllegalArgumentException 696 * if the provided task instance is null 697 */ 698 @Override 699 public boolean containsSymbol(ITaskInstance symbol) { 700 if (symbol == null) { 701 throw new IllegalArgumentException("symbol must not be null"); 702 } 703 704 return getEntry(symbol) != null; 705 } 706 707 /** 708 * <p> 709 * Internally used to create task instance buckets in case the number of 710 * stored task instances increased above {@link #MAX_LIST_SIZE}. 711 * </p> 712 */ 713 private void createSymbolBuckets() { 714 // System.out.println("creating symbol buckets"); 715 symbolBuckets = new HashMap<Integer, List<Map.Entry<ITaskInstance, V>>>(); 716 717 for (final Map.Entry<ITaskInstance, V> symbol : symbolList) { 718 addToSymbolBucket(symbol); 719 } 720 } 721 722 /* 723 * (non-Javadoc) 724 * 725 * @see java.lang.Object#equals(java.lang.Object) 726 */ 727 @SuppressWarnings("unchecked") 728 @Override 729 public boolean equals(Object obj) { 730 if (this == obj) { 731 return true; 732 } else if (this.getClass().isInstance(obj)) { 733 final TaskSymbolBucketedMap<V> other = (TaskSymbolBucketedMap<V>) obj; 734 735 return (symbolList.size() == other.symbolList.size()) 736 && (symbolList.containsAll(other.symbolList)); 737 } else { 738 return false; 739 } 740 } 741 742 /** 743 * <p> 744 * returns the search order for a task instance which is different for event 745 * task instances, sequence instances, selection instances, and iteration 746 * instances 747 * </p> 748 */ 749 private int[] getBucketSearchOrder(ITaskInstance taskInstance) { 750 // 0 = sequence; 1 = selection; 2 = iteration; 3 = optional; 4 = event 751 // task in general; 752 // other = hashCode of name of event type 753 754 if (taskInstance instanceof IEventTaskInstance) { 755 // event tasks are most likely equal to those of the same type 756 // happening on the same 757 // target. Afterwards, they should be equal to those of the event 758 // type with the same 759 // name. Afterwards, they may be equal to iterations, optionals, 760 // other event tasks, 761 // selections, and finally the rest. 762 final Event event = ((IEventTaskInstance) taskInstance).getEvent(); 763 return new int[] { 764 event.getTarget().hashCode() 765 + event.getType().getName().hashCode(), 766 event.getType().getName().hashCode(), 2, 3, 4, 1 }; 767 } else if (taskInstance instanceof ISequenceInstance) { 768 return new int[] { 0, 2, 3, 1 }; 769 } else if (taskInstance instanceof ISelectionInstance) { 770 return new int[] { 1, 4, 2, 3 }; 771 } else if (taskInstance instanceof IIterationInstance) { 772 return new int[] { 2, 1, 4 }; 773 } 774 775 return null; 776 } 777 778 /** 779 * <p> 780 * searches for the entry belonging to the given task instance. The method 781 * either uses the list if buckets are not used yet, or it uses the buckets 782 * and searches them in the order defined by the method 783 * {@link #getBucketSearchOrder(ITaskInstance)}. If the task instances isn't 784 * found and the bucket search order does not refer all buckets, then also 785 * the other buckets are searched for the task instance. 786 * </p> 787 */ 788 private Map.Entry<ITaskInstance, V> getEntry(ITaskInstance symbol) { 789 Map.Entry<ITaskInstance, V> entry = null; 790 if (symbolBuckets == null) { 791 entry = lookup(symbol, symbolList); 792 } else { 793 final int[] bucketSearchOrder = getBucketSearchOrder(symbol); 794 for (final int bucketId : bucketSearchOrder) { 795 final List<Map.Entry<ITaskInstance, V>> list = symbolBuckets 796 .get(bucketId); 797 if (list != null) { 798 entry = lookup(symbol, list); 799 if (entry != null) { 800 break; 801 } 802 } 803 } 804 805 // try to search the other buckets 806 if (entry == null) { 807 Arrays.sort(bucketSearchOrder); 808 for (final Map.Entry<Integer, List<Map.Entry<ITaskInstance, V>>> bucket : symbolBuckets 809 .entrySet()) { 810 if (Arrays.binarySearch(bucketSearchOrder, bucket.getKey()) < 0) { 811 final List<Map.Entry<ITaskInstance, V>> list = bucket 812 .getValue(); 813 if (list != null) { 814 entry = lookup(symbol, list); 815 if (entry != null) { 816 break; 817 } 818 } 819 } 820 } 821 } 822 } 823 824 return entry; 825 } 826 827 /** 828 * <p> 829 * Returns a collection of all task instances in this map. 830 * </p> 831 * 832 * @return as described 833 */ 834 @Override 835 public Collection<ITaskInstance> getSymbols() { 836 return new ReadOnlyCollectionFacade<ITaskInstance>(symbolList, 837 new SymbolFacade()); 838 } 839 840 /** 841 * <p> 842 * Returns the value associated to the provided task instance in this map. 843 * If there is no value associated to the given task instance or if the task 844 * instance is not stored in this map, the method returns null. 845 * </p> 846 * 847 * @param symbol 848 * the task instance to return the value for 849 * 850 * @return as described 851 * 852 * @throws IllegalArgumentException 853 * if the provided task instance is null 854 */ 855 @Override 856 public V getValue(ITaskInstance symbol) { 857 if (symbol == null) { 858 throw new IllegalArgumentException("symbol must not be null"); 859 } 860 861 final Map.Entry<ITaskInstance, V> entry = getEntry(symbol); 862 863 if (entry != null) { 864 return entry.getValue(); 865 } else { 866 return null; 867 } 868 } 869 870 /** 871 * <p> 872 * Returns a collection of all values associated to task instances in this 873 * map. May contain null values, if some of the task instances are mapped to 874 * null. The length of the returned collection is in any case the same as 875 * the size of the map. 876 * </p> 877 * 878 * @return as described 879 */ 880 @Override 881 public Collection<V> getValues() { 882 return new ReadOnlyCollectionFacade<V>(symbolList, new ValueFacade()); 883 } 884 885 /* 886 * (non-Javadoc) 887 * 888 * @see java.lang.Object#hashCode() 889 */ 890 @Override 891 public int hashCode() { 892 return symbolList.size(); 893 } 894 895 /** 896 * <p> 897 * Returns true if this map is empty, i.e. if {@link #size()} returns 0 898 * </p> 899 * 900 * @return as described 901 */ 902 @Override 903 public boolean isEmpty() { 904 return symbolList.isEmpty(); 905 } 906 907 /** 908 * <p> 909 * Convenience method to look up a symbol in a list of entries using the 910 * comparator. 911 * </p> 912 */ 913 private Map.Entry<ITaskInstance, V> lookup(ITaskInstance symbol, 914 List<Map.Entry<ITaskInstance, V>> list) { 915 for (final Map.Entry<ITaskInstance, V> candidate : list) { 916 if (comparator.equals(candidate.getKey(), symbol)) { 917 return candidate; 918 } 919 } 920 921 return null; 922 } 923 924 /** 925 * <p> 926 * Removes the entry for a given task instance from the buckets. It uses the 927 * bucket search order defined to find the task instance as fast as 928 * possible. 929 * </p> 930 */ 931 private Map.Entry<ITaskInstance, V> removeFromSymbolBuckets( 932 ITaskInstance symbol) { 933 int bucketId = defaultBucket; 934 final int[] bucketSearchOrder = getBucketSearchOrder(symbol); 935 936 if ((bucketSearchOrder != null) && (bucketSearchOrder.length > 0)) { 937 bucketId = bucketSearchOrder[0]; 938 } 939 940 final List<Map.Entry<ITaskInstance, V>> list = symbolBuckets 941 .get(bucketId); 942 Map.Entry<ITaskInstance, V> result = null; 943 944 if (list != null) { 945 for (int i = 0; i < list.size(); i++) { 946 if (comparator.equals(list.get(i).getKey(), symbol)) { 947 result = list.remove(i); 948 break; 949 } 950 } 951 952 if (list.isEmpty()) { 953 symbolBuckets.remove(bucketId); 954 } 955 } 956 957 return result; 958 } 959 960 /** 961 * <p> 962 * Removes a task instance and its associated value from the map. If the 963 * task instance is stored several times, only the first of its occurrences 964 * is removed. 965 * </p> 966 * 967 * @param symbol 968 * the task instance to be removed from the map 969 * 970 * @return as described 971 * 972 * @throws IllegalArgumentException 973 * if the provided task instance is null 974 */ 975 @Override 976 public V removeSymbol(ITaskInstance symbol) { 977 if (symbol == null) { 978 throw new IllegalArgumentException("symbol must not be null"); 979 } 980 981 for (int i = 0; i < symbolList.size(); i++) { 982 if (comparator.equals(symbolList.get(i).getKey(), symbol)) { 983 // found the symbol. Remove it from the list, and if required, 984 // also from the map. 985 final V value = symbolList.remove(i).getValue(); 986 987 if (symbolList.size() > MAX_LIST_SIZE) { 988 removeFromSymbolBuckets(symbol); 989 } 990 991 return value; 992 } 993 } 994 995 return null; 996 } 997 998 /** 999 * <p> 1000 * Updates the default bucket id to a new one 1001 * </p> 1002 */ 1003 private void setNewDefaultBucketId() { 1004 final int oldDefaultBucket = defaultBucket; 1005 do { 1006 defaultBucket += 1; 1007 } while (symbolBuckets.containsKey(defaultBucket)); 1008 1009 symbolBuckets 1010 .put(defaultBucket, symbolBuckets.remove(oldDefaultBucket)); 1011 } 1012 1013 /** 1014 * <p> 1015 * Returns the size of the map, i.e. the number of task instance entries 1016 * </p> 1017 * 1018 * @return as described 1019 */ 1020 @Override 1021 public int size() { 1022 return symbolList.size(); 1023 } 919 1024 920 1025 }
Note: See TracChangeset
for help on using the changeset viewer.