0001: /*
0002: * ObjectInspector.java
0003: *
0004: * Created on 11 May 2003, 18:59
0005: */
0006:
0007: package com.jofti.introspect;
0008:
0009: import java.io.File;
0010: import java.io.ObjectStreamField;
0011: import java.lang.reflect.Array;
0012: import java.lang.reflect.InvocationTargetException;
0013: import java.lang.reflect.Method;
0014: import java.nio.ByteBuffer;
0015: import java.nio.CharBuffer;
0016: import java.nio.DoubleBuffer;
0017: import java.nio.FloatBuffer;
0018: import java.nio.IntBuffer;
0019: import java.nio.LongBuffer;
0020: import java.nio.ShortBuffer;
0021: import java.nio.charset.Charset;
0022: import java.sql.Timestamp;
0023: import java.text.CollationKey;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.util.Set;
0032:
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035:
0036: import com.jofti.btree.ValueObject;
0037: import com.jofti.config.IndexConfig;
0038: import com.jofti.exception.JoftiException;
0039: import com.jofti.exception.PropertyNotIndexedException;
0040: import com.jofti.model.ComparableBoolean;
0041: import com.jofti.model.ParsedObject;
0042: import com.jofti.util.ReflectionUtil;
0043:
0044: import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
0045:
0046: /**
0047: * A JavaBean idiom compliant introspector. This means that Objects have to implement thet getter/setter
0048: * metaphor in order to be correctly interrogated.</p>
0049: *
0050: * The general usage of the parsing is package.Class.property. This value needs to be specified int he config file
0051: * in this format and the introspector will use this mechanism to navigate to the correct property. </p>
0052: * @author xenephon (xenephon@jofti.com)
0053: */
0054: public class JavaBeanClassIntrospector implements ClassIntrospector {
0055:
0056: private static Log log = LogFactory
0057: .getLog(JavaBeanClassIntrospector.class);
0058:
0059: private static final String METHOD_PREFIX = "get";
0060:
0061: private static final Class[] noArgs = new Class[] {};
0062:
0063: private static final int KEY_DIMENSION_VALUE = -1;
0064:
0065: private static int keyDimensionCount = KEY_DIMENSION_VALUE;
0066:
0067: private static final String NULL_ATTRIBUTE = "NULL METHOD";
0068:
0069: private static final String ARRAY_ATTRIBUTE = "[element]";
0070:
0071: private static Method ARRAY_METHOD = null;
0072:
0073: private static Method COLLECTIONS_METHOD = null;
0074:
0075: private static Method NULL_METHOD = null;
0076:
0077: private static final Set defaultClasses = new HashSet();
0078:
0079: static {
0080: defaultClasses.add(java.lang.String.class);
0081: defaultClasses.add(java.lang.Integer.class);
0082: defaultClasses.add(java.lang.Double.class);
0083: defaultClasses.add(java.lang.Float.class);
0084: defaultClasses.add(java.lang.Long.class);
0085: defaultClasses.add(java.lang.Short.class);
0086: defaultClasses.add(java.lang.Byte.class);
0087: defaultClasses.add(java.lang.Character.class);
0088: defaultClasses.add(java.math.BigInteger.class);
0089: defaultClasses.add(java.math.BigDecimal.class);
0090: defaultClasses.add(java.util.Date.class);
0091: defaultClasses.add(Timestamp.class);
0092: defaultClasses.add(java.net.URI.class);
0093: defaultClasses.add(ComparableBoolean.class);
0094:
0095: defaultClasses.add(java.lang.String[].class);
0096: defaultClasses.add(java.lang.Integer[].class);
0097: defaultClasses.add(java.lang.Double[].class);
0098: defaultClasses.add(java.lang.Float[].class);
0099: defaultClasses.add(java.lang.Long[].class);
0100: defaultClasses.add(java.lang.Short[].class);
0101: defaultClasses.add(java.lang.Byte[].class);
0102: defaultClasses.add(java.lang.Character[].class);
0103: defaultClasses.add(java.math.BigInteger[].class);
0104: defaultClasses.add(java.math.BigDecimal[].class);
0105: defaultClasses.add(java.util.Date[].class);
0106: defaultClasses.add(Timestamp[].class);
0107: defaultClasses.add(java.net.URI[].class);
0108: defaultClasses.add(ComparableBoolean[].class);
0109: defaultClasses.add(java.lang.Boolean[].class);
0110: defaultClasses.add(int[].class);
0111: defaultClasses.add(long[].class);
0112: defaultClasses.add(short[].class);
0113: defaultClasses.add(byte[].class);
0114: defaultClasses.add(boolean[].class);
0115: defaultClasses.add(char[].class);
0116: defaultClasses.add(float[].class);
0117: defaultClasses.add(double[].class);
0118: }
0119:
0120: private static final Set excludedComparableClasses = new HashSet();
0121:
0122: static {
0123: excludedComparableClasses.add(ByteBuffer.class);
0124: excludedComparableClasses.add(CharBuffer.class);
0125: excludedComparableClasses.add(Charset.class);
0126: excludedComparableClasses.add(CollationKey.class);
0127: excludedComparableClasses.add(DoubleBuffer.class);
0128: excludedComparableClasses.add(File.class);
0129: excludedComparableClasses.add(FloatBuffer.class);
0130: excludedComparableClasses.add(IntBuffer.class);
0131: excludedComparableClasses.add(LongBuffer.class);
0132: excludedComparableClasses.add(ObjectStreamField.class);
0133: excludedComparableClasses.add(ShortBuffer.class);
0134:
0135: }
0136:
0137: static {
0138: try {
0139: ARRAY_METHOD = JavaBeanClassIntrospector.class
0140: .getDeclaredMethod("arrayMethod", new Class[] {});
0141:
0142: COLLECTIONS_METHOD = JavaBeanClassIntrospector.class
0143: .getDeclaredMethod("collectionsMethod",
0144: new Class[] {});
0145:
0146: NULL_METHOD = JavaBeanClassIntrospector.class
0147: .getDeclaredMethod("nullMethod", new Class[] {});
0148: } catch (Exception e) {
0149:
0150: }
0151: }
0152:
0153: private void arrayMethod() {
0154:
0155: }
0156:
0157: private void nullMethod() {
0158:
0159: }
0160:
0161: private void collectionsMethod() {
0162:
0163: }
0164:
0165: private Set primitiveSet = java.util.Collections
0166: .unmodifiableSet(defaultClasses);
0167:
0168: Map dimensionMap = new ConcurrentHashMap();
0169:
0170: Map interfaceMap = new ConcurrentHashMap();
0171:
0172: Map concreteMap = new ConcurrentHashMap();
0173:
0174: Map keyDimensionMap = new ConcurrentHashMap();
0175:
0176: private ClassUtils utils = new ClassUtils();
0177:
0178: /* (non-Javadoc)
0179: * @see com.jofti.introspect.ClassIntrospector#parseConfig(com.jofti.config.IndexConfig)
0180: */
0181: public void parseConfig(IndexConfig config) throws JoftiException {
0182:
0183: Map configMap = config.getIndexMappings();
0184: // check if we have a config map
0185: if (configMap == null) {
0186: return;
0187: }
0188:
0189: // starts at 0
0190: int dimensionCount = 0;
0191:
0192: // look through mappings to construct methods
0193: for (Iterator it = configMap.keySet().iterator(); it.hasNext();) {
0194: // what class are we dealing with
0195: String className = (String) it.next();
0196: Class clazz = null;
0197:
0198: // see if the JVM knows about the class
0199: try {
0200: clazz = ReflectionUtil.classForName(className);
0201: } catch (Exception e) {
0202: log.info("No class found for " + className);
0203: throw new JoftiException(e);
0204: }
0205:
0206: // set up the parsed Object
0207: ParsedObject parsedObject = new ParsedObject();
0208:
0209: parsedObject.setClassValue(clazz);
0210: Map classMap = new ConcurrentHashMap();
0211: Map methodMap = new ConcurrentHashMap();
0212: Map dimensionClassMap = new ConcurrentHashMap();
0213: Map fieldMethodMap = new ConcurrentHashMap();
0214:
0215: parsedObject.setFieldValues(classMap);
0216: parsedObject.setMethodValues(methodMap);
0217: parsedObject.setDimensionClassMappings(dimensionClassMap);
0218: parsedObject.setFieldMethods(fieldMethodMap);
0219:
0220: // we need to look at proxy or interface stuff here
0221: List properties = (List) configMap.get(className);
0222: if (clazz.isInterface()) {
0223: parsedObject.setInterface(true);
0224: }
0225: // now we get the map of attributes to index
0226:
0227: dimensionCount = parseAttribtes(dimensionCount, clazz,
0228: classMap, methodMap, dimensionClassMap,
0229: fieldMethodMap, properties);
0230:
0231: dimensionMap.put(clazz, parsedObject);
0232:
0233: }
0234:
0235: parseBuiltInClasses(dimensionCount);
0236: }
0237:
0238: /**
0239: * @param dimensionCount
0240: * @param clazz
0241: * @param classMap
0242: * @param methodMap
0243: * @param dimensionClassMap
0244: * @param attributes
0245: * @return
0246: * @throws JoftiException
0247: */
0248:
0249: private int parseAttribtes(int dimensionCount, Class clazz,
0250: Map classMap, Map methodMap, Map dimensionClassMap,
0251: Map fieldMethodMap, List attributes) throws JoftiException {
0252:
0253: if (attributes != null) {
0254:
0255: int size = attributes.size();
0256: Iterator attribIterator = attributes.iterator();
0257: for (int i = 0; i < size; i++) {
0258: String attribute = (String) attribIterator.next();
0259:
0260: Object[] methods = getMethodArray(clazz, attribute);
0261: if (methods == null || methods.length == 0) {
0262: throw new JoftiException(
0263: "No getter method found for attribute "
0264: + attribute + " in class " + clazz);
0265: } else {
0266:
0267: Class returnType = null;
0268: if (methods[methods.length - 1] == ARRAY_METHOD) {
0269:
0270: throw new JoftiException(
0271: "terminating value cannot be an array type");
0272: } else if (methods[methods.length - 1] instanceof Method) {
0273: returnType = ((Method) methods[methods.length - 1])
0274: .getReturnType();
0275: } else {
0276: // must contain the return value as an object
0277: returnType = (Class) methods[methods.length - 1];
0278: }
0279:
0280: // see if method type is a primitive we can change
0281: if (returnType.isPrimitive()
0282: || returnType == Boolean.class) {
0283: returnType = boxPrimitive(returnType);
0284: }
0285:
0286: // see if the final value from method array is comparable
0287:
0288: if (Comparable.class.isAssignableFrom(returnType)
0289: && !excludedComparableClasses
0290: .contains(returnType)) {
0291: Integer dimension = new Integer(dimensionCount);
0292: classMap.put(attribute, dimension);
0293: methodMap.put(methods, dimension);
0294: dimensionClassMap.put(dimension, returnType);
0295: fieldMethodMap.put(attribute, methods);
0296: dimensionCount++;
0297: } else {
0298: throw new JoftiException("Attribute "
0299: + attribute
0300: + " is not Comparable in class "
0301: + clazz
0302: + " or is an excluded Comparable type");
0303: }
0304:
0305: }
0306: }
0307:
0308: }
0309: return dimensionCount;
0310: }
0311:
0312: /**
0313: * @param clazz
0314: * @param attribute
0315: * @return
0316: */
0317: public Object[] getMethodsForAttribute(Class clazz, String attribute)
0318: throws JoftiException {
0319:
0320: Object[] results = null;
0321:
0322: // first see if we already have the field
0323: ParsedObject obj = (ParsedObject) dimensionMap.get(clazz);
0324: if (obj != null) {
0325: // see if it one we have already specified
0326: Map fieldMethods = obj.getFieldMethods();
0327: // we have a parsed class
0328:
0329: // see if it is a primitive class and value is value
0330: if (fieldMethods.containsKey(NULL_ATTRIBUTE)) {
0331: if ("value".equalsIgnoreCase(attribute)) {
0332: return (Object[]) fieldMethods.get(NULL_ATTRIBUTE);
0333: } else {
0334: throw new JoftiException(
0335: "Attribute "
0336: + attribute
0337: + " is not allowed for Class "
0338: + clazz
0339: + " can only have attribute VALUE or must be a whole object ");
0340: }
0341: }
0342: results = (Object[]) fieldMethods.get(attribute);
0343:
0344: if (results == null) {
0345:
0346: // oj lets see if it is a primitive so it must have
0347: // a null method
0348:
0349: results = getMethodArray(clazz, attribute);
0350: }
0351: return results;
0352: } else {
0353: results = getMethodArray(clazz, attribute);
0354: }
0355: return results;
0356: }
0357:
0358: private Object[] getMethodArray(Class clazz, String attribute)
0359: throws JoftiException {
0360: Object[] methods = null;
0361: try {
0362: methods = preProcessAttribute(attribute);
0363: methods = constructMethodArray(methods, clazz);
0364:
0365: } catch (JoftiException e) {
0366: log.warn("No getter method found for attribute "
0367: + attribute + ": " + e);
0368: throw new JoftiException(
0369: "No getter method found for attribute " + attribute
0370: + " on " + clazz, e);
0371: }
0372: return methods;
0373: }
0374:
0375: /**
0376: * @param dimensionCount
0377: */
0378: private void parseBuiltInClasses(int dimensionCount) {
0379: // now parse in the default classes
0380: for (Iterator it = defaultClasses.iterator(); it.hasNext();) {
0381:
0382: ParsedObject parsedObject = new ParsedObject();
0383: Class temp = (Class) it.next();
0384: parsedObject.setClassValue(temp);
0385: Map classMap = new HashMap();
0386: Map methodMap = new HashMap();
0387: Map fieldMethodMap = new HashMap();
0388:
0389: parsedObject.setFieldValues(classMap);
0390: parsedObject.setMethodValues(methodMap);
0391: parsedObject.setFieldMethods(fieldMethodMap);
0392:
0393: Integer dimension = new Integer(dimensionCount);
0394: String attribute = null;
0395:
0396: Object[] meth = null;
0397: if (temp.isArray()) {
0398: meth = new Object[2];
0399: meth[0] = ARRAY_METHOD;
0400: meth[1] = temp.getComponentType();
0401: attribute = ARRAY_ATTRIBUTE;
0402: } else {
0403: meth = new Object[1];
0404: meth[0] = NULL_METHOD;
0405: attribute = NULL_ATTRIBUTE;
0406: }
0407:
0408: methodMap.put(meth, dimension);
0409: classMap.put(attribute, dimension);
0410: fieldMethodMap.put(attribute, meth);
0411:
0412: dimensionCount++;
0413: dimensionMap.put(temp, parsedObject);
0414: }
0415: }
0416:
0417: public Class boxPrimitive(Class clazz) {
0418: if (clazz == Integer.TYPE) {
0419: return Integer.class;
0420: } else if (clazz == Double.TYPE) {
0421: return Double.class;
0422: } else if (clazz == Long.TYPE) {
0423: return Long.class;
0424: } else if (clazz == Float.TYPE) {
0425: return Float.class;
0426: } else if (clazz == Short.TYPE) {
0427: return Short.class;
0428: } else if (clazz == Byte.TYPE) {
0429: return Byte.class;
0430: } else if (clazz == Character.TYPE) {
0431: return Character.class;
0432: } else {
0433: return ComparableBoolean.class;
0434: }
0435: }
0436:
0437: /* (non-Javadoc)
0438: * @see com.jofti.introspect.ClassIntrospector#getClassForAttribute(java.lang.Class, java.lang.String)
0439: */
0440: public Class getClassForAttribute(Class className, String attribute)
0441: throws JoftiException {
0442:
0443: ParsedObject obj = (ParsedObject) dimensionMap.get(className);
0444: if (obj != null) {
0445: Object returnObj = null;
0446: // first see if it has the null method
0447: if ((returnObj = obj.getFieldValues().get(NULL_ATTRIBUTE)) != null) {
0448: // we should return the dimension ofr the primitive class itself
0449: if (attribute != null
0450: && !"value".equalsIgnoreCase(attribute)) {
0451: throw new JoftiException(
0452: "Only field value 'VALUE' can be used for class "
0453: + className);
0454: }
0455: return className;
0456: } else if ((returnObj = obj.getFieldValues().get(
0457: ARRAY_ATTRIBUTE)) != null) {
0458: // we should return the dimension ofr the primitive class itself
0459: if (attribute.equalsIgnoreCase(ARRAY_ATTRIBUTE)) {
0460: return className;
0461: } else {
0462: throw new PropertyNotIndexedException(" property "
0463: + attribute
0464: + " cannot refer to an element in "
0465: + className + " use " + ARRAY_ATTRIBUTE);
0466:
0467: }
0468:
0469: } else {
0470: try {
0471: return (Class) obj.getDimensionClassMappings().get(
0472: obj.getFieldValues().get(attribute));
0473: } catch (Throwable e) {
0474: throw new PropertyNotIndexedException(" property "
0475: + attribute + " not indexed in "
0476: + className);
0477: }
0478: }
0479: } else {
0480: throw new PropertyNotIndexedException(" class " + className
0481: + " not in index");
0482: }
0483: }
0484:
0485: private Object[] preProcessAttribute(String attribute)
0486: throws JoftiException {
0487: List tempList = new ArrayList();
0488: int startFragment = 0;
0489: int endFragment = 0;
0490: boolean inArray = false;
0491: if (attribute.length() == 1) {
0492: return new Object[] { attribute };
0493: }
0494: for (int i = 0; i < attribute.length(); i++) {
0495: if (attribute.charAt(i) == '[') {
0496: inArray = true;
0497: startFragment = i;
0498: } else if (attribute.charAt(i) == ']') {
0499:
0500: endFragment = i;
0501: } else if (i == attribute.length() - 1) {
0502: endFragment = i;
0503: } else if (!inArray) {
0504: if (attribute.charAt(i) == '.') {
0505: endFragment = i;
0506: }
0507: } else {
0508:
0509: }
0510: if (endFragment > startFragment) {
0511: // we should extract the string
0512: if (inArray || i == attribute.length() - 1) {
0513: endFragment = endFragment + 1;
0514: }
0515: String temp = attribute.substring(startFragment,
0516: endFragment);
0517: if (temp.startsWith(".")) {
0518: temp = temp.substring(1);
0519: }
0520:
0521: tempList.add(temp);
0522: startFragment = endFragment;
0523: inArray = false;
0524: }
0525: }
0526: return tempList.toArray();
0527:
0528: }
0529:
0530: private Object[] constructMethodArray(Object[] attributes,
0531: Class originalClass) throws JoftiException {
0532:
0533: List methods = new ArrayList();
0534:
0535: try {
0536: // we have an array thing
0537:
0538: Method tempMethod = null;
0539: int j = attributes.length;
0540: int k = 0;
0541: for (int i = 0; i < j; i++) {
0542:
0543: if (originalClass.isArray()
0544: && attributes[i].equals("[element]")) {
0545:
0546: // check that the component type is not an object
0547: if (originalClass.getComponentType().equals(
0548: Object.class)) {
0549: throw new JoftiException(
0550: "Types to be indexed in an Object array must be specified");
0551: }
0552:
0553: // add in the extra method that signifies an array
0554: // make sure that element is the attribute
0555:
0556: methods.add(ARRAY_METHOD);
0557:
0558: // increment j
0559:
0560: originalClass = originalClass.getComponentType();
0561: methods.add(originalClass);
0562:
0563: } else if (originalClass.isArray()) {
0564: // we have to get they type out of it
0565: String tempClass = ((String) attributes[i])
0566: .substring(1, ((String) attributes[i])
0567: .length() - 1);
0568: methods.add(ARRAY_METHOD);
0569: Class elementClass = null;
0570: try {
0571: elementClass = ReflectionUtil
0572: .classForName(tempClass);
0573: } catch (Exception e) {
0574: throw new JoftiException(e);
0575: }
0576:
0577: if (originalClass.getComponentType()
0578: .isAssignableFrom(elementClass)) {
0579: originalClass = elementClass;
0580:
0581: methods.add(originalClass);
0582:
0583: } else {
0584: throw new JoftiException("element type "
0585: + elementClass
0586: + " is not assignable to "
0587: + originalClass.getComponentType());
0588: }
0589:
0590: } else if (originalClass
0591: .isAssignableFrom(Collection.class)
0592: && attributes[i].equals("[element]")) {
0593:
0594: // check that the component type is not an object
0595: if (originalClass.getComponentType() == null
0596: || originalClass.getComponentType().equals(
0597: Object.class)) {
0598: throw new JoftiException(
0599: "Types to be indexed in a Collection must be specified by Type");
0600: }
0601:
0602: } else if (Collection.class
0603: .isAssignableFrom(originalClass)) {
0604:
0605: String tempClass = ((String) attributes[i])
0606: .substring(1, ((String) attributes[i])
0607: .length() - 1);
0608: methods.add(COLLECTIONS_METHOD);
0609: Class elementClass = null;
0610: try {
0611: elementClass = ReflectionUtil
0612: .classForName(tempClass);
0613: } catch (Exception e) {
0614: throw new JoftiException(e);
0615: }
0616:
0617: methods.add(elementClass);
0618: originalClass = elementClass;
0619: } else {
0620:
0621: tempMethod = getMethod(originalClass,
0622: (String) attributes[i], noArgs);
0623:
0624: methods.add(tempMethod);
0625: originalClass = tempMethod.getReturnType();
0626:
0627: }
0628:
0629: }
0630:
0631: } catch (NoSuchMethodException nme) {
0632: throw new JoftiException(nme);
0633: }
0634:
0635: return methods.toArray();
0636: }
0637:
0638: private Method getMethod(Class originalClass, String attribute,
0639: Class[] args) throws NoSuchMethodException {
0640: StringBuffer buf = new StringBuffer(attribute);
0641: buf.setCharAt(0, Character.toUpperCase(buf.charAt(0)));
0642: buf.insert(0, METHOD_PREFIX);
0643: return originalClass.getMethod(buf.toString(), args);
0644:
0645: }
0646:
0647: /* (non-Javadoc)
0648: * @see com.jofti.introspect.ClassIntrospector#getDimension(java.lang.String, java.lang.String)
0649: */
0650:
0651: public int getDimension(String className, String propertyName)
0652: throws JoftiException {
0653: Class clazz = null;
0654:
0655: try {
0656: clazz = ReflectionUtil.classForName(className);
0657: } catch (Exception e) {
0658: log.info("No class found for " + className);
0659: throw new JoftiException(e);
0660: }
0661: return getDimension(clazz, propertyName);
0662: }
0663:
0664: public int getDimension(Class clazz, String propertyName)
0665: throws JoftiException {
0666:
0667: int temp = 0;
0668: ParsedObject obj = (ParsedObject) dimensionMap.get(clazz);
0669: if (obj != null) {
0670: Object returnObj = null;
0671: // first see if it has the null method
0672: if ((returnObj = obj.getFieldValues().get(NULL_ATTRIBUTE)) != null) {
0673: // we should return the dimension ofr the primitive class itself
0674: temp = ((Integer) returnObj).intValue();
0675: } else if ((returnObj = obj.getFieldValues().get(
0676: ARRAY_ATTRIBUTE)) != null) {
0677: // we should return the dimension ofr the array class itself
0678: temp = ((Integer) returnObj).intValue();
0679: } else {
0680: try {
0681: temp = ((Integer) obj.getFieldValues().get(
0682: propertyName)).intValue();
0683: } catch (Throwable e) {
0684: throw new PropertyNotIndexedException(
0685: "no dimension found for property "
0686: + propertyName);
0687: }
0688: }
0689: } else {
0690: throw new PropertyNotIndexedException(
0691: "no dimension found for class " + clazz);
0692: }
0693: return temp;
0694: }
0695:
0696: /* (non-Javadoc)
0697: * @see com.jofti.introspect.ClassIntrospector#getKeyDimension()
0698: */
0699: public int getKeyDimension() {
0700:
0701: return KEY_DIMENSION_VALUE;
0702: }
0703:
0704: public synchronized int getKeyDimension(Class className) {
0705:
0706: Integer temp = (Integer) keyDimensionMap.get(className);
0707: if (temp == null) {
0708: temp = new Integer(keyDimensionCount--);
0709: keyDimensionMap.put(className, temp);
0710: }
0711: return temp.intValue();
0712: }
0713:
0714: /* (non-Javadoc)
0715: * @see com.jofti.introspect.ClassIntrospector#getAttributeValues()
0716: */
0717: public Map getAttributeValues(Object obj) throws JoftiException {
0718:
0719: if (obj == null) {
0720: return new HashMap(1);
0721: }
0722:
0723: obj = utils.wrapObject(obj);
0724:
0725: Class objClass = obj.getClass();
0726: List parsedList = getParsedObjectsForClass(objClass);
0727:
0728: //
0729: if (parsedList.size() > 0) {
0730: return getValuesFromParsedObjects(obj, parsedList);
0731: } else {
0732:
0733: if (log.isDebugEnabled()) {
0734: log.debug("no mapping found for class " + objClass);
0735: }
0736: return new HashMap(1);
0737: }
0738: }
0739:
0740: public List getParsedObjectsForClass(Class clazz) {
0741: List temp = new ArrayList();
0742:
0743: // first get the interfaces
0744: Class[] interfaceArray = (Class[]) interfaceMap.get(clazz);
0745: if (interfaceArray == null) {
0746: interfaceArray = utils.getInterfaces(clazz);
0747: // we can't cache proxy xlasses as they might change
0748: if (!java.lang.reflect.Proxy.isProxyClass(clazz)) {
0749: interfaceMap.put(clazz, interfaceArray);
0750: }
0751: }
0752: temp = getParsedClassesForArray(interfaceArray, temp);
0753:
0754: // if not a proxy then do the concrete classes
0755: if (!java.lang.reflect.Proxy.isProxyClass(clazz)) {
0756: Class[] concreteArray = (Class[]) concreteMap.get(clazz);
0757: if (concreteArray == null) {
0758: concreteArray = utils.getClasses(clazz);
0759: concreteMap.put(clazz, concreteArray);
0760: }
0761: temp = getParsedClassesForArray(concreteArray, temp);
0762: }
0763: return temp;
0764:
0765: }
0766:
0767: private List getParsedClassesForArray(Class[] array, List temp) {
0768: for (int i = 0; i < array.length; i++) {
0769: // get all the parsedObjects that match
0770: ParsedObject parsedObj = (ParsedObject) dimensionMap
0771: .get(array[i]);
0772: if (parsedObj != null) {
0773: temp.add(parsedObj);
0774: }
0775: }
0776: return temp;
0777: }
0778:
0779: private Map getValuesFromParsedObjects(Object obj,
0780: List parsedObjects) {
0781: Map temp = new HashMap(2);
0782:
0783: for (int i = 0; i < parsedObjects.size(); i++) {
0784: ParsedObject parsedObj = (ParsedObject) parsedObjects
0785: .get(i);
0786: temp = getValuesFromParsedObject(temp, obj, parsedObj);
0787: }
0788: return temp;
0789:
0790: }
0791:
0792: public Map getResultValuesFromObject(Map temp, Object obj,
0793: Map methodMap) {
0794: int size = methodMap.size();
0795: Iterator it = methodMap.entrySet().iterator();
0796: for (int j = 0; j < size; j++) {
0797: Object tempObj = obj;
0798: // loop through the objects and index the value
0799: // turn this into a queue
0800: Map.Entry entry = (Map.Entry) it.next();
0801: Object[] methods = (Object[]) entry.getValue();
0802: if (methods == null) {
0803:
0804: temp.put(entry.getKey(), tempObj);
0805: } else {
0806: List results = new ArrayList();
0807: try {
0808: tempObj = processMethodArray(tempObj, methods);
0809:
0810: if (tempObj != null) {
0811:
0812: temp.put(entry.getKey(), tempObj);
0813: } else {
0814: temp.put(entry.getKey(), null);
0815: }
0816: } catch (Exception e) {
0817: throw new IllegalArgumentException(e.getMessage());
0818:
0819: }
0820: }
0821:
0822: }
0823: return temp;
0824:
0825: }
0826:
0827: private Map getValuesFromParsedObject(Map temp, Object obj,
0828: ParsedObject parsedObj) {
0829:
0830: Set tempSet = parsedObj.getMethodValues().entrySet();
0831: int size = tempSet.size();
0832: Iterator it = tempSet.iterator();
0833: for (int j = 0; j < size; j++) {
0834: Object tempObj = obj;
0835: // loop through the objects and index the value
0836: // turn this into a queue
0837: Map.Entry entry = (Map.Entry) it.next();
0838: Object[] methods = (Object[]) entry.getKey();
0839: if (methods == null) {
0840:
0841: temp.put(entry.getValue(), utils.wrapObject(tempObj));
0842: } else {
0843: List results = new ArrayList();
0844: try {
0845: tempObj = processMethodArray(tempObj, methods);
0846:
0847: if (tempObj != null) {
0848:
0849: temp.put(entry.getValue(), utils
0850: .wrapObject(tempObj));
0851: } else {
0852: temp.put(entry.getValue(),
0853: ValueObject.MIN_COMAPARBLE_VALUE);
0854: }
0855: } catch (Exception e) {
0856: throw new IllegalArgumentException(e.getMessage());
0857:
0858: }
0859: }
0860:
0861: }
0862: return temp;
0863: }
0864:
0865: public Object getResultFromMethods(Object obj, Object[] methods)
0866: throws JoftiException {
0867: Object retVal = null;
0868: try {
0869: retVal = processMethodArray(obj, methods);
0870: } catch (Exception e) {
0871: throw new JoftiException(e);
0872: }
0873: return retVal;
0874:
0875: }
0876:
0877: /**
0878: * @param tempObj
0879: * @param methods
0880: * @return
0881: * @throws IllegalAccessException
0882: * @throws InvocationTargetException
0883: */
0884: private Object processMethodArray(Object tempObj, Object[] methods)
0885: throws IllegalAccessException, InvocationTargetException {
0886: for (int i = 0; i < methods.length; i++) {
0887: if (tempObj == null) {
0888: break;
0889: }
0890: if (methods[i] == ARRAY_METHOD) {
0891: // we need to get the values
0892: tempObj = getRealArrayValues(tempObj,
0893: (Class) methods[i + 1]);
0894: } else if (methods[i] == NULL_METHOD) {
0895: // we need to use the actual value
0896: // we do not need to do anything here
0897: } else if (tempObj.getClass().isArray()
0898: && methods[i] instanceof Method) {
0899: //apply the method to each object
0900: tempObj = applyMethodsToArray(tempObj,
0901: (Method) methods[i]);
0902: } else if (tempObj.getClass().isArray()
0903: && methods[i] instanceof Class) {
0904: // get the values out of the array
0905: tempObj = getArrayValues(tempObj, (Class) methods[i]);
0906: } else if (methods[i] == COLLECTIONS_METHOD) {
0907: // we need to get the values
0908: tempObj = getRealCollectionValues((Collection) tempObj,
0909: (Class) methods[i + 1]);
0910: } else if (Collection.class.isAssignableFrom(tempObj
0911: .getClass())
0912: && methods[i] instanceof Method) {
0913: //apply the method to each object
0914: tempObj = applyMethodsToCollection(
0915: (Collection) tempObj, (Method) methods[i]);
0916: } else if (Collection.class.isAssignableFrom(tempObj
0917: .getClass())
0918: && methods[i] instanceof Class) {
0919: // get the values out of the array
0920: tempObj = getCollectionValues((Collection) tempObj,
0921: (Class) methods[i]);
0922: } else {
0923:
0924: tempObj = ((Method) methods[i]).invoke(tempObj, noArgs);
0925: }
0926: }
0927: //tempObject = method.invoke(obj,noArgs);
0928: return tempObj;
0929: }
0930:
0931: private Object[] applyMethodsToArray(Object array, Method meth) {
0932:
0933: int size = Array.getLength(array);
0934: List res = new ArrayList();
0935: for (int i = 0; i < size; i++) {
0936:
0937: Object temp = Array.get(array, i);
0938:
0939: if (temp != null) {
0940:
0941: try {
0942: temp = meth.invoke(temp, noArgs);
0943: } catch (IllegalArgumentException e) {
0944:
0945: log.warn(e);
0946: } catch (IllegalAccessException e) {
0947:
0948: log.warn(e);
0949: } catch (InvocationTargetException e) {
0950: log.warn(e);
0951: }
0952:
0953: }
0954: if (temp != null) {
0955: res.add(temp);
0956: }
0957: }
0958: if (res.size() == 0) {
0959: return null;
0960: } else {
0961: return res.toArray();
0962: }
0963:
0964: }
0965:
0966: private Object[] applyMethodsToCollection(Collection col,
0967: Method meth) {
0968:
0969: List res = new ArrayList();
0970:
0971: int size = col.size();
0972: Iterator it = col.iterator();
0973: for (int i = 0; i < size; i++) {
0974:
0975: Object temp = it.next();
0976:
0977: if (temp != null) {
0978:
0979: try {
0980: temp = meth.invoke(temp, noArgs);
0981: } catch (IllegalArgumentException e) {
0982: log.warn(e);
0983: } catch (IllegalAccessException e) {
0984: log.warn(e);
0985:
0986: } catch (InvocationTargetException e) {
0987: log.warn(e);
0988: }
0989:
0990: }
0991: if (temp != null) {
0992: res.add(temp);
0993: }
0994: }
0995: if (res.size() == 0) {
0996: return null;
0997: } else {
0998: return res.toArray();
0999: }
1000:
1001: }
1002:
1003: private Object[] getArrayValues(Object array, Class type) {
1004:
1005: int size = Array.getLength(array);
1006: List res = new ArrayList();
1007: for (int i = 0; i < size; i++) {
1008:
1009: Object temp = Array.get(array, i);
1010: if (type.isPrimitive()) {
1011: res.add(utils.wrapObject(temp));
1012: } else if (temp != null
1013: && (type.isAssignableFrom(temp.getClass()) || type
1014: .isAssignableFrom(utils.wrapObject(
1015: temp.getClass()).getClass()))) {
1016: res.add(utils.wrapObject(temp));
1017: }
1018: }
1019: if (res.size() == 0) {
1020: return null;
1021: } else {
1022: return res.toArray();
1023: }
1024:
1025: }
1026:
1027: private Object[] getCollectionValues(Collection col, Class type) {
1028:
1029: List res = new ArrayList();
1030: int size = col.size();
1031: Iterator it = col.iterator();
1032: for (int i = 0; i < size; i++) {
1033:
1034: Object temp = it.next();
1035:
1036: if (temp != null
1037: && (type.isAssignableFrom(temp.getClass()) || type
1038: .isAssignableFrom(utils.wrapObject(
1039: temp.getClass()).getClass()))) {
1040: res.add(utils.wrapObject(temp));
1041: }
1042: }
1043: if (res.size() == 0) {
1044: return null;
1045: } else {
1046: return res.toArray();
1047: }
1048:
1049: }
1050:
1051: private Object[] getRealCollectionValues(Collection col, Class type) {
1052:
1053: List res = new ArrayList();
1054:
1055: int size = col.size();
1056: Iterator it = col.iterator();
1057: for (int i = 0; i < size; i++) {
1058:
1059: Object temp = it.next();
1060:
1061: if (temp != null
1062: && (type.isAssignableFrom(temp.getClass()) || type
1063: .isAssignableFrom(utils.wrapObject(
1064: temp.getClass()).getClass()))) {
1065: res.add(temp);
1066: }
1067: }
1068: if (res.size() == 0) {
1069: return null;
1070: } else {
1071: return res.toArray();
1072: }
1073:
1074: }
1075:
1076: private Object[] getRealArrayValues(Object array, Class type) {
1077:
1078: int size = Array.getLength(array);
1079: List res = new ArrayList();
1080: for (int i = 0; i < size; i++) {
1081:
1082: Object temp = Array.get(array, i);
1083: if (array.getClass().getComponentType().isPrimitive()) {
1084: res.add(temp);
1085: } else if (temp != null
1086: && (type.isAssignableFrom(temp.getClass()) || type
1087: .isAssignableFrom(utils.wrapObject(
1088: temp.getClass()).getClass()))) {
1089: res.add(temp);
1090: }
1091: }
1092: if (res.size() == 0) {
1093: return null;
1094: } else {
1095: return res.toArray();
1096: }
1097:
1098: }
1099:
1100: /* (non-Javadoc)
1101: * @see com.jofti.introspect.ClassIntrospector#getKeyDimensions()
1102: */
1103: public Map getKeyDimensions() {
1104: return keyDimensionMap;
1105: }
1106:
1107: /* (non-Javadoc)
1108: * @see com.jofti.introspect.ClassIntrospector#getPrimitiveClasses()
1109: */
1110: public Set getPrimitiveClasses() {
1111: return primitiveSet;
1112: }
1113:
1114: public Map getDimensions() {
1115:
1116: return dimensionMap;
1117: }
1118:
1119: }
|