package net.contrapunctus.rngzip; import java.io.IOException; import java.io.PrintStream; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.Stack; import net.contrapunctus.rngzip.io.RNGZInputInterface; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; public abstract class Decompressor { static private abstract class Event { abstract void exec (ContentHandler ch) throws SAXException; abstract void show (PrintStream out); boolean finished () { return true; } } static private class StartEvent extends Event { String elt; AttributesImpl att; private boolean finished_p; StartEvent(String e) { elt = e; att = new AttributesImpl(); finished_p = false; } void exec (ContentHandler ch) throws SAXException { ch.startElement("", "", elt, att); } void show (PrintStream out) { out.print("+" + elt + " "); showAttributes(out, att); out.println(); } boolean finished() { return finished_p; } void commit() { finished_p = true; } } // end class StartEvent static private class CharEvent extends Event { String data; CharEvent(String s) { data = s; } void exec (ContentHandler ch) throws SAXException { ch.characters(data.toCharArray(), 0, data.length()); } void show (PrintStream out) { String s = "$"+data; if(s.length() > 12) s = s.substring(0, 9)+"..."; out.println(s); } } // end class CharEvent static private class EndEvent extends Event { String elt; EndEvent(String e) { elt = e; } void exec (ContentHandler ch) throws SAXException { ch.endElement(null, null, elt); } void show (PrintStream out) { out.println("-" + elt); } } // end class EndEvent private Queue<Event> eventQ = new LinkedList<Event>(); private Stack<StartEvent> startStack = new Stack<StartEvent>(); protected Stack<String> eltStack = new Stack<String>(); private ContentHandler ch; private String attrKey; // current attribute key private StringBuilder attrVal = new StringBuilder(); private static final boolean DEBUG = System.getProperty("DEBUG_Decompressor") != null; private static final boolean TRACE_MEM = System.getProperty("TRACE_MEM") != null; private static final PrintStream dbg = System.err; private static Random rnd = new Random(); private void trace (String where) { dbg.println("===== " + where); boolean firstp = true; for(StartEvent se : startStack) { if(se != null) { dbg.printf(" %5s ", firstp? "stack" : ""); firstp = false; se.show(dbg); } } firstp = true; for(Event e : eventQ) { dbg.printf(" %5s ", firstp? "queue" : ""); firstp = false; e.show(dbg); } } private void reportMemoryStats() { dbg.printf("MEM: Decompressor: %d events queued%n", eventQ.size()); } protected void initialize (ContentHandler h) throws SAXException { this.ch = h; startStack.push(null); // sentinel, to detect end of document ch.startDocument(); } protected void startElement(String e) throws SAXException { StartEvent ev = new StartEvent(e); startStack.push(ev); eltStack.push(e); eventQ.add(ev); if(DEBUG) trace("startElement"); } protected void addAttribute(String a) { if(attrKey != null) commitAttribute(); attrKey = a; eltStack.push('@'+a); if(DEBUG) trace("addAttribute"); } private void commitAttribute() { assert attrKey != null; eltStack.pop(); // pop "@name" startStack.peek().att.addAttribute (null, null, attrKey, null, attrVal.toString()); attrKey = null; attrVal.setLength(0); if(DEBUG) trace("commitAttribute"); } protected void chars(String s) throws SAXException { if(attrKey != null) { attrVal.append(s); } else { eventQ.add(new CharEvent(s)); } } protected void epsilon() throws SAXException { if(attrKey != null) { commitAttribute(); } else { endElement(); } } private void endElement() throws SAXException { // TODO: commit corresponding start element StartEvent ev = startStack.pop(); if( ev == null ) { endDocument(); // flushes rest of queue } else { ev.commit(); runQueue(); String elt = eltStack.pop(); assert elt.equals(ev.elt); eventQ.add(new EndEvent(elt)); if(DEBUG) trace("endElement"); if(TRACE_MEM && rnd.nextInt(100) == 0) { reportMemoryStats(); } } } private void endDocument() throws SAXException { runQueue(); ch.endDocument(); if(DEBUG) trace("endDocument"); } private void runQueue() throws SAXException { Event ev = eventQ.peek(); while( ev != null && ev.finished() ) { eventQ.remove(); ev.exec(ch); ev = eventQ.peek(); } } public static void showAttributes(PrintStream out, Attributes a) { out.print("@( "); for(int i = 0; i < a.getLength(); i++) { out.printf("%s=\"%s\" ", a.getQName(i), a.getValue(i)); } out.print(") "); } }