Eliminate samskivert dependency in core.
I've long wanted to do this, but the launcher's use of all the samskivert UI stuff made it onerous. Now that the launcher is a separate module, we can remove the samskivert dependency in core (and tools), which simplifies things and makes it easier to depend on core in a (future) self-updating app (when we add support for that).
This commit is contained in:
@@ -13,11 +13,6 @@
|
||||
<description>Core Getdown functionality</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.samskivert</groupId>
|
||||
<artifactId>samskivert</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
||||
@@ -5,13 +5,129 @@
|
||||
|
||||
package com.threerings.getdown;
|
||||
|
||||
import com.samskivert.util.Logger;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by the Getdown code.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static final Logger log = Logger.getLogger("com.threerings.getdown");
|
||||
public static class Shim {
|
||||
/**
|
||||
* Logs a debug message.
|
||||
*
|
||||
* @param message the message to be logged.
|
||||
* @param args a list of key/value pairs and an optional final Throwable.
|
||||
*/
|
||||
public void debug (Object message, Object... args) { doLog(0, message, args); }
|
||||
|
||||
/**
|
||||
* Logs an info message.
|
||||
*
|
||||
* @param message the message to be logged.
|
||||
* @param args a list of key/value pairs and an optional final Throwable.
|
||||
*/
|
||||
public void info (Object message, Object... args) { doLog(1, message, args); }
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
*
|
||||
* @param message the message to be logged.
|
||||
* @param args a list of key/value pairs and an optional final Throwable.
|
||||
*/
|
||||
public void warning (Object message, Object... args) { doLog(2, message, args); }
|
||||
|
||||
protected void doLog (int levIdx, Object message, Object[] args) {
|
||||
if (_impl.isLoggable(LEVELS[levIdx])) {
|
||||
Throwable err = null;
|
||||
int nn = args.length;
|
||||
if (message instanceof Throwable) {
|
||||
err = (Throwable)message;
|
||||
} else if (nn % 2 == 1 && (args[nn - 1] instanceof Throwable)) {
|
||||
err = (Throwable)args[--nn];
|
||||
}
|
||||
_impl.log(LEVELS[levIdx], format(message, args), err);
|
||||
}
|
||||
}
|
||||
|
||||
protected final Logger _impl = Logger.getLogger("com.threerings.getdown");
|
||||
}
|
||||
|
||||
/** We dispatch our log messages through this logging shim. */
|
||||
public static final Shim log = new Shim();
|
||||
|
||||
public static String format (Object message, Object... args) {
|
||||
if (args.length == 0) return String.valueOf(message);
|
||||
StringBuilder buf = new StringBuilder(String.valueOf(message));
|
||||
if (buf.length() > 0) {
|
||||
buf.append(' ');
|
||||
}
|
||||
buf.append('[');
|
||||
for (int ii = 0; ii < args.length; ii += 2) {
|
||||
if (ii > 0) {
|
||||
buf.append(',').append(' ');
|
||||
}
|
||||
buf.append(args[ii]).append('=');
|
||||
try {
|
||||
buf.append(args[ii+1]);
|
||||
} catch (Throwable t) {
|
||||
buf.append("<toString() failure: ").append(t).append(">");
|
||||
}
|
||||
}
|
||||
return buf.append(']').toString();
|
||||
}
|
||||
|
||||
static {
|
||||
Formatter formatter = new OneLineFormatter();
|
||||
Logger logger = LogManager.getLogManager().getLogger("");
|
||||
for (Handler handler : logger.getHandlers()) {
|
||||
handler.setFormatter(formatter);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class OneLineFormatter extends Formatter {
|
||||
@Override public String format (LogRecord record) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
// append the timestamp
|
||||
_date.setTime(record.getMillis());
|
||||
_format.format(_date, buf, _fpos);
|
||||
|
||||
// append the log level
|
||||
buf.append(" ");
|
||||
buf.append(record.getLevel().getLocalizedName());
|
||||
buf.append(" ");
|
||||
|
||||
// append the message itself
|
||||
buf.append(formatMessage(record));
|
||||
buf.append(System.lineSeparator());
|
||||
|
||||
// if an exception was also provided, append that
|
||||
if (record.getThrown() != null) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
record.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
buf.append(sw.toString());
|
||||
} catch (Exception ex) {
|
||||
buf.append("Format failure:").append(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected Date _date = new Date();
|
||||
protected SimpleDateFormat _format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:SSS");
|
||||
protected FieldPosition _fpos = new FieldPosition(SimpleDateFormat.DATE_FIELD);
|
||||
}
|
||||
|
||||
protected static final String DATE_FORMAT = "{0,date} {0,time}";
|
||||
protected static final Level[] LEVELS = {Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE};
|
||||
}
|
||||
|
||||
@@ -25,13 +25,6 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.samskivert.text.MessageUtil;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
import com.samskivert.util.RunAnywhere;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.classpath.ClassPaths;
|
||||
import com.threerings.getdown.classpath.ClassPath;
|
||||
import com.threerings.getdown.util.*;
|
||||
@@ -229,8 +222,8 @@ public class Application
|
||||
_appid = appid;
|
||||
_signers = (signers == null) ? Collections.<Certificate>emptyList() : signers;
|
||||
_config = getLocalPath(CONFIG_FILE);
|
||||
_extraJvmArgs = (jvmargs == null) ? ArrayUtil.EMPTY_STRING : jvmargs;
|
||||
_extraAppArgs = (appargs == null) ? ArrayUtil.EMPTY_STRING : appargs;
|
||||
_extraJvmArgs = (jvmargs == null) ? EMPTY_STRING_ARRAY : jvmargs;
|
||||
_extraAppArgs = (appargs == null) ? EMPTY_STRING_ARRAY : appargs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -952,7 +945,7 @@ public class Application
|
||||
}
|
||||
|
||||
// we love our Mac users, so we do nice things to preserve our application identity
|
||||
if (RunAnywhere.isMacOS()) {
|
||||
if (LaunchUtil.isMacOS()) {
|
||||
args.add("-Xdock:icon=" + getLocalPath(_dockIconPath).getAbsolutePath());
|
||||
args.add("-Xdock:name=" + _name);
|
||||
}
|
||||
@@ -1097,7 +1090,7 @@ public class Application
|
||||
try {
|
||||
log.info("Loading " + _class);
|
||||
Class<?> appclass = loader.loadClass(_class);
|
||||
Method main = appclass.getMethod("main", SA_PROTO.getClass());
|
||||
Method main = appclass.getMethod("main", EMPTY_STRING_ARRAY.getClass());
|
||||
log.info("Invoking main({" + StringUtil.join(args, ", ") + "})");
|
||||
main.invoke(null, new Object[] { args });
|
||||
} catch (Exception e) {
|
||||
@@ -1674,7 +1667,8 @@ public class Application
|
||||
_trackingStart = time;
|
||||
}
|
||||
if (_trackingId == 0) {
|
||||
_trackingId = RandomUtil.getInRange(100000000, 1000000000);
|
||||
int low = 100000000, high = 1000000000;
|
||||
_trackingId = low + _rando.nextInt(high-low);
|
||||
}
|
||||
StringBuilder cookie = new StringBuilder("&utmcc=__utma%3D").append(_trackingGAHash);
|
||||
cookie.append(".").append(_trackingId);
|
||||
@@ -1683,7 +1677,8 @@ public class Application
|
||||
cookie.append("__utmz%3D").append(_trackingGAHash).append(".");
|
||||
cookie.append(_trackingStart).append(".1.1.");
|
||||
cookie.append("utmcsr%3D(direct)%7Cutmccn%3D(direct)%7Cutmcmd%3D(none)%3B");
|
||||
cookie.append("&utmn=").append(RandomUtil.getInRange(1000000000, 2000000000));
|
||||
int low = 1000000000, high = 2000000000;
|
||||
cookie.append("&utmn=").append(_rando.nextInt(high-low));
|
||||
return cookie.toString();
|
||||
}
|
||||
|
||||
@@ -1764,7 +1759,9 @@ public class Application
|
||||
/** Channel to the file underlying _lock. Kept around solely so the lock doesn't close. */
|
||||
protected FileChannel _lockChannel;
|
||||
|
||||
protected static final String[] SA_PROTO = ArrayUtil.EMPTY_STRING;
|
||||
protected Random _rando = new Random();
|
||||
|
||||
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
protected static final String ENV_VAR_PREFIX = "%ENV.";
|
||||
protected static final Pattern ENV_VAR_PATTERN = Pattern.compile("%ENV\\.(.*?)%");
|
||||
|
||||
@@ -12,11 +12,10 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import com.samskivert.text.MessageUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.util.Config;
|
||||
import com.threerings.getdown.util.MessageUtil;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -16,10 +16,9 @@ import java.util.Locale;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
@@ -312,7 +311,7 @@ public class Config
|
||||
public String[] getList (String name)
|
||||
{
|
||||
String value = getString(name);
|
||||
return (value == null) ? ArrayUtil.EMPTY_STRING : StringUtil.parseStringArray(value);
|
||||
return (value == null) ? new String[0] : StringUtil.parseStringArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,7 @@ import java.util.*;
|
||||
import java.util.jar.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.samskivert.util.Logger;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
/**
|
||||
@@ -125,7 +123,7 @@ public class FileUtil
|
||||
StreamUtil.copy(jin, fout);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(
|
||||
Logger.format("Failure unpacking", "jar", jar, "entry", efile), e);
|
||||
Log.format("Failure unpacking", "jar", jar, "entry", efile), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.samskivert.util.RunAnywhere;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
/**
|
||||
@@ -111,7 +108,7 @@ public class LaunchUtil
|
||||
// from /usr/bin/java, and not if launched by directly referring to <java.home>/bin/java,
|
||||
// even though the former is a symlink to the latter! To work around this, see if the
|
||||
// desired jvm is in fact pointed to by /usr/bin/java and, if so, use that instead.
|
||||
if (RunAnywhere.isMacOS()) {
|
||||
if (isMacOS()) {
|
||||
try {
|
||||
File localVM = new File("/usr/bin/java").getCanonicalFile();
|
||||
if (localVM.equals(new File(vmpath).getCanonicalFile())) {
|
||||
@@ -188,6 +185,21 @@ public class LaunchUtil
|
||||
return (osname.indexOf("windows 98") != -1 || osname.indexOf("windows me") != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we're running in a JVM that identifies its operating system as Windows.
|
||||
*/
|
||||
public static final boolean isWindows () { return _isWindows; }
|
||||
|
||||
/**
|
||||
* Returns true if we're running in a JVM that identifies its operating system as MacOS.
|
||||
*/
|
||||
public static final boolean isMacOS () { return _isMacOS; }
|
||||
|
||||
/**
|
||||
* Returns true if we're running in a JVM that identifies its operating system as Linux.
|
||||
*/
|
||||
public static final boolean isLinux () { return _isLinux; }
|
||||
|
||||
/**
|
||||
* Checks whether a Java Virtual Machine can be located in the supplied path.
|
||||
*/
|
||||
@@ -213,4 +225,24 @@ public class LaunchUtil
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Flag indicating that we're on Windows; initialized when this class is first loaded. */
|
||||
protected static boolean _isWindows;
|
||||
/** Flag indicating that we're on MacOS; initialized when this class is first loaded. */
|
||||
protected static boolean _isMacOS;
|
||||
/** Flag indicating that we're on Linux; initialized when this class is first loaded. */
|
||||
protected static boolean _isLinux;
|
||||
|
||||
static {
|
||||
try {
|
||||
String osname = System.getProperty("os.name");
|
||||
osname = (osname == null) ? "" : osname;
|
||||
_isWindows = (osname.indexOf("Windows") != -1);
|
||||
_isMacOS = (osname.indexOf("Mac OS") != -1 ||
|
||||
osname.indexOf("MacOS") != -1);
|
||||
_isLinux = (osname.indexOf("Linux") != -1);
|
||||
} catch (Exception e) {
|
||||
// can't grab system properties; we'll just pretend we're not on any of these OSes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
public class MessageUtil {
|
||||
|
||||
/**
|
||||
* Composes a message key with an array of arguments. The message can subsequently be
|
||||
* decomposed and translated without prior knowledge of how many arguments were provided.
|
||||
*/
|
||||
public static String compose (String key, Object... args)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(key);
|
||||
buf.append('|');
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
buf.append('|');
|
||||
}
|
||||
// escape the string while adding to the buffer
|
||||
String arg = (args[i] == null) ? "" : String.valueOf(args[i]);
|
||||
int alength = arg.length();
|
||||
for (int p = 0; p < alength; p++) {
|
||||
char ch = arg.charAt(p);
|
||||
if (ch == '|') {
|
||||
buf.append("\\!");
|
||||
} else if (ch == '\\') {
|
||||
buf.append("\\\\");
|
||||
} else {
|
||||
buf.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a message with String args. This is just a convenience so callers do not have to
|
||||
* cast their String[] to an Object[].
|
||||
*/
|
||||
public static String compose (String key, String... args)
|
||||
{
|
||||
return compose(key, (Object[]) args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to "taint" any string that has been entered by an entity outside the application
|
||||
* so that the translation code knows not to attempt to translate this string when doing
|
||||
* recursive translations.
|
||||
*/
|
||||
public static String taint (Object text)
|
||||
{
|
||||
return TAINT_CHAR + text;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for calling {@link #compose(String,Object[])} with an array of
|
||||
* arguments that will be automatically tainted (see {@link #taint}).
|
||||
*/
|
||||
public static String tcompose (String key, Object... args)
|
||||
{
|
||||
int acount = args.length;
|
||||
String[] targs = new String[acount];
|
||||
for (int ii = 0; ii < acount; ii++) {
|
||||
targs[ii] = taint(args[ii]);
|
||||
}
|
||||
return compose(key, (Object[]) targs);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for calling {@link #compose(String,String[])} with an array of argument
|
||||
* that will be automatically tainted.
|
||||
*/
|
||||
public static String tcompose (String key, String... args)
|
||||
{
|
||||
for (int ii = 0, nn = args.length; ii < nn; ii++) {
|
||||
args[ii] = taint(args[ii]);
|
||||
}
|
||||
return compose(key, args);
|
||||
}
|
||||
|
||||
/** Text prefixed by this character will be considered tainted when doing recursive
|
||||
* translations and won't be translated. */
|
||||
protected static final String TAINT_CHAR = "~";
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
public class StreamUtil {
|
||||
/**
|
||||
* Convenient close for a stream. Use in a finally clause and love life.
|
||||
*/
|
||||
public static void close (InputStream in)
|
||||
{
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Error closing input stream", "stream", in, "cause", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient close for a stream. Use in a finally clause and love life.
|
||||
*/
|
||||
public static void close (OutputStream out)
|
||||
{
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Error closing output stream", "stream", out, "cause", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient close for a Reader. Use in a finally clause and love life.
|
||||
*/
|
||||
public static void close (Reader in)
|
||||
{
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Error closing reader", "reader", in, "cause", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient close for a Writer. Use in a finally clause and love life.
|
||||
*/
|
||||
public static void close (Writer out)
|
||||
{
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Error closing writer", "writer", out, "cause", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the contents of the supplied input stream to the supplied output stream.
|
||||
*/
|
||||
public static <T extends OutputStream> T copy (InputStream in, T out)
|
||||
throws IOException
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
for (int read = 0; (read = in.read(buffer)) > 0; ) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the supplied stream into a byte array.
|
||||
*/
|
||||
public static byte[] toByteArray (InputStream stream)
|
||||
throws IOException
|
||||
{
|
||||
return copy(stream, new ByteArrayOutputStream()).toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* @return true if the string is null or consists only of whitespace, false otherwise.
|
||||
*/
|
||||
public static boolean isBlank (String value)
|
||||
{
|
||||
for (int ii = 0, ll = (value == null) ? 0 : value.length(); ii < ll; ii++) {
|
||||
if (!Character.isWhitespace(value.charAt(ii))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of integers from it's string representation. The array should be represented
|
||||
* as a bare list of numbers separated by commas, for example:
|
||||
*
|
||||
* <pre>25, 17, 21, 99</pre>
|
||||
*
|
||||
* Any inability to parse the int array will result in the function returning null.
|
||||
*/
|
||||
public static int[] parseIntArray (String source)
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(source, ",");
|
||||
int[] vals = new int[tok.countTokens()];
|
||||
for (int i = 0; tok.hasMoreTokens(); i++) {
|
||||
try {
|
||||
// trim the whitespace from the token
|
||||
vals[i] = Integer.parseInt(tok.nextToken().trim());
|
||||
} catch (NumberFormatException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of strings from a single string. The array should be represented as a bare
|
||||
* list of strings separated by commas, for example:
|
||||
*
|
||||
* <pre>mary, had, a, little, lamb, and, an, escaped, comma,,</pre>
|
||||
*
|
||||
* If a comma is desired in one of the strings, it should be escaped by putting two commas in a
|
||||
* row. Any inability to parse the string array will result in the function returning null.
|
||||
*/
|
||||
public static String[] parseStringArray (String source)
|
||||
{
|
||||
return parseStringArray(source, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #parseStringArray(String)} but can be instructed to invoke {@link String#intern}
|
||||
* on the strings being parsed into the array.
|
||||
*/
|
||||
public static String[] parseStringArray (String source, boolean intern)
|
||||
{
|
||||
int tcount = 0, tpos = -1, tstart = 0;
|
||||
|
||||
// empty strings result in zero length arrays
|
||||
if (source.length() == 0) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// sort out escaped commas
|
||||
source = source.replace(",,", "%COMMA%");
|
||||
|
||||
// count up the number of tokens
|
||||
while ((tpos = source.indexOf(",", tpos+1)) != -1) {
|
||||
tcount++;
|
||||
}
|
||||
|
||||
String[] tokens = new String[tcount+1];
|
||||
tpos = -1; tcount = 0;
|
||||
|
||||
// do the split
|
||||
while ((tpos = source.indexOf(",", tpos+1)) != -1) {
|
||||
tokens[tcount] = source.substring(tstart, tpos);
|
||||
tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ",");
|
||||
if (intern) {
|
||||
tokens[tcount] = tokens[tcount].intern();
|
||||
}
|
||||
tstart = tpos+1;
|
||||
tcount++;
|
||||
}
|
||||
|
||||
// grab the last token
|
||||
tokens[tcount] = source.substring(tstart);
|
||||
tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ",");
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the supplied string if it is non-null, "" if it is null.
|
||||
*/
|
||||
public static String deNull (String value)
|
||||
{
|
||||
return (value == null) ? "" : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string from the supplied bytes that is the HEX encoded representation of those
|
||||
* bytes. Returns the empty string for a <code>null</code> or empty byte array.
|
||||
*
|
||||
* @param bytes the bytes for which we want a string representation.
|
||||
* @param count the number of bytes to stop at (which will be coerced into being {@code <=} the
|
||||
* length of the array).
|
||||
*/
|
||||
public static String hexlate (byte[] bytes, int count)
|
||||
{
|
||||
if (bytes == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
count = Math.min(count, bytes.length);
|
||||
char[] chars = new char[count*2];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int val = bytes[i];
|
||||
if (val < 0) {
|
||||
val += 256;
|
||||
}
|
||||
chars[2*i] = XLATE.charAt(val/16);
|
||||
chars[2*i+1] = XLATE.charAt(val%16);
|
||||
}
|
||||
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string from the supplied bytes that is the HEX encoded representation of those
|
||||
* bytes.
|
||||
*/
|
||||
public static String hexlate (byte[] bytes)
|
||||
{
|
||||
return (bytes == null) ? "" : hexlate(bytes, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins an array of strings (or objects which will be converted to strings) into a single
|
||||
* string separated by commas.
|
||||
*/
|
||||
public static String join (Object[] values)
|
||||
{
|
||||
return join(values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins an array of strings into a single string, separated by commas, and optionally escaping
|
||||
* commas that occur in the individual string values such that a subsequent call to {@link
|
||||
* #parseStringArray} would recreate the string array properly. Any elements in the values
|
||||
* array that are null will be treated as an empty string.
|
||||
*/
|
||||
public static String join (Object[] values, boolean escape)
|
||||
{
|
||||
return join(values, ", ", escape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the supplied array of strings into a single string separated by the supplied
|
||||
* separator.
|
||||
*/
|
||||
public static String join (Object[] values, String separator)
|
||||
{
|
||||
return join(values, separator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for the various <code>join</code> methods.
|
||||
*/
|
||||
protected static String join (Object[] values, String separator, boolean escape)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
int vlength = values.length;
|
||||
for (int i = 0; i < vlength; i++) {
|
||||
if (i > 0) {
|
||||
buf.append(separator);
|
||||
}
|
||||
String value = (values[i] == null) ? "" : values[i].toString();
|
||||
buf.append((escape) ? value.replace(",", ",,") : value);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** Used by {@link #hexlate} and {@link #unhexlate}. */
|
||||
protected static final String XLATE = "0123456789abcdef";
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import java.io.PrintStream;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.data.SysProps;
|
||||
import static com.threerings.getdown.Log.log;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.threerings.getdown.classpath;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.*;
|
||||
@@ -13,7 +14,6 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.samskivert.util.FileUtil;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
|
||||
@@ -45,14 +45,14 @@ public class ClassPathsTest
|
||||
when(_application.getDigest(_secondJar)).thenReturn("second");
|
||||
when(_application.getCodeCacheRetentionDays()).thenReturn(1);
|
||||
|
||||
File firstCachedJarFile = FileUtil.newFile(
|
||||
_appdir.getRoot(), ClassPaths.CACHE_DIR, "fi", "first.jar");
|
||||
Path firstCachedJarFile = _appdir.getRoot().toPath().
|
||||
resolve(ClassPaths.CACHE_DIR).resolve("fi").resolve("first.jar");
|
||||
|
||||
File secondCachedJarFile = FileUtil.newFile(
|
||||
_appdir.getRoot(), ClassPaths.CACHE_DIR, "se", "second.jar");
|
||||
Path secondCachedJarFile = _appdir.getRoot().toPath().
|
||||
resolve(ClassPaths.CACHE_DIR).resolve("se").resolve("second.jar");
|
||||
|
||||
String expectedClassPath = firstCachedJarFile.getAbsolutePath() + File.pathSeparator +
|
||||
secondCachedJarFile.getAbsolutePath();
|
||||
String expectedClassPath = firstCachedJarFile.toAbsolutePath() + File.pathSeparator +
|
||||
secondCachedJarFile.toAbsolutePath();
|
||||
|
||||
ClassPath classPath = ClassPaths.buildCachedClassPath(_application);
|
||||
assertEquals(expectedClassPath, classPath.asArgumentString());
|
||||
|
||||
@@ -8,8 +8,7 @@ package com.threerings.getdown.util;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.RandomUtil;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -165,6 +164,8 @@ public class ConfigTest
|
||||
|
||||
protected static String whitespace ()
|
||||
{
|
||||
return RandomUtil.getBoolean() ? " " : "";
|
||||
return _rando.nextBoolean() ? " " : "";
|
||||
}
|
||||
|
||||
protected static Random _rando = new Random();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
<artifactId>getdown-tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.samskivert</groupId>
|
||||
<artifactId>samskivert</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jregistrykey</groupId>
|
||||
<artifactId>jregistrykey</artifactId>
|
||||
|
||||
@@ -21,11 +21,11 @@ import java.util.zip.ZipEntry;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Digest;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import com.threerings.getdown.util.StreamUtil;
|
||||
|
||||
/**
|
||||
* Generates patch files between two particular revisions of an
|
||||
|
||||
@@ -15,10 +15,9 @@ import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
import com.threerings.getdown.util.StreamUtil;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user