001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.util;
018
019 import java.io.Closeable;
020 import java.io.File;
021 import java.io.FileNotFoundException;
022 import java.io.IOException;
023 import java.io.InputStream;
024 import java.lang.annotation.Annotation;
025 import java.lang.reflect.AnnotatedElement;
026 import java.lang.reflect.InvocationTargetException;
027 import java.lang.reflect.Method;
028 import java.net.URL;
029 import java.nio.channels.ReadableByteChannel;
030 import java.nio.charset.Charset;
031 import java.util.ArrayList;
032 import java.util.Arrays;
033 import java.util.Collection;
034 import java.util.Collections;
035 import java.util.Iterator;
036 import java.util.List;
037 import java.util.Scanner;
038
039 import org.w3c.dom.Node;
040 import org.w3c.dom.NodeList;
041
042 import org.apache.camel.CamelExecutionException;
043 import org.apache.camel.Exchange;
044 import org.apache.camel.Message;
045 import org.apache.camel.RuntimeCamelException;
046 import org.apache.camel.TypeConverter;
047 import org.apache.camel.component.file.GenericFile;
048 import org.apache.commons.logging.Log;
049 import org.apache.commons.logging.LogFactory;
050
051 /**
052 * A number of useful helper methods for working with Objects
053 *
054 * @version $Revision: 905171 $
055 */
056 public final class ObjectHelper {
057 private static final transient Log LOG = LogFactory.getLog(ObjectHelper.class);
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 in which it uses type coerce to coerce
067 * types between the left and right values. This allows you to equal test eg String and Integer as
068 * Camel will be able to coerce the types
069 */
070 public static boolean typeCoerceEquals(TypeConverter converter, Object leftValue, Object rightValue) {
071 // try without type coerce
072 boolean answer = equal(leftValue, rightValue);
073 if (answer) {
074 return true;
075 }
076
077 if (leftValue == null || rightValue == null) {
078 // no reason to continue as the first equal did not match and now one of the values is null
079 // so it wont help to type coerce to a null type
080 return false;
081 }
082
083 // convert left to right
084 Object value = converter.convertTo(rightValue.getClass(), leftValue);
085 answer = equal(value, rightValue);
086 if (answer) {
087 return true;
088 }
089
090 // convert right to left
091 value = converter.convertTo(leftValue.getClass(), rightValue);
092 answer = equal(leftValue, value);
093 return answer;
094 }
095
096 /**
097 * A helper method for comparing objects for equality in which it uses type coerce to coerce
098 * types between the left and right values. This allows you to equal test eg String and Integer as
099 * Camel will be able to coerce the types
100 */
101 public static boolean typeCoerceNotEquals(TypeConverter converter, Object leftValue, Object rightValue) {
102 return !typeCoerceEquals(converter, leftValue, rightValue);
103 }
104
105 /**
106 * A helper method for comparing objects ordering in which it uses type coerce to coerce
107 * types between the left and right values. This allows you to equal test eg String and Integer as
108 * Camel will be able to coerce the types
109 */
110 @SuppressWarnings("unchecked")
111 public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
112 if (rightValue instanceof Comparable) {
113 Object value = converter.convertTo(rightValue.getClass(), leftValue);
114 if (value != null) {
115 return ((Comparable) rightValue).compareTo(value) * -1;
116 }
117 }
118
119 if (leftValue instanceof Comparable) {
120 Object value = converter.convertTo(leftValue.getClass(), rightValue);
121 if (value != null) {
122 return ((Comparable) leftValue).compareTo(value);
123 }
124 }
125
126 // use regular compare
127 return compare(leftValue, rightValue);
128 }
129
130 /**
131 * A helper method for comparing objects for equality while handling nulls
132 */
133 public static boolean equal(Object a, Object b) {
134 if (a == b) {
135 return true;
136 }
137
138 if (a instanceof byte[] && b instanceof byte[]) {
139 return equalByteArray((byte[])a, (byte[])b);
140 }
141
142 return a != null && b != null && a.equals(b);
143 }
144
145 /**
146 * A helper method for comparing byte arrays for equality while handling
147 * nulls
148 */
149 public static boolean equalByteArray(byte[] a, byte[] b) {
150 if (a == b) {
151 return true;
152 }
153
154 // loop and compare each byte
155 if (a != null && b != null && a.length == b.length) {
156 for (int i = 0; i < a.length; i++) {
157 if (a[i] != b[i]) {
158 return false;
159 }
160 }
161 // all bytes are equal
162 return true;
163 }
164
165 return false;
166 }
167
168 /**
169 * Returns true if the given object is equal to any of the expected value
170 */
171 public static boolean isEqualToAny(Object object, Object... values) {
172 for (Object value : values) {
173 if (equal(object, value)) {
174 return true;
175 }
176 }
177 return false;
178 }
179
180 /**
181 * A helper method for performing an ordered comparison on the objects
182 * handling nulls and objects which do not handle sorting gracefully
183 */
184 public static int compare(Object a, Object b) {
185 return compare(a, b, false);
186 }
187
188 /**
189 * A helper method for performing an ordered comparison on the objects
190 * handling nulls and objects which do not handle sorting gracefully
191 *
192 * @param a the first object
193 * @param b the second object
194 * @param ignoreCase ignore case for string comparison
195 */
196 @SuppressWarnings("unchecked")
197 public static int compare(Object a, Object b, boolean ignoreCase) {
198 if (a == b) {
199 return 0;
200 }
201 if (a == null) {
202 return -1;
203 }
204 if (b == null) {
205 return 1;
206 }
207 if (ignoreCase && a instanceof String && b instanceof String) {
208 return ((String) a).compareToIgnoreCase((String) b);
209 }
210 if (a instanceof Comparable) {
211 Comparable comparable = (Comparable)a;
212 return comparable.compareTo(b);
213 } else {
214 int answer = a.getClass().getName().compareTo(b.getClass().getName());
215 if (answer == 0) {
216 answer = a.hashCode() - b.hashCode();
217 }
218 return answer;
219 }
220 }
221
222 public static Boolean toBoolean(Object value) {
223 if (value instanceof Boolean) {
224 return (Boolean)value;
225 }
226 if (value instanceof String) {
227 return "true".equalsIgnoreCase(value.toString()) ? Boolean.TRUE : Boolean.FALSE;
228 }
229 if (value instanceof Integer) {
230 return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
231 }
232 return null;
233 }
234
235 /**
236 * Asserts whether the value is <b>not</b> <tt>null</tt>
237 *
238 * @param value the value to test
239 * @param name the key that resolved the value
240 * @throws IllegalArgumentException is thrown if assertion fails
241 */
242 public static void notNull(Object value, String name) {
243 if (value == null) {
244 throw new IllegalArgumentException(name + " must be specified");
245 }
246 }
247
248 /**
249 * Asserts whether the value is <b>not</b> <tt>null</tt>
250 *
251 * @param value the value to test
252 * @param on additional description to indicate where this problem occured (appended as toString())
253 * @param name the key that resolved the value
254 * @throws IllegalArgumentException is thrown if assertion fails
255 */
256 public static void notNull(Object value, String name, Object on) {
257 if (on == null) {
258 notNull(value, name);
259 } else if (value == null) {
260 throw new IllegalArgumentException(name + " must be specified on: " + on);
261 }
262 }
263
264 /**
265 * Asserts whether the string is <b>not</b> empty.
266 *
267 * @param value the string to test
268 * @param name the key that resolved the value
269 * @throws IllegalArgumentException is thrown if assertion fails
270 */
271 public static void notEmpty(String value, String name) {
272 if (isEmpty(value)) {
273 throw new IllegalArgumentException(name + " must be specified and not empty");
274 }
275 }
276
277 /**
278 * Asserts whether the string is <b>not</b> empty.
279 *
280 * @param value the string to test
281 * @param on additional description to indicate where this problem occured (appended as toString())
282 * @param name the key that resolved the value
283 * @throws IllegalArgumentException is thrown if assertion fails
284 */
285 public static void notEmpty(String value, String name, Object on) {
286 if (on == null) {
287 notNull(value, name);
288 } else if (isEmpty(value)) {
289 throw new IllegalArgumentException(name + " must be specified and not empty on: " + on);
290 }
291 }
292
293 /**
294 * Tests whether the value is <tt>null</tt> or an empty string.
295 *
296 * @param value the value, if its a String it will be tested for text length as well
297 * @return true if empty
298 */
299 public static boolean isEmpty(Object value) {
300 return !isNotEmpty(value);
301 }
302
303 /**
304 * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string.
305 *
306 * @param value the value, if its a String it will be tested for text length as well
307 * @return true if <b>not</b> empty
308 */
309 public static boolean isNotEmpty(Object value) {
310 if (value == null) {
311 return false;
312 } else if (value instanceof String) {
313 String text = (String) value;
314 return text.trim().length() > 0;
315 } else {
316 return true;
317 }
318 }
319
320 public static String[] splitOnCharacter(String value, String needle, int count) {
321 String rc[] = new String[count];
322 rc[0] = value;
323 for (int i = 1; i < count; i++) {
324 String v = rc[i - 1];
325 int p = v.indexOf(needle);
326 if (p < 0) {
327 return rc;
328 }
329 rc[i - 1] = v.substring(0, p);
330 rc[i] = v.substring(p + 1);
331 }
332 return rc;
333 }
334
335 /**
336 * Removes any starting characters on the given text which match the given
337 * character
338 *
339 * @param text the string
340 * @param ch the initial characters to remove
341 * @return either the original string or the new substring
342 */
343 public static String removeStartingCharacters(String text, char ch) {
344 int idx = 0;
345 while (text.charAt(idx) == ch) {
346 idx++;
347 }
348 if (idx > 0) {
349 return text.substring(idx);
350 }
351 return text;
352 }
353
354 public static String capitalize(String text) {
355 if (text == null) {
356 return null;
357 }
358 int length = text.length();
359 if (length == 0) {
360 return text;
361 }
362 String answer = text.substring(0, 1).toUpperCase();
363 if (length > 1) {
364 answer += text.substring(1, length);
365 }
366 return answer;
367 }
368
369 public static String after(String text, String after) {
370 if (!text.contains(after)) {
371 return null;
372 }
373 return text.substring(text.indexOf(after) + after.length());
374 }
375
376 public static String before(String text, String before) {
377 if (!text.contains(before)) {
378 return null;
379 }
380 return text.substring(0, text.indexOf(before));
381 }
382
383 public static String between(String text, String after, String before) {
384 text = after(text, after);
385 if (text == null) {
386 return null;
387 }
388 return before(text, before);
389 }
390
391 /**
392 * Returns true if the collection contains the specified value
393 */
394 @SuppressWarnings("unchecked")
395 public static boolean contains(Object collectionOrArray, Object value) {
396 if (collectionOrArray instanceof Collection) {
397 Collection collection = (Collection)collectionOrArray;
398 return collection.contains(value);
399 } else if (collectionOrArray instanceof String && value instanceof String) {
400 String str = (String)collectionOrArray;
401 String subStr = (String)value;
402 return str.contains(subStr);
403 } else {
404 Iterator iter = createIterator(collectionOrArray);
405 while (iter.hasNext()) {
406 if (equal(value, iter.next())) {
407 return true;
408 }
409 }
410 }
411 return false;
412 }
413
414 /**
415 * Creates an iterator over the value if the value is a collection, an
416 * Object[] or a primitive type array; otherwise to simplify the caller's
417 * code, we just create a singleton collection iterator over a single value
418 * <p/>
419 * Will default use comma for String separating String values.
420 *
421 * @param value the value
422 * @return the iterator
423 */
424 public static Iterator<Object> createIterator(Object value) {
425 return createIterator(value, ",");
426 }
427
428 /**
429 * Creates an iterator over the value if the value is a collection, an
430 * Object[] or a primitive type array; otherwise to simplify the caller's
431 * code, we just create a singleton collection iterator over a single value
432 *
433 * @param value the value
434 * @param delimiter delimiter for separating String values
435 * @return the iterator
436 */
437 @SuppressWarnings("unchecked")
438 public static Iterator<Object> createIterator(Object value, String delimiter) {
439 // if its a message than we want to iterate its body
440 if (value instanceof Message) {
441 value = ((Message) value).getBody();
442 }
443
444 if (value == null) {
445 return Collections.emptyList().iterator();
446 } else if (value instanceof Iterator) {
447 return (Iterator)value;
448 } else if (value instanceof Collection) {
449 Collection collection = (Collection)value;
450 return collection.iterator();
451 } else if (value.getClass().isArray()) {
452 // TODO we should handle primitive array types?
453 List<Object> list = Arrays.asList((Object[])value);
454 return list.iterator();
455 } else if (value instanceof NodeList) {
456 // lets iterate through DOM results after performing XPaths
457 final NodeList nodeList = (NodeList)value;
458 return CastUtils.cast(new Iterator<Node>() {
459 int idx = -1;
460
461 public boolean hasNext() {
462 return ++idx < nodeList.getLength();
463 }
464
465 public Node next() {
466 return nodeList.item(idx);
467 }
468
469 public void remove() {
470 throw new UnsupportedOperationException();
471 }
472 });
473 } else if (value instanceof String) {
474 final String s = (String) value;
475
476 // this code is optimized to only use a Scanner if needed, eg there is a delimiter
477
478 if (s.contains(delimiter)) {
479 // use a scanner if it contains the delimiter
480 Scanner scanner = new Scanner((String)value);
481 scanner.useDelimiter(delimiter);
482 return CastUtils.cast(scanner);
483 } else {
484 // use a plain iterator that returns the value as is as there are only a single value
485 return CastUtils.cast(new Iterator<String>() {
486 int idx = -1;
487
488 public boolean hasNext() {
489 // empty string should not be regarded as having next
490 return ++idx == 0 && ObjectHelper.isNotEmpty(s);
491 }
492
493 public String next() {
494 return s;
495 }
496
497 public void remove() {
498 throw new UnsupportedOperationException();
499 }
500 });
501 }
502 } else {
503 return Collections.singletonList(value).iterator();
504 }
505 }
506
507 /**
508 * Returns the predicate matching boolean on a {@link List} result set where
509 * if the first element is a boolean its value is used otherwise this method
510 * returns true if the collection is not empty
511 *
512 * @return <tt>true</tt> if the first element is a boolean and its value
513 * is true or if the list is non empty
514 */
515 public static boolean matches(List<Object> list) {
516 if (!list.isEmpty()) {
517 Object value = list.get(0);
518 if (value instanceof Boolean) {
519 return (Boolean)value;
520 } else {
521 // lets assume non-empty results are true
522 return true;
523 }
524 }
525 return false;
526 }
527
528 /**
529 * A helper method to access a system property, catching any security
530 * exceptions
531 *
532 * @param name the name of the system property required
533 * @param defaultValue the default value to use if the property is not
534 * available or a security exception prevents access
535 * @return the system property value or the default value if the property is
536 * not available or security does not allow its access
537 */
538 public static String getSystemProperty(String name, String defaultValue) {
539 try {
540 return System.getProperty(name, defaultValue);
541 } catch (Exception e) {
542 if (LOG.isDebugEnabled()) {
543 LOG.debug("Caught security exception accessing system property: " + name + ". Reason: " + e, e);
544 }
545 return defaultValue;
546 }
547 }
548
549 /**
550 * A helper method to access a boolean system property, catching any
551 * security exceptions
552 *
553 * @param name the name of the system property required
554 * @param defaultValue the default value to use if the property is not
555 * available or a security exception prevents access
556 * @return the boolean representation of the system property value or the
557 * default value if the property is not available or security does
558 * not allow its access
559 */
560 public static boolean getSystemProperty(String name, Boolean defaultValue) {
561 String result = getSystemProperty(name, defaultValue.toString());
562 return Boolean.parseBoolean(result);
563 }
564
565 /**
566 * Returns the type name of the given type or null if the type variable is
567 * null
568 */
569 public static String name(Class<?> type) {
570 return type != null ? type.getName() : null;
571 }
572
573 /**
574 * Returns the type name of the given value
575 */
576 public static String className(Object value) {
577 return name(value != null ? value.getClass() : null);
578 }
579
580 /**
581 * Returns the canonical type name of the given value
582 */
583 public static String classCanonicalName(Object value) {
584 if (value != null) {
585 return value.getClass().getCanonicalName();
586 } else {
587 return null;
588 }
589 }
590
591 /**
592 * Attempts to load the given class name using the thread context class
593 * loader or the class loader used to load this class
594 *
595 * @param name the name of the class to load
596 * @return the class or null if it could not be loaded
597 */
598 public static Class<?> loadClass(String name) {
599 return loadClass(name, ObjectHelper.class.getClassLoader());
600 }
601
602 /**
603 * Attempts to load the given class name using the thread context class
604 * loader or the given class loader
605 *
606 * @param name the name of the class to load
607 * @param loader the class loader to use after the thread context class
608 * loader
609 * @return the class or null if it could not be loaded
610 */
611 public static Class<?> loadClass(String name, ClassLoader loader) {
612 return loadClass(name, loader, true);
613 }
614
615 /**
616 * Attempts to load the given class name using the thread context class
617 * loader or the given class loader
618 *
619 * @param name the name of the class to load
620 * @param loader the class loader to use after the thread context class
621 * loader
622 * @param needToWarn if it is true will use log a warning message for not loading the class
623 * @return the class or null if it could not be loaded
624 */
625 public static Class<?> loadClass(String name, ClassLoader loader, boolean needToWarn) {
626 // must clean the name so its pure java name, eg remoing \n or whatever people can do in the Spring XML
627 name = normalizeClassName(name);
628
629 // special for byte[] as its common to use
630 if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
631 return byte[].class;
632 } else if ("java.lang.String".equals(name) || "String".equals(name)) {
633 return String.class;
634 }
635
636 // try context class loader first
637 Class<?> clazz = doLoadClass(name, Thread.currentThread().getContextClassLoader());
638 if (clazz == null) {
639 // then the provided loader
640 clazz = doLoadClass(name, loader);
641 }
642 if (clazz == null) {
643 // and fallback to the loader the loaded the ObjectHelper class
644 clazz = doLoadClass(name, ObjectHelper.class.getClassLoader());
645 }
646
647 if (clazz == null) {
648 if (needToWarn) {
649 LOG.warn("Cannot find class: " + name);
650 }
651 }
652
653 return clazz;
654 }
655
656 /**
657 * Loads the given class with the provided classloader (may be null).
658 * Will ignore any class not found and return null.
659 *
660 * @param name the name of the class to load
661 * @param loader a provided loader (may be null)
662 * @return the class, or null if it could not be loaded
663 */
664 private static Class<?> doLoadClass(String name, ClassLoader loader) {
665 ObjectHelper.notEmpty(name, "name");
666 if (loader == null) {
667 return null;
668 }
669 try {
670 if (LOG.isTraceEnabled()) {
671 LOG.trace("Loading class: " + name + " using classloader: " + loader);
672 }
673 return loader.loadClass(name);
674 } catch (ClassNotFoundException e) {
675 if (LOG.isTraceEnabled()) {
676 LOG.trace("Cannot load class: " + name + " using classloader: " + loader, e);
677 }
678
679 }
680 return null;
681 }
682
683 /**
684 * Attempts to load the given resource as a stream using the thread context
685 * class loader or the class loader used to load this class
686 *
687 * @param name the name of the resource to load
688 * @return the stream or null if it could not be loaded
689 */
690 public static InputStream loadResourceAsStream(String name) {
691 InputStream in = null;
692
693 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
694 if (contextClassLoader != null) {
695 in = contextClassLoader.getResourceAsStream(name);
696 }
697 if (in == null) {
698 in = ObjectHelper.class.getClassLoader().getResourceAsStream(name);
699 }
700
701 return in;
702 }
703
704 /**
705 * Attempts to load the given resource as a stream using the thread context
706 * class loader or the class loader used to load this class
707 *
708 * @param name the name of the resource to load
709 * @return the stream or null if it could not be loaded
710 */
711 public static URL loadResourceAsURL(String name) {
712 URL url = null;
713
714 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
715 if (contextClassLoader != null) {
716 url = contextClassLoader.getResource(name);
717 }
718 if (url == null) {
719 url = ObjectHelper.class.getClassLoader().getResource(name);
720 }
721
722 return url;
723 }
724
725 /**
726 * A helper method to invoke a method via reflection and wrap any exceptions
727 * as {@link RuntimeCamelException} instances
728 *
729 * @param method the method to invoke
730 * @param instance the object instance (or null for static methods)
731 * @param parameters the parameters to the method
732 * @return the result of the method invocation
733 */
734 public static Object invokeMethod(Method method, Object instance, Object... parameters) {
735 try {
736 return method.invoke(instance, parameters);
737 } catch (IllegalAccessException e) {
738 throw new RuntimeCamelException(e);
739 } catch (InvocationTargetException e) {
740 throw new RuntimeCamelException(e.getCause());
741 }
742 }
743
744 /**
745 * Tests whether the target method overrides the source method.
746 * <p/>
747 * Tests whether they have the same name, return type, and parameter list.
748 *
749 * @param source the source method
750 * @param target the target method
751 * @return <tt>true</tt> if it override, <tt>false</tt> otherwise
752 */
753 public static boolean isOverridingMethod(Method source, Method target) {
754 if (source.getName().equals(target.getName())
755 && source.getReturnType().equals(target.getReturnType())
756 && source.getParameterTypes().length == target.getParameterTypes().length) {
757
758 // test if parameter types is the same as well
759 for (int i = 0; i < source.getParameterTypes().length; i++) {
760 if (!(source.getParameterTypes()[i].equals(target.getParameterTypes()[i]))) {
761 return false;
762 }
763 }
764
765 // the have same name, return type and parameter list, so its overriding
766 return true;
767 }
768
769 return false;
770 }
771
772 /**
773 * Returns a list of methods which are annotated with the given annotation
774 *
775 * @param type the type to reflect on
776 * @param annotationType the annotation type
777 * @return a list of the methods found
778 */
779 public static List<Method> findMethodsWithAnnotation(Class<?> type,
780 Class<? extends Annotation> annotationType) {
781 return findMethodsWithAnnotation(type, annotationType, false);
782 }
783
784 /**
785 * Returns a list of methods which are annotated with the given annotation
786 *
787 * @param type the type to reflect on
788 * @param annotationType the annotation type
789 * @param checkMetaAnnotations check for meta annotations
790 * @return a list of the methods found
791 */
792 public static List<Method> findMethodsWithAnnotation(Class<?> type,
793 Class<? extends Annotation> annotationType,
794 boolean checkMetaAnnotations) {
795 List<Method> answer = new ArrayList<Method>();
796 do {
797 Method[] methods = type.getDeclaredMethods();
798 for (Method method : methods) {
799 if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
800 answer.add(method);
801 }
802 }
803 type = type.getSuperclass();
804 } while (type != null);
805 return answer;
806 }
807
808 /**
809 * Checks if a Class or Method are annotated with the given annotation
810 *
811 * @param elem the Class or Method to reflect on
812 * @param annotationType the annotation type
813 * @param checkMetaAnnotations check for meta annotations
814 * @return true if annotations is present
815 */
816 public static boolean hasAnnotation(AnnotatedElement elem, Class<? extends Annotation> annotationType,
817 boolean checkMetaAnnotations) {
818 if (elem.isAnnotationPresent(annotationType)) {
819 return true;
820 }
821 if (checkMetaAnnotations) {
822 for (Annotation a : elem.getAnnotations()) {
823 for (Annotation meta : a.annotationType().getAnnotations()) {
824 if (meta.annotationType().getName().equals(annotationType.getName())) {
825 return true;
826 }
827 }
828 }
829 }
830 return false;
831 }
832
833 /**
834 * Turns the given object arrays into a meaningful string
835 *
836 * @param objects an array of objects or null
837 * @return a meaningful string
838 */
839 public static String asString(Object[] objects) {
840 if (objects == null) {
841 return "null";
842 } else {
843 StringBuffer buffer = new StringBuffer("{");
844 int counter = 0;
845 for (Object object : objects) {
846 if (counter++ > 0) {
847 buffer.append(", ");
848 }
849 String text = (object == null) ? "null" : object.toString();
850 buffer.append(text);
851 }
852 buffer.append("}");
853 return buffer.toString();
854 }
855 }
856
857 /**
858 * Returns true if a class is assignable from another class like the
859 * {@link Class#isAssignableFrom(Class)} method but which also includes
860 * coercion between primitive types to deal with Java 5 primitive type
861 * wrapping
862 */
863 public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
864 a = convertPrimitiveTypeToWrapperType(a);
865 b = convertPrimitiveTypeToWrapperType(b);
866 return a.isAssignableFrom(b);
867 }
868
869 /**
870 * Converts primitive types such as int to its wrapper type like
871 * {@link Integer}
872 */
873 public static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
874 Class<?> rc = type;
875 if (type.isPrimitive()) {
876 if (type == int.class) {
877 rc = Integer.class;
878 } else if (type == long.class) {
879 rc = Long.class;
880 } else if (type == double.class) {
881 rc = Double.class;
882 } else if (type == float.class) {
883 rc = Float.class;
884 } else if (type == short.class) {
885 rc = Short.class;
886 } else if (type == byte.class) {
887 rc = Byte.class;
888 } else if (type == boolean.class) {
889 rc = Boolean.class;
890 }
891 }
892 return rc;
893 }
894
895 /**
896 * Helper method to return the default character set name
897 */
898 public static String getDefaultCharacterSet() {
899 return Charset.defaultCharset().name();
900 }
901
902 /**
903 * Returns the Java Bean property name of the given method, if it is a
904 * setter
905 */
906 public static String getPropertyName(Method method) {
907 String propertyName = method.getName();
908 if (propertyName.startsWith("set") && method.getParameterTypes().length == 1) {
909 propertyName = propertyName.substring(3, 4).toLowerCase() + propertyName.substring(4);
910 }
911 return propertyName;
912 }
913
914 /**
915 * Returns true if the given collection of annotations matches the given
916 * type
917 */
918 public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
919 for (Annotation annotation : annotations) {
920 if (type.isInstance(annotation)) {
921 return true;
922 }
923 }
924 return false;
925 }
926
927 /**
928 * Closes the given resource if it is available, logging any closing
929 * exceptions to the given log
930 *
931 * @param closeable the object to close
932 * @param name the name of the resource
933 * @param log the log to use when reporting closure warnings
934 */
935 public static void close(Closeable closeable, String name, Log log) {
936 if (closeable != null) {
937 try {
938 closeable.close();
939 } catch (IOException e) {
940 if (log != null) {
941 log.warn("Cannot close: " + name + ". Reason: " + e, e);
942 }
943 }
944 }
945 }
946
947 /**
948 * Converts the given value to the required type or throw a meaningful exception
949 */
950 @SuppressWarnings("unchecked")
951 public static <T> T cast(Class<T> toType, Object value) {
952 if (toType == boolean.class) {
953 return (T)cast(Boolean.class, value);
954 } else if (toType.isPrimitive()) {
955 Class newType = convertPrimitiveTypeToWrapperType(toType);
956 if (newType != toType) {
957 return (T)cast(newType, value);
958 }
959 }
960 try {
961 return toType.cast(value);
962 } catch (ClassCastException e) {
963 throw new IllegalArgumentException("Failed to convert: "
964 + value + " to type: " + toType.getName() + " due to: " + e, e);
965 }
966 }
967
968 /**
969 * A helper method to create a new instance of a type using the default
970 * constructor arguments.
971 */
972 public static <T> T newInstance(Class<T> type) {
973 try {
974 return type.newInstance();
975 } catch (InstantiationException e) {
976 throw new RuntimeCamelException(e);
977 } catch (IllegalAccessException e) {
978 throw new RuntimeCamelException(e);
979 }
980 }
981
982 /**
983 * A helper method to create a new instance of a type using the default
984 * constructor arguments.
985 */
986 public static <T> T newInstance(Class<?> actualType, Class<T> expectedType) {
987 try {
988 Object value = actualType.newInstance();
989 return cast(expectedType, value);
990 } catch (InstantiationException e) {
991 throw new RuntimeCamelException(e);
992 } catch (IllegalAccessException e) {
993 throw new RuntimeCamelException(e);
994 }
995 }
996
997 /**
998 * Returns true if the given name is a valid java identifier
999 */
1000 public static boolean isJavaIdentifier(String name) {
1001 if (name == null) {
1002 return false;
1003 }
1004 int size = name.length();
1005 if (size < 1) {
1006 return false;
1007 }
1008 if (Character.isJavaIdentifierStart(name.charAt(0))) {
1009 for (int i = 1; i < size; i++) {
1010 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1011 return false;
1012 }
1013 }
1014 return true;
1015 }
1016 return false;
1017 }
1018
1019 /**
1020 * Returns the type of the given object or null if the value is null
1021 */
1022 public static Object type(Object bean) {
1023 return bean != null ? bean.getClass() : null;
1024 }
1025
1026 /**
1027 * Evaluate the value as a predicate which attempts to convert the value to
1028 * a boolean otherwise true is returned if the value is not null
1029 */
1030 public static boolean evaluateValuePredicate(Object value) {
1031 if (value instanceof Boolean) {
1032 return (Boolean)value;
1033 } else if (value instanceof String) {
1034 if ("true".equals(value)) {
1035 return true;
1036 } else if ("false".equals(value)) {
1037 return false;
1038 }
1039 }
1040 return value != null;
1041 }
1042
1043 /**
1044 * Wraps the caused exception in a {@link RuntimeCamelException} if its not
1045 * already such an exception.
1046 *
1047 * @param e the caused exception
1048 * @return the wrapper exception
1049 */
1050 public static RuntimeCamelException wrapRuntimeCamelException(Throwable e) {
1051 if (e instanceof RuntimeCamelException) {
1052 // don't double wrap
1053 return (RuntimeCamelException)e;
1054 } else {
1055 return new RuntimeCamelException(e);
1056 }
1057 }
1058
1059 /**
1060 * Wraps the caused exception in a {@link CamelExecutionException} if its not
1061 * already such an exception.
1062 *
1063 * @param e the caused exception
1064 * @return the wrapper exception
1065 */
1066 public static CamelExecutionException wrapCamelExecutionException(Exchange exchange, Throwable e) {
1067 if (e instanceof CamelExecutionException) {
1068 // don't double wrap
1069 return (CamelExecutionException)e;
1070 } else {
1071 return new CamelExecutionException("Exception occurred during execution", exchange, e);
1072 }
1073 }
1074
1075 /**
1076 * Cleans the string to pure java identifier so we can use it for loading class names.
1077 * <p/>
1078 * Especially from Spring DSL people can have \n \t or other characters that otherwise
1079 * would result in ClassNotFoundException
1080 *
1081 * @param name the class name
1082 * @return normalized classname that can be load by a class loader.
1083 */
1084 public static String normalizeClassName(String name) {
1085 StringBuffer sb = new StringBuffer(name.length());
1086 for (char ch : name.toCharArray()) {
1087 if (ch == '.' || ch == '[' || ch == ']' || ch == '-' || Character.isJavaIdentifierPart(ch)) {
1088 sb.append(ch);
1089 }
1090 }
1091 return sb.toString();
1092 }
1093
1094 /**
1095 * Creates an iterator to walk the exception from the bottom up
1096 * (the last caused by going upwards to the root exception).
1097 *
1098 * @param exception the exception
1099 * @return the iterator
1100 */
1101 public static Iterator<Throwable> createExceptionIterator(Throwable exception) {
1102 return new ExceptionIterator(exception);
1103 }
1104
1105 /**
1106 * Retrieves the given exception type from the exception.
1107 * <p/>
1108 * Is used to get the caused exception that typically have been wrapped in some sort
1109 * of Camel wrapper exception
1110 * <p/>
1111 * The strategy is to look in the exception hierarchy to find the first given cause that matches the type.
1112 * Will start from the bottom (the real cause) and walk upwards.
1113 *
1114 * @param type the exception type wanted to retrieve
1115 * @param exception the caused exception
1116 * @return the exception found (or <tt>null</tt> if not found in the exception hierarchy)
1117 */
1118 public static <T> T getException(Class<T> type, Throwable exception) {
1119 if (exception == null) {
1120 return null;
1121 }
1122
1123 // walk the hierarchy and look for it
1124 Iterator<Throwable> it = createExceptionIterator(exception);
1125 while (it.hasNext()) {
1126 Throwable e = it.next();
1127 if (type.isInstance(e)) {
1128 return type.cast(e);
1129 }
1130 }
1131
1132 // not found
1133 return null;
1134 }
1135
1136 /**
1137 * Creates a {@link Scanner} for scanning the given value.
1138 *
1139 * @param exchange the current exchange
1140 * @param value the value, typically the message IN body
1141 * @return the scanner, is newer <tt>null</tt>
1142 */
1143 public static Scanner getScanner(Exchange exchange, Object value) {
1144 if (value instanceof GenericFile) {
1145 // generic file is just a wrapper for the real file so call again with the real file
1146 GenericFile<?> gf = (GenericFile<?>) value;
1147 return getScanner(exchange, gf.getFile());
1148 }
1149
1150 String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
1151
1152 Scanner scanner = null;
1153 if (value instanceof Readable) {
1154 scanner = new Scanner((Readable)value);
1155 } else if (value instanceof InputStream) {
1156 scanner = charset == null ? new Scanner((InputStream)value) : new Scanner((InputStream)value, charset);
1157 } else if (value instanceof File) {
1158 try {
1159 scanner = charset == null ? new Scanner((File)value) : new Scanner((File)value, charset);
1160 } catch (FileNotFoundException e) {
1161 throw new RuntimeCamelException(e);
1162 }
1163 } else if (value instanceof String) {
1164 scanner = new Scanner((String)value);
1165 } else if (value instanceof ReadableByteChannel) {
1166 scanner = charset == null ? new Scanner((ReadableByteChannel)value) : new Scanner((ReadableByteChannel)value, charset);
1167 }
1168
1169 if (scanner == null) {
1170 // value is not a suitable type, try to convert value to a string
1171 String text = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, value);
1172 if (text != null) {
1173 scanner = new Scanner(text);
1174 }
1175 }
1176
1177 if (scanner == null) {
1178 scanner = new Scanner("");
1179 }
1180
1181 return scanner;
1182 }
1183
1184 public static String getIdentityHashCode(Object object) {
1185 return "0x" + Integer.toHexString(System.identityHashCode(object));
1186 }
1187
1188 private static class ExceptionIterator implements Iterator<Throwable> {
1189 private List<Throwable> tree = new ArrayList<Throwable>();
1190 private Iterator<Throwable> it;
1191
1192 public ExceptionIterator(Throwable exception) {
1193 Throwable current = exception;
1194 // spool to the bottom of the caused by tree
1195 while (current != null) {
1196 tree.add(current);
1197 current = current.getCause();
1198 }
1199
1200 // reverse tree so we go from bottom to top
1201 Collections.reverse(tree);
1202 it = tree.iterator();
1203 }
1204
1205 public boolean hasNext() {
1206 return it.hasNext();
1207 }
1208
1209 public Throwable next() {
1210 return it.next();
1211 }
1212
1213 public void remove() {
1214 it.remove();
1215 }
1216 }
1217 }