/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eigenbase.util; import java.awt.Toolkit; import java.io.*; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.math.*; import java.net.*; import java.nio.charset.*; import java.sql.*; import java.text.*; import java.util.*; import java.util.jar.*; import java.util.logging.*; import java.util.regex.*; import javax.annotation.Nullable; import net.hydromatic.linq4j.Ord; import com.google.common.base.Function; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** * Miscellaneous utility functions. */ public class Util { private Util() {} //~ Static fields/initializers --------------------------------------------- /** * Name of the system property that controls whether the AWT work-around is * enabled. This workaround allows Farrago to load its native libraries * despite a conflict with AWT and allows applications that use AWT to * function normally. * * @see #loadLibrary(String) */ public static final String AWT_WORKAROUND_PROPERTY = "org.eigenbase.util.AWT_WORKAROUND"; /** * System-dependent newline character. * * <p>In general, you should not use this in expected results of tests. * Expected results should be the expected result on Linux (or Mac OS) using * '\n'. Apply {@link Util#toLinux(String)} to Windows actual results, if * necessary, to make them look like Linux actual.</p> */ public static final String LINE_SEPARATOR = System.getProperty("line.separator"); /** * System-dependent file separator, for example, "/" or "\." */ public static final String FILE_SEPARATOR = System.getProperty("file.separator"); /** * Datetime format string for generating a timestamp string to be used as * part of a filename. Conforms to SimpleDateFormat conventions. */ public static final String FILE_TIMESTAMP_FORMAT = "yyyy-MM-dd_HH_mm_ss"; private static boolean driversLoaded = false; /** * Regular expression for a valid java identifier which contains no * underscores and can therefore be returned intact by {@link #toJavaId}. */ private static final Pattern JAVA_ID_PATTERN = Pattern.compile("[a-zA-Z_$][a-zA-Z0-9$]*"); /** * @see #loadLibrary(String) */ private static Toolkit awtToolkit; /** * Maps classes to the map of their enum values. Uses a weak map so that * classes are not prevented from being unloaded. */ private static final LoadingCache<Class, Map<String, Enum>> ENUM_CONSTANTS = CacheBuilder.newBuilder() .weakKeys() .build( new CacheLoader<Class, Map<String, Enum>>() { @Override public Map<String, Enum> load(Class clazz) { //noinspection unchecked return enumConstants(clazz); } }); //~ Methods ---------------------------------------------------------------- /** * Does nothing with its argument. Call this method when you have a value * you are not interested in, but you don't want the compiler to warn that * you are not using it. */ public static void discard(Object o) { if (false) { discard(o); } } /** * Does nothing with its argument. Call this method when you have a value * you are not interested in, but you don't want the compiler to warn that * you are not using it. */ public static void discard(int i) { if (false) { discard(i); } } /** * Does nothing with its argument. Call this method when you have a value * you are not interested in, but you don't want the compiler to warn that * you are not using it. */ public static boolean discard(boolean b) { return b; } /** * Does nothing with its argument. Call this method when you have a value * you are not interested in, but you don't want the compiler to warn that * you are not using it. */ public static void discard(double d) { if (false) { discard(d); } } /** * Records that an exception has been caught but will not be re-thrown. If * the tracer is not null, logs the exception to the tracer. * * @param e Exception * @param logger If not null, logs exception to this logger */ public static void swallow( Throwable e, Logger logger) { if (logger != null) { logger.log(Level.FINER, "Discarding exception", e); } } /** * Returns whether two objects are equal or are both null. */ public static boolean equal( Object s0, Object s1) { if (s0 == s1) { return true; } else if (s0 == null) { return false; } else { return s0.equals(s1); } } /** * Returns whether two lists are equal to each other using shallow * comparisons. * * @param list0 First list * @param list1 Second list * @return Whether lists are same length and all of their elements are * equal using {@code ==} (may be null). */ public static <T> boolean equalShallow( List<? extends T> list0, List<? extends T> list1) { if (list0.size() != list1.size()) { return false; } for (int i = 0; i < list0.size(); i++) { if (list0.get(i) != list1.get(i)) { return false; } } return true; } /** * Combines two integers into a hash code. */ public static int hash( int i, int j) { return (i << 4) ^ j; } /** * Computes a hash code from an existing hash code and an object (which may * be null). */ public static int hash( int h, Object o) { int k = (o == null) ? 0 : o.hashCode(); return ((h << 4) | h) ^ k; } /** * Computes a hash code from an existing hash code and an array of objects * (which may be null). */ public static int hashArray( int h, Object[] a) { // The hashcode for a null array and an empty array should be different // than h, so use magic numbers. if (a == null) { return hash(h, 19690429); } if (a.length == 0) { return hash(h, 19690721); } for (int i = 0; i < a.length; i++) { h = hash(h, a[i]); } return h; } /** * Computes a hash code over var args. */ public static int hashV(Object... a) { int h = 19690721; for (Object o : a) { h = hash(h, o); } return h; } /** Computes the hash code of a {@code double} value. Equivalent to * {@link Double}{@code .hashCode(double)}, but that method was only * introduced in JDK 1.8. * * @param v Value * @return Hash code */ public static int hashCode(double v) { long bits = Double.doubleToLongBits(v); return (int) (bits ^ (bits >>> 32)); } /** * Returns a set of the elements which are in <code>set1</code> but not in * <code>set2</code>, without modifying either. */ public static <T> Set<T> minus(Set<T> set1, Set<T> set2) { if (set1.isEmpty()) { return set1; } else if (set2.isEmpty()) { return set1; } else { Set<T> set = new HashSet<T>(set1); set.removeAll(set2); return set; } } /** * Computes <code>nlogn(n)</code> using the natural logarithm (or <code> * n</code> if <code>n < {@link Math#E}</code>, so the result is never * negative. */ public static double nLogN(double d) { return (d < Math.E) ? d : (d * Math.log(d)); } /** * Prints an object using reflection. We can handle <code>null</code>; * arrays of objects and primitive values; for regular objects, we print all * public fields. */ public static void print( PrintWriter pw, Object o) { print(pw, o, 0); } public static void print( PrintWriter pw, Object o, int indent) { if (o == null) { pw.print("null"); return; } Class clazz = o.getClass(); if (o instanceof String) { printJavaString(pw, (String) o, true); } else if ( (clazz == Integer.class) || (clazz == Boolean.class) || (clazz == Character.class) || (clazz == Byte.class) || (clazz == Short.class) || (clazz == Long.class) || (clazz == Float.class) || (clazz == Double.class) || (clazz == Void.class)) { pw.print(o.toString()); } else if (clazz.isArray()) { // o is an array, but we can't cast to Object[] because it may be // an array of primitives. Object[] a; // for debug if (o instanceof Object[]) { a = (Object[]) o; discard(a); } int n = Array.getLength(o); pw.print("{"); for (int i = 0; i < n; i++) { if (i > 0) { pw.println(","); } else { pw.println(); } for (int j = 0; j < indent; j++) { pw.print("\t"); } print( pw, Array.get(o, i), indent + 1); } pw.print("}"); } else if (o instanceof Iterator) { pw.print(clazz.getName()); Iterator iter = (Iterator) o; pw.print(" {"); int i = 0; while (iter.hasNext()) { if (i++ > 0) { pw.println(","); } print( pw, iter.next(), indent + 1); } pw.print("}"); } else if (o instanceof Enumeration) { pw.print(clazz.getName()); Enumeration e = (Enumeration) o; pw.print(" {"); int i = 0; while (e.hasMoreElements()) { if (i++ > 0) { pw.println(","); } print( pw, e.nextElement(), indent + 1); } pw.print("}"); } else { pw.print(clazz.getName()); pw.print(" {"); Field[] fields = clazz.getFields(); int printed = 0; for (Field field : fields) { if (isStatic(field)) { continue; } if (printed++ > 0) { pw.println(","); } else { pw.println(); } for (int j = 0; j < indent; j++) { pw.print("\t"); } pw.print(field.getName()); pw.print("="); Object val; try { val = field.get(o); } catch (IllegalAccessException e) { throw newInternal(e); } print(pw, val, indent + 1); } pw.print("}"); } } /** * Prints a string, enclosing in double quotes (") and escaping if * necessary. For examples, <code>printDoubleQuoted(w,"x\"y",false)</code> * prints <code>"x\"y"</code>. */ public static void printJavaString( PrintWriter pw, String s, boolean nullMeansNull) { if (s == null) { if (nullMeansNull) { pw.print("null"); } else { //pw.print(""); } } else { String s1 = replace(s, "\\", "\\\\"); String s2 = replace(s1, "\"", "\\\""); String s3 = replace(s2, "\n\r", "\\n"); String s4 = replace(s3, "\n", "\\n"); String s5 = replace(s4, "\r", "\\r"); pw.print("\""); pw.print(s5); pw.print("\""); } } public static void println( PrintWriter pw, Object o) { print(pw, o, 0); pw.println(); } /** * Formats a {@link BigDecimal} value to a string in scientific notation For * example<br> * * <ul> * <li>A value of 0.00001234 would be formated as <code>1.234E-5</code></li> * <li>A value of 100000.00 would be formated as <code>1.00E5</code></li> * <li>A value of 100 (scale zero) would be formated as * <code>1E2</code></li> * </ul> * * <p>If <code>bd</code> has a precision higher than 20, this method will * truncate the output string to have a precision of 20 (no rounding will be * done, just a truncate). */ public static String toScientificNotation(BigDecimal bd) { final int truncateAt = 20; String unscaled = bd.unscaledValue().toString(); if (bd.signum() < 0) { unscaled = unscaled.substring(1); } int len = unscaled.length(); int scale = bd.scale(); int e = len - scale - 1; StringBuilder ret = new StringBuilder(); if (bd.signum() < 0) { ret.append('-'); } // do truncation unscaled = unscaled.substring( 0, Math.min(truncateAt, len)); ret.append(unscaled.charAt(0)); if (scale == 0) { // trim trailing zeroes since they aren't significant int i = unscaled.length(); while (i > 1) { if (unscaled.charAt(i - 1) != '0') { break; } --i; } unscaled = unscaled.substring(0, i); } if (unscaled.length() > 1) { ret.append("."); ret.append(unscaled.substring(1)); } ret.append("E"); ret.append(e); return ret.toString(); } /** * Replaces every occurrence of <code>find</code> in <code>s</code> with * <code>replace</code>. */ public static String replace( String s, String find, String replace) { // let's be optimistic int found = s.indexOf(find); if (found == -1) { return s; } StringBuilder sb = new StringBuilder(s.length()); int start = 0; for (;;) { for (; start < found; start++) { sb.append(s.charAt(start)); } if (found == s.length()) { break; } sb.append(replace); start += find.length(); found = s.indexOf(find, start); if (found == -1) { found = s.length(); } } return sb.toString(); } /** * Creates a file-protocol URL for the given file. */ public static URL toURL(File file) throws MalformedURLException { String path = file.getAbsolutePath(); // This is a bunch of weird code that is required to // make a valid URL on the Windows platform, due // to inconsistencies in what getAbsolutePath returns. String fs = System.getProperty("file.separator"); if (fs.length() == 1) { char sep = fs.charAt(0); if (sep != '/') { path = path.replace(sep, '/'); } if (path.charAt(0) != '/') { path = '/' + path; } } path = "file://" + path; return new URL(path); } /** * Gets a timestamp string for use in file names. The generated timestamp * string reflects the current time. */ public static String getFileTimestamp() { SimpleDateFormat sdf = new SimpleDateFormat(FILE_TIMESTAMP_FORMAT); return sdf.format(new java.util.Date()); } /** * Converts double-quoted Java strings to their contents. For example, * <code>"foo\"bar"</code> becomes <code>foo"bar</code>. */ public static String stripDoubleQuotes(String value) { assert value.charAt(0) == '"'; assert value.charAt(value.length() - 1) == '"'; String s5 = value.substring(1, value.length() - 1); String s4 = Util.replace(s5, "\\r", "\r"); String s3 = Util.replace(s4, "\\n", "\n"); String s2 = Util.replace(s3, "\\\"", "\""); String s1 = Util.replace(s2, "\\\\", "\\"); return s1; } /** * Converts an arbitrary string into a string suitable for use as a Java * identifier. * * <p>The mapping is one-to-one (that is, distinct strings will produce * distinct java identifiers). The mapping is also reversible, but the * inverse mapping is not implemented.</p> * * <p>A valid Java identifier must start with a Unicode letter, underscore, * or dollar sign ($). The other characters, if any, can be a Unicode * letter, underscore, dollar sign, or digit.</p> * * <p>This method uses an algorithm similar to URL encoding. Valid * characters are unchanged; invalid characters are converted to an * underscore followed by the hex code of the character; and underscores are * doubled.</p> * * Examples: * * <ul> * <li><code>toJavaId("foo")</code> returns <code>"foo"</code> * <li><code>toJavaId("foo bar")</code> returns <code>"foo_20_bar"</code> * <li><code>toJavaId("foo_bar")</code> returns <code>"foo__bar"</code> * <li><code>toJavaId("0bar")</code> returns <code>"_40_bar"</code> (digits * are illegal as a prefix) * <li><code>toJavaId("foo0bar")</code> returns <code>"foo0bar"</code> * </ul> */ public static String toJavaId( String s, int ordinal) { // If it's already a valid Java id (and doesn't contain any // underscores), return it unchanged. if (JAVA_ID_PATTERN.matcher(s).matches()) { // prepend "ID$" to string so it doesn't clash with java keywords return "ID$" + ordinal + "$" + s; } // Escape underscores and other undesirables. StringBuilder buf = new StringBuilder(s.length() + 10); buf.append("ID$"); buf.append(ordinal); buf.append("$"); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '_') { buf.append("__"); } else if ( (c < 0x7F) /* Normal ascii character */ && !Character.isISOControl(c) && ((i == 0) ? Character.isJavaIdentifierStart(c) : Character.isJavaIdentifierPart(c))) { buf.append(c); } else { buf.append("_"); buf.append(Integer.toString(c, 16)); buf.append("_"); } } return buf.toString(); } public static String toLinux(String s) { return s.replaceAll("\r\n", "\n"); } /** * Materializes the results of a {@link java.util.Iterator} as a {@link * java.util.List}. * * @param iter iterator to materialize * @return materialized list */ public static <T> List<T> toList(Iterator<T> iter) { List<T> list = new ArrayList<T>(); while (iter.hasNext()) { list.add(iter.next()); } return list; } static boolean isStatic(java.lang.reflect.Member member) { int modifiers = member.getModifiers(); return java.lang.reflect.Modifier.isStatic(modifiers); } /** * @return true if s==null or if s.length()==0 */ public static boolean isNullOrEmpty(String s) { return (null == s) || (s.length() == 0); } /** * Converts a list of a string, with commas between elements. * * For example, * <code>commaList(Arrays.asList({"a", "b"}))</code> * returns "a, b". * * @param list List * @return String representation of string */ public static <T> String commaList(List<T> list) { return sepList(list, ", "); } /** Converts a list of a string, with a given separator between elements. */ public static <T> String sepList(List<T> list, String sep) { final int max = list.size() - 1; switch (max) { case -1: return ""; case 0: return list.get(0).toString(); } final StringBuilder buf = new StringBuilder(); for (int i = 0;; i++) { buf.append(list.get(i)); if (i == max) { return buf.toString(); } buf.append(sep); } } /** * Returns the {@link Charset} object representing the value of {@link * SaffronProperties#defaultCharset} * * @throws java.nio.charset.IllegalCharsetNameException If the given charset * name is illegal * @throws java.nio.charset.UnsupportedCharsetException If no support for * the named charset is * available in this * instance of the Java * virtual machine */ public static Charset getDefaultCharset() { return Charset.forName(SaffronProperties.instance().defaultCharset.get()); } public static Error newInternal() { return newInternal("(unknown cause)"); } public static Error newInternal(String s) { return new AssertionError("Internal error: " + s); } public static Error newInternal(Throwable e) { return newInternal(e, "(unknown cause)"); } public static Error newInternal(Throwable e, String s) { String message = "Internal error: " + s; if (false) { // TODO re-enable this code when we're no longer throwing spurious // internal errors (which should be parse errors, for example) System.err.println(message); e.printStackTrace(System.err); } AssertionError ae = new AssertionError(message); ae.initCause(e); return ae; } /** * Retrieves messages in a exception and writes them to a string. In the * string returned, each message will appear on a different line. * * @return a non-null string containing all messages of the exception */ public static String getMessages(Throwable t) { StringBuilder sb = new StringBuilder(); for (Throwable curr = t; curr != null; curr = curr.getCause()) { String msg = ((curr instanceof EigenbaseException) || (curr instanceof SQLException)) ? curr.getMessage() : curr.toString(); if (sb.length() > 0) { sb.append("\n"); } sb.append(msg); } return sb.toString(); } /** * Returns the stack trace of a throwable. Called from native code. * * @param t Throwable * @return Stack trace */ public static String getStackTrace(Throwable t) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); pw.flush(); return sw.toString(); } /** * Checks a pre-condition. * * <p>For example, * * <pre> * /** * * @ pre x != 0 * * / * void foo(int x) { * Util.pre(x != 0, "x != 0"); * }</pre> * * @param b Result of evaluating the pre-condition. * @param description Description of the pre-condition. */ public static void pre(boolean b, String description) { if (!b) { throw newInternal("pre-condition failed: " + description); } } /** * Checks a post-condition. * * <p>For example, * * <pre> * /** * * @ post return != 0 * * / * void foo(int x) { * int res = bar(x); * Util.post(res != 0, "return != 0"); * }</pre> * * @param b Result of evaluating the pre-condition. * @param description Description of the pre-condition. */ public static void post(boolean b, String description) { if (!b) { throw newInternal("post-condition failed: " + description); } } /** * Checks an invariant. * * <p>This is similar to <code>assert</code> keyword, except that the * condition is always evaluated even if asserts are disabled. */ public static void permAssert(boolean b, String description) { if (!b) { throw newInternal("invariant violated: " + description); } } /** * Returns a {@link java.lang.RuntimeException} indicating that a particular * feature has not been implemented, but should be. * * <p>If every 'hole' in our functionality uses this method, it will be * easier for us to identity the holes. Throwing a * {@link java.lang.UnsupportedOperationException} isn't as good, because * sometimes we actually want to partially implement an API. * * <p>Example usage: * * <blockquote> * <pre><code>class MyVisitor extends BaseVisitor { * void accept(Foo foo) { * // Exception will identify which subclass forgot to override * // this method * throw Util.needToImplement(this); * } * }</code></pre> * </blockquote> * * @param o The object which was the target of the call, or null. Passing * the object gives crucial information if a method needs to be * overridden and a subclass forgot to do so. * @return an {@link UnsupportedOperationException}. */ public static RuntimeException needToImplement(Object o) { String description = null; if (o != null) { description = o.getClass().toString() + ": " + o.toString(); } throw new UnsupportedOperationException(description); } /** * Flags a piece of code as needing to be cleaned up before you check in. * * <p>Introduce a call to this method to indicate that a piece of code, or a * javadoc comment, needs work before you check in. If you have an IDE which * can easily trace references, this is an easy way to maintain a to-do * list. * * <p><strong>Checked-in code must never call this method</strong>: you must * remove all calls/references to this method before you check in. * * <p>The <code>argument</code> has generic type and determines the type of * the result. This allows you to use the method inside an expression, for * example * * <blockquote> * <pre><code>int x = Util.deprecated(0, false);</code></pre> * </blockquote> * * but the usual usage is to pass in a descriptive string. * * <h3>Examples</h3> * * <h4>Example #1: Using <code>deprecated</code> to fail if a piece of * supposedly dead code is reached</h4> * * <blockquote> * <pre><code>void foo(int x) { * if (x < 0) { * // If this code is executed, an error will be thrown. * Util.deprecated( * "no longer need to handle negative numbers", true); * bar(x); * } else { * baz(x); * } * }</code></pre> * </blockquote> * * <h4>Example #2: Using <code>deprecated</code> to comment out dead * code</h4> * * <blockquote> * <pre>if (Util.deprecated(false, false)) { * // This code will not be executed, but an error will not be thrown. * baz(); * }</pre> * </blockquote> * * @param argument Arbitrary argument to the method. * @param fail Whether to throw an exception if this method is called * @return The value of the <code>argument</code>. * @deprecated If a piece of code calls this method, it indicates that the * code needs to be cleaned up. */ public static <T> T deprecated(T argument, boolean fail) { if (fail) { throw new UnsupportedOperationException(); } return argument; } /** * Uses {@link System#loadLibrary(String)} to load a native library * correctly under mingw (Windows/Cygwin) and Linux environments. * * <p>This method also implements a work-around for applications that wish * to load AWT. AWT conflicts with some native libraries in a way that * requires AWT to be loaded first. This method checks the system property * named {@link #AWT_WORKAROUND_PROPERTY} and if it is set to "on" (default; * case-insensitive) it pre-loads AWT to avoid the conflict. * * @param libName the name of the library to load, as in {@link * System#loadLibrary(String)}. */ public static void loadLibrary(String libName) { String awtSetting = System.getProperty(AWT_WORKAROUND_PROPERTY, "on"); if ((awtToolkit == null) && awtSetting.equalsIgnoreCase("on")) { // REVIEW jvs 8-Sept-2006: workaround upon workaround. This // is required because in native code, we sometimes (see Farrago) // have to use dlopen("libfoo.so", RTLD_GLOBAL) in order for native // plugins to load correctly. But the RTLD_GLOBAL causes trouble // later if someone tries to use AWT from within the same JVM. // So... preload AWT here unless someone configured explicitly // not to do so. try { awtToolkit = Toolkit.getDefaultToolkit(); } catch (Throwable ex) { // Suppress problems so that a headless server doesn't fail on // startup. If AWT is actually needed, the same exception will // show up later, which is fine. // NOTE jvs 27-Mar-2007: If this exception occurs, we'll // retry the AWT load on each loadLibrary call. That's okay, // since there are only a few libraries and they're loaded // via static initializers. } } System.loadLibrary(libName); } /** * Returns whether an array of strings contains a given string among the * first <code>length</code> entries. * * @param a Array of strings * @param length Number of entries to search * @param s String to seek * @return Whether array contains the name */ public static boolean contains( String[] a, int length, String s) { for (int i = 0; i < length; i++) { if (a[i].equals(s)) { return true; } } return false; } /** * Reads all remaining contents from a {@link java.io.Reader} and returns * them as a string. * * @param reader reader to read from * @return reader contents as string */ public static String readAllAsString(Reader reader) throws IOException { StringBuilder sb = new StringBuilder(); char[] buf = new char[4096]; for (;;) { int n = reader.read(buf); if (n == -1) { break; } sb.append(buf, 0, n); } return sb.toString(); } /** * Closes a Jar, ignoring any I/O exception. This should only be * used in finally blocks when it's necessary to avoid throwing an exception * which might mask a real exception. * * @param jar jar to close */ public static void squelchJar(JarFile jar) { try { if (jar != null) { jar.close(); } } catch (IOException ex) { // intentionally suppressed } } /** * Closes an InputStream, ignoring any I/O exception. This should only be * used in finally blocks when it's necessary to avoid throwing an exception * which might mask a real exception. * * @param stream stream to close */ public static void squelchStream(InputStream stream) { try { if (stream != null) { stream.close(); } } catch (IOException ex) { // intentionally suppressed } } /** * Closes an OutputStream, ignoring any I/O exception. This should only be * used in finally blocks when it's necessary to avoid throwing an exception * which might mask a real exception. If you want to make sure that data has * been successfully flushed, do NOT use this anywhere else; use * stream.close() instead. * * @param stream stream to close */ public static void squelchStream(OutputStream stream) { try { if (stream != null) { stream.close(); } } catch (IOException ex) { // intentionally suppressed } } /** * Closes a Reader, ignoring any I/O exception. This should only be used in * finally blocks when it's necessary to avoid throwing an exception which * might mask a real exception. * * @param reader reader to close */ public static void squelchReader(Reader reader) { try { if (reader != null) { reader.close(); } } catch (IOException ex) { // intentionally suppressed } } /** * Closes a Writer, ignoring any I/O exception. This should only be used in * finally blocks when it's necessary to avoid throwing an exception which * might mask a real exception. If you want to make sure that data has been * successfully flushed, do NOT use this anywhere else; use writer.close() * instead. * * @param writer writer to close */ public static void squelchWriter(Writer writer) { try { if (writer != null) { writer.close(); } } catch (IOException ex) { // intentionally suppressed } } /** * Closes a Statement, ignoring any SQL exception. This should only be used * in finally blocks when it's necessary to avoid throwing an exception * which might mask a real exception. * * @param stmt stmt to close */ public static void squelchStmt(Statement stmt) { try { if (stmt != null) { stmt.close(); } } catch (SQLException ex) { // intentionally suppressed } } /** * Closes a Connection, ignoring any SQL exception. This should only be used * in finally blocks when it's necessary to avoid throwing an exception * which might mask a real exception. * * @param connection connection to close */ public static void squelchConnection(Connection connection) { try { if (connection != null) { connection.close(); } } catch (SQLException ex) { // intentionally suppressed } } /** * Trims trailing spaces from a string. * * @param s string to be trimmed * @return trimmed string */ public static String rtrim(String s) { int n = s.length() - 1; if (n >= 0) { if (s.charAt(n) != ' ') { return s; } while ((--n) >= 0) { if (s.charAt(n) != ' ') { return s.substring(0, n + 1); } } } return ""; } /** * Pads a string with spaces up to a given length. * * @param s string to be padded * @param len desired length * @return padded string */ public static String rpad(String s, int len) { if (s.length() >= len) { return s; } StringBuilder sb = new StringBuilder(s); while (sb.length() < len) { sb.append(' '); } return sb.toString(); } /** * Converts an iterable to a string. */ public static <T> String toString( Iterable<T> iterable, String start, String sep, String end) { final StringBuilder buf = new StringBuilder(); buf.append(start); for (Ord<T> ord : Ord.zip(iterable)) { if (ord.i > 0) { buf.append(sep); } buf.append(ord.e); } buf.append(end); return buf.toString(); } /** * Converts a Java timezone to POSIX format, so that the boost C++ library * can instantiate timezone objects. * * <p><a * href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html">POSIX * IEEE 1003.1</a> defines a format for timezone specifications. * * <p>The boost C++ library can read these specifications and instantiate <a * href="http://www.boost.org/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone"> * posix_time_zone</a> objects from them. The purpose of this method, * therefore, is to allow the C++ code such as the fennel calculator to use * the same notion of timezone as Java code. * * <p>The format is as follows: * * <blockquote>"std offset dst [offset],start[/time],end[/time]" * </blockquote> * * where: * * <ul> * <li>'std' specifies the abbrev of the time zone. * <li>'offset' is the offset from UTC, and takes the form * <code>[+|-]hh[:mm[:ss]] {h=0-23, m/s=0-59}</code></li> * <li>'dst' specifies the abbrev of the time zone during daylight savings * time * <li>The second offset is how many hours changed during DST. Default=1 * <li>'start' and 'end' are the dates when DST goes into (and out of) * effect.<br> * <br> * They can each be one of three forms: * * <ol> * <li>Mm.w.d {month=1-12, week=1-5 (5 is always last), day=0-6} * <li>Jn {n=1-365 Feb29 is never counted} * <li>n {n=0-365 Feb29 is counted in leap years} * </ol> * </li> * * <li>'time' has the same format as 'offset', and defaults to 02:00:00.</li> * </ul> * * <p>For example:</p> * * <ul> * <li>"PST-8PDT01:00:00,M4.1.0/02:00:00,M10.1.0/02:00:00"; or more tersely * <li>"PST-8PDT,M4.1.0,M10.1.0" * </ul> * * <p>(Real format strings do not contain spaces; they are in the above * template only for readability.) * * <p>Boost apparently diverges from the POSIX standard in how it treats the * sign of timezone offsets. The POSIX standard states '<i>If preceded by a * '-', the timezone shall be east of the Prime Meridian; otherwise, it * shall be west</i>', yet boost requires the opposite. For instance, PST * has offset '-8' above. This method generates timezone strings consistent * with boost's expectations. * * @param tz Timezone * @param verbose Whether to include fields which can be omitted because * they have their default values * @return Timezone in POSIX format (offset sign reversed, per boost's * idiosyncracies) */ public static String toPosix(TimeZone tz, boolean verbose) { StringBuilder buf = new StringBuilder(); buf.append(tz.getDisplayName(false, TimeZone.SHORT)); appendPosixTime(buf, tz.getRawOffset()); final int dstSavings = tz.getDSTSavings(); if (dstSavings == 0) { return buf.toString(); } buf.append(tz.getDisplayName(true, TimeZone.SHORT)); if (verbose || (dstSavings != 3600000)) { // POSIX allows us to omit DST offset if it is 1:00:00 appendPosixTime(buf, dstSavings); } String patternString = ".*," + "startMode=([0-9]*)," + "startMonth=([0-9]*)," + "startDay=([-0-9]*)," + "startDayOfWeek=([0-9]*)," + "startTime=([0-9]*)," + "startTimeMode=([0-9]*)," + "endMode=([0-9]*)," + "endMonth=([0-9]*)," + "endDay=([-0-9]*)," + "endDayOfWeek=([0-9]*)," + "endTime=([0-9]*)," + "endTimeMode=([0-9]*).*"; Pattern pattern = Pattern.compile(patternString); String tzString = tz.toString(); Matcher matcher = pattern.matcher(tzString); if (!matcher.matches()) { throw new AssertionError("tz.toString not of expected format: " + tzString); } int j = 0; int startMode = Integer.valueOf(matcher.group(++j)); int startMonth = Integer.valueOf(matcher.group(++j)); int startDay = Integer.valueOf(matcher.group(++j)); int startDayOfWeek = Integer.valueOf(matcher.group(++j)); int startTime = Integer.valueOf(matcher.group(++j)); int startTimeMode = Integer.valueOf(matcher.group(++j)); int endMode = Integer.valueOf(matcher.group(++j)); int endMonth = Integer.valueOf(matcher.group(++j)); int endDay = Integer.valueOf(matcher.group(++j)); int endDayOfWeek = Integer.valueOf(matcher.group(++j)); int endTime = Integer.valueOf(matcher.group(++j)); int endTimeMode = Integer.valueOf(matcher.group(++j)); appendPosixDaylightTransition( tz, buf, startMode, startDay, startMonth, startDayOfWeek, startTime, startTimeMode, verbose, false); appendPosixDaylightTransition( tz, buf, endMode, endDay, endMonth, endDayOfWeek, endTime, endTimeMode, verbose, true); return buf.toString(); } /** * Writes a daylight savings time transition to a POSIX timezone * description. * * @param tz Timezone * @param buf Buffer to append to * @param mode Transition mode * @param day Day of transition * @param month Month of transition * @param dayOfWeek Day of week of transition * @param time Time of transition in millis * @param timeMode Mode of time transition * @param verbose Verbose * @param isEnd Whether this transition is leaving DST */ private static void appendPosixDaylightTransition( TimeZone tz, StringBuilder buf, int mode, int day, int month, int dayOfWeek, int time, int timeMode, boolean verbose, boolean isEnd) { buf.append(','); int week = day; switch (mode) { case 1: // SimpleTimeZone.DOM_MODE throw Util.needToImplement(0); case 3: // SimpleTimeZone.DOW_GE_DOM_MODE // If the day is 1, 8, 15, 22, we can translate this to case 2. switch (day) { case 1: week = 1; // 1st week of month break; case 8: week = 2; // 2nd week of month break; case 15: week = 3; // 3rd week of month break; case 22: week = 4; // 4th week of month break; default: throw new AssertionError( "POSIX timezone format cannot represent " + tz); } // fall through case 2: // SimpleTimeZone.DOW_IN_MONTH_MODE buf.append('M'); buf.append(month + 1); // 1 <= m <= 12 buf.append('.'); if (week == -1) { // java represents 'last week' differently from POSIX week = 5; } buf.append(week); // 1 <= n <= 5, 5 means 'last' buf.append('.'); buf.append(dayOfWeek - 1); // 0 <= d <= 6, 0=Sunday break; case 4: // SimpleTimeZone.DOW_LE_DOM_MODE throw Util.needToImplement(0); default: throw new AssertionError("unexpected value: " + mode); } switch (timeMode) { case 0: // SimpleTimeZone.WALL_TIME break; case 1: // SimpleTimeZone.STANDARD_TIME, e.g. Australia/Sydney if (isEnd) { time += tz.getDSTSavings(); } break; case 2: // SimpleTimeZone.UTC_TIME, e.g. Europe/Paris time += tz.getRawOffset(); if (isEnd) { time += tz.getDSTSavings(); } break; } if (verbose || (time != 7200000)) { // POSIX allows us to omit the time if it is 2am (the default) buf.append('/'); appendPosixTime(buf, time); } } /** * Given a time expressed in milliseconds, append the time formatted as * "hh[:mm[:ss]]". * * @param buf Buffer to append to * @param millis Milliseconds */ private static void appendPosixTime(StringBuilder buf, int millis) { if (millis < 0) { buf.append('-'); millis = -millis; } int hours = millis / 3600000; buf.append(hours); millis -= hours * 3600000; if (millis == 0) { return; } buf.append(':'); int minutes = millis / 60000; if (minutes < 10) { buf.append('0'); } buf.append(minutes); millis -= minutes * 60000; if (millis == 0) { return; } buf.append(':'); int seconds = millis / 1000; if (seconds < 10) { buf.append('0'); } buf.append(seconds); } /** * Parses a locale string. * * <p>The inverse operation of {@link java.util.Locale#toString()}. * * @param localeString Locale string, e.g. "en" or "en_US" * @return Java locale object */ public static Locale parseLocale(String localeString) { String[] strings = localeString.split("_"); switch (strings.length) { case 1: return new Locale(strings[0]); case 2: return new Locale(strings[0], strings[1]); case 3: return new Locale(strings[0], strings[1], strings[2]); default: throw newInternal( "bad locale string '" + localeString + "'"); } } /** * Runs an external application. * * @param cmdarray command and arguments, see {@link ProcessBuilder} * @param logger if not null, command and exit status will be logged * @param appInput if not null, data will be copied to application's stdin * @param appOutput if not null, data will be captured from application's * stdout and stderr * @return application process exit value * @throws IOException * @throws InterruptedException */ public static int runApplication( String[] cmdarray, Logger logger, Reader appInput, Writer appOutput) throws IOException, InterruptedException { return runAppProcess( newAppProcess(cmdarray), logger, appInput, appOutput); } /** * Constructs a {@link ProcessBuilder} to run an external application. * * @param cmdarray command and arguments. * @return a ProcessBuilder. */ public static ProcessBuilder newAppProcess(String[] cmdarray) { // Concatenate quoted words from cmdarray. // REVIEW mb 2/24/09 Why is this needed? StringBuilder buf = new StringBuilder(); for (int i = 0; i < cmdarray.length; ++i) { if (i > 0) { buf.append(" "); } buf.append('"'); buf.append(cmdarray[i]); buf.append('"'); } String fullcmd = buf.toString(); buf.setLength(0); return new ProcessBuilder(cmdarray); } /** * Runs an external application process. * * @param pb {@link ProcessBuilder} for the application; might be returned by {@link #newAppProcess}. * @param logger if not null, command and exit status will be logged here * @param appInput if not null, data will be copied to application's stdin * @param appOutput if not null, data will be captured from application's * stdout and stderr * @return application process exit value * @throws IOException * @throws InterruptedException */ public static int runAppProcess( ProcessBuilder pb, Logger logger, Reader appInput, Writer appOutput) throws IOException, InterruptedException { pb.redirectErrorStream(true); if (logger != null) { logger.info("start process: " + pb.command()); } Process p = pb.start(); // Setup the input/output streams to the subprocess. // The buffering here is arbitrary. Javadocs strongly encourage // buffering, but the size needed is very dependent on the // specific application being run, the size of the input // provided by the caller, and the amount of output expected. // Since this method is currently used only by unit tests, // large-ish fixed buffer sizes have been chosen. If this // method becomes used for something in production, it might // be better to have the caller provide them as arguments. if (appInput != null) { OutputStream out = new BufferedOutputStream( p.getOutputStream(), 100 * 1024); int c; while ((c = appInput.read()) != -1) { out.write(c); } out.flush(); } if (appOutput != null) { InputStream in = new BufferedInputStream( p.getInputStream(), 100 * 1024); int c; while ((c = in.read()) != -1) { appOutput.write(c); } appOutput.flush(); in.close(); } p.waitFor(); int status = p.exitValue(); if (logger != null) { logger.info("exit status=" + status + " from " + pb.command()); } return status; } /** * Converts a list whose members are automatically down-cast to a given * type. * * <p>If a member of the backing list is not an instanceof <code>E</code>, * the accessing method (such as {@link List#get}) will throw a {@link * ClassCastException}. * * <p>All modifications are automatically written to the backing list. Not * synchronized. * * @param list Backing list. * @param clazz Class to cast to. * @return A list whose members are of the desired type. */ public static <E> List<E> cast(List<? super E> list, Class<E> clazz) { return new CastingList<E>(list, clazz); } /** * Converts a iterator whose members are automatically down-cast to a given * type. * * <p>If a member of the backing iterator is not an instanceof <code> * E</code>, {@link Iterator#next()}) will throw a {@link * ClassCastException}. * * <p>All modifications are automatically written to the backing iterator. * Not synchronized. * * @param iter Backing iterator. * @param clazz Class to cast to. * @return An iterator whose members are of the desired type. */ public static <E> Iterator<E> cast( final Iterator<?> iter, final Class<E> clazz) { return new Iterator<E>() { public boolean hasNext() { return iter.hasNext(); } public E next() { return clazz.cast(iter.next()); } public void remove() { iter.remove(); } }; } /** * Converts an {@link Iterable} whose members are automatically down-cast to * a given type. * * <p>All modifications are automatically written to the backing iterator. * Not synchronized. * * @param iterable Backing iterable * @param clazz Class to cast to * @return An iterable whose members are of the desired type. */ public static <E> Iterable<E> cast( final Iterable<? super E> iterable, final Class<E> clazz) { return new Iterable<E>() { public Iterator<E> iterator() { return cast(iterable.iterator(), clazz); } }; } /** * Makes a collection of untyped elements appear as a list of strictly typed * elements, by filtering out those which are not of the correct type. * * <p>The returned object is an {@link Iterable}, * which makes it ideal for use with the 'foreach' construct. For example, * * <blockquote><code>List<Number> numbers = Arrays.asList(1, 2, 3.14, * 4, null, 6E23);<br> * for (int myInt : filter(numbers, Integer.class)) {<br> *     print(i);<br> * }</code></blockquote> * * will print 1, 2, 4. * * @param iterable Iterable * @param includeFilter Class whose instances to include */ public static <E> Iterable<E> filter( final Iterable<? extends Object> iterable, final Class<E> includeFilter) { return new Iterable<E>() { public Iterator<E> iterator() { return new Filterator<E>(iterable.iterator(), includeFilter); } }; } public static <E> Collection<E> filter( final Collection<?> collection, final Class<E> includeFilter) { return new AbstractCollection<E>() { private int size = -1; public Iterator<E> iterator() { return new Filterator<E>(collection.iterator(), includeFilter); } public int size() { if (size == -1) { // Compute size. This is expensive, but the value // collection.size() is not correct since we're // filtering values. (Some java.util algorithms // call next() on the result of iterator() size() times.) int s = 0; Iterator<E> iter = iterator(); while (iter.hasNext()) { iter.next(); s++; } size = s; } return size; } }; } /** * Returns a subset of a list containing only elements of a given type. * * <p>Modifications to the list are NOT written back to the source list. * * @param list List of objects * @param includeFilter Class to filter for * @return List of objects of given class (or a subtype) */ public static <E> List<E> filter( final List<?> list, final Class<E> includeFilter) { List<E> result = new ArrayList<E>(); for (Object o : list) { if (includeFilter.isInstance(o)) { result.add(includeFilter.cast(o)); } } return result; } /** * Sorts a collection. */ public static <E extends Comparable<E>> List<E> sort(Collection<? extends E> collection) { if (collection instanceof List && isSorted((List) collection)) { // Avoid creating a copy of a list that is already sorted. //noinspection unchecked return (List) collection; } final Object[] elements = collection.toArray(); Arrays.sort(elements); //noinspection unchecked return (ImmutableList) ImmutableList.copyOf(elements); } /** Returns whether an iterable is in non-descending order. */ public static <E extends Comparable<E>> boolean isSorted(Iterable<? extends E> list) { final Iterator<? extends E> iterator = list.iterator(); if (!iterator.hasNext()) { return true; } E e = iterator.next(); for (;;) { if (!iterator.hasNext()) { return true; } E next = iterator.next(); if (e.compareTo(next) > 0) { return false; } e = next; } } /** Returns whether an iterable is in ascending order. */ public static <E extends Comparable<E>> boolean isStrictlySorted(Iterable<? extends E> list) { final Iterator<? extends E> iterator = list.iterator(); if (!iterator.hasNext()) { return true; } E e = iterator.next(); for (;;) { if (!iterator.hasNext()) { return true; } E next = iterator.next(); if (e.compareTo(next) >= 0) { return false; } e = next; } } /** * Converts a {@link Properties} object to a <code>{@link Map}<String, * String></code>. * * <p>This is necessary because {@link Properties} is a dinosaur class. It * ought to extend <code>Map<String,String></code>, but instead * extends <code>{@link Hashtable}<Object,Object></code>. * * <p>Typical usage, to iterate over a {@link Properties}: * * <blockquote> * <code> * Properties properties;<br> * for (Map.Entry<String, String> entry = * Util.toMap(properties).entrySet()) {<br> * println("key=" + entry.getKey() + ", value=" + entry.getValue());<br> * } * </code> * </blockquote> */ public static Map<String, String> toMap( final Properties properties) { return (Map) properties; } /** * Returns a hashmap with given contents. * * <p>Use this method in initializers. Type parameters are inferred from * context, and the contents are initialized declaratively. For example, * * <blockquote><code>Map<String, Integer> population =<br> *   Olap4jUtil.mapOf(<br> *     "UK", 65000000,<br> *     "USA", 300000000);</code></blockquote> * * @param key First key * @param value First value * @param keyValues Second and sequent key/value pairs * @param <K> Key type * @param <V> Value type * @return Map with given contents */ public static <K, V> Map<K, V> mapOf(K key, V value, Object... keyValues) { final Map<K, V> map = new LinkedHashMap<K, V>(1 + keyValues.length); map.put(key, value); for (int i = 0; i < keyValues.length;) { //noinspection unchecked map.put((K) keyValues[i++], (V) keyValues[i++]); } return map; } /** * Returns an exception indicating that we didn't expect to find this * enumeration here. * * @param value Enumeration value which was not expected * @return an error, to be thrown */ public static <E extends Enum<E>> Error unexpected(E value) { return new AssertionError( "Was not expecting value '" + value + "' for enumeration '" + value.getDeclaringClass().getName() + "' in this context"); } /** * Creates a map of the values of an enumeration by name. * * @param clazz Enumeration class * @return map of values */ public static <T extends Enum<T>> Map<String, T> enumConstants( Class<T> clazz) { final T[] ts = clazz.getEnumConstants(); if (ts == null) { // not an enum type return null; } ImmutableMap.Builder<String, T> builder = ImmutableMap.builder(); for (T t : ts) { builder.put(t.name(), t); } return builder.build(); } /** * Returns the value of an enumeration with a particular name. * * <p>Similar to {@link Enum#valueOf(Class, String)}, but returns {@code * null} rather than throwing {@link IllegalArgumentException}. * * @param clazz Enum class * @param name Name of enum constant * @param <T> Enum class type * @return Enum constant or null */ @SuppressWarnings({"unchecked" }) public static synchronized <T extends Enum<T>> T enumVal( Class<T> clazz, String name) { return (T) ENUM_CONSTANTS.getUnchecked(clazz).get(name); } /** * Creates a list that returns every {@code n}th element of a list, * starting at element {@code k}. * * <p>It is OK if the list is empty or its size is not a multiple of * {@code n}.</p> * * <p>For instance, {@code quotientList(list, 2, 0)} returns the even * elements of a list, and {@code quotientList(list, 2, 1)} returns the odd * elements. Those lists are the same length only if list has even size.</p> */ public static <E> List<E> quotientList( final List<E> list, final int n, final int k) { if (n <= 0 || k < 0 || k >= n) { throw new IllegalArgumentException( "n must be positive; k must be between 0 and n - 1"); } final int size = (list.size() + n - k - 1) / n; return new AbstractList<E>() { public E get(int index) { return list.get(index * n + k); } public int size() { return size; } }; } public static <T> T first(T t0, T t1) { return t0 != null ? t0 : t1; } public static <T> Iterable<T> orEmpty(Iterable<T> t0) { return t0 != null ? t0 : ImmutableList.<T>of(); } /** Returns the last element of a list. * * @throws java.lang.IndexOutOfBoundsException if the list is empty */ public static <E> E last(List<E> list) { return list.get(list.size() - 1); } /** Returns every element of a list but its last element. */ public static <E> List<E> skipLast(List<E> list) { return skipLast(list, 1); } /** Returns every element of a list but its last {@code n} elements. */ public static <E> List<E> skipLast(List<E> list, int n) { return list.subList(0, list.size() - n); } /** Returns the last {@code n} elements of a list. */ public static <E> List<E> last(List<E> list, int n) { return list.subList(list.size() - n, list.size()); } /** Returns all but the first element of a list. */ public static <E> List<E> skip(List<E> list) { return skip(list, 1); } /** Returns all but the first {@code n} elements of a list. */ public static <E> List<E> skip(List<E> list, int fromIndex) { return list.subList(fromIndex, list.size()); } public static List<Integer> range(final int end) { return new AbstractList<Integer>() { public int size() { return end; } public Integer get(int index) { return index; } }; } public static List<Integer> range(final int start, final int end) { return new AbstractList<Integer>() { public int size() { return end - start; } public Integer get(int index) { return start + index; } }; } /** * Converts an underscore-separated name into a camelCase name. * For example, {@code uncamel("MY_JDBC_DRIVER")} returns "myJdbcDriver". */ public static String toCamelCase(String name) { StringBuilder buf = new StringBuilder(); int nextUpper = -1; for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if (c == '_') { nextUpper = i + 1; continue; } if (nextUpper == i) { c = Character.toUpperCase(c); } else { c = Character.toLowerCase(c); } buf.append(c); } return buf.toString(); } /** * Converts a camelCase name into an upper-case underscore-separated name. * For example, {@code camelToUpper("myJdbcDriver")} returns * "MY_JDBC_DRIVER". */ public static String camelToUpper(String name) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if (Character.isUpperCase(c)) { buf.append('_'); } else { c = Character.toUpperCase(c); } buf.append(c); } return buf.toString(); } /** * Returns whether the elements of {@code list} are distinct. */ public static <E> boolean isDistinct(List<E> list) { return firstDuplicate(list) < 0; } /** * Returns the ordinal of the first element in the list which is equal to a * previous element in the list. * * <p>For example, <code>firstDuplicate(Arrays.asList("a", "b", "c", "b", * "a"))</code> returns 3, the ordinal of the 2nd "b". * * @param list List * @return Ordinal of first duplicate, or -1 if not found */ public static <E> int firstDuplicate(List<E> list) { final int size = list.size(); if (size < 2) { // Lists of size 0 and 1 are always distinct. return -1; } if (size < 15) { // For smaller lists, avoid the overhead of creating a set. Threshold // determined empirically using UtilTest.testIsDistinctBenchmark. for (int i = 1; i < size; i++) { E e = list.get(i); for (int j = i - 1; j >= 0; j--) { E e1 = list.get(j); if (equal(e, e1)) { return i; } } } return -1; } final Map<E, Object> set = new HashMap<E, Object>(size); for (E e : list) { if (set.put(e, "") != null) { return set.size(); } } return -1; } public static int match2(List<String> strings, String name, boolean caseSensitive1) { if (caseSensitive1) { return strings.indexOf(name); } for (int i = 0; i < strings.size(); i++) { String s = strings.get(i); if (s.equalsIgnoreCase(name)) { return i; } } return -1; } public static boolean match(boolean caseSensitive, String name1, String name0) { return caseSensitive ? name0.equals(name1) : name0.equalsIgnoreCase(name1); } /** Returns whether one list is a prefix of another. */ public static <E> boolean startsWith(List<E> list0, List<E> list1) { return list0.equals(list1) || list0.size() > list1.size() && list0.subList(0, list1.size()).equals(list1); } /** Converts a number into human-readable form, with 3 digits and a "K", "M" * or "G" multiplier for thousands, millions or billions. * * <p>Examples: -2, 0, 1, 999, 1.00K, 1.99K, 3.45M, 4.56B.</p> */ public static String human(double d) { if (d == 0d) { return "0"; } if (d < 0d) { return "-" + human(-d); } final int digitCount = (int) Math.floor(Math.log10(d)); switch (digitCount) { case 0: case 1: case 2: return Integer.toString((int) d); case 3: case 4: case 5: return digits3(Math.round(d / 10D), digitCount % 3) + "K"; case 6: case 7: case 8: return digits3(Math.round(d / 10000D), digitCount % 3) + "M"; case 9: case 10: case 11: return digits3(Math.round(d / 10000000D), digitCount % 3) + "G"; default: return Double.toString(d); } } private static String digits3(long x, int z) { final String s = Long.toString(x); switch (z) { case 0: return s.charAt(0) + "." + s.substring(1, 3); case 1: return s.substring(0, 2) + "." + s.substring(2, 3); default: return s.substring(0, 3); } } /** Returns a map that is a view onto a collection of values, using the * provided function to convert a value to a key. * * <p>Unlike * {@link com.google.common.collect.Maps#uniqueIndex(Iterable, com.google.common.base.Function)}, * returns a view whose contents change as the collection of values changes. * * @param values Collection of values * @param function Function to map value to key * @param <K> Key type * @param <V> Value type * @return Map that is a view onto the values */ public static <K, V> Map<K, V> asIndexMap( final Collection<V> values, final Function<V, K> function) { final Collection<Map.Entry<K, V>> entries = Collections2.transform(values, new Function<V, Map.Entry<K, V>>() { public Map.Entry<K, V> apply(@Nullable V input) { return Pair.of(function.apply(input), input); } }); final Set<Map.Entry<K, V>> entrySet = new AbstractSet<Map.Entry<K, V>>() { public Iterator<Map.Entry<K, V>> iterator() { return entries.iterator(); } public int size() { return entries.size(); } }; return new AbstractMap<K, V>() { public Set<Entry<K, V>> entrySet() { return entrySet; } }; } //~ Inner Classes ---------------------------------------------------------- /** * Exception used to interrupt a tree walk of any kind. */ public static class FoundOne extends ControlFlowException { private final Object node; /** Singleton instance. Can be used if you don't care about node. */ public static final FoundOne NULL = new FoundOne(null); public FoundOne(Object node) { this.node = node; } public Object getNode() { return node; } } } // End Util.java