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

Last change on this file since 1285 was 1285, checked in by pharms, 11 years ago
  • improved performance of task instance trie generation by using different symbol management strategies while creating the trie. This performance improvement is significant and allows to detect tasks now in a much faster manner.
File size: 27.7 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.IEventType;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
35import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
36
37/**
38 * TODO correct comment
39 * <p>
40 * This class is a data structure for holding symbols which is more efficient than a simple list.
41 * This data structure can be used with a comparator to adapt the effective list behavior and to
42 * define the equals strategy for comparing objects. After a certain size ({@link #MAX_LIST_SIZE}),
43 * the symbol map creates a symbol index consisting of buckets. This allows searching for symbols
44 * in a more efficient order as the search can start in the most appropriate of the internal
45 * buckets.
46 * </p>
47 * <p>
48 * The class is called a map, although it is not. It may contain the same element as separate keys.
49 * This implementation is done for performance improvements. If it is required to really assure,
50 * that a key exists only once, then each call to the {@link #addSymbol(Object, Object)} method
51 * should be done only, if the {@link #containsSymbol(Object)} method for the same symbol returns
52 * false.
53 * </p>
54 *
55 * @see SymbolComparator
56 *
57 * @author Patrick Harms
58 * @param <V>
59 */
60class TaskSymbolBucketedMap<V> implements SymbolMap<ITaskInstance, V>, Serializable {
61
62    /**
63     * <p>
64     * default serial version UID
65     * </p>
66     */
67    private static final long serialVersionUID = 1L;
68
69    /**
70     * <p>
71     * the maximum number of symbols in this map which is still only treated as list instead of
72     * using buckets.
73     * </p>
74     */
75    private static final int MAX_LIST_SIZE = 15;
76   
77    /**
78     * <p>
79     * Comparator to be used for comparing the symbols with each other and to determine a bucket
80     * search order
81     * </p>
82     */
83    private TaskComparator comparator;
84
85    /**
86     * <p>
87     * Internally maintained plain list of symbols and associated values
88     * </p>
89     */
90    private List<Map.Entry<ITaskInstance, V>> symbolList;
91
92    /**
93     * <p>
94     * If the size of the map exceeds {@link #MAX_LIST_SIZE}, this is the symbol index using buckets
95     * for optimizing the search order.
96     * </p>
97     */
98    private Map<Integer, List<Map.Entry<ITaskInstance, V>>> symbolBuckets;
99   
100    /**
101     * <p>
102     * When using buckets, not any symbol may be associated a correct bucket by the used
103     * comparator. Therefore, we set a default bucket for all such symbols. This may change
104     * if the comparator defines the same bucket for a specific symbol.
105     * </p>
106     */
107    private int defaultBucket = 0;
108   
109    /**
110     * <p>
111     * Instantiates a symbol map with a comparator
112     * </p>
113     *
114     * @param comparator the comparator to use for comparing symbols and for determining bucket
115     *                   search orders
116     *
117     * @throws IllegalArgumentException if the provided comparator is null
118     */
119    public TaskSymbolBucketedMap(TaskComparator comparator) {
120        if (comparator == null) {
121            throw new IllegalArgumentException("comparator must not be null");
122        }
123       
124        this.comparator = comparator;
125        this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>();
126    }
127
128    /**
129     * <p>
130     * Copy constructure
131     * </p>
132     *
133     * @param otherMap the other map to be copied including its comparator
134     *
135     * @throws IllegalArgumentException if the provided other map is null
136     */
137    public TaskSymbolBucketedMap(TaskSymbolBucketedMap<V> otherMap) {
138        if (otherMap == null) {
139            throw new IllegalArgumentException("otherMap must not be null");
140        }
141
142        this.comparator = otherMap.comparator;
143        this.symbolList = new ArrayList<Map.Entry<ITaskInstance, V>>(otherMap.symbolList);
144       
145        if (this.symbolList.size() > MAX_LIST_SIZE) {
146            createSymbolBuckets();
147        }
148    }
149
150    /**
151     * <p>
152     * Returns the size of the map, i.e. the number of symbol entries
153     * </p>
154     *
155     * @return as described
156     */
157    public int size() {
158        return symbolList.size();
159    }
160
161    /**
162     * <p>
163     * Returns true if this map is empty, i.e. if {@link #size()} returns 0
164     * </p>
165     *
166     * @return as described
167     */
168    public boolean isEmpty() {
169        return symbolList.isEmpty();
170    }
171
172    /**
173     * <p>
174     * Returns true if the provided symbol was stored in this map.
175     * </p>
176     *
177     * @param symbol the symbol to check if it was stored in this map
178     *
179     * @return as described
180     *
181     * @throws IllegalArgumentException if the provided symbol is null
182     */
183    public boolean containsSymbol(ITaskInstance symbol) {
184        if (symbol == null) {
185            throw new IllegalArgumentException("symbol must not be null");
186        }
187       
188        return getEntry(symbol) != null;
189    }
190
191    /**
192     * <p>
193     * Returns the value associated to the provided symbol in this map. If there is no value
194     * associated to the given symbol or if the symbol is not stored in this map, the method
195     * returns null.
196     * </p>
197     *
198     * @param symbol the symbol to return the value for
199     *
200     * @return as described
201     *
202     * @throws IllegalArgumentException if the provided symbol is null
203     */
204    public V getValue(ITaskInstance symbol) {
205        if (symbol == null) {
206            throw new IllegalArgumentException("symbol must not be null");
207        }
208       
209        Map.Entry<ITaskInstance, V> entry = getEntry(symbol);
210       
211        if (entry != null) {
212            return entry.getValue();
213        }
214        else {
215            return null;
216        }
217    }
218
219    /**
220     * <p>
221     * Adds a symbol and an associated value to the map. If the value is null, the symbol is added,
222     * anyway and {@link #containsSymbol(Object)} will return true for that symbol. Adding the
223     * same symbol twice will produce two entries. This is contradictory to typical map
224     * implementations. To prevent this, the {@link #containsSymbol(Object)} and
225     * {@link #removeSymbol(Object)} methods should be used to ensure map behavior.
226     * </p>
227     *
228     * @param symbol the symbol to add to the map
229     * @param value  the value to associate to the symbol in this map
230     *
231     * @return as described
232     *
233     * @throws IllegalArgumentException if the provided symbol is null
234     */
235    public void addSymbol(ITaskInstance symbol, V value) {
236        if (symbol == null) {
237            throw new IllegalArgumentException("symbol must not be null");
238        }
239       
240        Map.Entry<ITaskInstance, 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 symbol and its associated value from the map. If the symbol is stored several
257     * times, the first of its occurrences is removed.
258     * </p>
259     *
260     * @param symbol the symbol to be removed from the map
261     *
262     * @return as described
263     *
264     * @throws IllegalArgumentException if the provided symbol is null
265     */
266    public V removeSymbol(ITaskInstance 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 symbols in this map.
290     * </p>
291     *
292     * @return as described
293     */
294    public Collection<ITaskInstance> getSymbols() {
295        return new ReadOnlyCollectionFacade<ITaskInstance>(symbolList, new SymbolFacade());
296    }
297   
298    /**
299     * <p>
300     * Returns a collection of all values associated to symbols in this map. May contain null
301     * values, if some of the symbols are mapped to null. The length of the returned collection
302     * 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 symbols 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 symbol buckets in case the number of stored symbols increased
352     * above {@link #MAX_LIST_SIZE}.
353     * </p>
354     */
355    private void createSymbolBuckets() {
356        //System.out.println("creating symbol buckets");
357        symbolBuckets = new HashMap<Integer, List<Map.Entry<ITaskInstance, V>>>();
358       
359        for (Map.Entry<ITaskInstance, V> symbol : symbolList) {
360            addToSymbolBucket(symbol);
361        }
362    }
363
364    /**
365     * <p>
366     * Adds a symbol and its value to its corresponding bucket. The corresponding bucket is
367     * retrieved from the symbol comparator. It is the first element of the search order returned
368     * by the symbol comparator. If the comparator does not define a search order for the symbol
369     * the entry is added to the default bucket. If the comparator defines a bucket id
370     * identical to the default bucket id, the default bucket id is shifted to another value.
371     * </p>
372     */
373    private void addToSymbolBucket(Map.Entry<ITaskInstance, V> symbolEntry) {
374        int bucketId = defaultBucket;
375        int[] bucketSearchOrder = getBucketSearchOrder(symbolEntry.getKey());
376       
377        if ((bucketSearchOrder != null) && (bucketSearchOrder.length > 0)) {
378            bucketId = bucketSearchOrder[0];
379           
380            if (bucketId == defaultBucket) {
381                setNewDefaultBucketId();
382            }
383        }
384       
385        List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId);
386       
387        if (list == null) {
388            list = new LinkedList<Map.Entry<ITaskInstance, V>>();
389            symbolBuckets.put(bucketId, list);
390        }
391       
392        list.add(symbolEntry);
393    }
394   
395    /**
396     *
397     */
398    public 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        ITask task = taskInstance.getTask();
403       
404        if (task instanceof IEventTask) {
405            // event tasks are most likely equal to those of the event type with the same name,
406            // Afterwards, they may be equal to iterations, optionals, other event tasks,
407            // selections, and finally the rest.
408            IEventType eventType = ((IEventTask) task).getEventType();
409            return new int[] { eventType.getName().hashCode(), 2, 3, 4, 1 };                       
410        }
411        else if (task instanceof ISequence) {
412            return new int[] { 0, 2, 3, 1 };                       
413        }
414        else if (task instanceof ISelection) {
415            return new int[] { 1, 4, 2, 3 };                       
416        }
417        else if (task instanceof IIteration) {
418            return new int[] { 2, 1, 4 };                       
419        }
420       
421        return null;
422    }
423
424    /**
425     * <p>
426     * Removes the entry for a given symbol from the buckets. It uses the bucket search order
427     * defined by the symbol comparator to find the symbol 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.get(oldDefaultBucket));
470    }
471
472    /**
473     * <p>
474     * searches for the entry belonging to the given symbol. The method either uses the list if
475     * buckets are not used yet, or it uses the buckets and searches them in the order defined
476     * by the comparator. If the symbol isn't found and the comparator does not refer all buckets,
477     * then also the other buckets are searched for the symbol.
478     * </p>
479     */
480    private Map.Entry<ITaskInstance, V> getEntry(ITaskInstance symbol) {
481        Map.Entry<ITaskInstance, V> entry = null;
482        if (symbolBuckets == null) {
483            entry = lookup(symbol, symbolList);
484        }
485        else {
486            int[] bucketSearchOrder = getBucketSearchOrder(symbol);
487            for (int bucketId : bucketSearchOrder) {
488                List<Map.Entry<ITaskInstance, V>> list = symbolBuckets.get(bucketId);
489                if (list != null) {
490                    entry = lookup(symbol, list);
491                    if (entry != null) {
492                        break;
493                    }
494                }
495            }
496           
497            // try to search the other buckets
498            if (entry == null) {
499                Arrays.sort(bucketSearchOrder);
500                for (Map.Entry<Integer, List<Map.Entry<ITaskInstance, V>>> bucket : symbolBuckets.entrySet()) {
501                    if (Arrays.binarySearch(bucketSearchOrder, bucket.getKey()) < 0) {
502                        List<Map.Entry<ITaskInstance, V>> list = bucket.getValue();
503                        if (list != null) {
504                            entry = lookup(symbol, list);
505                            if (entry != null) {
506                                break;
507                            }
508                        }
509                    }
510                }
511            }
512        }
513       
514        return entry;
515    }
516
517    /**
518     * <p>
519     * Convenience method to look up a symbol in a list of entries using the comparator.
520     * </p>
521     */
522    private Map.Entry<ITaskInstance, V> lookup(ITaskInstance symbol, List<Map.Entry<ITaskInstance, V>> list) {
523        for (Map.Entry<ITaskInstance, V> candidate : list) {
524            if (comparator.equals(candidate.getKey(), symbol)) {
525                return candidate;
526            }
527        }
528       
529        return null;
530    }
531
532    /**
533     * <p>
534     * Internally used data structure for storing symbol value pairs
535     * </p>
536     *
537     * @author Patrick Harms
538     */
539    private class SymbolMapEntry implements Map.Entry<ITaskInstance, V> {
540       
541        /**
542         * the symbol to map to a value
543         */
544        private ITaskInstance symbol;
545       
546        /**
547         * the value associated with the symbol
548         */
549        private V value;
550
551        /**
552         * <p>
553         * Simple constructor for initializing the entry with a symbol and its associated value.
554         * </p>
555         */
556        private SymbolMapEntry(ITaskInstance symbol, V value) {
557            super();
558            this.symbol = symbol;
559            this.value = value;
560        }
561
562        /* (non-Javadoc)
563         * @see java.util.Map.Entry#getKey()
564         */
565        @Override
566        public ITaskInstance getKey() {
567            return symbol;
568        }
569
570        /* (non-Javadoc)
571         * @see java.util.Map.Entry#getValue()
572         */
573        @Override
574        public V getValue() {
575            return value;
576        }
577
578        /* (non-Javadoc)
579         * @see java.util.Map.Entry#setValue(java.lang.Object)
580         */
581        @Override
582        public V setValue(V value) {
583            V oldValue = this.value;
584            this.value = value;
585            return oldValue;
586        }
587
588        /* (non-Javadoc)
589         * @see java.lang.Object#hashCode()
590         */
591        @Override
592        public int hashCode() {
593            return symbol.hashCode();
594        }
595
596        /* (non-Javadoc)
597         * @see java.lang.Object#equals(java.lang.Object)
598         */
599        @SuppressWarnings("unchecked")
600        @Override
601        public boolean equals(Object obj) {
602            if (this == obj) {
603                return true;
604            }
605            else if (this.getClass().isInstance(obj)) {
606                SymbolMapEntry other = (SymbolMapEntry) obj;
607                return (symbol.equals(other.symbol) &&
608                        (value == null ? other.value == null : value.equals(other.value)));
609            }
610            else {
611                return false;
612            }
613        }
614
615        /* (non-Javadoc)
616         * @see java.lang.Object#toString()
617         */
618        @Override
619        public String toString() {
620            return symbol + "=" + value;
621        }
622
623    }
624
625    /**
626     * <p>
627     * Used to create an efficient facade for accessing the internal list of entries either only
628     * for the symbols or only for the values. It is a default implementation of the collection
629     * interface. The entry facade provided to the constructor decides, if either the list
630     * accesses only the symbols or only the values.
631     * </p>
632     *
633     * @author Patrick Harms
634     */
635    private class ReadOnlyCollectionFacade<TYPE> implements Collection<TYPE> {
636       
637        /**
638         * the list facaded by this facade
639         */
640        private List<Map.Entry<ITaskInstance, V>> list;
641       
642        /**
643         * the facade to be used for the entries
644         */
645        private EntryFacade<TYPE> entryFacade;
646       
647        /**
648         * <p>
649         * Initializes the facade with the facaded list and the facade to be used for the entries
650         * </p>
651         */
652        private ReadOnlyCollectionFacade(List<Map.Entry<ITaskInstance, V>> list, EntryFacade<TYPE> entryFacade)
653        {
654            this.list = list;
655            this.entryFacade = entryFacade;
656        }
657
658        /* (non-Javadoc)
659         * @see java.util.Collection#size()
660         */
661        @Override
662        public int size() {
663            return list.size();
664        }
665
666        /* (non-Javadoc)
667         * @see java.util.Collection#isEmpty()
668         */
669        @Override
670        public boolean isEmpty() {
671            return list.isEmpty();
672        }
673
674        /* (non-Javadoc)
675         * @see java.util.Collection#contains(java.lang.Object)
676         */
677        @Override
678        public boolean contains(Object o) {
679            if (o == null) {
680                for (Map.Entry<ITaskInstance, V> entry : list) {
681                    if (entryFacade.getFacadedElement(entry) == null) {
682                        return true;
683                    }
684                }
685            }
686            else {
687                for (Map.Entry<ITaskInstance, V> entry : list) {
688                    if (o.equals(entryFacade.getFacadedElement(entry))) {
689                        return true;
690                    }
691                }
692            }
693           
694            return false;
695        }
696
697        /* (non-Javadoc)
698         * @see java.util.Collection#toArray()
699         */
700        @Override
701        public Object[] toArray() {
702            Object[] result = new Object[list.size()];
703           
704            for (int i = 0; i < list.size(); i++) {
705                result[i] = entryFacade.getFacadedElement(list.get(i));
706            }
707           
708            return result;
709        }
710
711        /* (non-Javadoc)
712         * @see java.util.Collection#toArray(T[])
713         */
714        @SuppressWarnings("unchecked")
715        @Override
716        public <T> T[] toArray(T[] a) {
717            T[] result = a;
718           
719            for (int i = 0; i < list.size(); i++) {
720                result[i] = (T) entryFacade.getFacadedElement(list.get(i));
721            }
722           
723            return result;
724        }
725
726        /* (non-Javadoc)
727         * @see java.util.Collection#add(java.lang.Object)
728         */
729        @Override
730        public boolean add(TYPE e) {
731            throw new UnsupportedOperationException("this collection is read only");
732        }
733
734        /* (non-Javadoc)
735         * @see java.util.Collection#remove(java.lang.Object)
736         */
737        @Override
738        public boolean remove(Object o) {
739            throw new UnsupportedOperationException("this collection is read only");
740        }
741
742        /* (non-Javadoc)
743         * @see java.util.Collection#containsAll(java.util.Collection)
744         */
745        @Override
746        public boolean containsAll(Collection<?> c) {
747            for (Object candidate : c) {
748                if (!contains(candidate)) {
749                    return false;
750                }
751            }
752           
753            return true;
754        }
755
756        /* (non-Javadoc)
757         * @see java.util.Collection#addAll(java.util.Collection)
758         */
759        @Override
760        public boolean addAll(Collection<? extends TYPE> c) {
761            throw new UnsupportedOperationException("this collection is read only");
762        }
763
764        /* (non-Javadoc)
765         * @see java.util.Collection#removeAll(java.util.Collection)
766         */
767        @Override
768        public boolean removeAll(Collection<?> c) {
769            throw new UnsupportedOperationException("this collection is read only");
770        }
771
772        /* (non-Javadoc)
773         * @see java.util.Collection#retainAll(java.util.Collection)
774         */
775        @Override
776        public boolean retainAll(Collection<?> c) {
777            throw new UnsupportedOperationException("this collection is read only");
778        }
779
780        /* (non-Javadoc)
781         * @see java.util.Collection#clear()
782         */
783        @Override
784        public void clear() {
785            throw new UnsupportedOperationException("this collection is read only");
786        }
787
788        /* (non-Javadoc)
789         * @see java.util.Collection#iterator()
790         */
791        @Override
792        public Iterator<TYPE> iterator() {
793            return new ReadOnlyCollectionIteratorFacade<TYPE>(list.iterator(), entryFacade);
794        }
795       
796    }
797
798    /**
799     * <p>
800     * Implementation of an iterator to facade an iterator on the internal list of symbol entries.
801     * </p>
802     *
803     * @author Patrick Harms
804     */
805    private class ReadOnlyCollectionIteratorFacade<TYPE> implements Iterator<TYPE> {
806       
807        /**
808         * the facaded iterator
809         */
810        private Iterator<Map.Entry<ITaskInstance, V>> iterator;
811       
812        /**
813         * the facade for the entries provided by the facaded iterator
814         */
815        private EntryFacade<TYPE> entryFacade;
816       
817        /**
818         * <p>
819         * initialized this facade with the facaded iterator and the entry facade to be used for
820         * the entries.
821         * </p>
822         */
823        private ReadOnlyCollectionIteratorFacade(Iterator<Map.Entry<ITaskInstance, V>> iterator,
824                                                 EntryFacade<TYPE>         entryFacade)
825        {
826            this.iterator = iterator;
827            this.entryFacade = entryFacade;
828        }
829
830        /* (non-Javadoc)
831         * @see java.util.Iterator#hasNext()
832         */
833        @Override
834        public boolean hasNext() {
835            return iterator.hasNext();
836        }
837
838        /* (non-Javadoc)
839         * @see java.util.Iterator#next()
840         */
841        @Override
842        public TYPE next() {
843            return entryFacade.getFacadedElement(iterator.next());
844        }
845
846        /* (non-Javadoc)
847         * @see java.util.Iterator#remove()
848         */
849        @Override
850        public void remove() {
851            throw new UnsupportedOperationException("this iterator is read only");
852        }
853       
854    }
855       
856    /**
857     * <p>
858     * Used to facade symbol entries and to return only this part of an entry, that is relevant.
859     * </p>
860     *
861     * @author Patrick Harms
862     */
863    private abstract class EntryFacade<T> {
864       
865        /**
866         * <p>
867         * Returns only the part of an entry that is relevant or required.
868         * </p>
869         *
870         * @param entry of which the part shall be returned
871         *
872         * @return the part of the entry to be returned
873         */
874        protected abstract T getFacadedElement(Entry<ITaskInstance, V> entry);
875       
876    }
877   
878    /**
879     * <p>
880     * Implementation of the entry facade returning the entries key, i.e. the symbol.
881     * </p>
882     *
883     * @author Patrick Harms
884     */
885    private class SymbolFacade extends EntryFacade<ITaskInstance> {
886
887        /* (non-Javadoc)
888         * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry)
889         */
890        @Override
891        protected ITaskInstance getFacadedElement(Entry<ITaskInstance, V> entry) {
892            return entry.getKey();
893        }
894    }
895   
896    /**
897     * <p>
898     * Implementation of the entry facade returning the entries value, i.e. the value associated to
899     * the symbol.
900     * </p>
901     *
902     * @author Patrick Harms
903     */
904    private class ValueFacade extends EntryFacade<V> {
905
906        /* (non-Javadoc)
907         * @see ReadOnlyCollectionIteratorFacade#getFacadedElement(Entry)
908         */
909        @Override
910        protected V getFacadedElement(Entry<ITaskInstance, V> entry) {
911            return entry.getValue();
912        }
913    }
914
915}
Note: See TracBrowser for help on using the repository browser.