1 package org.catacomb.interlish.content;
2
3 import org.catacomb.interlish.structure.*;
4 import org.catacomb.report.E;
5
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.HashSet;
9
10
11 public class KeyedList<V> implements ElementWriter {
12
13 ArrayList<V> items;
14 HashMap<String, V> itemHM;
15
16 Class itemClass;
17
18 ArrayList<String> keyCache;
19
20 HashMap<String, String> shortToFullHM;
21
22 HashSet<String> duplicateShorts;
23
24 int inewid = 0;
25
26 ArrayList<ListWatcher> listWatchers;
27
28 public KeyedList() {
29 items = new ArrayList<V>();
30 itemHM = new HashMap<String, V>();
31 shortToFullHM = new HashMap<String, String>();
32 }
33
34
35 public KeyedList(String s) {
36 this();
37 try {
38 itemClass = Class.forName(s);
39 } catch (Exception ex) {
40 E.error("cant find class " + s + " " + ex);
41 }
42 }
43
44
45 public KeyedList(Class c) {
46 this();
47 itemClass = c;
48 }
49
50
51 public KeyedList(ArrayList<V> elts) {
52 this();
53 addAll(elts);
54 }
55
56 public void add(V v) {
57 addItem(v);
58 }
59
60
61 public void addAll(ArrayList<V> elts) {
62 for (V v : elts) {
63 addItem(v);
64 }
65 }
66
67 public ArrayList<V> getItems(String[] ks) {
68 ArrayList<V> ret = new ArrayList<V>();
69 if (ks != null) {
70 for (String s : ks) {
71 if (hasItem(s)) {
72 ret.add(get(s));
73 }
74 }
75 }
76 return ret;
77 }
78
79 public ArrayList<V> getItems(ArrayList<String> ks) {
80 ArrayList<V> ret = new ArrayList<V>();
81 for (String s : ks) {
82 ret.add(get(s));
83 }
84 return ret;
85 }
86
87
88 public ArrayList<V> getItems() {
89 return items;
90 }
91
92 public void silentAddItem(V obj) {
93 items.add(obj);
94 String sid = addToHM(obj);
95 addKey(sid);
96 reportChange();
97 }
98
99
100 public void addItem(V obj) {
101 silentAddItem(obj);
102 reportChange();
103 }
104
105 private String addToHM(V obj) {
106 String sid = "";
107 if (obj instanceof IDd) {
108 sid = ((IDd)obj).getID();
109 } else {
110 sid = obj.toString();
111 }
112 itemHM.put(sid, obj);
113 keyCache = null;
114 return sid;
115 }
116
117
118 public void remove(V obj) {
119 String sid = "";
120 if (obj instanceof IDd) {
121 sid = ((IDd)obj).getID();
122 } else {
123 sid = obj.toString();
124 }
125 items.remove(obj);
126 itemHM.remove(sid);
127 removeKey(sid);
128 reportChange();
129 }
130
131
132 public void put(String s, V v) {
133 items.add(v);
134 itemHM.put(s, v);
135 keyCache = null;
136 addKey(s);
137 }
138
139
140 public void putNew(String sid, V psc) {
141 if (itemHM.containsKey(sid)) {
142 E.warning("put new tried to override existing item " + sid);
143 } else {
144 put(sid, psc);
145 }
146 }
147
148
149 public boolean hasItem(String s) {
150 return (shortToFullHM.containsKey(s) || itemHM.containsKey(s));
151 }
152
153
154 public V get(String s) {
155 V ret = null;
156 if (itemHM.containsKey(s)) {
157 ret = itemHM.get(s);
158 } else {
159 String sf = getFullID(s);
160 if (itemHM.containsKey(sf)) {
161 ret = itemHM.get(sf);
162 } else {
163 E.error("cant get " + s + " from keyed list");
164 }
165
166 }
167 return ret;
168 }
169
170
171 @SuppressWarnings( { "unchecked" })
172 public V getOrMake(String s) {
173 V ret = null;
174 if (hasItem(s)) {
175 ret = get(s);
176
177 } else if (itemClass != null) {
178 try {
179 Object obj = itemClass.newInstance();
180 ret = (V)obj;
181
182 if (ret instanceof IDable) {
183 ((IDable)ret).setID(s);
184 } else {
185 E.warning("autogenerated items should be IDable " + itemClass);
186 }
187
188 addItem(ret);
189
190 } catch (Exception ex) {
191 E.error("cant make item of type " + itemClass + " " + ex);
192 }
193
194
195 } else {
196 E.error("cant make new item - null classs");
197 }
198
199
200 return ret;
201
202 }
203
204
205 @SuppressWarnings( { "unchecked" })
206 public void superceded(V old) {
207 if (old instanceof Supercedable) {
208 V repl = (V)((Supercedable)old).getSupercessor();
209
210 if (items.contains(old)) {
211 items.remove(old);
212 items.add(repl);
213 } else {
214 E.error("supercedee isnt in list " + old + " " + old.hashCode());
215 for (Object obj : items) {
216 E.info("is in: " + obj + " " + obj.hashCode());
217 }
218 }
219
220 addToHM(repl);
221
222 } else {
223 E.error(" cant supercede non supercedable: " + old);
224 }
225 }
226
227 public void quietSuperceded(V old) {
228 if (items.contains(old)) {
229 superceded(old);
230 }
231
232 }
233
234
235
236 public V getFirst() {
237 return items.get(0);
238 }
239
240
241
242 public Element makeElement(ElementFactory ef, Elementizer eltz) {
243 Element elt = ef.makeElement("KeyedList");
244 for (V obj : getItems()) {
245
246 Element chld = eltz.elementize(obj);
247 ef.addElement(elt, chld);
248 }
249 return elt;
250 }
251
252
253 public ArrayList<String> getKeys() {
254 if (keyCache == null) {
255 keyCache = new ArrayList<String>();
256 keyCache.addAll(itemHM.keySet());
257 }
258 return keyCache;
259 }
260
261
262 public ArrayList<String> getShortKeys() {
263 ArrayList<String> al = new ArrayList<String>();
264 al.addAll(shortToFullHM.keySet());
265 return al;
266 }
267
268
269 public void remove(String s) {
270 if (hasItem(s)) {
271 V val = get(s);
272 items.remove(val);
273 itemHM.remove(s);
274 removeKey(s);
275 reportChange();
276 }
277 }
278
279 private void removeKey(String s) {
280 String ss = s.substring(s.lastIndexOf(".") + 1, s.length());
281 shortToFullHM.remove(ss);
282 keyCache = null;
283 }
284
285
286
287 private void addKey(String s) {
288 String ss = s.substring(s.lastIndexOf(".") + 1, s.length());
289 if (shortToFullHM.containsKey(ss)) {
290
291 E.shortWarning("duplicate items - deleted " +
292 shortToFullHM.get(ss) + " and " + s);
293
294 shortToFullHM.remove(ss);
295 if (duplicateShorts == null) {
296 duplicateShorts = new HashSet<String>();
297 }
298 duplicateShorts.add(ss);
299
300 } else {
301 shortToFullHM.put(ss, s);
302 }
303 }
304
305
306 public boolean hasFullID(String sid) {
307 boolean ret = false;
308 if (sid.indexOf(".") > 0) {
309 ret = itemHM.containsKey(sid);
310 } else {
311 ret = shortToFullHM.containsKey(sid);
312 }
313 return ret;
314 }
315
316
317 public String getFullID(String sid) {
318 String ret = null;
319 if (sid == null) {
320
321 } else {
322 if (sid.indexOf(".") > 0) {
323 ret = sid;
324 } else {
325 if (shortToFullHM.containsKey(sid)) {
326 String s = shortToFullHM.get(sid);
327 if (s.equals("_duplicate_")) {
328 E.shortError("Duplicate short ID - must use full ID " + sid);
329 } else {
330 ret = s;
331 }
332
333 } else {
334 E.error("no such key " + sid);
335 }
336 }
337 }
338 return ret;
339 }
340
341
342 public String printIDs() {
343 StringBuffer sb = new StringBuffer();
344 int iel = 0;
345 for (String s : itemHM.keySet()) {
346 sb.append(s);
347 sb.append(", ");
348 iel += 1;
349 if (iel % 4 == 0) {
350 sb.append("\n");
351 }
352 }
353 return sb.toString();
354 }
355
356
357 public void dump() {
358 for (String s : itemHM.keySet()) {
359 E.info("kl item " + s + " " + itemHM.get(s));
360 }
361 for (String s : shortToFullHM.keySet()) {
362 E.info("short key " + s + " " + shortToFullHM.get(s));
363 }
364 }
365
366
367 public String[] getKeysArray() {
368 return getKeys().toArray(new String[0]);
369 }
370
371 public String[] getShortKeysArray() {
372 return getShortKeys().toArray(new String[0]);
373 }
374
375
376 public ArrayList<V> getDescendants(String rtid) {
377 ArrayList<V> ret = new ArrayList<V>();
378 String fr = rtid + ".";
379 for (String sk : itemHM.keySet()) {
380 if (sk.startsWith(fr)) {
381 ret.add(itemHM.get(sk));
382 }
383 }
384 return ret;
385 }
386
387
388 public String newName(String root) {
389 while (itemHM.containsKey(root + "_" + inewid)) {
390 inewid += 1;
391 }
392 return root + "_" + inewid;
393 }
394
395
396 public void removeListWatcher(ListWatcher lw) {
397 listWatchers.remove(lw);
398 }
399
400
401 public void reportChange() {
402 if (listWatchers != null) {
403 for (ListWatcher lw : listWatchers) {
404 lw.listChanged(this);
405 }
406 }
407 }
408
409 public void addListWatcher(ListWatcher lw) {
410 if (listWatchers == null) {
411 listWatchers = new ArrayList<ListWatcher>();
412 }
413 listWatchers.add(lw);
414 }
415
416
417 public int size() {
418 return items.size();
419 }
420
421
422
423
424 }