com.googlecode.ehcache.annotations.key.AbstractDeepCacheKeyGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache-spring-annotations Show documentation
Show all versions of ehcache-spring-annotations Show documentation
Provides a simple model for integrating Ehcache in a Spring project via annotations.
/**
* Copyright 2010 Nicholas Blair, Eric Dalquist
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.ehcache.annotations.key;
import java.io.Serializable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import com.googlecode.ehcache.annotations.util.guice.ReferenceMap;
import com.googlecode.ehcache.annotations.util.guice.ReferenceType;
/**
* Base class for key generators that do deep inspection of the key data for generation. Arrays,
* {@link Iterable} and {@link Map} are iterated over and their values recursively inspected. Also
* supports reflective recursion which can be useful for objects that may not support the hashCode,
* equals or other methods required by the key generation implementation.
*
* Reflective recursion add significant overhead to the deep inspection process.
*
* @author Eric Dalquist
* @version $Revision: 350 $
*/
public abstract class AbstractDeepCacheKeyGenerator extends AbstractCacheKeyGenerator {
static final Map, Set> IMPLEMENTS_CACHE = new ReferenceMap, Set>(ReferenceType.WEAK, ReferenceType.STRONG);
private enum ImplementsMethod {
HASH_CODE,
EQUALS,
TO_STRING;
}
private boolean useReflection = false;
public AbstractDeepCacheKeyGenerator() {
super();
}
public AbstractDeepCacheKeyGenerator(boolean includeMethod, boolean includeParameterTypes) {
super(includeMethod, includeParameterTypes);
}
public final boolean isUseReflection() {
return this.useReflection;
}
/**
* Determines if reflection should be used on each object that is used in key generation. If true
* each object that is added to the key has {@link #shouldReflect(Object)} called on it. If that
* returns true reflection is used to recurse on all of the fields of the object.
*
* @param Defaults to false.
*/
public final void setUseReflection(boolean useReflection) {
this.useReflection = useReflection;
}
/* (non-Javadoc)
* @see com.googlecode.ehcache.annotations.key.AbstractCacheKeyGenerator#generateKey(java.lang.Object[])
*/
@Override
public final T generateKey(Object... data) {
final G generator = this.getGenerator(data);
this.deepHashCode(generator, data);
return this.generateKey(generator);
}
/**
* Calls {@link #deepHashCode(KeyGenerationStream, Object)} on each element in the array.
* @param a will never be null
*/
protected void deepHashCode(G generator, Object a[]) {
this.beginRecursion(generator, a);
for (final Object element : a) {
this.deepHashCode(generator, element);
}
this.endRecursion(generator, a);
}
/**
* Calls {@link #deepHashCode(KeyGenerationStream, Object)} on each element in the {@link Iterable}
* @param a will never be null
*/
protected void deepHashCode(G generator, Iterable> a) {
this.beginRecursion(generator, a);
for (final Object element : a) {
this.deepHashCode(generator, element);
}
this.endRecursion(generator, a);
}
/**
* Calls {@link #deepHashCode(KeyGenerationStream, Object)} on both the key and the value.
* @param a will never be null
*/
protected void deepHashCode(G generator, Map.Entry, ?> e) {
this.beginRecursion(generator, e);
this.deepHashCode(generator, e.getKey());
this.deepHashCode(generator, e.getValue());
this.endRecursion(generator, e);
}
/**
* Does instanceof checks to determine the correct {@link #deepHashCode} method to call or
* {@link #write(KeyGenerationStream, Object)} is called if no other recursion is needed or
* {@link #reflectionDeepHashCode(Object, Object)} is called if {@link #setUseReflection(boolean)}
* is true.
*/
protected final void deepHashCode(G generator, Object element) {
if (element == null) {
this.appendNull(generator);
return;
}
if (!register(element)) {
this.appendGraphCycle(generator, element);
return;
}
try {
if (element instanceof byte[])
this.append(generator, (byte[]) element);
else if (element instanceof short[])
this.append(generator, (short[]) element);
else if (element instanceof int[])
this.append(generator, (int[]) element);
else if (element instanceof long[])
this.append(generator, (long[]) element);
else if (element instanceof char[])
this.append(generator, (char[]) element);
else if (element instanceof float[])
this.append(generator, (float[]) element);
else if (element instanceof double[])
this.append(generator, (double[]) element);
else if (element instanceof boolean[])
this.append(generator, (boolean[]) element);
else if (element instanceof Object[])
this.deepHashCode(generator, (Object[]) element);
else if (element instanceof Iterable>)
this.deepHashCode(generator, (Iterable>)element);
else if (element instanceof Map, ?>)
this.deepHashCode(generator, ((Map, ?>)element).entrySet());
else if (element instanceof Map.Entry, ?>)
this.deepHashCode(generator, (Map.Entry, ?>)element);
else if (this.useReflection)
this.reflectionDeepHashCode(generator, element);
else
this.append(generator, element);
}
finally {
unregister(element);
}
}
/**
* Calls {@link #shouldReflect(Object)} to determine if the object needs to be reflected on to
* generate a good key. If so {@link AccessibleObject#setAccessible(AccessibleObject[], boolean)} is
* used to enable access to private, protected and default fields. Each non-transient, non-static field
* has {@link #deepHashCode(Object, Object)} called on it.
*/
protected final void reflectionDeepHashCode(G generator, final Object element) {
//Special objects which shouldn't be reflected on due to lack of interesting fields
if (element instanceof Class>) {
this.append(generator, element);
return;
}
//Determine if the element should be reflected on
if (!this.shouldReflect(element)) {
this.append(generator, element);
return;
}
//Accumulate the data that makes up the object being reflected on so it can be recursed on as a single grouping of data
final List