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.IOException;
021 import java.io.InputStream;
022 import java.lang.annotation.Annotation;
023 import java.lang.reflect.AnnotatedElement;
024 import java.lang.reflect.InvocationTargetException;
025 import java.lang.reflect.Method;
026 import java.nio.charset.Charset;
027 import java.util.ArrayList;
028 import java.util.Arrays;
029 import java.util.Collection;
030 import java.util.Collections;
031 import java.util.Iterator;
032 import java.util.List;
033
034 import org.w3c.dom.Node;
035 import org.w3c.dom.NodeList;
036
037
038 import org.apache.camel.RuntimeCamelException;
039 import org.apache.commons.logging.Log;
040 import org.apache.commons.logging.LogFactory;
041
042
043 /**
044 * A number of useful helper methods for working with Objects
045 *
046 * @version $Revision: 56884 $
047 */
048 public final class ObjectHelper {
049 private static final transient Log LOG = LogFactory.getLog(ObjectHelper.class);
050
051 /**
052 * Utility classes should not have a public constructor.
053 */
054 private ObjectHelper() {
055 }
056
057 /**
058 * @deprecated use the equal method instead. Will be removed in Camel 2.0.
059 *
060 * @see #equal(Object, Object)
061 */
062 @Deprecated
063 public static boolean equals(Object a, Object b) {
064 return equal(a, b);
065 }
066
067 /**
068 * A helper method for comparing objects for equality while handling nulls
069 */
070 public static boolean equal(Object a, Object b) {
071 if (a == b) {
072 return true;
073 }
074
075 if (a instanceof byte[] && b instanceof byte[]) {
076 return equalByteArray((byte[]) a, (byte[]) b);
077 }
078
079 return a != null && b != null && a.equals(b);
080 }
081
082 /**
083 * A helper method for comparing byte arrays for equality while handling nulls
084 */
085 public static boolean equalByteArray(byte[] a, byte[] b) {
086 if (a == b) {
087 return true;
088 }
089
090 // loop and compare each byte
091 if (a != null && b != null && a.length == b.length) {
092 for (int i = 0; i < a.length; i++) {
093 if (a[i] != b[i]) {
094 return false;
095 }
096 }
097 // all bytes are equal
098 return true;
099 }
100
101 return false;
102 }
103
104 /**
105 * Returns true if the given object is equal to any of the expected value
106 */
107 public static boolean isEqualToAny(Object object, Object... values) {
108 for (Object value : values) {
109 if (equal(object, value)) {
110 return true;
111 }
112 }
113 return false;
114 }
115
116 /**
117 * A helper method for performing an ordered comparison on the objects
118 * handling nulls and objects which do not handle sorting gracefully
119 */
120 public static int compare(Object a, Object b) {
121 if (a == b) {
122 return 0;
123 }
124 if (a == null) {
125 return -1;
126 }
127 if (b == null) {
128 return 1;
129 }
130 if (a instanceof Comparable) {
131 Comparable comparable = (Comparable)a;
132 return comparable.compareTo(b);
133 } else {
134 int answer = a.getClass().getName().compareTo(b.getClass().getName());
135 if (answer == 0) {
136 answer = a.hashCode() - b.hashCode();
137 }
138 return answer;
139 }
140 }
141
142 public static Boolean toBoolean(Object value) {
143 if (value instanceof Boolean) {
144 return (Boolean)value;
145 }
146 if (value instanceof String) {
147 return "true".equalsIgnoreCase(value.toString()) ? Boolean.TRUE : Boolean.FALSE;
148 }
149 if (value instanceof Integer) {
150 return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
151 }
152 return null;
153 }
154
155 public static void notNull(Object value, String name) {
156 if (value == null) {
157 throw new IllegalArgumentException(name + " must be specified");
158 }
159 }
160
161 public static String[] splitOnCharacter(String value, String needle, int count) {
162 String rc[] = new String[count];
163 rc[0] = value;
164 for (int i = 1; i < count; i++) {
165 String v = rc[i - 1];
166 int p = v.indexOf(needle);
167 if (p < 0) {
168 return rc;
169 }
170 rc[i - 1] = v.substring(0, p);
171 rc[i] = v.substring(p + 1);
172 }
173 return rc;
174 }
175
176 /**
177 * Removes any starting characters on the given text which match the given
178 * character
179 *
180 * @param text the string
181 * @param ch the initial characters to remove
182 * @return either the original string or the new substring
183 */
184 public static String removeStartingCharacters(String text, char ch) {
185 int idx = 0;
186 while (text.charAt(idx) == ch) {
187 idx++;
188 }
189 if (idx > 0) {
190 return text.substring(idx);
191 }
192 return text;
193 }
194
195 public static String capitalize(String text) {
196 if (text == null) {
197 return null;
198 }
199 int length = text.length();
200 if (length == 0) {
201 return text;
202 }
203 String answer = text.substring(0, 1).toUpperCase();
204 if (length > 1) {
205 answer += text.substring(1, length);
206 }
207 return answer;
208 }
209
210
211 /**
212 * Returns true if the collection contains the specified value
213 */
214 @SuppressWarnings("unchecked")
215 public static boolean contains(Object collectionOrArray, Object value) {
216 if (collectionOrArray instanceof Collection) {
217 Collection collection = (Collection)collectionOrArray;
218 return collection.contains(value);
219 } else if (collectionOrArray instanceof String && value instanceof String) {
220 String str = (String) collectionOrArray;
221 String subStr = (String) value;
222 return str.contains(subStr);
223 } else {
224 Iterator iter = createIterator(collectionOrArray);
225 while (iter.hasNext()) {
226 if (equal(value, iter.next())) {
227 return true;
228 }
229 }
230 }
231 return false;
232 }
233
234 /**
235 * Creates an iterator over the value if the value is a collection, an
236 * Object[] or a primitive type array; otherwise to simplify the caller's
237 * code, we just create a singleton collection iterator over a single value
238 */
239 @SuppressWarnings("unchecked")
240 public static Iterator createIterator(Object value) {
241 if (value == null) {
242 return Collections.EMPTY_LIST.iterator();
243 } else if (value instanceof Iterator) {
244 return (Iterator) value;
245 } else if (value instanceof Collection) {
246 Collection collection = (Collection)value;
247 return collection.iterator();
248 } else if (value.getClass().isArray()) {
249 // TODO we should handle primitive array types?
250 List<Object> list = Arrays.asList((Object[]) value);
251 return list.iterator();
252 } else if (value instanceof NodeList) {
253 // lets iterate through DOM results after performing XPaths
254 final NodeList nodeList = (NodeList) value;
255 return new Iterator<Node>() {
256 int idx = -1;
257
258 public boolean hasNext() {
259 return ++idx < nodeList.getLength();
260 }
261
262 public Node next() {
263 return nodeList.item(idx);
264 }
265
266 public void remove() {
267 throw new UnsupportedOperationException();
268 }
269 };
270 } else {
271 return Collections.singletonList(value).iterator();
272 }
273 }
274
275 /**
276 * Returns the predicate matching boolean on a {@link List} result set where
277 * if the first element is a boolean its value is used otherwise this method
278 * returns true if the collection is not empty
279 *
280 * @return <tt>true</tt> if the first element is a boolean and its value is true or
281 * if the list is non empty
282 */
283 public static boolean matches(List list) {
284 if (!list.isEmpty()) {
285 Object value = list.get(0);
286 if (value instanceof Boolean) {
287 Boolean flag = (Boolean)value;
288 return flag.booleanValue();
289 } else {
290 // lets assume non-empty results are true
291 return true;
292 }
293 }
294 return false;
295 }
296
297 public static boolean isNotNullAndNonEmpty(String text) {
298 return text != null && text.trim().length() > 0;
299 }
300
301 public static boolean isNullOrBlank(String text) {
302 return text == null || text.trim().length() <= 0;
303 }
304
305 /**
306 * A helper method to access a system property, catching any security
307 * exceptions
308 *
309 * @param name the name of the system property required
310 * @param defaultValue the default value to use if the property is not
311 * available or a security exception prevents access
312 * @return the system property value or the default value if the property is
313 * not available or security does not allow its access
314 */
315 public static String getSystemProperty(String name, String defaultValue) {
316 try {
317 return System.getProperty(name, defaultValue);
318 } catch (Exception e) {
319 if (LOG.isDebugEnabled()) {
320 LOG.debug("Caught security exception accessing system property: " + name + ". Reason: " + e,
321 e);
322 }
323 return defaultValue;
324 }
325 }
326
327 /**
328 * A helper method to access a boolean system property, catching any security
329 * exceptions
330 *
331 * @param name the name of the system property required
332 * @param defaultValue the default value to use if the property is not
333 * available or a security exception prevents access
334 * @return the boolean representation of the system property value
335 * or the default value if the property is not available or
336 * security does not allow its access
337 */
338 public static boolean getSystemProperty(String name, Boolean defaultValue) {
339 String result = getSystemProperty(name, defaultValue.toString());
340 return Boolean.parseBoolean(result);
341 }
342
343 /**
344 * Returns the type name of the given type or null if the type variable is
345 * null
346 */
347 public static String name(Class type) {
348 return type != null ? type.getName() : null;
349 }
350
351 /**
352 * Returns the type name of the given value
353 */
354 public static String className(Object value) {
355 return name(value != null ? value.getClass() : null);
356 }
357
358 /**
359 * Returns the canonical type name of the given value
360 */
361 public static String classCanonicalName(Object value) {
362 if (value != null) {
363 return value.getClass().getCanonicalName();
364 } else {
365 return null;
366 }
367 }
368
369 /**
370 * Attempts to load the given class name using the thread context class
371 * loader or the class loader used to load this class
372 *
373 * @param name the name of the class to load
374 * @return the class or null if it could not be loaded
375 */
376 public static Class<?> loadClass(String name) {
377 return loadClass(name, ObjectHelper.class.getClassLoader());
378 }
379
380 /**
381 * Attempts to load the given class name using the thread context class
382 * loader or the given class loader
383 *
384 * @param name the name of the class to load
385 * @param loader the class loader to use after the thread context class
386 * loader
387 * @return the class or null if it could not be loaded
388 */
389 public static Class<?> loadClass(String name, ClassLoader loader) {
390 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
391 if (contextClassLoader != null) {
392 try {
393 return contextClassLoader.loadClass(name);
394 } catch (ClassNotFoundException e) {
395 try {
396 return loader.loadClass(name);
397 } catch (ClassNotFoundException e1) {
398 LOG.debug("Could not find class: " + name + ". Reason: " + e);
399 }
400 }
401 }
402 return null;
403 }
404
405 /**
406 * Attempts to load the given resource as a stream using the thread context class
407 * loader or the class loader used to load this class
408 *
409 * @param name the name of the resource to load
410 * @return the stream or null if it could not be loaded
411 */
412 public static InputStream loadResourceAsStream(String name) {
413 InputStream in = null;
414
415 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
416 if (contextClassLoader != null) {
417 in = contextClassLoader.getResourceAsStream(name);
418 }
419 if (in == null) {
420 in = ObjectHelper.class.getClassLoader().getResourceAsStream(name);
421 }
422
423 return in;
424 }
425
426 /**
427 * A helper method to invoke a method via reflection and wrap any exceptions
428 * as {@link RuntimeCamelException} instances
429 *
430 * @param method the method to invoke
431 * @param instance the object instance (or null for static methods)
432 * @param parameters the parameters to the method
433 * @return the result of the method invocation
434 */
435 public static Object invokeMethod(Method method, Object instance, Object... parameters) {
436 try {
437 return method.invoke(instance, parameters);
438 } catch (IllegalAccessException e) {
439 throw new RuntimeCamelException(e);
440 } catch (InvocationTargetException e) {
441 throw new RuntimeCamelException(e.getCause());
442 }
443 }
444
445 /**
446 * Returns a list of methods which are annotated with the given annotation
447 *
448 * @param type the type to reflect on
449 * @param annotationType the annotation type
450 * @return a list of the methods found
451 */
452 public static List<Method> findMethodsWithAnnotation(Class<?> type,
453 Class<? extends Annotation> annotationType) {
454 return findMethodsWithAnnotation(type, annotationType, false);
455 }
456
457 /**
458 * Returns a list of methods which are annotated with the given annotation
459 *
460 * @param type the type to reflect on
461 * @param annotationType the annotation type
462 * @param checkMetaAnnotations check for meta annotations
463 * @return a list of the methods found
464 */
465 public static List<Method> findMethodsWithAnnotation(Class<?> type,
466 Class<? extends Annotation> annotationType, boolean checkMetaAnnotations) {
467 List<Method> answer = new ArrayList<Method>();
468 do {
469 Method[] methods = type.getDeclaredMethods();
470 for (Method method : methods) {
471 if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
472 answer.add(method);
473 }
474 }
475 type = type.getSuperclass();
476 } while (type != null);
477 return answer;
478 }
479
480 /**
481 * Checks if a Class or Method are annotated with the given annotation
482 *
483 * @param elem the Class or Method to reflect on
484 * @param annotationType the annotation type
485 * @param checkMetaAnnotations check for meta annotations
486 * @return true if annotations is present
487 */
488 public static boolean hasAnnotation(AnnotatedElement elem,
489 Class<? extends Annotation> annotationType, boolean checkMetaAnnotations) {
490 if (elem.isAnnotationPresent(annotationType)) {
491 return true;
492 }
493 if (checkMetaAnnotations) {
494 for (Annotation a : elem.getAnnotations()) {
495 for (Annotation meta : a.annotationType().getAnnotations()) {
496 if (meta.annotationType().getName().equals(annotationType.getName())) {
497 return true;
498 }
499 }
500 }
501 }
502 return false;
503 }
504
505 /**
506 * Turns the given object arrays into a meaningful string
507 *
508 * @param objects an array of objects or null
509 * @return a meaningful string
510 */
511 public static String asString(Object[] objects) {
512 if (objects == null) {
513 return "null";
514 } else {
515 StringBuffer buffer = new StringBuffer("{");
516 int counter = 0;
517 for (Object object : objects) {
518 if (counter++ > 0) {
519 buffer.append(", ");
520 }
521 String text = (object == null) ? "null" : object.toString();
522 buffer.append(text);
523 }
524 buffer.append("}");
525 return buffer.toString();
526 }
527 }
528
529 /**
530 * Returns true if a class is assignable from another class like the
531 * {@link Class#isAssignableFrom(Class)} method but which also includes
532 * coercion between primitive types to deal with Java 5 primitive type
533 * wrapping
534 */
535 public static boolean isAssignableFrom(Class a, Class b) {
536 a = convertPrimitiveTypeToWrapperType(a);
537 b = convertPrimitiveTypeToWrapperType(b);
538 return a.isAssignableFrom(b);
539 }
540
541 /**
542 * Converts primitive types such as int to its wrapper type like
543 * {@link Integer}
544 */
545 public static Class convertPrimitiveTypeToWrapperType(Class type) {
546 Class rc = type;
547 if (type.isPrimitive()) {
548 if (type == int.class) {
549 rc = Integer.class;
550 } else if (type == long.class) {
551 rc = Long.class;
552 } else if (type == double.class) {
553 rc = Double.class;
554 } else if (type == float.class) {
555 rc = Float.class;
556 } else if (type == short.class) {
557 rc = Short.class;
558 } else if (type == byte.class) {
559 rc = Byte.class;
560 // TODO: Why is boolean disabled
561 /*
562 } else if (type == boolean.class) {
563 rc = Boolean.class;
564 */
565 }
566 }
567 return rc;
568 }
569
570 /**
571 * Helper method to return the default character set name
572 */
573 public static String getDefaultCharacterSet() {
574 return Charset.defaultCharset().name();
575 }
576
577 /**
578 * Returns the Java Bean property name of the given method, if it is a setter
579 */
580 public static String getPropertyName(Method method) {
581 String propertyName = method.getName();
582 if (propertyName.startsWith("set") && method.getParameterTypes().length == 1) {
583 propertyName = propertyName.substring(3, 4).toLowerCase() + propertyName.substring(4);
584 }
585 return propertyName;
586 }
587
588 /**
589 * Returns true if the given collection of annotations matches the given type
590 */
591 public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
592 for (Annotation annotation : annotations) {
593 if (type.isInstance(annotation)) {
594 return true;
595 }
596 }
597 return false;
598 }
599
600 /**
601 * Closes the given resource if it is available, logging any closing exceptions to the given log
602 *
603 * @param closeable the object to close
604 * @param name the name of the resource
605 * @param log the log to use when reporting closure warnings
606 */
607 public static void close(Closeable closeable, String name, Log log) {
608 if (closeable != null) {
609 try {
610 closeable.close();
611 } catch (IOException e) {
612 if (log != null) {
613 log.warn("Could not close: " + name + ". Reason: " + e, e);
614 }
615 }
616 }
617 }
618
619 /**
620 * Converts the given value to the required type or throw a meaningful exception
621 */
622 public static <T> T cast(Class<T> toType, Object value) {
623 if (toType == boolean.class) {
624 return (T)cast(Boolean.class, value);
625 } else if (toType.isPrimitive()) {
626 Class newType = convertPrimitiveTypeToWrapperType(toType);
627 if (newType != toType) {
628 return (T)cast(newType, value);
629 }
630 }
631 try {
632 return toType.cast(value);
633 } catch (ClassCastException e) {
634 throw new IllegalArgumentException("Failed to convert: " + value + " to type: "
635 + toType.getName() + " due to: " + e, e);
636 }
637 }
638
639 /**
640 * A helper method to create a new instance of a type using the default constructor arguments.
641 */
642 public static <T> T newInstance(Class<T> type) {
643 try {
644 return type.newInstance();
645 } catch (InstantiationException e) {
646 throw new RuntimeCamelException(e);
647 } catch (IllegalAccessException e) {
648 throw new RuntimeCamelException(e);
649 }
650 }
651
652 /**
653 * A helper method to create a new instance of a type using the default constructor arguments.
654 */
655 public static <T> T newInstance(Class<?> actualType, Class<T> expectedType) {
656 try {
657 Object value = actualType.newInstance();
658 return cast(expectedType, value);
659 } catch (InstantiationException e) {
660 throw new RuntimeCamelException();
661 } catch (IllegalAccessException e) {
662 throw new RuntimeCamelException(e);
663 }
664 }
665
666 /**
667 * Returns true if the given name is a valid java identifier
668 */
669 public static boolean isJavaIdentifier(String name) {
670 if (name == null) {
671 return false;
672 }
673 int size = name.length();
674 if (size < 1) {
675 return false;
676 }
677 if (Character.isJavaIdentifierStart(name.charAt(0))) {
678 for (int i = 1; i < size; i++) {
679 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
680 return false;
681 }
682 }
683 return true;
684 }
685 return false;
686 }
687
688 /**
689 * Returns the type of the given object or null if the value is null
690 */
691 public static Object type(Object bean) {
692 return bean != null ? bean.getClass() : null;
693 }
694
695 /**
696 * Evaluate the value as a predicate which attempts to convert the value to
697 * a boolean otherwise true is returned if the value is not null
698 */
699 public static boolean evaluateValuePredicate(Object value) {
700 if (value instanceof Boolean) {
701 Boolean aBoolean = (Boolean)value;
702 return aBoolean.booleanValue();
703 } else if (value instanceof String) {
704 if ("true".equals(value)) {
705 return true;
706 } else if ("false".equals(value)) {
707 return false;
708 }
709 }
710 return value != null;
711 }
712
713 /**
714 * Wraps the caused exception in a {@link RuntimeCamelException} if its not already such an exception.
715 *
716 * @param e the caused exception
717 * @return the wrapper exception
718 */
719 public static RuntimeCamelException wrapRuntimeCamelException(Throwable e) {
720 if (e instanceof RuntimeCamelException) {
721 // don't double wrap if already a RuntimeCamelException
722 return (RuntimeCamelException) e;
723 } else {
724 return new RuntimeCamelException(e);
725 }
726 }
727
728 }