mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-21 22:02:06 +01:00
Implement message stanza <thread/> and <subject/> as ExtensionElement
Fixes SMACK-852.
This commit is contained in:
parent
f9fb4d7627
commit
14c351397d
8 changed files with 172 additions and 38 deletions
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software, 2014-2016 Florian Schmaus
|
||||
* Copyright 2003-2007 Jive Software, 2014-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -36,9 +36,12 @@ import org.jivesoftware.smack.compression.zlib.ZlibXmppCompressionFactory;
|
|||
import org.jivesoftware.smack.initializer.SmackInitializer;
|
||||
import org.jivesoftware.smack.isr.InstantStreamResumptionModuleDescriptor;
|
||||
import org.jivesoftware.smack.packet.Bind;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Message.Body;
|
||||
import org.jivesoftware.smack.provider.BindIQProvider;
|
||||
import org.jivesoftware.smack.provider.BodyElementProvider;
|
||||
import org.jivesoftware.smack.provider.MessageSubjectElementProvider;
|
||||
import org.jivesoftware.smack.provider.MessageThreadElementProvider;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.jivesoftware.smack.provider.SaslChallengeProvider;
|
||||
import org.jivesoftware.smack.provider.SaslFailureProvider;
|
||||
|
@ -130,6 +133,8 @@ public final class SmackInitialization {
|
|||
|
||||
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
|
||||
ProviderManager.addExtensionProvider(Body.ELEMENT, Body.NAMESPACE, new BodyElementProvider());
|
||||
ProviderManager.addExtensionProvider(Message.Thread.ELEMENT, Message.Thread.NAMESPACE, new MessageThreadElementProvider());
|
||||
ProviderManager.addExtensionProvider(Message.Subject.ELEMENT, Message.Subject.NAMESPACE, new MessageSubjectElementProvider());
|
||||
|
||||
ProviderManager.addNonzaProvider(SaslChallengeProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(SaslSuccessProvider.INSTANCE);
|
||||
|
|
|
@ -26,9 +26,11 @@ import java.util.Set;
|
|||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message.Thread;
|
||||
import org.jivesoftware.smack.util.EqualsUtil;
|
||||
import org.jivesoftware.smack.util.HashCode;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.TypedCloneable;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
|
@ -67,7 +69,6 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
public static final String BODY = "body";
|
||||
|
||||
private Type type;
|
||||
private String thread = null;
|
||||
|
||||
/**
|
||||
* Creates a new, "normal" message.
|
||||
|
@ -150,7 +151,6 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
Message(MessageBuilder messageBuilder) {
|
||||
super(messageBuilder);
|
||||
type = messageBuilder.type;
|
||||
thread = messageBuilder.thread;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,7 +165,6 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
public Message(Message other) {
|
||||
super(other);
|
||||
this.type = other.type;
|
||||
this.thread = other.thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -502,7 +501,11 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
* @return the thread id of the message, or <code>null</code> if it doesn't exist.
|
||||
*/
|
||||
public String getThread() {
|
||||
return thread;
|
||||
Message.Thread thread = getExtension(Message.Thread.class);
|
||||
if (thread == null) {
|
||||
return null;
|
||||
}
|
||||
return thread.getThread();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -515,7 +518,7 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
@Deprecated
|
||||
// TODO: Remove when stanza builder is ready.
|
||||
public void setThread(String thread) {
|
||||
this.thread = thread;
|
||||
addExtension(new Message.Thread(thread));
|
||||
}
|
||||
|
||||
private String determineLanguage(String language) {
|
||||
|
@ -559,7 +562,6 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
buf.optAttribute("type", type);
|
||||
buf.rightAngleBracket();
|
||||
|
||||
buf.optElement("thread", thread);
|
||||
// Append the error subpacket if the message type is an error.
|
||||
if (type == Type.error) {
|
||||
appendErrorIfExists(buf);
|
||||
|
@ -754,6 +756,60 @@ public final class Message extends MessageOrPresence<MessageBuilder>
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavaLangClash")
|
||||
public static class Thread implements ExtensionElement {
|
||||
public static final String ELEMENT = "thread";
|
||||
public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE;
|
||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
public static final String PARENT_ATTRIBUTE_NAME = "parent";
|
||||
|
||||
private final String thread;
|
||||
private final String parent;
|
||||
|
||||
public Thread(String thread) {
|
||||
this(thread, null);
|
||||
}
|
||||
|
||||
public Thread(String thread, String parent) {
|
||||
this.thread = StringUtils.requireNotNullNorEmpty(thread, "thread must not be null nor empty");
|
||||
this.parent = StringUtils.requireNullOrNotEmpty(parent, "parent must be null or not empty");
|
||||
}
|
||||
|
||||
public String getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QName getQName() {
|
||||
return QNAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||
xml.optAttribute(PARENT_ATTRIBUTE_NAME, parent);
|
||||
xml.rightAngleBracket();
|
||||
xml.escape(thread);
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of a message.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2019 Florian Schmaus
|
||||
* Copyright 2019-2020 Florian Schmaus
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -29,8 +29,6 @@ public final class MessageBuilder extends MessageOrPresenceBuilder<Message, Mess
|
|||
|
||||
Message.Type type;
|
||||
|
||||
String thread;
|
||||
|
||||
MessageBuilder(Message message, String stanzaId) {
|
||||
super(message, stanzaId);
|
||||
copyFromMessage(message);
|
||||
|
@ -51,13 +49,11 @@ public final class MessageBuilder extends MessageOrPresenceBuilder<Message, Mess
|
|||
|
||||
private void copyFromMessage(Message message) {
|
||||
type = message.getType();
|
||||
thread = message.getThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
|
||||
builder.addValue("type", type)
|
||||
.addValue("thread", thread)
|
||||
;
|
||||
}
|
||||
|
||||
|
@ -67,7 +63,11 @@ public final class MessageBuilder extends MessageOrPresenceBuilder<Message, Mess
|
|||
}
|
||||
|
||||
public MessageBuilder setThread(String thread) {
|
||||
this.thread = thread;
|
||||
return setThread(thread, null);
|
||||
}
|
||||
|
||||
public MessageBuilder setThread(String thread, String parent) {
|
||||
addExtension(new Message.Thread(thread, parent));
|
||||
return getThis();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 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.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
|
||||
public class MessageSubjectElementProvider extends ExtensionElementProvider<Message.Subject> {
|
||||
|
||||
@Override
|
||||
public Message.Subject parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
||||
throws XmlPullParserException, IOException, SmackParsingException {
|
||||
String xmlLangSubject = ParserUtils.getXmlLang(parser);
|
||||
String subject = parser.nextText();
|
||||
|
||||
return new Message.Subject(xmlLangSubject, subject);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2020 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.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
|
||||
public class MessageThreadElementProvider extends ExtensionElementProvider<Message.Thread> {
|
||||
|
||||
@Override
|
||||
public Message.Thread parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
||||
throws XmlPullParserException, IOException, SmackParsingException {
|
||||
String parent = parser.getAttributeValue(Message.Thread.PARENT_ATTRIBUTE_NAME);
|
||||
String thread = parser.nextText();
|
||||
|
||||
return new Message.Thread(thread, parent);
|
||||
}
|
||||
|
||||
}
|
|
@ -179,7 +179,6 @@ public class PacketParserUtils {
|
|||
// 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.
|
||||
String thread = null;
|
||||
outerloop: while (true) {
|
||||
XmlPullParser.Event eventType = parser.next();
|
||||
switch (eventType) {
|
||||
|
@ -187,18 +186,6 @@ public class PacketParserUtils {
|
|||
String elementName = parser.getName();
|
||||
String namespace = parser.getNamespace();
|
||||
switch (elementName) {
|
||||
case "subject":
|
||||
String xmlLangSubject = ParserUtils.getXmlLang(parser);
|
||||
String subject = parseElementText(parser);
|
||||
|
||||
Message.Subject subjectExtensionElement = new Message.Subject(xmlLangSubject, subject);
|
||||
message.addExtension(subjectExtensionElement);
|
||||
break;
|
||||
case "thread":
|
||||
if (thread == null) {
|
||||
thread = parser.nextText();
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
message.setError(parseError(parser, messageXmlEnvironment));
|
||||
break;
|
||||
|
@ -217,8 +204,6 @@ public class PacketParserUtils {
|
|||
}
|
||||
}
|
||||
|
||||
message.setThread(thread);
|
||||
|
||||
// TODO check for duplicate body elements. This means we need to check for duplicate xml:lang pairs and for
|
||||
// situations where we have a body element with an explicit xml lang set and once where the value is inherited
|
||||
// and both values are equal.
|
||||
|
|
|
@ -223,6 +223,7 @@ public class PacketParserUtilsTest {
|
|||
|
||||
// message has default language, subject has no language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -239,10 +240,11 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().isEmpty());
|
||||
assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
|
||||
assertNull(message.getSubject(otherLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has non-default language, subject has no language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -258,10 +260,11 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().isEmpty());
|
||||
assertEquals(otherLanguage, message.getSubject(otherLanguage));
|
||||
assertNull(message.getSubject(defaultLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has no language, subject has no language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -276,10 +279,11 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().isEmpty());
|
||||
assertEquals(defaultLanguage, message.getSubject(null));
|
||||
assertNull(message.getSubject(otherLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has no language, subject has default language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -295,10 +299,11 @@ public class PacketParserUtilsTest {
|
|||
assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
|
||||
assertNull(message.getSubject(otherLanguage));
|
||||
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has no language, subject has non-default language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -315,10 +320,11 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().contains(otherLanguage));
|
||||
assertEquals(otherLanguage, message.getSubject(otherLanguage));
|
||||
assertNull(message.getSubject(defaultLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has default language, subject has non-default language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -336,10 +342,11 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().contains(otherLanguage));
|
||||
assertEquals(otherLanguage, message.getSubject(otherLanguage));
|
||||
assertNull(message.getSubject(defaultLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has non-default language, subject has default language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -357,7 +364,7 @@ public class PacketParserUtilsTest {
|
|||
assertTrue(message.getSubjectLanguages().contains(defaultLanguage));
|
||||
assertEquals(defaultLanguage, message.getSubject(defaultLanguage));
|
||||
assertNull(message.getSubject(otherLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
}
|
||||
|
||||
|
@ -523,6 +530,7 @@ public class PacketParserUtilsTest {
|
|||
|
||||
// message has default language, first subject no language, second subject other language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -544,10 +552,11 @@ public class PacketParserUtilsTest {
|
|||
assertEquals(2, message.getSubjects().size());
|
||||
assertEquals(1, message.getSubjectLanguages().size());
|
||||
assertTrue(message.getSubjectLanguages().contains(otherLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
// message has non-default language, first subject no language, second subject default language
|
||||
control = XMLBuilder.create("message")
|
||||
.ns(StreamOpen.CLIENT_NAMESPACE)
|
||||
.a("from", "romeo@montague.lit/orchard")
|
||||
.a("to", "juliet@capulet.lit/balcony")
|
||||
.a("id", "zid615d9")
|
||||
|
@ -569,7 +578,7 @@ public class PacketParserUtilsTest {
|
|||
assertEquals(2, message.getSubjects().size());
|
||||
assertEquals(1, message.getSubjectLanguages().size());
|
||||
assertTrue(message.getSubjectLanguages().contains(defaultLanguage));
|
||||
assertXmlSimilar(control, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||
assertXmlSimilar(control, message.toXML());
|
||||
|
||||
/*
|
||||
// message has no language, first subject no language, second subject default language
|
||||
|
|
|
@ -336,7 +336,9 @@ public class ChatConnectionTest {
|
|||
jid = JidTestUtil.DUMMY_AT_EXAMPLE_ORG;
|
||||
}
|
||||
chatMsg.from(jid);
|
||||
chatMsg.setThread(threadId);
|
||||
if (threadId != null) {
|
||||
chatMsg.setThread(threadId);
|
||||
}
|
||||
return chatMsg;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue