33
33
import org.culturegraph.mf.util.ResourceUtil;
34
34
35
35
/**
36
- *
36
+ *
37
37
* writes a stream to XML
38
- *
39
- * @author Markus Michael Geipel
40
- *
38
+ *
39
+ * @author Markus Michael Geipel, Christoph Böhme
40
+ *
41
41
*/
42
42
@Description("writes a stream to xml")
43
43
@In(StreamReceiver.class)
44
44
@Out(String.class)
45
45
public final class SimpleXmlWriter extends DefaultStreamPipe<ObjectReceiver<String>> {
46
+
46
47
public static final String ATTRIBUTE_MARKER = "~";
47
- // public static final String TEXT_CONTENT_MARKER = "_text";
48
48
public static final String NAMESPACES = "namespaces";
49
- public static final String NEW_LINE = "\n";
50
49
51
- private Element element;
50
+ public static final String DEFAULT_ROOT_TAG = "records";
51
+ public static final String DEFAULT_RECORD_TAG = "record";
52
+
53
+ private static final String NEW_LINE = "\n";
54
+ private static final String INDENT = "\t";
55
+
56
+ private static final String BEGIN_ATTRIBUTE = "=\"";
57
+ private static final String END_ATTRIBUTE = "\"";
58
+ private static final String BEGIN_OPEN_ELEMENT = "<";
59
+ private static final String END_OPEN_ELEMENT = ">";
60
+ private static final String END_EMPTY_ELEMENT = " />";
61
+ private static final String BEGIN_CLOSE_ELEMENT = "</";
62
+ private static final String END_CLOSE_ELEMENT = ">";
63
+
64
+ private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
65
+ private static final String XMLNS_MARKER = " xmlns:";
66
+
67
+ private final StringBuilder builder = new StringBuilder();
68
+
69
+ private String rootTag = DEFAULT_ROOT_TAG;
70
+ private String recordTag = DEFAULT_RECORD_TAG;
52
71
private Map<String, String> namespaces = new HashMap<String, String>();
53
- private String recordTag = "record";
54
- private String rootTag = "records";
55
- private boolean start = true;
56
- private boolean separateRoots;
57
72
private boolean writeXmlHeader = true;
73
+ private boolean separateRoots;
74
+
75
+ private Element element;
76
+ private boolean atStreamStart = true;
58
77
59
78
public void setRootTag(final String rootTag) {
60
79
this.rootTag = rootTag;
61
80
}
62
81
63
- public void setWriteXmlHeader(final boolean writeXmlHeader) {
64
- this.writeXmlHeader = writeXmlHeader;
65
- }
66
-
67
- public void setSeparateRoots(final boolean separateRoots) {
68
- this.separateRoots = separateRoots;
82
+ public void setRecordTag(final String tag) {
83
+ recordTag = tag;
69
84
}
70
85
71
86
public void setNamespaceFile(final String file) {
72
87
final Properties properties = ResourceUtil.loadProperties(file);
73
- for (Entry<Object, Object> entry : properties.entrySet()) {
88
+ for (final Entry<Object, Object> entry : properties.entrySet()) {
74
89
namespaces.put(entry.getKey().toString(), entry.getValue().toString());
75
90
}
76
91
}
77
92
78
- private void writeHeader() {
79
- final StringBuilder builder = new StringBuilder();
93
+ public void setWriteXmlHeader(final boolean writeXmlHeader) {
94
+ this.writeXmlHeader = writeXmlHeader;
95
+ }
80
96
81
- if (writeXmlHeader ) {
82
- builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") ;
83
- }
97
+ public void setSeparateRoots(final boolean separateRoots ) {
98
+ this.separateRoots = separateRoots ;
99
+ }
84
100
85
- builder.append("<");
86
- builder.append(rootTag);
87
- for (Entry<String, String> entry : namespaces.entrySet()) {
88
- builder.append(" xmlns:");
89
- builder.append(entry.getKey());
90
- builder.append("=\"");
91
- escape(builder, entry.getValue());
92
- builder.append("\"");
93
- }
94
- builder.append(">");
95
- getReceiver().process(builder.toString());
96
- start = false;
101
+ public void configure(final MultiMap multimap) {
102
+ this.namespaces = multimap.getMap(NAMESPACES);
97
103
}
98
104
99
105
@Override
100
106
public void startRecord(final String identifier) {
101
- if (separateRoots || start) {
107
+ if (separateRoots) {
108
+ writeHeader();
109
+ } else if (atStreamStart) {
102
110
writeHeader();
111
+ sendAndClearData();
103
112
}
113
+ atStreamStart = false;
114
+
104
115
element = new Element(recordTag);
105
116
}
106
117
107
118
@Override
108
119
public void endRecord() {
109
- if (recordTag.isEmpty()) {
110
- final StringBuilder builder = new StringBuilder();
111
- for (Element child : element.getChildren()) {
112
- child.writeToStringBuilder(builder, 1);
113
- }
114
- getReceiver().process(builder.toString());
115
- } else {
116
- getReceiver().process(element.toString());
117
- }
120
+ element.writeElement(builder, 1);
118
121
if (separateRoots) {
119
122
writeFooter();
120
123
}
124
+ sendAndClearData();
121
125
}
122
126
123
127
@Override
@@ -137,42 +141,106 @@ public void literal(final String name, final String value) {
137
141
} else if (name.startsWith(ATTRIBUTE_MARKER)) {
138
142
element.addAttribute(name.substring(1), value);
139
143
} else {
140
- final Element temp = element.createChild(name);
141
- temp.setText(value);
144
+ element.createChild(name).setText(value);
142
145
}
143
146
}
144
147
145
- public void configure(final MultiMap multimap) {
146
- this.namespaces = multimap.getMap(NAMESPACES);
147
- }
148
-
149
- public void setRecordTag(final String tag) {
150
- recordTag = tag;
148
+ @Override
149
+ protected void onResetStream() {
150
+ writeFooter();
151
+ sendAndClearData();
152
+ atStreamStart = true;
151
153
}
152
154
153
155
@Override
154
156
protected void onCloseStream() {
155
157
if (!separateRoots) {
156
158
writeFooter();
159
+ sendAndClearData();
157
160
}
158
161
}
159
162
163
+ private void sendAndClearData() {
164
+ getReceiver().process(builder.toString());
165
+ builder.delete(0, builder.length());
166
+ }
167
+
168
+ private void writeHeader() {
169
+ if (writeXmlHeader) {
170
+ builder.append(XML_HEADER);
171
+ }
172
+
173
+ builder.append(BEGIN_OPEN_ELEMENT);
174
+ builder.append(rootTag);
175
+ for (final Entry<String, String> entry : namespaces.entrySet()) {
176
+ builder.append(XMLNS_MARKER);
177
+ builder.append(entry.getKey());
178
+ builder.append(BEGIN_ATTRIBUTE);
179
+ writeEscaped(builder, entry.getValue());
180
+ builder.append(END_ATTRIBUTE);
181
+ }
182
+ builder.append(END_OPEN_ELEMENT);
183
+ }
184
+
160
185
private void writeFooter() {
161
- getReceiver().process("</" + rootTag + ">");
186
+ builder.append(NEW_LINE);
187
+ builder.append(BEGIN_CLOSE_ELEMENT);
188
+ builder.append(rootTag);
189
+ builder.append(END_CLOSE_ELEMENT);
190
+ }
191
+
192
+ protected static void writeEscaped(final StringBuilder builder, final String str) {
193
+
194
+ final int len = str.length();
195
+ for (int i = 0; i < len; ++i) {
196
+ final char c = str.charAt(i);
197
+ final String entityName;
198
+ switch (c) {
199
+ case '&':
200
+ entityName = "amp";
201
+ break;
202
+ case '<':
203
+ entityName = "lt";
204
+ break;
205
+ case '>':
206
+ entityName = "gt";
207
+ break;
208
+ case '\'':
209
+ entityName = "apos";
210
+ break;
211
+ case '"':
212
+ entityName = "quot";
213
+ break;
214
+ default:
215
+ entityName = null;
216
+ break;
217
+ }
218
+
219
+ if (entityName == null) {
220
+ builder.append(c);
221
+ } else {
222
+ builder.append('&');
223
+ builder.append(entityName);
224
+ builder.append(';');
225
+ }
226
+ }
162
227
}
163
228
164
229
/**
230
+ * An XML element.
165
231
*
166
232
*/
167
233
private static final class Element {
234
+
168
235
private static final List<Element> NO_CHILDREN = Collections.emptyList();
169
236
170
237
private final StringBuilder attributes = new StringBuilder();
171
- private String text = "";
172
- private List<Element> children = NO_CHILDREN;
173
238
private final Element parent;
174
239
private final String name;
175
240
241
+ private String text = "";
242
+ private List<Element> children = NO_CHILDREN;
243
+
176
244
public Element(final String name) {
177
245
this.name = name;
178
246
this.parent = null;
@@ -183,16 +251,12 @@ private Element(final String name, final Element parent) {
183
251
this.parent = parent;
184
252
}
185
253
186
- public List<Element> getChildren() {
187
- return children;
188
- }
189
-
190
254
public void addAttribute(final String name, final String value) {
191
255
attributes.append(" ");
192
256
attributes.append(name);
193
- attributes.append("=\"" );
194
- escape (attributes, value);
195
- attributes.append("\"" );
257
+ attributes.append(BEGIN_ATTRIBUTE );
258
+ writeEscaped (attributes, value);
259
+ attributes.append(END_ATTRIBUTE );
196
260
}
197
261
198
262
public void setText(final String text) {
@@ -212,90 +276,44 @@ public Element getParent() {
212
276
return parent;
213
277
}
214
278
215
- @Override
216
- public String toString() {
217
- final StringBuilder builder = new StringBuilder();
218
- writeToStringBuilder(builder, 1);
219
- return builder.toString();
220
- }
221
-
222
- public void writeToStringBuilder(final StringBuilder builder, final int indent) {
223
- builder.append(NEW_LINE);
224
- indent(builder, indent);
225
- builder.append("<");
226
- builder.append(name);
227
- builder.append(attributes);
228
- if (text.isEmpty() && children.isEmpty()) {
229
- builder.append(" /");
279
+ public void writeElement(final StringBuilder builder, final int indent) {
280
+ if (!name.isEmpty()) {
281
+ builder.append(NEW_LINE);
282
+ writeIndent(builder, indent);
283
+ builder.append(BEGIN_OPEN_ELEMENT);
284
+ builder.append(name);
285
+ builder.append(attributes);
286
+ if (text.isEmpty() && children.isEmpty()) {
287
+ builder.append(END_EMPTY_ELEMENT);
288
+ return;
289
+ }
290
+ builder.append(END_OPEN_ELEMENT);
230
291
}
231
292
232
- builder.append(">" );
293
+ writeEscaped(builder, text );
233
294
234
- escape(builder, text);
235
-
236
- for (Element element : children) {
237
- element.writeToStringBuilder(builder, indent + 1);
295
+ for (final Element element : children) {
296
+ element.writeElement(builder, indent + 1);
238
297
}
298
+
239
299
if (text.isEmpty() && !children.isEmpty()) {
240
300
builder.append(NEW_LINE);
241
- indent (builder, indent);
301
+ writeIndent (builder, indent);
242
302
}
243
303
244
- if (!text.isEmpty() || !children .isEmpty()) {
245
- builder.append("</" );
304
+ if (!name .isEmpty()) {
305
+ builder.append(BEGIN_CLOSE_ELEMENT );
246
306
builder.append(name);
247
- builder.append(">" );
307
+ builder.append(END_CLOSE_ELEMENT );
248
308
}
249
309
}
250
310
251
- private static void indent (final StringBuilder builder, final int indent) {
311
+ private static void writeIndent (final StringBuilder builder, final int indent) {
252
312
for (int i = 0; i < indent; ++i) {
253
- builder.append("\t" );
313
+ builder.append(INDENT );
254
314
}
255
315
}
256
- }
257
316
258
- @Override
259
- protected void onResetStream() {
260
- writeFooter();
261
- start = true;
262
- }
263
-
264
- protected static void escape(final StringBuilder builder, final String str) {
265
-
266
- final int len = str.length();
267
- for (int i = 0; i < len; ++i) {
268
- final char c = str.charAt(i);
269
- final String entityName;
270
- switch (c) {
271
- case '&':
272
- entityName = "amp";
273
- break;
274
- case '<':
275
- entityName = "lt";
276
- break;
277
- case '>':
278
- entityName = "gt";
279
- break;
280
- case '\'':
281
- entityName = "apos";
282
- break;
283
- case '"':
284
- entityName = "quot";
285
- break;
286
- default:
287
- entityName = null;
288
- break;
289
- }
290
-
291
- if (entityName == null) {
292
- builder.append(c);
293
- } else {
294
- builder.append('&');
295
- builder.append(entityName);
296
- builder.append(';');
297
- }
298
- }
299
317
}
300
318
301
319
}
0 commit comments