The illustrious beginnings of Getdown.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<!-- build configuration -->
|
||||
<project name="getdown" default="compile" basedir=".">
|
||||
|
||||
<!-- various basic settings -->
|
||||
<property name="app.name" value="getdown"/>
|
||||
<property name="src.dir" value="src/java"/>
|
||||
<property name="deploy.dir" value="dist"/>
|
||||
<property name="dist.jar" value="${app.name}.jar"/>
|
||||
|
||||
<!-- we want to access the environment -->
|
||||
<property environment="env"/>
|
||||
<property name="java.libraries" value="${env.JAVA_LIBS}"/>
|
||||
|
||||
<!-- used when generating javadocs -->
|
||||
<property name="javadoc.dir" value="${deploy.dir}/docs"/>
|
||||
<property name="doc.packages" value="com.threerings.getdown.*"/>
|
||||
<property name="doc.overview" value="com/threerings/getdown/overview.html"/>
|
||||
<property name="copy.pre" value="Copyright © 2004"/>
|
||||
<property name="copyright.holder" value="Three Rings Design, Inc."/>
|
||||
<property name="copy.post" value="All Rights Reserved."/>
|
||||
|
||||
<!-- declare our classpath -->
|
||||
<path id="clazzpath">
|
||||
<fileset dir="${java.libraries}" includesfile="lib/LIBS"/>
|
||||
<fileset dir="lib" includes="**/*.jar"/>
|
||||
<pathelement location="${deploy.dir}/classes"/>
|
||||
</path>
|
||||
|
||||
<!-- prepares the application directories -->
|
||||
<target name="prepare">
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<mkdir dir="${deploy.dir}/classes"/>
|
||||
<mkdir dir="${javadoc.dir}"/>
|
||||
<!-- copy media and properties into the target directory -->
|
||||
<copy todir="${deploy.dir}/classes">
|
||||
<fileset dir="${src.dir}" includes="**/*.jpg"/>
|
||||
<fileset dir="${src.dir}" includes="**/*.png"/>
|
||||
<fileset dir="${src.dir}" includes="**/*.properties"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- cleans out the built application -->
|
||||
<target name="clean">
|
||||
<delete dir="${deploy.dir}"/>
|
||||
</target>
|
||||
|
||||
<!-- build the java class files -->
|
||||
<target name="compile" depends="prepare">
|
||||
<javac srcdir="${src.dir}" destdir="${deploy.dir}/classes"
|
||||
debug="on" optimize="off" deprecation="on"
|
||||
classpathref="clazzpath" includeAntRuntime="no"/>
|
||||
</target>
|
||||
|
||||
<!-- build the javadoc documentation -->
|
||||
<target name="javadoc" depends="prepare">
|
||||
<javadoc sourcepath="${src.dir}"
|
||||
packagenames="${doc.packages}"
|
||||
windowtitle="${app.name} API"
|
||||
doctitle="${app.name} API"
|
||||
overview="${src.dir}/${doc.overview}"
|
||||
bottom="${copy.pre} ${copyright.holder} ${copy.post}"
|
||||
destdir="${javadoc.dir}">
|
||||
<classpath refid="clazzpath"/>
|
||||
<link href="http://www.samskivert.com/code/samskivert/javadoc"/>
|
||||
<link href="http://java.sun.com/products/jdk/1.4/docs/api"/>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<!-- a target for rebuilding everything -->
|
||||
<target name="all" depends="clean,prepare,compile,javadoc,dist"/>
|
||||
|
||||
<!-- builds our distribution files -->
|
||||
<target name="dist" depends="prepare,compile">
|
||||
<jar destfile="${deploy.dir}/${dist.jar}" manifest="lib/manifest.mf"
|
||||
basedir="${deploy.dir}/classes"/>
|
||||
</target>
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
Main-Class: com.threerings.getdown.launcher.Getdown
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// $Id: Log.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the Getdown code.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("getdown");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// $Id: Application.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.io.NestableIOException;
|
||||
import com.samskivert.text.MessageUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.util.ConfigUtil;
|
||||
|
||||
/**
|
||||
* Parses and provide access to the information contained in the
|
||||
* <code>getdown.txt</code> configuration file.
|
||||
*/
|
||||
public class Application
|
||||
{
|
||||
/** The name of our configuration file. */
|
||||
public static final String CONFIG_FILE = "getdown.txt";
|
||||
|
||||
/**
|
||||
* Creates an application instance which records the location of the
|
||||
* <code>getdown.txt</code> configuration file from the supplied
|
||||
* application directory.
|
||||
*/
|
||||
public Application (File appdir)
|
||||
{
|
||||
_appdir = appdir;
|
||||
_config = new File(appdir, CONFIG_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource that refers to the application configuration
|
||||
* file itself.
|
||||
*/
|
||||
public Resource getConfigResource ()
|
||||
{
|
||||
try {
|
||||
return new Resource(_appbase, _appdir, CONFIG_FILE);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Booched appbase '" + _appbase + "'!?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the code {@link Resource} objects used by this
|
||||
* application.
|
||||
*/
|
||||
public List getCodeResources ()
|
||||
{
|
||||
return _codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the non-code {@link Resource} objects used by
|
||||
* this application.
|
||||
*/
|
||||
public List getResources ()
|
||||
{
|
||||
return _resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the application to parse its <code>getdown.txt</code>
|
||||
* configuration and prepare itself for operation. The application
|
||||
* base URL will be parsed first so that if there are errors
|
||||
* discovered later, the caller can use the application base to
|
||||
* download a new <code>config.txt</code> file and try again.
|
||||
*
|
||||
* @exception IOException thrown if there is an error reading the file
|
||||
* or an error encountered during its parsing.
|
||||
*/
|
||||
public void init ()
|
||||
throws IOException
|
||||
{
|
||||
// parse our configuration file
|
||||
HashMap cdata = ConfigUtil.parseConfig(_config);
|
||||
|
||||
// first determine our application base, this way if anything goes
|
||||
// wrong later in the process, our caller can use the appbase to
|
||||
// download a new configuration file
|
||||
String appbase = (String)cdata.get("appbase");
|
||||
if (appbase == null) {
|
||||
throw new IOException("m.missing_appbase");
|
||||
}
|
||||
try {
|
||||
// make sure there's a trailing slash
|
||||
if (!appbase.endsWith("/")) {
|
||||
appbase = appbase + "/";
|
||||
}
|
||||
_appbase = new URL(appbase);
|
||||
} catch (Exception e) {
|
||||
String err = MessageUtil.tcompose("m.invalid_appbase", appbase);
|
||||
throw new NestableIOException(err, e);
|
||||
}
|
||||
|
||||
// extract our version information
|
||||
String vstr = (String)cdata.get("version");
|
||||
if (vstr != null) {
|
||||
try {
|
||||
_version = Integer.parseInt(vstr);
|
||||
} catch (Exception e) {
|
||||
String err = MessageUtil.tcompose("m.invalid_version", vstr);
|
||||
throw new NestableIOException(err, e);
|
||||
}
|
||||
}
|
||||
|
||||
// determine our application class name
|
||||
_class = (String)cdata.get("class");
|
||||
if (_class == null) {
|
||||
throw new IOException("m.missing_class");
|
||||
}
|
||||
|
||||
// parse our code resources
|
||||
String[] codes = ConfigUtil.getMultiValue(cdata, "code");
|
||||
if (codes == null) {
|
||||
throw new IOException("m.missing_code");
|
||||
}
|
||||
for (int ii = 0; ii < codes.length; ii++) {
|
||||
try {
|
||||
_codes.add(new Resource(_appbase, _appdir, codes[ii]));
|
||||
} catch (Exception e) {
|
||||
Log.warning("Invalid code resource '" + codes[ii] + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
// parse our non-code resources
|
||||
String[] rsrcs = ConfigUtil.getMultiValue(cdata, "resource");
|
||||
if (rsrcs != null) {
|
||||
for (int ii = 0; ii < rsrcs.length; ii++) {
|
||||
try {
|
||||
_resources.add(new Resource(_appbase, _appdir, rsrcs[ii]));
|
||||
} catch (Exception e) {
|
||||
Log.warning("Invalid resource '" + rsrcs[ii] + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transfer our JVM arguments
|
||||
String[] jvmargs = ConfigUtil.getMultiValue(cdata, "jvmarg");
|
||||
if (jvmargs != null) {
|
||||
for (int ii = 0; ii < jvmargs.length; ii++) {
|
||||
_jvmargs.add(jvmargs[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
// transfer our application arguments
|
||||
String[] appargs = ConfigUtil.getMultiValue(cdata, "apparg");
|
||||
if (appargs != null) {
|
||||
for (int ii = 0; ii < appargs.length; ii++) {
|
||||
_appargs.add(appargs[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
// Log.info("Parsed application " + _appbase);
|
||||
// Log.info("Version: " + _version);
|
||||
// Log.info("Class: " + _class);
|
||||
// Log.info("Code: " + StringUtil.toString(_codes.iterator()));
|
||||
// Log.info("Resources: " + StringUtil.toString(_resources.iterator()));
|
||||
// Log.info("JVM Args: " + StringUtil.toString(_jvmargs.iterator()));
|
||||
// Log.info("App Args: " + StringUtil.toString(_appargs.iterator()));
|
||||
}
|
||||
|
||||
protected File _appdir;
|
||||
protected File _config;
|
||||
|
||||
protected int _version = -1;
|
||||
protected URL _appbase;
|
||||
protected String _class;
|
||||
|
||||
protected ArrayList _codes = new ArrayList();
|
||||
protected ArrayList _resources = new ArrayList();
|
||||
|
||||
protected ArrayList _jvmargs = new ArrayList();
|
||||
protected ArrayList _appargs = new ArrayList();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// $Id: Digest.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.text.MessageUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.util.ConfigUtil;
|
||||
|
||||
/**
|
||||
* Manages the <code>digest.txt</code> file and the computing and
|
||||
* processing of MD5 digests for an application.
|
||||
*/
|
||||
public class Digest
|
||||
{
|
||||
/** The name of our MD5 digest file. */
|
||||
public static final String DIGEST_FILE = "digest.txt";
|
||||
|
||||
/**
|
||||
* Creates a digest instance which will parse and validate the
|
||||
* <code>digest.txt</code> in the supplied application directory.
|
||||
*/
|
||||
public Digest (File appdir)
|
||||
throws IOException
|
||||
{
|
||||
// parse and validate our digest file contents
|
||||
String metaDigest = "";
|
||||
StringBuffer data = new StringBuffer();
|
||||
List pairs = ConfigUtil.parsePairs(new File(appdir, DIGEST_FILE));
|
||||
for (Iterator iter = pairs.iterator(); iter.hasNext(); ) {
|
||||
String[] pair = (String[])iter.next();
|
||||
if (pair[0].equals(DIGEST_FILE)) {
|
||||
metaDigest = pair[1];
|
||||
break;
|
||||
}
|
||||
_digests.put(pair[0], pair[1]);
|
||||
note(data, pair[0], pair[1]);
|
||||
}
|
||||
|
||||
// we've reached the end, validate our contents
|
||||
MessageDigest md = getMessageDigest();
|
||||
byte[] contents = data.toString().getBytes("UTF-8");
|
||||
String md5 = StringUtil.hexlate(md.digest(contents));
|
||||
if (!md5.equals(metaDigest)) {
|
||||
String err = MessageUtil.tcompose(
|
||||
"m.invalid_digest_file", metaDigest, md5);
|
||||
throw new IOException(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored digest value for the file with the supplied
|
||||
* path, or null if no digest exists for a file with that path.
|
||||
*/
|
||||
public String getDigest (String path)
|
||||
{
|
||||
return (String)_digests.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a digest file at the specified location using the supplied
|
||||
* list of resources.
|
||||
*/
|
||||
public static void createDigest (List resources, File output)
|
||||
throws IOException
|
||||
{
|
||||
MessageDigest md = getMessageDigest();
|
||||
StringBuffer data = new StringBuffer();
|
||||
PrintWriter pout = new PrintWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(output), "UTF-8"));
|
||||
|
||||
// compute and append the MD5 digest of each resource in the list
|
||||
for (Iterator iter = resources.iterator(); iter.hasNext(); ) {
|
||||
Resource rsrc = (Resource)iter.next();
|
||||
String path = rsrc.getPath();
|
||||
String digest = rsrc.computeDigest(md);
|
||||
note(data, path, digest);
|
||||
pout.println(path + " = " + digest);
|
||||
}
|
||||
|
||||
// finally compute and append the digest for the file contents
|
||||
md.reset();
|
||||
byte[] contents = data.toString().getBytes("UTF-8");
|
||||
pout.println(DIGEST_FILE + " = " +
|
||||
StringUtil.hexlate(md.digest(contents)));
|
||||
|
||||
pout.close();
|
||||
}
|
||||
|
||||
/** Used by {@link #createDigest} and {@link Digest}. */
|
||||
protected static void note (StringBuffer data, String path, String digest)
|
||||
{
|
||||
data.append(path).append(" = ").append(digest).append("\n");
|
||||
}
|
||||
|
||||
/** Used by {@link #createDigest} and {@link Digest}. */
|
||||
protected static MessageDigest getMessageDigest ()
|
||||
{
|
||||
try {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new RuntimeException("JVM does not support MD5. Gurp!");
|
||||
}
|
||||
}
|
||||
|
||||
protected HashMap _digests = new HashMap();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// $Id: Resource.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Models a single file resource used by an {@link Application}.
|
||||
*/
|
||||
public class Resource
|
||||
{
|
||||
/**
|
||||
* Creates a resource with the supplied application base URL and
|
||||
* resource path.
|
||||
*/
|
||||
public Resource (URL appbase, File appdir, String path)
|
||||
throws MalformedURLException
|
||||
{
|
||||
_path = path;
|
||||
_remote = new URL(appbase, path);
|
||||
_local = new File(appdir, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path associated with this resource.
|
||||
*/
|
||||
public String getPath ()
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the MD5 hash of this resource's underlying file.
|
||||
* <em>Note:</m> This is both CPU and I/O intensive.
|
||||
*/
|
||||
public String computeDigest (MessageDigest md)
|
||||
throws IOException
|
||||
{
|
||||
md.reset();
|
||||
byte[] buffer = new byte[DIGEST_BUFFER_SIZE];
|
||||
int read;
|
||||
FileInputStream fin = new FileInputStream(_local);
|
||||
while ((read = fin.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, read);
|
||||
}
|
||||
return StringUtil.hexlate(md.digest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
// return _path;
|
||||
// return _netloc.toString();
|
||||
return _local.getPath();
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
try {
|
||||
Resource rsrc = new Resource(
|
||||
new URL("file:/"), new File("."), args[0]);
|
||||
System.out.println("MD5: " + rsrc.computeDigest(
|
||||
MessageDigest.getInstance("MD5")));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
protected String _path;
|
||||
protected URL _remote;
|
||||
protected File _local;
|
||||
|
||||
protected final static int DIGEST_BUFFER_SIZE = 5 * 1025;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// $Id: Getdown.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.launcher;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.data.Application;
|
||||
|
||||
/**
|
||||
* Manages the main control for the Getdown application updater and
|
||||
* deployment system.
|
||||
*/
|
||||
public class Getdown
|
||||
{
|
||||
public Getdown (File appDir)
|
||||
{
|
||||
_app = new Application(appDir);
|
||||
}
|
||||
|
||||
public void run ()
|
||||
{
|
||||
try {
|
||||
_app.init();
|
||||
} catch (Exception e) {
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
// ensure the proper parameters are passed
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: java -jar getdown.jar app_dir");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// ensure a valid directory was supplied
|
||||
File appDir = new File(args[0]);
|
||||
if (!appDir.exists() || !appDir.isDirectory()) {
|
||||
Log.warning("Invalid app_dir '" + args[0] + "'.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
try {
|
||||
Getdown app = new Getdown(appDir);
|
||||
app.run();
|
||||
} catch (Exception e) {
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Application _app;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// $Id: DigesterTask.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Digest;
|
||||
|
||||
/**
|
||||
* An ant task used to create a <code>digest.txt</code> for a Getdown
|
||||
* application deployment.
|
||||
*/
|
||||
public class DigesterTask extends Task
|
||||
{
|
||||
/**
|
||||
* Sets the application directory.
|
||||
*/
|
||||
public void setAppdir (File appdir)
|
||||
{
|
||||
_appdir = appdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual work of the task.
|
||||
*/
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
// make sure appdir is set
|
||||
if (_appdir == null) {
|
||||
String errmsg = "Must specify the path to the application " +
|
||||
"directory via the 'appdir' attribute.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
File target = new File(_appdir, Digest.DIGEST_FILE);
|
||||
System.out.println("Generating digest file '" + target + "'...");
|
||||
|
||||
// create our application and instruct it to parse its business
|
||||
Application app = new Application(_appdir);
|
||||
try {
|
||||
app.init();
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException("Error parsing getdown.txt: " +
|
||||
ioe.getMessage(), ioe);
|
||||
}
|
||||
|
||||
ArrayList rsrcs = new ArrayList();
|
||||
rsrcs.add(app.getConfigResource());
|
||||
CollectionUtil.addAll(rsrcs, app.getCodeResources().iterator());
|
||||
CollectionUtil.addAll(rsrcs, app.getResources().iterator());
|
||||
|
||||
// now generate the digest file
|
||||
try {
|
||||
Digest.createDigest(rsrcs, target);
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException("Error creating digest: " +
|
||||
ioe.getMessage(), ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/** The application directory in which we're creating a digest file. */
|
||||
protected File _appdir;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// $Id: ConfigUtil.java,v 1.1 2004/07/02 11:01:21 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Parses a file containing key/value pairs and returns a {@link HashMap}
|
||||
* with the values. Keys may be repeated, in which case they will be made
|
||||
* to reference an array of values.
|
||||
*/
|
||||
public class ConfigUtil
|
||||
{
|
||||
/**
|
||||
* Parses a configuration file containing key/value pairs. The file
|
||||
* must be in the UTF-8 encoding.
|
||||
*
|
||||
* @return a list of <code>String[]</code> instances containing the
|
||||
* key/value pairs in the order they were parsed from the file.
|
||||
*/
|
||||
public static List parsePairs (File config)
|
||||
throws IOException
|
||||
{
|
||||
ArrayList pairs = new ArrayList();
|
||||
|
||||
// parse our configuration file
|
||||
BufferedReader bin = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(config), "UTF-8"));
|
||||
String line = null;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
// nix comments
|
||||
int cidx = line.indexOf("#");
|
||||
if (cidx != -1) {
|
||||
line = line.substring(0, cidx);
|
||||
}
|
||||
|
||||
// trim whitespace and skip blank lines
|
||||
line = line.trim();
|
||||
if (StringUtil.blank(line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse our key/value pair
|
||||
String[] pair = new String[2];
|
||||
int eidx = line.indexOf("=");
|
||||
if (eidx != -1) {
|
||||
pair[0] = line.substring(0, eidx).trim();
|
||||
pair[1] = line.substring(eidx+1).trim();
|
||||
} else {
|
||||
pair[0] = line;
|
||||
pair[1] = "";
|
||||
}
|
||||
|
||||
pairs.add(pair);
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a configuration file containing key/value pairs. The file
|
||||
* must be in the UTF-8 encoding.
|
||||
*
|
||||
* @return a map from keys to values, where a value will be an array
|
||||
* of strings if more than one key/value pair in the config file was
|
||||
* associated with the same key.
|
||||
*/
|
||||
public static HashMap parseConfig (File config)
|
||||
throws IOException
|
||||
{
|
||||
List pairs = parsePairs(config);
|
||||
HashMap data = new HashMap();
|
||||
|
||||
for (Iterator iter = pairs.iterator(); iter.hasNext(); ) {
|
||||
String[] pair = (String[])iter.next();
|
||||
Object value = data.get(pair[0]);
|
||||
if (value == null) {
|
||||
data.put(pair[0], pair[1]);
|
||||
} else if (value instanceof String) {
|
||||
data.put(pair[0], new String[] { (String)value, pair[1] });
|
||||
} else if (value instanceof String[]) {
|
||||
String[] values = (String[])value;
|
||||
String[] nvalues = new String[values.length+1];
|
||||
System.arraycopy(values, 0, nvalues, 0, values.length);
|
||||
nvalues[values.length] = pair[1];
|
||||
data.put(pair[0], nvalues);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Massages a single string into an array and leaves existing array
|
||||
* values as is. Simplifies access to parameters that are expected to
|
||||
* be arrays.
|
||||
*/
|
||||
public static String[] getMultiValue (HashMap data, String name)
|
||||
{
|
||||
Object value = data.get(name);
|
||||
if (value instanceof String) {
|
||||
return new String[] { (String)value };
|
||||
} else {
|
||||
return (String[])value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user