001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine.util;
051:
052: import org.apache.log4j.Logger;
053: import java.lang.reflect.*;
054: import java.util.*;
055: import org.jaffa.util.StringHelper;
056:
057: /** Helper class for introspecting Domain objects. */
058: public class Introspection {
059:
060: private static final Logger log = Logger
061: .getLogger(Introspection.class);
062:
063: /** This will introspect the input Class for an AccessibleObject, for each of the input fieldName passed in the 'fields' Map.
064: * For each fieldName, this method will introspect the input class for an AccessibleObject in the following order.
065: * 1- If an entry exists for the fieldName in the members Map, search for a Field object having the same name as that memberName
066: * 2- Else search for a Method object - getFieldName() or isFieldName()
067: * 3- If no AccessibleObject is found, then recursively call itself for each parent class.
068: * It will return a Map of key=fieldName/value=Field/Method object pairs.
069: * The returnType of the Field/Method will have to match the type passed in the input 'fields' Map.
070: * NOTE: An inaccessible Field/Method will be forced to be accessible !!
071: * No exception is raised, in case an appropriate method is not found for a fieldName.
072: * The calling class can always compare the size of the input Map with the Map returned by this method,
073: * and raise suitable exceptions.
074: * @param clazz The class to be introspected.
075: * @param fields The key of this HashMap is the name of the field (String) and the value is the data type (String).
076: * @param members This map will contain the fields which are to be introspected by using member variables as opposed to getters/setters. The key of this HashMap is the name of the field (String) and the value is the memberName (String).
077: * @return a map of key=fieldName/value=Field/Method object pairs.
078: */
079: public static Map getAccessors(Class clazz, Map fields, Map members) {
080: return getAccessibleObjects(clazz, fields, members, true);
081: }
082:
083: /** This will introspect the input Class for an AccessibleObject, for each of the input fieldName passed in the 'fields' Map.
084: * For each fieldName, this method will introspect the input class for an AccessibleObject in the following order.
085: * 1- If an entry exists for the fieldName in the members Map, search for a Field object having the same name as that memberName
086: * 2- Else search for a Method object - setFieldName(Object obj)
087: * 3- If no AccessibleObject is found, then recursively call itself for each parent class.
088: * It will return a Map of key=fieldName/value=Field/Method object pairs.
089: * The returnType/paramter of the Field/Method will have to match the type passed in the input 'fields' Map.
090: * NOTE: An inaccessible Field/Method will be forced to be accessible !!
091: * No exception is raised, in case an appropriate method is not found for a fieldName.
092: * The calling class can always compare the size of the input Map with the Map returned by this method,
093: * and raise suitable exceptions.
094: * @param clazz The class to be introspected.
095: * @param fields The key of this HashMap is the name of the field (String) and the value is the data type (String).
096: * @param members This map will contain the fields which are to be introspected by using member variables as opposed to getters/setters. The key of this HashMap is the name of the field (String) and the value is the memberName (String).
097: * @return a map of key=fieldName/value=Field/Method object pairs.
098: */
099: public static Map getMutators(Class clazz, Map fields, Map members) {
100: return getAccessibleObjects(clazz, fields, members, false);
101: }
102:
103: /** This method will introspect the input class for for a Field object having the same name as the input fieldName.
104: * If no Field is found, then it'll recursively call itself for each parent class.
105: * NOTE: An inaccessible Field will be forced to be accessible !!
106: * @param clazz The class to be introspected.
107: * @param fieldName The fieldName for which the Field object is to be found.
108: * @param datatype This is an optional input. If passed then it'll ensure that the Field object has the correct signature.
109: * @return the for a Field object having the same name as the input fieldName. A null is returned, in case the field doesn't exist.
110: */
111: public static Field getAccessibleField(Class clazz,
112: String fieldName, Class datatype) {
113: return (Field) getAccessibleObject(clazz, fieldName, true,
114: true, false, datatype);
115: }
116:
117: /** This method will introspect the input class for an accessor Method object - getFieldName() or isFieldName()
118: * If no Method is found, then it'll recursively call itself for each parent class.
119: * NOTE: An inaccessible Method will be forced to be accessible !!
120: * @param clazz The class to be introspected.
121: * @param fieldName The fieldName for which the accessor is to be found.
122: * @param datatype This is an optional input. If passed then it'll ensure that the accessor has the correct signature.
123: * @return the accessor Method for the property. A null is returned, in case the property doesn't exist.
124: */
125: public static Method getAccessorMethod(Class clazz,
126: String fieldName, Class datatype) {
127: return (Method) getAccessibleObject(clazz, fieldName, true,
128: false, true, datatype);
129: }
130:
131: /** This method will introspect the input class for a mutator Method object - setFieldName().
132: * If no Method is found, then it'll recursively call itself for each parent class.
133: * NOTE: An inaccessible Method will be forced to be accessible !!
134: * @param clazz The class to be introspected.
135: * @param fieldName The fieldName for which the mutator is to be found.
136: * @param datatype This is an optional input. If passed then it'll ensure that the mutator has the correct signature.
137: * @return the mutator Method for the property. A null is returned, in case the property doesn't exist.
138: */
139: public static Method getMutatorMethod(Class clazz,
140: String fieldName, Class datatype) {
141: return (Method) getAccessibleObject(clazz, fieldName, false,
142: false, true, datatype);
143: }
144:
145: /** This will introspect the input Class for an AccessibleObject, for each of the input fieldName passed in the 'fields' Map.
146: * For each fieldName, this method will introspect the input class for an AccessibleObject in the following order.
147: * 1- If an entry exists for the fieldName in the members Map, search for a Field object having the same name as that memberName
148: * 2- Else search for a Method object - getFieldName() or isFieldName() if accessor is true, or setFieldName(Object obj) if accessor is false
149: * 3- If no AccessibleObject is found, then recursively call itself for each parent class.
150: * It will return a Map of key=fieldName/value=Field/Method object pairs.
151: * The returnType/paramter of the Field/Method will have to match the type passed in the input 'fields' Map.
152: * NOTE: An inaccessible Field/Method will be forced to be accessible !!
153: * No exception is raised, in case an appropriate method is not found for a fieldName.
154: * The calling class can always compare the size of the input Map with the Map returned by this method,
155: * and raise suitable exceptions.
156: * @param clazz The class to be introspected.
157: * @param fields The key of this HashMap is the name of the field (String) and the value is the data type (String).
158: * @param members This map will contain the fields which are to be introspected by using member variables as opposed to getters/setters. The key of this HashMap is the name of the field (String) and the value is the memberName (String).
159: * @param accessor determines whether to return an accessor or a mutator.
160: * @return a map of key=fieldName/value=Field/Method object pairs.
161: */
162: private static Map getAccessibleObjects(Class clazz, Map fields,
163: Map members, boolean accessor) {
164: Map output = new HashMap();
165: for (Iterator itr = fields.keySet().iterator(); itr.hasNext();) {
166: String fieldName = (String) itr.next();
167: try {
168: Class datatype;
169: String type = (String) fields.get(fieldName);
170: if (type.equalsIgnoreCase("byte[]"))
171: datatype = byte[].class;
172: else
173: datatype = Class.forName(type);
174:
175: AccessibleObject obj = null;
176: if (members.containsKey(fieldName))
177: obj = getAccessibleObject(clazz, (String) members
178: .get(fieldName), accessor, true, false,
179: datatype);
180: else
181: obj = getAccessibleObject(clazz, fieldName,
182: accessor, false, true, datatype);
183: if (obj != null) {
184: output.put(fieldName, obj);
185: } else {
186: if (log.isDebugEnabled())
187: log.debug((accessor ? "Accessor" : "Mutator")
188: + " not found for field '" + fieldName
189: + '\'');
190: }
191: } catch (ClassNotFoundException e) {
192: String str = "Error in loading the Class for the datatype '"
193: + fields.get(fieldName)
194: + "' of the field '"
195: + fieldName + '\'';
196: log.warn(str, e);
197: }
198: }
199:
200: return output;
201: }
202:
203: /** This method will introspect the input class for an AccessibleObject in the following order.
204: * 1- If findField is true, search for a Field object having the same name as the input fieldName
205: * 2- If findMethod is true, search for a Method object - getFieldName() or isFieldName() if accessor is true, or setFieldName(Object obj) if accessor is false
206: * 3- If no AccessibleObject is found, then recursively call itself for each parent class.
207: * NOTE: An inaccessible Field/Method will be forced to be accessible !!
208: * @param clazz The class to be introspected.
209: * @param fieldName The fieldName for which the accessor/mutator is to be found.
210: * @param accessor determines whether to return an accessor or a mutator.
211: * @param findField this will determine if introspection should be performed for a Field object
212: * @param findMethod this will determine if introspection should be performed for a Method object
213: * @param datatype This is an optional input. If passed then it'll ensure that the accessor/mutator have the correct signature.
214: * @return the accessor/mutator Field/Method for the property. A null is returned, in case the property doesn't exist.
215: */
216: private static AccessibleObject getAccessibleObject(Class clazz,
217: String fieldName, boolean accessor, boolean findField,
218: boolean findMethod, Class datatype) {
219: AccessibleObject output = null;
220:
221: // Search for a Field
222: if (findField) {
223: try {
224: output = clazz.getDeclaredField(fieldName);
225: if (datatype != null
226: && !datatype.isAssignableFrom(((Field) output)
227: .getType()))
228: output = null;
229: } catch (NoSuchFieldException e) {
230: // do nothing
231: }
232: }
233:
234: // If Field is not found, then search for a Method
235: if (output == null && findMethod) {
236: String methodName;
237: if (accessor) {
238: methodName = "get" + StringHelper.getUpper1(fieldName);
239: try {
240: output = clazz.getDeclaredMethod(methodName,
241: new Class[0]);
242: if (datatype != null
243: && !datatype
244: .isAssignableFrom(((Method) output)
245: .getReturnType()))
246: output = null;
247: } catch (NoSuchMethodException e) {
248: // do nothing
249: }
250: if (output == null) {
251: methodName = "is"
252: + StringHelper.getUpper1(fieldName);
253: try {
254: output = clazz.getDeclaredMethod(methodName,
255: new Class[0]);
256: if (datatype != null
257: && !datatype
258: .isAssignableFrom(((Method) output)
259: .getReturnType()))
260: output = null;
261: } catch (NoSuchMethodException e) {
262: // do nothing
263: }
264: }
265: } else {
266: methodName = "set" + StringHelper.getUpper1(fieldName);
267: if (datatype != null) {
268: try {
269: output = clazz.getDeclaredMethod(methodName,
270: new Class[] { datatype });
271: } catch (NoSuchMethodException e) {
272: // do nothing
273: }
274: } else {
275: Method[] setters = clazz.getDeclaredMethods();
276: for (int i = 0; i < setters.length; i++) {
277: if (setters[i].getName().equals(methodName)
278: && setters[i].getParameterTypes().length == 1) {
279: output = setters[i];
280: break;
281: }
282: }
283: }
284: }
285: }
286:
287: // If no AccessibleObject is found, then recursively call itself for each parent class
288: if (output == null) {
289: Class super Class = clazz.getSuperclass();
290: if (super Class != null && super Class != Object.class)
291: output = getAccessibleObject(super Class, fieldName,
292: accessor, findField, findMethod, datatype);
293: }
294:
295: // Force an inaccessible Field/Method to be accessible.
296: if (output != null && !output.isAccessible())
297: output.setAccessible(true);
298:
299: return output;
300: }
301:
302: }
|