source: branches/ralph/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/TaskSymbolBucketedMap.java @ 1617

Last change on this file since 1617 was 1408, checked in by pharms, 10 years ago
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.IEventTaskInstance;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
34import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
35
36/**
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.
44 * </p>
45 * <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.
51 * </p>
52 *
53 * @see SymbolComparator
54 *
55 * @author Patrick Harms
56 * @param <V>
57 */
58class 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    }
919
920}
Note: See TracBrowser for help on using the repository browser.