package org.basex.util;
import static org.basex.core.Text.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import org.basex.io.*;
import org.basex.query.*;
import org.basex.util.list.*;
/**
* This class contains static methods, which are used throughout the project.
* The methods are used for dumping error output, debugging information,
* getting the application path, etc.
*
* @author BaseX Team 2005-17, BSD License
* @author Christian Gruen
*/
public final class Util {
/** Flag for using default standard input. */
private static final boolean NOCONSOLE = System.console() == null;
/** Hidden constructor. */
private Util() { }
/**
* Returns an information string for an unexpected exception.
* @param throwable exception
* @return dummy object
*/
public static String bug(final Throwable throwable) {
final TokenBuilder tb = new TokenBuilder(S_BUGINFO);
tb.add(NL).add("Contact: ").add(Prop.MAILING_LIST);
tb.add(NL).add("Version: ").add(Prop.TITLE);
tb.add(NL).add("Java: ").add(System.getProperty("java.vendor"));
tb.add(", ").add(System.getProperty("java.version"));
tb.add(NL).add("OS: ").add(System.getProperty("os.name"));
tb.add(", ").add(System.getProperty("os.arch"));
tb.add(NL).add("Stack Trace: ");
for(final String e : toArray(throwable)) tb.add(NL).add(e);
return tb.toString();
}
/**
* Throws a runtime exception for an unexpected exception.
* @return runtime exception (indicates that an error is raised)
*/
public static RuntimeException notExpected() {
return notExpected("Not Expected.");
}
/**
* Throws a runtime exception for an unexpected exception.
* @param message message
* @param ext optional extension
* @return runtime exception (indicates that an error is raised)
*/
public static RuntimeException notExpected(final Object message, final Object... ext) {
return new RuntimeException(info(message, ext));
}
/**
* Returns the class name of the specified object, excluding its path.
* @param object object
* @return class name
*/
public static String className(final Object object) {
return className(object.getClass());
}
/**
* Returns the name of the specified class, excluding its path.
* @param clazz class
* @return class name
*/
public static String className(final Class<?> clazz) {
return clazz.getSimpleName();
}
/**
* Returns a single line from standard input.
* @return string
*/
public static String input() {
final Scanner sc = new Scanner(System.in);
return sc.hasNextLine() ? sc.nextLine().trim() : "";
}
/**
* Returns a password from standard input.
* @return password or empty string
*/
public static String password() {
// use standard input if no console if defined (such as in Eclipse)
if(NOCONSOLE) return input();
// hide password
final char[] pw = System.console().readPassword();
return pw != null ? new String(pw) : "";
}
/**
* Prints a newline to standard output.
*/
public static void outln() {
out(NL);
}
/**
* Prints a string to standard output, followed by a newline.
* @param string output string
* @param ext text optional extensions
*/
public static void outln(final Object string, final Object... ext) {
out((string instanceof byte[] ? Token.string((byte[]) string) : string) + NL, ext);
}
/**
* Prints a string to standard output.
* @param string output string
* @param ext text optional extensions
*/
public static void out(final Object string, final Object... ext) {
System.out.print(info(string, ext));
}
/**
* Returns the root query exception.
* @param throwable throwable
* @return root exception
*/
public static Throwable rootException(final Throwable throwable) {
Throwable th = throwable;
while(true) {
final Throwable ca = th.getCause();
if(ca == null || th instanceof QueryException && !(ca instanceof QueryException)) return th;
debug(th);
th = ca;
}
}
/**
* Prints a string to standard error, followed by a newline.
* @param object error object
* @param ext text optional extensions
*/
public static void errln(final Object object, final Object... ext) {
err((object instanceof Throwable ? message((Throwable) object) : object) + NL, ext);
}
/**
* Prints a string to standard error.
* @param string debug string
* @param ext text optional extensions
*/
public static void err(final String string, final Object... ext) {
System.err.print(info(string, ext));
}
/**
* Returns a more user-friendly error message for the specified exception.
* @param throwable throwable reference
* @return error message
*/
public static String message(final Throwable throwable) {
debug(throwable);
if(throwable instanceof BindException) return SRV_RUNNING;
if(throwable instanceof ConnectException) return CONNECTION_ERROR;
if(throwable instanceof SocketTimeoutException) return TIMEOUT_EXCEEDED;
if(throwable instanceof SocketException) return CONNECTION_ERROR;
String msg = throwable.getMessage();
if(msg == null || msg.isEmpty() || throwable instanceof RuntimeException)
msg = throwable.toString();
if(throwable instanceof FileNotFoundException) return info(RES_NOT_FOUND_X, msg);
if(throwable instanceof UnknownHostException) return info(UNKNOWN_HOST_X, msg);
return msg;
}
/**
* Prints the exception stack trace if the {@link Prop#debug} flag is set.
* @param throwable exception
*/
public static void debug(final Throwable throwable) {
if(Prop.debug && throwable != null) stack(throwable);
}
/**
* Prints a string to standard error if the {@link Prop#debug} flag is set.
* @param string debug string
* @param ext text optional extensions
*/
public static void debug(final Object string, final Object... ext) {
if(Prop.debug) errln(string, ext);
}
/**
* Returns a string and replaces all % characters by the specified extensions
* (see {@link TokenBuilder#addExt} for details).
* @param string string to be extended
* @param ext text text extensions
* @return extended string
*/
public static String info(final Object string, final Object... ext) {
return Token.string(inf(string, ext));
}
/**
* Returns a token and replaces all % characters by the specified extensions
* (see {@link TokenBuilder#addExt} for details).
* @param string string to be extended
* @param ext text text extensions
* @return token
*/
public static byte[] inf(final Object string, final Object... ext) {
return new TokenBuilder().addExt(string, ext).finish();
}
/**
* Prints the current stack trace to System.err.
* @param message error message
*/
public static void stack(final String message) {
stack(message, Short.MAX_VALUE);
}
/**
* Prints the current stack trace to System.err.
* @param depth number of steps to print
*/
public static void stack(final int depth) {
stack("You're here:", depth);
}
/**
* Prints the current stack trace to System.err.
* @param message message
* @param depth number of steps to print
*/
private static void stack(final String message, final int depth) {
errln(message);
final String[] stack = toArray(new Throwable());
final int l = Math.min(Math.max(2, depth + 2), stack.length);
for(int s = 2; s < l; ++s) errln(stack[s]);
}
/**
* Prints the stack of the specified error to standard error.
* @param throwable error/exception instance
*/
public static void stack(final Throwable throwable) {
throwable.printStackTrace();
}
/**
* Returns an string array representation of the specified throwable.
* @param throwable throwable
* @return string array
*/
private static String[] toArray(final Throwable throwable) {
final StackTraceElement[] st = throwable.getStackTrace();
final int sl = st.length;
final String[] obj = new String[sl + 1];
obj[0] = throwable.toString();
for(int s = 0; s < sl; s++) obj[s + 1] = "\tat " + st[s];
return obj;
}
/**
* Starts the specified class in a separate process.
* @param clazz class to start
* @param args command-line arguments
* @return reference to a {@link Process} instance representing the started process
*/
public static Process start(final Class<?> clazz, final String... args) {
final String[] largs = { "java", "-Xmx" + Runtime.getRuntime().maxMemory(),
"-cp", System.getProperty("java.class.path") };
final StringList sl = new StringList().add(largs);
for(final Entry<Object, Object> o : System.getProperties().entrySet()) {
final String k = o.getKey().toString();
if(k.startsWith(Prop.DBPREFIX)) sl.add("-D" + k + '=' + o.getValue());
}
sl.add(clazz.getName()).add("-D").add(args);
try {
return new ProcessBuilder(sl.finish()).start();
} catch(final IOException ex) {
throw notExpected(ex);
}
}
/**
* Returns the error message of a process.
* @param process process
* @param timeout time out in milliseconds
* @return error message, or {@code null}
*/
public static String error(final Process process, final int timeout) {
final int wait = 200, to = Math.max(timeout / wait, 1);
for(int c = 0; c < to; c++) {
try {
final int exit = process.exitValue();
if(exit == 1) return Token.string(new IOStream(process.getErrorStream()).read());
break;
} catch(final IllegalThreadStateException ex) {
// process is still running
Performance.sleep(wait);
} catch(final IOException ex) {
// return error message of exception
return ex.getLocalizedMessage();
}
}
return null;
}
}