All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gentlyweb.utils.Accessor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006 - Gary Bentley
 *
 * 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.gentlyweb.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * This class is used to perform access into a Java object using a 
 * String value with a specific notation.
 * 

* The Accessor uses a dot notation such as field1.method1.method2 to * perform the access on an object. Each value in the notation refers to * a field or method (a no argument method) of the type of the previous * value. * For instance if you have the following class structure: *

*
 * public class A 
 * {
 *    public B = new B ();
 * }
 * 
 * public class B
 * {
 *    public C = new C ();
 * }
 * 
 * public class C
 * {
 *    String d = "";
 * }
 * 
*

* You would then use the notation: B.C.d to get access to * field d in Class C. *

* The Accessor also supports a [ ] notation for accessing * into Lists/Maps and Arrays. If the value between the [ ] * is an integer then we look for the associated type to be either * an array or a List, we then index into it with the integer. If * the value is NOT an integer then we use assume the * type is a Map and use it as a key into the Map. *

* For instance changing the example above: *

*
 * public class A 
 * {
 *    public List vals = new ArrayList ();
 * }
 * 
*

* Now we could use: vals[X] where X is a positive integer. * Or changing again: *

*
 * public class A 
 * {
 *    public Map vals = new HashMap ();
 * }
 * 
*

* We could use: vals[VALUE] where VALUE would then be * used as a Key into the vals HashMap. *

* Note: The Accessor is NOT designed to be an all purpose * method of gaining access to a class. It has specific uses and for * most will be of no use at all. It should be used for general purpose * applications where you want to access specific fields of an object * without having to know the exact type. One such application is in * the {@link GeneralComparator}, in that case arbitrary Objects can * be sorted without having to write complex Comparators or implementing * the Comparable interface AND it gives the flexibility that sorting * can be changed ad-hoc. *

* The Accessor looks for in the following order: *

    *
  • Public fields with the specified name.
  • *
  • If no field is found then the name is converted to a "JavaBeans" * get method, so a field name of value would be converted * to getValue and that method is looked for. The method must take * no arguments.
  • *
  • If we don't find the get* method then we look for a method with * the specified name. So a field name of value would mean that * a method (that again takes no arguments) is looked for.
  • *
*

* Note: we have had to add the 3rd type to allow for methods that don't follow * JavaBeans conventions (there are loads in the standard Java APIs which makes * accessing impossible otherwise). */ public class Accessor { private Object accessor = null; private String index = ""; public String getIndex () { return this.index; } public void setIndex (String index) { this.index = index; } public void setAccessor (Field f) { this.accessor = f; } public void setAccessor (Method m) { this.accessor = m; } public boolean isIndexCorrectForType (Class c) { // See if the index is a number...in which case // the class should be either an Array or a List. if (this.index.equals ("")) { return true; } try { Integer.parseInt (this.index); if ((c.isArray ()) || (c.isAssignableFrom (List.class)) ) { return true; } } catch (NumberFormatException e) { if (c.isAssignableFrom (Map.class)) { return true; } } return false; } public static Object getValueFromAccessorChain (Object obj, List chain) throws IllegalAccessException, InvocationTargetException { // For our accessor chain, use the Field and Methods // to get the actual value. Object retdata = obj; for (int i = 0; i < chain.size (); i++) { Accessor a = (Accessor) chain.get (i); // See what type the accessor is... if (a.accessor instanceof Method) { Method m = (Method) a.accessor; Object[] parms = {}; // Invoke the method... retdata = m.invoke (retdata, parms); if (retdata == null) { return null; } } if (a.accessor instanceof Field) { // It's a field...so... Field f = (Field) a.accessor; // Now get the value... retdata = f.get (retdata); } // See if we have an index... if (!a.getIndex ().equals ("")) { retdata = Accessor.getValueForIndex (retdata, a.getIndex ()); if (retdata == null) { return null; } } } return retdata; } public static Object getValueForIndex (Object data, String index) { try { int in = Integer.parseInt (index); // See if we have an Array or // List. if (data.getClass ().isArray ()) { if (in < Array.getLength (data)) { // It's an array. return Array.get (data, in); } } else { if (data instanceof List) { List l = (List) data; if (in < l.size ()) { return l.get (in); } } } } catch (NumberFormatException e) { // It's not a number so assume that // we have a Map. if (data instanceof Map) { Map map = (Map) data; return map.get (index); } } return null; } /** * Get the Java field associated with the named field. Return * null if there isn't one, or if we can't access it. * * @param field The name of the field. * @param clazz The Class to get the field from. * @return A List of Accessor objects used for delving into the * classes to be sorted. */ public static List getAccessorChain (String accessorRef, Class clazz) throws IllegalArgumentException { StringTokenizer t = new StringTokenizer (accessorRef, "."); Class c = clazz; List retdata = new ArrayList (); while (t.hasMoreTokens ()) { String tok = t.nextToken (); Accessor a = new Accessor (); String index = ""; if (tok.endsWith ("]")) { // It does, so now find the [ index = tok.substring ((tok.indexOf ("[") + 1), tok.length () - 1); a.setIndex (index); tok = tok.substring (0, tok.indexOf ("[")); } // Get the Fields. Field[] fields = c.getFields (); Field f = null; // See if the token matches... for (int i = 0; i < fields.length; i++) { if (fields[i].getName ().equals (tok)) { // Found it... f = fields[i]; break; } } if (f != null) { c = f.getType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Field: " + tok + " cannot be accessed with index: " + index + " since field type is: " + c.getName ()); } a.accessor = f; retdata.add (a); } else { // Now convert it to a method name and use the // JavaBeans convention... StringBuffer name = new StringBuffer (tok); name.setCharAt (0, Character.toUpperCase (tok.charAt (0))); name.insert (0, "get"); // Now get the method... Method m = Accessor.getNoParmJavaMethod (name.toString (), c); if (m != null) { c = m.getReturnType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called with index: " + index + " since method return type is: " + c.getName ()); } if (Void.class.isAssignableFrom (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called on class: " + c.getName () + " since return type is void"); } a.accessor = m; retdata.add (a); continue; } else { // The field is not either a standard JavaBeans method so now just // look for the method with the specified name. m = Accessor.getNoParmJavaMethod (tok, c); if (m == null) { throw new IllegalArgumentException ("Cannot find method with name: " + tok + " in class: " + c.getName ()); } c = m.getReturnType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called with index: " + index + " since method return type is: " + c.getName ()); } if (Void.class.isAssignableFrom (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called on class: " + c.getName () + " since return type is void"); } a.accessor = m; retdata.add (a); continue; } } } return retdata; } private static Method getNoParmJavaMethod (String method, Class c) { Method[] methods = c.getMethods (); for (int i = 0; i < methods.length; i++) { if ((methods[i].getName ().equals (method)) && (methods[i].getParameterTypes ().length == 0) ) { // This is the one... return methods[i]; } } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy