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.9 $
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 void setParent(Object parent, Object child) {
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 protected Object createNode(Object name) {
104 toState(1, name);
105 return name;
106 }
107
108 protected Object createNode(Object name, Object value) {
109 toState(2, name);
110 out.print(">");
111 out.print(transformValue(value.toString()));
112 return name;
113 }
114
115 protected Object createNode(Object name, Map attributes, Object value) {
116 toState(1, name);
117 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
118 Map.Entry entry = (Map.Entry) iter.next();
119 out.print(" ");
120 print(transformName(entry.getKey().toString()));
121 out.print("='");
122 print(transformValue(entry.getValue().toString()));
123 out.print("'");
124 }
125 if (value != null)
126 {
127 nodeIsEmpty = false;
128 out.print(">" + transformValue(value.toString()) + "</" + name + ">");
129 }
130 return name;
131 }
132
133 protected Object createNode(Object name, Map attributes) {
134 return createNode(name, attributes, null);
135 }
136
137 protected void nodeCompleted(Object parent, Object node) {
138 toState(3, node);
139 out.flush();
140 }
141
142 protected void print(Object node) {
143 out.print(node == null ? "null" : node.toString());
144 }
145
146 protected Object getName(String methodName) {
147 return super.getName(transformName(methodName));
148 }
149
150 protected String transformName(String name) {
151 if (name.startsWith("_")) name = name.substring(1);
152 return name.replace('_', '-');
153 }
154
155 /***
156 * Returns a String with special XML characters escaped as entities so that
157 * output XML is valid. Escapes the following characters as corresponding
158 * entities:
159 * <ul>
160 * <li>\' as &quot;</li>
161 * <li>& as &amp;</li>
162 * <li>< as &lt;</li>
163 * <li>> as &gt;</li>
164 * </ul>
165 *
166 * @param value to be searched and replaced for XML special characters.
167 * @return value with XML characters escaped
168 */
169 protected String transformValue(String value) {
170
171 if (value.matches(".*&.*")) {
172 value = value.replaceAll("&", "&");
173 }
174 if (value.matches(".*//'.*")) {
175 value = value.replaceAll("//'", """);
176 }
177 if (value.matches(".*<.*")) {
178 value = value.replaceAll("<", "<");
179 }
180 if (value.matches(".*>.*")) {
181 value = value.replaceAll(">", ">");
182 }
183 return value;
184 }
185
186 private void toState(int next, Object name) {
187 switch (state) {
188 case 0:
189 switch (next) {
190 case 1:
191 case 2:
192 out.print("<");
193 print(name);
194 break;
195 case 3:
196 throw new Error();
197 }
198 break;
199 case 1:
200 switch (next) {
201 case 1:
202 case 2:
203 out.print(">");
204 if (nospace) {
205 nospace = false;
206 } else {
207 out.println();
208 out.incrementIndent();
209 out.printIndent();
210 }
211 out.print("<");
212 print(name);
213 break;
214 case 3:
215 if (nodeIsEmpty) {
216 out.print(" />");
217 }
218 break;
219 }
220 break;
221 case 2:
222 switch (next) {
223 case 1:
224 case 2:
225 throw new Error();
226 case 3:
227 out.print("</");
228 print(name);
229 out.print(">");
230 break;
231 }
232 break;
233 case 3:
234 switch (next) {
235 case 1:
236 case 2:
237 if (nospace) {
238 nospace = false;
239 } else {
240 out.println();
241 out.printIndent();
242 }
243 out.print("<");
244 print(name);
245 break;
246 case 3:
247 if (nospace) {
248 nospace = false;
249 } else {
250 out.println();
251 out.decrementIndent();
252 out.printIndent();
253 }
254 out.print("</");
255 print(name);
256 out.print(">");
257 break;
258 }
259 break;
260 }
261 state = next;
262 }
263
264 }