com.jidesoft.converter.CacheMap Maven / Gradle / Ivy
/*
* @(#) CacheMap.java
*
* Copyright 2002 - 2003 JIDE Software. All rights reserved.
*/
package com.jidesoft.converter;
import javax.swing.event.EventListenerList;
import java.util.*;
/**
* CacheMap
is a two-level HashMap
. It uses Class as the key and you can
* map the key to an object and a context as a pair. We use context because we want to register
* multiple objects with the same Class. {@link #register(Class,Object,Object)} is the method to
* register a new entry. {@link #getRegisteredObject(Class,Object)} will allow you to look up the
* object by specifying the Class and the context.
*/
public class CacheMap {
private HashMap, Cache> _cache = new HashMap, Cache>();
private K _defaultContext; // used for fallback lookup.
/**
* Constructs a CacheMap
.
*
* @param defaultContext the default context.
*/
public CacheMap(K defaultContext) {
_defaultContext = defaultContext;
}
static class Cache extends HashMap {
public T getObject(K context) {
return get(context);
}
public void setObject(K context, T object) {
if (object == null) {
remove(context);
}
else {
put(context, object);
}
}
}
protected Cache getCache(Class> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Clazz cannot be null");
}
return _cache.get(clazz);
}
/**
* Gets the secondary keys that are registered with the class in CacheMap.
*
* @param clazz the class
* @param a the array to receive the keys.
*
* @return the secondary keys.
*/
public K[] getKeys(Class> clazz, K[] a) {
Cache cache = getCache(clazz);
if (cache != null) {
Set set = cache.keySet();
return set.toArray(a);
}
else {
return a;
}
}
protected Cache initCache(Class> clazz) {
Cache editors = getCache(clazz);
if (editors != null) {
return editors;
}
else {
editors = new Cache();
_cache.put(clazz, editors);
return editors;
}
}
/**
* Regsiters an object with the specified clazz and object.
*
* @param clazz the class which is used as the key.
* @param object the object, or the value of the mapping
* @param context the secondary key. It is used to register multiple objects to the same primary
* key (the clazz parameter in this case).
*/
public void register(Class> clazz, T object, K context) {
if (clazz == null) {
throw new IllegalArgumentException("Parameter clazz cannot be null");
}
Cache cache = initCache(clazz);
cache.setObject(context, object);
fireRegistrationChanged(new RegistrationEvent(this, RegistrationEvent.REGISTRATION_ADDED, object, clazz, context));
}
/**
* Unregistered the object associated with the specified class and context.
*
* @param clazz the class
* @param context the context
*/
public void unregister(Class> clazz, K context) {
Cache cache = getCache(clazz);
if (cache != null) {
Object object = cache.getObject(context);
cache.setObject(context, null);
fireRegistrationChanged(new RegistrationEvent(this, RegistrationEvent.REGISTRATION_REMOVED, object, clazz, context));
}
}
/**
* Gets registered object from CacheMap. The algorithm used to look up is
1. First check
* for exact match with clazz and context.
2. If didn't find, look for interfaces that clazz
* implements using the exact context.
3. If still didn't find, look for super class of
* clazz using the exact context.
4. If still didn't find, using the exact clazz with
* default context.
5. If still didn't find, return null.
If found a match in step 1, 2,
* 3 or 4, it will return the registered object immediately.
*
* @param clazz the class which is used as the primary key.
* @param context the context which is used as the secondary key. This parameter could be null
* in which case the default context is used.
*
* @return registered object the object associated with the class and the context.
*/
public T getRegisteredObject(Class> clazz, K context) {
if (clazz == null) {
return null;
}
Cache cache = getCache(clazz);
if (cache == null || !cache.containsKey(context)) {
List> classesToSearch = new ArrayList();
classesToSearch.add(clazz);
// Direct superinterfaces, recursively
Class>[] interfaces = clazz.getInterfaces();
for (Class> c : interfaces) {
classesToSearch.add(c);
}
Class superClass = clazz;
// Direct superclass, recursively
while (!superClass.isInterface()) {
superClass = superClass.getSuperclass();
if (superClass != null) {
classesToSearch.add(superClass);
interfaces = superClass.getInterfaces();
for (Class> c : interfaces) {
classesToSearch.add(c);
}
}
else {
break;
}
}
if (!classesToSearch.contains(Object.class)) {
classesToSearch.add(Object.class); // use Object as default fallback.
}
// search to match context first
for (Class> c : classesToSearch) {
Cache cacheForClass = getCache(c);
if (cacheForClass != null) {
T object = cacheForClass.getObject(context);
if (object != null) {
return object;
}
}
}
// fall back to default context
if (!_defaultContext.equals(context)) {
for (Class> c : classesToSearch) {
Cache cacheForClass = getCache(c);
if (cacheForClass != null) {
T object = cacheForClass.getObject(_defaultContext);
if (object != null) {
return object;
}
}
}
}
}
if (cache != null) {
T object = cache.getObject(context);
if (object == null && !_defaultContext.equals(context)) {
return getRegisteredObject(clazz, _defaultContext);
}
if (object != null) {
return object;
}
}
return null;
}
public List getValues() {
List list = new ArrayList();
Collection> col = _cache.values();
for (Cache o : col) {
Collection col2 = o.values();
for (T o2 : col2) {
if (!list.contains(o2)) {
list.add(o2);
}
}
}
return list;
}
public void clear() {
_cache.clear();
fireRegistrationChanged(new RegistrationEvent(this, RegistrationEvent.REGISTRATION_CLEARED));
}
/**
* List of listeners
*/
protected EventListenerList listenerList = new EventListenerList();
/**
* Adds a listener to the list that's notified each time a change to the registration occurs.
*
* @param l the RegistrationListener
*/
public void addRegistrationListener(RegistrationListener l) {
listenerList.add(RegistrationListener.class, l);
}
/**
* Removes a listener from the list that's notified each time a change to the registration
* occurs.
*
* @param l the RegistrationListener
*/
public void removeRegistrationListener(RegistrationListener l) {
listenerList.remove(RegistrationListener.class, l);
}
/**
* Returns an array of all the registration listeners registered on this registration.
*
* @return all of this registration's RegistrationListener
s or an empty array if no
* registration listeners are currently registered
*
* @see #addRegistrationListener
* @see #removeRegistrationListener
*/
public RegistrationListener[] getRegistrationListeners() {
return listenerList.getListeners(RegistrationListener.class);
}
/**
* Forwards the given notification event to all RegistrationListeners
that
* registered themselves as listeners for this table model.
*
* @param e the event to be forwarded
*
* @see #addRegistrationListener
* @see RegistrationEvent
* @see EventListenerList
*/
public void fireRegistrationChanged(RegistrationEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == RegistrationListener.class) {
((RegistrationListener) listeners[i + 1]).registrationChanged(e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy