From 6e40b32ee7b4e65f600f6aeb25838d6d40e76838 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 13 Dec 2009 07:27:51 +0100 Subject: [PATCH] Fixed up the DocBuffer a bit and added a basic XML writer. --- src/dolda/jsvc/next/ColumnWriter.java | 51 ++++++++ src/dolda/jsvc/next/DocBuffer.java | 33 +++++- src/dolda/jsvc/next/XmlWriter.java | 215 ++++++++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 src/dolda/jsvc/next/ColumnWriter.java create mode 100644 src/dolda/jsvc/next/XmlWriter.java diff --git a/src/dolda/jsvc/next/ColumnWriter.java b/src/dolda/jsvc/next/ColumnWriter.java new file mode 100644 index 0000000..0ea5b77 --- /dev/null +++ b/src/dolda/jsvc/next/ColumnWriter.java @@ -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(' '); + } +} diff --git a/src/dolda/jsvc/next/DocBuffer.java b/src/dolda/jsvc/next/DocBuffer.java index 432d817..b2d1f0a 100644 --- a/src/dolda/jsvc/next/DocBuffer.java +++ b/src/dolda/jsvc/next/DocBuffer.java @@ -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 index 0000000..6e7c968 --- /dev/null +++ b/src/dolda/jsvc/next/XmlWriter.java @@ -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 nsnames = new HashMap(); + 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("<"); + else if(c == '>') + out.write(">"); + else if(c == '&') + out.write("&"); + else if(c == qt) + out.write((c == '\'')?"'":"""); + 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 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(""); + } + + 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(""); + } + + 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("\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("\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(); + } +} -- 2.11.0