From 2aab2d7f11cdf1348debfa2dbbf1895acfb57af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jules=20Tr=C3=A9horel?= Date: Fri, 12 Feb 2016 11:08:26 +0100 Subject: [PATCH] Add parsing of message custom arguments Custom arguments are then stored via an extension --- .../packet/CustomAttributesExtension.java | 109 ++++++++++++++++++ .../smack/util/PacketParserUtils.java | 7 ++ .../smack/util/PacketParserUtilsTest.java | 43 +++++++ 3 files changed, 159 insertions(+) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/packet/CustomAttributesExtension.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/CustomAttributesExtension.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/CustomAttributesExtension.java new file mode 100644 index 0000000000..b120f75d75 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/CustomAttributesExtension.java @@ -0,0 +1,109 @@ +/** + * + * Copyright 2016 Jules Tréhorel + * + * All rights reserved. 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.packet; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.xmlpull.v1.XmlPullParser; + +/** + * An {@link ExtensionElement} storing the custom attributes listed in messages stanzas root + * element. As this use-case is a bad practice, this {@link ExtensionElement} allows XMPP clients to + * retrieve those (wrongly set) values in a clean way. + * + * @author Jules Tréhorel + */ +public class CustomAttributesExtension implements ExtensionElement { + public final static String NAME = "customAttributes"; + public final static String NAMESPACE = "http://smack.jivesoftware.com"; + + private StandardExtensionElement extension; + + private CustomAttributesExtension(Map properties) { + if (properties == null || properties.isEmpty()) + return; + + StandardExtensionElement.Builder extensionBuilder = StandardExtensionElement.builder(NAME, NAMESPACE); + for (Map.Entry entry : properties.entrySet()) { + extensionBuilder.addElement(entry.getKey(), entry.getValue()); + } + extension = extensionBuilder.build(); + } + + @Override + public String getElementName() { + return NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public boolean hasProperties() { + return extension != null && extension.getElements().size() > 0; + } + + public boolean hasProperty(String propertyKey) { + return hasProperties() && extension.getElements(propertyKey) != null; + } + + public String getPropertyValue(String propertyKey) { + if (!hasProperty(propertyKey)) { + return null; + } + return extension.getElements(propertyKey).get(0).getText(); // Only one element per key + } + + @Override + public XmlStringBuilder toXML() { + return extension.toXML(); + } + + public static final class Builder { + private final List attributesWhiteList = Arrays.asList(new String[] {"xml:lang", "id", "to", "from", "type"}); + + private Map properties; + + public Builder() { + } + + public Builder fromParser(XmlPullParser parser) { + Objects.requireNonNull(parser, "Parser must be set"); + + properties = new HashMap(); + for (int i = 0; i < parser.getAttributeCount(); i++) { + String attributeName = parser.getAttributeName(i); + boolean isLangAttribute = "lang".equals(attributeName) && "xml".equals(parser.getAttributePrefix(i)); + if (!attributesWhiteList.contains(attributeName) && !isLangAttribute) { + String attributeValue = parser.getAttributeValue(i); + properties.put(attributeName, attributeValue); + } + } + return this; + } + + public CustomAttributesExtension build() { + return new CustomAttributesExtension(properties); + } + } +} 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 e1aef47736..fbacd8db06 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 @@ -29,6 +29,7 @@ import java.util.logging.Logger; import org.jivesoftware.smack.compress.packet.Compress; +import org.jivesoftware.smack.packet.CustomAttributesExtension; import org.jivesoftware.smack.packet.EmptyResultIQ; import org.jivesoftware.smack.packet.ErrorIQ; import org.jivesoftware.smack.packet.IQ; @@ -236,6 +237,12 @@ public static Message parseMessage(XmlPullParser parser) defaultLanguage = Stanza.getDefaultLanguage(); } + // Extract custom attributes and bundle them into an extension + CustomAttributesExtension customAttributesExtension = new CustomAttributesExtension.Builder().fromParser(parser).build(); + if (customAttributesExtension.hasProperties()) { + message.addExtension(customAttributesExtension); + } + // Parse sub-elements. We include extra logic to make sure the values // are only read once. This is because it's possible for the names to appear // in arbitrary sub-elements. 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 117e9e3c18..a06f8a3a63 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 @@ -32,6 +32,7 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import org.jivesoftware.smack.packet.CustomAttributesExtension; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Presence; @@ -816,6 +817,48 @@ public void parseElementMultipleNamespace() assertXMLEqual(stanza, result.toString()); } + @Test + public void parseMessageWithCustomAttributes() + throws FactoryConfigurationError, Exception { + final String customAttrName = "customAttrName"; + final String customAttrValue = "customAttrValue"; + + final String stanza = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a(customAttrName, customAttrValue) + .a("xml:lang", Stanza.getDefaultLanguage()) + .e("body") + .t("This is a test of the custom attributes parsing in message stanza") + .asString(outputProperties); + + final String messageXmlResult = XMLBuilder.create("message") + .a("from", "romeo@montague.lit/orchard") + .a("to", "juliet@capulet.lit/balcony") + .a("id", "zid615d9") + .a("type", "chat") + .a("xml:lang", Stanza.getDefaultLanguage()) + .element(CustomAttributesExtension.NAME, CustomAttributesExtension.NAMESPACE) + .element(customAttrName, CustomAttributesExtension.NAMESPACE) + .t(customAttrValue) + .up() + .up() + .e("body") + .t("This is a test of the custom attributes parsing in message stanza") + .asString(outputProperties); + + Message message = PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(stanza)); + CustomAttributesExtension extension = message.getExtension(CustomAttributesExtension.NAME, CustomAttributesExtension.NAMESPACE); + + assertFalse(extension == null); + assertTrue(extension.hasProperties()); + assertTrue(extension.hasProperty(customAttrName)); + assertEquals(extension.getPropertyValue(customAttrName), customAttrValue); + assertXMLEqual(messageXmlResult, message.toXML().toString()); + } + @Test public void parseSASLFailureSimple() throws FactoryConfigurationError, SAXException, IOException, TransformerException, ParserConfigurationException, XmlPullParserException {