package net.contrapunctus.rngzip;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import net.contrapunctus.rngzip.io.InteractiveInput;
import net.contrapunctus.rngzip.io.RNGZInputInterface;
import net.contrapunctus.rngzip.io.RNGZInputStream;
import net.contrapunctus.rngzip.io.RNGZOutputInterface;
import net.contrapunctus.rngzip.io.RNGZOutputStream;
import net.contrapunctus.rngzip.io.RNGZSettings;
import net.contrapunctus.rngzip.io.VerboseOutput;
import net.contrapunctus.rngzip.util.BaliAutomaton;
import net.contrapunctus.rngzip.util.ErrorReporter;
import net.contrapunctus.rngzip.util.SchemaFormatException;
import net.contrapunctus.rngzip.util.SimpleXMLWriter;
import net.contrapunctus.rngzip.util.PrettyXMLWriter;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class Driver
{
public static void main(String[] args) throws Exception
{
new Driver().run(args);
}
private static final String myname = "rngzip";
Options opt = new Options(myname);
private BaliAutomaton automaton;
private long checksum;
static PrintStream err = System.err;
void run(String[] args) throws Exception
{
int first = opt.process(args);
if( opt.identify_p ) {
for( int i = first; i < args.length; i++) {
identify(args[i]);
}
}
if( opt.compress_p || opt.decompress_p ) {
if( opt.schema != null ) {
loadAutomaton(opt.schema);
}
Task task = opt.compress_p? new Zip() : new Unzip();
if( first < args.length ) {
for( int i = first; i < args.length; i++ ) {
applyToFile(task, new File(args[i]));
}
}
else {
applyToStdIO(task);
}
}
}
private HashMap<URL, BaliAutomaton> autoMap =
new HashMap<URL, BaliAutomaton>();
private void loadAutomaton (URL url) throws SchemaFormatException
{
automaton = autoMap.get(url);
if( automaton == null ) {
info("loading %s%n", url);
info("building automaton... ");
long start = 0;
if( opt.timings_p ) { start = System.currentTimeMillis(); }
automaton = BaliAutomaton.fromRNG(url);
checksum = automaton.checksum();
if( opt.timings_p ) {
long elapsed = System.currentTimeMillis() - start;
err.printf("%5d%s", elapsed,
opt.verbosity > 1? "ms\n" : ",");
}
else {
info("done%n");
}
autoMap.put(url, automaton);
}
}
private void loadAutomaton (File file)
throws FileNotFoundException, SchemaFormatException
{
if(!file.exists())
throw new FileNotFoundException(file.toString());
URL url = null;
try { url = file.toURI().toURL(); }
catch(MalformedURLException x) { assert false : x; }
loadAutomaton(url);
}
private void loadAutomaton (String spec)
throws FileNotFoundException, SchemaFormatException
{
try {
loadAutomaton(new URL(spec));
}
catch(MalformedURLException x) {
loadAutomaton(new File(spec));
}
}
private void identify(String name)
{
RNGZInputStream zin = null;
long len = -1;
try {
File file = new File(name);
len = file.length();
FileInputStream in = new FileInputStream(file);
zin = new RNGZInputStream(in, opt.settings);
}
catch(FileNotFoundException x) {
System.out.println(x.getMessage());
}
catch(IOException x) {
System.out.printf("%s (%s)%n", name, x.getMessage());
}
if( zin != null ) {
System.out.printf("%s: %d bytes %s", name, len, opt.settings);
URL url = zin.getSchemaURL();
if( opt.verbosity > 1 && url != null ) {
System.out.printf(" %s(%016x)%n", url, zin.getSchemaSum());
}
else {
System.out.println();
}
try { zin.close(); }
catch(IOException x) { }
}
}
private void error(String fmt, Object... args)
{
if(opt.verbosity >= 0) {
System.err.printf("%s: error: ", myname);
System.err.printf(fmt, args);
System.err.println();
}
}
private void info(String fmt, Object... args)
{
if(opt.verbosity > 1) {
err.printf(fmt, args);
}
}
private void applyToStdIO (Task task)
throws IOException, SAXException
{
task.setInput(System.in);
task.setOutput(System.out);
task.prepare();
task.run();
}
private void applyToFile (Task task, File infile)
throws IOException, SAXException
{
task.setInput(infile);
if( opt.stdout_p ) {
task.setOutput(System.out);
task.prepare();
task.run();
if( opt.timings_p ) {
err.printf("%5d%s", task.getTime(),
opt.verbosity > 1? "ms\n" : ",");
}
return;
}
File outfile = task.getOutput();
try {
if(!outfile.createNewFile() && !opt.force_p) {
error("%s already exists; use --force (-f) to overwrite.",
outfile);
return;
}
}
catch(IOException x) {
error("cannot create %s: %s", outfile, x.getMessage());
return;
}
OutputStream outstream = new FileOutputStream(outfile);
task.setOutput(outstream);
task.prepare();
info("%-25s ", infile);
task.run();
task.closeInput();
outstream.close();
if( opt.timings_p ) {
err.printf("%5d%s", task.getTime(),
opt.verbosity > 1? "ms\n" : ",");
}
float ratio = task.computeRatio();
boolean del_p = false; // did we delete the input file?
if( !opt.keep_p ) {
del_p = infile.delete();
}
info("%.2f%% -- %s %s%n",
ratio, del_p? "replaced with" : "created", outfile);
if(!opt.keep_p && !del_p) {
error("could not remove %s", infile);
}
}
private abstract class Task {
protected File infile;
protected File outfile;
protected OutputStream outstream;
private long elapsed;
abstract void setInput (File file) throws FileNotFoundException;
abstract void setInput (InputStream in);
abstract File getOutput ();
abstract void execute() throws IOException, SAXException;
abstract float computeRatio (long insize, long outsize);
void setOutput (OutputStream out) {
outstream = out;
}
float computeRatio() {
assert infile != null && outfile != null;
long insize = infile.length();
long outsize = outfile.length();
return computeRatio (insize, outsize);
}
void prepare() throws IOException { }
void run() throws IOException, SAXException {
long start = System.currentTimeMillis();
execute();
elapsed = System.currentTimeMillis() - start;
}
void closeInput() throws IOException { }
long getTime() { return elapsed; }
} // end class Task
private class Zip extends Task {
private InputSource insource;
void setInput (File file) {
infile = file;
insource = new InputSource(file.getPath());
}
void setInput (InputStream in) {
insource = new InputSource(in);
}
File getOutput() {
assert infile != null;
String name = infile.getPath() + opt.suffix;
outfile = new File (name);
return outfile;
}
float computeRatio (long insize, long outsize) {
return (insize - outsize) / (float)insize * 100;
}
void execute() throws IOException, SAXException {
assert insource != null && outstream != null;
RNGZOutputInterface rnz = opt.debug_p?
new VerboseOutput(System.err) :
new RNGZOutputStream(outstream, opt.settings, automaton);
ErrorReporter err = new ErrorReporter();
GenericCompressor gc = new GenericCompressor(automaton, err, rnz);
XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setFeature("http://xml.org/sax/features/validation", false);
xr.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
xr.setContentHandler(gc);
xr.setErrorHandler(err);
xr.parse(insource);
rnz.close();
}
} // end class Zip
private class Unzip extends Task {
private InputStream instream;
private RNGZInputInterface zin;
private ContentHandler ch;
void setInput (File file) throws FileNotFoundException {
infile = file;
instream = new FileInputStream(infile);
}
void setInput (InputStream in) {
instream = in;
}
File getOutput() {
assert infile != null;
String name = infile.getPath();
if( opt.suffix.length() > 0 && name.endsWith(opt.suffix) ) {
name = name.substring(0, name.length() - opt.suffix.length());
}
else {
name += ".xml";
}
outfile = new File (name);
return outfile;
}
float computeRatio (long insize, long outsize) {
return (outsize - insize) / (float)outsize * 100;
}
void prepare() throws IOException {
assert instream != null && outstream != null;
if( opt.debug_p ) {
zin = new InteractiveInput(instream, System.err);
}
else {
RNGZInputStream zis = new RNGZInputStream(instream, opt.settings);
if( zis.getSchemaURL() != null ) {
// input stream has schema ref embedded
if( opt.schema == null ) {
loadAutomaton(zis.getSchemaURL());
}
if( zis.getSchemaSum() != checksum ) {
error("MISMATCH %08X <> %08X", zis.getSchemaSum(), checksum);
error("Schema was %s", zis.getSchemaURL());
throw new IOException("SUMS DO NOT MATCH"); // FIX
}
}
else if( opt.schema == null ) {
throw new IOException("NO SCHEMA SPECIFIED"); // FIX
}
zin = zis;
}
ch = (opt.pretty_p?
new PrettyXMLWriter(outstream, opt.pretty_tab) :
new SimpleXMLWriter(outstream));
}
void execute() throws IOException, SAXException {
assert zin != null && ch != null;
new GenericDecompressor(automaton, zin, ch);
outstream.write('\n');
}
void closeInput() throws IOException {
instream.close();
}
} // end class Unzip
}