source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TaskSymbolBucketedMap.java @ 1853

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