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