View Javadoc
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   * Reader implementation using StAX.
23   *
24   * @author Erik van Zijst
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()) // skip whitespace
88                                      || (eventType == XMLStreamConstants.CDATA && reader.isWhiteSpace())
89                                      // skip whitespace
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 }