1
2
3
4
5
6 package org.astrogrid.io.xml;
7
8 import java.io.IOException;
9 import org.astrogrid.util.DomHelper;
10 import org.w3c.dom.Element;
11
12 /***
13 * Class used to write to an Xml Element. The element is always created within
14 * the context of another element (the root element being the file).
15 * <p>
16 * Not very happy with this at the moment; XmlPrinter both subclasses this and
17 * acts as the root node, which is a bit dodgy. It means for example that there
18 * are problems writing things in the XmlTagPrinter constructor, as the XmlPrinter
19 * hasn't initialised things properly
20 */
21
22 public class XmlTagPrinter
23 {
24 private XmlTagPrinter parent = null;
25 private XmlTagPrinter child = null;
26 private String name = null;
27 private String[] attrs = null;
28 private boolean closed = false;
29
30 /*** Constructor for a new xml tag (ie, a new element). The constructor should
31 * be called using the factory method XmlTagPrinter.newTag(), so that it can
32 * be properly attached to the parent */
33 protected XmlTagPrinter(String givenName, String[] givenAttrs, XmlTagPrinter parentWriter) throws IOException
34 {
35 super();
36
37 this.parent = parentWriter;
38 this.name = givenName;
39 this.attrs = givenAttrs;
40 }
41
42 /*** Opens the tag - ie writes out the name & atts */
43 protected void open() throws IOException {
44
45 if (name != null) {
46 if ((attrs == null) || (attrs.length == 0))
47 {
48 writeLine(0,"<"+name+">");
49 }
50 else
51 {
52 writeLine(0,"<"+name+" "+concatAttrs(attrs)+">");
53 }
54 }
55 }
56
57 protected String concatAttrs(String[] attrs) {
58 StringBuffer all = new StringBuffer();
59 for (int i = 0; i < attrs.length; i++) {
60 all.append(attrs[i]+" ");
61 }
62 return all.toString();
63 }
64
65 /***
66 * Writes out the given string indented relative to this tag.
67 */
68 public void writeLine(String string) throws IOException
69 {
70 assert !closed : "Cannot write to a closed tag ["+this+"]";
71 assert !hasChild() : "Cannot write to this tag ["+this+"], it has an open child "+getChild();
72 writeLine(1, string);
73 }
74
75 /***
76 * Writes a string with the given indentation, relative to this tag.
77 * By default calls the parentWriteLine implementation - calls parent with incremented
78 * indentation, so that the indent increases for each level of tag
79 */
80 protected void writeLine(int indent, String string) throws IOException
81 {
82 parent.writeLine(indent+1, string);
83 }
84
85 /***
86 * Convenience routine to write a child tag within this element
87 */
88 public void writeTag(String tag, String value) throws IOException
89 {
90 assertTagNameValid(tag);
91 closeChild();
92 this.writeLine("<"+tag+">"+transformSpecials(value)+"</"+tag+">");
93 }
94
95 /***
96 * Convenience routine to write a child comment tag within this element
97 */
98 public void writeComment(String text) throws IOException
99 {
100 closeChild();
101 this.writeLine("<!-- "+text+" -->");
102 }
103
104 /***
105 * Convenience routine to write a child tag with attributes (given as
106 * "name='Value' other='more' " etc) within this element
107 */
108 public void writeTag(String tag, String[] attrs, String value) throws IOException
109 {
110 assertTagNameValid(tag);
111 closeChild();
112 this.writeLine("<"+tag+" "+concatAttrs(attrs)+">"+transformSpecials(value)+"</"+tag+">");
113 }
114
115 /***
116 * Convenience routine to write out an element and all its children within this element
117 */
118 public void writeTag(Element e) throws IOException
119 {
120 closeChild();
121 this.writeLine(DomHelper.ElementToString(e));
122 }
123
124 /*** Checks to see if the tag name is valid - ie no spaces, diamond brackets
125 */
126 private void assertTagNameValid(String tagName) {
127 assert tagName.indexOf(" ")==-1 : "Illegal Space in tag '"+tagName+"'";
128 assert tagName.indexOf("<")==-1 : "Illegal bracket in tag '"+tagName+"'";
129 assert tagName.indexOf(">")==-1 : "Illegal bracket in tag '"+tagName+"'";
130 }
131
132 /***
133 * Transforms special characters to safe ones (eg & to &, < to <)
134 */
135 public static String transformSpecials(String s)
136 {
137 if (s==null) {
138 return "";
139 }
140
141
142 s = s.replaceAll("&", "&");
143 s = s.replaceAll("<", "<");
144 s = s.replaceAll(">", ">");
145 return s;
146 /***/
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 }
167
168 /***
169 * Called to take the given tag and make it the child. Closes existing child.
170 * Public for use by specialist derived Tags.
171 */
172 public XmlTagPrinter newTag(XmlTagPrinter tag) throws IOException
173 {
174 closeChild();
175 child = tag;
176 child.open();
177 return child;
178 }
179
180 /***
181 * Convenience routine to make a new child tag with the given tag name and a string of attribute/values.
182 * Automatically closes existing child
183 * @param attr a string such as " name='this' size='other' "
184 */
185 public XmlTagPrinter newTag(String tag, String[] attrs) throws IOException
186 {
187 return newTag(new XmlTagPrinter(tag, attrs, this));
188 }
189
190 /***
191 * Convenience routine to make a new child tag with the given tag name
192 * Automatically closes existing child
193 */
194 public XmlTagPrinter newTag(String tag) throws IOException
195 {
196 return newTag(new XmlTagPrinter(tag, null, this));
197 }
198
199
200 /***
201 * Returns true if the tag has a child tag - implying the child tag is open */
202 public boolean hasChild()
203 {
204 return (getChild() != null);
205 }
206
207 /***
208 * Returns the child tag - only used for administration purposes by
209 * subclasses
210 */
211 protected XmlTagPrinter getChild()
212 {
213
214 if ( (child != null) && (child.closed)) {
215 child = null;
216 }
217
218 return child;
219 }
220
221
222 /*** closes the child tag */
223 public void closeChild() throws IOException
224 {
225 if (getChild() != null)
226 {
227 child.close();
228 child = null;
229 }
230 }
231
232 /***
233 * Close tag and its child tags
234 */
235 public void close() throws IOException
236 {
237 assert !closed : "Reclosing tag "+name;
238
239 closeChild();
240
241 if (name != null) {
242 writeLine(0,"</"+name+">");
243 }
244
245 closed = true;
246 }
247
248 /*** For debug and display purposes
249 */
250 public String toString()
251 {
252 return "<"+name+">";
253 }
254
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287