1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 10:19:41 +02:00

Replace XPP3 by XmlPullParser interface wrapping StAX and XPP3

Introducing Smack's own XmlPullParser interface which tries to stay as
compatible as possible to XPP3. The interface is used to either wrap
StAX's XMLStreamReader if Smack is used on Java SE, and XPP3's
XmlPullParser if Smack is used on on Android.

Fixes SMACK-591.

Also introduce JUnit 5 and non-strict javadoc projects.
This commit is contained in:
Florian Schmaus 2019-05-06 22:06:13 +02:00
parent b3646abecd
commit 4133eb175c
414 changed files with 3855 additions and 2041 deletions

View file

@ -0,0 +1,7 @@
description = """\
Smack XML parser using Stax."""
dependencies {
compile project(':smack-xmlparser')
testCompile project(path: ":smack-xmlparser", configuration: "testRuntime")
}

View file

@ -0,0 +1,301 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.xml.stax;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public final class StaxXmlPullParser implements XmlPullParser {
private final XMLStreamReader xmlStreamReader;
private int depth;
StaxXmlPullParser(XMLStreamReader xmlStreamReader) {
this.xmlStreamReader = xmlStreamReader;
}
@Override
public Object getProperty(String name) {
return xmlStreamReader.getProperty(name);
}
@Override
public String getInputEncoding() {
return xmlStreamReader.getEncoding();
}
@Override
public int getNamespaceCount() {
return xmlStreamReader.getNamespaceCount();
}
@Override
public String getNamespacePrefix(int pos) {
return xmlStreamReader.getNamespacePrefix(pos);
}
@Override
public String getNamespaceUri(int pos) {
return xmlStreamReader.getNamespaceURI(pos);
}
@Override
public String getNamespace(String prefix) {
if (prefix == null) {
prefix = XMLConstants.DEFAULT_NS_PREFIX;
}
NamespaceContext namespaceContext = xmlStreamReader.getNamespaceContext();
return namespaceContext.getNamespaceURI(prefix);
}
@Override
public int getDepth() {
return depth;
}
@Override
public String getPositionDescription() {
Location location = xmlStreamReader.getLocation();
return location.toString();
}
@Override
public int getLineNumber() {
Location location = xmlStreamReader.getLocation();
return location.getLineNumber();
}
@Override
public int getColumnNumber() {
Location location = xmlStreamReader.getLocation();
return location.getColumnNumber();
}
@Override
public boolean isWhiteSpace() {
return xmlStreamReader.isWhiteSpace();
}
@Override
public String getText() {
return xmlStreamReader.getText();
}
@Override
public String getNamespace() {
NamespaceContext namespaceContext = xmlStreamReader.getNamespaceContext();
String prefix = getPrefix();
return namespaceContext.getNamespaceURI(prefix);
}
@Override
public String getName() {
QName qname = getQName();
return qname.getLocalPart();
}
@Override
public QName getQName() {
return xmlStreamReader.getName();
}
@Override
public String getPrefix() {
return xmlStreamReader.getPrefix();
}
@Override
public int getAttributeCount() {
return xmlStreamReader.getAttributeCount();
}
@Override
public String getAttributeNamespace(int index) {
return xmlStreamReader.getAttributeNamespace(index);
}
@Override
public String getAttributeName(int index) {
QName qname = getAttributeQName(index);
if (qname == null) {
return null;
}
return qname.getLocalPart();
}
@Override
public QName getAttributeQName(int index) {
return xmlStreamReader.getAttributeName(index);
}
@Override
public String getAttributePrefix(int index) {
return xmlStreamReader.getAttributePrefix(index);
}
@Override
public String getAttributeType(int index) {
return xmlStreamReader.getAttributeType(index);
}
@Override
public String getAttributeValue(int index) {
return xmlStreamReader.getAttributeValue(index);
}
@Override
public String getAttributeValue(String namespace, String name) {
String namespaceURI = namespace;
String localName = name;
return xmlStreamReader.getAttributeValue(namespaceURI, localName);
}
@Override
public Event getEventType() {
int staxEventInt = xmlStreamReader.getEventType();
return staxEventIntegerToEvent(staxEventInt);
}
private boolean delayedDepthDecrement;
@Override
public Event next() throws XmlPullParserException {
preNextEvent();
int staxEventInt;
try {
staxEventInt = xmlStreamReader.next();
} catch (XMLStreamException e) {
throw new XmlPullParserException(e);
}
Event event = staxEventIntegerToEvent(staxEventInt);
switch (event) {
case START_ELEMENT:
depth++;
break;
case END_ELEMENT:
delayedDepthDecrement = true;
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
}
return event;
}
@Override
public String nextText() throws IOException, XmlPullParserException {
final String nextText;
try {
nextText = xmlStreamReader.getElementText();
} catch (XMLStreamException e) {
throw new XmlPullParserException(e);
}
// XMLStreamReader.getElementText() will forward to the next END_ELEMENT, hence we need to set
// delayedDepthDecrement to true.
delayedDepthDecrement = true;
return nextText;
}
@Override
public TagEvent nextTag() throws IOException, XmlPullParserException {
preNextEvent();
int staxEventInt;
try {
staxEventInt = xmlStreamReader.nextTag();
} catch (XMLStreamException e) {
throw new XmlPullParserException(e);
}
switch (staxEventInt) {
case XMLStreamConstants.START_ELEMENT:
depth++;
return TagEvent.START_ELEMENT;
case XMLStreamConstants.END_ELEMENT:
delayedDepthDecrement = true;
return TagEvent.END_ELEMENT;
default:
throw new AssertionError();
}
}
private void preNextEvent() {
if (delayedDepthDecrement) {
depth--;
delayedDepthDecrement = false;
assert depth >= 0;
}
}
private static Event staxEventIntegerToEvent(int staxEventInt) {
switch (staxEventInt) {
case XMLStreamConstants.START_ELEMENT:
return Event.START_ELEMENT;
case XMLStreamConstants.END_ELEMENT:
return Event.END_ELEMENT;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
return Event.PROCESSING_INSTRUCTION;
case XMLStreamConstants.CHARACTERS:
return Event.TEXT_CHARACTERS;
case XMLStreamConstants.COMMENT:
return Event.COMMENT;
case XMLStreamConstants.SPACE:
return Event.IGNORABLE_WHITESPACE;
case XMLStreamConstants.START_DOCUMENT:
return Event.START_DOCUMENT;
case XMLStreamConstants.END_DOCUMENT:
return Event.END_DOCUMENT;
case XMLStreamConstants.ENTITY_REFERENCE:
return Event.ENTITY_REFERENCE;
case XMLStreamConstants.ATTRIBUTE:
return Event.OTHER;
case XMLStreamConstants.DTD:
return Event.OTHER;
case XMLStreamConstants.CDATA:
return Event.OTHER;
case XMLStreamConstants.NAMESPACE:
return Event.OTHER;
case XMLStreamConstants.NOTATION_DECLARATION:
return Event.OTHER;
case XMLStreamConstants.ENTITY_DECLARATION:
return Event.OTHER;
default:
throw new IllegalArgumentException("Unknown Stax event integer: " + staxEventInt);
}
}
@Override
public boolean supportsRoundtrip() {
// TODO: Is there a StAX parser implementation which does support roundtrip?
return false;
}
}

View file

@ -0,0 +1,50 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.xml.stax;
import java.io.Reader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smack.xml.XmlPullParserFactory;
public class StaxXmlPullParserFactory implements XmlPullParserFactory {
private static final XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
static {
// XPP3 appears to coalescing hence we need to configure our StAX parser to also return all available text on
// getText().
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
// Internal and external entity references are prohibited in XMPP (RFC 6120 § 11.1).
xmlInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
}
@Override
public StaxXmlPullParser newXmlPullParser(Reader reader) throws XmlPullParserException {
XMLStreamReader xmlStreamReader;
try {
xmlStreamReader = xmlInputFactory.createXMLStreamReader(reader);
} catch (XMLStreamException e) {
throw new XmlPullParserException(e);
}
return new StaxXmlPullParser(xmlStreamReader);
}
}

View file

@ -0,0 +1,23 @@
/**
*
* Copyright 2019 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Classes for XML handling using StAX (Streaming API for XML, JSR 173).
*
* @see <a href="https://docs.oracle.com/javase/tutorial/jaxp/stax/index.html">Streaming API for XML</a>
*/
package org.jivesoftware.smack.xml.stax;

View file

@ -0,0 +1 @@
org.jivesoftware.smack.xml.stax.StaxXmlPullParserFactory

View file

@ -0,0 +1,36 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.xml.stax;
import java.io.IOException;
import java.io.StringReader;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.junit.jupiter.api.Test;
public class StaxParserTest {
@Test
public void factoryTest() throws XmlPullParserException, IOException {
StaxXmlPullParserFactory staxXmlPullParserFactory = new StaxXmlPullParserFactory();
XmlPullParser parser = staxXmlPullParserFactory.newXmlPullParser(new StringReader("<element/>"));
parser.next();
}
}