/** * Copyright (C) 2012-2013 Selventa, Inc. * * This file is part of the OpenBEL Framework. * * This program 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, either version 3 of the License, or * (at your option) any later version. * * The OpenBEL Framework 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 the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>. * * Additional Terms under LGPL v3: * * This license does not authorize you and you are prohibited from using the * name, trademarks, service marks, logos or similar indicia of Selventa, Inc., * or, in the discretion of other licensors or authors of the program, the * name, trademarks, service marks, logos or similar indicia of such authors or * licensors, in any marketing or advertising materials relating to your * distribution of the program or any covered product. This restriction does * not waive or limit your obligation to keep intact all copyright notices set * forth in the program as delivered to you. * * If you distribute the program in whole or in part, or any modified version * of the program, and you assume contractual liability to the recipient with * respect to the program or modified version, then you will indemnify the * authors and licensors of the program for any liabilities that these * contractual assumptions directly impose on those licensors and authors. */ package org.openbel.framework.common; import static java.io.File.separator; import static java.lang.Integer.parseInt; import static java.lang.System.arraycopy; import static java.lang.Thread.currentThread; import static java.lang.reflect.Array.newInstance; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static org.openbel.framework.common.PathConstants.BEL_SCRIPT_EXTENSION; import static org.openbel.framework.common.PathConstants.XBEL_EXTENSION; import static org.openbel.framework.common.Strings.SHA_256; import java.io.*; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.DatagramSocket; import java.net.ServerSocket; import java.nio.channels.FileChannel; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.openbel.framework.common.bel.parser.BELParser; import org.openbel.framework.common.enums.FunctionEnum; import org.openbel.framework.common.enums.RelationshipType; import org.openbel.framework.common.model.Term; /** * Utility methods used throughout the BEL codebase. * * @author Anthony Bargnesi {@code <abargnesi@selventa.com>} */ public class BELUtilities { private static final long[] byteTable = createLookupTable(); private static final long HMULT = 7664345821815920749L; private static final long HSTART = 0xBB40E64DA205B064L; private static int ONE_MEGABYTE = 1024 * 1024; private static int pid = -1; private static final int MIN_PORT = 0; private static final int MAX_PORT = 65535; private static final int MIN_EPHEMERAL_PORT = 49152; private static final Pattern NON_WORD_PATTERN = Pattern.compile("[\\W]"); private static final Set<String> _functions = new HashSet<String>(); private static final Set<String> _relationships = new HashSet<String>(); private static final Set<String> _keywords = new HashSet<String>(); static { for (FunctionEnum fx : FunctionEnum.values()) { _functions.add(fx.getAbbreviation()); _functions.add(fx.getDisplayValue()); } for (RelationshipType r : RelationshipType.values()) { _relationships.add(r.getAbbreviation()); _relationships.add(r.getDisplayValue()); } _keywords.addAll(Arrays.asList("ALL", "ANNOTATION", "AS", "DEFAULT", "DEFINE", "DOCUMENT", "LIST", "NAMESPACE", "PATTERN", "SET", "STATEMENT_GROUP", "UNSET", "URL")); } /** * Returns a hash set of type {@code E} optimized to the * {@link Collection#size() size} of the supplied {@link Collection * collection}. * * @param <E> Formal type parameter collection element * @param c {@link Collection}; may be null * @return Hash set of type {@code E} */ public static <E> HashSet<E> asHashSet(final Collection<E> c) { if (noItems(c)) { return new HashSet<E>(); } return new HashSet<E>(c); } /** * Inserts the platform-specific filesystem path separator between the * provided strings. * * @param strings * @return String in the following form: * {@code strings[0]<path_separator>strings[1]<path_separator>...<strings[n]>} * , or <b>null</b> if {@code strings} is null */ public static String asPath(final String... strings) { if (strings == null) return null; final StringBuilder bldr = new StringBuilder(); for (final String string : strings) { if (bldr.length() != 0) bldr.append(separator); bldr.append(string); } return bldr.toString(); } /** * Inserts the platform-specific filesystem path separator between * {@code directory} and {@code filename} and returns the resulting string. * * @param directory Non-null string * @param filename Non-null string * @return String following the format * {@code directory<path_separator>filename} */ public static String asPath(final String directory, final String filename) { return directory.concat(separator).concat(filename); } /** * Closes a {@link Closeable closeable resource} without reporting an * {@link IOException io error}. * <p> * This method is a no-op if {@code closeable} is {@code null}. * </p> * * @param closeable the {@link Closeable closeable} to close */ public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException e) {} } } /** * Computes a 64bit hash code of a {@link CharSequence character sequence}. * <p> * Using a 64bit hash code minimizes collisions at the cost of size. * </p> * * @param cs the {@link CharSequence character sequence} to hash * @return the 64bit hash */ public static long computeHash64(CharSequence cs) { long h = HSTART; final long hmult = HMULT; final long[] ht = byteTable; final int len = cs.length(); for (int i = 0; i < len; i++) { char ch = cs.charAt(i); h = (h * hmult) ^ ht[ch & 0xff]; h = (h * hmult) ^ ht[(ch >>> 8) & 0xff]; } return h; } /** * Computes a SHA-256 hash of data from the {@link InputStream input}. * * @param input the data {@link InputStream input stream}, which cannot be * {@code null} * @return the {@link String SHA-256 hash} * @throws IOException Thrown if an IO error occurred reading from the * {@link InputStream input} * @throws InvalidArgument Thrown if {@code input} is {@code null} */ public static String computeHashSHA256(final InputStream input) throws IOException { if (input == null) { throw new InvalidArgument("input", input); } MessageDigest sha256; try { sha256 = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new MissingAlgorithmException(SHA_256, e); } final DigestInputStream dis = new DigestInputStream(input, sha256); while (dis.read() != -1) {} byte[] mdbytes = sha256.digest(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < mdbytes.length; i++) { sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16) .substring(1)); } return sb.toString(); } /** * Concatenates the two arrays {@code a} and {@code b} and returns the * result. * * @param a {@code int[]} a * @param b {@code int[]} b * @return {@code int[]} concatenation */ public static int[] concatArrays(int[] a, int[] b) { final int alen = a.length; final int blen = b.length; if (alen == 0) { return b; } if (blen == 0) { return a; } final int[] result = new int[alen + blen]; arraycopy(a, 0, result, 0, alen); arraycopy(b, 0, result, alen, blen); return result; } /** * Returns a hash map of type {@code K, V} optimized to trade memory * efficiency for CPU time. * <p> * Use constrained hash maps when the capacity of a hash map is known to be * greater than sixteen (the default initial capacity) and the addition of * elements beyond the map's capacity will not occur. The hash map * implementation will automatically adjust the size to the next nearest * power of two. * </p> * * @param <K> Formal type parameter key * @param <V> Formal type parameter value * @param s Initial hash map capacity * @return Hash map of type {@code K, V} */ public static <K, V> HashMap<K, V> constrainedHashMap(final int s) { return new HashMap<K, V>(s, 1.0F); } /** * Returns a hash set of type {@code T} optimized to trade memory efficiency * for CPU time. * <p> * Use constrained hash sets when the capacity of a hash set is known to be * greater than sixteen (the default initial capacity) and the addition of * elements beyond the set's capacity will not occur. The hash set * implementation will automatically adjust the size to the next nearest * power of two. * </p> * * @param <T> Formal type parameter * @param s Initial hash set capacity * @return Hash set of type {@code T} */ public static <T> HashSet<T> constrainedHashSet(final int s) { return new HashSet<T>(s, 1.0F); } /** * Copy bytes from the {@link InputStream input stream} to the * {@link OutputStream output stream} in 4 kilobyte increments. * * @param input the {@link InputStream input stream} to read from, which * cannot be null * @param output the {@link OutputStream output stream} to write to, which * cannot be null * @return the number of {@code bytes} read * @throws IOException Thrown if an IO error occurred while copying data * @throws InvalidArgument Thrown if either {@code input} or {@code output} * is {@code null} */ public static long copy(final InputStream input, final OutputStream output) throws IOException { // guard against null streams if (input == null) { throw new InvalidArgument("input", input); } if (output == null) { throw new InvalidArgument("output", output); } byte[] buf = new byte[4096]; long count = 0; int bytesRead = 0; while ((bytesRead = input.read(buf)) != -1) { output.write(buf, 0, bytesRead); count += bytesRead; } return count; } /** * Copies a {@link File source file} to a {@link File destination file}. * * @param src the {@link File source file}, which must be non-null and * readable * @param dest the {@link File destination file}, which must be non-null and * writable * @throws IOException Thrown if an IO error occurred copying the * {@link File src} to {@link File dest} * @throws InvalidArgument Thrown if * <ul> * <li>{@code src} or {@code dest} is {@code null}</li> * <li>{@code src} is not readable</li> * <li>{@code dest} is not writeable</li> * </ul> */ public static void copyFile(final File src, final File dest) throws IOException { if (src == null || !src.canRead()) { throw new InvalidArgument("src", src); } if (dest == null || !dest.canWrite()) { throw new InvalidArgument("dest", dest); } // initialize streams/channels FileInputStream srcInput = null; FileChannel srcChannel = null; FileOutputStream destOutput = null; FileChannel destChannel = null; try { srcInput = new FileInputStream(src); srcChannel = srcInput.getChannel(); destOutput = new FileOutputStream(dest); destChannel = destOutput.getChannel(); // transfer from src to dest channel, one megabyte at a time long size = srcChannel.size(); long pos = 0; long count = 0; while (pos < size) { count = (size - pos) > ONE_MEGABYTE ? ONE_MEGABYTE : (size - pos); pos += destChannel.transferFrom(srcChannel, pos, count); } } finally { // close channels and streams if (destChannel != null) { destChannel.close(); } if (destOutput != null) { destOutput.close(); } if (srcChannel != null) { srcChannel.close(); } if (srcInput != null) { srcInput.close(); } } } /** * Create the provided directory, and all necessary subdirectories, if they * do not already exist. * * @param directory Path to create * @throws RuntimeException Thrown if directory creation failed */ public static void createDirectories(final String directory) { if (directory == null) return; final File f = new File(directory); if (!f.isDirectory()) { if (!f.mkdirs()) throw new RuntimeException("couldn't create " + directory); } } /** * Create the provided directory, if it does not already exist. * * @param directory Path to create * @throws RuntimeException Thrown if directory creation failed */ public static void createDirectory(final String directory) { if (directory == null) return; final File f = new File(directory); if (!f.isDirectory()) { if (!f.mkdir()) throw new RuntimeException("couldn't create " + directory); } } /** * Deletes the directory {@code dir}, and all of its contents. * * @param dir {@link File} * @return boolean {@code true} if success, {@code false} otherwise */ public static boolean deleteDirectory(final File dir) { if (!deleteDirectoryContents(dir)) { return false; } return dir.delete(); } /** * Recursively deletes all files and folders within the directory * <tt>dir</tt>. * * @param dir {@link File}, the directory to empty contents for * @return boolean determines whether or not the delete was successful, * <tt>true</tt> if success, <tt>false</tt> otherwise */ public static boolean deleteDirectoryContents(final File dir) { if (dir == null || !dir.isDirectory()) { return false; } File[] files = dir.listFiles(); for (final File file : files) { if (file.isDirectory()) { if (!deleteDirectory(file)) { return false; } } else { if (!file.delete()) { return false; } } } return true; } /** * Returns a {@link Set} view of the entries contained in this map. * <p> * This method should <strong>always</strong> be preferred instead of * iterating a maps keys and invoking {@link Map#get(Object) get()} on each * iteration. The following code should never be used: * * <pre> * <code> * Map<String, String> map = [...] * for (final String key : map.keySet()) { * String value = map.get(key); * // do something with key/value * } * </code> * </pre> * * Instead, write: * * <pre> * <code> * Map<String, String> map = [...] * for (final Entry<String, String> e : entries(map)) { * // do something with e.getKey/e.getValue * } * </code> * </pre> * * </p> * * @param m {@link Map}; may be null * @return {@link Set} of {@link java.util.Map.Entry map entries} */ public static <K, V> Set<Map.Entry<K, V>> entries(Map<K, V> m) { if (m == null) { return emptySet(); } return m.entrySet(); } /** * Extends {@code Object.equals()} to make {@code null}s equal. * * @param o1 an object * @param o2 another object * @return {@code true} if both {@code o1} and {@code o2} are null or * {@code o1} equals {@code o2} in the sense of * {@link Object#equals(Object)} . */ public static boolean equals(Object o1, Object o2) { if (o1 == null) { return o2 == null; } return o1.equals(o2); } /** * Returns the first cause (<em>primium movens</em>) for a {@link Throwable * throwable}. * * @param t the {@link Throwable throwable} * @return {@link Throwable} the first cause for the throwable, the original * throwable if this is the first cause, or <tt>null</tt> if the <tt>t</tt> * throwable was null */ public static Throwable getFirstCause(Throwable t) { if (t == null) { return null; } Throwable cause = t; while (cause.getCause() != null) { cause = cause.getCause(); } return cause; } /** * Returns the first message available in a stack trace. * <p> * This method can be used to obtain the first occurrence of * {@link Exception#getMessage() the exception message} through a series of * {@link Exception#getCause() causes}. * </p> * * @param t the {@link Throwable throwable} * @return {@link String}; may be null */ public static String getFirstMessage(Throwable t) { if (t == null) { return null; } String ret = t.getMessage(); Throwable cause = t; while (cause.getCause() != null) { cause = cause.getCause(); if (cause.getMessage() != null) { ret = cause.getMessage(); } } return ret; } /** * Returns the first cause stacktrace for a {@link Throwable throwable}. * * @param t the {@link Throwable throwable} * @return {@link Throwable} the first cause stacktrace for the throwable, * the original throwable stacktrace if this is the first cause, or * <tt>null</tt> if the <tt>t</tt> was null */ public static String getFirstStacktrace(Throwable t) { if (t == null) { return null; } Throwable cause = t; while (cause.getCause() != null) { cause = cause.getCause(); } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); try { cause.printStackTrace(pw); } finally { pw.close(); } return sw.toString(); } /** * Returns the virtual machine's process identifier. * * @return {@code int} */ public static int getPID() { if (pid == -1) { RuntimeMXBean mx = ManagementFactory.getRuntimeMXBean(); String name = mx.getName(); String token = name.split("@")[0]; pid = parseInt(token); } return pid; } /** * Returns the {@link Thread#currentThread() current thread's} identifier. * * @return {@code long} */ public static long getThreadID() { return currentThread().getId(); } /** * Returns {@code true} if the collection is non-null and non-empty, * {@code false} otherwise. * * @param c Collection, may be null * @return boolean */ public static boolean hasItems(final Collection<?> c) { return c != null && !c.isEmpty(); } /** * Returns {@code true} if the map is non-null and is non-empty, * {@code false} otherwise. * * @param <K> Captured key type * @param <V> Captured value type * @param m Map of type {@code <K, V>}, may be null * @return boolean */ public static <K, V> boolean hasItems(final Map<K, V> m) { return m != null && !m.isEmpty(); } /** * Returns {@code true} if the array is non-null and has a length greater * than zero, {@code false} otherwise. * * @param <T> Captured array type * @param t Array of type {@code <T>} * @return boolean */ public static <T> boolean hasItems(final T[] t) { return t != null && t.length > 0; } /** * Returns {@code true} if the string is non-null and non-empty, * {@code false} otherwise. * * @param s String, may be null * @return boolean */ public static boolean hasLength(final String s) { return s != null && !s.isEmpty(); } /** * Returns an array-based variant of the provided indexed map. This method * <b>should not</b> be used if any of the keys used are greater than the * size of the map. This is very useful if you have a {@link Map map} where * each value is given an index starting from zero. This is a very common * pattern in the BEL framework. * <p> * Here is an example where the use of this method would be beneficial. * Given some {@code list} of 100,000 elements, an index is assigned to each * element of the list: * * <pre> * <code> * Map<Integer, MyObject> map = sizedHashMap(list.size()); * for (int i = 0; i < list.size(); i++) { * map.put(i, list.get(i)); * } * </code> * </pre> * * A typical use of {@code map} follows: * * <pre> * <code> * for (int i = 0; i < map.size(); i++) { * MyObject m = map.get(i); * // use "m" * } * </code> * </pre> * * The problem with such code is the costly invocation to * {@link Map#get(Object)} on every iteration. This becomes very expensive * with very large collections. * </p> * <p> * Different ways of accessing the data becomes necessary (for example, * using a {@link List list} structure and calling {@link List#get(int)}, * see guidelines below). Taking this a step further, we can very easily * rewrite this map as an array and get considerable speed improvements. * * <pre> * <code> * </code> * MyObject[] indexed = index(map); * for (int i = 0; i < indexed.length; i++) { * MyObject m = indexed[i]; * // use "m" * } * </pre> * * Whatever costs you pay up front for indexing the map quickly vanish on * each iteration of the loop. * </p> * <p> * <h3>Guidelines</h3> Considering the following as a good baseline when it * comes to using large numbers of objects. * <ol> * <li>{@link Map#get(Object)} can be fast when using optimized maps and * hash functions though will usually perform worse than {@link List lists}. * <li>{@link List#get(int)} almost always beats {@link Map#get(Object)} in * performance.</li> * <li>Index access into an array always beats accessing either a * {@link List list} or {@link Map map}. * </ol> * * @param map The {@link Map map} to index. The keys used by the map * <b>should never</b> be greater than the {@link Map#size() size} of the * map. Under ideal conditions, each value {@code 0 <= x < = map.size()} * should have a key assigned. Otherwise, the resulting array will waste * memory (i.e., it will be a spare array). * @return {@code T[]} */ public static <T> T[] index(Class<T> cls, Map<Integer, T> map) { final int size = map.size(); @SuppressWarnings("unchecked") T[] ret = (T[]) newInstance(cls, size); Set<Entry<Integer, T>> entries = entries(map); for (final Entry<Integer, T> e : entries) { if (e.getKey() == null) continue; int key = e.getKey(); T value = e.getValue(); ret[key] = value; } return ret; } /** * Applies the function {@code fx} to each of the * {@link BELUtilities#entries(Map) entries} within the supplied map. * * @param map Non-null {@link Map} * @param fx Non-null {@link MapFunction} to apply */ public static <K, V> void mapfx(Map<K, V> map, MapFunction<K, V> fx) { Set<Entry<K, V>> entries = entries(map); for (final Entry<K, V> entry : entries) { K key = entry.getKey(); V value = entry.getValue(); fx.apply(key, value); } } /** * Applies the search function {@code fx} to each of the items in the * {@link Iterable iterable}, returning the first found match or null. * * @param iter {@link Iterable} * @param fx {@link SearchFunction} * @return {@code <T>} */ public static <T> T search(Iterable<T> iter, SearchFunction<T> fx) { for (final T t : iter) { if (fx.match(t)) return t; } return null; } /** * Returns true if a string contains one or more alphanumeric (i.e., the * {@code Alnum} character class) characters and nothing else. * * @param s {@link String} * @return boolean */ public static boolean isAlphanumeric(String s) { if (!hasLength(s)) { return false; } Pattern p = Pattern.compile("\\p{Alnum}+"); Matcher m = p.matcher(s); return m.matches(); } /** * Returns {@code true} if {@link String s} is numeric (i.e., the * {@code Digit} POSIX character class) characters and nothing else. * * @param s {@link String} * @return boolean */ public static boolean isNumeric(String s) { if (!hasLength(s)) { return false; } Pattern p = Pattern.compile("\\p{Digit}+"); Matcher m = p.matcher(s); return m.matches(); } /** * Returns {@code true} if {@code file} ends with * {@value PathConstants#BEL_SCRIPT_EXTENSION} or * {@value PathConstants#XBEL_EXTENSION}, {@code false} otherwise. * * @param file {@link File} * @return boolean */ public static boolean isBELDocument(final File file) { if (file == null) { return false; } return isBELDocument(file.getPath()); } /** * Returns {@code true} if {@code path} ends with * {@value PathConstants#BEL_SCRIPT_EXTENSION} or * {@value PathConstants#XBEL_EXTENSION}, {@code false} otherwise. * * @param path {@link String} path * @return boolean */ public static boolean isBELDocument(final String path) { if (path == null) { return false; } if (path.endsWith(BEL_SCRIPT_EXTENSION)) { return true; } if (path.endsWith(XBEL_EXTENSION)) { return true; } return false; } /** * Returns {@code true} if {@code file} ends with * {@value PathConstants#BEL_SCRIPT_EXTENSION}, {@code false} otherwise. * * @param file {@link File} * @return boolean */ public static boolean isBELScript(final File file) { if (file == null) { return false; } return isBELScript(file.getPath()); } /** * Returns {@code true} if {@code path} ends with * {@value PathConstants#BEL_SCRIPT_EXTENSION}, {@code false} otherwise. * * @param path {@link String} path * @return boolean */ public static boolean isBELScript(final String path) { if (path == null) { return false; } if (path.endsWith(BEL_SCRIPT_EXTENSION)) { return true; } return false; } /** * Returns {@code true} if {@code file} ends with * {@value PathConstants#XBEL_EXTENSION}, {@code false} otherwise. * * @param file {@link File} * @return boolean */ public static boolean isXBEL(final File file) { if (file == null) { return false; } return isXBEL(file.getPath()); } /** * Returns {@code true} if {@code path} ends with * {@value PathConstants#XBEL_EXTENSION}, {@code false} otherwise. * * @param path {@link String} path * @return boolean */ public static boolean isXBEL(final String path) { if (path == null) { return false; } if (path.endsWith(XBEL_EXTENSION)) { return true; } return false; } /** * Joins the elements of the provided {@link Collection collection} into a * single {@link String string}, using the provided {@code separator} and * string elements. * <p> * For example: <br> * <blockquote> {@code join(asList("foo", "bar"), "*")}<br> * </blockquote> returns<br> * <blockquote> {@code "foo*bar"} </blockquote> * </p> * * @param strings {@link String Strings} to join together * @param separator Separator {@link String string} * @return String */ public static String join(Collection<String> strings, String separator) { StringBuilder sb = new StringBuilder(); if (strings != null) { Iterator<String> i = strings.iterator(); while (i.hasNext()) { sb.append(i.next()); if (i.hasNext()) { sb.append(separator); } } } return sb.toString(); } /** * Returns {@code true} if the collection is null or empty, {@code false} * otherwise. * * @param c Collection, may be null * @return boolean */ public static boolean noItems(final Collection<?> c) { return !hasItems(c); } /** * Returns {@code true} if the map is null or empty, {@code false} * otherwise. * * @param <K> Captured key type * @param <V> Captured value type * @param m Map of type {@code <K, V>}, may be null * @return boolean */ public static <K, V> boolean noItems(final Map<K, V> m) { return !hasItems(m); } /** * Returns {@code true} if the array is null or has no elements, * {@code false} otherwise. * * @param <T> Captured array type * @param t Array of type {@code <T>}, may be null * @return boolean */ public static <T> boolean noItems(final T[] t) { return !hasItems(t); } /** * Returns {@code true} if the string is null or empty, {@code false} * otherwise. * * @param s String, may be null * @return boolean */ public static boolean noLength(final String s) { return !hasLength(s); } /** * Returns {@code true} if any {@link String} in <tt>strings</tt> is null or * empty, {@code false} otherwise. * * @param strings {@link String[]}, may be null * @return boolean */ public static boolean noLength(final String... strings) { if (strings == null) { return true; } for (final String string : strings) { if (!hasLength(string)) { return true; } } return false; } /** * Returns {@code true} if no null arguments are provided, {@code false} * otherwise. * * @param objects Objects, may be null * @return boolean */ public static boolean noNulls(final Object... objects) { if (objects == null) return false; for (final Object object : objects) { if (object == null) return false; } return true; } /** * Returns {@code true} if null arguments are provided, {@code false} * otherwise. * * @param objects Objects, may be null * @return boolean */ public static boolean nulls(final Object... objects) { if (objects == null) return true; for (final Object object : objects) { if (object == null) return true; } return false; } /** * Returns a hash map of type {@code K, V} optimized to the specified * capacity and load factor. * <p> * Use optimized hash maps when the capacity of a hash map is known to be * greater than sixteen (the default initial capacity) and a load factor is * desired to control resizing behavior. The hash map implementation will * automatically adjust the size to the next nearest power of two. * </p> * * @param <K> Formal type parameter key * @param <V> Formal type parameter value * @param s Initial hash map capacity * @param lf Hash map load factor * @return Hash map of type {@code K, V} */ public static <K, V> HashMap<K, V> optimizedHashMap(final int s, final float lf) { return new HashMap<K, V>(s, lf); } /** * Returns a hash set of type {@code T} optimized to the specified capacity * and load factor. * <p> * Use optimized hash sets when the capacity of a hash set is known to be * greater than sixteen (the default initial capacity) and a load factor is * desired to control resizing behavior. The hash set implementation will * automatically adjust the size to the next nearest power of two. * </p> * * @param <T> Formal type parameter * @param s Initial hash set capacity * @param lf Hash set load factor * @return Hash set of type {@code T} */ public static <T> HashSet<T> optimizedHashSet(final int s, final float lf) { return new HashSet<T>(s, lf); } /** * Returns {@code true} if {@code file} is non-null and can be read, * {@code false} otherwise. * * @param file File; may be null * @return boolean */ public static boolean readable(final File file) { if (file != null && file.canRead()) return true; return false; } /** * Returns a sized array list of type {@code T}. * * @param <T> Formal type parameter * @param size Array list size * @return Array list of type {@code T} */ public static <T> ArrayList<T> sizedArrayList(final int size) { return new ArrayList<T>(size); } /** * Returns a hash map of type {@code K, V} with initial capacity * {@code size}. * <p> * Use sized hash maps when the capacity of a hash map is known to be * greater than sixteen (the default initial capacity). The hash map * implementation will automatically adjust the size to the next nearest * power of two. * </p> * * @param <K> Formal type parameter key * @param <V> Formal type parameter value * @param size Hash map initial capacity * @return Hash map of type {@code K, V} */ public static <K, V> HashMap<K, V> sizedHashMap(final int size) { return new HashMap<K, V>(size); } /** * Returns a hash set of type {@code T} with initial capacity {@code size}. * <p> * Use sized hash sets when the capacity of a hash set is known to be * greater than sixteen (the default initial capacity). The hash set * implementation will automatically adjust the size to the next nearest * power of two. * </p> * * @param <T> Formal type parameter * @param size Hash set initial capacity * @return Hash set of type {@code T} */ public static <T> HashSet<T> sizedHashSet(final int size) { return new HashSet<T>(size); } /** * Check equality of two substrings. This method does not create * intermediate {@link String} objects and is roughly equivalent to: * * <pre> * <code> * String sub1 = s1.substring(fromIndex1, toIndex1); * String sub2 = s2.substring(fromIndex2, toIndex2); * sub1.equals(sub2); * </code> * </pre> * * @param s1 First string * @param fromIndex1 Starting index within {@code s1} * @param toIndex1 Ending index within {@code s1} * @param s2 Second string * @param fromIndex2 Starting index within {@code s2} * @param toIndex2 Ending index within {@code s2} * @return {@code boolean} */ public static boolean substringEquals(final String s1, final int fromIndex1, final int toIndex1, final String s2, final int fromIndex2, final int toIndex2) { if (toIndex1 < fromIndex1) { throw new IndexOutOfBoundsException(); } if (toIndex2 < fromIndex2) { throw new IndexOutOfBoundsException(); } final int len1 = toIndex1 - fromIndex1; final int len2 = toIndex2 - fromIndex2; if (len1 != len2) { return false; } for (int i = 0; i < len1; ++i) { if (s1.charAt(fromIndex1 + i) != s2.charAt(fromIndex2 + i)) { return false; } } return true; } /** * Converts into seconds and returns a string in the format * {@code <seconds>.<milliseconds>}. * * @param milliseconds * @return String */ public static String timeFormat(final long milliseconds) { double seconds = milliseconds / 1000.0d; final NumberFormat fmt = new DecimalFormat("#0.000"); return fmt.format(seconds); } /** * Captures all objects of type {@code <T>} contained in the provided list * as a new checked list. * * @param <T> Captured type for new checked list * @param objects List of objects * @param t Class type to capture * @return Checked list of type {@code T}; may be empty */ @SuppressWarnings("unchecked") public static <T> List<T> typedList(List<?> objects, Class<T> t) { if (objects == null || objects.isEmpty()) { return emptyList(); } List<T> ret = new ArrayList<T>(); for (final Object o : objects) { Class<?> oc = o.getClass(); if (oc == t || t.isAssignableFrom(oc)) { ret.add((T) o); } } return ret; } private static final long[] createLookupTable() { long[] byteTable = new long[256]; long h = 0x544B2FBACAAF1684L; for (int i = 0; i < 256; i++) { for (int j = 0; j < 31; j++) { h = (h >>> 7) ^ h; h = (h << 11) ^ h; h = (h >>> 10) ^ h; } byteTable[i] = h; } return byteTable; } /** * Retrieve an available ephemeral port. * * <p> * The number of attempts to obtain an available port is controlled by * {@code attempts}. A {@code -1} is returned if no available port could * be found in this number of attempts. * </p> * * @see <a href="http://en.wikipedia.org/wiki/Ephemeral_port">Ephemeral port</a> * @return {@code int} the available ephemeral port, or {@code -1} if an * ephemeral port could not be found. */ public static int ephemeralPort() { for (int i = MIN_EPHEMERAL_PORT; i < MAX_PORT; i++) { if (portAvailable(i)) return i; } return -1; } /** * Checks whether {@code port} is available. * * @param port the port to check, which must be between * {@link BELUtilities#MIN_PORT} and {@link BELUtilities#MAX_PORT} * @return {@code true} if the port is available for use, {@false} * otherwise */ public static boolean portAvailable(int port) { if (port < MIN_PORT || port > MAX_PORT) { throw new InvalidArgument("port is out of range"); } ServerSocket ss = null; DatagramSocket ds = null; try { ss = new ServerSocket(port); ss.setReuseAddress(true); ds = new DatagramSocket(port); ds.setReuseAddress(true); return true; } catch (IOException e) { // Do nothing } finally { if (ds != null) { ds.close(); } if (ss != null) { try { ss.close(); } catch (IOException e) { /* should not be thrown */ } } } return false; } /** * Return a quoted {@link String param} if necessary. A quoted parameter * is required in order to parse as a {@link Term BEL term} using the * {@link BELParser BEL parser}. * * @param param {@link String}; {@code null} returns {@code null} * @return quoted {@link String} if necessary, the original * {@link String param}, or {@code null} if {@code param} was {@code null} */ public static String quoteParameter(final String param) { // return null if null if (noLength(param)) return param; // return immediately if already quoted if (param.startsWith("\"") && param.endsWith("\"")) return param; // return quoted if string contains non-word character Matcher m = NON_WORD_PATTERN.matcher(param); if (m.find()) { return "\"" + param + "\""; } // return quoted if string matches a function if (_functions.contains(param)) return "\"" + param + "\""; // return quoted if string matches a relationship if (_relationships.contains(param)) return "\"" + param + "\""; if (_keywords.contains(param)) return "\"" + param + "\""; // return as is return param; } /** * Default private constructor. */ private BELUtilities() { } }