+package org.ibex.xt;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class Node {
+
+ public String name = null;
+ public String cdata = null;
+ public int numattrs = 0;
+ public String[] attrs = null;
+ public String uri = null;
+ private int delta = 0;
+
+ public Node() { }
+ public Node(Node n) { copyFrom(n); }
+ public final void clear() { name = null; cdata = null; numattrs = 0; delta = 0; uri = null; }
+ public final void copyFrom(Node n) {
+ name=n.name; cdata=n.cdata; numattrs=n.numattrs; delta=n.delta; uri=n.uri;
+ if (n.attrs == null) { attrs = null; return; }
+ attrs = new String[n.attrs.length]; System.arraycopy(n.attrs, 0, attrs, 0, attrs.length); }
+ public final String attr(String key) {
+ for(int i=0; i<numattrs; i++) if (key.equals(attrs[i*2])) return attrs[i*2+1]; return null; }
+
+ public static abstract class Stream {
+ public static final Stream NULL = new Stream() { public boolean _read(Node n) { return false; } };
+ public static interface Functor { public Node.Stream wrap(Node.Stream in); }
+ public static class ConstantFunctor implements Functor {
+ private final Node.Stream stream;
+ public ConstantFunctor(Node.Stream stream) { this.stream = stream; }
+ public Node.Stream wrap(Node.Stream in) { return stream; }
+ }
+ public static abstract class Filter extends Stream {
+ Stream upstream;
+ Filter() { }
+ public Filter(Stream upstream) { this.upstream = upstream; }
+ public boolean upstreamRead(Node n) { upstream = upstream.simplify(); return upstream.read(n); }
+ public void wrapUpstream(Functor f) { upstream = f.wrap(upstream); }
+ public Filter graft(Functor f, Node n) {
+ upstream = new Graft((upstream instanceof Peekable) ? (Peekable)upstream : new Peekable(upstream), n, f);
+ return this;
+ }
+ }
+
+ public Stream simplify() { return this; }
+ protected abstract boolean _read(Node n);
+ public final boolean read(Node n) { n.clear(); if (!_read(n)) { n.clear(); return false; } return true; }
+
+ public static class Peekable extends Filter {
+ public Peekable(Stream s) { super(s); }
+ private Node pending = null;
+ public boolean peek(Node n) {
+ if (pending == null) {
+ Node n2 = new Node();
+ if (!upstreamRead(n2)) return false;
+ pending = n2;
+ }
+ n.copyFrom(pending);
+ return true;
+ }
+ public boolean _read(Node n) {
+ if (pending != null) { n.copyFrom(pending); pending = null; return true; }
+ return upstreamRead(n);
+ }
+ }
+
+ private static class Graft extends Filter {
+ private Stream inner2;
+ private Peekable a;
+ int total = 0;
+ int net = 0;
+ boolean simple = false;
+ private Node pending = new Node();
+ public Graft(final Peekable a, final Node n, final Functor f) {
+ this.a = a;
+ upstream = inner2 = new Stream() {
+ public boolean _read(Node n) {
+ if (!Graft.this.a.peek(n)) return false;
+ if (net + n.delta <= 0) return false;
+ Graft.this.a.read(n);
+ net += n.delta;
+ return true;
+ } };
+ wrapUpstream(f);
+ if (__read(pending)) pending.delta = n.delta;
+ total = n.delta;
+ }
+ public boolean _read(Node n) {
+ if (pending != null) { n.copyFrom(pending); pending = null; return true; }
+ boolean ret = __read(n);
+ if (ret) total += n.delta;
+ return ret;
+ }
+ public boolean __read(Node n) {
+ if (simple) return a.read(n);
+ if (upstreamRead(n)) return true;
+ while(inner2.read(n));
+ if (!a.read(n)) return false;
+ n.delta += net - total;
+ simple = true;
+ return true;
+ }
+ }
+
+ public static class FromXML extends Node.Stream {
+ private final XML.Pull xml;
+ private XML.Element parent = null;
+ private XML.Element e;
+ private int currentdelta = 0;
+ public FromXML(Reader r) { this.xml = new XML.Pull(r); }
+ protected boolean _read(Node n) { try {
+ Object ret = xml.read();
+ if (ret == null) return false;
+ if (ret instanceof String) {
+ n.cdata = (String)ret;
+ n.delta = xml.level - currentdelta;
+ currentdelta = xml.level;
+ return true;
+ }
+ XML.Element e = (XML.Element)ret;
+ n.name = e.getLocalName();
+ n.uri = e.getUri();
+ n.numattrs = e.getAttrLen();
+ n.delta = e.level - currentdelta;
+ currentdelta = e.level;
+ if (n.attrs == null || n.attrs.length < n.numattrs*2) n.attrs = new String[n.numattrs*4];
+ for(int i=0; i<n.numattrs; i++) { n.attrs[i*2] = e.getAttrKey(i); n.attrs[i*2+1] = e.getAttrVal(i); }
+ return true;
+ } catch (Exception e) { throw new RuntimeException(e); } }
+ }
+
+ public void toXML(Writer writer) throws IOException { Node n = new Node(); if (read(n)) toXML(writer, n); }
+ private Node toXML(Writer w, Node n) throws IOException {
+ final String name = n.name;
+ if (n.cdata != null) {
+ w.write(n.cdata);
+ if (!read(n)) n = null;
+ } else {
+ w.write("<");
+ w.write(name);
+ for(int i=0; i < n.numattrs * 2; i+=2) {
+ w.write(" ");
+ w.write(n.attrs[i]);
+ w.write("=\"");
+ w.write(n.attrs[i+1]);
+ w.write("\"");
+ }
+ if (!read(n)) n = null;
+ if (n == null || n.delta <= 0) {
+ w.write("/>");
+ } else {
+ w.write(">");
+ while(n != null && n.delta > 0) n = toXML(w, n);
+ w.write("</");
+ w.write(name);
+ w.write(">");
+ }
+ }
+ if (n != null) n.delta++;
+ return n;
+ }
+ }
+}