001: /* ====================================================================
002: The Jicarilla Software License
003:
004: Copyright (c) 2003 Leo Simons.
005: All rights reserved.
006:
007: Permission is hereby granted, free of charge, to any person obtaining
008: a copy of this software and associated documentation files (the
009: "Software"), to deal in the Software without restriction, including
010: without limitation the rights to use, copy, modify, merge, publish,
011: distribute, sublicense, and/or sell copies of the Software, and to
012: permit persons to whom the Software is furnished to do so, subject to
013: the following conditions:
014:
015: The above copyright notice and this permission notice shall be
016: included in all copies or substantial portions of the Software.
017:
018: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
019: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
020: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
021: IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
022: CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
023: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
024: SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
025: ==================================================================== */
026: package org.jicarilla.lang;
027:
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.Collections;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.AbstractCollection;
036: import java.util.AbstractSet;
037: import java.util.LinkedHashSet;
038: import java.util.ListIterator;
039:
040: /**
041: * A class which supports rich multiway branching using
042: * {@link Selector Selectors}. Note it supports duplicates, unlike most Maps,
043: * and this leads to behaviour that might sometimes be a little unexpected. It
044: * also means the use of {@link #entrySet()} is a bad idea; you are better of
045: * using the {@link #entryList()} which will give you the duplicates as well,
046: * have more natural behaviour, and has a <b>much</b> better performance.
047: *
048: * @author <a href="lsimons at jicarilla dot org">Leo Simons</a>
049: * @version $Id: SelectorSwitch.java,v 1.1 2004/03/23 13:37:58 lsimons Exp $
050: */
051: public class SelectorSwitch implements Switch {
052: protected List m_backend;
053: protected Set m_backendSet;
054: protected Collection m_backendValues;
055: private Set m_backendKeySet;
056:
057: // ----------------------------------------------------------------------
058: // Constructors
059: // ----------------------------------------------------------------------
060:
061: public SelectorSwitch() {
062: this (new ArrayList());
063: }
064:
065: public SelectorSwitch(List backend) {
066: Assert.assertFalse("backend argument may not contain null",
067: backend.contains(null));
068: m_backend = backend;
069: }
070:
071: public SelectorSwitch(Map initialValues) {
072: this ();
073: //if( initialValues != null )
074: //{
075: putAll(initialValues);
076: //}
077: }
078:
079: // ----------------------------------------------------------------------
080: // Interface: Map
081: // ----------------------------------------------------------------------
082:
083: public int size() {
084: return m_backend.size();
085: }
086:
087: public void clear() {
088: m_backend.clear();
089: }
090:
091: public boolean isEmpty() {
092: return m_backend.isEmpty();
093: }
094:
095: public boolean containsKey(Object key) {
096: Iterator it = m_backend.iterator();
097: while (it.hasNext()) {
098: Object next = it.next();
099: //if( next == null )
100: // continue;
101:
102: Entry entry = (Entry) next;
103: Selector selector = entry.m_selector;
104: if (selector.select(key))
105: return true;
106: }
107: return false;
108: }
109:
110: public boolean containsValue(Object value) {
111: Iterator it = m_backend.iterator();
112: while (it.hasNext()) {
113: Object next = it.next();
114: //if( next == null )
115: // continue;
116:
117: Entry entry = (Entry) next;
118: Object val = entry.m_value;
119:
120: if (val == null && value == null)
121: return true;
122:
123: if (val != null && val.equals(value))
124: return true;
125: }
126: return false;
127: }
128:
129: public Collection values() {
130: if (m_backendValues == null)
131: m_backendValues = new ValueCollection();
132:
133: return m_backendValues;
134: }
135:
136: public void putAll(Map t) {
137: Iterator it = t.entrySet().iterator();
138: while (it.hasNext()) {
139: Object next = it.next();
140:
141: Assert.assertNotNull(
142: "This map has an Entry which is null!", next);
143:
144: Map.Entry o = (Map.Entry) next;
145:
146: put(o.getKey(), o.getValue());
147: }
148: }
149:
150: /**
151: * Warning: expensive operation! Use entryList() instead where possible.
152: *
153: * @return
154: */
155: public Set entrySet() {
156: if (m_backendSet == null)
157: m_backendSet = new BackendSet();
158:
159: return m_backendSet;
160: }
161:
162: public Set keySet() {
163: if (m_backendKeySet == null)
164: m_backendKeySet = new BackendKeySet();
165:
166: return m_backendKeySet;
167: }
168:
169: public Object get(Object key) {
170: Iterator it = m_backend.iterator();
171: while (it.hasNext()) {
172: Object next = it.next();
173: //if( next == null )
174: // continue;
175:
176: Entry entry = (Entry) next;
177: Selector selector = entry.m_selector;
178: if (selector.select(key))
179: return entry.m_value;
180: }
181: return null;
182: }
183:
184: public Object remove(Object key) {
185: Iterator it = m_backend.iterator();
186: while (it.hasNext()) {
187: Object next = it.next();
188: //if( next == null )
189: // continue;
190:
191: Entry entry = (Entry) next;
192: Object val = entry.m_value;
193: Selector selector = entry.m_selector;
194:
195: //if( selector == null )
196: // continue;
197:
198: if (selector != null && selector.select(key)) {
199: it.remove();
200: return val;
201: }
202: }
203: return null;
204: }
205:
206: public Object put(Object key, Object value) {
207: Selector selector;
208: if (key instanceof Selector || key == null)
209: selector = (Selector) key;
210: else
211: selector = new EquivalenceSelector(key);
212:
213: Object oldValue = get(key);
214:
215: Entry entry = new Entry(selector, value);
216: m_backend.add(entry);
217:
218: return oldValue;
219: }
220:
221: public boolean equals(Object o) {
222: if (o == this )
223: return true;
224:
225: if (!(o instanceof Switch))
226: return false;
227:
228: return m_backend.equals(((Switch) o).entryList());
229: }
230:
231: public int hashCode() {
232: return m_backend.hashCode() + 1;
233: }
234:
235: // ----------------------------------------------------------------------
236: // Interface: Switch
237: // ----------------------------------------------------------------------
238:
239: public Set criteriaSet() {
240: Iterator it = m_backend.iterator();
241: Set criteriaSet = new LinkedHashSet();
242:
243: while (it.hasNext()) {
244: Object next = it.next();
245: //if( next == null )
246: // continue;
247:
248: Entry entry = (Entry) next;
249: Selector selector = entry.m_selector;
250:
251: if (selector instanceof CriterionExposingSelector) {
252: CriterionExposingSelector s = (CriterionExposingSelector) selector;
253: criteriaSet.add(s.getCriterion());
254: } else {
255: criteriaSet.add(selector);
256: }
257: }
258:
259: return Collections.unmodifiableSet(criteriaSet);
260: }
261:
262: public List entryList() {
263: return m_backend;
264: }
265:
266: // ----------------------------------------------------------------------
267: // Inner Class: Entry
268: // ----------------------------------------------------------------------
269:
270: /**
271: * Helper class to build our hashmap-like behaviour.
272: */
273: protected static class Entry implements Switch.Entry {
274: /** This entry its selector. */
275: protected final Selector m_selector;
276: /** This entry its sink. */
277: protected Object m_value;
278:
279: /**
280: * Construct a new instance with the specified selector and value.
281: *
282: * @param selector this entry its selector
283: * @param value this entry its value
284: */
285: public Entry(final Selector selector, final Object value) {
286: Assert.assertNotNull("selector argument may not be null",
287: selector);
288: m_selector = selector;
289: m_value = value;
290: }
291:
292: public Selector getSelector() {
293: return m_selector;
294: }
295:
296: public int hashCode() {
297: int key1 = 0;
298: //if( m_selector != null )
299: key1 = m_selector.hashCode();
300:
301: int key2 = 0;
302: if (m_value != null)
303: key2 = m_value.hashCode();
304:
305: return key1 ^ key2;
306: }
307:
308: public Object getKey() {
309: return m_selector;
310: }
311:
312: public Object getValue() {
313: return m_value;
314: }
315:
316: public boolean equals(Object o) {
317: if (o == null)
318: return false;
319: if (!(o instanceof Switch.Entry))
320: return false;
321:
322: Switch.Entry entry = (Switch.Entry) o;
323: Selector selector = entry.getSelector();
324: Object value = entry.getValue();
325:
326: if (selector == null)
327: return false;
328: if (m_value == null && value != null)
329: return false;
330:
331: return m_selector.equals(selector) && m_value.equals(value);
332: }
333:
334: public Object setValue(Object value) {
335: Object temp = m_value;
336: m_value = value;
337: return temp;
338: }
339: }
340:
341: // ----------------------------------------------------------------------
342: // Inner Classes: Sets & Collections
343: // ----------------------------------------------------------------------
344:
345: protected class BackendSet extends AbstractSet {
346: public int size() {
347: return (new LinkedHashSet(m_backend)).size();
348: }
349:
350: public Iterator iterator() {
351: return new BackendSetIterator();
352: }
353: }
354:
355: protected class BackendKeySet extends AbstractSet {
356: public int size() {
357: return (new LinkedHashSet(m_backend)).size();
358: }
359:
360: public Iterator iterator() {
361: return new BackendKeySetIterator();
362: }
363: }
364:
365: protected class ValueCollection extends AbstractCollection {
366: public int size() {
367: return m_backend.size();
368: }
369:
370: public Iterator iterator() {
371: return new ValueCollectionIterator();
372: }
373:
374: }
375:
376: // ----------------------------------------------------------------------
377: // Inner Classes: Iterators
378: // ----------------------------------------------------------------------
379:
380: protected class BackendSetIterator implements Iterator {
381: protected Set m_backingSet = new LinkedHashSet(m_backend);
382: protected Iterator m_backingIterator = m_backingSet.iterator();
383:
384: protected Object m_cache;
385:
386: public void remove() {
387: m_backingIterator.remove();
388:
389: // the corresponding item in the backing list will
390: // always be the _last_ one. We will need to remove
391: // that as well. Expensive!
392:
393: ListIterator it = m_backend.listIterator();
394: while (it.hasNext()) {
395: it.next();
396: }
397:
398: while (true) // it.hasPrevious() is redundant
399: // because the entry should always
400: // be there
401: {
402: Entry entry = (Entry) it.previous();
403: if (entry.equals(m_cache)) {
404: it.remove();
405: m_cache = null; // loose the reference in case
406: // user does something real
407: // stupid
408: break;
409: }
410: }
411:
412: }
413:
414: public boolean hasNext() {
415: return m_backingIterator.hasNext();
416: }
417:
418: public Object next() {
419: m_cache = m_backingIterator.next();
420: return m_cache;
421: }
422:
423: //return (new LinkedHashSet( m_backend )).iterator();
424: }
425:
426: protected class BackendKeySetIterator implements Iterator {
427: protected Iterator m_backingIterator = m_backend.iterator();
428:
429: public void remove() {
430: m_backingIterator.remove();
431: }
432:
433: public boolean hasNext() {
434: return m_backingIterator.hasNext();
435: }
436:
437: public Object next() {
438: Object next = m_backingIterator.next();
439: //while( next == null && m_backingIterator.hasNext() )
440: //{
441: // next = m_backingIterator.next();
442: //}
443: //if( next == null )
444: // return null;
445:
446: return ((Entry) next).getSelector();
447: }
448: }
449:
450: protected class ValueCollectionIterator implements Iterator {
451: protected Iterator m_backingIterator = m_backend.iterator();
452:
453: public void remove() {
454: m_backingIterator.remove();
455: }
456:
457: public boolean hasNext() {
458: return m_backingIterator.hasNext();
459: }
460:
461: public Object next() {
462: Object next = m_backingIterator.next();
463: //while( next == null && m_backingIterator.hasNext() )
464: //{
465: // next = m_backingIterator.next();
466: //}
467: //if( next == null )
468: // return null;
469:
470: return ((Entry) next).getValue();
471: }
472: }
473: }
|