001: /*
002: * @(#) CacheMap.java
003: *
004: * Copyright 2002 - 2003 JIDE Software. All rights reserved.
005: */
006: package com.jidesoft.converter;
007:
008: import javax.swing.event.EventListenerList;
009: import java.util.*;
010:
011: /**
012: * <code>CacheMap</code> is a two-level <code>HashMap</code>.
013: * It uses Class as the key and you can map the key to an object and a context as a pair.
014: * We use context because we want to register multiple objects with the same Class.
015: * {@link #register(Class,Object,Object)} is the method to register a new entry. {@link #getRegisteredObject(Class,Object)}
016: * will allow you to look up the object by specifying the Class and the context.
017: */
018: public class CacheMap<T, K> {
019:
020: private HashMap<Class<?>, Cache<K, T>> _cache = new HashMap<Class<?>, Cache<K, T>>();
021:
022: private K _defaultContext; // used for fallback lookup.
023:
024: /**
025: * Constructs a <code>CacheMap</code>.
026: *
027: * @param defaultContext the default context.
028: */
029: public CacheMap(K defaultContext) {
030: _defaultContext = defaultContext;
031: }
032:
033: static class Cache<K, T> extends HashMap<K, T> {
034: public T getObject(K context) {
035: return get(context);
036: }
037:
038: public void setObject(K context, T object) {
039: if (object == null) {
040: remove(context);
041: } else {
042: put(context, object);
043: }
044: }
045: }
046:
047: protected Cache<K, T> getCache(Class<?> clazz) {
048: if (clazz == null) {
049: new IllegalArgumentException("Clazz cannot be null");
050: }
051: return _cache.get(clazz);
052: }
053:
054: /**
055: * Gets the secondary keys that are registered with the class in CacheMap.
056: *
057: * @param clazz the class
058: * @param a the array to receive the keys.
059: * @return the secondary keys.
060: */
061: public K[] getKeys(Class<?> clazz, K[] a) {
062: Cache<K, T> cache = getCache(clazz);
063: if (cache != null) {
064: Set<K> set = cache.keySet();
065: return set.toArray(a);
066: } else {
067: return a;
068: }
069: }
070:
071: protected Cache<K, T> initCache(Class<?> clazz) {
072: Cache<K, T> editors = getCache(clazz);
073: if (editors != null) {
074: return editors;
075: } else {
076: editors = new Cache();
077: _cache.put(clazz, editors);
078: return editors;
079: }
080: }
081:
082: /**
083: * Regsiters an object with the specified clazz and object.
084: *
085: * @param clazz the class which is used as the key.
086: * @param object the object, or the value of the mapping
087: * @param context the secondary key. It is used to register multiple objects to the same primary key (the clazz parameter in this case).
088: */
089: public void register(Class<?> clazz, T object, K context) {
090: if (clazz == null) {
091: throw new IllegalArgumentException(
092: "Parameter clazz cannot be null");
093: }
094:
095: Cache<K, T> cache = initCache(clazz);
096: cache.setObject(context, object);
097: fireRegistrationChanged(new RegistrationEvent(this ,
098: RegistrationEvent.REGISTRATION_ADDED, object, clazz,
099: context));
100: }
101:
102: /**
103: * Unregistered the object associated with the specified class and context.
104: *
105: * @param clazz the class
106: * @param context the context
107: */
108: public void unregister(Class<?> clazz, K context) {
109: Cache<K, T> cache = getCache(clazz);
110: if (cache != null) {
111: Object object = cache.getObject(context);
112: cache.setObject(context, null);
113: fireRegistrationChanged(new RegistrationEvent(this ,
114: RegistrationEvent.REGISTRATION_REMOVED, object,
115: clazz, context));
116: }
117: }
118:
119: /**
120: * Gets registered object from CacheMap. The algorithm used to look up is <BR>
121: * 1. First check for exact match with clazz.<BR>
122: * 2. If didn't find, look for interfaces that clazz implements.<BR>
123: * 3. If still didn't find, look for super class of clazz<BR>
124: * 4. If still didn't find, return null.<BR>
125: * If found a match in step 1, 2, and 3, it will return the registered object immediately.
126: *
127: * @param clazz the class which is used as the primary key.
128: * @param context the context which is used as the secondary key. This parameter could be null in which case the default context is used.
129: * @return registered object the object associated with the class and the context.
130: */
131: public T getRegisteredObject(Class<?> clazz, K context) {
132: if (clazz == null) {
133: return null;
134: }
135:
136: Cache<K, T> cache = getCache(clazz);
137:
138: if (cache == null || !cache.containsKey(context)) {
139: List<Class<?>> classesToSearch = new ArrayList();
140:
141: // Direct superinterfaces, recursively
142: Class<?>[] interfaces = clazz.getInterfaces();
143: for (Class<?> c : interfaces) {
144: classesToSearch.add(c);
145: }
146:
147: // Direct superclass, recursively
148: while (!clazz.isInterface()) {
149: clazz = clazz.getSuperclass();
150: if (clazz != null) {
151: classesToSearch.add(clazz);
152: interfaces = clazz.getInterfaces();
153: for (Class<?> c : interfaces) {
154: classesToSearch.add(c);
155: }
156: } else {
157: break;
158: }
159: }
160:
161: if (!classesToSearch.contains(Object.class)) {
162: classesToSearch.add(Object.class); // use Object as default fallback.
163: }
164:
165: for (Class<?> c : classesToSearch) {
166: cache = getCache(c);
167:
168: if (cache != null) {
169: T object = cache.getObject(context);
170: if (object == null
171: && !_defaultContext.equals(context)) {
172: return getRegisteredObject(c, _defaultContext);
173: }
174: if (object != null) {
175: return object;
176: }
177: }
178: }
179: } else {
180: T object = cache.getObject(context);
181: if (object == null && !_defaultContext.equals(context)) {
182: return getRegisteredObject(clazz, _defaultContext);
183: }
184: if (object != null) {
185: return object;
186: }
187: }
188:
189: return null;
190: }
191:
192: public List<T> getValues() {
193: List<T> list = new ArrayList();
194: Collection<Cache<K, T>> col = _cache.values();
195: for (Cache<K, T> o : col) {
196: Collection<T> col2 = o.values();
197: for (T o2 : col2) {
198: if (!list.contains(o2)) {
199: list.add(o2);
200: }
201: }
202: }
203: return list;
204: }
205:
206: public void clear() {
207: _cache.clear();
208: fireRegistrationChanged(new RegistrationEvent(this ,
209: RegistrationEvent.REGISTRATION_CLEARED));
210: }
211:
212: /**
213: * List of listeners
214: */
215: protected EventListenerList listenerList = new EventListenerList();
216:
217: /**
218: * Adds a listener to the list that's notified each time a change
219: * to the registration occurs.
220: *
221: * @param l the RegistrationListener
222: */
223: public void addRegistrationListener(RegistrationListener l) {
224: listenerList.add(RegistrationListener.class, l);
225: }
226:
227: /**
228: * Removes a listener from the list that's notified each time a
229: * change to the registration occurs.
230: *
231: * @param l the RegistrationListener
232: */
233: public void removeRegistrationListener(RegistrationListener l) {
234: listenerList.remove(RegistrationListener.class, l);
235: }
236:
237: /**
238: * Returns an array of all the registration listeners
239: * registered on this registration.
240: *
241: * @return all of this registration's <code>RegistrationListener</code>s
242: * or an empty array if no registration listeners are currently registered
243: * @see #addRegistrationListener
244: * @see #removeRegistrationListener
245: */
246: public RegistrationListener[] getRegistrationListeners() {
247: return listenerList.getListeners(RegistrationListener.class);
248: }
249:
250: /**
251: * Forwards the given notification event to all
252: * <code>RegistrationListeners</code> that registered
253: * themselves as listeners for this table model.
254: *
255: * @param e the event to be forwarded
256: * @see #addRegistrationListener
257: * @see RegistrationEvent
258: * @see EventListenerList
259: */
260: public void fireRegistrationChanged(RegistrationEvent e) {
261: // Guaranteed to return a non-null array
262: Object[] listeners = listenerList.getListenerList();
263: // Process the listeners last to first, notifying
264: // those that are interested in this event
265: for (int i = listeners.length - 2; i >= 0; i -= 2) {
266: if (listeners[i] == RegistrationListener.class) {
267: ((RegistrationListener) listeners[i + 1])
268: .registrationChanged(e);
269: }
270: }
271: }
272: }
|