
org.ow2.bonita.util.Misc Maven / Gradle / Ivy
/**
* Copyright (C) 2007 Bull S. A. S.
* Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation
* version 2.1 of the License.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/
package org.ow2.bonita.util;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.TypeVariable;
import java.net.URL;
import java.security.Permission;
import java.security.Permissions;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Date;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MalformedObjectNameException;
import javax.management.ReflectionException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.xml.sax.InputSource;
/**
* @author Marc Blachon, Guillaume Porcher, Charles Souillard, Miguel Valdes,
* Pierre Vigneras
*/
public abstract class Misc {
// public interface Cloner {
// T newClone(T t);
// }
//
// public interface Factory {
// T newInstance(Object... objects);
// }
private static final Logger LOG = Logger.getLogger(Misc.class.getName());
private static Object formatterLock = new Object();
private Misc() {
}
/**
* A RANDOM instance. Prevent creation of many instances.
*/
public static final Random RANDOM = new Random();
/**
* The line separator as defined by the property line.separator
.
*/
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* The file that represents the temporary directory. Usually, it is /tmp on
* Unix platform and C:/TMP on MS Windows.
*/
public static final File TMP_DIR = new File(System.getProperty("java.io.tmpdir"));
// Time formatter
private static final DateFormat DELAY_FORMATTER = new SimpleDateFormat("HH:mm:ss.SSS");
static {
Misc.DELAY_FORMATTER.setTimeZone(TimeZone.getTimeZone("GMT"));
}
/**
* Number of milleseconds in a given day
*/
public static final long DAY = 1000 * 60 * 60 * 24; // one day
private static final AtomicLong SEQUENCE_NUMBER = new AtomicLong(0);
/**
* Generate a unique identifier prefixed by the given String.
*
* @param prefix the prefix String
* @return an UUID String prefixed by prefix
* @see UUID
*/
public static String getUniqueId(final String prefix) {
return prefix + java.util.UUID.randomUUID();
}
/**
* Generates a human readable id prefixed by the given String.
*
* The generated identifier is unique only for the duration of the existence
* of this class.
*
* @param prefix the string prefiy
* @return a human readable id prefixed by prefix
.
*/
public static String getHumanReadableId(final String prefix) {
return prefix + Misc.getHumanReadableId();
}
/**
* Generates a human readable id as a long.
*
* The generated identifier is unique only for the duration of the existence
* of this class.
*
* @return a unique id as a long.
*/
public static long getHumanReadableId() {
return Misc.SEQUENCE_NUMBER.getAndIncrement();
}
/**
* Returns a random number between min and max value
*
* @param min a positive integer
* @param max a positive integer
* @return a random number between min and max value
* @throws IllegalArgumentException if min >= max
*/
public static int random(final int min, final int max) {
if (min >= max) {
throw new IllegalArgumentException("Pre-condition failed: min(" + min + ") < max(" + max + ")");
}
final int n = max - min;
if (n < 0) {
throw new IllegalArgumentException("Overflow due to big parameters (min: " + min + ", max: " + max + "), sorry!");
}
return Misc.RANDOM.nextInt(max - min) + min;
}
/**
* Generate a RANDOM String of the given size.
*
* @param size the size of the generated string
* @return the RANDOM String
*/
public static String getRandomString(final int size) {
final char[] s = new char[size];
int c = 'A';
int r1 = 0;
for (int i = 0; i < size; i++) {
r1 = (int) (Math.random() * 3);
switch (r1) {
case 0:
c = '0' + (int) (Math.random() * 10);
break;
case 1:
c = 'a' + (int) (Math.random() * 26);
break;
case 2:
c = 'A' + (int) (Math.random() * 26);
break;
}
s[i] = (char) c;
}
return new String(s);
}
/**
*
* Format a delay.
*
*
*
* This method returns a human readable string for delay such as the one used
* in benchmarks.
*
* This method is thread safe.
*
*
* @param delay a long
value
* @return a String
value
*/
public static final String formatDelay(final long delay) {
// Don't use Math.abs() here !!
// Math.abs(Long.MIN_VALUE) returns Long.MIN_VALUE !
if (delay == Long.MAX_VALUE) {
return "INFINITY";
}
if (delay == Long.MIN_VALUE) {
return "-INFINITY";
}
if (Math.abs(delay) >= Misc.DAY) {
final long days = delay / Misc.DAY;
return days + " day" + ((days > 1) ? "s" : "");
}
// Formatter are not thread safe. See Sun Bugs #6231579 and Sun Bug
// #6178997.
synchronized (Misc.formatterLock) {
return (delay < 0) ? "-" + Misc.DELAY_FORMATTER.format(new Date(-delay)) : Misc.DELAY_FORMATTER.format(new Date(delay));
}
}
/**
*
* Equivalent to {@link #formatDelay(long)}.
*
*
* @param delay a double
value
* @return a String
value
*/
public static final String formatDelay(final double delay) {
return Misc.formatDelay((long) delay);
}
/**
*
* Return the list of Class
objects representing every types a
* given class implements.
*
*
* @param type a Class
value
* @return a Set
value
*/
public static Set> findAllTypes(final Class< ? > type) {
final Set> superTypes = Misc.findAllSuperTypes(type);
final Set> result = new HashSet>(superTypes);
for (final Class< ? > i : superTypes) {
result.addAll(Misc.findAllInterfaces(i));
}
result.addAll(Misc.findAllInterfaces(type));
return result;
}
/**
* Return the generic name of a given class.
*
* For example, given java.util.Map.class, it returns string
* java.util.Map
*
* @param clazz the class
* @return the generic name of a given class.
*/
public static String getGenericFullName(final Class< ? > clazz) {
final StringBuilder sb = new StringBuilder(clazz.getCanonicalName());
final TypeVariable< ? >[] types = clazz.getTypeParameters();
// This algorithm is weak! It only works for basic type. It will not work
// for type such as: Type< A > or Type extends B> ...
// Please fix it!
// TODO: Fix.
if (types.length != 0) {
sb.append('<');
for (final TypeVariable< ? > type : types) {
sb.append(type.getName());
sb.append(',');
}
sb.replace(sb.length() - 1, sb.length(), ">");
}
return new String(sb);
}
/**
*
* Return the list of Class
objects representing all super type a
* given class implements.
*
*
* @param type a Class
value
* @return a Set
value
*/
public static Set> findAllSuperTypes(final Class< ? > type) {
final Set> classes = new HashSet>();
for (Class< ? > c = type; c != null; c = c.getSuperclass()) {
classes.add(c);
}
return classes;
}
/**
*
* Return the list of Class
objects representing all interfaces a
* given class implements.
*
*
* @param type a Class
value
* @return a List
value
*/
public static Set> findAllInterfaces(final Class< ? > type) {
final Set> classes = new HashSet>();
final Class< ? >[] interfaces = type.getInterfaces();
for (final Class< ? > i : interfaces) {
classes.add(i);
classes.addAll(Misc.findAllInterfaces(i));
}
final Class< ? > superClass = type.getSuperclass();
if (superClass != null) {
classes.addAll(Misc.findAllInterfaces(superClass));
}
return classes;
}
/**
*
* Return the Class[]
array representing the types a given method
* take as parameters.
*
*
* @param subClasses the classes which are to be subclasses of parameters
* @param classToTest the class which declares the given method
* @param methodName the method name
*
* @return the formal parameters class array
* @throws NoSuchMethodException if a method cannot be found.
*/
@SuppressWarnings("unchecked")
public static Class< ? >[] findMethodClassArgs(final Class< ? >[] subClasses, final Class< ? > classToTest, final String methodName)
throws NoSuchMethodException {
final Set>[] classesList = new Set[subClasses.length];
for (int i = 0; i < classesList.length; i++) {
classesList[i] = Misc.findAllTypes(subClasses[i]);
}
final Method[] methods = classToTest.getDeclaredMethods();
for (int i = methods.length - 1; i >= 0; i--) {
// Same method name?
if (!methods[i].getName().equals(methodName)) {
continue;
}
// Same number of arguments?
final Class< ? >[] formal = methods[i].getParameterTypes();
if (formal.length != subClasses.length) {
continue;
}
if (Misc.checkFormal(formal, classesList)) {
return formal;
}
}
throw new NoSuchMethodException("Can't find formal " + " parameters for specified " + "arguments: " + "subClasses: "
+ Misc.componentsToString(subClasses, false) + ", classToTest: " + classToTest + ", methodName: " + methodName);
}
/**
*
* Return the Class[]
array representing the types a constructor
* take as parameters.
*
*
* @param subClasses the classes which are to be subclasses of parameters
* @param classToTest the class which declares the constructor
*
* @return the formal parameters class array
* @throws NoSuchMethodException if a constructor cannot be found
*/
@SuppressWarnings("unchecked")
public static Class< ? >[] findConstructorClassArgs(final Class< ? >[] subClasses, final Class< ? > classToTest) throws NoSuchMethodException {
final Set>[] classesList = new Set[subClasses.length];
for (int i = 0; i < classesList.length; i++) {
classesList[i] = Misc.findAllTypes(subClasses[i]);
}
final Constructor< ? >[] constructors = classToTest.getDeclaredConstructors();
for (int i = constructors.length - 1; i >= 0; i--) {
// Same number of arguments?
final Class< ? >[] formal = constructors[i].getParameterTypes();
if (formal.length != subClasses.length) {
continue;
}
if (Misc.checkFormal(formal, classesList)) {
return formal;
}
}
throw new NoSuchMethodException("Can't find formal " + "parameters for specified " + "arguments: " + "subClasses: "
+ Misc.componentsToString(subClasses, false) + ", classToTest: " + classToTest);
}
private static boolean checkFormal(final Class< ? >[] formal, final Set>[] types) {
for (int i = 0; i < formal.length; i++) {
final Iterator> iterator = types[i].iterator();
boolean found = false;
while (iterator.hasNext()) {
final Class< ? > type = iterator.next();
if (type.equals(formal[i])) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
/**
*
* Return an "identity string" for a given object.
*
*
*
* The string returned is:
*
* "null" if o == null
* - Otherwize,
*
o.getClass().getName() + "#" + System.identityHashCode(o)
*
*
*
* @param o the object to return the identity string of
* @return the identity string as defined above
*/
public static String identityToString(final Object o) {
if (o == null) {
return "null";
}
return o.getClass().getName() + "#" + System.identityHashCode(o);
}
/**
*
* Smart toString() implementation of arrays.
*
*
* @param args the array to return a smart string of.
* @return the smart string of the given array.
*/
public static String componentsToString(final Object[] args, final boolean deepToString) {
if (args == null) {
return "null";
}
Class< ? > componentType = args.getClass().getComponentType();
final StringBuilder string = new StringBuilder(componentType.getName());
string.append("[");
final int length = args.length;
if (length != 0) {
final int max = length - 1;
for (int i = 0; i < max; i++) {
if (args[i] == null) {
string.append("null");
} else if (args[i].getClass().isArray()) {
componentType = args[i].getClass().getComponentType();
if (componentType.isPrimitive()) {
string.append(Misc.primitiveComponentsToString(args[i]));
} else {
string.append(Misc.componentsToString((Object[]) args[i], deepToString));
}
} else {
string.append(deepToString ? Misc.deepToString(args[i]) : args[i].toString());
}
string.append("; ");
}
if (args[max] == null) {
string.append("null");
} else if (args[max].getClass().isArray()) {
componentType = args[max].getClass().getComponentType();
if (componentType.isPrimitive()) {
string.append(Misc.primitiveComponentsToString(args[max]));
} else {
string.append(Misc.componentsToString((Object[]) args[max], deepToString));
}
} else {
string.append(deepToString ? Misc.deepToString(args[max]) : args[max].toString());
}
}
string.append("]");
return new String(string);
}
/**
*
* Smart toString() implementation of an array of primitive types.
*
*
* @param array the array to return a smart string of.
* @return the smart string of the given array.
*/
public static String primitiveComponentsToString(final Object array) {
if (array == null) {
return "null";
}
final Class< ? > c = array.getClass();
if (!c.isArray() || !c.getComponentType().isPrimitive()) {
throw new IllegalArgumentException("array must be an " + "array of primitive" + " component");
}
final StringBuilder string = new StringBuilder(c.getComponentType().getName());
string.append("[");
final int length = Array.getLength(array);
if (length != 0) {
final int max = length - 1;
for (int i = 0; i < max; i++) {
string.append(Array.get(array, i));
string.append("; ");
}
string.append(Array.get(array, max));
}
string.append("]");
return new String(string);
}
/**
* Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new
* FileInputStream(file))};
*
* @param file the file to read
* @return the whole content of the file in a single String.
* @throws IOException If an I/O exception occured
*/
public static byte[] getAllContentFrom(final File file) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(file);
return Misc.getAllContentFrom(in);
} finally {
Misc.close(in);
}
}
/**
* Equivalent to {@link #getAllContentFrom(InputStream)
* getAllContentFrom(source.getByteStream(source))};
*
* @param file the file to read
* @return the whole content of the file in a single String.
* @throws IOException If an I/O exception occured
*/
public static byte[] getAllContentFrom(final InputSource source) throws IOException {
InputStream in = null;
try {
in = source.getByteStream();
return Misc.getAllContentFrom(in);
} finally {
Misc.close(in);
}
}
/**
* Return the whole underlying stream content into a single String.
*
* Warning: the whole content of stream will be kept in memory!! Use with
* care!
*
* @param in the stream to read
* @return the whole content of the stream in a single String.
* @throws IOException if an I/O exception occured
*/
public static byte[] getAllContentFrom(final URL url) throws IOException {
final InputStream in = url.openStream();
return Misc.getAllContentFrom(in);
}
/**
* Return the whole underlying stream content into a single String.
*
* Warning: the whole content of stream will be kept in memory!! Use with
* care!
*
* @param in the stream to read
* @return the whole content of the stream in a single String.
* @throws IOException if an I/O exception occured
*/
public static byte[] getAllContentFrom(final InputStream in) throws IOException {
final BufferedInputStream bis = new BufferedInputStream(in);
final ByteArrayOutputStream result = new ByteArrayOutputStream();
int c;
while ((c = bis.read()) != -1) {
result.write(c);
}
return result.toByteArray();
}
/**
* Equivalent to {@link #reflectClose(closeable)}
*/
public static Exception close(final Closeable closeable) {
return Misc.reflectClose(closeable);
}
/**
* Equivalent to {@link #reflectClose(encoder)
*/
public static Exception close(final XMLEncoder encoder) {
return Misc.reflectClose(encoder);
}
/**
* Equivalent to {@link #reflectClose(decoder)
*/
public static Exception close(final XMLDecoder decoder) {
return Misc.reflectClose(decoder);
}
/**
* Invoke the close() method on the given object.
*
* This method uses the reflection API to find a close() method with no
* arguments. Any exception thrown (including NoSuchMethodException) will be
* both logged using {@link #LOG} and returned. If the parameter is null,
* nothing is done and null is returned.
*
* @param o the object to call the close() method on.
* @return the exception thrown if any, null otherwise.
*/
public static Exception reflectClose(final Object o) {
if (o == null) {
return null;
}
try {
final Method m = o.getClass().getMethod("close", new Class[0]);
m.invoke(o, new Object[0]);
} catch (final Exception e) {
final StringBuilder sb = new StringBuilder("Exception thrown during close() on: ");
sb.append(o.toString());
sb.append(Misc.LINE_SEPARATOR);
sb.append("Exception message is: ");
sb.append(e.getMessage());
sb.append(Misc.LINE_SEPARATOR);
sb.append(Misc.getStackTraceFrom(e));
Misc.LOG.warning(sb.toString());
return e;
}
return null;
}
/**
* @param permissions
* @return
*/
public static int getPermissionsSize(final Permissions permissions) {
int size = 0;
final Enumeration p = permissions.elements();
while (p.hasMoreElements()) {
size++;
}
return size;
}
/**
* Return a proxy implementing all the interfaces specified that forward
* method invocations to the specified MBean.
*
* @param
* @param mbeanInterface the interface the proxy should implement (the MBean
* should obviously also implement that interface).
* @param jmxServiceUrl the JMX service URL
* @param jmxObjectName the name the MBean has been registered to
* @return a proxy implementing the specified interface and that forward
* method invocations to the specified MBean.
*
* @throws IOException for any IO problem
* @throws MalformedObjectNameException for any JMX Naming problem
* @throws MBeanException for any MBean problem
* @throws ReflectionException for any problem related to reflection
*/
@SuppressWarnings("unchecked")
public static T getMBeanProxy(final Class mbeanInterface, final String jmxServiceUrl, final String jmxObjectName) throws IOException,
MalformedObjectNameException, InstanceNotFoundException, MBeanException, ReflectionException {
return (T) Proxy.newProxyInstance(Misc.class.getClassLoader(), new Class[] {
mbeanInterface
}, new MBeanInvocationHandler(jmxServiceUrl, jmxObjectName));
}
/**
* Return a proxy that forward void-method invocations to
* each object specified in the list elements
.
*
* The invocation order follows the given list order.
*
* @param the interface type of the returned proxy and of each elements.
* @param elements the elements to forward method invocations to
* @return the forwarding chaining proxy
* @see Chainer
* @see InvocationHandler
* @see Proxy
*/
@SuppressWarnings("unchecked")
public static T getChainOf(final List elements) {
Misc.checkArgsNotNull(elements);
final Chainer chain = new Chainer();
final Set> classes = new HashSet>();
final Set> initial = Misc.findAllInterfaces(elements.get(0).getClass());
classes.addAll(initial);
for (final T element : elements) {
chain.add(element);
// We can't find the generic type T at runtime.
// But this is required by Proxy.newProxyInstance().
// So, we find the common interfaces implemented by all elements.
final Set> interfaces = Misc.findAllInterfaces(element.getClass());
classes.retainAll(interfaces);
}
if (classes.size() == 0) {
throw new IllegalArgumentException("Can't find a common interface between classes of elements: " + elements);
}
return (T) Proxy.newProxyInstance(Misc.class.getClassLoader(), classes.toArray(new Class[classes.size()]), chain);
}
/**
* Return a proxy that log method invocations through the provided logger.
*
* @param the target object type
* @param target the target object method invocations should be forwarded to
* @param logger the logger to use for logging
* @return a proxy that log method invocations through the provided logger.
* @see LoggingInvocationHandler
* @see InvocationHandler
* @see Proxy
*/
@SuppressWarnings("unchecked")
public static T getLoggerProxyFor(final T target, final Logger logger) {
final Set> classes = Misc.findAllInterfaces(target.getClass());
return (T) Proxy.newProxyInstance(Misc.class.getClassLoader(), classes.toArray(new Class[classes.size()]), new LoggingInvocationHandler(target, logger));
}
/**
* Represents null value returned by {@link Misc#findNull(Object...)}.
*
* @see Misc#findNull(Object...)
* @author Pierre Vigneras
* @date Feb 7, 2008
*/
public static class NullCheckResult {
private final int size;
private final BitSet bitSet;
NullCheckResult(final BitSet bitSet, final int size) {
this.bitSet = bitSet;
this.size = size;
}
/**
* Returns true if some parameters given to {@link Misc#findNull(Object...)}
* were null.
*
* @return true if some parameters given to {@link Misc#findNull(Object...)}
* were null.
* @see Misc#findNull(Object...)
*/
public boolean hasNull() {
return this.bitSet.cardinality() != 0;
}
/**
* Returns the number of parameters given to
* {@link Misc#findNull(Object...)}
*
* @return the number of parameters given to
* {@link Misc#findNull(Object...)}
* @see Misc#findNull(Object...)
*/
public int getSize() {
return this.size;
}
/**
* Returns true if the i th parameter given to
* {@link Misc#findNull(Object...)} was null.
*
* @param i the rank of the parameter given to
* {@link Misc#findNull(Object...)}.
* @return true if the i th parameter given to
* {@link Misc#findNull(Object...)} was null.
*/
public boolean isNull(final int i) {
return this.bitSet.get(i);
}
}
/**
* Find null parameters in the given list.
*
* This method returns a {@link NullCheckResult}.
*
*
* @param params the parameters to check
* @return a {@link NullCheckResult} representing null parameters.
* @see NullCheckResult
*/
public static NullCheckResult findNull(final Object... params) {
if (params == null) {
final BitSet bitSet = new BitSet(1);
bitSet.set(0);
return new NullCheckResult(bitSet, 1);
}
final BitSet bitSet = new BitSet(params.length);
for (int i = 0; i < params.length; i++) {
if (params[i] == null) {
bitSet.set(i, true);
} else {
bitSet.set(i, false);
}
}
return new NullCheckResult(bitSet, params.length);
}
/**
* Check that the given parameters are not null.
*
* This method should only be used to check that some parameters given to a
* given method are not null. The exception message tries its best to produce
* a helpful message by scanning the stack trace.
*
* @param params the parameters to check
* @throws an IllegalArgumentException if at least one of the parameters is
* null
*/
public static void checkArgsNotNull(final Object... params) {
Misc.checkArgsNotNull(1, params);
}
/**
* Check that the given parameters are not null.
*
* This method should only be used to check that some parameters given to a
* given method are not null. The exception message tries its best to produce
* a helpful message by scanning the stack trace.
*
* @param offset the offset to use in the stack trace to produce error message
* @param params the parameters to check
* @throws an IllegalArgumentException if at least one of the parameters is
* null
*/
public static void checkArgsNotNull(final int offset, final Object... params) {
final NullCheckResult result = Misc.findNull(params);
if (result.hasNull()) {
// Guess the signature of the caller
final StackTraceElement callerSTE = Misc.getCaller(offset + 1);
final String className = callerSTE.getClassName();
final String methodName = callerSTE.getMethodName();
final StringBuilder sb = new StringBuilder("Some parameters are null in " + className + "." + methodName + "(): ");
for (int i = 0; i < result.getSize(); i++) {
if (result.isNull(i)) {
sb.append("null");
} else {
sb.append(params[i].getClass().getName());
}
if (i < result.getSize() - 1) {
sb.append(", ");
}
}
throw new IllegalArgumentException(sb.toString());
}
}
/**
* Return the StackTraceElement at the given offset from this method
* invocation.
*
* @param offset
* @return a StackTraceElement
*/
public static StackTraceElement getCaller(final int offset) {
final StackTraceElement[] stes = Thread.currentThread().getStackTrace();
StackTraceElement callerSTE = null;
for (int i = 0; i < stes.length - offset - 1; i++) {
if (stes[i].getClassName().equals(Misc.class.getName()) && stes[i].getMethodName().equals("getCaller")) {
callerSTE = stes[i + 1 + offset];
break;
}
}
Misc.badStateIfNull(callerSTE, "Ouch! Can't get the stack trace back to the caller of this method!");
return callerSTE;
}
/**
* Return strings mapped to null values in a given @{link
* {@link NullCheckResult}. .
*
* If the returned @{link List} of String is called l
then, it
* verifies: l.contains(names[i])
if and only if
* nullCheckResult.isNull(i)
returns true
.
*
* Note that the number of String names given should be of the same size that
* the one used to get the given {@link NullCheckResult} using
* {@link #findNull(Object...)}. An {@link IllegalArgumentException} is thrown
* otherwise.
*
* @param nullCheckResult the result as returned by
* {@link #findNull(Object...)}
* @param names the strings that should be mapped to null values
* @return a List of string mapped to the given {@link NullCheckResult}.
* @throws IllegalArgumentException if the number of given names is different
* that {@link NullCheckResult#size()}
*
* @see #findNull(Object...)
* @see NullCheckResult
*/
public static List getStringFrom(final NullCheckResult nullCheckResult, final String... names) {
final int n = names.length;
if (nullCheckResult.getSize() != n) {
throw new IllegalArgumentException("Number of string names is different " + "from the arguments used to get the nullCheckResult: " + n + " != "
+ nullCheckResult.getSize());
}
final List list = new ArrayList();
if (!nullCheckResult.hasNull()) {
return list;
}
for (int i = 0; i < n; i++) {
if (nullCheckResult.isNull(i)) {
list.add(names[i]);
}
}
return list;
}
/**
* This method throw an IllegalStateException if the given parameter is null
*
* @param valueToCheck the value to check
* @param msg the message for the thrown exception
*
* @see IllegalStateException
*/
public static void badStateIfNull(final Object valueToCheck, final String msg) {
Misc.badStateIfTrue(valueToCheck == null, msg);
}
/**
* This method throw an IllegalStateException if the given parameter is not
* null
*
* @param valueToCheck the value to check
* @param msg the message for the thrown exception
*
* @see IllegalStateException
*/
public static void badStateIfNotNull(final Object valueToCheck, final String msg) {
Misc.badStateIfTrue(valueToCheck != null, msg);
}
/**
* This method throw an IllegalStateException if the given parameter is true
*
* @param valueToCheck the value to check
* @param msg the message for the thrown exception
*
* @see IllegalStateException
*/
public static void badStateIfTrue(final boolean valueToCheck, final String msg) {
if (valueToCheck) {
throw new IllegalStateException(msg);
}
}
/**
* This method throw an IllegalStateException if the given parameter is false
*
* @param valueToCheck the value to check
* @param msg the message for the thrown exception
*
* @see IllegalStateException
*/
public static void badStateIfFalse(final boolean valueToCheck, final String msg) {
Misc.badStateIfTrue(!valueToCheck, msg);
}
/**
* This method throw an IllegalStateException if the given parameters are
* equals (using {@link Object#equals(Object)}
*
* @param a the first object
* @param b the second object
* @param msg the message for the thrown exception
*
* @see IllegalStateException
*/
public static void badStateIfEquals(final Object a, final Object b, final String msg) {
Misc.badStateIfTrue(a.equals(b), msg);
}
/**
* Log a message to the logger of the caller at the given offset in the stack
* trace.
*
* If A.f()
calls B.g()
that finally calls
* dynamicLog(1, msg)
then, the msg will be logged with a code
* similar to:
* getLogger(B.getClass().getName()).log(level, msg);
*
* If the call was dynamicLog(2, msg)
then, the code would be:
* getLogger(A.getClass().getName()).log(level, msg);
*
*
* @param offset the offset in the stack trace
* @param level the level to log the message to
* @param msg the message to log
* @return
*/
public static void dynamicLog(final int offset, final Level level, final String msg) {
final StackTraceElement callerSTE = Misc.getCaller(offset);
final String className = callerSTE.getClassName();
final String methodName = callerSTE.getMethodName();
final Logger logger = Logger.getLogger(className);
final LogRecord record = new LogRecord(level, msg);
record.setSourceClassName(className);
record.setSourceMethodName(methodName);
record.setLoggerName(logger.getName());
logger.log(record);
}
/**
* Log the given message at the given level using caller's Logger.
*
* @param level a level
* @param msg the message
* @see #dynamicLog(int, Level, String)
*/
public static void log(final Level level, final String msg) {
Misc.dynamicLog(2, level, msg);
}
/**
* This method logs at the given level a "warning message" if the given
* parameter is null
*
* @param level a log level
* @param valueToCheck the value to check
* @param variableName the variable name holding valueToCheck. Can be null.
*
*/
public static void warnIfNull(final Level level, final Object valueToCheck, final String variableName) {
if (valueToCheck == null) {
final String msg = "Warning: " + ((variableName == null) ? "a variable" : variableName) + " is null!";
Misc.dynamicLog(1, level, msg);
}
}
/**
* This method logs at the given level a "warning" if the given parameter is
* not null
*
* @param level a log level
* @param valueToCheck the value to check
* @param variableName the variable name holding valueToCheck. Can be null.
*/
public static void warnIfNotNull(final Level level, final Object valueToCheck, final String variableName) {
if (valueToCheck != null) {
final String msg = "Warning: " + ((variableName == null) ? "a variable" : variableName) + " is not null!";
Misc.dynamicLog(1, level, msg);
}
}
/**
* This method logs at the given level a "warning" if the given parameter is
* true
*
* @param level a log level
* @param valueToCheck the value to check
* @param variableName the variable name holding valueToCheck. Can be null.
*/
public static void warnIfTrue(final Level level, final boolean valueToCheck, final String variableName) {
if (valueToCheck) {
final String msg = "Warning: " + ((variableName == null) ? "a variable" : variableName) + " is true!";
Misc.dynamicLog(1, level, msg);
}
}
/**
* This method logs at the given level a "warning" if the given parameter is
* false
*
* @param level a log level
* @param valueToCheck the value to check
* @param variableName the variable name holding valueToCheck. Can be null.
*/
public static void warnIfFalse(final Level level, final boolean valueToCheck, final String variableName) {
if (!valueToCheck) {
final String msg = "Warning: " + ((variableName == null) ? "a variable" : variableName) + " is false!";
Misc.dynamicLog(1, level, msg);
}
}
/**
* This method logs at the given level a "warning" if the given parameter are
* equals
*
* @param level a log level
* @param a an object
* @param b another object
*/
public static void warnIfEquals(final Level level, final Object a, final Object b) {
if (a.equals(b)) {
Misc.dynamicLog(1, level, "Warning: equals objects: " + Misc.LINE_SEPARATOR + Misc.details(a, b));
}
}
private static String details(final Object a, final Object b) {
return "a.toString(): " + a.toString() + Misc.LINE_SEPARATOR + "b.toString(): " + b.toString() + Misc.LINE_SEPARATOR + "a.idendityToString(): "
+ Misc.identityToString(a) + Misc.LINE_SEPARATOR + "b.identityToString(): " + Misc.identityToString(b);
}
/**
* This method logs at the given level a "warning" if the given parameter are
* not equals
*
* @param level a log level
* @param a an object
* @param b another object
*/
public static void warnIfNotEquals(final Level level, final Object a, final Object b) {
if (!a.equals(b)) {
Misc.dynamicLog(1, level, "Warning: non-equals objects: " + Misc.LINE_SEPARATOR + Misc.details(a, b));
}
}
public static String getCurrentThreadStackTrace() {
final StackTraceElement[] elements = Thread.currentThread().getStackTrace();
final StringBuilder stringBuilder = new StringBuilder();
for (final StackTraceElement element : elements) {
stringBuilder.append(element.toString());
stringBuilder.append(Misc.LINE_SEPARATOR);
}
return stringBuilder.toString();
}
public static String getStackTraceFrom(final Throwable t) {
final StringWriter stringBuilder = new StringWriter();
for (final StackTraceElement element : t.getStackTrace()) {
stringBuilder.append(" at ");
stringBuilder.append(element.toString());
stringBuilder.append(Misc.LINE_SEPARATOR);
}
return stringBuilder.toString();
}
public static String deepToString(final Object o) {
return Misc.recursiveDeepToString(o, new HashMap