Fixed up the DocBuffer a bit and added a basic XML writer.
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 13 Dec 2009 06:27:51 +0000 (07:27 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 13 Dec 2009 06:27:51 +0000 (07:27 +0100)
src/dolda/jsvc/next/ColumnWriter.java [new file with mode: 0644]
src/dolda/jsvc/next/DocBuffer.java
src/dolda/jsvc/next/XmlWriter.java [new file with mode: 0644]

diff --git a/src/dolda/jsvc/next/ColumnWriter.java b/src/dolda/jsvc/next/ColumnWriter.java
new file mode 100644 (file)
index 0000000..0ea5b77
--- /dev/null
@@ -0,0 +1,51 @@
+package dolda.jsvc.next;
+
+import java.io.*;
+
+public class ColumnWriter extends FilterWriter {
+    public int line, col;
+    public boolean start = true;
+    
+    public ColumnWriter(Writer out) {
+       super(out);
+       line = col = 0;
+    }
+    
+    private void hc(int c) {
+       if(c == '\n') {
+           col = 0;
+           line++;
+           start = true;
+       } else if(c == '\t') {
+           col = (col - (col % 8)) + 8;
+       } else {
+           col++;
+       }
+       if(!Character.isWhitespace(c))
+           start = false;
+    }
+
+    public void write(int c) throws IOException {
+       super.write(c);
+       hc(c);
+    }
+    
+    public void write(String s, int off, int len) throws IOException {
+       super.write(s, off, len);
+       for(int i = 0; i < s.length(); i++)
+           hc(s.charAt(i));
+    }
+    
+    public void write(char[] b, int off, int len) throws IOException {
+       super.write(b, off, len);
+       for(int i = 0; i < len; i++, off++)
+           hc(b[off]);
+    }
+    
+    public void indent(int level) throws IOException {
+       if(!start)
+           write('\n');
+       while(col < level)
+           write(' ');
+    }
+}
index 432d817..b2d1f0a 100644 (file)
@@ -19,7 +19,8 @@ public class DocBuffer {
     private Node findcursor(Node c, String name) {
        if(c instanceof Element) {
            Element el = (Element)c;
-           if(el.getNamespaceURI().equals(ns) && el.getTagName().equals("cursor") && el.getAttributeNS(ns, "name").equals(name))
+           String ns = el.getNamespaceURI();
+           if((ns != null) && ns.equals(DocBuffer.ns) && el.getTagName().equals("cursor") && el.getAttributeNS(null, "name").equals(name))
                return(c);
        }
        for(Node n = c.getFirstChild(); n != null; n = n.getNextSibling()) {
@@ -45,12 +46,12 @@ public class DocBuffer {
        Node c = cursor(cursor);
        if(c == null)
            throw(new RuntimeException("No such cursor: `" + cursor + "'"));
-       c.getParentNode().insertBefore(c, doc.importNode(n, true));
+       c.getParentNode().insertBefore(doc.importNode(n, true), c);
     }
     
     public Element makecursor(String name) {
        Element el = doc.createElementNS(ns, "cursor");
-       Attr a = doc.createAttributeNS(ns, "name");
+       Attr a = doc.createAttributeNS(null, "name");
        a.setValue(name);
        el.setAttributeNodeNS(a);
        return(el);
@@ -70,4 +71,30 @@ public class DocBuffer {
     public Text text(String text) {
        return(doc.createTextNode(text));
     }
+    
+    public void finalise() {
+       Node n = doc;
+       while(true) {
+           Node nx;
+           if(n.getFirstChild() != null) {
+               nx = n.getFirstChild();
+           } else if(n.getNextSibling() != null) {
+               nx = n.getNextSibling();
+           } else {
+               for(nx = n.getParentNode(); nx != null; nx = nx.getParentNode()) {
+                   if(nx.getNextSibling() != null) {
+                       nx = nx.getNextSibling();
+                       break;
+                   }
+               }
+           }
+           String ns = n.getNamespaceURI();
+           if((ns != null) && ns.equals(DocBuffer.ns))
+               n.getParentNode().removeChild(n);
+           if(nx == null)
+               break;
+           else
+               n = nx;
+       }
+    }
 }
diff --git a/src/dolda/jsvc/next/XmlWriter.java b/src/dolda/jsvc/next/XmlWriter.java
new file mode 100644 (file)
index 0000000..6e7c968
--- /dev/null
@@ -0,0 +1,215 @@
+package dolda.jsvc.next;
+
+import java.io.*;
+import java.util.*;
+import org.w3c.dom.*;
+import dolda.jsvc.util.Misc;
+
+public class XmlWriter {
+    private Map<String, String> nsnames = new HashMap<String, String>();
+    private Document doc;
+    private int nsser = 1;
+    
+    public XmlWriter(Document doc) {
+       this.doc = doc;
+    }
+    
+    public void setnsname(String uri, String name) {
+       nsnames.put(uri, name);
+    }
+
+    private String nsname(String uri) {
+       String ret;
+       if(nsnames.containsKey(uri))
+           return(nsnames.get(uri));
+       do {
+           ret = "n" + (nsser++);
+       } while(nsnames.containsValue(ret));
+       nsnames.put(uri, ret);
+       return(ret);
+    }
+    
+    protected void findallnsnames() {
+       Node n = doc;
+       while(true) {
+           String ns = n.getNamespaceURI();
+           if(ns != null)
+               nsname(ns);
+           if(n.getFirstChild() != null) {
+               n = n.getFirstChild();
+           } else if(n.getNextSibling() != null) {
+               n = n.getNextSibling();
+           } else {
+               for(n = n.getParentNode(); n != null; n = n.getParentNode()) {
+                   if(n.getNextSibling() != null) {
+                       n = n.getNextSibling();
+                       break;
+                   }
+               }
+               if(n == null)
+                   break;
+           }
+       }
+    }
+    
+    protected boolean prebreak(ColumnWriter out, Element el) {
+       return(false);
+    }
+
+    protected int indent(ColumnWriter out, Element el) {
+       return(-1);
+    }
+
+    protected boolean postbreak(ColumnWriter out, Element el) {
+       return(false);
+    }
+
+    protected boolean asempty(ColumnWriter out, Element el) {
+       return(true);
+    }
+
+    protected void attribute(ColumnWriter out, String nm, String val, int indent) throws IOException {
+       char qt = '\"';
+       if((val.indexOf("\"") >= 0) && (val.indexOf('\'') < 0))
+           qt = '\'';
+       out.write(" " + nm + "=" + qt);
+       for(int i = 0; i < val.length(); i++) {
+           char c = val.charAt(i);
+           if(c == '<')
+               out.write("&lt;");
+           else if(c == '>')
+               out.write("&gt;");
+           else if(c == '&')
+               out.write("&amp;");
+           else if(c == qt)
+               out.write((c == '\'')?"&apos;":"&quot;");
+           else
+               out.write(c);
+       }
+       out.write(qt);
+    }
+    
+    protected void attribute(ColumnWriter out, Attr attr, int indent) throws IOException {
+       String ns = attr.getNamespaceURI();
+       if(ns == null)
+           attribute(out, attr.getName(), attr.getValue(), indent);
+       else
+           attribute(out, nsname(ns) + ":" + attr.getName(), attr.getValue(), indent);
+    }
+
+    protected void element(ColumnWriter out, Element el, int indent) throws IOException {
+       if(prebreak(out, el))
+           out.indent(indent);
+       
+       String tagname = el.getTagName();
+       String ns = nsname(el.getNamespaceURI());
+       if(ns != null)
+           tagname = ns + ":" + tagname;
+       out.write("<" + tagname);
+       NamedNodeMap attrs = el.getAttributes();
+       int acol = out.col + 1;
+       if(attrs != null) {
+           for(int i = 0; i < attrs.getLength(); i++) {
+               Attr attr = (Attr)attrs.item(i);
+               attribute(out, attr, acol);
+           }
+       }
+       if(el == doc.getDocumentElement()) {
+           for(Map.Entry<String, String> nd : nsnames.entrySet()) {
+               String nm = nd.getValue();
+               if(nm == null)
+                   attribute(out, "xmlns", nd.getKey(), acol);
+               else
+                   attribute(out, "xmlns:" + nm, nd.getKey(), acol);
+           }
+       }
+       
+       if((el.getFirstChild() == null) && asempty(out, el)) {
+           out.write(" />");
+       } else {
+           out.write(">");
+           int inner = indent(out, el);
+           if(inner >= 0) {
+               out.indent(indent + inner);
+           }
+           
+           for(Node ch = el.getFirstChild(); ch != null; ch = ch.getNextSibling())
+               node(out, ch, (inner >= 0)?(indent + inner):indent);
+           
+           if(inner >= 0)
+               out.indent(indent);
+           out.write("</" + tagname + ">");
+       }
+       
+       if(postbreak(out, el))
+           out.indent(indent);
+    }
+    
+    protected void text(ColumnWriter out, String s, int indent) throws IOException {
+       out.write(s);
+    }
+
+    protected void text(ColumnWriter out, Text txt, int indent) throws IOException {
+       String s = txt.getData();
+       text(out, s, indent);
+    }
+
+    protected void comment(ColumnWriter out, Comment c, int indent) throws IOException {
+       out.write("<!--");
+       String s = c.getData();
+       text(out, s, indent);
+       out.write("-->");
+    }
+
+    protected void node(ColumnWriter out, Node n, int indent) throws IOException {
+       if(n instanceof Element) {
+           Element el = (Element)n;
+           element(out, el, indent);
+       } else if(n instanceof Text) {
+           Text txt = (Text)n;
+           text(out, txt, indent);
+       } else if(n instanceof Comment) {
+           Comment c = (Comment)n;
+           comment(out, c, indent);
+       } else {
+           throw(new RuntimeException(String.format("Unknown DOM node encountered (%s)", n.getClass())));
+       }
+    }
+    
+    public void write(Writer out) throws IOException {
+       findallnsnames();
+       ColumnWriter col = new ColumnWriter(out);
+       DocumentType t = doc.getDoctype();
+       if(t != null)
+           out.write(String.format("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", t.getName(), t.getPublicId(), t.getSystemId()));
+       node(col, doc.getDocumentElement(), 0);
+    }
+
+    public void write(OutputStream out) throws IOException {
+       /* The OutputStreamWriter may need to be flushed to clear any
+        * internal buffers it may have, but it would be a pity to
+        * force-flush the underlying stream just because of that. */
+       class FlushGuard extends FilterOutputStream {
+           FlushGuard(OutputStream out) {
+               super(out);
+           }
+           
+           public void flush() {}
+       }
+       Writer w = new OutputStreamWriter(new FlushGuard(out), Misc.utf8);
+       w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+       write(w);
+       w.flush();
+    }
+    
+    public static void main(String[] args) throws Exception {
+       Html barda = Html.xhtml11("Barda");
+       barda.addcss("/slen.css", "Test");
+       barda.insert("body", barda.el("h1", barda.text("Mast")));
+       barda.finalise();
+       XmlWriter w = new XmlWriter(barda.doc);
+       w.setnsname(Html.ns, null);
+       w.write(System.out);
+       System.out.flush();
+    }
+}