X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fjrw%2Fsp%2FFormatter.java;fp=src%2Fjrw%2Fsp%2FFormatter.java;h=0791614aae0e881284acc8c66b0ee94fab8f61ac;hb=6e0043cc3f99a31bac74d6d0c7399e4c0b60d3fb;hp=0000000000000000000000000000000000000000;hpb=ad84ba3b456f1a9858fcfe1fee28b81197654c8d;p=jrw.git diff --git a/src/jrw/sp/Formatter.java b/src/jrw/sp/Formatter.java new file mode 100644 index 0000000..0791614 --- /dev/null +++ b/src/jrw/sp/Formatter.java @@ -0,0 +1,180 @@ +package jrw.sp; + +import jrw.util.*; +import java.util.*; + +public class Formatter extends LazyPChannel { + private final Element root; + private final String header; + private final List stack = new ArrayList<>(); + private final Map ns = new IdentityHashMap<>(); + private boolean headed = false; + + class Frame { + Element el; + Iterator> ai; + Iterator ci; + boolean sh; + boolean h, e, t; + + Frame(Element el) { + this.el = el; + this.ai = el.attribs.entrySet().iterator(); + this.ci = el.children.iterator(); + this.sh = shorten(el); + } + } + + private void countns(Map freq, Set attrs, Element el) { + for(Name anm : el.attribs.keySet()) { + if(anm.ns != null) { + attrs.add(anm.ns); + Integer f = freq.get(anm.ns); + freq.put(anm.ns, ((f == null) ? 0 : f) + 1); + } + } + Integer f = freq.get(el.name.ns); + freq.put(el.name.ns, ((f == null) ? 0 : f) + 1); + for(Node ch : el.children) { + if(ch instanceof Element) + countns(freq, attrs, (Element)ch); + } + } + + private void calcnsnames() { + Map freq = new IdentityHashMap<>(); + Set attrs = new HashSet<>(); + countns(freq, attrs, root); + if(freq.get(null) != null) { + ns.put(null, null); + freq.remove(null); + } else if(!attrs.contains(root.name.ns)) { + ns.put(root.name.ns, null); + freq.remove(root.name.ns); + } + List order = new ArrayList<>(freq.keySet()); + Collection ass = new HashSet<>(); + ass.add(null); + Collections.sort(order, (x, y) -> (freq.get(y) - freq.get(x))); + for(Namespace ns : order) { + String p = ns.prefabb; + if((p != null) && !ass.contains(p)) { + this.ns.put(ns, p); + ass.add(p); + } else { + int i; + if(p == null) { + p = "ns"; + i = 1; + } else { + i = 2; + } + while(ass.contains(p + i)) + i++; + this.ns.put(ns, p + i); + ass.add(p + i); + } + } + } + + public Formatter(DocType doctype, Element root) { + this.header = "\n" + doctype.format() + "\n"; + this.root = root; + calcnsnames(); + Frame rf = new Frame(root); + Map ra = new HashMap<>(root.attribs); + for(Map.Entry ent : this.ns.entrySet()) { + Namespace ns = ent.getKey(); + String abb = ent.getValue(); + if(ns == null) + continue; + ra.put(new Name((abb == null) ? "xmlns" : ("xmlns:" + abb)), ns.uri); + } + rf.ai = ra.entrySet().iterator(); + stack.add(rf); + } + + private String fmtname(Name nm) { + String abb = ns.get(nm.ns); + return((abb == null) ? nm.local : (abb + ":" + nm.local)); + } + + private String head(Element el) { + return(String.format("<%s", fmtname(el.name))); + } + + private String tail(Element el) { + return(String.format("", fmtname(el.name))); + } + + private String attrquote(String val) { + char qc; + if(val.indexOf('"') >= 0) { + qc = '\''; + val = val.replace("'", "'"); + } else { + qc = '"'; + val = val.replace("\"", """); + } + val = val.replace("&", "&"); + val = val.replace("<", "<"); + val = val.replace(">", ">"); + return(qc + val + qc); + } + + private String attr(Name nm, String value) { + String anm = (nm.ns == null) ? nm.local : fmtname(nm); + return(String.format(" %s=%s", anm, attrquote(value))); + } + + private String quote(String text) { + text = text.replace("&", "&"); + text = text.replace("<", "<"); + text = text.replace(">", ">"); + return(text); + } + + protected boolean shorten(Element el) { + return(el.children.isEmpty()); + } + + protected boolean produce() { + if(!headed) { + headed = true; + if(write(header)) + return(false); + } + if(stack.isEmpty()) + return(true); + Frame f = stack.get(stack.size() - 1); + if(!f.h && (f.h = true) && write(head(f.el))) + return(false); + while(f.ai.hasNext()) { + Map.Entry ent = f.ai.next(); + if(write(attr(ent.getKey(), ent.getValue()))) + return(false); + } + if(!f.sh) { + if(!f.e && (f.e = true) && write(">")) + return(false); + if(f.ci.hasNext()) { + Node ch = f.ci.next(); + if(ch instanceof Text) { + write(quote(((Text)ch).text)); + } else if(ch instanceof Raw) { + write(((Raw)ch).text); + } else { + stack.add(new Frame((Element)ch)); + } + return(false); + } + if(!f.t && (f.t = true) && write(tail(f.el))) + return(false); + } else { + if(!f.e && (f.e = true) && write(" />")) + return(false); + } + stack.remove(stack.size() - 1); + return(false); + } +}