1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.xolite.impl;
17
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import net.sf.xolite.XMLSerializeException;
26 import net.sf.xolite.XMLSerializer;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31
32
33
34
35
36
37
38 public abstract class BaseXMLSerializer implements XMLSerializer {
39
40
41 private static Log log = LogFactory.getLog(BaseXMLSerializer.class);
42
43 protected static final int STATE_INIT = 0;
44 protected static final int STATE_ELEMENT_START_OPENED = 1;
45 protected static final int STATE_ELEMENT_START_CLOSED = 2;
46 protected static final int STATE_TEXT = 3;
47 protected static final int STATE_TEXT_MULTI_LINE = 4;
48 protected static final int STATE_ELEMENT_CLOSED = 5;
49 protected static final int STATE_FINISHED = -1;
50
51 private int state = STATE_INIT;
52 private int level = -1;
53 private List<LevelState> levelStates;
54 private int attributeIndex;
55 private Map<Object, Object> customObjects;
56
57
58 protected BaseXMLSerializer() {
59 levelStates = new ArrayList<LevelState>();
60 }
61
62
63 protected int getLevel() {
64 return level;
65 }
66
67
68 public void startDocument() throws XMLSerializeException {
69 state = STATE_INIT;
70 level = -1;
71 try {
72 startDocumentImpl();
73 } catch (XMLSerializeException xse) {
74 throw xse;
75 } catch (Exception e) {
76 throw new XMLSerializeException("Cannot start document", e);
77 }
78 }
79
80
81 public void endDocument() throws XMLSerializeException {
82 if (level >= 0) throw new XMLSerializeException("All tags are not closed at the end of the document");
83 try {
84 endDocumentImpl();
85 } catch (XMLSerializeException xse) {
86 throw xse;
87 } catch (Exception e) {
88 throw new XMLSerializeException("Cannot end document", e);
89 }
90 state = STATE_FINISHED;
91 }
92
93
94 private LevelState increaseLevel() throws XMLSerializeException {
95 if ((level < 0) && (state != STATE_INIT))
96 throw new XMLSerializeException("Bad documents state: " + state + ", should be STATE_INIT");
97 LevelState levelSt;
98 level++;
99 int len = levelStates.size();
100 if (level < len) levelSt = levelStates.get(level);
101 else if (level == len) {
102 LevelState parent = (level == 0) ? null : (LevelState) levelStates.get(level - 1);
103 levelSt = new LevelState(parent);
104 levelStates.add(levelSt);
105 } else {
106 throw new IllegalStateException("Invalid level=" + level + " when levelStates.size()=" + len);
107 }
108 return levelSt;
109 }
110
111
112 private void decreaseLevel() throws XMLSerializeException {
113 if (level < 0) throw new XMLSerializeException("No more XML element open");
114 LevelState levelSt = levelStates.get(level);
115 levelSt.clear();
116 level--;
117 }
118
119
120 private LevelState getLevelState() {
121 return levelStates.get(level);
122 }
123
124
125 private LevelState getNextLevelState() throws XMLSerializeException {
126 LevelState levelSt = increaseLevel();
127 level--;
128 return levelSt;
129 }
130
131
132
133
134
135 protected String getLastElementName() {
136 return getLevelState().getCurrentTag();
137 }
138
139
140
141
142
143 protected int getLastElementLength() {
144 LevelState lvlState = getLevelState();
145 int len = lvlState.getCurrentTag().length();
146 String prefix = lvlState.getCurrentPrefix();
147 if ((prefix != null) && !prefix.equals("")) {
148 len += prefix.length() + 1;
149 }
150 return len;
151 }
152
153
154 private void closeElementStart(boolean complexContent) throws XMLSerializeException {
155 if (state == STATE_ELEMENT_START_OPENED) {
156 try {
157 closeElementStartImpl(complexContent);
158 } catch (XMLSerializeException xse) {
159 throw xse;
160 } catch (Exception e) {
161 throw new XMLSerializeException("Cannot finalize element start", e);
162 }
163 state = STATE_ELEMENT_START_CLOSED;
164 }
165 }
166
167
168
169
170
171 public void startPrefixMapping(String prefix, String namespaceUri) throws XMLSerializeException {
172 if (prefix == null)
173 throw new XMLSerializeException("Cannot define mapping for null prefix, use \"\" to define default prefix");
174 if ((namespaceUri == null) || "".equals(namespaceUri))
175 throw new XMLSerializeException("Cannot define mapping for null namespaceUri");
176
177 if (state == STATE_ELEMENT_START_OPENED) {
178
179 try {
180 LevelState levelSt = getLevelState();
181 if (levelSt.mapPrefix(prefix, namespaceUri)) definePrefixMapping(prefix, namespaceUri);
182 } catch (XMLSerializeException xse) {
183 throw xse;
184 } catch (Exception e) {
185 throw new XMLSerializeException("Cannot define prefix mapping " + prefix + "=" + namespaceUri, e);
186 }
187 } else {
188
189 LevelState levelSt = getNextLevelState();
190 levelSt.mapPrefix(prefix, namespaceUri);
191 }
192 }
193
194
195 public void startElement(String uri, String tag) throws XMLSerializeException {
196 if (tag == null) throw new XMLSerializeException("Element Tag cannot be null");
197 if (tag.length() == 0) throw new XMLSerializeException("Element Tag cannot be empty string");
198 closeElementStart(true);
199 LevelState levelSt = increaseLevel();
200 if (log.isDebugEnabled()) log.debug("Start element <" + tag + "> level = " + level);
201 if (uri == null) uri = levelSt.getDefaultNamespaceUri();
202 String prefix = levelSt.getPrefix(uri);
203 levelSt.setCurrentValues(uri, prefix, tag);
204 attributeIndex = 0;
205 try {
206 startElementImpl(uri, prefix, tag);
207
208 for (Iterator<String> iter = levelSt.getNamespaceDefinitons(); iter.hasNext();) {
209 String defUri = iter.next();
210 String defPrefix = levelSt.getPrefix(defUri);
211 definePrefixMapping(defPrefix, defUri);
212 }
213 } catch (XMLSerializeException xse) {
214 throw xse;
215 } catch (Exception e) {
216 throw new XMLSerializeException("Cannot start element", e);
217 }
218 state = STATE_ELEMENT_START_OPENED;
219 }
220
221
222 public void attribute(String name, String value) throws XMLSerializeException {
223 attribute(null, name, value);
224 }
225
226
227 public void attribute(String uri, String name, String value) throws XMLSerializeException {
228 if (state != STATE_ELEMENT_START_OPENED)
229 throw new XMLSerializeException("writeAttribute must be called just after writeElementStart");
230 if (value == null) value = "";
231 LevelState levelSt = getLevelState();
232 String prefix = levelSt.getPrefix(uri);
233 try {
234 attributeImpl(uri, prefix, name, value, attributeIndex);
235 attributeIndex++;
236 } catch (XMLSerializeException xse) {
237 throw xse;
238 } catch (Exception e) {
239 throw new XMLSerializeException("Cannot serialize attribute", e);
240 }
241 }
242
243
244 public void characters(String text) throws XMLSerializeException {
245 if ((text != null) && (!text.equals(""))) {
246 closeElementStart(false);
247 try {
248 charactersImpl(text, false);
249 } catch (XMLSerializeException xse) {
250 throw xse;
251 } catch (Exception e) {
252 throw new XMLSerializeException("Cannot serialize text", e);
253 }
254 state = STATE_TEXT;
255 }
256 }
257
258
259 public void charactersMultiLine(String text) throws XMLSerializeException {
260 if ((text != null) && (!text.equals(""))) {
261 closeElementStart(false);
262 try {
263 charactersImpl(text, true);
264 } catch (XMLSerializeException xse) {
265 throw xse;
266 } catch (Exception e) {
267 throw new XMLSerializeException("Cannot serialize text", e);
268 }
269 state = STATE_TEXT_MULTI_LINE;
270 }
271 }
272
273
274 public void endElement(String uri, String tag) throws XMLSerializeException {
275 LevelState levelSt = getLevelState();
276 levelSt.checkCurrentValues(uri, tag);
277 String prefix = levelSt.getPrefix(uri);
278 try {
279 if (state == STATE_ELEMENT_START_OPENED) {
280 endInlineElementImpl(uri, prefix, tag);
281 } else if ((state == STATE_ELEMENT_START_CLOSED) || (state == STATE_TEXT) || (state == STATE_TEXT_MULTI_LINE)) {
282 endTextElementImpl(uri, prefix, tag, state == STATE_TEXT_MULTI_LINE);
283 } else {
284 endComplexElementImpl(uri, prefix, tag);
285 }
286 } catch (XMLSerializeException xse) {
287 throw xse;
288 } catch (Exception e) {
289 throw new XMLSerializeException("Cannot end element", e);
290 }
291 decreaseLevel();
292 if (log.isDebugEnabled()) log.debug("End element <" + tag + "> leval = " + level);
293 state = STATE_ELEMENT_CLOSED;
294 }
295
296
297 public void simpleElement(String namespaceUri, String localName, String text) throws XMLSerializeException {
298 startElement(namespaceUri, localName);
299 characters(text);
300 endElement(namespaceUri, localName);
301 }
302
303
304
305
306
307 protected abstract void startDocumentImpl() throws Exception;
308
309
310 protected abstract void endDocumentImpl() throws Exception;
311
312
313 protected abstract void startElementImpl(String uri, String prefix, String tag) throws Exception;
314
315
316 protected abstract void attributeImpl(String uri, String prefix, String name, String value, int index) throws Exception;
317
318
319 protected abstract void closeElementStartImpl(boolean complexContent) throws Exception;
320
321
322 protected abstract void charactersImpl(String text, boolean multilineContent) throws Exception;
323
324
325 protected abstract void endInlineElementImpl(String uri, String prefix, String tag) throws Exception;
326
327
328 protected abstract void endTextElementImpl(String uri, String prefix, String tag, boolean multilineContent)
329 throws Exception;
330
331
332 protected abstract void endComplexElementImpl(String uri, String prefix, String tag) throws Exception;
333
334
335 protected void definePrefixMapping(String prefix, String uri) throws Exception {
336 String xmlns = "xmlns";
337 if ("".equals(prefix)) {
338 prefix = "xmlns";
339 xmlns = null;
340 }
341 attributeImpl(uri, xmlns, prefix, uri, attributeIndex);
342 attributeIndex++;
343 }
344
345
346 public Object getCustomObject(Object key) {
347 return (customObjects == null) ? null : customObjects.get(key);
348 }
349
350
351 public void putCustomObject(Object key, Object value) {
352 if (customObjects == null) customObjects = new HashMap<Object, Object>();
353 customObjects.put(key, value);
354 }
355
356
357
358
359
360
361
362
363 private static class LevelState {
364
365
366 private String defaultNamespaceUri;
367 private String currentNamespaceUri;
368 private String currentPrefix;
369 private String currentTag;
370 private Map<String, String> prefixMappings;
371 private LevelState parent;
372
373
374 LevelState(LevelState parentState) {
375 parent = parentState;
376 prefixMappings = new HashMap<String, String>();
377 }
378
379
380 String getCurrentPrefix() {
381 return currentPrefix;
382 }
383
384
385 String getCurrentTag() {
386 return currentTag;
387 }
388
389
390 void clear() {
391 defaultNamespaceUri = null;
392 currentNamespaceUri = null;
393 currentPrefix = null;
394 currentTag = null;
395 prefixMappings.clear();
396 }
397
398
399 void setCurrentValues(String uri, String prefix, String tag) {
400 currentNamespaceUri = uri;
401 currentPrefix = prefix;
402 currentTag = tag;
403 }
404
405
406 void checkCurrentValues(String uri, String tag) throws XMLSerializeException {
407 if (!checkEquals(currentNamespaceUri, uri))
408 throw new XMLSerializeException("The closed element namespace URI is not the same as the opened one: "
409 + currentNamespaceUri + " != " + uri);
410 if (!checkEquals(currentTag, tag))
411 throw new XMLSerializeException("The closed element tag is not the same as the opened one: " + currentTag
412 + " != " + tag);
413 }
414
415
416 private boolean checkEquals(String str1, String str2) {
417 if (str1 == str2) return true;
418 if (str1 == null) return str2 != null;
419 return str1.equals(str2);
420 }
421
422
423 String getPrefix(String namespaceUri) throws XMLSerializeException {
424 String prefix = null;
425 if (namespaceUri != null) {
426 prefix = searchPrefix(namespaceUri);
427 if (prefix == null) {
428 char nsChar = 'a';
429 while (prefix == null) {
430 String candidatePrefix = "" + nsChar + nsChar;
431 String uri = searchNamespaceUri(candidatePrefix);
432 if (uri == null) {
433 prefix = candidatePrefix;
434 }
435 }
436 mapPrefix(prefix, namespaceUri);
437 }
438 }
439 return prefix;
440 }
441
442
443 boolean mapPrefix(String prefix, String namespaceUri) {
444 boolean added = false;
445 String existingPrefix = searchPrefix(namespaceUri);
446 if (existingPrefix == null) {
447 prefixMappings.put(namespaceUri, prefix);
448 if (prefix.equals("")) defaultNamespaceUri = namespaceUri;
449 added = true;
450 }
451 return added;
452 }
453
454
455 private String searchPrefix(String namespaceUri) {
456 String prefix = null;
457 if (checkEquals(currentNamespaceUri, namespaceUri)) prefix = currentPrefix;
458 if (prefix == null) {
459 prefix = prefixMappings.get(namespaceUri);
460 if ((prefix == null) && (parent != null)) prefix = parent.searchPrefix(namespaceUri);
461 }
462 return prefix;
463 }
464
465
466 private String searchNamespaceUri(String prefix) {
467 String namespaceUri = null;
468 if (checkEquals(currentPrefix, prefix)) namespaceUri = currentNamespaceUri;
469 if (namespaceUri == null) {
470 for (String ns : prefixMappings.keySet()) {
471 if (prefix.equals(prefixMappings.get(ns))) {
472 namespaceUri = ns;
473 break;
474 }
475 }
476 if ((namespaceUri == null) && (parent != null)) namespaceUri = parent.searchNamespaceUri(prefix);
477 }
478 return namespaceUri;
479 }
480
481
482 Iterator<String> getNamespaceDefinitons() {
483 return prefixMappings.keySet().iterator();
484 }
485
486
487 String getDefaultNamespaceUri() {
488 if ((defaultNamespaceUri == null) && (parent != null)) defaultNamespaceUri = parent.getDefaultNamespaceUri();
489 return defaultNamespaceUri;
490 }
491
492 }
493
494 }