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