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