1package autotest.tko;
2
3import autotest.tko.TkoUtils.FieldInfo;
4
5import java.util.AbstractCollection;
6import java.util.ArrayList;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.List;
10import java.util.Map;
11
12/**
13 * A modifiable, ordered Collection of unique HeaderFields indexed by both field name and field SQL
14 * name.
15 */
16public class HeaderFieldCollection extends AbstractCollection<HeaderField> {
17    private Map<String, HeaderField> fieldsByName = new HashMap<String, HeaderField>();
18    private Map<String, HeaderField> fieldsBySqlName = new HashMap<String, HeaderField>();
19    private List<HeaderField> orderedFields = new ArrayList<HeaderField>();
20
21    public void populateFromList(String fieldListName) {
22        for (FieldInfo fieldInfo : TkoUtils.getFieldList(fieldListName)) {
23            HeaderField field = new SimpleHeaderField(fieldInfo.name, fieldInfo.field);
24            add(field);
25        }
26    }
27
28    @Override
29    public boolean add(HeaderField field) {
30        if (contains(field)) {
31            return false;
32        }
33
34        orderedFields.add(field);
35        fieldsByName.put(field.getName(), field);
36        fieldsBySqlName.put(field.getSqlName(), field);
37        assert checkConsistency();
38        return true;
39    }
40
41    /**
42     * Called only within an assertion.
43     */
44    public boolean checkConsistency() {
45        assert fieldsByName.size() == fieldsBySqlName.size();
46        assert fieldsByName.size() == orderedFields.size();
47        for (HeaderField field : fieldsByName.values()) {
48            assert fieldsByName.get(field.getName()) == field;
49            assert fieldsBySqlName.get(field.getSqlName()) == field;
50            assert orderedFields.contains(field);
51        }
52        return true;
53    }
54
55    /**
56     * We perform strict input checking here, and both add() and remove() use this.
57     */
58    @Override
59    public boolean contains(Object o) {
60        if (o == null || !(o instanceof HeaderField)) {
61            return false;
62        }
63
64        HeaderField field = (HeaderField) o;
65        boolean containsName = fieldsByName.containsKey(field.getName());
66        boolean containsSqlName = fieldsBySqlName.containsKey(field.getSqlName());
67
68        if (containsName && containsSqlName) {
69            return true;
70        }
71        if (!containsName && containsSqlName) {
72            throw new IllegalArgumentException("Duplicate SQL name: " + field + ", "
73                                               + fieldsBySqlName.get(field.getSqlName()));
74        }
75        if (containsName && !containsSqlName) {
76            throw new IllegalArgumentException("Duplicate name: " + field + ", "
77                                               + fieldsByName.get(field.getName()));
78        }
79        return false;
80    }
81
82    @Override
83    public Iterator<HeaderField> iterator() {
84        final Iterator<HeaderField> baseIterator = orderedFields.iterator();
85        return new Iterator<HeaderField>() {
86            HeaderField lastElement;
87
88            @Override
89            public boolean hasNext() {
90                return baseIterator.hasNext();
91            }
92
93            @Override
94            public HeaderField next() {
95                lastElement = baseIterator.next();
96                return lastElement;
97            }
98
99            @Override
100            public void remove() {
101                baseIterator.remove();
102                fieldsByName.remove(lastElement.getName());
103                fieldsBySqlName.remove(lastElement.getSqlName());
104                assert checkConsistency();
105            }
106        };
107    }
108
109    @Override
110    public int size() {
111        return fieldsByName.size();
112    }
113
114    public HeaderField getFieldByName(String name) {
115        assert fieldsByName.containsKey(name) : name;
116        return fieldsByName.get(name);
117    }
118
119    public HeaderField getFieldBySqlName(String sqlName) {
120        assert fieldsBySqlName.containsKey(sqlName) : sqlName;
121        return fieldsBySqlName.get(sqlName);
122    }
123
124    public boolean containsName(String name) {
125        return fieldsByName.containsKey(name);
126    }
127
128    public boolean containsSqlName(String sqlName) {
129        return fieldsBySqlName.containsKey(sqlName);
130    }
131
132    /**
133     * Note this is O(n).
134     */
135    @Override
136    public boolean remove(Object o) {
137        if (!contains(o)) {
138            return false;
139        }
140
141        HeaderField field = (HeaderField) o;
142        orderedFields.remove(field);
143        fieldsByName.remove(field.getName());
144        fieldsBySqlName.remove(field.getSqlName());
145        return true;
146    }
147}
148