1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.xml;
47
48 import groovy.util.BuilderSupport;
49 import groovy.util.IndentPrinter;
50
51 import java.io.PrintWriter;
52 import java.io.Writer;
53 import java.util.Iterator;
54 import java.util.Map;
55
56 /***
57 * A helper class for creating XML or HTML markup
58 *
59 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
60 * @author Stefan Matthias Aust
61 * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
62 * @version $Revision: 1.10 $
63 */
64 public class MarkupBuilder extends BuilderSupport {
65
66 private IndentPrinter out;
67 private boolean nospace;
68 private int state;
69 private boolean nodeIsEmpty = true;
70
71 public MarkupBuilder() {
72 this(new IndentPrinter());
73 }
74
75 public MarkupBuilder(PrintWriter writer) {
76 this(new IndentPrinter(writer));
77 }
78
79 public MarkupBuilder(Writer writer) {
80 this(new IndentPrinter(new PrintWriter(writer)));
81 }
82
83 public MarkupBuilder(IndentPrinter out) {
84 this.out = out;
85 }
86
87 protected IndentPrinter getPrinter() {
88 return this.out;
89 }
90
91 protected void setParent(Object parent, Object child) {
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 protected Object createNode(Object name) {
108 toState(1, name);
109 return name;
110 }
111
112 protected Object createNode(Object name, Object value) {
113 toState(2, name);
114 out.print(">");
115 out.print(transformValue(value.toString()));
116 return name;
117 }
118
119 protected Object createNode(Object name, Map attributes, Object value) {
120 toState(1, name);
121 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
122 Map.Entry entry = (Map.Entry) iter.next();
123 out.print(" ");
124 print(transformName(entry.getKey().toString()));
125 out.print("='");
126 print(transformValue(entry.getValue().toString()));
127 out.print("'");
128 }
129 if (value != null)
130 {
131 nodeIsEmpty = false;
132 out.print(">" + transformValue(value.toString()) + "</" + name + ">");
133 }
134 return name;
135 }
136
137 protected Object createNode(Object name, Map attributes) {
138 return createNode(name, attributes, null);
139 }
140
141 protected void nodeCompleted(Object parent, Object node) {
142 toState(3, node);
143 out.flush();
144 }
145
146 protected void print(Object node) {
147 out.print(node == null ? "null" : node.toString());
148 }
149
150 protected Object getName(String methodName) {
151 return super.getName(transformName(methodName));
152 }
153
154 protected String transformName(String name) {
155 if (name.startsWith("_")) name = name.substring(1);
156 return name.replace('_', '-');
157 }
158
159 /***
160 * Returns a String with special XML characters escaped as entities so that
161 * output XML is valid. Escapes the following characters as corresponding
162 * entities:
163 * <ul>
164 * <li>\' as &quot;</li>
165 * <li>& as &amp;</li>
166 * <li>< as &lt;</li>
167 * <li>> as &gt;</li>
168 * </ul>
169 *
170 * @param value to be searched and replaced for XML special characters.
171 * @return value with XML characters escaped
172 */
173 protected String transformValue(String value) {
174
175 if (value.matches(".*&.*")) {
176 value = value.replaceAll("&", "&");
177 }
178 if (value.matches(".*//'.*")) {
179 value = value.replaceAll("//'", """);
180 }
181 if (value.matches(".*<.*")) {
182 value = value.replaceAll("<", "<");
183 }
184 if (value.matches(".*>.*")) {
185 value = value.replaceAll(">", ">");
186 }
187 return value;
188 }
189
190 private void toState(int next, Object name) {
191 switch (state) {
192 case 0:
193 switch (next) {
194 case 1:
195 case 2:
196 out.print("<");
197 print(name);
198 break;
199 case 3:
200 throw new Error();
201 }
202 break;
203 case 1:
204 switch (next) {
205 case 1:
206 case 2:
207 out.print(">");
208 if (nospace) {
209 nospace = false;
210 } else {
211 out.println();
212 out.incrementIndent();
213 out.printIndent();
214 }
215 out.print("<");
216 print(name);
217 break;
218 case 3:
219 if (nodeIsEmpty) {
220 out.print(" />");
221 }
222 break;
223 }
224 break;
225 case 2:
226 switch (next) {
227 case 1:
228 case 2:
229 throw new Error();
230 case 3:
231 out.print("</");
232 print(name);
233 out.print(">");
234 break;
235 }
236 break;
237 case 3:
238 switch (next) {
239 case 1:
240 case 2:
241 if (nospace) {
242 nospace = false;
243 } else {
244 out.println();
245 out.printIndent();
246 }
247 out.print("<");
248 print(name);
249 break;
250 case 3:
251 if (nospace) {
252 nospace = false;
253 } else {
254 out.println();
255 out.decrementIndent();
256 out.printIndent();
257 }
258 out.print("</");
259 print(name);
260 out.print(">");
261 break;
262 }
263 break;
264 }
265 state = next;
266 }
267
268 }