
jasima.core.util.Util Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2010-2015 Torsten Hildebrandt and jasima contributors
*
* This file is part of jasima, v1.2.
*
* jasima is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* jasima 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with jasima. If not, see .
*******************************************************************************/
package jasima.core.util;
import jasima.core.statistics.SummaryStat;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Some static utility methods that don't really fit anywhere else.
*
* @author Torsten Hildebrandt
* @version "$Id: Util.java 550 2015-01-23 15:07:23Z [email protected] $"
*/
public class Util {
/**
* Converts an exception's stack trace to a single line string.
*/
public static String exceptionToString(Throwable t) {
// convert exception to string
Writer sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
String s = sw.toString();
return s.replace(System.getProperty("line.separator") + '\t', " \\\\ ")
.trim();
}
/**
* Returns a new array with a certain number of new objects of a certain
* type.
*
* @param numElements
* Number of elements in the result array.
* @param componentType
* Class of the array elements.
* @return The new array with all elements initialized with new objects.
*/
public static T[] initializedArray(int numElements,
Class componentType) {
try {
@SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(componentType, numElements);
for (int i = 0; i < numElements; i++) {
res[i] = componentType.newInstance();
}
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Generic method to remove the first occurrence of an element from an
* array. A new array without the given element is returned (or the old
* array if element was not found).
*/
@SuppressWarnings("unchecked")
public static T[] removeFromArray(T[] a, T elementToRemove) {
ArrayList l = new ArrayList(Arrays.asList(a));
if (l.remove(elementToRemove)) {
return l.toArray((T[]) Array.newInstance(a.getClass()
.getComponentType(), l.size()));
} else {
return a;
}
}
/**
* Generic method to add an element to an array. A new array containing the
* given element is returned.
*/
@SuppressWarnings("unchecked")
public static T[] addToArray(T[] a, T newElement, Class compType) {
if (a == null || a.length == 0) {
T[] res = (T[]) Array.newInstance(compType, 1);
res[0] = newElement;
return res;
} else {
ArrayList l = new ArrayList(Arrays.asList(a));
l.add(newElement);
return l.toArray((T[]) Array.newInstance(compType, l.size()));
}
}
/**
* Attempts trivial type conversion. This methods supports all cating
* conversions (JLS 5.5) and always returns null when the input object is
* null. If the target type is {@link String}, the result is the return
* value of the input object's {@link Object#toString()} method. Any object
* can be converted to {@link Integer}, {@link Double} and {@link Boolean},
* but those conversions can throw exceptions.
*
* @param o
* the object to be converted
* @param klass
* the target type
* @return the converted object
* @throws IllegalArgumentException
* if the conversion is not supported
* @throws NumberFormatException
* if the input object is not assignable to {@link Number} and
* the return value of its {@link Object#toString()} method
* can't be converted to the numeric target type
*/
@SuppressWarnings("unchecked")
public static E convert(Object o, Class klass)
throws IllegalArgumentException {
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6456930
// klass.cast will not unbox and can't accept primitive values
if (o == null)
return null;
if (klass.isAssignableFrom(o.getClass())) {
return (E) o;
}
if (klass == String.class) {
return (E) o.toString();
}
if (klass == int.class || klass == Integer.class) {
if (o instanceof Number)
return (E) (Integer) ((Number) o).intValue();
return (E) Integer.valueOf(o.toString());
}
if (klass == double.class || klass == Double.class) {
if (o instanceof Number)
return (E) (Double) ((Number) o).doubleValue();
return (E) Double.valueOf(o.toString());
}
if (klass == boolean.class || klass == Boolean.class) {
String str = o.toString();
if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("yes")
|| str.equalsIgnoreCase("1"))
return (E) Boolean.TRUE;
if (str.equalsIgnoreCase("false") || str.equalsIgnoreCase("no")
|| str.equalsIgnoreCase("0"))
return (E) Boolean.FALSE;
throw new IllegalArgumentException(String.format(
"Can't convert %s to bool.", o));
}
if (klass.isEnum()) {
return (E) Enum.valueOf(klass.asSubclass(Enum.class), o.toString());
}
throw new IllegalArgumentException(String.format(
"Can't convert from %s to %s.",
o.getClass().getCanonicalName(), klass.getCanonicalName()));
}
/**
* Sets a property named with propPath to a certain value using reflection.
*
* Example: setProperty( obj, "a.b.c", 5 ); is equivalent to a direct call
* obj.getA().getB().setC(5)
*/
public static void setProperty(Object o, String propPath, Object value) {
try {
String[] segments = propPath.split("\\.");
// call getters until we finally arrive where we can call the
// setter-method
for (int i = 0; i < segments.length; i++) {
BeanInfo bi = Introspector.getBeanInfo(o.getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
PropertyDescriptor match = null;
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(segments[i])) {
match = pd;
break; // for
}
}
if (match == null)
throw new IllegalArgumentException("segment '"
+ segments[i] + "' not found of property path '"
+ propPath + "'.");
Method m;
if (i == segments.length - 1) {
// call setter
m = match.getWriteMethod();
if (m == null)
throw new RuntimeException("Property '" + segments[i]
+ "' is not writable.");
o = m.invoke(o, convert(value, match.getPropertyType()));
} else {
// call getter and continue
m = match.getReadMethod();
o = m.invoke(o);
}
}
} catch (Exception e1) {
throw new RuntimeException("Can't set property '" + propPath
+ "' to value '" + value + "'. " + e1.getMessage(), e1);
}
}
/**
* Determines the type of a property named with propPath using reflection.
*
* This method interprets propPath the same way as
* {@link #getProperty(Object, String)} does.
*/
public static Class> getPropertyType(Object o, String propPath) {
try {
String[] segments = propPath.split("\\.");
// call getters until we finally arrive where we can call the
// setter-method
for (int i = 0; i < segments.length; i++) {
BeanInfo bi = Introspector.getBeanInfo(o.getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
PropertyDescriptor match = null;
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(segments[i])) {
match = pd;
break; // for
}
}
if (match == null)
throw new IllegalArgumentException("segment '"
+ segments[i] + "' not found of property path '"
+ propPath + "'.");
if (i == segments.length - 1) {
// return property type
return match.getPropertyType();
} else {
// call getter and continue
o = match.getReadMethod().invoke(o);
}
}
throw new AssertionError();
} catch (Exception e1) {
throw new RuntimeException("Can't determine type of property "
+ propPath, e1);
}
}
/**
* Gets a property named with propPath using reflection.
*
* Example: getProperty( obj, "a.b.c" ); is equivalent to a direct call
* obj.getA().getB().getC()
*/
public static Object getProperty(Object o, String propPath) {
try {
String[] segments = propPath.split("\\.");
// call getters until we finally arrive where we can call the
// final get-method
for (int i = 0; i < segments.length; i++) {
BeanInfo bi = Introspector.getBeanInfo(o.getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
PropertyDescriptor match = null;
for (PropertyDescriptor pd : pds) {
if (pd.getName().equals(segments[i])) {
match = pd;
break; // for
}
}
if (match == null)
throw new IllegalArgumentException("segment '"
+ segments[i] + "' not found of property path '"
+ propPath + "'.");
// call getter and continue
Method m = match.getReadMethod();
o = m.invoke(o);
}
return o;
} catch (Exception e1) {
throw new RuntimeException(
"Can't get property '" + propPath + "'.", e1);
}
}
/**
* Finds (bean) properties of o
which have both getter and
* setter methods.
*/
public static PropertyDescriptor[] findWritableProperties(Object o) {
try {
BeanInfo bi = Introspector.getBeanInfo(o.getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
ArrayList list = new ArrayList();
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null && pd.getReadMethod() != null)
list.add(pd);
}
return list.toArray(new PropertyDescriptor[list.size()]);
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
}
/**
* This method returns a clone of an object, if this object is cloneable.
* The clone is created by calling clone()
using Java
* reflection, therefore clone()
not necessarily has to be
* public.
*
* @param o
* The object to be cloned.
* @return A clone of o was cloneable, or otherwise the original object.
*/
@SuppressWarnings("unchecked")
public static T cloneIfPossible(T o) {
// array?
Class> o2 = o.getClass().getComponentType();
if (o2 == null)
o2 = o.getClass();
if (Cloneable.class.isAssignableFrom(o2)) {
// o or an array's components are clonable
try {
Method cloneMethod = o.getClass().getMethod("clone",
new Class[] {});
return (T) cloneMethod.invoke(o, new Object[] {});
} catch (NoSuchMethodException ignore) {
// clonable, but no public clone-method, return o as is
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return o;
}
/**
* Produces a deep clone of {@code array}, i.e., for each element of
* {@code array} creating a clone is attempted using
* {@link #cloneIfPossible(Object)}.
*
* @param array
* The array to be cloned.
* @return A clone of {@code array} with each element also cloned.
*/
public static T[] deepCloneArrayIfPossible(T[] array) {
if (array == null)
return null;
T[] clone = array.clone();
for (int i = 0; i < clone.length; i++) {
clone[i] = cloneIfPossible(array[i]);
}
return clone;
}
/**
* Two double values are considered equal if the absolute value of their
* differences is smaller than this constant.
*/
public static final double EPSILON = 1e-10;
/*
* @return the next non-empty line (everything after '#' is a comment and
* ignored unless it is escaped with a preceding back slash)
*/
public static String nextNonEmptyLine(BufferedReader r) throws IOException {
String s = r.readLine();
while (s != null) {
boolean foundHash = false;
// find part which is not a comment
int hashPos = s.indexOf('#');
if (hashPos >= 0) {
foundHash = true;
s = s.replace("\\#", "\u02AD"); // escaped?, temporarily replace
// with something safe
hashPos = s.indexOf('#');
if (hashPos >= 0) {
s = s.substring(0, hashPos);
}
}
s = s.trim();
if (s.length() > 0) {
if (foundHash)
s = s.replace('\u02AD', '#');
return s;
}
s = r.readLine();
}
return null;
}
public static String[] lines(File f) throws IOException {
return lines(new FileReader(f));
}
public static String[] lines(Reader r) throws IOException {
BufferedReader br = new BufferedReader(r);
ArrayList ss = new ArrayList();
String s;
while ((s = br.readLine()) != null) {
ss.add(s);
}
return ss.toArray(new String[ss.size()]);
}
/**
*
* @return An array containing all entries of "ss" not starting with
* "prefix".
*/
public static String[] filter(String[] ss, String prefix) {
ArrayList list = new ArrayList(Arrays.asList(ss));
for (Iterator i = list.iterator(); i.hasNext();) {
if (i.next().startsWith(prefix))
i.remove();
}
return list.toArray(new String[list.size()]);
}
/**
* Examples: "5" -> {5}; "23,5..10,3" -> {23,5,6,7,8,9,10,3}; "1,2,3" ->
* {1,2,3}
*/
public static int[] parseIntList(String list) {
ArrayList res = new ArrayList();
for (String s : list.split(",")) {
if (s.contains("..")) {
String[] sp = s.split("\\.\\.");
int i1 = Integer.parseInt(sp[0]);
int i2 = Integer.parseInt(sp[1]);
for (int i = i1; i <= i2; i++)
res.add(i);
} else
res.add(Integer.parseInt(s));
}
// convert Integer[] to int[]
int[] is = new int[res.size()];
for (int i = 0; i < res.size(); i++)
is[i] = res.get(i);
return is;
}
/**
* Converts a list of comma-separated double values (with dot as decimal
* separator) to a double-array. Example: parseDblList("1.23,4.56") ->
* {1.23,4.56}
*/
public static double[] parseDblList(String s) {
ArrayList ll = new ArrayList();
StringTokenizer st = new StringTokenizer(s, ",");
while (st.hasMoreElements()) {
double v = Double.parseDouble(st.nextToken().trim());
ll.add(v);
}
double[] res = new double[ll.size()];
for (int i = 0; i < res.length; i++) {
res[i] = ll.get(i);
}
return res;
}
public static String[][] read2DimStrings(BufferedReader r, int numRows)
throws IOException {
String[][] ss = new String[numRows][];
for (int i = 0; i < numRows; i++) {
ss[i] = nextNonEmptyLine(r).trim().split("\\s+");
}
return ss;
}
public static double deleteArrayElement(double[] prios, int elemIdx,
double fillWith) {
double res = prios[elemIdx];
System.arraycopy(prios, elemIdx + 1, prios, elemIdx, prios.length
- elemIdx - 1);
prios[prios.length - 1] = fillWith;
return res;
}
public static T deleteArrayElement(T[] prios, int elemIdx, T fillWith) {
T res = prios[elemIdx];
System.arraycopy(prios, elemIdx + 1, prios, elemIdx, prios.length
- elemIdx - 1);
prios[prios.length - 1] = fillWith;
return res;
}
public static T moveArrayElementToBack(T[] prios, int elemIdx) {
T res = prios[elemIdx];
System.arraycopy(prios, elemIdx + 1, prios, elemIdx, prios.length
- elemIdx - 1);
prios[prios.length - 1] = res;
return res;
}
public static double mean(Collection extends Number> coll) {
if (coll == null || coll.size() < 1)
throw new IllegalArgumentException();
double res = 0;
for (Number n : coll) {
res += n.doubleValue();
}
return res / coll.size();
}
public static double stdDev(Collection extends Number> coll) {
if (coll == null || coll.size() < 2)
throw new IllegalArgumentException();
double mean = mean(coll);
double res = 0;
for (Number n : coll) {
double d = n.doubleValue() - mean;
res += d * d;
}
return res / (coll.size() - 1);
}
public static double sum(final double[] productMix) {
if (productMix == null || productMix.length == 0)
throw new IllegalArgumentException(Arrays.toString(productMix));
double res = productMix[0];
for (int i = 1; i < productMix.length; i++) {
res += productMix[i];
}
return res;
}
public static int sum(final int[] is) {
if (is == null || is.length == 0)
throw new IllegalArgumentException(Arrays.toString(is));
int res = is[0];
for (int i = 1; i < is.length; i++) {
res += is[i];
}
return res;
}
public static int min(int[] vs) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
int min = vs[0];
for (int i = 1; i < vs.length; i++) {
if (vs[i] < min)
min = vs[i];
}
return min;
}
public static int minIdx(int[] vs) {
return minIdx(vs, 0);
}
public static int minIdx(int[] vs, int startIdx) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
int min = vs[startIdx];
int minIdx = startIdx;
for (int i = startIdx + 1; i < vs.length; i++) {
if (vs[i] < min) {
min = vs[i];
minIdx = i;
}
}
return minIdx;
}
public static double min(double[] vs) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
double min = vs[0];
for (int i = 1; i < vs.length; i++) {
if (vs[i] < min)
min = vs[i];
}
return min;
}
public static int minIdx(double[] vs) {
return minIdx(vs, 0);
}
public static int minIdx(double[] vs, int startIdx) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
double min = vs[startIdx];
int minIdx = startIdx;
for (int i = startIdx + 1; i < vs.length; i++) {
if (vs[i] < min) {
min = vs[i];
minIdx = i;
}
}
return minIdx;
}
public static int max(int[] vs) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
int max = vs[0];
for (int i = 1; i < vs.length; i++) {
if (vs[i] > max)
max = vs[i];
}
return max;
}
public static double max(double[] vs) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
double max = vs[0];
for (int i = 1; i < vs.length; i++) {
if (vs[i] > max)
max = vs[i];
}
return max;
}
public static int maxIdx(double[] vs) {
if (vs == null || vs.length == 0)
throw new IllegalArgumentException(Arrays.toString(vs));
double max = vs[0];
int maxIdx = 0;
for (int i = 1; i < vs.length; i++) {
if (vs[i] > max) {
max = vs[i];
maxIdx = i;
}
}
return maxIdx;
}
/**
* Rounds the given double value to a certain number of decimal places.
* {@code decimals} can be positive or negative.
*
* @see #round(double[], int)
*/
public static double round(final double val, final int decimals) {
if (decimals >= 0) {
long fact = powerOfTen(decimals);
return Math.round(val * fact) / ((double) fact);
} else {
long fact = powerOfTen(-decimals);
return Math.round(val / fact) * ((double) fact);
}
}
private static long powerOfTen(int exp) {
assert exp >= 0;
long fact = 1;
for (int i = 0; i < exp; i++) {
fact *= 10;
}
return fact;
}
/**
* Rounds all values in the double array {@code vs} to a certain number of
* decimal places. This method does not create a copy of {@code vs}, but
* modifies its contents.
*
* @return the parameter {@code vs} to allow easy chaining of method calls.
*
* @see #round(double, int)
*/
public static double[] round(final double[] vs, final int decimals) {
for (int i = 0; i < vs.length; i++) {
vs[i] = round(vs[i], decimals);
}
return vs;
}
/**
* @return True, if the absolute value of their differences of two double
* values is smaller than the constant EPSILON (default: 10^-10).
*/
public static boolean equals(final double v1, final double v2) {
return Math.abs(v1 - v2) < EPSILON;
}
/**
* Converts an array (either Object[] or of a primitive type) to a String
* containing it's elements in square brackets.
*/
public static String arrayToString(Object arbitraryArray) {
Class> compType = arbitraryArray.getClass().getComponentType();
if (compType == null)
throw new IllegalArgumentException();
if (compType.isPrimitive()) {
if (compType == Integer.TYPE)
return Arrays.toString((int[]) arbitraryArray);
else if (compType == Long.TYPE)
return Arrays.toString((long[]) arbitraryArray);
else if (compType == Short.TYPE)
return Arrays.toString((short[]) arbitraryArray);
else if (compType == Byte.TYPE)
return Arrays.toString((byte[]) arbitraryArray);
else if (compType == Boolean.TYPE)
return Arrays.toString((boolean[]) arbitraryArray);
else if (compType == Double.TYPE)
return Arrays.toString((double[]) arbitraryArray);
else if (compType == Float.TYPE)
return Arrays.toString((float[]) arbitraryArray);
else
throw new AssertionError();
} else
return Arrays.deepToString((Object[]) arbitraryArray);
}
/**
* Convenience method to put mean, max and variance of a ValueStat object in
* a result map.
*
* @param vs
* the statistic
* @param prefix
* name prefix
* @param res
* result map where keys should be added
*/
public static void putMeanMaxVar(SummaryStat vs, String prefix,
Map res) {
res.put(prefix + "Mean", vs);
if (vs.numObs() > 0)
res.put(prefix + "Max", vs.max());
if (vs.numObs() >= 2)
res.put(prefix + "Variance", vs.variance());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy