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

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