001/** 002 * 003 * Copyright 2019 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.xml.stax; 018 019import java.io.IOException; 020 021import javax.xml.XMLConstants; 022import javax.xml.namespace.NamespaceContext; 023import javax.xml.namespace.QName; 024import javax.xml.stream.Location; 025import javax.xml.stream.XMLStreamConstants; 026import javax.xml.stream.XMLStreamException; 027import javax.xml.stream.XMLStreamReader; 028 029import org.jivesoftware.smack.xml.XmlPullParser; 030import org.jivesoftware.smack.xml.XmlPullParserException; 031 032public final class StaxXmlPullParser implements XmlPullParser { 033 034 private final XMLStreamReader xmlStreamReader; 035 036 private int depth; 037 038 StaxXmlPullParser(XMLStreamReader xmlStreamReader) { 039 this.xmlStreamReader = xmlStreamReader; 040 } 041 042 @Override 043 public Object getProperty(String name) { 044 return xmlStreamReader.getProperty(name); 045 } 046 047 @Override 048 public String getInputEncoding() { 049 return xmlStreamReader.getEncoding(); 050 } 051 052 @Override 053 public int getNamespaceCount() { 054 return xmlStreamReader.getNamespaceCount(); 055 } 056 057 @Override 058 public String getNamespacePrefix(int pos) { 059 return xmlStreamReader.getNamespacePrefix(pos); 060 } 061 062 @Override 063 public String getNamespaceUri(int pos) { 064 return xmlStreamReader.getNamespaceURI(pos); 065 } 066 067 @Override 068 public String getNamespace(String prefix) { 069 if (prefix == null) { 070 prefix = XMLConstants.DEFAULT_NS_PREFIX; 071 } 072 NamespaceContext namespaceContext = xmlStreamReader.getNamespaceContext(); 073 return namespaceContext.getNamespaceURI(prefix); 074 } 075 076 @Override 077 public String getNamespace() { 078 String prefix = getPrefix(); 079 return getNamespace(prefix); 080 } 081 082 @Override 083 public int getDepth() { 084 return depth; 085 } 086 087 @Override 088 public String getPositionDescription() { 089 Location location = xmlStreamReader.getLocation(); 090 return location.toString(); 091 } 092 093 @Override 094 public int getLineNumber() { 095 Location location = xmlStreamReader.getLocation(); 096 return location.getLineNumber(); 097 } 098 099 @Override 100 public int getColumnNumber() { 101 Location location = xmlStreamReader.getLocation(); 102 return location.getColumnNumber(); 103 } 104 105 @Override 106 public boolean isWhiteSpace() { 107 return xmlStreamReader.isWhiteSpace(); 108 } 109 110 @Override 111 public String getText() { 112 return xmlStreamReader.getText(); 113 } 114 115 @Override 116 public String getName() { 117 QName qname = getQName(); 118 return qname.getLocalPart(); 119 } 120 121 @Override 122 public QName getQName() { 123 return xmlStreamReader.getName(); 124 } 125 126 @Override 127 public String getPrefix() { 128 return xmlStreamReader.getPrefix(); 129 } 130 131 @Override 132 public int getAttributeCount() { 133 return xmlStreamReader.getAttributeCount(); 134 } 135 136 @Override 137 public String getAttributeNamespace(int index) { 138 return xmlStreamReader.getAttributeNamespace(index); 139 } 140 141 @Override 142 public String getAttributeName(int index) { 143 QName qname = getAttributeQName(index); 144 if (qname == null) { 145 return null; 146 } 147 return qname.getLocalPart(); 148 } 149 150 @Override 151 public QName getAttributeQName(int index) { 152 return xmlStreamReader.getAttributeName(index); 153 } 154 155 @Override 156 public String getAttributePrefix(int index) { 157 return xmlStreamReader.getAttributePrefix(index); 158 } 159 160 @Override 161 public String getAttributeType(int index) { 162 return xmlStreamReader.getAttributeType(index); 163 } 164 165 @Override 166 public String getAttributeValue(int index) { 167 return xmlStreamReader.getAttributeValue(index); 168 } 169 170 @Override 171 public String getAttributeValue(String namespace, String name) { 172 String namespaceURI = namespace; 173 String localName = name; 174 return xmlStreamReader.getAttributeValue(namespaceURI, localName); 175 } 176 177 @Override 178 public Event getEventType() { 179 int staxEventInt = xmlStreamReader.getEventType(); 180 return staxEventIntegerToEvent(staxEventInt); 181 } 182 183 private boolean delayedDepthDecrement; 184 185 @Override 186 public Event next() throws XmlPullParserException { 187 preNextEvent(); 188 189 int staxEventInt; 190 try { 191 staxEventInt = xmlStreamReader.next(); 192 } catch (XMLStreamException e) { 193 throw new XmlPullParserException(e); 194 } 195 196 Event event = staxEventIntegerToEvent(staxEventInt); 197 switch (event) { 198 case START_ELEMENT: 199 depth++; 200 break; 201 case END_ELEMENT: 202 delayedDepthDecrement = true; 203 break; 204 default: 205 // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement. 206 break; 207 } 208 return event; 209 } 210 211 @Override 212 public String nextText() throws IOException, XmlPullParserException { 213 final String nextText; 214 try { 215 nextText = xmlStreamReader.getElementText(); 216 } catch (XMLStreamException e) { 217 throw new XmlPullParserException(e); 218 } 219 220 // XMLStreamReader.getElementText() will forward to the next END_ELEMENT, hence we need to set 221 // delayedDepthDecrement to true. 222 delayedDepthDecrement = true; 223 224 return nextText; 225 } 226 227 @Override 228 public TagEvent nextTag() throws IOException, XmlPullParserException { 229 preNextEvent(); 230 231 int staxEventInt; 232 try { 233 staxEventInt = xmlStreamReader.nextTag(); 234 } catch (XMLStreamException e) { 235 throw new XmlPullParserException(e); 236 } 237 238 switch (staxEventInt) { 239 case XMLStreamConstants.START_ELEMENT: 240 depth++; 241 return TagEvent.START_ELEMENT; 242 case XMLStreamConstants.END_ELEMENT: 243 delayedDepthDecrement = true; 244 return TagEvent.END_ELEMENT; 245 default: 246 throw new AssertionError(); 247 } 248 } 249 250 private void preNextEvent() { 251 if (delayedDepthDecrement) { 252 depth--; 253 delayedDepthDecrement = false; 254 assert depth >= 0; 255 } 256 } 257 258 private static Event staxEventIntegerToEvent(int staxEventInt) { 259 switch (staxEventInt) { 260 case XMLStreamConstants.START_ELEMENT: 261 return Event.START_ELEMENT; 262 case XMLStreamConstants.END_ELEMENT: 263 return Event.END_ELEMENT; 264 case XMLStreamConstants.PROCESSING_INSTRUCTION: 265 return Event.PROCESSING_INSTRUCTION; 266 case XMLStreamConstants.CHARACTERS: 267 return Event.TEXT_CHARACTERS; 268 case XMLStreamConstants.COMMENT: 269 return Event.COMMENT; 270 case XMLStreamConstants.SPACE: 271 return Event.IGNORABLE_WHITESPACE; 272 case XMLStreamConstants.START_DOCUMENT: 273 return Event.START_DOCUMENT; 274 case XMLStreamConstants.END_DOCUMENT: 275 return Event.END_DOCUMENT; 276 case XMLStreamConstants.ENTITY_REFERENCE: 277 return Event.ENTITY_REFERENCE; 278 case XMLStreamConstants.ATTRIBUTE: 279 return Event.OTHER; 280 case XMLStreamConstants.DTD: 281 return Event.OTHER; 282 case XMLStreamConstants.CDATA: 283 return Event.OTHER; 284 case XMLStreamConstants.NAMESPACE: 285 return Event.OTHER; 286 case XMLStreamConstants.NOTATION_DECLARATION: 287 return Event.OTHER; 288 case XMLStreamConstants.ENTITY_DECLARATION: 289 return Event.OTHER; 290 default: 291 throw new IllegalArgumentException("Unknown Stax event integer: " + staxEventInt); 292 } 293 } 294 295 @Override 296 public boolean supportsRoundtrip() { 297 // TODO: Is there a StAX parser implementation which does support roundtrip? 298 return false; 299 } 300}