com.jsftoolkit.utils.Utils Maven / Gradle / Ivy
package com.jsftoolkit.utils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
/**
* Utility functions. We reinvent the wheel here because we want to avoid
* requiring external dependencies.
*
* See the unit tests for further examples of usage. Methods that are not unit
* tested are noted.
*
* @author noah
*
*/
public class Utils {
/**
* Base class for iterators that do not support remove.
*
* @author noah
*
* @param
*/
public static abstract class StaticIterator implements Iterator {
public final void remove() {
throw new UnsupportedOperationException();
}
}
/**
*
* @param
* @param value
* @param def
* @return def, if value is null. Otherwise, value.
*/
public static T getValue(T value, T def) {
return value == null ? def : value;
}
/**
* Similar to {@link #getValue(Object, Object)}, but allows both return
* values to be specified.
*
* @param
* @param obj
* @param isNull
* returned if obj is null
* @param isntNull
* returned if obj is not null
* @return isNull or isntNull
*/
public static T getValue(Object obj, T isNull, T isntNull) {
return obj == null ? isNull : isntNull;
}
/**
* Gets the value of the given key from the map, and if it is not null
* returns it. Otherwise returns def.
*
* @param
* @param
* @param map
* @param key
* @param def
* @return
*/
public static V getValue(Map map, K key, V def) {
return getValue(map.get(key), def);
}
/**
* Checks map for the given key, setting it to the given default if the
* value returned is null.
*
* @param
* @param
* @param map
* @param key
* @param def
* @return the value in the map.
*/
public static V mapDefault(Map map, K key, V def) {
V v = map.get(key);
if (v == null) {
map.put(key, def);
v = def;
}
return v;
}
/**
*
* @param s
* @param def
* the default value
* @return s if s is not empty according to {@link #isEmpty(String)},
* otherwise def
*/
public static String getValue(String s, String def) {
return isEmpty(s) ? def : s;
}
public static boolean isEmpty(Object o) {
return isEmpty(Utils.toString(o));
}
/**
*
* @param s
* @return true if s is null or contains only whitespace characters
* according to {@link Character#isWhitespace(char)}. Otherwise,
* false.
*/
public static boolean isEmpty(CharSequence s) {
if (s != null) {
for (int i = 0; i < s.length(); i++) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
}
return true;
}
/**
* Same as join(array, del, 0, array.length)
*
* @param array
* @param del
* @return
*/
public static String join(final Object[] array, Object del) {
return array == null ? "" : join(array, del, 0, array.length);
}
/**
* Joins the given array by creating an
* {@link #arrayIterator(Object[], int, int)} and passing it to
* {@link #join(Iterator, Object)}
*
* @param array
* @param del
* @param startIndex
* 0 to array.length
* @param length
* number of element to return
* @return
*/
public static String join(final Object[] array, Object del,
final int startIndex, final int length) {
return join(arrayIterator(array, startIndex, length), del);
}
/**
* Factory for iterators over a particular array. Simply delegates to
* {@link Utils#arrayIterator(Object[], int, int)}.
*
* @author noah
*
* @param
*/
public static class ArrayIterable implements Iterable {
private final T[] array;
private final int startIndex;
private final int length;
/**
*
* @see Utils#arrayIterator(Object[], int, int)
*/
public ArrayIterable(final T[] array, final int startIndex,
final int length) {
super();
this.array = array;
this.startIndex = startIndex;
this.length = length;
}
/**
* @see Utils#arrayIterator(Object[], int, int)
*/
public Iterator iterator() {
return arrayIterator(array, startIndex, length);
}
/**
* @see Utils#arrayIterator(Object[], int, int)
*/
public static Iterable iterable(T[] array, int startIndex,
int length) {
return new ArrayIterable(array, startIndex, length);
}
}
/**
*
* @param
* @param array
* the elements t iterate over.
* @return an iterator over the given elements.
*/
public static Iterator arrayIterator(final T... array) {
return arrayIterator(array, 0, array.length);
}
/**
* Creates an iterator that returns elements from the given array. If length <
* 0, the iterator will traverse backwards from startIndex(inclusive),
* returning a total of length elements. Array indecies that go above
* array.length or below 0 are wrapped according to the semantics of
* {@link #wrap(int, int)} e.g.
*
* array = {"foo","bar","baz"}
* elements of arrayIterator(array,0,3) = "foo","bar","baz"
* elements of arrayIterator(array,3,0) = empty
* elements of arrayIterator(array,1,2) = "bar","baz"
* elements of arrayIterator(array,1,1) = "bar"
* elements of arrayIterator(array,1,-1) = "bar"
* elements of arrayIterator(array,1,-2) = "bar","foo"
* elements of arrayIterator(array,3,-2) = "foo","baz"
* elements of arrayIterator(array,-1,-2) = "baz","bar"
* elements of arrayIterator(array,-1,-5) = "baz","bar","foo","baz","bar"
*
*
* @param
* @param array
* @param startIndex
* @param length
* @return
*/
public static Iterator arrayIterator(final T[] array,
final int startIndex, final int length) {
if (length < 0) {
return new StaticIterator() {
int i = wrap(startIndex, array.length);
int remaining = array.length > 0 ? -length : 0;
public boolean hasNext() {
return remaining > 0;
}
public T next() {
T t = array[i];
i = wrap(i - 1, array.length);
remaining--;
return t;
}
};
}
return new StaticIterator() {
int i = wrap(startIndex, array.length);
int remaining = array.length > 0 ? length : 0;
public boolean hasNext() {
return remaining > 0;
}
public T next() {
T t = array[i];
i = wrap(i + 1, array.length);
remaining--;
return t;
}
};
}
/**
* Wraps values with modulo arithmetic. Negative values are considered
* offsets from top. e.g. -1 wraps to top - 1. -top wraps to 0. top + 1
* wraps to 1. If top <= 0, returns 0.
*
* @param value
* @param top
* @return a number between 0 (inclusive) and top (exclusive)
*/
public static int wrap(int value, int top) {
if (top <= 0) {
return 0;
}
value %= top;
return value < 0 ? top + value : value;
}
/**
* Concatenates the {@link Object#toString()} of elements from it, appending
* del between elements. e.g. joining "foo","bar","baz" with del "," =
* "foo,bar,baz"
*
* @param it
* @param del
* @return
*/
public static String join(Iterator> it, Object del) {
StringBuilder sb = new StringBuilder();
if (it.hasNext()) {
sb.append(it.next());
while (it.hasNext()) {
sb.append(del);
sb.append(it.next());
}
}
return sb.toString();
}
/**
* {@link #toString(Object)}'s object and calls split on it.
*
* @param object
* @param del
* @return
*/
public static String[] split(Object object, String del) {
return split(object, del, 0);
}
/**
*
* @param object
* @param del
* @param limit
* same as {@link String#split(String, int)}
* @return
*/
public static String[] split(Object object, String del, int limit) {
return toString(object).split(del, limit);
}
/**
*
* @param obj
* @return the {@link Object#toString()} of obj if it is not null, or the
* empty string otherwise.
*/
public static String toString(Object obj) {
return toString(obj, "");
}
/**
*
* @param obj
* @param def
* @return the {@link Object#toString()} if obj is not null, def otherwise.
*/
public static String toString(Object obj, String def) {
return obj == null ? def : obj.toString();
}
/**
* Finds a method on the given class that would accept parameters of the
* given types and has the given return type (if specified).
*
* @param clazz
* @param returnType
* the return type of the method, or null if it doesn't matter.
* @param paramTypes
* the types of the parameters to be passed.
* @return the {@link Method}, if found.
*/
public static Method findMethod(Class> clazz, Class> returnType,
Class>... paramTypes) {
Method[] methods = clazz.getMethods();
methodLoop: for (Method method : methods) {
Class>[] types = method.getParameterTypes();
if (types.length == paramTypes.length
&& (returnType == null || method.getReturnType()
.isAssignableFrom(returnType))) {
for (int i = 0; i < types.length; i++) {
if (!types[i].isAssignableFrom(paramTypes[i])) {
continue methodLoop;
}
}
return method;
}
}
return null;
}
/**
* null safe version of {@link #findMethod(Class, Class, Class[])}. If
* object is null, returns null, otherwise calls
* {@link #findMethod(Class, Class, Class[])} with object.getClass().
*
* @param object
* @param returnType
* @param paramTypes
* @return
*/
public static Method findMethod(Object object, Class> returnType,
Class>... paramTypes) {
return object == null ? null : findMethod(object.getClass(),
returnType, paramTypes);
}
/**
* Gets the specified index from the list if the list is not null and index
* < list.size(). Otherwise returns null.
*
* @param
* @param list
* @param index
* @return
*/
public static T get(List list, int index) {
if (list != null && list.size() > index) {
return list.get(index);
}
return null;
}
/**
* Null safe equals. o1 and o2 may be null. If o1 and o2 are null, returns
* true.
*
* @param o1
* @param o2
* @return
*/
public static boolean equals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
return o1 != null && o1.equals(o2);
}
/**
* Loads a class path resource as an {@link InputStream} and calls
* {@link #toString(InputStream, String)}.
*
* @param resource
* @param encoding
* @return
* @throws IOException
* if resource is not found.
*/
public static String resourceToString(String resource, String encoding)
throws IOException {
InputStream stream = Utils.class.getResourceAsStream(resource);
if (stream == null) {
throw new FileNotFoundException(resource);
}
return toString(stream, encoding);
}
/**
* Similar to {@link #resourceToString(String, String)}, but returns a
* default value if anything goes wrong (file not found, couldn't be read,
* etc.)
*
* @param resource
* @param encoding
* @param def
* the default value
* @return
*/
public static String resourceToString(String resource, String encoding,
String def) {
try {
return resourceToString(resource, encoding);
} catch (IOException e) {
return def;
}
}
/**
* Reads the given stream in the given encoding, into a String.
*
* @param in
* @param encoding
* @return
* @throws IOException
*/
public static String toString(InputStream in, String encoding)
throws IOException {
return toString(new InputStreamReader(in, getCharset(encoding)));
}
/**
* Converts the given bytes into a string. If encoding is null, the platform
* default is used.
*
* @param bytes
* @param encoding
* @return
* @throws UnsupportedEncodingException
*/
public static String toString(byte[] bytes, String encoding)
throws UnsupportedEncodingException {
return encoding == null ? new String(bytes) : new String(bytes,
encoding);
}
/**
* Reads all the characters and returns them as a string.
*
* @param reader
* @return
* @throws IOException
*/
public static String toString(Reader reader) throws IOException {
StringWriter writer = new StringWriter();
writeAll(reader, writer);
return writer.toString();
}
/**
* Gets the supported charset with the given name, returning the default
* charset if the named charset is null or not supported.
*
* @param charsetName
* @return
*/
public static Charset getCharset(String charsetName) {
return charsetName != null && Charset.isSupported(charsetName) ? Charset
.forName(charsetName)
: Charset.defaultCharset();
}
/**
* Size of default byte and char buffers.
*/
private static final int BUFFER_SIZE = 16384;
/**
* Copies characters from the given reader to the give writter until the
* reader returns EOS.
*
* @param in
* @param out
* @throws IOException
*/
public static void writeAll(Reader in, Writer out) throws IOException {
writeAll(in, out, new char[BUFFER_SIZE]);
}
/**
* Same as {@link #writeAll(Reader, Writer)}, but uses the given buffer.
*
* @param in
* @param out
* @param b
* @throws IOException
*/
public static void writeAll(Reader in, Writer out, char[] b)
throws IOException {
int read;
while ((read = in.read(b)) != -1) {
out.write(b, 0, read);
}
}
/**
* Copies all the bytes from in to out, using the given buffer.
*
* @param in
* @param out
* @param b
* @throws IOException
*/
public static void writeAll(InputStream in, OutputStream out, byte[] b)
throws IOException {
int read;
while ((read = in.read(b)) != -1) {
out.write(b, 0, read);
}
}
/**
* Same as {@link #writeAll(InputStream, OutputStream, byte[])}, but uses a
* buffer of the default size.
*
* @param in
* @param out
* @throws IOException
*/
public static void writeAll(InputStream in, OutputStream out)
throws IOException {
writeAll(in, out, new byte[BUFFER_SIZE]);
}
/**
*
* @param string
* @return string, with the first char in uppercase.
*/
public static String capitalize(String string) {
return string != null && string.length() > 0 ? Character
.toUpperCase(string.charAt(0))
+ string.substring(1) : "";
}
/**
*
* @param string
* @return string, with the first character lowercase.
*/
public static String uncapitalize(String string) {
return string != null && string.length() > 0 ? Character
.toLowerCase(string.charAt(0))
+ string.substring(1) : "";
}
/**
* Concatenates the two 'arrays', returning the new array.
*
* @param array
* @param append
* @return
*/
@SuppressWarnings("unchecked")
public static String[] append(String[] array, String... append) {
return doAppend(new String[array.length + append.length], array, append);
}
public static Object[] append(Object[] array, Object... append) {
return doAppend(new Object[array.length + append.length], array, append);
}
private static T[] doAppend(T[] _new, T[] array, T[] append) {
System.arraycopy(array, 0, _new, 0, array.length);
System.arraycopy(append, 0, _new, array.length, append.length);
return _new;
}
/**
* Gets the value of the named property via introspection.
*
* @param
* @param obj
* @param property
* @return
* @throws IntrospectionException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@SuppressWarnings("unchecked")
public static T getProperty(Object obj, String property)
throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(property, obj.getClass());
return (T) pd.getReadMethod().invoke(obj);
}
/**
* same as get(array, index, null)
*
* @param
* @param array
* @param index
* @return
*/
public static T get(T[] array, int index) {
return get(array, index, null);
}
/**
* Gets the given array index, if it exists, otherwise returns def.
*
* @param
* @param array
* @param index
* @param def
* @return
*/
public static T get(T[] array, int index, T def) {
return array != null && index < array.length ? array[index] : def;
}
/**
* Creates a new set from the given items, the order they are given will be
* their iteration order.
*
* @param
* @param items
* @return
*/
public static LinkedHashSet asSet(T... items) {
LinkedHashSet set = new LinkedHashSet();
if (items != null) {
for (T item : items) {
set.add(item);
}
}
return set;
}
/**
* Collects the values of the map associated with the given keys into a set.
*
* @param
* @param
* @param map
* @param keys
* @return
*/
public static Set getValues(Map map, Iterator keys) {
Set values = new HashSet();
while (keys.hasNext()) {
values.add(map.get(keys.next()));
}
return values;
}
/**
* Fills the given array with the given value.
*
* @param array
* @param value
* @return array
*/
public static boolean[] fill(boolean[] array, boolean value) {
Arrays.fill(array, value);
return array;
}
/**
* Fills the given array with the given value.
*
* @param array
* @param value
* @return array
*/
public static T[] fill(T[] array, T value) {
Arrays.fill(array, value);
return array;
}
/**
* Casts the given ints down to bytes.
*
* @param bytes
* @return
*/
public static byte[] truncate(int[] bytes) {
byte[] array = new byte[bytes.length];
for (int i = 0; i < array.length; i++) {
array[i] = (byte) bytes[i];
}
return array;
}
/**
* Casts the given ints to chars.
*
* @param chars
* @return
*/
public static char[] toCharArray(int[] chars) {
char[] array = new char[chars.length];
for (int i = 0; i < array.length; i++) {
array[i] = (char) chars[i];
}
return array;
}
/**
* Set the array index to true, returning array. For chaining. Untested.
*
* @param array
* @param index
* @return
*/
public static boolean[] set(boolean[] array, int index) {
array[index] = true;
return array;
}
/**
* Set the array index to false, returning array. For chaining. Untested.
*
* @param array
* @param index
* @return
*/
public static boolean[] unset(boolean[] array, int index) {
array[index] = false;
return array;
}
/**
* Utility class for chaining put operations on map.
*
* @author noah
*
* @param
* @param
*/
public static class MapBuilder {
private final Map map;
public MapBuilder(Map map) {
super();
this.map = map;
}
public MapBuilder put(K key, V value) {
map.put(key, value);
return this;
}
public Map getMap() {
return map;
}
public static MapBuilder putFirst(K key, V value) {
return new MapBuilder(new HashMap()).put(key, value);
}
}
/**
*
* @param array
* @return an array with null elements removed.
*/
public static Object[] prune(Object[] array) {
List