/*
* Copyright (c) 2012, grossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.util.reflection;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jowidgets.util.Assert;
import org.jowidgets.util.IIterationCallback;
/**
* This class holds introspection / reflection result inside a cache for a faster access
*/
public final class IntrospectionCache {
private static final Map<Class<?>, Map<String, PropertyDescriptor>> PROPERTIES_CACHE = new ConcurrentHashMap<Class<?>, Map<String, PropertyDescriptor>>();
private static final Map<Class<?>, Map<String, PropertyDescriptor>> PROPERTIES_HIERARCHY_CACHE = new ConcurrentHashMap<Class<?>, Map<String, PropertyDescriptor>>();
private IntrospectionCache() {}
public static void clearCache() {
PROPERTIES_CACHE.clear();
}
/**
* Gets a property decriptors for a specific type from the type hierarchy (including the given type)
*
* @param type The type to get the property descriptor for
* @return The property descriptor as a unmodifiable collection, may be empty but never null
*/
public static Collection<PropertyDescriptor> getPropertyDescriptorsFromHierarchy(final Class<?> type) {
Assert.paramNotNull(type, "type");
final Collection<PropertyDescriptor> result = getPropertyDescriptorsFromHierarchyImpl(type).values();
return Collections.unmodifiableCollection(result);
}
/**
* Gets the property descriptor for a specific type from the type hierarchy (including the given type)
*
* @param type
* @param propertyName
* @return The property decriptor or null, if no such property exists
*/
public static PropertyDescriptor getPropertyDescriptorFromHierarchy(final Class<?> type, final String propertyName) {
Assert.paramNotNull(type, "type");
Assert.paramNotNull(propertyName, "propertyName");
return getPropertyDescriptorsFromHierarchyImpl(type).get(propertyName);
}
/**
* Gets the write method for a defined property in the type hierarchy (including the given type)
*
* @param type
* @param propertyName
* @return The write method, or null if the property does not exist or is readonly
*/
public static Method getWriteMethodFromHierarchy(final Class<?> type, final String propertyName) {
final PropertyDescriptor propertyDescriptor = getPropertyDescriptorFromHierarchy(type, propertyName);
if (propertyDescriptor != null) {
return propertyDescriptor.getWriteMethod();
}
else {
return null;
}
}
/**
* Gets the read method for a defined property in the type hierarchy (including the given type)
*
* @param type
* @param propertyName
* @return The read method, or null if the property does not exist or has no read method
*/
public static Method getReadMethodFromHierarchy(final Class<?> type, final String propertyName) {
final PropertyDescriptor propertyDescriptor = getPropertyDescriptorFromHierarchy(type, propertyName);
if (propertyDescriptor != null) {
return propertyDescriptor.getReadMethod();
}
else {
return null;
}
}
/**
* Gets a property decriptors for a specific type
*
* @param type The type to get the property descriptor for
* @return The property descriptor as a unmodifiable collection, may be empty but never null
*/
public static Collection<PropertyDescriptor> getPropertyDescriptors(final Class<?> type) {
Assert.paramNotNull(type, "type");
final Collection<PropertyDescriptor> result = getPropertyDescriptorsImpl(type).values();
return Collections.unmodifiableCollection(result);
}
/**
* Gets the property descriptor for a specific type.
*
* @param type
* @param propertyName
* @return The property decriptor or null, if no such property exists
*/
public static PropertyDescriptor getPropertyDescriptor(final Class<?> type, final String propertyName) {
Assert.paramNotNull(type, "type");
Assert.paramNotNull(propertyName, "propertyName");
return getPropertyDescriptorsImpl(type).get(propertyName);
}
private static Map<String, PropertyDescriptor> getPropertyDescriptorsFromHierarchyImpl(final Class<?> type) {
Map<String, PropertyDescriptor> properties = PROPERTIES_HIERARCHY_CACHE.get(type);
if (properties == null) {
properties = createPropertyDescriptorsFromHierarchy(type);
PROPERTIES_HIERARCHY_CACHE.put(type, properties);
}
return properties;
}
private static Map<String, PropertyDescriptor> getPropertyDescriptorsImpl(final Class<?> type) {
Map<String, PropertyDescriptor> properties = PROPERTIES_CACHE.get(type);
if (properties == null) {
properties = createPropertyDescriptors(type);
PROPERTIES_CACHE.put(type, properties);
}
return properties;
}
private static Map<String, PropertyDescriptor> createPropertyDescriptorsFromHierarchy(final Class<?> type) {
final Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>();
final IIterationCallback<Class<?>> iterationCalback = new IIterationCallback<Class<?>>() {
@Override
public void next(final Class<?> iterationType) {
result.putAll(getPropertyDescriptorsImpl(iterationType));
}
};
ReflectionUtils.iterateHierarchy(type, iterationCalback);
return result;
}
private static Map<String, PropertyDescriptor> createPropertyDescriptors(final Class<?> type) {
final Map<String, PropertyDescriptor> result = new ConcurrentHashMap<String, PropertyDescriptor>();
try {
for (final PropertyDescriptor descriptor : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
result.put(descriptor.getName(), descriptor);
}
}
catch (final Exception e) {
throw new RuntimeException(e);
}
return result;
}
}