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