StaxXmlPullParser.java

  1. /**
  2.  *
  3.  * Copyright 2019 Florian Schmaus
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.xml.stax;

  18. import java.io.IOException;

  19. import javax.xml.XMLConstants;
  20. import javax.xml.namespace.NamespaceContext;
  21. import javax.xml.namespace.QName;
  22. import javax.xml.stream.Location;
  23. import javax.xml.stream.XMLStreamConstants;
  24. import javax.xml.stream.XMLStreamException;
  25. import javax.xml.stream.XMLStreamReader;

  26. import org.jivesoftware.smack.xml.XmlPullParser;
  27. import org.jivesoftware.smack.xml.XmlPullParserException;

  28. public final class StaxXmlPullParser implements XmlPullParser {

  29.     private final XMLStreamReader xmlStreamReader;

  30.     private int depth;

  31.     StaxXmlPullParser(XMLStreamReader xmlStreamReader) {
  32.         this.xmlStreamReader = xmlStreamReader;
  33.     }

  34.     @Override
  35.     public Object getProperty(String name) {
  36.         return xmlStreamReader.getProperty(name);
  37.     }

  38.     @Override
  39.     public String getInputEncoding() {
  40.         return xmlStreamReader.getEncoding();
  41.     }

  42.     @Override
  43.     public int getNamespaceCount() {
  44.         return xmlStreamReader.getNamespaceCount();
  45.     }

  46.     @Override
  47.     public String getNamespacePrefix(int pos) {
  48.         return xmlStreamReader.getNamespacePrefix(pos);
  49.     }

  50.     @Override
  51.     public String getNamespaceUri(int pos) {
  52.         return xmlStreamReader.getNamespaceURI(pos);
  53.     }

  54.     @Override
  55.     public String getNamespace(String prefix) {
  56.         if (prefix == null) {
  57.             prefix = XMLConstants.DEFAULT_NS_PREFIX;
  58.         }
  59.         NamespaceContext namespaceContext = xmlStreamReader.getNamespaceContext();
  60.         return namespaceContext.getNamespaceURI(prefix);
  61.     }

  62.     @Override
  63.     public String getNamespace() {
  64.         String prefix = getPrefix();
  65.         return getNamespace(prefix);
  66.     }

  67.     @Override
  68.     public int getDepth() {
  69.         return depth;
  70.     }

  71.     @Override
  72.     public String getPositionDescription() {
  73.         Location location = xmlStreamReader.getLocation();
  74.         return location.toString();
  75.     }

  76.     @Override
  77.     public int getLineNumber() {
  78.         Location location = xmlStreamReader.getLocation();
  79.         return location.getLineNumber();
  80.     }

  81.     @Override
  82.     public int getColumnNumber() {
  83.         Location location = xmlStreamReader.getLocation();
  84.         return location.getColumnNumber();
  85.     }

  86.     @Override
  87.     public boolean isWhiteSpace() {
  88.         return xmlStreamReader.isWhiteSpace();
  89.     }

  90.     @Override
  91.     public String getText() {
  92.         return xmlStreamReader.getText();
  93.     }

  94.     @Override
  95.     public String getName() {
  96.         QName qname = getQName();
  97.         return qname.getLocalPart();
  98.     }

  99.     @Override
  100.     public QName getQName() {
  101.         return xmlStreamReader.getName();
  102.     }

  103.     @Override
  104.     public String getPrefix() {
  105.         return xmlStreamReader.getPrefix();
  106.     }

  107.     @Override
  108.     public int getAttributeCount() {
  109.         return xmlStreamReader.getAttributeCount();
  110.     }

  111.     @Override
  112.     public String getAttributeNamespace(int index) {
  113.         return xmlStreamReader.getAttributeNamespace(index);
  114.     }

  115.     @Override
  116.     public String getAttributeName(int index) {
  117.         QName qname = getAttributeQName(index);
  118.         if (qname == null) {
  119.             return null;
  120.         }
  121.         return qname.getLocalPart();
  122.     }

  123.     @Override
  124.     public QName getAttributeQName(int index) {
  125.         return xmlStreamReader.getAttributeName(index);
  126.     }

  127.     @Override
  128.     public String getAttributePrefix(int index) {
  129.         return xmlStreamReader.getAttributePrefix(index);
  130.     }

  131.     @Override
  132.     public String getAttributeType(int index) {
  133.         return xmlStreamReader.getAttributeType(index);
  134.     }

  135.     @Override
  136.     public String getAttributeValue(int index) {
  137.         return xmlStreamReader.getAttributeValue(index);
  138.     }

  139.     @Override
  140.     public String getAttributeValue(String namespace, String name) {
  141.         String namespaceURI = namespace;
  142.         String localName = name;
  143.         return xmlStreamReader.getAttributeValue(namespaceURI, localName);
  144.     }

  145.     @Override
  146.     public Event getEventType() {
  147.         int staxEventInt = xmlStreamReader.getEventType();
  148.         return staxEventIntegerToEvent(staxEventInt);
  149.     }

  150.     private boolean delayedDepthDecrement;

  151.     @Override
  152.     public Event next() throws XmlPullParserException {
  153.         preNextEvent();

  154.         int staxEventInt;
  155.         try {
  156.             staxEventInt = xmlStreamReader.next();
  157.         } catch (XMLStreamException e) {
  158.             throw new XmlPullParserException(e);
  159.         }

  160.         Event event = staxEventIntegerToEvent(staxEventInt);
  161.         switch (event) {
  162.         case START_ELEMENT:
  163.             depth++;
  164.             break;
  165.         case END_ELEMENT:
  166.             delayedDepthDecrement = true;
  167.             break;
  168.         default:
  169.             // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
  170.             break;
  171.         }
  172.         return event;
  173.     }

  174.     @Override
  175.     public String nextText() throws IOException, XmlPullParserException {
  176.         final String nextText;
  177.         try {
  178.             nextText = xmlStreamReader.getElementText();
  179.         } catch (XMLStreamException e) {
  180.             throw new XmlPullParserException(e);
  181.         }

  182.         // XMLStreamReader.getElementText() will forward to the next END_ELEMENT, hence we need to set
  183.         // delayedDepthDecrement to true.
  184.         delayedDepthDecrement = true;

  185.         return nextText;
  186.     }

  187.     @Override
  188.     public TagEvent nextTag() throws IOException, XmlPullParserException {
  189.         preNextEvent();

  190.         int staxEventInt;
  191.         try {
  192.             staxEventInt = xmlStreamReader.nextTag();
  193.         } catch (XMLStreamException e) {
  194.             throw new XmlPullParserException(e);
  195.         }

  196.         switch (staxEventInt) {
  197.         case XMLStreamConstants.START_ELEMENT:
  198.             depth++;
  199.             return TagEvent.START_ELEMENT;
  200.         case XMLStreamConstants.END_ELEMENT:
  201.             delayedDepthDecrement = true;
  202.             return TagEvent.END_ELEMENT;
  203.         default:
  204.             throw new AssertionError();
  205.         }
  206.     }

  207.     private void preNextEvent() {
  208.         if (delayedDepthDecrement) {
  209.             depth--;
  210.             delayedDepthDecrement = false;
  211.             assert depth >= 0;
  212.         }
  213.     }

  214.     private static Event staxEventIntegerToEvent(int staxEventInt) {
  215.         switch (staxEventInt) {
  216.         case XMLStreamConstants.START_ELEMENT:
  217.             return Event.START_ELEMENT;
  218.         case XMLStreamConstants.END_ELEMENT:
  219.             return Event.END_ELEMENT;
  220.         case XMLStreamConstants.PROCESSING_INSTRUCTION:
  221.             return Event.PROCESSING_INSTRUCTION;
  222.         case XMLStreamConstants.CHARACTERS:
  223.             return Event.TEXT_CHARACTERS;
  224.         case XMLStreamConstants.COMMENT:
  225.             return Event.COMMENT;
  226.         case XMLStreamConstants.SPACE:
  227.             return Event.IGNORABLE_WHITESPACE;
  228.         case XMLStreamConstants.START_DOCUMENT:
  229.             return Event.START_DOCUMENT;
  230.         case XMLStreamConstants.END_DOCUMENT:
  231.             return Event.END_DOCUMENT;
  232.         case XMLStreamConstants.ENTITY_REFERENCE:
  233.             return Event.ENTITY_REFERENCE;
  234.         case XMLStreamConstants.ATTRIBUTE:
  235.             return Event.OTHER;
  236.         case XMLStreamConstants.DTD:
  237.             return Event.OTHER;
  238.         case XMLStreamConstants.CDATA:
  239.             return Event.OTHER;
  240.         case XMLStreamConstants.NAMESPACE:
  241.             return Event.OTHER;
  242.         case XMLStreamConstants.NOTATION_DECLARATION:
  243.             return Event.OTHER;
  244.         case XMLStreamConstants.ENTITY_DECLARATION:
  245.             return Event.OTHER;
  246.         default:
  247.             throw new IllegalArgumentException("Unknown Stax event integer: " + staxEventInt);
  248.         }
  249.     }

  250.     @Override
  251.     public boolean supportsRoundtrip() {
  252.         // TODO: Is there a StAX parser implementation which does support roundtrip?
  253.         return false;
  254.     }
  255. }