Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* @(#)CacheMap.java 5/19/2013
*
* Copyright 2002 - 2013 JIDE Software Inc. All rights reserved.
*/
package com.jidefx.utils;
import jidefx.utils.TypeUtils;
import java.util.*;
/**
* {@code CacheMap} is a two-level {@code 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<>();
private K _defaultContext; // used for fallback lookup.
/**
* Constructs a {@code CacheMap}.
*
* @param defaultContext the default context.
*/
public CacheMap(K defaultContext) {
_defaultContext = defaultContext;
}
static class Cache extends HashMap {
private static final long serialVersionUID = 7764545350468551102L;
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 cache = getCache(clazz);
if (cache != null) {
return cache;
}
else {
cache = new Cache<>();
_cache.put(clazz, cache);
return cache;
}
}
/**
* Registers 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");
}
// register primitive type automatically
if (TypeUtils.isPrimitiveWrapper(clazz)) {
Class> primitiveType = TypeUtils.convertWrapperToPrimitiveType(clazz);
register(primitiveType, object, context);
}
Cache cache = initCache(clazz);
cache.setObject(context, object);
}
/**
* Unregisters 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) {
cache.setObject(context, null);
if (cache.size() == 0) {
_cache.remove(clazz);
}
}
}
/**
* 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);
if (TypeUtils.isPrimitive(clazz)) {
classesToSearch.add(TypeUtils.convertPrimitiveToWrapperType(clazz));
}
else if (TypeUtils.isPrimitiveWrapper(clazz)) {
classesToSearch.add(TypeUtils.convertWrapperToPrimitiveType(clazz));
}
// Direct super interfaces, recursively
addAllInterfaces(classesToSearch, clazz);
Class> superClass = clazz;
// Direct super class, recursively
while (!superClass.isInterface()) {
superClass = superClass.getSuperclass();
if (superClass != null) {
classesToSearch.add(superClass);
addAllInterfaces(classesToSearch, superClass);
}
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;
}
private void addAllInterfaces(List> list, Class> clazz) {
Class>[] interfaces = clazz.getInterfaces();
for (Class> it : interfaces) {
list.add(it);
addAllInterfaces(list, it);
}
}
/**
* Gets the exact match registered object. Different from {@link #getRegisteredObject(Class, Object)} which will try
* different context and super classes and interfaces to find match. This method will do an exact match.
*
* @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 getMatchRegisteredObject(Class> clazz, K context) {
if (clazz == null) {
return null;
}
if (context == null) {
context = _defaultContext;
}
Cache cache = getCache(clazz);
if (cache != null) {
T object = cache.getObject(context);
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;
}
/**
* Remove all registrations for the designated class.
*
* @param clazz the class
*/
@SuppressWarnings("unchecked")
public void remove(Class> clazz) {
Cache cache = getCache(clazz);
if (cache != null) {
Object[] keys = cache.keySet().toArray();
for (Object context : keys) {
cache.setObject((K) context, null);
}
}
_cache.remove(clazz);
}
public void clear() {
_cache.clear();
}
}