Mega Code Archive

 
Categories / Java / Reflection
 

Fast Property accessor for a single class

/**  * Copyright (C) 2010 altuure <altuure [AT] gmail [DOT] com> http://www.altuure.com/projects/yagdao  *  * 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.altuure.yagdao.common; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /**  * Fast Property accessor for a single class.  *   * @author altuure  */ @SuppressWarnings("rawtypes") public class FastPropertyUtil {   public static final Log log = LogFactory.getLog(FastPropertyUtil.class);   /**    * init setter and getter lazy.    */   public static final boolean LAZY = true;   /**    * Get method prefix.    */   private static final String GET = "get";   /**    * is method prefix.    */   private static final String IS = "is";   /**    * set method prefix.    */   private static final String SET = "set";   /**    * set method cache.    */   private Map<String, NestedHandler> setters = new HashMap<String, NestedHandler>();   /**    * getter method cache.    */   private Map<String, NestedHandler> getters = new HashMap<String, NestedHandler>();   /**    * wrapped class .    */   private Class adaptedClass;   /**    * create new instance for given class.    *     * @see #LAZY    * @param adaptedClass    *            wrapped class    */   public FastPropertyUtil(Class adaptedClass) {     this(adaptedClass, LAZY);   }   /**    * create new instance for given class .    *     * @param adaptedClass    *            wrapped class    * @param initLazy    *            true if init lazy or false init now    */   public FastPropertyUtil(Class adaptedClass, boolean initLazy) {     super();     this.adaptedClass = adaptedClass;     init(initLazy);   }   /**    * set object property value.    *     * @param obj    *            object    * @param property    *            property    * @param value    *            value    * @throws IllegalArgumentException    *             underlying exception    * @see {@link Method#invoke(Object, Object...)}    * @throws IllegalAccessException    *             underlying exception    * @see {@link Method#invoke(Object, Object...)}    * @throws InvocationTargetException    *             underlying exception    * @throws InstantiationException    *             new instance error    * @see {@link Method#invoke(Object, Object...)}    */   public void set(Object obj, String property, Object value)       throws IllegalArgumentException, IllegalAccessException,       InvocationTargetException, InstantiationException {     if (setters.containsKey(property)) {       NestedHandler member = setters.get(property);       member.set(obj, value);     } else {       NestedHandler nestedHandler = new NestedHandler(property, true);       setters.put(property, nestedHandler);       nestedHandler.set(obj, value);     }   }   /**    * get object property value.    *     * @param obj    *            object    * @param property    *            property    * @return value    * @throws IllegalArgumentException    *             underlying exception    * @see {@link Method#invoke(Object, Object...)}    * @throws IllegalAccessException    *             underlying exception    * @see {@link Method#invoke(Object, Object...)}    * @throws InvocationTargetException    *             underlying exception    * @see {@link Method#invoke(Object, Object...)}    */   public Object get(Object obj, String property)       throws IllegalArgumentException, IllegalAccessException,       InvocationTargetException {     if (getters.containsKey(property)) {       NestedHandler member = getters.get(property);       return member.get(obj);     } else {       NestedHandler nestedHandler = new NestedHandler(property, false);       getters.put(property, nestedHandler);       return nestedHandler.get(obj);     }   }   /**    * goes through fields and initialize caches setters and getters.    *     * @param initLazy    *            is lazy    */   private void init(boolean initLazy) {     if (initLazy)       return;     Field[] fields = adaptedClass.getFields();     for (Field field : fields) {       field.setAccessible(true);       setters.put(field.getName(), new NestedHandler(field));       getters.put(field.getName(), new NestedHandler(field));     }     Method[] methods = adaptedClass.getMethods();     for (Method method : methods) {       if (isPropertyAccessor(method))         continue;// not a property accessor;       String propertyName = getPropertyAccessor(method);       if (propertyName != null)         getters.put(propertyName, new NestedHandler(method));       propertyName = setPropertyAccessor(method);       if (propertyName != null)         setters.put(propertyName, new NestedHandler(method));     }   }   /**    * return a property name for setter methods .    *     * @param method    *            method    * @return null if not starts with set    */   private String setPropertyAccessor(Method method) {     return propertyAccessor(method, SET);   }   /**    * return a property name for getter methods .    *     * @param method    *            method    * @return null if not starts with is or get    */   private String getPropertyAccessor(Method method) {     return propertyAccessor(method, IS, GET);   }   /**    * check if the method starts with given prefix and if yes retriev the    * property name.    *     * @param method    *            method    * @param prefixs    *            potential prefix s    * @return null is not available    */   private String propertyAccessor(Method method, String... prefixs) {     String name = method.getName();     String property;     String prefix = null;     for (String prefixCandidate : prefixs) {       if (name.startsWith(prefixCandidate)) {         prefix = prefixCandidate;         break;       }     }     // if not qualified accessor return null     if (prefix == null)       return null;     if (name.length() < prefix.length() + 1)       return null;     property = name.substring(prefix.length(), prefix.length() + 1)         .toLowerCase(Locale.ENGLISH);     if (name.length() > prefix.length() + 2)       property += name.substring(prefix.length() + 1);     return property;   }   /**    * checks if there is any valid prefix: is,get,get.    *     * @param method    *            method to be check    * @return true if there is any parameter    */   private boolean isPropertyAccessor(Method method) {     String name = method.getName();     return name.startsWith(IS) || name.startsWith(GET)         || name.startsWith(SET);   }   /**    * property handle property.    *     * @author altuure    */   public class NestedHandler {     /**      * list of access members      */     private List<Member> members;     private List<Member> setMembers;     /**      * single method handler.      *       * @param member      *            delegated member      */     public NestedHandler(Member member) {       members = new ArrayList<Member>(1);       members.add(member);     }     /**      * create instance for given class for given property.      *       * @param property      *            property eg: name or product.name      * @param writeOperation      *            true if set else false      */     public NestedHandler(String property, boolean writeOperation) {       initThis(writeOperation, property);     }     /**      * parse property and find access method.      *       * @param writeOperation      *            true if set else false      * @param property      *            property      */     private void initThis(boolean writeOperation, String property) {       String[] split = splitDot(property);       members = new ArrayList<Member>(split.length - 1);       setMembers = new ArrayList<Member>(split.length - 1);       Class currentClass = adaptedClass;       for (int i = 0; i < split.length - 1; i++) {         String string = split[i];         Member member;         member = findAccessor(currentClass, i, string, GET, IS);         Member setMember = findAccessor(currentClass, i, string, SET);         if (member == null)           throw new IllegalArgumentException(               "no such property found " + currentClass.getName()                   + "." + string);         members.add(member);         setMembers.add(setMember);         if (member instanceof Method) {           Method m = (Method) member;           currentClass = m.getReturnType();         } else if (member instanceof Field) {           Field m = (Field) member;           currentClass = m.getType();         }       }       Member lastMember;       String lastProperty = split[split.length - 1];       if (writeOperation) {         lastMember = findAccessor(currentClass, split.length - 1,             lastProperty, SET);       } else {         lastMember = findAccessor(currentClass, split.length - 1,             lastProperty, GET, IS);       }       if (lastMember == null)         throw new IllegalArgumentException("no such property found "             + currentClass.getName() + "." + lastProperty);       members.add(lastMember);     }     /**      * find access method or field for given class.      *       * @param currentClass      *            given class      * @param i      *            depth      * @param property      *            property with no .      * @param prefixs      *            possible method prefixs      * @return null if not found      */     @SuppressWarnings("unchecked")     private Member findAccessor(Class currentClass, int i, String property,         String... prefixs) {       Member member = null;       if (i == 0 && getters.containsKey(property)) {         member = getters.get(property).getLastMember();       } else {         for (String prefix : prefixs) {           try {             member = findMethod(currentClass, property, prefix);             break;           } catch (NoSuchMethodException e) {             if (log.isDebugEnabled())               log.debug("method not found: "                   + currentClass.getName() + "#" + prefix                   + capitilize(property));           } catch (SecurityException e) {             if (log.isDebugEnabled())               log.debug("method is not accessible: "                   + currentClass.getName() + "#" + prefix                   + capitilize(property));           }         }         try {           if (member == null) {             final Field field = currentClass.getField(property);             field.setAccessible(true);             member = field;           }         } catch (NoSuchFieldException e) {           if (log.isDebugEnabled())             log.debug("field is not found: "                 + currentClass.getName() + "#" + property);         }       }       return member;     }     private Method findMethod(Class currentClass, String property,         String prefix) throws NoSuchMethodException {       String name = prefix + capitilize(property);       try {         return currentClass.getMethod(name);       } catch (NoSuchMethodException e) {         log.debug("method not found", e);       }       Method[] methods = currentClass.getMethods();       for (Method method : methods) {         if (method.getName().equals(name))           return method;       }       throw new NoSuchMethodException("no such method found in "           + currentClass.getName() + ":" + name);     }     /**      * get object property value.      *       * @param obj      *            object      * @return value      * @throws IllegalArgumentException      *             underlying exception      * @see {@link Method#invoke(Object, Object...)}      * @throws IllegalAccessException      *             underlying exception      * @see {@link Method#invoke(Object, Object...)}      * @throws InvocationTargetException      *             underlying exception      * @see {@link Method#invoke(Object, Object...)}      */     public Object get(Object obj) throws IllegalArgumentException,         IllegalAccessException, InvocationTargetException {       Object currentObject = obj;       for (Member member : members) {         currentObject = executeGetterMember(currentObject, member);       }       return currentObject;     }     /**      * set object property value.      *       * @param obj      *            object      * @param value      *            property value      * @throws IllegalArgumentException      *             underlying exception      * @see {@link Method#invoke(Object, Object...)}      * @throws IllegalAccessException      *             underlying exception      * @see {@link Method#invoke(Object, Object...)}      * @throws InvocationTargetException      *             underlying exception      * @throws InstantiationException      *             new instance error      * @see {@link Method#invoke(Object, Object...)}      */     public void set(Object obj, Object value)         throws IllegalArgumentException, IllegalAccessException,         InvocationTargetException, InstantiationException {       Object currentObject = obj;       for (int i = 0; i < members.size(); i++) {         Member member = members.get(i);         if (i < members.size() - 1) {           Object tempObject = executeGetterMember(currentObject,               member);           if (tempObject == null) {             Member setMember = setMembers.get(i);             tempObject = initSetterMember(currentObject, setMember);           }           currentObject = tempObject;         } else {           currentObject = executeSetterMember(currentObject, member,               value);         }       }     }     /**      * execute setter method or field.      *       * @param currentObject      *            obj      * @param member      *            member      * @param value      *            set value      * @return get value      * @throws IllegalAccessException      *             delegated exception      * @throws java.lang.reflect.InvocationTargetException      *             delegated exception      */     private Object executeSetterMember(Object currentObject, Member member,         Object value) throws IllegalAccessException,         InvocationTargetException {       if (member instanceof Method) {         Method m = (Method) member;         currentObject = m.invoke(currentObject, value);       } else if (member instanceof Field) {         Field m = (Field) member;         m.set(currentObject, value);         currentObject = null;       }       return currentObject;     }     private Object initSetterMember(Object currentObject, Member member)         throws IllegalAccessException, InvocationTargetException,         InstantiationException {       Object value = null;       if (member instanceof Method) {         Method m = (Method) member;         value = m.getParameterTypes()[0].newInstance();         m.invoke(currentObject, value);       } else if (member instanceof Field) {         Field m = (Field) member;         value = m.getType().newInstance();         m.set(currentObject, value);       }       return value;     }     /**      * Execute getter method or field.      *       * @param currentObject      *            obj      * @param member      *            executed value      * @return return value      * @throws IllegalAccessException      *             delegated exception      * @throws java.lang.reflect.InvocationTargetException      *             delegated exception      */     private Object executeGetterMember(Object currentObject, Member member)         throws IllegalAccessException, InvocationTargetException {       if (member instanceof Method) {         Method m = (Method) member;         currentObject = m.invoke(currentObject);       } else if (member instanceof Field) {         Field m = (Field) member;         currentObject = m.get(currentObject);       }       return currentObject;     }     /**      * last property field or method.      *       * @return member      */     public Member getLastMember() {       return members.get(members.size() - 1);     }   }   /**    * upperCase only first string.    *     * @param text    *            whole input text    * @return capitilized text    */   public static String capitilize(String text) {     if (text == null)       return null;     if (text.length() == 0)       return text;     String result = text.substring(0, 1).toUpperCase(Locale.ENGLISH);     if (text.length() > 1)       result += text.substring(1);     return result;   }   /**    * split text with dot seperator.    *     * @param text    *            text to split    * @return splitted text    */   public static String[] splitDot(String text) {     String DOT_SEPERATOR_REGEX = "\\.";     return text.split(DOT_SEPERATOR_REGEX);   } }