001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.util;
018    
019    import java.io.Closeable;
020    import java.io.File;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.lang.annotation.Annotation;
025    import java.lang.reflect.AnnotatedElement;
026    import java.lang.reflect.Array;
027    import java.lang.reflect.Constructor;
028    import java.lang.reflect.Field;
029    import java.lang.reflect.InvocationTargetException;
030    import java.lang.reflect.Method;
031    import java.net.URL;
032    import java.nio.channels.ReadableByteChannel;
033    import java.nio.charset.Charset;
034    import java.util.ArrayList;
035    import java.util.Arrays;
036    import java.util.Collection;
037    import java.util.Collections;
038    import java.util.Enumeration;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Locale;
042    import java.util.Map;
043    import java.util.NoSuchElementException;
044    import java.util.Properties;
045    import java.util.Scanner;
046    
047    import org.w3c.dom.Node;
048    import org.w3c.dom.NodeList;
049    
050    import org.apache.camel.CamelContext;
051    import org.apache.camel.CamelExecutionException;
052    import org.apache.camel.Exchange;
053    import org.apache.camel.Message;
054    import org.apache.camel.Ordered;
055    import org.apache.camel.RuntimeCamelException;
056    import org.apache.camel.TypeConverter;
057    import org.apache.camel.WrappedFile;
058    import org.slf4j.Logger;
059    import org.slf4j.LoggerFactory;
060    
061    
062    /**
063     * A number of useful helper methods for working with Objects
064     *
065     * @version 
066     */
067    public final class ObjectHelper {
068        private static final Logger LOG = LoggerFactory.getLogger(ObjectHelper.class);
069        private static final String DEFAULT_DELIMITER = ",";
070        @SuppressWarnings("unchecked")
071        private static final List<?> PRIMITIVE_ARRAY_TYPES = Arrays.asList(byte[].class, short[].class, int[].class, long[].class,
072                                                                           float[].class, double[].class, char[].class, boolean[].class);
073    
074        /**
075         * Utility classes should not have a public constructor.
076         */
077        private ObjectHelper() {
078        }
079    
080        /**
081         * A helper method for comparing objects for equality in which it uses type coercion to coerce
082         * types between the left and right values. This allows you test for equality for example with
083         * a String and Integer type as Camel will be able to coerce the types.
084         */
085        public static boolean typeCoerceEquals(TypeConverter converter, Object leftValue, Object rightValue) {
086            // sanity check
087            if (leftValue == null && rightValue == null) {
088                // they are equal
089                return true;
090            } else if (leftValue == null || rightValue == null) {
091                // only one of them is null so they are not equal
092                return false;
093            }
094    
095            // try without type coerce
096            boolean answer = equal(leftValue, rightValue);
097            if (answer) {
098                return true;
099            }
100    
101            // are they same type, if so return false as the equals returned false
102            if (leftValue.getClass().isInstance(rightValue)) {
103                return false;
104            }
105    
106            // convert left to right
107            Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
108            answer = equal(value, rightValue);
109            if (answer) {
110                return true;
111            }
112    
113            // convert right to left
114            value = converter.tryConvertTo(leftValue.getClass(), rightValue);
115            answer = equal(leftValue, value);
116            return answer;
117        }
118    
119        /**
120         * A helper method for comparing objects for inequality in which it uses type coercion to coerce
121         * types between the left and right values.  This allows you test for inequality for example with
122         * a String and Integer type as Camel will be able to coerce the types.
123         */
124        public static boolean typeCoerceNotEquals(TypeConverter converter, Object leftValue, Object rightValue) {
125            return !typeCoerceEquals(converter, leftValue, rightValue);
126        }
127    
128        /**
129         * A helper method for comparing objects ordering in which it uses type coercion to coerce
130         * types between the left and right values.  This allows you test for ordering for example with
131         * a String and Integer type as Camel will be able to coerce the types.
132         */
133        @SuppressWarnings({"unchecked", "rawtypes"})
134        public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
135    
136            // if both values is numeric then compare using numeric
137            Long leftNum = converter.tryConvertTo(Long.class, leftValue);
138            Long rightNum = converter.tryConvertTo(Long.class, rightValue);
139            if (leftNum != null && rightNum != null) {
140                return leftNum.compareTo(rightNum);
141            }
142    
143            // also try with floating point numbers
144            Double leftDouble = converter.tryConvertTo(Double.class, leftValue);
145            Double rightDouble = converter.tryConvertTo(Double.class, rightValue);
146            if (leftDouble != null && rightDouble != null) {
147                return leftDouble.compareTo(rightDouble);
148            }
149    
150            // prefer to NOT coerce to String so use the type which is not String
151            // for example if we are comparing String vs Integer then prefer to coerce to Integer
152            // as all types can be converted to String which does not work well for comparison
153            // as eg "10" < 6 would return true, where as 10 < 6 will return false.
154            // if they are both String then it doesn't matter
155            if (rightValue instanceof String && (!(leftValue instanceof String))) {
156                // if right is String and left is not then flip order (remember to * -1 the result then)
157                return typeCoerceCompare(converter, rightValue, leftValue) * -1;
158            }
159    
160            // prefer to coerce to the right hand side at first
161            if (rightValue instanceof Comparable) {
162                Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
163                if (value != null) {
164                    return ((Comparable) rightValue).compareTo(value) * -1;
165                }
166            }
167    
168            // then fallback to the left hand side
169            if (leftValue instanceof Comparable) {
170                Object value = converter.tryConvertTo(leftValue.getClass(), rightValue);
171                if (value != null) {
172                    return ((Comparable) leftValue).compareTo(value);
173                }
174            }
175    
176            // use regular compare
177            return compare(leftValue, rightValue);
178        }
179    
180        /**
181         * A helper method for comparing objects for equality while handling nulls
182         */
183        public static boolean equal(Object a, Object b) {
184            if (a == b) {
185                return true;
186            }
187    
188            if (a instanceof byte[] && b instanceof byte[]) {
189                return equalByteArray((byte[])a, (byte[])b);
190            }
191    
192            return a != null && b != null && a.equals(b);
193        }
194    
195        /**
196         * A helper method for comparing byte arrays for equality while handling
197         * nulls
198         */
199        public static boolean equalByteArray(byte[] a, byte[] b) {
200            if (a == b) {
201                return true;
202            }
203    
204            // loop and compare each byte
205            if (a != null && b != null && a.length == b.length) {
206                for (int i = 0; i < a.length; i++) {
207                    if (a[i] != b[i]) {
208                        return false;
209                    }
210                }
211                // all bytes are equal
212                return true;
213            }
214    
215            return false;
216        }
217    
218        /**
219         * Returns true if the given object is equal to any of the expected value
220         */
221        public static boolean isEqualToAny(Object object, Object... values) {
222            for (Object value : values) {
223                if (equal(object, value)) {
224                    return true;
225                }
226            }
227            return false;
228        }
229    
230        /**
231         * A helper method for performing an ordered comparison on the objects
232         * handling nulls and objects which do not handle sorting gracefully
233         */
234        public static int compare(Object a, Object b) {
235            return compare(a, b, false);
236        }
237    
238        /**
239         * A helper method for performing an ordered comparison on the objects
240         * handling nulls and objects which do not handle sorting gracefully
241         *
242         * @param a  the first object
243         * @param b  the second object
244         * @param ignoreCase  ignore case for string comparison
245         */
246        @SuppressWarnings({"unchecked", "rawtypes"})
247        public static int compare(Object a, Object b, boolean ignoreCase) {
248            if (a == b) {
249                return 0;
250            }
251            if (a == null) {
252                return -1;
253            }
254            if (b == null) {
255                return 1;
256            }
257            if (a instanceof Ordered && b instanceof Ordered) {
258                return ((Ordered) a).getOrder() - ((Ordered) b).getOrder();
259            }
260            if (ignoreCase && a instanceof String && b instanceof String) {
261                return ((String) a).compareToIgnoreCase((String) b);
262            }
263            if (a instanceof Comparable) {
264                Comparable comparable = (Comparable)a;
265                return comparable.compareTo(b);
266            }
267            int answer = a.getClass().getName().compareTo(b.getClass().getName());
268            if (answer == 0) {
269                answer = a.hashCode() - b.hashCode();
270            }
271            return answer;
272        }
273    
274        public static Boolean toBoolean(Object value) {
275            if (value instanceof Boolean) {
276                return (Boolean)value;
277            }
278            if (value instanceof String) {
279                return Boolean.valueOf((String)value);
280            }
281            if (value instanceof Integer) {
282                return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
283            }
284            return null;
285        }
286    
287        /**
288         * Asserts whether the value is <b>not</b> <tt>null</tt>
289         *
290         * @param value  the value to test
291         * @param name   the key that resolved the value
292         * @return the passed {@code value} as is
293         * @throws IllegalArgumentException is thrown if assertion fails
294         */
295        public static <T> T notNull(T value, String name) {
296            if (value == null) {
297                throw new IllegalArgumentException(name + " must be specified");
298            }
299    
300            return value;
301        }
302    
303        /**
304         * Asserts whether the value is <b>not</b> <tt>null</tt>
305         *
306         * @param value  the value to test
307         * @param on     additional description to indicate where this problem occurred (appended as toString())
308         * @param name   the key that resolved the value
309         * @return the passed {@code value} as is
310         * @throws IllegalArgumentException is thrown if assertion fails
311         */
312        public static <T> T notNull(T value, String name, Object on) {
313            if (on == null) {
314                notNull(value, name);
315            } else if (value == null) {
316                throw new IllegalArgumentException(name + " must be specified on: " + on);
317            }
318    
319            return value;
320        }
321    
322        /**
323         * Asserts whether the string is <b>not</b> empty.
324         *
325         * @param value  the string to test
326         * @param name   the key that resolved the value
327         * @return the passed {@code value} as is
328         * @throws IllegalArgumentException is thrown if assertion fails
329         */
330        public static String notEmpty(String value, String name) {
331            if (isEmpty(value)) {
332                throw new IllegalArgumentException(name + " must be specified and not empty");
333            }
334    
335            return value;
336        }
337    
338        /**
339         * Asserts whether the string is <b>not</b> empty.
340         *
341         * @param value  the string to test
342         * @param on     additional description to indicate where this problem occurred (appended as toString())
343         * @param name   the key that resolved the value
344         * @return the passed {@code value} as is
345         * @throws IllegalArgumentException is thrown if assertion fails
346         */
347        public static String notEmpty(String value, String name, Object on) {
348            if (on == null) {
349                notNull(value, name);
350            } else if (isEmpty(value)) {
351                throw new IllegalArgumentException(name + " must be specified and not empty on: " + on);
352            }
353    
354            return value;
355        }
356    
357        /**
358         * Tests whether the value is <tt>null</tt> or an empty string.
359         *
360         * @param value  the value, if its a String it will be tested for text length as well
361         * @return true if empty
362         */
363        public static boolean isEmpty(Object value) {
364            return !isNotEmpty(value);
365        }
366    
367        /**
368         * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string.
369         *
370         * @param value  the value, if its a String it will be tested for text length as well
371         * @return true if <b>not</b> empty
372         */
373        public static boolean isNotEmpty(Object value) {
374            if (value == null) {
375                return false;
376            } else if (value instanceof String) {
377                String text = (String) value;
378                return text.trim().length() > 0;
379            } else {
380                return true;
381            }
382        }
383    
384        public static String[] splitOnCharacter(String value, String needle, int count) {
385            String rc[] = new String[count];
386            rc[0] = value;
387            for (int i = 1; i < count; i++) {
388                String v = rc[i - 1];
389                int p = v.indexOf(needle);
390                if (p < 0) {
391                    return rc;
392                }
393                rc[i - 1] = v.substring(0, p);
394                rc[i] = v.substring(p + 1);
395            }
396            return rc;
397        }
398    
399        /**
400         * Removes any starting characters on the given text which match the given
401         * character
402         *
403         * @param text the string
404         * @param ch the initial characters to remove
405         * @return either the original string or the new substring
406         */
407        public static String removeStartingCharacters(String text, char ch) {
408            int idx = 0;
409            while (text.charAt(idx) == ch) {
410                idx++;
411            }
412            if (idx > 0) {
413                return text.substring(idx);
414            }
415            return text;
416        }
417    
418        public static String capitalize(String text) {
419            if (text == null) {
420                return null;
421            }
422            int length = text.length();
423            if (length == 0) {
424                return text;
425            }
426            String answer = text.substring(0, 1).toUpperCase(Locale.ENGLISH);
427            if (length > 1) {
428                answer += text.substring(1, length);
429            }
430            return answer;
431        }
432    
433        public static String after(String text, String after) {
434            if (!text.contains(after)) {
435                return null;
436            }
437            return text.substring(text.indexOf(after) + after.length());
438        }
439    
440        public static String before(String text, String before) {
441            if (!text.contains(before)) {
442                return null;
443            }
444            return text.substring(0, text.indexOf(before));
445        }
446    
447        public static String between(String text, String after, String before) {
448            text = after(text, after);
449            if (text == null) {
450                return null;
451            }
452            return before(text, before);
453        }
454    
455        /**
456         * Returns true if the collection contains the specified value
457         */
458        public static boolean contains(Object collectionOrArray, Object value) {
459            // favor String types
460            if (collectionOrArray != null && (collectionOrArray instanceof StringBuffer || collectionOrArray instanceof StringBuilder)) {
461                collectionOrArray = collectionOrArray.toString();
462            }
463            if (value != null && (value instanceof StringBuffer || value instanceof StringBuilder)) {
464                value = value.toString();
465            }
466    
467            if (collectionOrArray instanceof Collection) {
468                Collection<?> collection = (Collection<?>)collectionOrArray;
469                return collection.contains(value);
470            } else if (collectionOrArray instanceof String && value instanceof String) {
471                String str = (String)collectionOrArray;
472                String subStr = (String)value;
473                return str.contains(subStr);
474            } else {
475                Iterator<Object> iter = createIterator(collectionOrArray);
476                while (iter.hasNext()) {
477                    if (equal(value, iter.next())) {
478                        return true;
479                    }
480                }
481            }
482            return false;
483        }
484    
485        /**
486         * Creates an iterator over the value if the value is a collection, an
487         * Object[], a String with values separated by comma,
488         * or a primitive type array; otherwise to simplify the caller's code,
489         * we just create a singleton collection iterator over a single value
490         * <p/>
491         * Will default use comma for String separating String values.
492         * This method does <b>not</b> allow empty values
493         *
494         * @param value  the value
495         * @return the iterator
496         */
497        public static Iterator<Object> createIterator(Object value) {
498            return createIterator(value, DEFAULT_DELIMITER);
499        }
500    
501        /**
502         * Creates an iterator over the value if the value is a collection, an
503         * Object[], a String with values separated by the given delimiter,
504         * or a primitive type array; otherwise to simplify the caller's
505         * code, we just create a singleton collection iterator over a single value
506         * <p/>
507         * This method does <b>not</b> allow empty values
508         *
509         * @param value      the value
510         * @param delimiter  delimiter for separating String values
511         * @return the iterator
512         */
513        public static Iterator<Object> createIterator(Object value, String delimiter) {
514            return createIterator(value, delimiter, false);
515        }
516    
517        /**
518         * Creates an iterator over the value if the value is a collection, an
519         * Object[], a String with values separated by the given delimiter,
520         * or a primitive type array; otherwise to simplify the caller's
521         * code, we just create a singleton collection iterator over a single value
522         * 
523         * </p> In case of primitive type arrays the returned {@code Iterator} iterates
524         * over the corresponding Java primitive wrapper objects of the given elements
525         * inside the {@code value} array. That's we get an autoboxing of the primitive
526         * types here for free as it's also the case in Java language itself.
527         * 
528         * @param value             the value
529         * @param delimiter         delimiter for separating String values
530         * @param allowEmptyValues  whether to allow empty values
531         * @return the iterator
532         */
533        @SuppressWarnings("unchecked")
534        public static Iterator<Object> createIterator(Object value, String delimiter, final boolean allowEmptyValues) {
535    
536            // if its a message than we want to iterate its body
537            if (value instanceof Message) {
538                value = ((Message) value).getBody();
539            }
540    
541            if (value == null) {
542                return Collections.emptyList().iterator();
543            } else if (value instanceof Iterator) {
544                return (Iterator<Object>)value;
545            } else if (value instanceof Iterable) {
546                return ((Iterable<Object>)value).iterator();
547            } else if (value.getClass().isArray()) {
548                if (isPrimitiveArrayType(value.getClass())) {
549                    final Object array = value;
550                    return new Iterator<Object>() {
551                        private int idx;
552    
553                        public boolean hasNext() {
554                            return idx < Array.getLength(array);
555                        }
556    
557                        public Object next() {
558                            if (!hasNext()) {
559                                throw new NoSuchElementException("no more element available for '" + array + "' at the index " + idx);
560                            }
561    
562                            return Array.get(array, idx++);
563                        }
564    
565                        public void remove() {
566                            throw new UnsupportedOperationException();
567                        }
568    
569                    };
570                } else {
571                    List<Object> list = Arrays.asList((Object[]) value);
572                    return list.iterator();
573                }
574            } else if (value instanceof NodeList) {
575                // lets iterate through DOM results after performing XPaths
576                final NodeList nodeList = (NodeList) value;
577                return new Iterator<Object>() {
578                    private int idx;
579    
580                    public boolean hasNext() {
581                        return idx < nodeList.getLength();
582                    }
583    
584                    public Object next() {
585                        if (!hasNext()) {
586                            throw new NoSuchElementException("no more element available for '" + nodeList + "' at the index " + idx);
587                        }
588    
589                        return nodeList.item(idx++);
590                    }
591    
592                    public void remove() {
593                        throw new UnsupportedOperationException();
594                    }
595                };
596            } else if (value instanceof String) {
597                final String s = (String) value;
598    
599                // this code is optimized to only use a Scanner if needed, eg there is a delimiter
600    
601                if (delimiter != null && s.contains(delimiter)) {
602                    // use a scanner if it contains the delimiter
603                    Scanner scanner = new Scanner((String)value);
604    
605                    if (DEFAULT_DELIMITER.equals(delimiter)) {
606                        // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
607                        // which may have balanced parentheses pairs as well.
608                        // if the value contains parentheses we need to balance those, to avoid iterating
609                        // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
610                        // the regexp will split by comma, but honor parentheses pair that may include commas
611                        // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
612                        // then the regexp will split that into two:
613                        // -> bean=foo?method=killer(a,b)
614                        // -> bean=bar?method=great(a,b)
615                        // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
616                        delimiter = ",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))";
617                    }
618    
619                    scanner.useDelimiter(delimiter);
620                    return CastUtils.cast(scanner);
621                } else {
622                    // use a plain iterator that returns the value as is as there are only a single value
623                    return new Iterator<Object>() {
624                        private int idx;
625    
626                        public boolean hasNext() {
627                            return idx == 0 && (allowEmptyValues || ObjectHelper.isNotEmpty(s));
628                        }
629    
630                        public Object next() {
631                            if (!hasNext()) {
632                                throw new NoSuchElementException("no more element available for '" + s + "' at the index " + idx);
633                            }
634    
635                            idx++;
636                            return s;
637                        }
638    
639                        public void remove() {
640                            throw new UnsupportedOperationException();
641                        }
642                    };
643                }
644            } else {
645                return Collections.singletonList(value).iterator();
646            }
647        }
648    
649        /**
650         * Returns the predicate matching boolean on a {@link List} result set where
651         * if the first element is a boolean its value is used otherwise this method
652         * returns true if the collection is not empty
653         *
654         * @return <tt>true</tt> if the first element is a boolean and its value
655         *         is true or if the list is non empty
656         */
657        public static boolean matches(List<?> list) {
658            if (!list.isEmpty()) {
659                Object value = list.get(0);
660                if (value instanceof Boolean) {
661                    return (Boolean)value;
662                } else {
663                    // lets assume non-empty results are true
664                    return true;
665                }
666            }
667            return false;
668        }
669    
670        /**
671         * A helper method to access a system property, catching any security exceptions
672         *
673         * @param name         the name of the system property required
674         * @param defaultValue the default value to use if the property is not
675         *                     available or a security exception prevents access
676         * @return the system property value or the default value if the property is
677         *         not available or security does not allow its access
678         */
679        public static String getSystemProperty(String name, String defaultValue) {
680            try {
681                return System.getProperty(name, defaultValue);
682            } catch (Exception e) {
683                if (LOG.isDebugEnabled()) {
684                    LOG.debug("Caught security exception accessing system property: " + name + ". Will use default value: " + defaultValue, e);
685                }
686                return defaultValue;
687            }
688        }
689    
690        /**
691         * A helper method to access a boolean system property, catching any
692         * security exceptions
693         *
694         * @param name         the name of the system property required
695         * @param defaultValue the default value to use if the property is not
696         *                     available or a security exception prevents access
697         * @return the boolean representation of the system property value or the
698         *         default value if the property is not available or security does
699         *         not allow its access
700         */
701        public static boolean getSystemProperty(String name, Boolean defaultValue) {
702            String result = getSystemProperty(name, defaultValue.toString());
703            return Boolean.parseBoolean(result);
704        }
705       
706        /**
707         * A helper method to access a camel context properties with a prefix
708         *
709         * @param prefix       the prefix
710         * @param camelContext the camel context
711         * @return the properties which holds the camel context properties with the prefix,
712         *         and the key omit the prefix part
713         */
714        public static Properties getCamelPropertiesWithPrefix(String prefix, CamelContext camelContext) {
715            Properties answer = new Properties();
716            Map<String, String> camelProperties = camelContext.getProperties();
717            if (camelProperties != null) {
718                for (Map.Entry<String, String> entry : camelProperties.entrySet()) {
719                    String key = entry.getKey();
720                    if (key != null && key.startsWith(prefix)) {
721                        answer.put(key.substring(prefix.length()), entry.getValue());
722                    }
723                }
724            }
725            return answer;
726        }
727    
728        /**
729         * Returns the type name of the given type or null if the type variable is
730         * null
731         */
732        public static String name(Class<?> type) {
733            return type != null ? type.getName() : null;
734        }
735    
736        /**
737         * Returns the type name of the given value
738         */
739        public static String className(Object value) {
740            return name(value != null ? value.getClass() : null);
741        }
742    
743        /**
744         * Returns the canonical type name of the given value
745         */
746        public static String classCanonicalName(Object value) {
747            if (value != null) {
748                return value.getClass().getCanonicalName();
749            } else {
750                return null;
751            }
752        }
753    
754        /**
755         * Attempts to load the given class name using the thread context class
756         * loader or the class loader used to load this class
757         *
758         * @param name the name of the class to load
759         * @return the class or <tt>null</tt> if it could not be loaded
760         */
761        public static Class<?> loadClass(String name) {
762            return loadClass(name, ObjectHelper.class.getClassLoader());
763        }
764        
765        /**
766         * Attempts to load the given class name using the thread context class
767         * loader or the given class loader
768         *
769         * @param name the name of the class to load
770         * @param loader the class loader to use after the thread context class loader
771         * @return the class or <tt>null</tt> if it could not be loaded
772         */
773        public static Class<?> loadClass(String name, ClassLoader loader) {
774            return loadClass(name, loader, false);
775        }
776    
777        /**
778         * Attempts to load the given class name using the thread context class
779         * loader or the given class loader
780         *
781         * @param name the name of the class to load
782         * @param loader the class loader to use after the thread context class loader
783         * @param needToWarn when <tt>true</tt> logs a warning when a class with the given name could not be loaded
784         * @return the class or <tt>null</tt> if it could not be loaded
785         */
786        public static Class<?> loadClass(String name, ClassLoader loader, boolean needToWarn) {
787            // must clean the name so its pure java name, eg removing \n or whatever people can do in the Spring XML
788            name = normalizeClassName(name);
789            if (ObjectHelper.isEmpty(name)) {
790                return null;
791            }
792    
793            // Try simple type first
794            Class<?> clazz = loadSimpleType(name);
795            if (clazz == null) {
796                // try context class loader
797                clazz = doLoadClass(name, Thread.currentThread().getContextClassLoader());
798            }
799            if (clazz == null) {
800                // then the provided loader
801                clazz = doLoadClass(name, loader);
802            }
803            if (clazz == null) {
804                // and fallback to the loader the loaded the ObjectHelper class
805                clazz = doLoadClass(name, ObjectHelper.class.getClassLoader());
806            }
807    
808            if (clazz == null) {
809                if (needToWarn) {
810                    LOG.warn("Cannot find class: " + name);
811                } else {
812                    LOG.debug("Cannot find class: " + name);
813                }
814            }
815    
816            return clazz;
817        }
818    
819    
820        /**
821         * Load a simple type
822         *
823         * @param name the name of the class to load
824         * @return the class or <tt>null</tt> if it could not be loaded
825         */
826        public static Class<?> loadSimpleType(String name) {
827            // special for byte[] or Object[] as its common to use
828            if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
829                return byte[].class;
830            } else if ("java.lang.Byte[]".equals(name) || "Byte[]".equals(name)) {
831                return Byte[].class;
832            } else if ("java.lang.Object[]".equals(name) || "Object[]".equals(name)) {
833                return Object[].class;
834            } else if ("java.lang.String[]".equals(name) || "String[]".equals(name)) {
835                return String[].class;
836            // and these is common as well
837            } else if ("java.lang.String".equals(name) || "String".equals(name)) {
838                return String.class;
839            } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) {
840                return Boolean.class;
841            } else if ("boolean".equals(name)) {
842                return boolean.class;
843            } else if ("java.lang.Integer".equals(name) || "Integer".equals(name)) {
844                return Integer.class;
845            } else if ("int".equals(name)) {
846                return int.class;
847            } else if ("java.lang.Long".equals(name) || "Long".equals(name)) {
848                return Long.class;
849            } else if ("long".equals(name)) {
850                return long.class;
851            } else if ("java.lang.Short".equals(name) || "Short".equals(name)) {
852                return Short.class;
853            } else if ("short".equals(name)) {
854                return short.class;
855            } else if ("java.lang.Byte".equals(name) || "Byte".equals(name)) {
856                return Byte.class;
857            } else if ("byte".equals(name)) {
858                return byte.class;
859            } else if ("java.lang.Float".equals(name) || "Float".equals(name)) {
860                return Float.class;
861            } else if ("float".equals(name)) {
862                return float.class;
863            } else if ("java.lang.Double".equals(name) || "Double".equals(name)) {
864                return Double.class;
865            } else if ("double".equals(name)) {
866                return double.class;
867            }
868    
869            return null;
870        }
871    
872        /**
873         * Loads the given class with the provided classloader (may be null).
874         * Will ignore any class not found and return null.
875         *
876         * @param name    the name of the class to load
877         * @param loader  a provided loader (may be null)
878         * @return the class, or null if it could not be loaded
879         */
880        private static Class<?> doLoadClass(String name, ClassLoader loader) {
881            ObjectHelper.notEmpty(name, "name");
882            if (loader == null) {
883                return null;
884            }
885    
886            try {
887                LOG.trace("Loading class: {} using classloader: {}", name, loader);
888                return loader.loadClass(name);
889            } catch (ClassNotFoundException e) {
890                if (LOG.isTraceEnabled()) {
891                    LOG.trace("Cannot load class: " + name + " using classloader: " + loader, e);
892                }
893            }
894    
895            return null;
896        }
897    
898        /**
899         * Attempts to load the given resource as a stream using the thread context
900         * class loader or the class loader used to load this class
901         *
902         * @param name the name of the resource to load
903         * @return the stream or null if it could not be loaded
904         */
905        public static InputStream loadResourceAsStream(String name) {
906            InputStream in = null;
907    
908            String resolvedName = resolveUriPath(name);
909            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
910            if (contextClassLoader != null) {
911                in = contextClassLoader.getResourceAsStream(resolvedName);
912            }
913            if (in == null) {
914                in = ObjectHelper.class.getClassLoader().getResourceAsStream(resolvedName);
915            }
916            if (in == null) {
917                in = ObjectHelper.class.getResourceAsStream(resolvedName);
918            }
919    
920            return in;
921        }
922    
923        /**
924         * Attempts to load the given resource as a stream using the thread context
925         * class loader or the class loader used to load this class
926         *
927         * @param name the name of the resource to load
928         * @return the stream or null if it could not be loaded
929         */
930        public static URL loadResourceAsURL(String name) {
931            URL url = null;
932    
933            String resolvedName = resolveUriPath(name);
934            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
935            if (contextClassLoader != null) {
936                url = contextClassLoader.getResource(resolvedName);
937            }
938            if (url == null) {
939                url = ObjectHelper.class.getClassLoader().getResource(resolvedName);
940            }
941            if (url == null) {
942                url = ObjectHelper.class.getResource(resolvedName);
943            }
944    
945            return url;
946        }
947    
948        /**
949         * Attempts to load the given resources from the given package name using the thread context
950         * class loader or the class loader used to load this class
951         *
952         * @param packageName the name of the package to load its resources
953         * @return the URLs for the resources or null if it could not be loaded
954         */
955        public static Enumeration<URL> loadResourcesAsURL(String packageName) {
956            Enumeration<URL> url = null;
957    
958            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
959            if (contextClassLoader != null) {
960                try {
961                    url = contextClassLoader.getResources(packageName);
962                } catch (IOException e) {
963                    // ignore
964                }
965            }
966            if (url == null) {
967                try {
968                    url = ObjectHelper.class.getClassLoader().getResources(packageName);
969                } catch (IOException e) {
970                    // ignore
971                }
972            }
973    
974            return url;
975        }
976        
977        /**
978         * Helper operation used to remove relative path notation from 
979         * resources.  Most critical for resources on the Classpath
980         * as resource loaders will not resolve the relative paths correctly.
981         * 
982         * @param name the name of the resource to load
983         * @return the modified or unmodified string if there were no changes
984         */
985        private static String resolveUriPath(String name) {
986            // compact the path and use / as separator as that's used for loading resources on the classpath
987            return FileUtil.compactPath(name, '/');
988        }
989    
990        /**
991         * A helper method to invoke a method via reflection and wrap any exceptions
992         * as {@link RuntimeCamelException} instances
993         *
994         * @param method the method to invoke
995         * @param instance the object instance (or null for static methods)
996         * @param parameters the parameters to the method
997         * @return the result of the method invocation
998         */
999        public static Object invokeMethod(Method method, Object instance, Object... parameters) {
1000            try {
1001                return method.invoke(instance, parameters);
1002            } catch (IllegalAccessException e) {
1003                throw new RuntimeCamelException(e);
1004            } catch (InvocationTargetException e) {
1005                throw ObjectHelper.wrapRuntimeCamelException(e.getCause());
1006            }
1007        }
1008    
1009        /**
1010         * Tests whether the target method overrides the source method.
1011         * <p/>
1012         * Tests whether they have the same name, return type, and parameter list.
1013         *
1014         * @param source  the source method
1015         * @param target  the target method
1016         * @return <tt>true</tt> if it override, <tt>false</tt> otherwise
1017         */
1018        public static boolean isOverridingMethod(Method source, Method target) {
1019            if (source.getName().equals(target.getName())
1020                    && source.getReturnType().equals(target.getReturnType()) 
1021                    && source.getParameterTypes().length == target.getParameterTypes().length) {
1022    
1023                // test if parameter types is the same as well
1024                for (int i = 0; i < source.getParameterTypes().length; i++) {
1025                    if (!(source.getParameterTypes()[i].equals(target.getParameterTypes()[i]))) {
1026                        return false;
1027                    }
1028                }
1029    
1030                // the have same name, return type and parameter list, so its overriding
1031                return true;
1032            }
1033    
1034            return false;
1035        }
1036    
1037        /**
1038         * Returns a list of methods which are annotated with the given annotation
1039         *
1040         * @param type the type to reflect on
1041         * @param annotationType the annotation type
1042         * @return a list of the methods found
1043         */
1044        public static List<Method> findMethodsWithAnnotation(Class<?> type,
1045                                                             Class<? extends Annotation> annotationType) {
1046            return findMethodsWithAnnotation(type, annotationType, false);
1047        }
1048    
1049        /**
1050         * Returns a list of methods which are annotated with the given annotation
1051         *
1052         * @param type the type to reflect on
1053         * @param annotationType the annotation type
1054         * @param checkMetaAnnotations check for meta annotations
1055         * @return a list of the methods found
1056         */
1057        public static List<Method> findMethodsWithAnnotation(Class<?> type,
1058                                                             Class<? extends Annotation> annotationType,
1059                                                             boolean checkMetaAnnotations) {
1060            List<Method> answer = new ArrayList<Method>();
1061            do {
1062                Method[] methods = type.getDeclaredMethods();
1063                for (Method method : methods) {
1064                    if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
1065                        answer.add(method);
1066                    }
1067                }
1068                type = type.getSuperclass();
1069            } while (type != null);
1070            return answer;
1071        }
1072    
1073        /**
1074         * Checks if a Class or Method are annotated with the given annotation
1075         *
1076         * @param elem the Class or Method to reflect on
1077         * @param annotationType the annotation type
1078         * @param checkMetaAnnotations check for meta annotations
1079         * @return true if annotations is present
1080         */
1081        public static boolean hasAnnotation(AnnotatedElement elem, Class<? extends Annotation> annotationType,
1082                                            boolean checkMetaAnnotations) {
1083            if (elem.isAnnotationPresent(annotationType)) {
1084                return true;
1085            }
1086            if (checkMetaAnnotations) {
1087                for (Annotation a : elem.getAnnotations()) {
1088                    for (Annotation meta : a.annotationType().getAnnotations()) {
1089                        if (meta.annotationType().getName().equals(annotationType.getName())) {
1090                            return true;
1091                        }
1092                    }
1093                }
1094            }
1095            return false;
1096        }
1097    
1098        /**
1099         * Turns the given object arrays into a meaningful string
1100         *
1101         * @param objects an array of objects or null
1102         * @return a meaningful string
1103         */
1104        public static String asString(Object[] objects) {
1105            if (objects == null) {
1106                return "null";
1107            } else {
1108                StringBuilder buffer = new StringBuilder("{");
1109                int counter = 0;
1110                for (Object object : objects) {
1111                    if (counter++ > 0) {
1112                        buffer.append(", ");
1113                    }
1114                    String text = (object == null) ? "null" : object.toString();
1115                    buffer.append(text);
1116                }
1117                buffer.append("}");
1118                return buffer.toString();
1119            }
1120        }
1121    
1122        /**
1123         * Returns true if a class is assignable from another class like the
1124         * {@link Class#isAssignableFrom(Class)} method but which also includes
1125         * coercion between primitive types to deal with Java 5 primitive type
1126         * wrapping
1127         */
1128        public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
1129            a = convertPrimitiveTypeToWrapperType(a);
1130            b = convertPrimitiveTypeToWrapperType(b);
1131            return a.isAssignableFrom(b);
1132        }
1133    
1134        /**
1135         * Returns if the given {@code clazz} type is a Java primitive array type.
1136         * 
1137         * @param clazz the Java type to be checked
1138         * @return {@code true} if the given type is a Java primitive array type
1139         */
1140        public static boolean isPrimitiveArrayType(Class<?> clazz) {
1141            return PRIMITIVE_ARRAY_TYPES.contains(clazz);
1142        }
1143    
1144        public static int arrayLength(Object[] pojo) {
1145            return pojo.length;
1146        }
1147    
1148        /**
1149         * Converts primitive types such as int to its wrapper type like
1150         * {@link Integer}
1151         */
1152        public static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
1153            Class<?> rc = type;
1154            if (type.isPrimitive()) {
1155                if (type == int.class) {
1156                    rc = Integer.class;
1157                } else if (type == long.class) {
1158                    rc = Long.class;
1159                } else if (type == double.class) {
1160                    rc = Double.class;
1161                } else if (type == float.class) {
1162                    rc = Float.class;
1163                } else if (type == short.class) {
1164                    rc = Short.class;
1165                } else if (type == byte.class) {
1166                    rc = Byte.class;
1167                } else if (type == boolean.class) {
1168                    rc = Boolean.class;
1169                }
1170            }
1171            return rc;
1172        }
1173    
1174        /**
1175         * Helper method to return the default character set name
1176         */
1177        public static String getDefaultCharacterSet() {
1178            return Charset.defaultCharset().name();
1179        }
1180    
1181        /**
1182         * Returns the Java Bean property name of the given method, if it is a
1183         * setter
1184         */
1185        public static String getPropertyName(Method method) {
1186            String propertyName = method.getName();
1187            if (propertyName.startsWith("set") && method.getParameterTypes().length == 1) {
1188                propertyName = propertyName.substring(3, 4).toLowerCase(Locale.ENGLISH) + propertyName.substring(4);
1189            }
1190            return propertyName;
1191        }
1192    
1193        /**
1194         * Returns true if the given collection of annotations matches the given type
1195         */
1196        public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
1197            for (Annotation annotation : annotations) {
1198                if (type.isInstance(annotation)) {
1199                    return true;
1200                }
1201            }
1202            return false;
1203        }
1204    
1205        /**
1206         * Gets the annotation from the given instance.
1207         *
1208         * @param instance the instance
1209         * @param type  the annotation
1210         * @return the annotation, or <tt>null</tt> if the instance does not have the given annotation
1211         */
1212        public static <A extends java.lang.annotation.Annotation> A getAnnotation(Object instance, Class<A> type) {
1213            return instance.getClass().getAnnotation(type);
1214        }
1215    
1216        /**
1217         * Closes the given resource if it is available, logging any closing
1218         * exceptions to the given log
1219         *
1220         * @param closeable the object to close
1221         * @param name the name of the resource
1222         * @param log the log to use when reporting closure warnings
1223         * @deprecated will be removed in Camel 3.0. Instead use {@link org.apache.camel.util.IOHelper#close(java.io.Closeable, String, org.slf4j.Logger)} instead
1224         */
1225        @Deprecated
1226        public static void close(Closeable closeable, String name, Logger log) {
1227            IOHelper.close(closeable, name, log);
1228        }
1229    
1230    
1231        /**
1232         * Converts the given value to the required type or throw a meaningful exception
1233         */
1234        @SuppressWarnings("unchecked")
1235        public static <T> T cast(Class<T> toType, Object value) {
1236            if (toType == boolean.class) {
1237                return (T)cast(Boolean.class, value);
1238            } else if (toType.isPrimitive()) {
1239                Class<?> newType = convertPrimitiveTypeToWrapperType(toType);
1240                if (newType != toType) {
1241                    return (T)cast(newType, value);
1242                }
1243            }
1244            try {
1245                return toType.cast(value);
1246            } catch (ClassCastException e) {
1247                throw new IllegalArgumentException("Failed to convert: " 
1248                    + value + " to type: " + toType.getName() + " due to: " + e, e);
1249            }
1250        }
1251    
1252        /**
1253         * A helper method to create a new instance of a type using the default
1254         * constructor arguments.
1255         */
1256        public static <T> T newInstance(Class<T> type) {
1257            try {
1258                return type.newInstance();
1259            } catch (InstantiationException e) {
1260                throw new RuntimeCamelException(e);
1261            } catch (IllegalAccessException e) {
1262                throw new RuntimeCamelException(e);
1263            }
1264        }
1265    
1266        /**
1267         * A helper method to create a new instance of a type using the default
1268         * constructor arguments.
1269         */
1270        public static <T> T newInstance(Class<?> actualType, Class<T> expectedType) {
1271            try {
1272                Object value = actualType.newInstance();
1273                return cast(expectedType, value);
1274            } catch (InstantiationException e) {
1275                throw new RuntimeCamelException(e);
1276            } catch (IllegalAccessException e) {
1277                throw new RuntimeCamelException(e);
1278            }
1279        }
1280    
1281        /**
1282         * Does the given class have a default public no-arg constructor.
1283         */
1284        public static boolean hasDefaultPublicNoArgConstructor(Class<?> type) {
1285            // getConstructors() returns only public constructors
1286            for (Constructor<?> ctr : type.getConstructors()) {
1287                if (ctr.getParameterTypes().length == 0) {
1288                    return true;
1289                }
1290            }
1291            return false;
1292        }
1293    
1294        /**
1295         * Returns true if the given name is a valid java identifier
1296         */
1297        public static boolean isJavaIdentifier(String name) {
1298            if (name == null) {
1299                return false;
1300            }
1301            int size = name.length();
1302            if (size < 1) {
1303                return false;
1304            }
1305            if (Character.isJavaIdentifierStart(name.charAt(0))) {
1306                for (int i = 1; i < size; i++) {
1307                    if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1308                        return false;
1309                    }
1310                }
1311                return true;
1312            }
1313            return false;
1314        }
1315    
1316        /**
1317         * Returns the type of the given object or null if the value is null
1318         */
1319        public static Object type(Object bean) {
1320            return bean != null ? bean.getClass() : null;
1321        }
1322    
1323        /**
1324         * Evaluate the value as a predicate which attempts to convert the value to
1325         * a boolean otherwise true is returned if the value is not null
1326         */
1327        public static boolean evaluateValuePredicate(Object value) {
1328            if (value instanceof Boolean) {
1329                return (Boolean)value;
1330            } else if (value instanceof String) {
1331                if ("true".equalsIgnoreCase((String)value)) {
1332                    return true;
1333                } else if ("false".equalsIgnoreCase((String)value)) {
1334                    return false;
1335                }
1336            } else if (value instanceof NodeList) {
1337                // is it an empty dom with empty attributes
1338                if (value instanceof Node && ((Node)value).hasAttributes()) {
1339                    return true;
1340                }
1341                NodeList list = (NodeList) value;
1342                return list.getLength() > 0;
1343            } else if (value instanceof Collection) {
1344                // is it an empty collection
1345                Collection<?> col = (Collection<?>) value;
1346                return col.size() > 0;
1347            }
1348            return value != null;
1349        }
1350    
1351        /**
1352         * Wraps the caused exception in a {@link RuntimeCamelException} if its not
1353         * already such an exception.
1354         *
1355         * @param e the caused exception
1356         * @return the wrapper exception
1357         */
1358        public static RuntimeCamelException wrapRuntimeCamelException(Throwable e) {
1359            if (e instanceof RuntimeCamelException) {
1360                // don't double wrap
1361                return (RuntimeCamelException)e;
1362            } else {
1363                return new RuntimeCamelException(e);
1364            }
1365        }
1366    
1367        /**
1368         * Wraps the caused exception in a {@link CamelExecutionException} if its not
1369         * already such an exception.
1370         *
1371         * @param e the caused exception
1372         * @return the wrapper exception
1373         */
1374        public static CamelExecutionException wrapCamelExecutionException(Exchange exchange, Throwable e) {
1375            if (e instanceof CamelExecutionException) {
1376                // don't double wrap
1377                return (CamelExecutionException)e;
1378            } else {
1379                return new CamelExecutionException("Exception occurred during execution", exchange, e);
1380            }
1381        }
1382    
1383        /**
1384         * Cleans the string to a pure Java identifier so we can use it for loading class names.
1385         * <p/>
1386         * Especially from Spring DSL people can have \n \t or other characters that otherwise
1387         * would result in ClassNotFoundException
1388         *
1389         * @param name the class name
1390         * @return normalized classname that can be load by a class loader.
1391         */
1392        public static String normalizeClassName(String name) {
1393            StringBuilder sb = new StringBuilder(name.length());
1394            for (char ch : name.toCharArray()) {
1395                if (ch == '.' || ch == '[' || ch == ']' || ch == '-' || Character.isJavaIdentifierPart(ch)) {
1396                    sb.append(ch);
1397                }
1398            }
1399            return sb.toString();
1400        }
1401    
1402        /**
1403         * Creates an iterator to walk the exception from the bottom up
1404         * (the last caused by going upwards to the root exception).
1405         *
1406         * @param exception  the exception
1407         * @return the iterator
1408         */
1409        public static Iterator<Throwable> createExceptionIterator(Throwable exception) {
1410            return new ExceptionIterator(exception);
1411        }
1412    
1413        /**
1414         * Retrieves the given exception type from the exception.
1415         * <p/>
1416         * Is used to get the caused exception that typically have been wrapped in some sort
1417         * of Camel wrapper exception
1418         * <p/>
1419         * The strategy is to look in the exception hierarchy to find the first given cause that matches the type.
1420         * Will start from the bottom (the real cause) and walk upwards.
1421         *
1422         * @param type the exception type wanted to retrieve
1423         * @param exception the caused exception
1424         * @return the exception found (or <tt>null</tt> if not found in the exception hierarchy)
1425         */
1426        public static <T> T getException(Class<T> type, Throwable exception) {
1427            if (exception == null) {
1428                return null;
1429            }
1430    
1431            // walk the hierarchy and look for it
1432            Iterator<Throwable> it = createExceptionIterator(exception);
1433            while (it.hasNext()) {
1434                Throwable e = it.next();
1435                if (type.isInstance(e)) {
1436                    return type.cast(e);
1437                }
1438            }
1439    
1440            // not found
1441            return null;
1442        }
1443    
1444        /**
1445         * Creates a {@link Scanner} for scanning the given value.
1446         *
1447         * @param exchange  the current exchange
1448         * @param value     the value, typically the message IN body
1449         * @return the scanner, is newer <tt>null</tt>
1450         */
1451        public static Scanner getScanner(Exchange exchange, Object value) {
1452            if (value instanceof WrappedFile) {
1453                // generic file is just a wrapper for the real file so call again with the real file
1454                WrappedFile<?> gf = (WrappedFile<?>) value;
1455                return getScanner(exchange, gf.getFile());
1456            }
1457    
1458            String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
1459    
1460            Scanner scanner = null;
1461            if (value instanceof Readable) {
1462                scanner = new Scanner((Readable)value);
1463            } else if (value instanceof InputStream) {
1464                scanner = charset == null ? new Scanner((InputStream)value) : new Scanner((InputStream)value, charset);
1465            } else if (value instanceof File) {
1466                try {
1467                    scanner = charset == null ? new Scanner((File)value) : new Scanner((File)value, charset);
1468                } catch (FileNotFoundException e) {
1469                    throw new RuntimeCamelException(e);
1470                }
1471            } else if (value instanceof String) {
1472                scanner = new Scanner((String)value);
1473            } else if (value instanceof ReadableByteChannel) {
1474                scanner = charset == null ? new Scanner((ReadableByteChannel)value) : new Scanner((ReadableByteChannel)value, charset);
1475            }
1476    
1477            if (scanner == null) {
1478                // value is not a suitable type, try to convert value to a string
1479                String text = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, value);
1480                if (text != null) {
1481                    scanner = new Scanner(text);
1482                }
1483            }
1484    
1485            if (scanner == null) {
1486                scanner = new Scanner("");
1487            }
1488    
1489            return scanner;
1490        }
1491    
1492        public static String getIdentityHashCode(Object object) {
1493            return "0x" + Integer.toHexString(System.identityHashCode(object));
1494        }
1495    
1496        /**
1497         * Lookup the constant field on the given class with the given name
1498         *
1499         * @param clazz  the class
1500         * @param name   the name of the field to lookup
1501         * @return the value of the constant field, or <tt>null</tt> if not found
1502         */
1503        public static String lookupConstantFieldValue(Class<?> clazz, String name) {
1504            if (clazz == null) {
1505                return null;
1506            }
1507    
1508            // remove leading dots
1509            if (name.startsWith(",")) {
1510                name = name.substring(1);
1511            }
1512    
1513            for (Field field : clazz.getFields()) {
1514                if (field.getName().equals(name)) {
1515                    try {
1516                        Object v = field.get(null);
1517                        return v.toString();
1518                    } catch (IllegalAccessException e) {
1519                        // ignore
1520                        return null;
1521                    }
1522                }
1523            }
1524    
1525            return null;
1526        }
1527    
1528        /**
1529         * Is the given value a numeric NaN type
1530         * 
1531         * @param value the value
1532         * @return <tt>true</tt> if its a {@link Float#NaN} or {@link Double#NaN}.
1533         */
1534        public static boolean isNaN(Object value) {
1535            if (value == null || !(value instanceof Number)) {
1536                return false;
1537            }
1538            // value must be a number
1539            return value.equals(Float.NaN) || value.equals(Double.NaN);
1540        }
1541    
1542        private static final class ExceptionIterator implements Iterator<Throwable> {
1543            private List<Throwable> tree = new ArrayList<Throwable>();
1544            private Iterator<Throwable> it;
1545    
1546            public ExceptionIterator(Throwable exception) {
1547                Throwable current = exception;
1548                // spool to the bottom of the caused by tree
1549                while (current != null) {
1550                    tree.add(current);
1551                    current = current.getCause();
1552                }
1553    
1554                // reverse tree so we go from bottom to top
1555                Collections.reverse(tree);
1556                it = tree.iterator();
1557            }
1558    
1559            public boolean hasNext() {
1560                return it.hasNext();
1561            }
1562    
1563            public Throwable next() {
1564                return it.next();
1565            }
1566    
1567            public void remove() {
1568                it.remove();
1569            }
1570        }
1571    }