Java tutorial
/* * Copyright 2004-2005 the original author or authors. * * 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 org.codehaus.groovy.grails.commons; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; import org.springframework.util.ReflectionUtils.MethodCallback; /** * Accesses class "properties": static fields, static getters, instance fields * or instance getters. * * Method and Field instances are cached for fast access. * @author Lari Hotari, Sagire Software Oy * @author Graeme Rocher */ public class ClassPropertyFetcher { private final Log log = LogFactory.getLog(getClass()); private final Class<?> clazz; final Map<String, PropertyFetcher> staticFetchers = new HashMap<String, PropertyFetcher>(); final Map<String, PropertyFetcher> instanceFetchers = new HashMap<String, PropertyFetcher>(); private final ReferenceInstanceCallback callback; private PropertyDescriptor[] propertyDescriptors; private static Map<Class<?>, ClassPropertyFetcher> cachedClassPropertyFetchers = new ConcurrentHashMap<Class<?>, ClassPropertyFetcher>(); public static void clearClassPropertyFetcherCache() { cachedClassPropertyFetchers.clear(); } public static ClassPropertyFetcher forClass(Class<?> c) { return forClass(c, null); } public static ClassPropertyFetcher forClass(final Class<?> c, ReferenceInstanceCallback callback) { ClassPropertyFetcher cpf = cachedClassPropertyFetchers.get(c); if (cpf == null) { if (callback == null) { callback = new ReferenceInstanceCallback() { private Object o; public Object getReferenceInstance() { if (o == null) { o = BeanUtils.instantiate(c); } return o; } }; } cpf = new ClassPropertyFetcher(c, callback); cachedClassPropertyFetchers.put(c, cpf); } return cpf; } ClassPropertyFetcher(Class<?> clazz, ReferenceInstanceCallback callback) { this.clazz = clazz; this.callback = callback; init(); } public Object getReference() { if (callback != null) { return callback.getReferenceInstance(); } return null; } public PropertyDescriptor[] getPropertyDescriptors() { return propertyDescriptors; } public boolean isReadableProperty(String name) { return staticFetchers.containsKey(name) || instanceFetchers.containsKey(name); } private void init() { FieldCallback fieldCallback = new ReflectionUtils.FieldCallback() { public void doWith(Field field) { if (field.isSynthetic()) { return; } final int modifiers = field.getModifiers(); if (!Modifier.isPublic(modifiers)) { return; } final String name = field.getName(); if (name.indexOf('$') == -1) { boolean staticField = Modifier.isStatic(modifiers); if (staticField) { staticFetchers.put(name, new FieldReaderFetcher(field, staticField)); } else { instanceFetchers.put(name, new FieldReaderFetcher(field, staticField)); } } } }; MethodCallback methodCallback = new ReflectionUtils.MethodCallback() { public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { if (method.isSynthetic()) { return; } if (!Modifier.isPublic(method.getModifiers())) { return; } if (Modifier.isStatic(method.getModifiers()) && method.getReturnType() != Void.class) { if (method.getParameterTypes().length == 0) { String name = method.getName(); if (name.indexOf('$') == -1) { if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) { name = name.substring(3); } else if (name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2)) && (method.getReturnType() == Boolean.class || method.getReturnType() == boolean.class)) { name = name.substring(2); } PropertyFetcher fetcher = new GetterPropertyFetcher(method, true); staticFetchers.put(name, fetcher); staticFetchers.put(StringUtils.uncapitalize(name), fetcher); } } } } }; List<Class<?>> allClasses = resolveAllClasses(clazz); for (Class<?> c : allClasses) { Field[] fields = c.getDeclaredFields(); for (Field field : fields) { try { fieldCallback.doWith(field); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Shouldn't be illegal to access field '" + field.getName() + "': " + ex); } } Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { try { methodCallback.doWith(method); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Shouldn't be illegal to access method '" + method.getName() + "': " + ex); } } } propertyDescriptors = BeanUtils.getPropertyDescriptors(clazz); for (PropertyDescriptor desc : propertyDescriptors) { Method readMethod = desc.getReadMethod(); if (readMethod != null) { boolean staticReadMethod = Modifier.isStatic(readMethod.getModifiers()); if (staticReadMethod) { staticFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod)); } else { instanceFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod)); } } } } private List<Class<?>> resolveAllClasses(Class<?> c) { List<Class<?>> list = new ArrayList<Class<?>>(); Class<?> currentClass = c; while (currentClass != null) { list.add(currentClass); currentClass = currentClass.getSuperclass(); } Collections.reverse(list); return list; } public Object getPropertyValue(String name) { return getPropertyValue(name, false); } public Object getPropertyValue(final Object object, String name) { PropertyFetcher fetcher = resolveFetcher(name, true); return getPropertyWithFetcherAndCallback(name, fetcher, new ReferenceInstanceCallback() { @Override public Object getReferenceInstance() { return object; } }); } public Object getPropertyValue(String name, boolean onlyInstanceProperties) { PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties); return getPropertyValueWithFetcher(name, fetcher); } private Object getPropertyValueWithFetcher(String name, PropertyFetcher fetcher) { ReferenceInstanceCallback referenceInstanceCallback = callback; return getPropertyWithFetcherAndCallback(name, fetcher, referenceInstanceCallback); } private Object getPropertyWithFetcherAndCallback(String name, PropertyFetcher fetcher, ReferenceInstanceCallback referenceInstanceCallback) { if (fetcher != null) { try { return fetcher.get(referenceInstanceCallback); } catch (Exception e) { log.warn("Error fetching property's " + name + " value from class " + clazz.getName(), e); } } return null; } public <T> T getStaticPropertyValue(String name, Class<T> c) { PropertyFetcher fetcher = staticFetchers.get(name); if (fetcher != null) { Object v = getPropertyValueWithFetcher(name, fetcher); return returnOnlyIfInstanceOf(v, c); } return null; } public <T> T getPropertyValue(String name, Class<T> c) { return returnOnlyIfInstanceOf(getPropertyValue(name, false), c); } @SuppressWarnings("unchecked") private <T> T returnOnlyIfInstanceOf(Object value, Class<T> type) { if ((value != null) && (type == Object.class || GrailsClassUtils.isGroovyAssignableFrom(type, value.getClass()))) { return (T) value; } return null; } private PropertyFetcher resolveFetcher(String name, boolean onlyInstanceProperties) { PropertyFetcher fetcher = null; if (!onlyInstanceProperties) { fetcher = staticFetchers.get(name); } if (fetcher == null) { fetcher = instanceFetchers.get(name); } return fetcher; } public Class<?> getPropertyType(String name) { return getPropertyType(name, false); } public Class<?> getPropertyType(String name, boolean onlyInstanceProperties) { PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties); if (fetcher != null) { return fetcher.getPropertyType(name); } return null; } public static interface ReferenceInstanceCallback { public Object getReferenceInstance(); } static interface PropertyFetcher { public Object get(ReferenceInstanceCallback callback) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException; public Class<?> getPropertyType(String name); } static class GetterPropertyFetcher implements PropertyFetcher { private final Method readMethod; private final boolean staticMethod; GetterPropertyFetcher(Method readMethod, boolean staticMethod) { this.readMethod = readMethod; this.staticMethod = staticMethod; ReflectionUtils.makeAccessible(readMethod); } public Object get(ReferenceInstanceCallback callback) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (staticMethod) { return readMethod.invoke(null); } if (callback != null) { return readMethod.invoke(callback.getReferenceInstance()); } return null; } public Class<?> getPropertyType(String name) { return readMethod.getReturnType(); } } static class FieldReaderFetcher implements PropertyFetcher { private final Field field; private final boolean staticField; public FieldReaderFetcher(Field field, boolean staticField) { this.field = field; this.staticField = staticField; ReflectionUtils.makeAccessible(field); } public Object get(ReferenceInstanceCallback callback) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (staticField) { return field.get(null); } if (callback != null) { return field.get(callback.getReferenceInstance()); } return null; } public Class<?> getPropertyType(String name) { return field.getType(); } } }