diff --git a/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
index 2cb5e9c1e..6dfe0884d 100644
--- a/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
+++ b/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
@@ -570,4 +570,18 @@ public class StringUtils {
return new String(randBuffer);
}
+ /**
+ * Returns true if string is not null and is not empty, false otherwise
+ * Examples:
+ * isNotEmpty(null) - false
+ * isNotEmpty("") - false
+ * isNotEmpty(" ") - true
+ * isNotEmpty("empty") - true
+ *
+ * @param string checked String
+ * @return true if string is not null and is not empty, false otherwise
+ */
+ public static boolean isNotEmpty(CharSequence string) {
+ return string != null && string.length() != 0;
+ }
}
diff --git a/extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProvider.java b/extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProvider.java
index bfb3b3d4a..64d80a6d8 100644
--- a/extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProvider.java
+++ b/extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProvider.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2003-2007 Jive Software.
+ * Copyright 2003-2007 Jive Software, 2014 Vyacheslav Blinov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.jivesoftware.smackx.xhtmlim.provider;
import org.jivesoftware.smack.packet.PacketExtension;
@@ -22,70 +21,118 @@ import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
/**
* The XHTMLExtensionProvider parses XHTML packets.
*
- * @author Gaston Dombiak
+ * @author Vyacheslav Blinov
*/
+@SuppressWarnings("PMD.CyclomaticComplexity")
public class XHTMLExtensionProvider implements PacketExtensionProvider {
+ public static final String BODY_ELEMENT = "body";
- /**
- * Creates a new XHTMLExtensionProvider.
- * ProviderManager requires that every PacketExtensionProvider has a public, no-argument constructor
- */
- public XHTMLExtensionProvider() {
- }
-
- /**
- * Parses a XHTMLExtension packet (extension sub-packet).
- *
- * @param parser the XML parser, positioned at the starting element of the extension.
- * @return a PacketExtension.
- * @throws Exception if a parsing error occurs.
- */
- public PacketExtension parseExtension(XmlPullParser parser)
- throws Exception {
+ @Override
+ public PacketExtension parseExtension(XmlPullParser parser) throws IOException, XmlPullParserException {
XHTMLExtension xhtmlExtension = new XHTMLExtension();
- boolean done = false;
- StringBuilder buffer = new StringBuilder();
+ final String XHTML_EXTENSION_ELEMENT_NAME = xhtmlExtension.getElementName();
+
int startDepth = parser.getDepth();
- int depth = parser.getDepth();
- String lastTag = "";
- while (!done) {
+ int tagDepth = parser.getDepth();
+ boolean tagStarted = false;
+ StringBuilder buffer = new StringBuilder();
+
+ while (true) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
- if (parser.getName().equals("body")) {
+ boolean appendNamespace = false;
+ if (BODY_ELEMENT.equals(parser.getName())) {
buffer = new StringBuilder();
- depth = parser.getDepth();
+ tagDepth = parser.getDepth();
+ appendNamespace = true;
}
- lastTag = parser.getText();
- buffer.append(parser.getText());
+ maybeCloseTag(tagStarted, buffer);
+ appendStartTagPartial(buffer, parser, appendNamespace);
+ tagStarted = true;
} else if (eventType == XmlPullParser.TEXT) {
- if (buffer != null) {
- // We need to return valid XML so any inner text needs to be re-escaped
- buffer.append(StringUtils.escapeForXML(parser.getText()));
- }
+ tagStarted = maybeCloseTag(tagStarted, buffer);
+ appendText(buffer, parser);
} else if (eventType == XmlPullParser.END_TAG) {
- if (parser.getName().equals("body") && parser.getDepth() <= depth) {
- buffer.append(parser.getText());
- xhtmlExtension.addBody(buffer.toString());
- }
- else if (parser.getName().equals(xhtmlExtension.getElementName())
- && parser.getDepth() <= startDepth) {
- done = true;
- }
- else {
- // This is a check for tags that are both a start and end tag like
- // So that they aren't doubled
- if(!lastTag.equals(parser.getText())) {
- buffer.append(parser.getText());
+ String name = parser.getName();
+ if (XHTML_EXTENSION_ELEMENT_NAME.equals(name) && parser.getDepth() <= startDepth) {
+ return xhtmlExtension;
+ } else {
+ // xpp does not allows us to detect if tag is self-closing, so we have to
+ // handle self-closing tags by our own means
+ appendEndTag(buffer, parser, tagStarted);
+ tagStarted = false;
+ if (BODY_ELEMENT.equals(name) && parser.getDepth() <= tagDepth) {
+ xhtmlExtension.addBody(buffer.toString());
}
}
}
}
-
- return xhtmlExtension;
}
+ private static void appendStartTagPartial(StringBuilder builder, XmlPullParser parser, boolean withNamespace) {
+ builder.append('<');
+
+ String prefix = parser.getPrefix();
+ if (StringUtils.isNotEmpty(prefix)) {
+ builder.append(prefix).append(':');
+ }
+ builder.append(parser.getName());
+
+ int attributesCount = parser.getAttributeCount();
+ // handle namespace
+ if (withNamespace) {
+ String namespace = parser.getNamespace();
+ if (StringUtils.isNotEmpty(namespace)) {
+ builder.append(" xmlns='").append(namespace).append('\'');
+ }
+ }
+ // handle attributes
+ for (int i = 0; i < attributesCount; ++i) {
+ builder.append(' ');
+ String attributeNamespace = parser.getAttributeNamespace(i);
+ if (StringUtils.isNotEmpty(attributeNamespace)) {
+ builder.append(attributeNamespace).append(':');
+ }
+ builder.append(parser.getAttributeName(i));
+ String value = parser.getAttributeValue(i);
+ if (value != null) {
+ // We need to return valid XML so any inner text needs to be re-escaped
+ builder.append("='").append(StringUtils.escapeForXML(value)).append('\'');
+ }
+ }
+ }
+
+
+ private static void appendEndTag(StringBuilder builder, XmlPullParser parser, boolean tagStarted) {
+ if (tagStarted) {
+ builder.append("/>");
+ } else {
+ builder.append("").append(parser.getName()).append('>');
+ }
+ }
+
+ private static boolean appendText(StringBuilder builder, XmlPullParser parser) {
+ String text = parser.getText();
+ if (text == null) {
+ return false;
+ } else {
+ // We need to return valid XML so any inner text needs to be re-escaped
+ builder.append(StringUtils.escapeForXML(parser.getText()));
+ return true;
+ }
+ }
+
+ private static boolean maybeCloseTag(boolean tagStarted, StringBuilder builder) {
+ if (tagStarted) {
+ builder.append('>');
+ }
+ return false;
+ }
}
diff --git a/extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java b/extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java
new file mode 100644
index 000000000..23b94d831
--- /dev/null
+++ b/extensions/src/test/java/org/jivesoftware/smackx/xhtmlim/provider/XHTMLExtensionProviderTest.java
@@ -0,0 +1,59 @@
+/**
+ *
+ * Copyright 2014 Vyacheslav Blinov
+ *
+ * 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.smackx.xhtmlim.provider;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+
+public class XHTMLExtensionProviderTest {
+ public static final String XHTML_EXTENSION_SAMPLE_RESOURCE_NAME = "xhtml.xml";
+
+ @Test
+ public void parsesWell() throws IOException, XmlPullParserException {
+ XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(getClass().getResourceAsStream(XHTML_EXTENSION_SAMPLE_RESOURCE_NAME), "UTF-8");
+ parser.next();
+
+ XHTMLExtensionProvider provider = new XHTMLExtensionProvider();
+ PacketExtension extension = provider.parseExtension(parser);
+
+ assertThat(extension, is(instanceOf(XHTMLExtension.class)));
+ XHTMLExtension attachmentsInfo = (XHTMLExtension) extension;
+
+ assertEquals(sampleXhtml(), attachmentsInfo.getBodies().next());
+ }
+
+ private String sampleXhtml() {
+ return "