Improved the DocBuffer and DOM writers to where they are kind of usable.
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 14 Dec 2009 01:25:44 +0000 (02:25 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 14 Dec 2009 01:25:44 +0000 (02:25 +0100)
The bsvc ShellPage uses them for reference.

samples/bsh/src/dolda/bsvc/ShellPage.java
src/dolda/jsvc/next/DocBuffer.java
src/dolda/jsvc/next/Html.java
src/dolda/jsvc/next/HtmlWriter.java [new file with mode: 0644]
src/dolda/jsvc/next/XHtmlWriter.java [new file with mode: 0644]
src/dolda/jsvc/next/XmlWriter.java

index c26998e..13be814 100644 (file)
@@ -2,10 +2,12 @@ package dolda.bsvc;
 
 import dolda.jsvc.*;
 import dolda.jsvc.util.*;
+import dolda.jsvc.next.*;
+import org.w3c.dom.*;
 import java.io.*;
 import bsh.Interpreter;
 
-public class ShellPage extends SimpleWriter {
+public class ShellPage implements Responder {
     private Console cons = new Console();
     private Interpreter ip = new Interpreter(cons);
     
@@ -54,19 +56,14 @@ public class ShellPage extends SimpleWriter {
        }
     }
     
-    public void respond(Request req, PrintWriter out) {
+    public void respond(Request req) {
        MultiMap<String, String> params = req.params();
        String cmd = params.get("cmd");
        
-       out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-       out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
-       out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\">");
-       out.println("<head>");
-       out.println("<title>Shell</title>");
-       out.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"css\" />");
-       out.println("</head>");
-       out.println("<body>");
-       out.println("<h1>Shell</h1>");
+       Html buf = Html.xhtml11("Shell");
+       buf.addcss("css", null);
+       buf.insert("body", buf.el("h1", buf.text("Shell")));
+       
        if((req.method() == "POST") && (cmd != null)) {
            String eo, ee;
            synchronized(cons) {
@@ -75,47 +72,40 @@ public class ShellPage extends SimpleWriter {
                try {
                    ip.set("req", req);
                    resp = ip.eval(cmd);
-                   out.println("<pre>");
-                   out.println(Misc.htmlq((resp == null)?"(null)":(resp.toString())));
-                   out.println("</pre>");
+                   buf.insert("body", buf.el("pre", buf.text((resp == null)?"(null)":(resp.toString()))));
                } catch(bsh.EvalError exc) {
-                   out.println("<h2>Evaluation error</h2>");
-                   out.println("<pre>");
-                   out.print(exc.toString());
-                   out.println("</pre>");
+                   buf.insert("body", buf.el("h2", buf.text("Evaluation error")));
+                   buf.insert("body", buf.el("pre", buf.text(exc.toString())));
                    if(exc instanceof bsh.TargetError) {
                        bsh.TargetError te = (bsh.TargetError)exc;
-                       out.println("<h3>Target error</h3>");
-                       out.println("<pre>");
-                       te.getTarget().printStackTrace(out);
-                       out.println("</pre>");
+                       buf.insert("body", buf.el("h3", buf.text("Target error")));
+                       StringWriter sbuf = new StringWriter();
+                       te.getTarget().printStackTrace(new PrintWriter(sbuf));
+                       buf.insert("body", buf.el("pre", buf.text(sbuf.toString())));
                    }
                }
                eo = new String(cons.obuf.toByteArray(), Misc.utf8);
                ee = new String(cons.ebuf.toByteArray(), Misc.utf8);
            }
            if(eo.length() > 0) {
-               out.println("<h2>Output</h2>");
-               out.println("<pre>");
-               out.println(Misc.htmlq(eo));
-               out.println("</pre>");
+               buf.insert("body", buf.el("h2", buf.text("Output")));
+               buf.insert("body", buf.el("pre", buf.text(eo)));
            }
            if(ee.length() > 0) {
-               out.println("<h2>Errors</h2>");
-               out.println("<pre>");
-               out.println(Misc.htmlq(ee));
-               out.println("</pre>");
+               buf.insert("body", buf.el("h2", buf.text("Errors")));
+               buf.insert("body", buf.el("pre", buf.text(ee)));
            }
        }
-       out.println("<form action=\"sh\" method=\"post\">");
-       out.println("<textarea cols=\"80\" rows=\"5\" name=\"cmd\">");
-       if(cmd != null)
-           out.print(cmd);
-       out.println("</textarea>");
-       out.println("<input type=\"submit\" value=\"Evaluate\" />");
-       out.println("<input type=\"reset\" value=\"Reset\" />");
-       out.println("</form>");
-       out.println("</body>");
-       out.println("</html>");
+       
+       Element form;
+       buf.insert("body", buf.el("form", form = buf.el("p", null), "action=sh", "method=post"));
+       form.appendChild(buf.el("textarea", buf.text(cmd), "cols=80", "rows=5", "name=cmd"));
+       form.appendChild(buf.el("input", null, "type=submit", "value=Evaluate"));
+       form.appendChild(buf.el("input", null, "type=reset", "value=Reset"));
+       try {
+           buf.output(req);
+       } catch(IOException e) {
+           throw(new RuntimeException(e));
+       }
     }
 }
index b2d1f0a..2690309 100644 (file)
@@ -46,7 +46,9 @@ public class DocBuffer {
        Node c = cursor(cursor);
        if(c == null)
            throw(new RuntimeException("No such cursor: `" + cursor + "'"));
-       c.getParentNode().insertBefore(doc.importNode(n, true), c);
+       if(n.getOwnerDocument() != doc)
+           n = doc.importNode(n, true);
+       c.getParentNode().insertBefore(n, c);
     }
     
     public Element makecursor(String name) {
@@ -69,6 +71,8 @@ public class DocBuffer {
     }
     
     public Text text(String text) {
+       if(text == null)
+           return(null);
        return(doc.createTextNode(text));
     }
     
index c90bb40..4c5b417 100644 (file)
@@ -5,6 +5,8 @@ import org.w3c.dom.ls.*;
 import javax.xml.validation.*;
 import java.net.*;
 import java.io.*;
+import dolda.jsvc.*;
+import dolda.jsvc.util.*;
 
 public class Html extends DocBuffer {
     public static final String ns = "http://www.w3.org/1999/xhtml";
@@ -18,8 +20,8 @@ public class Html extends DocBuffer {
        Html buf = new Html("-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd");
        Node html = buf.doc.getDocumentElement();
        Node head = DomUtil.insertel(html, "head");
-       head.appendChild(buf.makecursor("head"));
        Node tit = DomUtil.insertel(head, "title");
+       head.appendChild(buf.makecursor("head"));
        DomUtil.inserttext(tit, title);
        Node body = DomUtil.insertel(html, "body");
        body.appendChild(buf.makecursor("body"));
@@ -53,4 +55,36 @@ public class Html extends DocBuffer {
            throw(new Error(e));
        }
     }
+    
+    private static boolean asxhtml(Request req) {
+       String ah = req.inheaders().get("Accept");
+       AcceptMap ctmap = AcceptMap.parse((ah == null)?"":ah);
+       AcceptMap.Entry ha = ctmap.accepts("text/html");
+       AcceptMap.Entry xa = ctmap.accepts("text/xhtml+xml");
+       if(xa == null)
+           xa = ctmap.accepts("application/xhtml+xml");
+       if((ha == null) && (xa == null))
+           return(false);
+       else if((ha != null) && (xa == null))
+           return(false);
+       else if((ha == null) && (xa != null))
+           return(true);
+       if(xa.q < ha.q)
+           return(false);
+       return(true);
+    }
+
+    public void output(Request req) throws IOException {
+       finalise();
+       validate();
+       XmlWriter w;
+       if(asxhtml(req)) {
+           req.outheaders().put("Content-Type", "application/xhtml+xml");
+           w = new XHtmlWriter(doc);
+       } else {
+           req.outheaders().put("Content-Type", "text/html; charset=utf-8");
+           w = new HtmlWriter(doc);
+       }
+       w.write(req.output());
+    }
 }
diff --git a/src/dolda/jsvc/next/HtmlWriter.java b/src/dolda/jsvc/next/HtmlWriter.java
new file mode 100644 (file)
index 0000000..40e5d53
--- /dev/null
@@ -0,0 +1,46 @@
+package dolda.jsvc.next;
+
+import java.io.*;
+import org.w3c.dom.*;
+
+public class HtmlWriter extends XHtmlWriter {
+    public HtmlWriter(Document doc) {
+       super(doc);
+    }
+    
+    protected boolean asempty(ColumnWriter out, Element el) {
+       if(!super.asempty(out, el))
+           return(false);
+       String n = el.getTagName();
+       if(n.equals("br") || n.equals("hr") || n.equals("img") ||
+          n.equals("input"))
+           return(true);
+       return(false);
+    }
+    
+    protected void attribute(ColumnWriter out, Attr attr, int indent) throws IOException {
+       if(attr.getNamespaceURI() != null)
+           throw(new RuntimeException("HTML does not support non-null-NS attributes (" + attr.getNamespaceURI() + " encountered)"));
+       super.attribute(out, attr, indent);
+    }
+
+    protected void element(ColumnWriter out, Element el, int indent) throws IOException {
+       if(!el.getNamespaceURI().equals(Html.ns))
+           throw(new RuntimeException("HTML does not support non-HTML elements (namespace " + el.getNamespaceURI() + " encountered)"));
+       super.element(out, el, indent);
+    }
+
+    public void write(Writer out) throws IOException {
+       DocumentType dt = doc.getDoctype();
+       if(dt == null)
+           throw(new RuntimeException("Writing HTML requires an HTML document"));
+       if(!dt.getName().equals("html"))
+           throw(new RuntimeException("Writing HTML requires an HTML document, not `" + dt.getName() + "'"));
+       String pubid = dt.getPublicId();
+       if(pubid.equals("-//W3C//DTD XHTML 1.1//EN")) {
+       } else {
+           throw(new RuntimeException("Unimplemented HTML doctype `" + pubid));
+       }
+       super.write(out);
+    }
+}
diff --git a/src/dolda/jsvc/next/XHtmlWriter.java b/src/dolda/jsvc/next/XHtmlWriter.java
new file mode 100644 (file)
index 0000000..678b3c7
--- /dev/null
@@ -0,0 +1,21 @@
+package dolda.jsvc.next;
+
+import java.io.*;
+import org.w3c.dom.*;
+
+public class XHtmlWriter extends IndentWriter {
+    public XHtmlWriter(Document doc) {
+       super(doc);
+       setnsname(Html.ns, null);
+    }
+    
+    protected int indent(ColumnWriter out, Element el) {
+       if(Html.ns.equals(el.getNamespaceURI())) {
+           if(el.getTagName().equals("pre"))
+               return(-1);
+           if(el.getTagName().equals("textarea"))
+               return(-1);
+       }
+       return(super.indent(out, el));
+    }
+}
index a1004e4..a5e698e 100644 (file)
@@ -7,7 +7,7 @@ import dolda.jsvc.util.Misc;
 
 public class XmlWriter {
     private Map<String, String> nsnames = new HashMap<String, String>();
-    private Document doc;
+    public final Document doc;
     private int nsser = 1;
     
     public XmlWriter(Document doc) {
@@ -146,7 +146,7 @@ public class XmlWriter {
     }
     
     protected void text(ColumnWriter out, String s, int indent) throws IOException {
-       out.write(s);
+       out.write(Misc.htmlq(s));
     }
 
     protected void text(ColumnWriter out, Text txt, int indent) throws IOException {
@@ -176,12 +176,16 @@ public class XmlWriter {
        }
     }
     
+    protected void doctype(ColumnWriter out, DocumentType dt) throws IOException {
+       out.write(String.format("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", dt.getName(), dt.getPublicId(), dt.getSystemId()));
+    }
+
     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()));
+           doctype(col, t);
        node(col, doc.getDocumentElement(), 0);
     }