Commit | Line | Data |
---|---|---|
6e40b32e FT |
1 | package dolda.jsvc.next; |
2 | ||
3 | import java.io.*; | |
4 | import java.util.*; | |
5 | import org.w3c.dom.*; | |
6 | import dolda.jsvc.util.Misc; | |
7 | ||
8 | public class XmlWriter { | |
9 | private Map<String, String> nsnames = new HashMap<String, String>(); | |
10 | private Document doc; | |
11 | private int nsser = 1; | |
12 | ||
13 | public XmlWriter(Document doc) { | |
14 | this.doc = doc; | |
15 | } | |
16 | ||
17 | public void setnsname(String uri, String name) { | |
18 | nsnames.put(uri, name); | |
19 | } | |
20 | ||
21 | private String nsname(String uri) { | |
22 | String ret; | |
23 | if(nsnames.containsKey(uri)) | |
24 | return(nsnames.get(uri)); | |
25 | do { | |
26 | ret = "n" + (nsser++); | |
27 | } while(nsnames.containsValue(ret)); | |
28 | nsnames.put(uri, ret); | |
29 | return(ret); | |
30 | } | |
31 | ||
32 | protected void findallnsnames() { | |
33 | Node n = doc; | |
34 | while(true) { | |
35 | String ns = n.getNamespaceURI(); | |
36 | if(ns != null) | |
37 | nsname(ns); | |
38 | if(n.getFirstChild() != null) { | |
39 | n = n.getFirstChild(); | |
40 | } else if(n.getNextSibling() != null) { | |
41 | n = n.getNextSibling(); | |
42 | } else { | |
43 | for(n = n.getParentNode(); n != null; n = n.getParentNode()) { | |
44 | if(n.getNextSibling() != null) { | |
45 | n = n.getNextSibling(); | |
46 | break; | |
47 | } | |
48 | } | |
49 | if(n == null) | |
50 | break; | |
51 | } | |
52 | } | |
53 | } | |
54 | ||
55 | protected boolean prebreak(ColumnWriter out, Element el) { | |
56 | return(false); | |
57 | } | |
58 | ||
59 | protected int indent(ColumnWriter out, Element el) { | |
60 | return(-1); | |
61 | } | |
62 | ||
63 | protected boolean postbreak(ColumnWriter out, Element el) { | |
64 | return(false); | |
65 | } | |
66 | ||
67 | protected boolean asempty(ColumnWriter out, Element el) { | |
68 | return(true); | |
69 | } | |
70 | ||
71 | protected void attribute(ColumnWriter out, String nm, String val, int indent) throws IOException { | |
72 | char qt = '\"'; | |
73 | if((val.indexOf("\"") >= 0) && (val.indexOf('\'') < 0)) | |
74 | qt = '\''; | |
75 | out.write(" " + nm + "=" + qt); | |
76 | for(int i = 0; i < val.length(); i++) { | |
77 | char c = val.charAt(i); | |
78 | if(c == '<') | |
79 | out.write("<"); | |
80 | else if(c == '>') | |
81 | out.write(">"); | |
82 | else if(c == '&') | |
83 | out.write("&"); | |
84 | else if(c == qt) | |
85 | out.write((c == '\'')?"'":"""); | |
86 | else | |
87 | out.write(c); | |
88 | } | |
89 | out.write(qt); | |
90 | } | |
91 | ||
92 | protected void attribute(ColumnWriter out, Attr attr, int indent) throws IOException { | |
93 | String ns = attr.getNamespaceURI(); | |
94 | if(ns == null) | |
95 | attribute(out, attr.getName(), attr.getValue(), indent); | |
96 | else | |
97 | attribute(out, nsname(ns) + ":" + attr.getName(), attr.getValue(), indent); | |
98 | } | |
99 | ||
100 | protected void element(ColumnWriter out, Element el, int indent) throws IOException { | |
101 | if(prebreak(out, el)) | |
102 | out.indent(indent); | |
103 | ||
104 | String tagname = el.getTagName(); | |
105 | String ns = nsname(el.getNamespaceURI()); | |
106 | if(ns != null) | |
107 | tagname = ns + ":" + tagname; | |
108 | out.write("<" + tagname); | |
109 | NamedNodeMap attrs = el.getAttributes(); | |
110 | int acol = out.col + 1; | |
111 | if(attrs != null) { | |
112 | for(int i = 0; i < attrs.getLength(); i++) { | |
113 | Attr attr = (Attr)attrs.item(i); | |
114 | attribute(out, attr, acol); | |
115 | } | |
116 | } | |
117 | if(el == doc.getDocumentElement()) { | |
118 | for(Map.Entry<String, String> nd : nsnames.entrySet()) { | |
119 | String nm = nd.getValue(); | |
120 | if(nm == null) | |
121 | attribute(out, "xmlns", nd.getKey(), acol); | |
122 | else | |
123 | attribute(out, "xmlns:" + nm, nd.getKey(), acol); | |
124 | } | |
125 | } | |
126 | ||
127 | if((el.getFirstChild() == null) && asempty(out, el)) { | |
128 | out.write(" />"); | |
129 | } else { | |
130 | out.write(">"); | |
131 | int inner = indent(out, el); | |
132 | if(inner >= 0) { | |
133 | out.indent(indent + inner); | |
134 | } | |
135 | ||
136 | for(Node ch = el.getFirstChild(); ch != null; ch = ch.getNextSibling()) | |
137 | node(out, ch, (inner >= 0)?(indent + inner):indent); | |
138 | ||
139 | if(inner >= 0) | |
140 | out.indent(indent); | |
141 | out.write("</" + tagname + ">"); | |
142 | } | |
143 | ||
144 | if(postbreak(out, el)) | |
938bdee1 | 145 | out.write('\n'); |
6e40b32e FT |
146 | } |
147 | ||
148 | protected void text(ColumnWriter out, String s, int indent) throws IOException { | |
149 | out.write(s); | |
150 | } | |
151 | ||
152 | protected void text(ColumnWriter out, Text txt, int indent) throws IOException { | |
153 | String s = txt.getData(); | |
154 | text(out, s, indent); | |
155 | } | |
156 | ||
157 | protected void comment(ColumnWriter out, Comment c, int indent) throws IOException { | |
158 | out.write("<!--"); | |
159 | String s = c.getData(); | |
160 | text(out, s, indent); | |
161 | out.write("-->"); | |
162 | } | |
163 | ||
164 | protected void node(ColumnWriter out, Node n, int indent) throws IOException { | |
165 | if(n instanceof Element) { | |
166 | Element el = (Element)n; | |
167 | element(out, el, indent); | |
168 | } else if(n instanceof Text) { | |
169 | Text txt = (Text)n; | |
170 | text(out, txt, indent); | |
171 | } else if(n instanceof Comment) { | |
172 | Comment c = (Comment)n; | |
173 | comment(out, c, indent); | |
174 | } else { | |
175 | throw(new RuntimeException(String.format("Unknown DOM node encountered (%s)", n.getClass()))); | |
176 | } | |
177 | } | |
178 | ||
179 | public void write(Writer out) throws IOException { | |
180 | findallnsnames(); | |
181 | ColumnWriter col = new ColumnWriter(out); | |
182 | DocumentType t = doc.getDoctype(); | |
183 | if(t != null) | |
184 | out.write(String.format("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", t.getName(), t.getPublicId(), t.getSystemId())); | |
185 | node(col, doc.getDocumentElement(), 0); | |
186 | } | |
187 | ||
188 | public void write(OutputStream out) throws IOException { | |
189 | /* The OutputStreamWriter may need to be flushed to clear any | |
190 | * internal buffers it may have, but it would be a pity to | |
191 | * force-flush the underlying stream just because of that. */ | |
192 | class FlushGuard extends FilterOutputStream { | |
193 | FlushGuard(OutputStream out) { | |
194 | super(out); | |
195 | } | |
196 | ||
197 | public void flush() {} | |
198 | } | |
199 | Writer w = new OutputStreamWriter(new FlushGuard(out), Misc.utf8); | |
200 | w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); | |
201 | write(w); | |
202 | w.flush(); | |
203 | } | |
6e40b32e | 204 | } |