/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelExecutionException;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Ordered;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.TypeConverter;
import org.apache.camel.WrappedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A number of useful helper methods for working with Objects
*
* @version
*/
public final class ObjectHelper {
private static final transient Logger LOG = LoggerFactory.getLogger(ObjectHelper.class);
private static final String DEFAULT_DELIMITER = ",";
@SuppressWarnings("unchecked")
private static final List> PRIMITIVE_ARRAY_TYPES = Arrays.asList(byte[].class, short[].class, int[].class, long[].class,
float[].class, double[].class, char[].class, boolean[].class);
/**
* Utility classes should not have a public constructor.
*/
private ObjectHelper() {
}
/**
* A helper method for comparing objects for equality in which it uses type coercion to coerce
* types between the left and right values. This allows you test for equality for example with
* a String and Integer type as Camel will be able to coerce the types.
*/
public static boolean typeCoerceEquals(TypeConverter converter, Object leftValue, Object rightValue) {
// sanity check
if (leftValue == null && rightValue == null) {
// they are equal
return true;
} else if (leftValue == null || rightValue == null) {
// only one of them is null so they are not equal
return false;
}
// try without type coerce
boolean answer = equal(leftValue, rightValue);
if (answer) {
return true;
}
// are they same type, if so return false as the equals returned false
if (leftValue.getClass().isInstance(rightValue)) {
return false;
}
// convert left to right
Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
answer = equal(value, rightValue);
if (answer) {
return true;
}
// convert right to left
value = converter.tryConvertTo(leftValue.getClass(), rightValue);
answer = equal(leftValue, value);
return answer;
}
/**
* A helper method for comparing objects for inequality in which it uses type coercion to coerce
* types between the left and right values. This allows you test for inequality for example with
* a String and Integer type as Camel will be able to coerce the types.
*/
public static boolean typeCoerceNotEquals(TypeConverter converter, Object leftValue, Object rightValue) {
return !typeCoerceEquals(converter, leftValue, rightValue);
}
/**
* A helper method for comparing objects ordering in which it uses type coercion to coerce
* types between the left and right values. This allows you test for ordering for example with
* a String and Integer type as Camel will be able to coerce the types.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
// if both values is numeric then compare using numeric
Long leftNum = converter.tryConvertTo(Long.class, leftValue);
Long rightNum = converter.tryConvertTo(Long.class, rightValue);
if (leftNum != null && rightNum != null) {
return leftNum.compareTo(rightNum);
}
// also try with floating point numbers
Double leftDouble = converter.tryConvertTo(Double.class, leftValue);
Double rightDouble = converter.tryConvertTo(Double.class, rightValue);
if (leftDouble != null && rightDouble != null) {
return leftDouble.compareTo(rightDouble);
}
// prefer to NOT coerce to String so use the type which is not String
// for example if we are comparing String vs Integer then prefer to coerce to Integer
// as all types can be converted to String which does not work well for comparison
// as eg "10" < 6 would return true, where as 10 < 6 will return false.
// if they are both String then it doesn't matter
if (rightValue instanceof String && (!(leftValue instanceof String))) {
// if right is String and left is not then flip order (remember to * -1 the result then)
return typeCoerceCompare(converter, rightValue, leftValue) * -1;
}
// prefer to coerce to the right hand side at first
if (rightValue instanceof Comparable) {
Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
if (value != null) {
return ((Comparable) rightValue).compareTo(value) * -1;
}
}
// then fallback to the left hand side
if (leftValue instanceof Comparable) {
Object value = converter.tryConvertTo(leftValue.getClass(), rightValue);
if (value != null) {
return ((Comparable) leftValue).compareTo(value);
}
}
// use regular compare
return compare(leftValue, rightValue);
}
/**
* A helper method for comparing objects for equality while handling nulls
*/
public static boolean equal(Object a, Object b) {
if (a == b) {
return true;
}
if (a instanceof byte[] && b instanceof byte[]) {
return equalByteArray((byte[])a, (byte[])b);
}
return a != null && b != null && a.equals(b);
}
/**
* A helper method for comparing byte arrays for equality while handling
* nulls
*/
public static boolean equalByteArray(byte[] a, byte[] b) {
if (a == b) {
return true;
}
// loop and compare each byte
if (a != null && b != null && a.length == b.length) {
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
// all bytes are equal
return true;
}
return false;
}
/**
* Returns true if the given object is equal to any of the expected value
*/
public static boolean isEqualToAny(Object object, Object... values) {
for (Object value : values) {
if (equal(object, value)) {
return true;
}
}
return false;
}
/**
* A helper method for performing an ordered comparison on the objects
* handling nulls and objects which do not handle sorting gracefully
*/
public static int compare(Object a, Object b) {
return compare(a, b, false);
}
/**
* A helper method for performing an ordered comparison on the objects
* handling nulls and objects which do not handle sorting gracefully
*
* @param a the first object
* @param b the second object
* @param ignoreCase ignore case for string comparison
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static int compare(Object a, Object b, boolean ignoreCase) {
if (a == b) {
return 0;
}
if (a == null) {
return -1;
}
if (b == null) {
return 1;
}
if (a instanceof Ordered && b instanceof Ordered) {
return ((Ordered) a).getOrder() - ((Ordered) b).getOrder();
}
if (ignoreCase && a instanceof String && b instanceof String) {
return ((String) a).compareToIgnoreCase((String) b);
}
if (a instanceof Comparable) {
Comparable comparable = (Comparable)a;
return comparable.compareTo(b);
}
int answer = a.getClass().getName().compareTo(b.getClass().getName());
if (answer == 0) {
answer = a.hashCode() - b.hashCode();
}
return answer;
}
public static Boolean toBoolean(Object value) {
if (value instanceof Boolean) {
return (Boolean)value;
}
if (value instanceof String) {
return "true".equalsIgnoreCase(value.toString()) ? Boolean.TRUE : Boolean.FALSE;
}
if (value instanceof Integer) {
return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
}
return null;
}
/**
* Asserts whether the value is notnull
*
* @param value the value to test
* @param name the key that resolved the value
* @return the passed {@code value} as is
* @throws IllegalArgumentException is thrown if assertion fails
*/
public static Object notNull(Object value, String name) {
if (value == null) {
throw new IllegalArgumentException(name + " must be specified");
}
return value;
}
/**
* Asserts whether the value is notnull
*
* @param value the value to test
* @param on additional description to indicate where this problem occurred (appended as toString())
* @param name the key that resolved the value
* @return the passed {@code value} as is
* @throws IllegalArgumentException is thrown if assertion fails
*/
public static Object notNull(Object value, String name, Object on) {
if (on == null) {
notNull(value, name);
} else if (value == null) {
throw new IllegalArgumentException(name + " must be specified on: " + on);
}
return value;
}
/**
* Asserts whether the string is not empty.
*
* @param value the string to test
* @param name the key that resolved the value
* @return the passed {@code value} as is
* @throws IllegalArgumentException is thrown if assertion fails
*/
public static String notEmpty(String value, String name) {
if (isEmpty(value)) {
throw new IllegalArgumentException(name + " must be specified and not empty");
}
return value;
}
/**
* Asserts whether the string is not empty.
*
* @param value the string to test
* @param on additional description to indicate where this problem occurred (appended as toString())
* @param name the key that resolved the value
* @return the passed {@code value} as is
* @throws IllegalArgumentException is thrown if assertion fails
*/
public static String notEmpty(String value, String name, Object on) {
if (on == null) {
notNull(value, name);
} else if (isEmpty(value)) {
throw new IllegalArgumentException(name + " must be specified and not empty on: " + on);
}
return value;
}
/**
* Tests whether the value is null or an empty string.
*
* @param value the value, if its a String it will be tested for text length as well
* @return true if empty
*/
public static boolean isEmpty(Object value) {
return !isNotEmpty(value);
}
/**
* Tests whether the value is notnull or an empty string.
*
* @param value the value, if its a String it will be tested for text length as well
* @return true if not empty
*/
public static boolean isNotEmpty(Object value) {
if (value == null) {
return false;
} else if (value instanceof String) {
String text = (String) value;
return text.trim().length() > 0;
} else {
return true;
}
}
public static String[] splitOnCharacter(String value, String needle, int count) {
String rc[] = new String[count];
rc[0] = value;
for (int i = 1; i < count; i++) {
String v = rc[i - 1];
int p = v.indexOf(needle);
if (p < 0) {
return rc;
}
rc[i - 1] = v.substring(0, p);
rc[i] = v.substring(p + 1);
}
return rc;
}
/**
* Removes any starting characters on the given text which match the given
* character
*
* @param text the string
* @param ch the initial characters to remove
* @return either the original string or the new substring
*/
public static String removeStartingCharacters(String text, char ch) {
int idx = 0;
while (text.charAt(idx) == ch) {
idx++;
}
if (idx > 0) {
return text.substring(idx);
}
return text;
}
public static String capitalize(String text) {
if (text == null) {
return null;
}
int length = text.length();
if (length == 0) {
return text;
}
String answer = text.substring(0, 1).toUpperCase(Locale.ENGLISH);
if (length > 1) {
answer += text.substring(1, length);
}
return answer;
}
public static String after(String text, String after) {
if (!text.contains(after)) {
return null;
}
return text.substring(text.indexOf(after) + after.length());
}
public static String before(String text, String before) {
if (!text.contains(before)) {
return null;
}
return text.substring(0, text.indexOf(before));
}
public static String between(String text, String after, String before) {
text = after(text, after);
if (text == null) {
return null;
}
return before(text, before);
}
/**
* Returns true if the collection contains the specified value
*/
public static boolean contains(Object collectionOrArray, Object value) {
if (collectionOrArray instanceof Collection) {
Collection> collection = (Collection>)collectionOrArray;
return collection.contains(value);
} else if (collectionOrArray instanceof String && value instanceof String) {
String str = (String)collectionOrArray;
String subStr = (String)value;
return str.contains(subStr);
} else {
Iterator