1 package com.atlassian.dbexporter.node.stax;
2
3 import com.atlassian.dbexporter.ImportExportErrorService;
4 import com.atlassian.dbexporter.node.NodeParser;
5 import com.atlassian.dbexporter.node.NodeStreamReader;
6
7 import javax.xml.stream.XMLStreamConstants;
8 import javax.xml.stream.XMLStreamException;
9 import javax.xml.stream.XMLStreamReader;
10 import java.io.Reader;
11 import java.io.Writer;
12 import java.math.BigDecimal;
13 import java.math.BigInteger;
14 import java.util.Date;
15
16 import static com.atlassian.dbexporter.node.stax.StaxUtils.newDateFormat;
17 import static com.atlassian.dbexporter.node.stax.StaxUtils.newXmlInputFactory;
18 import static com.atlassian.dbexporter.node.stax.StaxUtils.unicodeDecode;
19 import static com.google.common.base.Preconditions.checkNotNull;
20
21
22
23
24
25
26 public final class StaxStreamReader implements NodeStreamReader {
27 private static final String XMLSCHEMA_URI = "http://www.w3.org/2001/XMLSchema-instance";
28
29 private final ImportExportErrorService errorService;
30 private final XMLStreamReader reader;
31
32 public StaxStreamReader(ImportExportErrorService errorService, Reader input) {
33 this.errorService = checkNotNull(errorService);
34 this.reader = createXmlStreamReader(checkNotNull(input));
35 }
36
37 private XMLStreamReader createXmlStreamReader(Reader reader) {
38 try {
39 return newXmlInputFactory().createXMLStreamReader(reader);
40 } catch (XMLStreamException e) {
41 throw errorService.newParseException(e);
42 }
43 }
44
45 public NodeParser getRootNode() {
46 if (reader.getEventType() != XMLStreamConstants.START_DOCUMENT) {
47 throw new IllegalStateException("The root node has already been returned.");
48 } else {
49 try {
50 reader.nextTag();
51
52 return new NodeParser() {
53 public String getAttribute(String key) {
54 return getAttribute(key, null, false);
55 }
56
57 public String getRequiredAttribute(String key) {
58 return getAttribute(key, null, true);
59 }
60
61 private String getAttribute(String key, String namespaceUri, boolean required) {
62 requireStartElement();
63 for (int i = 0; i < reader.getAttributeCount(); i++) {
64 if (key.equals(reader.getAttributeName(i).getLocalPart()) &&
65 (namespaceUri == null || namespaceUri.equals(reader.getAttributeName(i).getNamespaceURI()))) {
66 return unicodeDecode(reader.getAttributeValue(i));
67 }
68 }
69 if (required) {
70 throw errorService.newParseException(String.format("Required attribute %s not found in node %s", key, getName()));
71 } else {
72 return null;
73 }
74 }
75
76 public String getName() {
77 return reader.getLocalName();
78 }
79
80 public boolean isClosed() {
81 return reader.getEventType() == XMLStreamConstants.END_ELEMENT || reader.getEventType() == XMLStreamConstants.END_DOCUMENT;
82 }
83
84 private int nextTagOrEndOfDocument() {
85 try {
86 int eventType = reader.next();
87 while ((eventType == XMLStreamConstants.CHARACTERS && reader.isWhiteSpace())
88 || (eventType == XMLStreamConstants.CDATA && reader.isWhiteSpace())
89
90 || eventType == XMLStreamConstants.SPACE
91 || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
92 || eventType == XMLStreamConstants.COMMENT) {
93 eventType = reader.next();
94 }
95 if (eventType != XMLStreamConstants.START_ELEMENT &&
96 eventType != XMLStreamConstants.END_ELEMENT &&
97 eventType != XMLStreamConstants.END_DOCUMENT) {
98 throw errorService.newParseException(
99 "Unable to find start or end tag, or end of document. Location: " +
100 reader.getLocation());
101 }
102 return eventType;
103 } catch (XMLStreamException e) {
104 throw errorService.newParseException(e);
105 }
106 }
107
108 public NodeParser getNextNode() {
109 int event = nextTagOrEndOfDocument();
110
111 assert reader.isStartElement() || reader.isEndElement() ||
112 XMLStreamConstants.END_DOCUMENT == reader.getEventType();
113
114 return XMLStreamConstants.END_DOCUMENT == event ?
115 null : this;
116 }
117
118 public String getContentAsString() {
119 requireStartElement();
120 try {
121 if (Boolean.parseBoolean(getAttribute("nil", XMLSCHEMA_URI, false))) {
122 nextTagOrEndOfDocument();
123 return null;
124 } else {
125 return unicodeDecode(reader.getElementText());
126 }
127 } catch (XMLStreamException e) {
128 throw errorService.newParseException(e);
129 }
130 }
131
132 public Boolean getContentAsBoolean() {
133 String value = getContentAsString();
134 return value == null ? null : Boolean.parseBoolean(value);
135 }
136
137 public Date getContentAsDate() {
138 String value = getContentAsString();
139 try {
140 return value == null ? null : newDateFormat().parse(value);
141 } catch (java.text.ParseException pe) {
142 throw errorService.newParseException(pe);
143 }
144 }
145
146 public BigInteger getContentAsBigInteger() {
147 String value = getContentAsString();
148 return value == null ? null : new BigInteger(value);
149 }
150
151 @Override
152 public BigDecimal getContentAsBigDecimal() {
153 String value = getContentAsString();
154 return value == null ? null : new BigDecimal(value);
155 }
156
157 public void getContent(Writer writer) {
158 throw new AssertionError("Not implemented.");
159 }
160
161 private void requireStartElement() throws IllegalStateException {
162 if (!reader.isStartElement()) {
163 throw new IllegalStateException("Not currently positioned " +
164 "at the start of a node.");
165 }
166 }
167
168 @Override
169 public String toString() {
170 final StringBuilder sb = new StringBuilder();
171 sb.append("<");
172 if (isClosed()) {
173 sb.append("/");
174 }
175 sb.append(getName());
176 if (!isClosed()) {
177 for (int i = 0; i < reader.getAttributeCount(); i++) {
178 sb.append(" ")
179 .append(reader.getAttributeName(i).getLocalPart())
180 .append("=\"")
181 .append(unicodeDecode(reader.getAttributeValue(i)))
182 .append("\"");
183 }
184 }
185 sb.append(">");
186 return sb.toString();
187 }
188 };
189 } catch (XMLStreamException e) {
190 throw errorService.newParseException(e);
191 }
192 }
193 }
194
195 public void close() {
196 try {
197 reader.close();
198 } catch (XMLStreamException e) {
199 throw errorService.newParseException(e);
200 }
201 }
202 }