- /*
- * Copyright 1999-2004 The Apache Software Foundation
- *
- * 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.apache.commons.jxpath.util;
-
- import java.beans.IndexedPropertyDescriptor;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.Array;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
-
- import org.apache.commons.jxpath.Container;
- import org.apache.commons.jxpath.DynamicPropertyHandler;
- import org.apache.commons.jxpath.JXPathException;
-
- /**
- * Collection and property access utilities.
- *
- * @author Dmitri Plotnikov
- * @version $Revision: 1.19 $ $Date: 2004/04/04 22:06:36 $
- */
- public class ValueUtils {
- private static Map dynamicPropertyHandlerMap = new HashMap();
- private static final int UNKNOWN_LENGTH_MAX_COUNT = 16000;
-
- /**
- * Returns true if the object is an array or a Collection
- */
- public static boolean isCollection(Object value) {
- if (value == null) {
- return false;
- }
- value = getValue(value);
- if (value.getClass().isArray()) {
- return true;
- }
- else if (value instanceof Collection) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns 1 if the type is a collection,
- * -1 if it is definitely not
- * and 0 if it may be a collection in some cases.
- */
- public static int getCollectionHint(Class clazz) {
- if (clazz.isArray()) {
- return 1;
- }
-
- if (Collection.class.isAssignableFrom(clazz)) {
- return 1;
- }
-
- if (clazz.isPrimitive()) {
- return -1;
- }
-
- if (clazz.isInterface()) {
- return 0;
- }
-
- if (Modifier.isFinal(clazz.getModifiers())) {
- return -1;
- }
-
- return 0;
- }
-
- /**
- * If there is a regular non-indexed read method for this property,
- * uses this method to obtain the collection and then returns its
- * length.
- * Otherwise, attempts to guess the length of the collection by
- * calling the indexed get method repeatedly. The method is supposed
- * to throw an exception if the index is out of bounds.
- */
- public static int getIndexedPropertyLength(
- Object object,
- IndexedPropertyDescriptor pd)
- {
- if (pd.getReadMethod() != null) {
- return getLength(getValue(object, pd));
- }
-
- Method readMethod = pd.getIndexedReadMethod();
- if (readMethod == null) {
- throw new JXPathException(
- "No indexed read method for property " + pd.getName());
- }
-
- for (int i = 0; i < UNKNOWN_LENGTH_MAX_COUNT; i++) {
- try {
- readMethod.invoke(object, new Object[] { new Integer(i)});
- }
- catch (Throwable t) {
- return i;
- }
- }
-
- throw new JXPathException(
- "Cannot determine the length of the indexed property "
- + pd.getName());
- }
-
- /**
- * Returns the length of the supplied collection. If the supplied object
- * is not a collection, returns 1. If collection is null, returns 0.
- */
- public static int getLength(Object collection) {
- if (collection == null) {
- return 0;
- }
- collection = getValue(collection);
- if (collection.getClass().isArray()) {
- return Array.getLength(collection);
- }
- else if (collection instanceof Collection) {
- return ((Collection) collection).size();
- }
- else {
- return 1;
- }
- }
-
- /**
- * Returns an iterator for the supplied collection. If the argument
- * is null, returns an empty iterator. If the argument is not
- * a collection, returns an iterator that produces just that one object.
- */
- public static Iterator iterate(Object collection) {
- if (collection == null) {
- return Collections.EMPTY_LIST.iterator();
- }
- if (collection.getClass().isArray()) {
- int length = Array.getLength(collection);
- if (length == 0) {
- return Collections.EMPTY_LIST.iterator();
- }
- ArrayList list = new ArrayList();
- for (int i = 0; i < length; i++) {
- list.add(Array.get(collection, i));
- }
- return list.iterator();
- }
- else if (collection instanceof Collection) {
- return ((Collection) collection).iterator();
- }
- else {
- return Collections.singletonList(collection).iterator();
- }
- }
-
- /**
- * Grows the collection if necessary to the specified size. Returns
- * the new, expanded collection.
- */
- public static Object expandCollection(Object collection, int size) {
- if (collection == null) {
- return null;
- }
- else if (collection.getClass().isArray()) {
- Object bigger =
- Array.newInstance(
- collection.getClass().getComponentType(),
- size);
- System.arraycopy(
- collection,
- 0,
- bigger,
- 0,
- Array.getLength(collection));
- return bigger;
- }
- else if (collection instanceof Collection) {
- while (((Collection) collection).size() < size) {
- ((Collection) collection).add(null);
- }
- return collection;
- }
- else {
- throw new JXPathException(
- "Cannot turn "
- + collection.getClass().getName()
- + " into a collection of size "
- + size);
- }
- }
-
- /**
- * Returns the index'th element from the supplied collection.
- */
- public static Object remove(Object collection, int index) {
- collection = getValue(collection);
- if (collection == null) {
- return null;
- }
- else if (collection.getClass().isArray()) {
- int length = Array.getLength(collection);
- Object smaller =
- Array.newInstance(
- collection.getClass().getComponentType(),
- length - 1);
- if (index > 0) {
- System.arraycopy(collection, 0, smaller, 0, index);
- }
- if (index < length - 1) {
- System.arraycopy(
- collection,
- index + 1,
- smaller,
- index,
- length - index - 1);
- }
- return smaller;
- }
- else if (collection instanceof List) {
- int size = ((List) collection).size();
- if (index < size) {
- ((List) collection).remove(index);
- }
- return collection;
- }
- else if (collection instanceof Collection) {
- Iterator it = ((Collection) collection).iterator();
- for (int i = 0; i < index; i++) {
- if (!it.hasNext()) {
- break;
- }
- it.next();
- }
- if (it.hasNext()) {
- it.next();
- it.remove();
- }
- return collection;
- }
- else {
- throw new JXPathException(
- "Cannot remove "
- + collection.getClass().getName()
- + "["
- + index
- + "]");
- }
- }
-
- /**
- * Returns the index'th element of the supplied collection.
- */
- public static Object getValue(Object collection, int index) {
- collection = getValue(collection);
- Object value = collection;
- if (collection != null) {
- if (collection.getClass().isArray()) {
- if (index < 0 || index >= Array.getLength(collection)) {
- return null;
- }
- value = Array.get(collection, index);
- }
- else if (collection instanceof List) {
- if (index < 0 || index >= ((List) collection).size()) {
- return null;
- }
- value = ((List) collection).get(index);
- }
- else if (collection instanceof Collection) {
- int i = 0;
- Iterator it = ((Collection) collection).iterator();
- for (; i < index; i++) {
- it.next();
- }
- if (it.hasNext()) {
- value = it.next();
- }
- else {
- value = null;
- }
- }
- }
- return value;
- }
-
- /**
- * Modifies the index'th element of the supplied collection.
- * Converts the value to the required type if necessary.
- */
- public static void setValue(Object collection, int index, Object value) {
- collection = getValue(collection);
- if (collection != null) {
- if (collection.getClass().isArray()) {
- Array.set(
- collection,
- index,
- convert(value, collection.getClass().getComponentType()));
- }
- else if (collection instanceof List) {
- ((List) collection).set(index, value);
- }
- else if (collection instanceof Collection) {
- throw new UnsupportedOperationException(
- "Cannot set value of an element of a "
- + collection.getClass().getName());
- }
- }
- }
-
- /**
- * Returns the value of the bean's property represented by
- * the supplied property descriptor.
- */
- public static Object getValue(
- Object bean,
- PropertyDescriptor propertyDescriptor)
- {
- Object value;
- try {
- Method method =
- getAccessibleMethod(propertyDescriptor.getReadMethod());
- if (method == null) {
- throw new JXPathException("No read method");
- }
- value = method.invoke(bean, new Object[0]);
- }
- catch (Exception ex) {
- throw new JXPathException(
- "Cannot access property: "
- + (bean == null ? "null" : bean.getClass().getName())
- + "."
- + propertyDescriptor.getName(),
- ex);
- }
- return value;
- }
-
- /**
- * Modifies the value of the bean's property represented by
- * the supplied property descriptor.
- */
- public static void setValue(
- Object bean,
- PropertyDescriptor propertyDescriptor,
- Object value)
- {
- try {
- Method method =
- getAccessibleMethod(propertyDescriptor.getWriteMethod());
- if (method == null) {
- throw new JXPathException("No write method");
- }
- value = convert(value, propertyDescriptor.getPropertyType());
- value = method.invoke(bean, new Object[] { value });
- }
- catch (Exception ex) {
- throw new JXPathException(
- "Cannot modify property: "
- + (bean == null ? "null" : bean.getClass().getName())
- + "."
- + propertyDescriptor.getName(),
- ex);
- }
- }
-
- private static Object convert(Object value, Class type) {
- try {
- return TypeUtils.convert(value, type);
- }
- catch (Exception ex) {
- throw new JXPathException(
- "Cannot convert value of class "
- + (value == null ? "null" : value.getClass().getName())
- + " to type "
- + type,
- ex);
- }
- }
-
- /**
- * Returns the index'th element of the bean's property represented by
- * the supplied property descriptor.
- */
- public static Object getValue(
- Object bean,
- PropertyDescriptor propertyDescriptor,
- int index)
- {
- if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
- try {
- IndexedPropertyDescriptor ipd =
- (IndexedPropertyDescriptor) propertyDescriptor;
- Method method = ipd.getIndexedReadMethod();
- if (method != null) {
- return method.invoke(
- bean,
- new Object[] { new Integer(index)});
- }
- }
- catch (InvocationTargetException ex) {
- Throwable t =
- ((InvocationTargetException) ex).getTargetException();
- if (t instanceof ArrayIndexOutOfBoundsException) {
- return null;
- }
-
- throw new JXPathException(
- "Cannot access property: " + propertyDescriptor.getName(),
- t);
- }
- catch (Throwable ex) {
- throw new JXPathException(
- "Cannot access property: " + propertyDescriptor.getName(),
- ex);
- }
- }
-
- // We will fall through if there is no indexed read
-
- return getValue(getValue(bean, propertyDescriptor), index);
- }
-
- /**
- * Modifies the index'th element of the bean's property represented by
- * the supplied property descriptor. Converts the value to the required
- * type if necessary.
- */
- public static void setValue(
- Object bean,
- PropertyDescriptor propertyDescriptor,
- int index,
- Object value)
- {
- if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
- try {
- IndexedPropertyDescriptor ipd =
- (IndexedPropertyDescriptor) propertyDescriptor;
- Method method = ipd.getIndexedWriteMethod();
- if (method != null) {
- method.invoke(
- bean,
- new Object[] {
- new Integer(index),
- convert(value, ipd.getIndexedPropertyType())});
- return;
- }
- }
- catch (Exception ex) {
- throw new RuntimeException(
- "Cannot access property: "
- + propertyDescriptor.getName()
- + ", "
- + ex.getMessage());
- }
- }
- // We will fall through if there is no indexed read
- Object collection = getValue(bean, propertyDescriptor);
- if (isCollection(collection)) {
- setValue(collection, index, value);
- }
- else if (index == 0) {
- setValue(bean, propertyDescriptor, value);
- }
- else {
- throw new RuntimeException(
- "Not a collection: " + propertyDescriptor.getName());
- }
- }
-
- /**
- * If the parameter is a container, opens the container and
- * return the contents. The method is recursive.
- */
- public static Object getValue(Object object) {
- while (object instanceof Container) {
- object = ((Container) object).getValue();
- }
- return object;
- }
-
- /**
- * Returns a shared instance of the dynamic property handler class
- * returned by <code>getDynamicPropertyHandlerClass()</code>.
- */
- public static DynamicPropertyHandler getDynamicPropertyHandler(Class clazz)
- {
- DynamicPropertyHandler handler =
- (DynamicPropertyHandler) dynamicPropertyHandlerMap.get(clazz);
- if (handler == null) {
- try {
- handler = (DynamicPropertyHandler) clazz.newInstance();
- }
- catch (Exception ex) {
- throw new JXPathException(
- "Cannot allocate dynamic property handler of class "
- + clazz.getName(),
- ex);
- }
- dynamicPropertyHandlerMap.put(clazz, handler);
- }
- return handler;
- }
-
- // -------------------------------------------------------- Private Methods
- //
- // The rest of the code in this file was copied FROM
- // org.apache.commons.beanutils.PropertyUtil. We don't want to introduce
- // a dependency on BeanUtils yet - DP.
- //
-
- /**
- * Return an accessible method (that is, one that can be invoked via
- * reflection) that implements the specified Method. If no such method
- * can be found, return <code>null</code>.
- *
- * @param method The method that we wish to call
- */
- public static Method getAccessibleMethod(Method method) {
-
- // Make sure we have a method to check
- if (method == null) {
- return (null);
- }
-
- // If the requested method is not public we cannot call it
- if (!Modifier.isPublic(method.getModifiers())) {
- return (null);
- }
-
- // If the declaring class is public, we are done
- Class clazz = method.getDeclaringClass();
- if (Modifier.isPublic(clazz.getModifiers())) {
- return (method);
- }
-
- // Check the implemented interfaces and subinterfaces
- method =
- getAccessibleMethodFromInterfaceNest(
- clazz,
- method.getName(),
- method.getParameterTypes());
- return (method);
- }
-
-
- /**
- * Return an accessible method (that is, one that can be invoked via
- * reflection) that implements the specified method, by scanning through
- * all implemented interfaces and subinterfaces. If no such Method
- * can be found, return <code>null</code>.
- *
- * @param clazz Parent class for the interfaces to be checked
- * @param methodName Method name of the method we wish to call
- * @param parameterTypes The parameter type signatures
- */
- private static Method getAccessibleMethodFromInterfaceNest(
- Class clazz,
- String methodName,
- Class parameterTypes[])
- {
-
- Method method = null;
-
- // Check the implemented interfaces of the parent class
- Class interfaces[] = clazz.getInterfaces();
- for (int i = 0; i < interfaces.length; i++) {
-
- // Is this interface public?
- if (!Modifier.isPublic(interfaces[i].getModifiers())) {
- continue;
- }
-
- // Does the method exist on this interface?
- try {
- method =
- interfaces[i].getDeclaredMethod(methodName, parameterTypes);
- }
- catch (NoSuchMethodException e) {
- ;
- }
- if (method != null) {
- break;
- }
-
- // Recursively check our parent interfaces
- method =
- getAccessibleMethodFromInterfaceNest(
- interfaces[i],
- methodName,
- parameterTypes);
- if (method != null) {
- break;
- }
- }
-
- // Return whatever we have found
- return (method);
- }
- }