KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jxpath > util > ValueUtils


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.jxpath.util;
17
18 import java.beans.IndexedPropertyDescriptor JavaDoc;
19 import java.beans.PropertyDescriptor JavaDoc;
20 import java.lang.reflect.Array JavaDoc;
21 import java.lang.reflect.InvocationTargetException JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import org.apache.commons.jxpath.Container;
33 import org.apache.commons.jxpath.DynamicPropertyHandler;
34 import org.apache.commons.jxpath.JXPathException;
35
36 /**
37  * Collection and property access utilities.
38  *
39  * @author Dmitri Plotnikov
40  * @version $Revision: 1.19 $ $Date: 2004/04/04 22:06:36 $
41  */

42 public class ValueUtils {
43     private static Map JavaDoc dynamicPropertyHandlerMap = new HashMap JavaDoc();
44     private static final int UNKNOWN_LENGTH_MAX_COUNT = 16000;
45
46     /**
47      * Returns true if the object is an array or a Collection
48      */

49     public static boolean isCollection(Object JavaDoc value) {
50         if (value == null) {
51             return false;
52         }
53         value = getValue(value);
54         if (value.getClass().isArray()) {
55             return true;
56         }
57         else if (value instanceof Collection JavaDoc) {
58             return true;
59         }
60         return false;
61     }
62     
63     /**
64      * Returns 1 if the type is a collection,
65      * -1 if it is definitely not
66      * and 0 if it may be a collection in some cases.
67      */

68     public static int getCollectionHint(Class JavaDoc clazz) {
69         if (clazz.isArray()) {
70             return 1;
71         }
72         
73         if (Collection JavaDoc.class.isAssignableFrom(clazz)) {
74             return 1;
75         }
76         
77         if (clazz.isPrimitive()) {
78             return -1;
79         }
80         
81         if (clazz.isInterface()) {
82             return 0;
83         }
84         
85         if (Modifier.isFinal(clazz.getModifiers())) {
86             return -1;
87         }
88                 
89         return 0;
90     }
91     
92     /**
93      * If there is a regular non-indexed read method for this property,
94      * uses this method to obtain the collection and then returns its
95      * length.
96      * Otherwise, attempts to guess the length of the collection by
97      * calling the indexed get method repeatedly. The method is supposed
98      * to throw an exception if the index is out of bounds.
99      */

100     public static int getIndexedPropertyLength(
101         Object JavaDoc object,
102         IndexedPropertyDescriptor JavaDoc pd)
103     {
104         if (pd.getReadMethod() != null) {
105             return getLength(getValue(object, pd));
106         }
107         
108         Method JavaDoc readMethod = pd.getIndexedReadMethod();
109         if (readMethod == null) {
110             throw new JXPathException(
111                 "No indexed read method for property " + pd.getName());
112         }
113
114         for (int i = 0; i < UNKNOWN_LENGTH_MAX_COUNT; i++) {
115             try {
116                 readMethod.invoke(object, new Object JavaDoc[] { new Integer JavaDoc(i)});
117             }
118             catch (Throwable JavaDoc t) {
119                 return i;
120             }
121         }
122         
123         throw new JXPathException(
124             "Cannot determine the length of the indexed property "
125                 + pd.getName());
126     }
127
128     /**
129      * Returns the length of the supplied collection. If the supplied object
130      * is not a collection, returns 1. If collection is null, returns 0.
131      */

132     public static int getLength(Object JavaDoc collection) {
133         if (collection == null) {
134             return 0;
135         }
136         collection = getValue(collection);
137         if (collection.getClass().isArray()) {
138             return Array.getLength(collection);
139         }
140         else if (collection instanceof Collection JavaDoc) {
141             return ((Collection JavaDoc) collection).size();
142         }
143         else {
144             return 1;
145         }
146     }
147
148     /**
149      * Returns an iterator for the supplied collection. If the argument
150      * is null, returns an empty iterator. If the argument is not
151      * a collection, returns an iterator that produces just that one object.
152      */

153     public static Iterator JavaDoc iterate(Object JavaDoc collection) {
154         if (collection == null) {
155             return Collections.EMPTY_LIST.iterator();
156         }
157         if (collection.getClass().isArray()) {
158             int length = Array.getLength(collection);
159             if (length == 0) {
160                 return Collections.EMPTY_LIST.iterator();
161             }
162             ArrayList JavaDoc list = new ArrayList JavaDoc();
163             for (int i = 0; i < length; i++) {
164                 list.add(Array.get(collection, i));
165             }
166             return list.iterator();
167         }
168         else if (collection instanceof Collection JavaDoc) {
169             return ((Collection JavaDoc) collection).iterator();
170         }
171         else {
172             return Collections.singletonList(collection).iterator();
173         }
174     }
175
176     /**
177      * Grows the collection if necessary to the specified size. Returns
178      * the new, expanded collection.
179      */

180     public static Object JavaDoc expandCollection(Object JavaDoc collection, int size) {
181         if (collection == null) {
182             return null;
183         }
184         else if (collection.getClass().isArray()) {
185             Object JavaDoc bigger =
186                 Array.newInstance(
187                     collection.getClass().getComponentType(),
188                     size);
189             System.arraycopy(
190                 collection,
191                 0,
192                 bigger,
193                 0,
194                 Array.getLength(collection));
195             return bigger;
196         }
197         else if (collection instanceof Collection JavaDoc) {
198             while (((Collection JavaDoc) collection).size() < size) {
199                 ((Collection JavaDoc) collection).add(null);
200             }
201             return collection;
202         }
203         else {
204             throw new JXPathException(
205                 "Cannot turn "
206                     + collection.getClass().getName()
207                     + " into a collection of size "
208                     + size);
209         }
210     }
211
212     /**
213      * Returns the index'th element from the supplied collection.
214      */

215     public static Object JavaDoc remove(Object JavaDoc collection, int index) {
216         collection = getValue(collection);
217         if (collection == null) {
218             return null;
219         }
220         else if (collection.getClass().isArray()) {
221             int length = Array.getLength(collection);
222             Object JavaDoc smaller =
223                 Array.newInstance(
224                     collection.getClass().getComponentType(),
225                     length - 1);
226             if (index > 0) {
227                 System.arraycopy(collection, 0, smaller, 0, index);
228             }
229             if (index < length - 1) {
230                 System.arraycopy(
231                     collection,
232                     index + 1,
233                     smaller,
234                     index,
235                     length - index - 1);
236             }
237             return smaller;
238         }
239         else if (collection instanceof List JavaDoc) {
240             int size = ((List JavaDoc) collection).size();
241             if (index < size) {
242                 ((List JavaDoc) collection).remove(index);
243             }
244             return collection;
245         }
246         else if (collection instanceof Collection JavaDoc) {
247             Iterator JavaDoc it = ((Collection JavaDoc) collection).iterator();
248             for (int i = 0; i < index; i++) {
249                 if (!it.hasNext()) {
250                     break;
251                 }
252                 it.next();
253             }
254             if (it.hasNext()) {
255                 it.next();
256                 it.remove();
257             }
258             return collection;
259         }
260         else {
261             throw new JXPathException(
262                 "Cannot remove "
263                     + collection.getClass().getName()
264                     + "["
265                     + index
266                     + "]");
267         }
268     }
269
270     /**
271      * Returns the index'th element of the supplied collection.
272      */

273     public static Object JavaDoc getValue(Object JavaDoc collection, int index) {
274         collection = getValue(collection);
275         Object JavaDoc value = collection;
276         if (collection != null) {
277             if (collection.getClass().isArray()) {
278                 if (index < 0 || index >= Array.getLength(collection)) {
279                     return null;
280                 }
281                 value = Array.get(collection, index);
282             }
283             else if (collection instanceof List JavaDoc) {
284                 if (index < 0 || index >= ((List JavaDoc) collection).size()) {
285                     return null;
286                 }
287                 value = ((List JavaDoc) collection).get(index);
288             }
289             else if (collection instanceof Collection JavaDoc) {
290                 int i = 0;
291                 Iterator JavaDoc it = ((Collection JavaDoc) collection).iterator();
292                 for (; i < index; i++) {
293                     it.next();
294                 }
295                 if (it.hasNext()) {
296                     value = it.next();
297                 }
298                 else {
299                     value = null;
300                 }
301             }
302         }
303         return value;
304     }
305
306     /**
307      * Modifies the index'th element of the supplied collection.
308      * Converts the value to the required type if necessary.
309      */

310     public static void setValue(Object JavaDoc collection, int index, Object JavaDoc value) {
311         collection = getValue(collection);
312         if (collection != null) {
313             if (collection.getClass().isArray()) {
314                 Array.set(
315                     collection,
316                     index,
317                     convert(value, collection.getClass().getComponentType()));
318             }
319             else if (collection instanceof List JavaDoc) {
320                 ((List JavaDoc) collection).set(index, value);
321             }
322             else if (collection instanceof Collection JavaDoc) {
323                 throw new UnsupportedOperationException JavaDoc(
324                     "Cannot set value of an element of a "
325                         + collection.getClass().getName());
326             }
327         }
328     }
329
330     /**
331      * Returns the value of the bean's property represented by
332      * the supplied property descriptor.
333      */

334     public static Object JavaDoc getValue(
335         Object JavaDoc bean,
336         PropertyDescriptor JavaDoc propertyDescriptor)
337     {
338         Object JavaDoc value;
339         try {
340             Method JavaDoc method =
341                 getAccessibleMethod(propertyDescriptor.getReadMethod());
342             if (method == null) {
343                 throw new JXPathException("No read method");
344             }
345             value = method.invoke(bean, new Object JavaDoc[0]);
346         }
347         catch (Exception JavaDoc ex) {
348             throw new JXPathException(
349                 "Cannot access property: "
350                     + (bean == null ? "null" : bean.getClass().getName())
351                     + "."
352                     + propertyDescriptor.getName(),
353                 ex);
354         }
355         return value;
356     }
357
358     /**
359      * Modifies the value of the bean's property represented by
360      * the supplied property descriptor.
361      */

362     public static void setValue(
363         Object JavaDoc bean,
364         PropertyDescriptor JavaDoc propertyDescriptor,
365         Object JavaDoc value)
366     {
367         try {
368             Method JavaDoc method =
369                 getAccessibleMethod(propertyDescriptor.getWriteMethod());
370             if (method == null) {
371                 throw new JXPathException("No write method");
372             }
373             value = convert(value, propertyDescriptor.getPropertyType());
374             value = method.invoke(bean, new Object JavaDoc[] { value });
375         }
376         catch (Exception JavaDoc ex) {
377             throw new JXPathException(
378                 "Cannot modify property: "
379                     + (bean == null ? "null" : bean.getClass().getName())
380                     + "."
381                     + propertyDescriptor.getName(),
382                 ex);
383         }
384     }
385
386     private static Object JavaDoc convert(Object JavaDoc value, Class JavaDoc type) {
387         try {
388             return TypeUtils.convert(value, type);
389         }
390         catch (Exception JavaDoc ex) {
391             throw new JXPathException(
392                 "Cannot convert value of class "
393                     + (value == null ? "null" : value.getClass().getName())
394                     + " to type "
395                     + type,
396                 ex);
397         }
398     }
399
400     /**
401      * Returns the index'th element of the bean's property represented by
402      * the supplied property descriptor.
403      */

404     public static Object JavaDoc getValue(
405         Object JavaDoc bean,
406         PropertyDescriptor JavaDoc propertyDescriptor,
407         int index)
408     {
409         if (propertyDescriptor instanceof IndexedPropertyDescriptor JavaDoc) {
410             try {
411                 IndexedPropertyDescriptor JavaDoc ipd =
412                     (IndexedPropertyDescriptor JavaDoc) propertyDescriptor;
413                 Method JavaDoc method = ipd.getIndexedReadMethod();
414                 if (method != null) {
415                     return method.invoke(
416                         bean,
417                         new Object JavaDoc[] { new Integer JavaDoc(index)});
418                 }
419             }
420             catch (InvocationTargetException JavaDoc ex) {
421                 Throwable JavaDoc t =
422                     ((InvocationTargetException JavaDoc) ex).getTargetException();
423                 if (t instanceof ArrayIndexOutOfBoundsException JavaDoc) {
424                     return null;
425                 }
426                 
427                 throw new JXPathException(
428                     "Cannot access property: " + propertyDescriptor.getName(),
429                     t);
430             }
431             catch (Throwable JavaDoc ex) {
432                 throw new JXPathException(
433                     "Cannot access property: " + propertyDescriptor.getName(),
434                     ex);
435             }
436         }
437
438         // We will fall through if there is no indexed read
439

440         return getValue(getValue(bean, propertyDescriptor), index);
441     }
442
443     /**
444      * Modifies the index'th element of the bean's property represented by
445      * the supplied property descriptor. Converts the value to the required
446      * type if necessary.
447      */

448     public static void setValue(
449         Object JavaDoc bean,
450         PropertyDescriptor JavaDoc propertyDescriptor,
451         int index,
452         Object JavaDoc value)
453     {
454         if (propertyDescriptor instanceof IndexedPropertyDescriptor JavaDoc) {
455             try {
456                 IndexedPropertyDescriptor JavaDoc ipd =
457                     (IndexedPropertyDescriptor JavaDoc) propertyDescriptor;
458                 Method JavaDoc method = ipd.getIndexedWriteMethod();
459                 if (method != null) {
460                     method.invoke(
461                         bean,
462                         new Object JavaDoc[] {
463                             new Integer JavaDoc(index),
464                             convert(value, ipd.getIndexedPropertyType())});
465                     return;
466                 }
467             }
468             catch (Exception JavaDoc ex) {
469                 throw new RuntimeException JavaDoc(
470                     "Cannot access property: "
471                         + propertyDescriptor.getName()
472                         + ", "
473                         + ex.getMessage());
474             }
475         }
476         // We will fall through if there is no indexed read
477
Object JavaDoc collection = getValue(bean, propertyDescriptor);
478         if (isCollection(collection)) {
479             setValue(collection, index, value);
480         }
481         else if (index == 0) {
482             setValue(bean, propertyDescriptor, value);
483         }
484         else {
485             throw new RuntimeException JavaDoc(
486                 "Not a collection: " + propertyDescriptor.getName());
487         }
488     }
489
490     /**
491      * If the parameter is a container, opens the container and
492      * return the contents. The method is recursive.
493      */

494     public static Object JavaDoc getValue(Object JavaDoc object) {
495         while (object instanceof Container) {
496             object = ((Container) object).getValue();
497         }
498         return object;
499     }
500     
501     /**
502      * Returns a shared instance of the dynamic property handler class
503      * returned by <code>getDynamicPropertyHandlerClass()</code>.
504      */

505     public static DynamicPropertyHandler getDynamicPropertyHandler(Class JavaDoc clazz)
506     {
507         DynamicPropertyHandler handler =
508             (DynamicPropertyHandler) dynamicPropertyHandlerMap.get(clazz);
509         if (handler == null) {
510             try {
511                 handler = (DynamicPropertyHandler) clazz.newInstance();
512             }
513             catch (Exception JavaDoc ex) {
514                 throw new JXPathException(
515                     "Cannot allocate dynamic property handler of class "
516                         + clazz.getName(),
517                     ex);
518             }
519             dynamicPropertyHandlerMap.put(clazz, handler);
520         }
521         return handler;
522     }
523
524     // -------------------------------------------------------- Private Methods
525
//
526
// The rest of the code in this file was copied FROM
527
// org.apache.commons.beanutils.PropertyUtil. We don't want to introduce
528
// a dependency on BeanUtils yet - DP.
529
//
530

531     /**
532      * Return an accessible method (that is, one that can be invoked via
533      * reflection) that implements the specified Method. If no such method
534      * can be found, return <code>null</code>.
535      *
536      * @param method The method that we wish to call
537      */

538     public static Method JavaDoc getAccessibleMethod(Method JavaDoc method) {
539
540         // Make sure we have a method to check
541
if (method == null) {
542             return (null);
543         }
544
545         // If the requested method is not public we cannot call it
546
if (!Modifier.isPublic(method.getModifiers())) {
547             return (null);
548         }
549
550         // If the declaring class is public, we are done
551
Class JavaDoc clazz = method.getDeclaringClass();
552         if (Modifier.isPublic(clazz.getModifiers())) {
553             return (method);
554         }
555
556         // Check the implemented interfaces and subinterfaces
557
method =
558             getAccessibleMethodFromInterfaceNest(
559                 clazz,
560                 method.getName(),
561                 method.getParameterTypes());
562         return (method);
563     }
564
565
566     /**
567      * Return an accessible method (that is, one that can be invoked via
568      * reflection) that implements the specified method, by scanning through
569      * all implemented interfaces and subinterfaces. If no such Method
570      * can be found, return <code>null</code>.
571      *
572      * @param clazz Parent class for the interfaces to be checked
573      * @param methodName Method name of the method we wish to call
574      * @param parameterTypes The parameter type signatures
575      */

576     private static Method JavaDoc getAccessibleMethodFromInterfaceNest(
577         Class JavaDoc clazz,
578         String JavaDoc methodName,
579         Class JavaDoc parameterTypes[])
580     {
581
582         Method JavaDoc method = null;
583
584         // Check the implemented interfaces of the parent class
585
Class JavaDoc interfaces[] = clazz.getInterfaces();
586         for (int i = 0; i < interfaces.length; i++) {
587
588             // Is this interface public?
589
if (!Modifier.isPublic(interfaces[i].getModifiers())) {
590                 continue;
591             }
592
593             // Does the method exist on this interface?
594
try {
595                 method =
596                     interfaces[i].getDeclaredMethod(methodName, parameterTypes);
597             }
598             catch (NoSuchMethodException JavaDoc e) {
599                 ;
600             }
601             if (method != null) {
602                 break;
603             }
604             
605             // Recursively check our parent interfaces
606
method =
607                 getAccessibleMethodFromInterfaceNest(
608                     interfaces[i],
609                     methodName,
610                     parameterTypes);
611             if (method != null) {
612                 break;
613             }
614         }
615
616         // Return whatever we have found
617
return (method);
618     }
619 }
Popular Tags