diff --git a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java index 5174a26c8..bd1442474 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java @@ -19,6 +19,8 @@ package org.jivesoftware.smack; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; @@ -41,6 +43,8 @@ import org.jivesoftware.smack.packet.Packet; */ public class PacketCollector { + private static final Logger LOGGER = Logger.getLogger(PacketCollector.class.getName()); + private PacketFilter packetFilter; private ArrayBlockingQueue resultQueue; private XMPPConnection connection; @@ -115,12 +119,16 @@ public class PacketCollector { */ @SuppressWarnings("unchecked") public

P nextResultBlockForever() { - try { - return (P) resultQueue.take(); - } - catch (InterruptedException e) { - throw new RuntimeException(e); + P res = null; + while (res == null) { + try { + res = (P) resultQueue.take(); + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, + "nextResultBlockForever was interrupted", e); + } } + return res; } /** @@ -142,12 +150,18 @@ public class PacketCollector { */ @SuppressWarnings("unchecked") public

P nextResult(long timeout) { - try { - return (P) resultQueue.poll(timeout, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - throw new RuntimeException(e); - } + P res = null; + long remainingWait = timeout; + final long waitStart = System.currentTimeMillis(); + while (res == null && remainingWait > 0) { + try { + res = (P) resultQueue.poll(remainingWait, TimeUnit.MILLISECONDS); + remainingWait = timeout - (System.currentTimeMillis() - waitStart); + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, "nextResult was interrupted", e); + } + } + return res; } /** diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java index 94f6b43fd..9f33a403e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; /** @@ -59,6 +60,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder; * * * @author Matt Tucker + * @see RFC 6120 - 8.3.2 Syntax: The Syntax of XMPP error stanzas */ public class XMPPError { @@ -272,7 +274,7 @@ public class XMPPError { /** * A class to represent predefined error conditions. */ - public static class Condition { + public static class Condition implements CharSequence { public static final Condition internal_server_error = new Condition("internal-server-error"); public static final Condition forbidden = new Condition("forbidden"); @@ -312,13 +314,35 @@ public class XMPPError { @Override public boolean equals(Object other) { + if (other == null) { + return false; + } return toString().equals(other.toString()); } + public boolean equals(CharSequence other) { + return StringUtils.nullSafeCharSequenceEquals(this, other); + } + @Override public int hashCode() { return value.hashCode(); } + + @Override + public int length() { + return value.length(); + } + + @Override + public char charAt(int index) { + return value.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return value.subSequence(start, end); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java index c15956ef8..99399070d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java @@ -297,8 +297,14 @@ public class PacketParserUtils { * @throws IOException */ public static CharSequence parseElement(XmlPullParser parser) throws XmlPullParserException, IOException { - assert(parser.getEventType() == XmlPullParser.START_TAG); - return parseContentDepth(parser, parser.getDepth()); + return parseElement(parser, false); + } + + public static CharSequence parseElement(XmlPullParser parser, + boolean fullNamespaces) throws XmlPullParserException, + IOException { + assert (parser.getEventType() == XmlPullParser.START_TAG); + return parseContentDepth(parser, parser.getDepth(), fullNamespaces); } /** @@ -325,28 +331,36 @@ public class PacketParserUtils { } // Advance the parser, since we want to parse the content of the current element parser.next(); - return parseContentDepth(parser, parser.getDepth()); + return parseContentDepth(parser, parser.getDepth(), false); + } + + public static CharSequence parseContentDepth(XmlPullParser parser, int depth) + throws XmlPullParserException, IOException { + return parseContentDepth(parser, depth, false); } /** * Returns the content from the current position of the parser up to the closing tag of the * given depth. Note that only the outermost namespace attributes ("xmlns") will be returned, - * not nested ones. + * not nested ones, if fullNamespaces is false. If it is true, then namespaces of + * parent elements will be added to child elements that don't define a different namespace. *

* This method is able to parse the content with MX- and KXmlParser. In order to achieve * this some trade-off has to be make, because KXmlParser does not support xml-roundtrip (ie. * return a String on getText() on START_TAG and END_TAG). We are therefore required to work * around this limitation, which results in only partial support for XML namespaces ("xmlns"): - * Only the outermost namespace of elements will be included in the resulting String. + * Only the outermost namespace of elements will be included in the resulting String, if + * fullNamespaces is set to false. *

* * @param parser * @param depth + * @param fullNamespaces * @return the content of the current depth * @throws XmlPullParserException * @throws IOException */ - public static CharSequence parseContentDepth(XmlPullParser parser, int depth) throws XmlPullParserException, IOException { + public static CharSequence parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException { XmlStringBuilder xml = new XmlStringBuilder(); int event = parser.getEventType(); boolean isEmptyElement = false; @@ -357,7 +371,7 @@ public class PacketParserUtils { while (true) { if (event == XmlPullParser.START_TAG) { xml.halfOpenElement(parser.getName()); - if (namespaceElement == null) { + if (namespaceElement == null || fullNamespaces) { String namespace = parser.getNamespace(); if (StringUtils.isNotEmpty(namespace)) { xml.attribute("xmlns", namespace); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index 1d68fde69..f1267db7f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -260,4 +260,18 @@ public class StringUtils { return null; } } + + public static boolean nullSafeCharSequenceEquals(CharSequence csOne, CharSequence csTwo) { + return nullSafeCharSequenceComperator(csOne, csTwo) == 0; + } + + public static int nullSafeCharSequenceComperator(CharSequence csOne, CharSequence csTwo) { + if (csOne == null ^ csTwo == null) { + return (csOne == null) ? -1 : 1; + } + if (csOne == null && csTwo == null) { + return 0; + } + return csOne.toString().compareTo(csTwo.toString()); + } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java b/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java index 791fd5b2c..e5a693613 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java @@ -67,7 +67,7 @@ public class ParsingExceptionTest { try { PacketParserUtils.parseMessage(parser); } catch (Exception e) { - content = PacketParserUtils.parseContentDepth(parser, parserDepth); + content = PacketParserUtils.parseContentDepth(parser, parserDepth, false); } assertThat(MESSAGE_EXCEPTION_ELEMENT + EXTENSION2 + "", equalsCharSequence(content)); } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java index fbed0e58a..1815b7bb0 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -24,9 +24,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.IOException; import java.util.Locale; import java.util.Properties; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; import org.jivesoftware.smack.packet.Message; @@ -34,6 +39,7 @@ import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.test.util.TestUtils; import org.junit.Test; +import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -790,6 +796,23 @@ public class PacketParserUtilsTest { assertEquals("", content); } + @Test + public void parseElementMultipleNamespace() + throws ParserConfigurationException, + FactoryConfigurationError, XmlPullParserException, + IOException, TransformerException, SAXException { + // @formatter:off + final String stanza = XMLBuilder.create("outer", "outerNamespace").a("outerAttribute", "outerValue") + .element("inner", "innerNamespace").a("innverAttribute", "innerValue") + .element("innermost") + .t("some text") + .asString(); + // @formatter:on + XmlPullParser parser = TestUtils.getParser(stanza, "outer"); + CharSequence result = PacketParserUtils.parseElement(parser, true); + assertXMLEqual(stanza, result.toString()); + } + private String determineNonDefaultLanguage() { String otherLanguage = "jp"; Locale[] availableLocales = Locale.getAvailableLocales(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java index 7bd68393d..92cc6a313 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java @@ -54,7 +54,7 @@ public class ItemProvider implements PacketExtensionProvider if (ProviderManager.getExtensionProvider(payloadElemName, payloadNS) == null) { - CharSequence payloadText = PacketParserUtils.parseElement(parser); + CharSequence payloadText = PacketParserUtils.parseElement(parser, true); return new PayloadItem(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText)); } else