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 }