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.IOException;
020import java.io.InputStream;
021import java.lang.annotation.Annotation;
022import java.lang.reflect.AnnotatedElement;
023import java.lang.reflect.Array;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.Field;
026import java.lang.reflect.Method;
027import java.net.URL;
028import java.nio.charset.Charset;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.Collection;
032import java.util.Collections;
033import java.util.Enumeration;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Locale;
037import java.util.Map;
038import java.util.Objects;
039import java.util.Optional;
040import java.util.function.Consumer;
041import java.util.function.Supplier;
042
043import org.w3c.dom.Node;
044import org.w3c.dom.NodeList;
045
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049/**
050 * A number of useful helper methods for working with Objects
051 */
052public final class ObjectHelper {
053
054    private static final Logger LOG = LoggerFactory.getLogger(ObjectHelper.class);
055
056    private static final Float FLOAT_NAN = Float.NaN;
057    private static final Double DOUBLE_NAN = Double.NaN;
058
059    /**
060     * Utility classes should not have a public constructor.
061     */
062    private ObjectHelper() {
063    }
064
065    /**
066     * A helper method for comparing objects for equality while handling nulls
067     */
068    public static boolean equal(Object a, Object b) {
069        return equal(a, b, false);
070    }
071
072    /**
073     * A helper method for comparing objects for equality while handling case insensitivity
074     */
075    public static boolean equalIgnoreCase(Object a, Object b) {
076        return equal(a, b, true);
077    }
078
079    /**
080     * A helper method for comparing objects for equality while handling nulls
081     */
082    public static boolean equal(final Object a, final Object b, final boolean ignoreCase) {
083        if (a == b) {
084            return true;
085        }
086
087        if (a == null || b == null) {
088            return false;
089        }
090
091        if (ignoreCase) {
092            if (a instanceof String && b instanceof String) {
093                return ((String) a).equalsIgnoreCase((String) b);
094            }
095        }
096
097        if (a.getClass().isArray() && b.getClass().isArray()) {
098            // uses array based equals
099            return Objects.deepEquals(a, b);
100        } else {
101            // use regular equals
102            return a.equals(b);
103        }
104    }
105
106    /**
107     * A helper method for comparing byte arrays for equality while handling nulls
108     */
109    public static boolean equalByteArray(byte[] a, byte[] b) {
110        return Arrays.equals(a, b);
111    }
112
113    /**
114     * Returns true if the given object is equal to any of the expected value
115     */
116    public static boolean isEqualToAny(Object object, Object... values) {
117        for (Object value : values) {
118            if (equal(object, value)) {
119                return true;
120            }
121        }
122        return false;
123    }
124
125    public static Boolean toBoolean(Object value) {
126        if (value instanceof Boolean) {
127            return (Boolean) value;
128        }
129        if (value instanceof String) {
130            // we only want to accept true or false as accepted values
131            String str = (String) value;
132            if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) {
133                return Boolean.valueOf(str);
134            }
135        }
136        if (value instanceof Integer) {
137            return (Integer) value > 0 ? Boolean.TRUE : Boolean.FALSE;
138        }
139        return null;
140    }
141
142    /**
143     * Asserts whether the value is <b>not</b> <tt>null</tt>
144     *
145     * @param  value                    the value to test
146     * @param  name                     the key that resolved the value
147     * @return                          the passed {@code value} as is
148     * @throws IllegalArgumentException is thrown if assertion fails
149     */
150    public static <T> T notNull(T value, String name) {
151        if (value == null) {
152            throw new IllegalArgumentException(name + " must be specified");
153        }
154
155        return value;
156    }
157
158    /**
159     * Asserts whether the value is <b>not</b> <tt>null</tt>
160     *
161     * @param  value                    the value to test
162     * @param  on                       additional description to indicate where this problem occurred (appended as
163     *                                  toString())
164     * @param  name                     the key that resolved the value
165     * @return                          the passed {@code value} as is
166     * @throws IllegalArgumentException is thrown if assertion fails
167     */
168    public static <T> T notNull(T value, String name, Object on) {
169        if (on == null) {
170            notNull(value, name);
171        } else if (value == null) {
172            throw new IllegalArgumentException(name + " must be specified on: " + on);
173        }
174
175        return value;
176    }
177
178    /**
179     * Tests whether the value is <tt>null</tt> or an empty string.
180     *
181     * @param  value the value, if its a String it will be tested for text length as well
182     * @return       true if empty
183     */
184    public static boolean isEmpty(Object value) {
185        if (value == null) {
186            return true;
187        } else if (value instanceof String) {
188            return ((String) value).trim().isEmpty();
189        } else if (value instanceof Collection) {
190            return ((Collection<?>) value).isEmpty();
191        } else if (value instanceof Map) {
192            return ((Map<?, ?>) value).isEmpty();
193        } else {
194            return false;
195        }
196    }
197
198    /**
199     * Tests whether the value is <b>not</b> <tt>null</tt>, an empty string or an empty collection/map.
200     *
201     * @param  value the value, if its a String it will be tested for text length as well
202     * @return       true if <b>not</b> empty
203     */
204    public static boolean isNotEmpty(Object value) {
205        if (value == null) {
206            return false;
207        } else if (value instanceof String) {
208            return !((String) value).trim().isEmpty();
209        } else if (value instanceof Collection) {
210            return !((Collection<?>) value).isEmpty();
211        } else if (value instanceof Map) {
212            return !((Map<?, ?>) value).isEmpty();
213        } else {
214            return true;
215        }
216    }
217
218    /**
219     * Returns the first non null object <tt>null</tt>.
220     *
221     * @param  values the values
222     * @return        an Optional
223     */
224    public static Optional<Object> firstNotNull(Object... values) {
225        for (Object value : values) {
226            if (value != null) {
227                return Optional.of(value);
228            }
229        }
230
231        return Optional.empty();
232    }
233
234    /**
235     * Tests whether the value is <tt>null</tt>, an empty string, an empty collection or a map
236     *
237     * @param value    the value, if its a String it will be tested for text length as well
238     * @param supplier the supplier, the supplier to be used to get a value if value is null
239     */
240    public static <T> T supplyIfEmpty(T value, Supplier<T> supplier) {
241        org.apache.camel.util.ObjectHelper.notNull(supplier, "Supplier");
242        if (isNotEmpty(value)) {
243            return value;
244        }
245
246        return supplier.get();
247    }
248
249    /**
250     * Tests whether the value is <b>not</b> <tt>null</tt>, an empty string, an empty collection or a map
251     *
252     * @param value    the value, if its a String it will be tested for text length as well
253     * @param consumer the consumer, the operation to be executed against value if not empty
254     */
255    public static <T> void ifNotEmpty(T value, Consumer<T> consumer) {
256        if (isNotEmpty(value)) {
257            consumer.accept(value);
258        }
259    }
260
261    /**
262     * Returns the predicate matching boolean on a {@link List} result set where if the first element is a boolean its
263     * value is used otherwise this method returns true if the collection is not empty
264     *
265     * @return <tt>true</tt> if the first element is a boolean and its value is true or if the list is non empty
266     */
267    public static boolean matches(List<?> list) {
268        if (!list.isEmpty()) {
269            Object value = list.get(0);
270            if (value instanceof Boolean) {
271                return (Boolean) value;
272            } else {
273                // lets assume non-empty results are true
274                return true;
275            }
276        }
277        return false;
278    }
279
280    /**
281     * A helper method to access a system property, catching any security exceptions
282     *
283     * @param  name         the name of the system property required
284     * @param  defaultValue the default value to use if the property is not available or a security exception prevents
285     *                      access
286     * @return              the system property value or the default value if the property is not available or security
287     *                      does not allow its access
288     */
289    public static String getSystemProperty(String name, String defaultValue) {
290        try {
291            return System.getProperty(name, defaultValue);
292        } catch (Exception e) {
293            LOG.debug("Caught security exception accessing system property: {}. Will use default value: {}",
294                    name, defaultValue, e);
295
296            return defaultValue;
297        }
298    }
299
300    /**
301     * A helper method to access a boolean system property, catching any security exceptions
302     *
303     * @param  name         the name of the system property required
304     * @param  defaultValue the default value to use if the property is not available or a security exception prevents
305     *                      access
306     * @return              the boolean representation of the system property value or the default value if the property
307     *                      is not available or security does not allow its access
308     */
309    public static boolean getSystemProperty(String name, Boolean defaultValue) {
310        String result = getSystemProperty(name, defaultValue.toString());
311        return Boolean.parseBoolean(result);
312    }
313
314    /**
315     * Returns the type name of the given type or null if the type variable is null
316     */
317    public static String name(Class<?> type) {
318        return type != null ? type.getName() : null;
319    }
320
321    /**
322     * Returns the type name of the given value
323     */
324    public static String className(Object value) {
325        return name(value != null ? value.getClass() : null);
326    }
327
328    /**
329     * Returns the canonical type name of the given value
330     */
331    public static String classCanonicalName(Object value) {
332        if (value != null) {
333            return value.getClass().getCanonicalName();
334        } else {
335            return null;
336        }
337    }
338
339    /**
340     * Attempts to load the given class name using the thread context class loader or the class loader used to load this
341     * class
342     *
343     * @param  name the name of the class to load
344     * @return      the class or <tt>null</tt> if it could not be loaded
345     */
346    public static Class<?> loadClass(String name) {
347        return loadClass(name, ObjectHelper.class.getClassLoader());
348    }
349
350    /**
351     * Attempts to load the given class name using the thread context class loader or the given class loader
352     *
353     * @param  name   the name of the class to load
354     * @param  loader the class loader to use after the thread context class loader
355     * @return        the class or <tt>null</tt> if it could not be loaded
356     */
357    public static Class<?> loadClass(String name, ClassLoader loader) {
358        return loadClass(name, loader, false);
359    }
360
361    /**
362     * Attempts to load the given class name using the thread context class loader or the given class loader
363     *
364     * @param  name       the name of the class to load
365     * @param  loader     the class loader to use after the thread context class loader
366     * @param  needToWarn when <tt>true</tt> logs a warning when a class with the given name could not be loaded
367     * @return            the class or <tt>null</tt> if it could not be loaded
368     */
369    public static Class<?> loadClass(String name, ClassLoader loader, boolean needToWarn) {
370        // must clean the name so its pure java name, eg removing \n or whatever people can do in the Spring XML
371        name = StringHelper.normalizeClassName(name);
372        if (org.apache.camel.util.ObjectHelper.isEmpty(name)) {
373            return null;
374        }
375
376        boolean array = false;
377
378        // Try simple type first
379        Class<?> clazz = loadSimpleType(name);
380        if (clazz == null) {
381            // special for array as we need to load the class and then after that instantiate an array class type
382            if (name.endsWith("[]")) {
383                name = name.substring(0, name.length() - 2);
384                array = true;
385            }
386        }
387
388        if (clazz == null) {
389            // try context class loader
390            clazz = doLoadClass(name, Thread.currentThread().getContextClassLoader());
391        }
392        if (clazz == null) {
393            // then the provided loader
394            clazz = doLoadClass(name, loader);
395        }
396        if (clazz == null) {
397            // and fallback to the loader the loaded the ObjectHelper class
398            clazz = doLoadClass(name, ObjectHelper.class.getClassLoader());
399        }
400        if (clazz != null && array) {
401            Object arr = Array.newInstance(clazz, 0);
402            clazz = arr.getClass();
403        }
404
405        if (clazz == null) {
406            if (needToWarn) {
407                LOG.warn("Cannot find class: {}", name);
408            } else {
409                LOG.debug("Cannot find class: {}", name);
410            }
411        }
412
413        return clazz;
414    }
415
416    /**
417     * Load a simple type
418     *
419     * @param  name the name of the class to load
420     * @return      the class or <tt>null</tt> if it could not be loaded
421     */
422    //CHECKSTYLE:OFF
423    public static Class<?> loadSimpleType(String name) {
424        // special for byte[] or Object[] as its common to use
425        if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
426            return byte[].class;
427        } else if ("java.lang.Byte[]".equals(name) || "Byte[]".equals(name)) {
428            return Byte[].class;
429        } else if ("java.lang.Object[]".equals(name) || "Object[]".equals(name)) {
430            return Object[].class;
431        } else if ("java.lang.String[]".equals(name) || "String[]".equals(name)) {
432            return String[].class;
433            // and these is common as well
434        } else if ("java.lang.String".equals(name) || "String".equals(name)) {
435            return String.class;
436        } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) {
437            return Boolean.class;
438        } else if ("boolean".equals(name)) {
439            return boolean.class;
440        } else if ("java.lang.Integer".equals(name) || "Integer".equals(name)) {
441            return Integer.class;
442        } else if ("int".equals(name)) {
443            return int.class;
444        } else if ("java.lang.Long".equals(name) || "Long".equals(name)) {
445            return Long.class;
446        } else if ("long".equals(name)) {
447            return long.class;
448        } else if ("java.lang.Short".equals(name) || "Short".equals(name)) {
449            return Short.class;
450        } else if ("short".equals(name)) {
451            return short.class;
452        } else if ("java.lang.Byte".equals(name) || "Byte".equals(name)) {
453            return Byte.class;
454        } else if ("byte".equals(name)) {
455            return byte.class;
456        } else if ("java.lang.Float".equals(name) || "Float".equals(name)) {
457            return Float.class;
458        } else if ("float".equals(name)) {
459            return float.class;
460        } else if ("java.lang.Double".equals(name) || "Double".equals(name)) {
461            return Double.class;
462        } else if ("double".equals(name)) {
463            return double.class;
464        } else if ("java.lang.Character".equals(name) || "Character".equals(name)) {
465            return Character.class;
466        } else if ("char".equals(name)) {
467            return char.class;
468        }
469        return null;
470    }
471    //CHECKSTYLE:ON
472
473    /**
474     * Loads the given class with the provided classloader (may be null). Will ignore any class not found and return
475     * null.
476     *
477     * @param  name   the name of the class to load
478     * @param  loader a provided loader (may be null)
479     * @return        the class, or null if it could not be loaded
480     */
481    private static Class<?> doLoadClass(String name, ClassLoader loader) {
482        StringHelper.notEmpty(name, "name");
483        if (loader == null) {
484            return null;
485        }
486
487        try {
488            LOG.trace("Loading class: {} using classloader: {}", name, loader);
489            return loader.loadClass(name);
490        } catch (ClassNotFoundException e) {
491            if (LOG.isTraceEnabled()) {
492                LOG.trace("Cannot load class: {} using classloader: {}", name, loader, e);
493            }
494        }
495
496        return null;
497    }
498
499    /**
500     * Attempts to load the given resource as a stream using the thread context class loader or the class loader used to
501     * load this class
502     *
503     * @param  name the name of the resource to load
504     * @return      the stream or null if it could not be loaded
505     */
506    public static InputStream loadResourceAsStream(String name) {
507        return loadResourceAsStream(name, null);
508    }
509
510    /**
511     * Attempts to load the given resource as a stream using first the given class loader, then the thread context class
512     * loader and finally the class loader used to load this class
513     *
514     * @param  name   the name of the resource to load
515     * @param  loader optional classloader to attempt first
516     * @return        the stream or null if it could not be loaded
517     */
518    public static InputStream loadResourceAsStream(String name, ClassLoader loader) {
519        try {
520            URL res = loadResourceAsURL(name, loader);
521            return res != null ? res.openStream() : null;
522        } catch (IOException e) {
523            return null;
524        }
525    }
526
527    /**
528     * Attempts to load the given resource as a stream using the thread context class loader or the class loader used to
529     * load this class
530     *
531     * @param  name the name of the resource to load
532     * @return      the stream or null if it could not be loaded
533     */
534    public static URL loadResourceAsURL(String name) {
535        return loadResourceAsURL(name, null);
536    }
537
538    /**
539     * Attempts to load the given resource as a stream using the thread context class loader or the class loader used to
540     * load this class
541     *
542     * @param  name   the name of the resource to load
543     * @param  loader optional classloader to attempt first
544     * @return        the stream or null if it could not be loaded
545     */
546    public static URL loadResourceAsURL(String name, ClassLoader loader) {
547
548        URL url = null;
549        String resolvedName = resolveUriPath(name);
550
551        // #1 First, try the given class loader
552
553        if (loader != null) {
554            url = loader.getResource(resolvedName);
555            if (url != null) {
556                return url;
557            }
558        }
559
560        // #2 Next, is the TCCL
561
562        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
563        if (tccl != null) {
564
565            url = tccl.getResource(resolvedName);
566            if (url != null) {
567                return url;
568            }
569
570            // #3 The TCCL may be able to see camel-core, but not META-INF resources
571
572            try {
573
574                Class<?> clazz = tccl.loadClass("org.apache.camel.impl.DefaultCamelContext");
575                url = clazz.getClassLoader().getResource(resolvedName);
576                if (url != null) {
577                    return url;
578                }
579
580            } catch (ClassNotFoundException e) {
581                // ignore
582            }
583        }
584
585        // #4 Last, for the unlikely case that stuff can be loaded from camel-util
586
587        url = ObjectHelper.class.getClassLoader().getResource(resolvedName);
588        if (url != null) {
589            return url;
590        }
591
592        url = ObjectHelper.class.getResource(resolvedName);
593        return url;
594    }
595
596    /**
597     * Attempts to load the given resources from the given package name using the thread context class loader or the
598     * class loader used to load this class
599     *
600     * @param  uri the name of the package to load its resources
601     * @return     the URLs for the resources or null if it could not be loaded
602     */
603    public static Enumeration<URL> loadResourcesAsURL(String uri) {
604        return loadResourcesAsURL(uri, null);
605    }
606
607    /**
608     * Attempts to load the given resources from the given package name using the thread context class loader or the
609     * class loader used to load this class
610     *
611     * @param  uri    the name of the package to load its resources
612     * @param  loader optional classloader to attempt first
613     * @return        the URLs for the resources or null if it could not be loaded
614     */
615    public static Enumeration<URL> loadResourcesAsURL(String uri, ClassLoader loader) {
616
617        Enumeration<URL> res = null;
618
619        // #1 First, try the given class loader
620
621        if (loader != null) {
622            try {
623                res = loader.getResources(uri);
624                if (res != null) {
625                    return res;
626                }
627            } catch (IOException e) {
628                // ignore
629            }
630        }
631
632        // #2 Next, is the TCCL
633
634        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
635        if (tccl != null) {
636
637            try {
638                res = tccl.getResources(uri);
639                if (res != null) {
640                    return res;
641                }
642            } catch (IOException e1) {
643                // ignore
644            }
645
646            // #3 The TCCL may be able to see camel-core, but not META-INF resources
647
648            try {
649
650                Class<?> clazz = tccl.loadClass("org.apache.camel.impl.DefaultCamelContext");
651                res = clazz.getClassLoader().getResources(uri);
652                if (res != null) {
653                    return res;
654                }
655
656            } catch (ClassNotFoundException | IOException e) {
657                // ignore
658            }
659        }
660
661        // #4 Last, for the unlikely case that stuff can be loaded from camel-util
662
663        try {
664            res = ObjectHelper.class.getClassLoader().getResources(uri);
665        } catch (IOException e) {
666            // ignore
667        }
668
669        return res;
670    }
671
672    /**
673     * Helper operation used to remove relative path notation from resources. Most critical for resources on the
674     * Classpath as resource loaders will not resolve the relative paths correctly.
675     *
676     * @param  name the name of the resource to load
677     * @return      the modified or unmodified string if there were no changes
678     */
679    private static String resolveUriPath(String name) {
680        // compact the path and use / as separator as that's used for loading resources on the classpath
681        return FileUtil.compactPath(name, '/');
682    }
683
684    /**
685     * Tests whether the target method overrides the source method.
686     * <p/>
687     * Tests whether they have the same name, return type, and parameter list.
688     *
689     * @param  source the source method
690     * @param  target the target method
691     * @return        <tt>true</tt> if it override, <tt>false</tt> otherwise
692     */
693    public static boolean isOverridingMethod(Method source, Method target) {
694        return isOverridingMethod(source, target, true);
695    }
696
697    /**
698     * Tests whether the target method overrides the source method.
699     * <p/>
700     * Tests whether they have the same name, return type, and parameter list.
701     *
702     * @param  source the source method
703     * @param  target the target method
704     * @param  exact  <tt>true</tt> if the override must be exact same types, <tt>false</tt> if the types should be
705     *                assignable
706     * @return        <tt>true</tt> if it override, <tt>false</tt> otherwise
707     */
708    public static boolean isOverridingMethod(Method source, Method target, boolean exact) {
709        return isOverridingMethod(target.getDeclaringClass(), source, target, exact);
710    }
711
712    /**
713     * Tests whether the target method overrides the source method from the inheriting class.
714     * <p/>
715     * Tests whether they have the same name, return type, and parameter list.
716     *
717     * @param  inheritingClass the class inheriting the target method overriding the source method
718     * @param  source          the source method
719     * @param  target          the target method
720     * @param  exact           <tt>true</tt> if the override must be exact same types, <tt>false</tt> if the types
721     *                         should be assignable
722     * @return                 <tt>true</tt> if it override, <tt>false</tt> otherwise
723     */
724    public static boolean isOverridingMethod(Class<?> inheritingClass, Method source, Method target, boolean exact) {
725
726        if (source.equals(target)) {
727            return true;
728        } else if (target.getDeclaringClass().isAssignableFrom(source.getDeclaringClass())) {
729            return false;
730        } else if (!source.getDeclaringClass().isAssignableFrom(inheritingClass)
731                || !target.getDeclaringClass().isAssignableFrom(inheritingClass)) {
732            return false;
733        }
734
735        if (!source.getName().equals(target.getName())) {
736            return false;
737        }
738
739        if (exact) {
740            if (!source.getReturnType().equals(target.getReturnType())) {
741                return false;
742            }
743        } else {
744            if (!source.getReturnType().isAssignableFrom(target.getReturnType())) {
745                boolean b1 = source.isBridge();
746                boolean b2 = target.isBridge();
747                // must not be bridge methods
748                if (!b1 && !b2) {
749                    return false;
750                }
751            }
752        }
753
754        // must have same number of parameter types
755        if (source.getParameterCount() != target.getParameterCount()) {
756            return false;
757        }
758
759        Class<?>[] sourceTypes = source.getParameterTypes();
760        Class<?>[] targetTypes = target.getParameterTypes();
761        // test if parameter types is the same as well
762        for (int i = 0; i < source.getParameterCount(); i++) {
763            if (exact) {
764                if (!(sourceTypes[i].equals(targetTypes[i]))) {
765                    return false;
766                }
767            } else {
768                if (!(sourceTypes[i].isAssignableFrom(targetTypes[i]))) {
769                    boolean b1 = source.isBridge();
770                    boolean b2 = target.isBridge();
771                    // must not be bridge methods
772                    if (!b1 && !b2) {
773                        return false;
774                    }
775                }
776            }
777        }
778
779        // the have same name, return type and parameter list, so its overriding
780        return true;
781    }
782
783    /**
784     * Returns a list of methods which are annotated with the given annotation
785     *
786     * @param  type           the type to reflect on
787     * @param  annotationType the annotation type
788     * @return                a list of the methods found
789     */
790    public static List<Method> findMethodsWithAnnotation(
791            Class<?> type,
792            Class<? extends Annotation> annotationType) {
793        return findMethodsWithAnnotation(type, annotationType, false);
794    }
795
796    /**
797     * Returns a list of methods which are annotated with the given annotation
798     *
799     * @param  type                 the type to reflect on
800     * @param  annotationType       the annotation type
801     * @param  checkMetaAnnotations check for meta annotations
802     * @return                      a list of the methods found
803     */
804    public static List<Method> findMethodsWithAnnotation(
805            Class<?> type,
806            Class<? extends Annotation> annotationType,
807            boolean checkMetaAnnotations) {
808        List<Method> answer = new ArrayList<>();
809        do {
810            Method[] methods = type.getDeclaredMethods();
811            for (Method method : methods) {
812                if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
813                    answer.add(method);
814                }
815            }
816            type = type.getSuperclass();
817        } while (type != null);
818        return answer;
819    }
820
821    /**
822     * Checks if a Class or Method are annotated with the given annotation
823     *
824     * @param  elem                 the Class or Method to reflect on
825     * @param  annotationType       the annotation type
826     * @param  checkMetaAnnotations check for meta annotations
827     * @return                      true if annotations is present
828     */
829    public static boolean hasAnnotation(
830            AnnotatedElement elem, Class<? extends Annotation> annotationType,
831            boolean checkMetaAnnotations) {
832        if (elem.isAnnotationPresent(annotationType)) {
833            return true;
834        }
835        if (checkMetaAnnotations) {
836            for (Annotation a : elem.getAnnotations()) {
837                for (Annotation meta : a.annotationType().getAnnotations()) {
838                    if (meta.annotationType().getName().equals(annotationType.getName())) {
839                        return true;
840                    }
841                }
842            }
843        }
844        return false;
845    }
846
847    /**
848     * Turns the given object arrays into a meaningful string
849     *
850     * @param  objects an array of objects or null
851     * @return         a meaningful string
852     */
853    public static String asString(Object[] objects) {
854        if (objects == null) {
855            return "null";
856        } else {
857            StringBuilder buffer = new StringBuilder("{");
858            int counter = 0;
859            for (Object object : objects) {
860                if (counter++ > 0) {
861                    buffer.append(", ");
862                }
863                String text = (object == null) ? "null" : object.toString();
864                buffer.append(text);
865            }
866            buffer.append("}");
867            return buffer.toString();
868        }
869    }
870
871    /**
872     * Returns true if a class is assignable from another class like the {@link Class#isAssignableFrom(Class)} method
873     * but which also includes coercion between primitive types to deal with Java 5 primitive type wrapping
874     */
875    public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
876        a = convertPrimitiveTypeToWrapperType(a);
877        b = convertPrimitiveTypeToWrapperType(b);
878        return a.isAssignableFrom(b);
879    }
880
881    /**
882     * Returns if the given {@code clazz} type is a Java primitive array type.
883     *
884     * @param  clazz the Java type to be checked
885     * @return       {@code true} if the given type is a Java primitive array type
886     */
887    public static boolean isPrimitiveArrayType(Class<?> clazz) {
888        if (clazz != null && clazz.isArray()) {
889            return clazz.getComponentType().isPrimitive();
890        }
891        return false;
892    }
893
894    /**
895     * Used by camel-bean
896     */
897    public static int arrayLength(Object[] pojo) {
898        return pojo.length;
899    }
900
901    /**
902     * Converts primitive types such as int to its wrapper type like {@link Integer}
903     */
904    public static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
905        Class<?> rc = type;
906        if (type.isPrimitive()) {
907            if (type == int.class) {
908                rc = Integer.class;
909            } else if (type == long.class) {
910                rc = Long.class;
911            } else if (type == double.class) {
912                rc = Double.class;
913            } else if (type == float.class) {
914                rc = Float.class;
915            } else if (type == short.class) {
916                rc = Short.class;
917            } else if (type == byte.class) {
918                rc = Byte.class;
919            } else if (type == boolean.class) {
920                rc = Boolean.class;
921            } else if (type == char.class) {
922                rc = Character.class;
923            }
924        }
925        return rc;
926    }
927
928    /**
929     * Helper method to return the default character set name
930     */
931    public static String getDefaultCharacterSet() {
932        return Charset.defaultCharset().name();
933    }
934
935    /**
936     * Returns the Java Bean property name of the given method, if it is a setter
937     */
938    public static String getPropertyName(Method method) {
939        String propertyName = method.getName();
940        if (propertyName.startsWith("set") && method.getParameterCount() == 1) {
941            propertyName = propertyName.substring(3, 4).toLowerCase(Locale.ENGLISH) + propertyName.substring(4);
942        }
943        return propertyName;
944    }
945
946    /**
947     * Returns true if the given collection of annotations matches the given type
948     */
949    public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
950        for (Annotation annotation : annotations) {
951            if (type.isInstance(annotation)) {
952                return true;
953            }
954        }
955        return false;
956    }
957
958    /**
959     * Gets the annotation from the given instance.
960     *
961     * @param  instance the instance
962     * @param  type     the annotation
963     * @return          the annotation, or <tt>null</tt> if the instance does not have the given annotation
964     */
965    public static <A extends java.lang.annotation.Annotation> A getAnnotation(Object instance, Class<A> type) {
966        return instance.getClass().getAnnotation(type);
967    }
968
969    /**
970     * Converts the given value to the required type or throw a meaningful exception
971     */
972    @SuppressWarnings("unchecked")
973    public static <T> T cast(Class<T> toType, Object value) {
974        if (toType == boolean.class) {
975            return (T) cast(Boolean.class, value);
976        } else if (toType.isPrimitive()) {
977            Class<?> newType = convertPrimitiveTypeToWrapperType(toType);
978            if (newType != toType) {
979                return (T) cast(newType, value);
980            }
981        }
982        try {
983            return toType.cast(value);
984        } catch (ClassCastException e) {
985            throw new IllegalArgumentException(
986                    "Failed to convert: "
987                                               + value + " to type: " + toType.getName() + " due to: " + e,
988                    e);
989        }
990    }
991
992    /**
993     * Does the given class have a default public no-arg constructor.
994     */
995    public static boolean hasDefaultPublicNoArgConstructor(Class<?> type) {
996        // getConstructors() returns only public constructors
997        for (Constructor<?> ctr : type.getConstructors()) {
998            if (ctr.getParameterCount() == 0) {
999                return true;
1000            }
1001        }
1002        return false;
1003    }
1004
1005    /**
1006     * Returns the type of the given object or null if the value is null
1007     */
1008    public static Object type(Object bean) {
1009        return bean != null ? bean.getClass() : null;
1010    }
1011
1012    /**
1013     * Evaluate the value as a predicate which attempts to convert the value to a boolean otherwise true is returned if
1014     * the value is not null
1015     */
1016    public static boolean evaluateValuePredicate(Object value) {
1017        if (value instanceof Boolean) {
1018            return (Boolean) value;
1019        } else if (value instanceof String) {
1020            String str = ((String) value).trim();
1021            if (str.isEmpty()) {
1022                return false;
1023            } else if ("true".equalsIgnoreCase(str)) {
1024                return true;
1025            } else if ("false".equalsIgnoreCase(str)) {
1026                return false;
1027            }
1028        } else if (value instanceof NodeList) {
1029            // is it an empty dom with empty attributes
1030            if (value instanceof Node && ((Node) value).hasAttributes()) {
1031                return true;
1032            }
1033            NodeList list = (NodeList) value;
1034            return list.getLength() > 0;
1035        } else if (value instanceof Collection) {
1036            // is it an empty collection
1037            return !((Collection<?>) value).isEmpty();
1038        }
1039        return value != null;
1040    }
1041
1042    /**
1043     * Creates an Iterable to walk the exception from the bottom up (the last caused by going upwards to the root
1044     * exception).
1045     *
1046     * @see              java.lang.Iterable
1047     * @param  exception the exception
1048     * @return           the Iterable
1049     */
1050    public static Iterable<Throwable> createExceptionIterable(Throwable exception) {
1051        List<Throwable> throwables = new ArrayList<>();
1052
1053        Throwable current = exception;
1054        // spool to the bottom of the caused by tree
1055        while (current != null) {
1056            throwables.add(current);
1057            current = current.getCause();
1058        }
1059        Collections.reverse(throwables);
1060
1061        return throwables;
1062    }
1063
1064    /**
1065     * Creates an Iterator to walk the exception from the bottom up (the last caused by going upwards to the root
1066     * exception).
1067     *
1068     * @see              Iterator
1069     * @param  exception the exception
1070     * @return           the Iterator
1071     */
1072    public static Iterator<Throwable> createExceptionIterator(Throwable exception) {
1073        return createExceptionIterable(exception).iterator();
1074    }
1075
1076    /**
1077     * Retrieves the given exception type from the exception.
1078     * <p/>
1079     * Is used to get the caused exception that typically have been wrapped in some sort of Camel wrapper exception
1080     * <p/>
1081     * The strategy is to look in the exception hierarchy to find the first given cause that matches the type. Will
1082     * start from the bottom (the real cause) and walk upwards.
1083     *
1084     * @param  type      the exception type wanted to retrieve
1085     * @param  exception the caused exception
1086     * @return           the exception found (or <tt>null</tt> if not found in the exception hierarchy)
1087     */
1088    public static <T> T getException(Class<T> type, Throwable exception) {
1089        if (exception == null) {
1090            return null;
1091        }
1092
1093        //check the suppressed exception first
1094        for (Throwable throwable : exception.getSuppressed()) {
1095            if (type.isInstance(throwable)) {
1096                return type.cast(throwable);
1097            }
1098        }
1099
1100        // walk the hierarchy and look for it
1101        for (final Throwable throwable : createExceptionIterable(exception)) {
1102            if (type.isInstance(throwable)) {
1103                return type.cast(throwable);
1104            }
1105        }
1106
1107        // not found
1108        return null;
1109    }
1110
1111    public static String getIdentityHashCode(Object object) {
1112        return "0x" + Integer.toHexString(System.identityHashCode(object));
1113    }
1114
1115    /**
1116     * Lookup the constant field on the given class with the given name
1117     *
1118     * @param  clazz the class
1119     * @param  name  the name of the field to lookup
1120     * @return       the value of the constant field, or <tt>null</tt> if not found
1121     */
1122    public static String lookupConstantFieldValue(Class<?> clazz, String name) {
1123        if (clazz == null) {
1124            return null;
1125        }
1126
1127        // remove leading dots
1128        if (name.startsWith(".")) {
1129            name = name.substring(1);
1130        }
1131
1132        for (Field field : clazz.getFields()) {
1133            if (field.getName().equals(name)) {
1134                try {
1135                    Object v = field.get(null);
1136                    return v.toString();
1137                } catch (IllegalAccessException e) {
1138                    // ignore
1139                    return null;
1140                }
1141            }
1142        }
1143
1144        return null;
1145    }
1146
1147    /**
1148     * Is the given value a numeric NaN type
1149     *
1150     * @param  value the value
1151     * @return       <tt>true</tt> if its a {@link Float#NaN} or {@link Double#NaN}.
1152     */
1153    public static boolean isNaN(Object value) {
1154        return value instanceof Number && (FLOAT_NAN.equals(value) || DOUBLE_NAN.equals(value));
1155    }
1156
1157    /**
1158     * Wraps the caused exception in a {@link RuntimeException} if its not already such an exception.
1159     *
1160     * @param      e the caused exception
1161     * @return       the wrapper exception
1162     * @deprecated   Use {@link org.apache.camel.RuntimeCamelException#wrapRuntimeCamelException} instead
1163     */
1164    @Deprecated
1165    public static RuntimeException wrapRuntimeCamelException(Throwable e) {
1166        try {
1167            Class<? extends RuntimeException> clazz = (Class) Class.forName("org.apache.camel.RuntimeException");
1168            if (clazz.isInstance(e)) {
1169                // don't double wrap
1170                return clazz.cast(e);
1171            } else {
1172                return clazz.getConstructor(Throwable.class).newInstance(e);
1173            }
1174        } catch (Throwable t) {
1175            // ignore
1176        }
1177        if (e instanceof RuntimeException) {
1178            // don't double wrap
1179            return (RuntimeException) e;
1180        } else {
1181            return new RuntimeException(e);
1182        }
1183    }
1184
1185    /**
1186     * Turns the input array to a list of objects.
1187     * 
1188     * @param  objects an array of objects or null
1189     * @return         an object list
1190     */
1191    public static List<Object> asList(Object[] objects) {
1192        return objects != null ? Arrays.asList(objects) : Collections.emptyList();
1193    }
1194
1195}