Browse Source

Introduce StanzaBuilder

As first step to immutable Stanza types.
inbox
Florian Schmaus 7 months ago
parent
commit
5db6191110
100 changed files with 2324 additions and 623 deletions
  1. +5
    -1
      documentation/overview.md
  2. +2
    -2
      smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
  3. +21
    -3
      smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java
  4. +23
    -0
      smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java
  5. +0
    -1
      smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java
  6. +3
    -0
      smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java
  7. +1
    -3
      smack-core/src/main/java/org/jivesoftware/smack/compress/provider/FailureProvider.java
  8. +7
    -3
      smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java
  9. +4
    -4
      smack-core/src/main/java/org/jivesoftware/smack/packet/ErrorIQ.java
  10. +2
    -6
      smack-core/src/main/java/org/jivesoftware/smack/packet/FullyQualifiedElement.java
  11. +27
    -43
      smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java
  12. +57
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java
  13. +28
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/IqView.java
  14. +87
    -31
      smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java
  15. +164
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/MessageBuilder.java
  16. +29
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java
  17. +46
    -26
      smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java
  18. +140
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/PresenceBuilder.java
  19. +59
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/PresenceView.java
  20. +5
    -1
      smack-core/src/main/java/org/jivesoftware/smack/packet/SimpleIQ.java
  21. +138
    -75
      smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java
  22. +301
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java
  23. +2
    -22
      smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaError.java
  24. +49
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaFactory.java
  25. +82
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaView.java
  26. +0
    -1
      smack-core/src/main/java/org/jivesoftware/smack/packet/XmlEnvironment.java
  27. +28
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/XmlLangElement.java
  28. +54
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/id/RandomStringStanzaIdSource.java
  29. +18
    -6
      smack-core/src/main/java/org/jivesoftware/smack/packet/id/StandardStanzaIdSource.java
  30. +21
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/id/StanzaIdSource.java
  31. +21
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/id/StanzaIdSourceFactory.java
  32. +41
    -0
      smack-core/src/main/java/org/jivesoftware/smack/packet/id/UuidStanzaIdSource.java
  33. +23
    -0
      smack-core/src/main/java/org/jivesoftware/smack/util/Function.java
  34. +14
    -1
      smack-core/src/main/java/org/jivesoftware/smack/util/MultiMap.java
  35. +43
    -26
      smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java
  36. +12
    -1
      smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
  37. +75
    -0
      smack-core/src/main/java/org/jivesoftware/smack/util/ToStringUtil.java
  38. +20
    -0
      smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java
  39. +10
    -10
      smack-core/src/test/java/org/jivesoftware/smack/filter/FromMatchesFilterTest.java
  40. +3
    -3
      smack-core/src/test/java/org/jivesoftware/smack/packet/IQResponseTest.java
  41. +37
    -38
      smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java
  42. +33
    -30
      smack-core/src/test/java/org/jivesoftware/smack/packet/PresenceTest.java
  43. +10
    -6
      smack-core/src/test/java/org/jivesoftware/smack/packet/ToStringTest.java
  44. +3
    -2
      smack-core/src/test/java/org/jivesoftware/smack/packet/XMPPErrorTest.java
  45. +1
    -26
      smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java
  46. +2
    -1
      smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppManager.java
  47. +16
    -12
      smack-experimental/src/main/java/org/jivesoftware/smackx/eme/element/ExplicitMessageEncryptionElement.java
  48. +11
    -6
      smack-experimental/src/main/java/org/jivesoftware/smackx/hints/element/StoreHint.java
  49. +9
    -3
      smack-experimental/src/main/java/org/jivesoftware/smackx/iot/control/element/IoTSetResponse.java
  50. +7
    -3
      smack-experimental/src/main/java/org/jivesoftware/smackx/iot/data/IoTDataManager.java
  51. +19
    -8
      smack-experimental/src/main/java/org/jivesoftware/smackx/iot/provisioning/IoTProvisioningManager.java
  52. +38
    -6
      smack-experimental/src/main/java/org/jivesoftware/smackx/muclight/MultiUserChatLight.java
  53. +3
    -1
      smack-experimental/src/main/java/org/jivesoftware/smackx/push_notifications/provider/RemoteDisablingProvider.java
  54. +5
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/chat_markers/AcknowledgedExtensionTest.java
  55. +5
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/chat_markers/DisplayedExtensionTest.java
  56. +7
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/chat_markers/MarkableExtensionTest.java
  57. +5
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/chat_markers/ReceivedExtensionTest.java
  58. +12
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/eme/ExplicitMessageEncryptionElementTest.java
  59. +10
    -9
      smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java
  60. +5
    -9
      smack-experimental/src/test/java/org/jivesoftware/smackx/push_notifications/RemoteDisablingPushNotificationsTest.java
  61. +2
    -1
      smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java
  62. +5
    -4
      smack-experimental/src/test/java/org/jivesoftware/smackx/spoiler/SpoilerTest.java
  63. +6
    -3
      smack-extensions/src/main/java/org/jivesoftware/smack/chat2/Chat.java
  64. +1
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java
  65. +4
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java
  66. +1
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java
  67. +1
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java
  68. +2
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/chatstates/ChatStateManager.java
  69. +11
    -6
      smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java
  70. +1
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/commands/provider/AdHocCommandDataProvider.java
  71. +2
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java
  72. +1
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferManager.java
  73. +2
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java
  74. +4
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java
  75. +16
    -12
      smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java
  76. +23
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java
  77. +4
    -0
      smack-extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/packet/JivePropertiesExtension.java
  78. +34
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MucEnterConfiguration.java
  79. +93
    -19
      smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java
  80. +6
    -3
      smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java
  81. +4
    -3
      smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java
  82. +15
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/ping/packet/Ping.java
  83. +6
    -2
      smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java
  84. +18
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptRequest.java
  85. +3
    -1
      smack-extensions/src/main/java/org/jivesoftware/smackx/vcardtemp/VCardManager.java
  86. +1
    -1
      smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/IBBPacketUtils.java
  87. +10
    -6
      smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionMessageTest.java
  88. +2
    -2
      smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java
  89. +1
    -1
      smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java
  90. +4
    -1
      smack-extensions/src/test/java/org/jivesoftware/smackx/last_interaction/IdleTest.java
  91. +3
    -2
      smack-extensions/src/test/java/org/jivesoftware/smackx/mood/MoodManagerTest.java
  92. +1
    -1
      smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ConfigureFormTest.java
  93. +22
    -14
      smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java
  94. +4
    -2
      smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneManagerTest.java
  95. +21
    -2
      smack-im/src/main/java/org/jivesoftware/smack/chat/Chat.java
  96. +35
    -26
      smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java
  97. +6
    -2
      smack-im/src/main/java/org/jivesoftware/smack/roster/RosterEntry.java
  98. +0
    -1
      smack-im/src/main/java/org/jivesoftware/smack/roster/provider/RosterVerStreamFeatureProvider.java
  99. +0
    -1
      smack-im/src/main/java/org/jivesoftware/smack/roster/provider/SubscriptionPreApprovalStreamFeatureProvider.java
  100. +46
    -38
      smack-im/src/test/java/org/jivesoftware/smack/chat/ChatConnectionTest.java

+ 5
- 1
documentation/overview.md View File

@@ -15,7 +15,11 @@ Smack Key Advantages
AbstractXMPPConnection connection = new XMPPTCPConnection("mtucker", "password", "jabber.org");
connection.connect().login();

Message message = new Message("jsmith@jivesoftware.com", "Howdy! How are you?");
Message message = connection.getStanzaFactory()
.buildMessageStanza()
.to("jsmith@jivesoftware.com")
.setBody("Howdy! How are you?")
.build();
connection.sendStanza(message);
```



+ 2
- 2
smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java View File

@@ -517,8 +517,8 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
} else {
StanzaError.Builder builder = PacketParserUtils.parseError(parser);
throw new XMPPException.XMPPErrorException(null, builder.build());
StanzaError stanzaError = PacketParserUtils.parseError(parser);
throw new XMPPException.XMPPErrorException(null, stanzaError);
}
default:
parseAndProcessNonza(parser);


+ 21
- 3
smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java View File

@@ -105,11 +105,13 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StanzaFactory;
import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
@@ -402,6 +404,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>();
private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>();

private final StanzaFactory stanzaFactory;

/**
* Create a new XMPPConnection to an XMPP server.
*
@@ -440,6 +444,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) {
listener.connectionCreated(this);
}

StanzaIdSource stanzaIdSource = configuration.constructStanzaIdSource();
stanzaFactory = new StanzaFactory(stanzaIdSource);
}

/**
@@ -733,7 +740,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// eventually load the roster. And we should load the roster before we
// send the initial presence.
if (config.isSendPresence() && !resumed) {
sendStanza(new Presence(Presence.Type.available));
Presence availablePresence = getStanzaFactory()
.buildPresenceStanza()
.ofType(Presence.Type.available)
.build();
sendStanza(availablePresence);
}
}

@@ -814,6 +825,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
}

@Override
public final StanzaFactory getStanzaFactory() {
return stanzaFactory;
}

@Override
public final void sendStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
Objects.requireNonNull(stanza, "Stanza must not be null");
@@ -893,7 +909,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
public void disconnect() {
Presence unavailablePresence = null;
if (isAuthenticated()) {
unavailablePresence = new Presence(Presence.Type.unavailable);
unavailablePresence = getStanzaFactory().buildPresenceStanza()
.ofType(Presence.Type.unavailable)
.build();
}
try {
disconnect(unavailablePresence);
@@ -1416,7 +1434,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an
// IQ of type 'error' with condition 'service-unavailable'.
final ErrorIQ errorIQ = IQ.createErrorResponse(iq, StanzaError.getBuilder(
replyCondition));
replyCondition).build());
// Use async sendStanza() here, since if sendStanza() would block, then some connections, e.g.
// XmppNioTcpConnection, would deadlock, as this operation is performed in the same thread that is
asyncGo(() -> {


+ 23
- 0
smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java View File

@@ -36,6 +36,9 @@ import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.CallbackHandler;

import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
@@ -159,6 +162,8 @@ public abstract class ConnectionConfiguration {

private final boolean compressionEnabled;

private final StanzaIdSourceFactory stanzaIdSourceFactory;

protected ConnectionConfiguration(Builder<?, ?> builder) {
authzid = builder.authzid;
username = builder.username;
@@ -213,6 +218,8 @@ public abstract class ConnectionConfiguration {

compressionEnabled = builder.compressionEnabled;

stanzaIdSourceFactory = builder.stanzaIdSourceFactory;

// If the enabledSaslmechanisms are set, then they must not be empty
assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty();

@@ -568,6 +575,10 @@ public abstract class ConnectionConfiguration {
return Collections.unmodifiableSet(enabledSaslMechanisms);
}

StanzaIdSource constructStanzaIdSource() {
return stanzaIdSourceFactory.constructStanzaIdSource();
}

/**
* A builder for XMPP connection configurations.
* <p>
@@ -612,6 +623,7 @@ public abstract class ConnectionConfiguration {
private Set<String> enabledSaslMechanisms;
private X509TrustManager customX509TrustManager;
private boolean compressionEnabled = false;
private StanzaIdSourceFactory stanzaIdSourceFactory = new StandardStanzaIdSource.Factory();

protected Builder() {
if (SmackConfiguration.DEBUG) {
@@ -1134,6 +1146,17 @@ public abstract class ConnectionConfiguration {
return getThis();
}

/**
* Set the factory for stanza ID sources to use.
*
* @param stanzaIdSourceFactory the factory for stanza ID sources to use.
* @return a reference to this builder.
* @since 4.4
*/
public B setStanzaIdSourceFactory(StanzaIdSourceFactory stanzaIdSourceFactory) {
this.stanzaIdSourceFactory = Objects.requireNonNull(stanzaIdSourceFactory);
return getThis();
}

public abstract C build();



+ 0
- 1
smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java View File

@@ -49,7 +49,6 @@ import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.FileUtils;
import org.jivesoftware.smack.util.PacketParserUtils;

import org.jivesoftware.smack.xml.XmlPullParser;




+ 3
- 0
smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java View File

@@ -29,6 +29,7 @@ import org.jivesoftware.smack.packet.FullyQualifiedElement;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaFactory;

import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityFullJid;
@@ -178,6 +179,8 @@ public interface XMPPConnection {
*/
boolean isUsingCompression();

StanzaFactory getStanzaFactory();

/**
* Sends the specified stanza to the server.
*


+ 1
- 3
smack-core/src/main/java/org/jivesoftware/smack/compress/provider/FailureProvider.java View File

@@ -26,7 +26,6 @@ import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.util.PacketParserUtils;

import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;

@@ -62,8 +61,7 @@ public final class FailureProvider extends NonzaProvider<Failure> {
case StreamOpen.SERVER_NAMESPACE:
switch (name) {
case StanzaError.ERROR:
StanzaError.Builder stanzaErrorBuilder = PacketParserUtils.parseError(parser, failureXmlEnvironment);
stanzaError = stanzaErrorBuilder.build();
stanzaError = PacketParserUtils.parseError(parser, failureXmlEnvironment);
break;
default:
LOGGER.warning("Unknown element in " + namespace + ": " + name);


+ 7
- 3
smack-core/src/main/java/org/jivesoftware/smack/packet/EmptyResultIQ.java View File

@@ -1,6 +1,6 @@
/**
*
* Copyright © 2014 Florian Schmaus
* Copyright © 2014-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.
@@ -18,14 +18,18 @@ package org.jivesoftware.smack.packet;

public class EmptyResultIQ extends IQ {

EmptyResultIQ(IqBuilder iqBuilder) {
super(iqBuilder, null, null);
}

// TODO: Deprecate when stanza builder and parsing logic is ready.
public EmptyResultIQ() {
super(null, null);
setType(IQ.Type.result);
}

public EmptyResultIQ(IQ request) {
this();
initializeAsResultFor(request);
this(StanzaBuilder.buildIqResultFor(request));
}

@Override


+ 4
- 4
smack-core/src/main/java/org/jivesoftware/smack/packet/ErrorIQ.java View File

@@ -27,13 +27,13 @@ public class ErrorIQ extends SimpleIQ {
* <p>
* According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an &lt;error/&gt; child element.", so the xmppError argument is mandatory.
* </p>
* @param xmppErrorBuilder the XMPPError builder (required).
* @param stanzaError the stanzaError (required).
*/
public ErrorIQ(StanzaError.Builder xmppErrorBuilder) {
public ErrorIQ(StanzaError stanzaError) {
super(ELEMENT, null);
Objects.requireNonNull(xmppErrorBuilder, "xmppErrorBuilder must not be null");
Objects.requireNonNull(stanzaError, "stanzaError must not be null");
setType(IQ.Type.error);
setError(xmppErrorBuilder);
setError(stanzaError);
}

}

+ 2
- 6
smack-core/src/main/java/org/jivesoftware/smack/packet/FullyQualifiedElement.java View File

@@ -18,7 +18,7 @@ package org.jivesoftware.smack.packet;

import javax.xml.namespace.QName;

public interface FullyQualifiedElement extends NamedElement {
public interface FullyQualifiedElement extends NamedElement, XmlLangElement {

/**
* Returns the root element XML namespace.
@@ -33,11 +33,7 @@ public interface FullyQualifiedElement extends NamedElement {
return new QName(namespaceURI, localPart);
}

/**
* Returns the xml:lang of this XML element, or null if one has not been set.
*
* @return the xml:lang of this XML element, or null.
*/
@Override
default String getLanguage() {
return null;
}


+ 27
- 43
smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java View File

@@ -42,7 +42,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
*
* @author Matt Tucker
*/
public abstract class IQ extends Stanza {
public abstract class IQ extends Stanza implements IqView {

// Don't name this field 'ELEMENT'. When it comes to IQ, ELEMENT is the child element!
public static final String IQ_ELEMENT = "iq";
@@ -54,6 +54,7 @@ public abstract class IQ extends Stanza {

private Type type = Type.get;

// TODO: This method should be protected!
public IQ(IQ iq) {
super(iq);
type = iq.getType();
@@ -62,7 +63,16 @@ public abstract class IQ extends Stanza {
this.childElementQName = iq.childElementQName;
}

// TODO: Deprecate when stanza builder is ready.
protected IQ(String childElementName, String childElementNamespace) {
this(IqBuilder.EMPTY, childElementName, childElementNamespace);
}

protected IQ(IqBuilder iqBuilder, String childElementName, String childElementNamespace) {
super(iqBuilder);

type = iqBuilder.type;

this.childElementName = childElementName;
this.childElementNamespace = childElementNamespace;
if (childElementName == null) {
@@ -72,11 +82,7 @@ public abstract class IQ extends Stanza {
}
}

/**
* Returns the type of the IQ packet.
*
* @return the type of the IQ packet.
*/
@Override
public Type getType() {
return type;
}
@@ -90,6 +96,7 @@ public abstract class IQ extends Stanza {
*
* @param type the type of the IQ packet.
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setType(Type type) {
this.type = Objects.requireNonNull(type, "type must not be null");
}
@@ -260,19 +267,6 @@ public abstract class IQ extends Stanza {
*/
protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);

protected final void initializeAsResultFor(IQ request) {
assert this != request;

if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
setType(Type.result);
}

/**
* Convenience method to create a new empty {@link Type#result IQ.Type.result}
* IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
@@ -311,7 +305,7 @@ public abstract class IQ extends Stanza {
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
*/
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) {
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError error) {
if (!request.isRequestIQ()) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
@@ -321,35 +315,25 @@ public abstract class IQ extends Stanza {
result.setFrom(request.getTo());
result.setTo(request.getFrom());

error.setStanza(result);

return result;
}

public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Condition condition) {
return createErrorResponse(request, StanzaError.getBuilder(condition));
}

/**
* Convenience method to create a new {@link Type#error IQ.Type.error} IQ
* based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
* IQ. The new stanza will be initialized with:<ul>
* <li>The sender set to the recipient of the originating IQ.
* <li>The recipient set to the sender of the originating IQ.
* <li>The type set to {@link Type#error IQ.Type.error}.
* <li>The id set to the id of the originating IQ.
* <li>The child element contained in the associated originating IQ.
* <li>The provided {@link StanzaError XMPPError}.
* </ul>
* Deprecated.
*
* @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
* @param error the error to associate with the created IQ packet.
* @throws IllegalArgumentException if the IQ stanza does not have a type of
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
* @param request the request.
* @param error the error.
* @return an error IQ.
* @deprecated use {@link #createErrorResponse(IQ, StanzaError)} instead.
*/
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError error) {
return createErrorResponse(request, StanzaError.getBuilder(error));
@Deprecated
// TODO: Remove in Smack 4.5.
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) {
return createErrorResponse(request, error.build());
}

public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Condition condition) {
return createErrorResponse(request, StanzaError.getBuilder(condition).build());
}

/**


+ 57
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java View File

@@ -0,0 +1,57 @@
/**
*
* 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.packet;

import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ToStringUtil;

public final class IqBuilder extends StanzaBuilder<IqBuilder> implements IqView {
static final IqBuilder EMPTY = new IqBuilder(StandardStanzaIdSource.DEFAULT);

IQ.Type type = Type.get;

IqBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}

IqBuilder(String stanzaId) {
super(stanzaId);
}

@Override
protected void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
builder.addValue("type", type);
}

public IqBuilder ofType(IQ.Type type) {
this.type = Objects.requireNonNull(type);
return getThis();
}

@Override
public IqBuilder getThis() {
return this;
}

@Override
public IQ.Type getType() {
return type;
}
}

+ 28
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/IqView.java View File

@@ -0,0 +1,28 @@
/**
*
* 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.packet;

public interface IqView extends StanzaView {

/**
* Returns the type of the IQ packet.
*
* @return the type of the IQ packet.
*/
IQ.Type getType();

}

+ 87
- 31
smack-core/src/main/java/org/jivesoftware/smack/packet/Message.java View File

@@ -24,6 +24,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.xml.namespace.QName;

import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.Objects;
@@ -58,7 +60,7 @@ import org.jxmpp.stringprep.XmppStringprepException;
*
* @author Matt Tucker
*/
public final class Message extends Stanza implements TypedCloneable<Message> {
public final class Message extends Stanza implements MessageView, TypedCloneable<Message> {

public static final String ELEMENT = "message";
public static final String BODY = "body";
@@ -66,11 +68,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
private Type type;
private String thread = null;

private final Set<Subject> subjects = new HashSet<Subject>();

/**
* Creates a new, "normal" message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message() {
}

@@ -78,7 +81,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Creates a new "normal" message to the specified recipient.
*
* @param to the recipient of the message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to) {
setTo(to);
}
@@ -88,7 +94,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
*
* @param to the user to send the message to.
* @param type the message type.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, Type type) {
this(to);
setType(type);
@@ -99,7 +108,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
*
* @param to the user to send the message to.
* @param body the body of the message.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, String body) {
this(to);
setBody(body);
@@ -111,7 +123,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param to the user to send the message to.
* @param body the body of the message.
* @throws XmppStringprepException if 'to' is not a valid XMPP address.
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(String to, String body) throws XmppStringprepException {
this(JidCreate.from(to), body);
}
@@ -122,12 +137,21 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param to TODO javadoc me please
* @param extensionElement TODO javadoc me please
* @since 4.2
* @deprecated use {@link StanzaBuilder}, preferable via {@link StanzaFactory}, instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Message(Jid to, ExtensionElement extensionElement) {
this(to);
addExtension(extensionElement);
}

Message(MessageBuilder messageBuilder) {
super(messageBuilder);
type = messageBuilder.type;
thread = messageBuilder.thread;
}

/**
* Copy constructor.
* <p>
@@ -141,15 +165,9 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
super(other);
this.type = other.type;
this.thread = other.thread;
this.subjects.addAll(other.subjects);
}

/**
* Returns the type of the message. If no type has been set this method will return {@link
* org.jivesoftware.smack.packet.Message.Type#normal}.
*
* @return the type of the message.
*/
@Override
public Type getType() {
if (type == null) {
return Type.normal;
@@ -161,7 +179,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Sets the type of the message.
*
* @param type the type of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setType(Type type) {
this.type = type;
}
@@ -195,8 +216,9 @@ public final class Message extends Stanza implements TypedCloneable<Message> {

private Subject getMessageSubject(String language) {
language = determineLanguage(language);
for (Subject subject : subjects) {
if (Objects.equals(language, subject.language)) {
for (Subject subject : getSubjects()) {
if (Objects.equals(language, subject.language)
|| (subject.language == null && Objects.equals(this.language, language))) {
return subject;
}
}
@@ -210,7 +232,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return a collection of all subjects in this message.
*/
public Set<Subject> getSubjects() {
return Collections.unmodifiableSet(subjects);
List<Subject> subjectList = getExtensions(Subject.class);

Set<Subject> subjects = new HashSet<>(subjectList.size());
subjects.addAll(subjectList);

return subjects;
}

/**
@@ -218,7 +245,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* message contents.
*
* @param subject the subject of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setSubject(String subject) {
if (subject == null) {
removeSubject(""); // use empty string because #removeSubject(null) is ambiguous
@@ -235,10 +265,20 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return the new {@link org.jivesoftware.smack.packet.Message.Subject}
* @throws NullPointerException if the subject is null, a null pointer exception is thrown
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public Subject addSubject(String language, String subject) {
language = determineLanguage(language);

List<Subject> currentSubjects = getExtensions(Subject.class);
for (Subject currentSubject : currentSubjects) {
if (language.equals(currentSubject.getLanguage())) {
throw new IllegalArgumentException("Subject with the language " + language + " already exists");
}
}

Subject messageSubject = new Subject(language, subject);
subjects.add(messageSubject);
addExtension(messageSubject);
return messageSubject;
}

@@ -248,11 +288,13 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param language the language of the subject which is to be removed
* @return true if a subject was removed and false if it was not.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeSubject(String language) {
language = determineLanguage(language);
for (Subject subject : subjects) {
for (Subject subject : getExtensions(Subject.class)) {
if (language.equals(subject.language)) {
return subjects.remove(subject);
return removeSubject(subject);
}
}
return false;
@@ -264,8 +306,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param subject the subject being removed from the message.
* @return true if the subject was successfully removed and false if it was not.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeSubject(Subject subject) {
return subjects.remove(subject);
return removeExtension(subject) != null;
}

/**
@@ -276,7 +320,7 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
public List<String> getSubjectLanguages() {
Subject defaultSubject = getMessageSubject(null);
List<String> languages = new ArrayList<String>();
for (Subject subject : subjects) {
for (Subject subject : getExtensions(Subject.class)) {
if (!subject.equals(defaultSubject)) {
languages.add(subject.language);
}
@@ -345,7 +389,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param body the body of the message.
* @see #setBody(String)
* @since 4.2
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setBody(CharSequence body) {
String bodyString;
if (body != null) {
@@ -360,7 +407,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* Sets the body of the message. The body is the main message contents.
*
* @param body the body of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setBody(String body) {
if (body == null) {
removeBody(""); // use empty string because #removeBody(null) is ambiguous
@@ -377,7 +427,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @return the new {@link org.jivesoftware.smack.packet.Message.Body}
* @throws NullPointerException if the body is null, a null pointer exception is thrown
* @since 3.0.2
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public Body addBody(String language, String body) {
language = determineLanguage(language);

@@ -393,7 +446,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
*
* @param language the language of the body which is to be removed
* @return true if a body was removed and false if it was not.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeBody(String language) {
language = determineLanguage(language);
for (Body body : getBodies()) {
@@ -412,7 +468,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* @param body the body being removed from the message.
* @return true if the body was successfully removed and false if it was not.
* @since 3.0.2
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public boolean removeBody(Body body) {
ExtensionElement removedElement = removeExtension(body);
return removedElement != null;
@@ -450,7 +509,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
* of "chat" messages.
*
* @param thread the thread id of the message.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove when stanza builder is ready.
public void setThread(String thread) {
this.thread = thread;
}
@@ -472,6 +534,10 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
return ELEMENT;
}

public MessageBuilder asBuilder() {
return StanzaBuilder.buildMessageFrom(this, getStanzaId());
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -491,18 +557,6 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
buf.optAttribute("type", type);
buf.rightAngleBracket();

// Add the subject in the default language
Subject defaultSubject = getMessageSubject(null);
if (defaultSubject != null) {
buf.element("subject", defaultSubject.subject);
}
// Add the subject in other languages
for (Subject subject : getSubjects()) {
// Skip the default language
if (subject.equals(defaultSubject))
continue;
buf.append(subject);
}
buf.optElement("thread", thread);
// Append the error subpacket if the message type is an error.
if (type == Type.error) {
@@ -537,10 +591,12 @@ public final class Message extends Stanza implements TypedCloneable<Message> {
public static final String ELEMENT = "subject";
public static final String NAMESPACE = StreamOpen.CLIENT_NAMESPACE;

public static final QName QNAME = new QName(NAMESPACE, ELEMENT);

private final String subject;
private final String language;

private Subject(String language, String subject) {
public Subject(String language, String subject) {
if (subject == null) {
throw new NullPointerException("Subject cannot be null.");
}


+ 164
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/MessageBuilder.java View File

@@ -0,0 +1,164 @@
/**
*
* 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.packet;

import org.jivesoftware.smack.packet.Message.Body;
import org.jivesoftware.smack.packet.Message.Subject;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.ToStringUtil;

public final class MessageBuilder extends StanzaBuilder<MessageBuilder> implements MessageView {
static final MessageBuilder EMPTY = new MessageBuilder(() -> {
return null;
});

Message.Type type;

String thread;

MessageBuilder(Message message, String stanzaId) {
super(message, stanzaId);
copyFromMessage(message);
}

MessageBuilder(Message message, StanzaIdSource stanzaIdSource) {
super(message, stanzaIdSource);
copyFromMessage(message);
}

MessageBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}

MessageBuilder(String stanzaId) {
super(stanzaId);
}

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)
;
}

public MessageBuilder ofType(Message.Type type) {
this.type = type;
return getThis();
}

public MessageBuilder setThread(String thread) {
this.thread = thread;
return getThis();
}

/**
* Sets the subject of the message. The subject is a short description of
* message contents.
*
* @param subject the subject of the message.
* @return a reference to this builder.
*/
public MessageBuilder setSubject(String subject) {
return addSubject(null, subject);
}

/**
* Adds a subject with a corresponding language.
*
* @param language the language of the subject being added.
* @param subject the subject being added to the message.
* @return a reference to this builder.
* @throws NullPointerException if the subject is null.
*/
public MessageBuilder addSubject(String language, String subject) {
language = StringUtils.requireNullOrNotEmpty(language, "language must be null or not empty");

for (Subject currentSubject : getExtensions(Subject.class)) {
if (StringUtils.nullSafeCharSequenceEquals(language, currentSubject.getLanguage())) {
throw new IllegalArgumentException("Subject with the language " + language + " already exists");
}
}

Subject messageSubject = new Subject(language, subject);
addExtension(messageSubject);

return this;
}

/**
* Sets the body of the message.
*
* @param body the body of the message.
* @return a reference to this builder.
* @see #setBody(String)
*/
public MessageBuilder setBody(CharSequence body) {
return setBody(body.toString());
}

/**
* Sets the body of the message. The body is the main message contents.
*
* @param body the body of the message.
* @return a reference to this builder.
*/
public MessageBuilder setBody(String body) {
return addBody(null, body);
}

/**
* Adds a body with a corresponding language.
*
* @param language the language of the body being added.
* @param body the body being added to the message.
* @return a reference to this builder.
*/
public MessageBuilder addBody(String language, String body) {
language = StringUtils.requireNullOrNotEmpty(language, "language must be null or not empty");

for (Body currentBody : getExtensions(Body.class)) {
if (StringUtils.nullSafeCharSequenceEquals(language, currentBody.getLanguage())) {
throw new IllegalArgumentException("Bodyt with the language " + language + " already exists");
}
}

Body messageBody = new Body(language, body);
addExtension(messageBody);

return this;
}

@Override
public MessageBuilder getThis() {
return this;
}

public Message build() {
return new Message(this);
}

@Override
public Message.Type getType() {
return type;
}
}

+ 29
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/MessageView.java View File

@@ -0,0 +1,29 @@
/**
*
* 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.packet;

public interface MessageView extends StanzaView {

/**
* Returns the type of the message. If no type has been set this method will return {@link
* org.jivesoftware.smack.packet.Message.Type#normal}.
*
* @return the type of the message.
*/
Message.Type getType();

}

+ 46
- 26
smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java View File

@@ -19,7 +19,8 @@ package org.jivesoftware.smack.packet;

import java.util.Locale;

import org.jivesoftware.smack.packet.id.StanzaIdUtil;
import javax.net.SocketFactory;

import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.TypedCloneable;
@@ -60,7 +61,7 @@ import org.jxmpp.jid.Jid;
*
* @author Matt Tucker
*/
public final class Presence extends Stanza implements TypedCloneable<Presence> {
public final class Presence extends Stanza implements PresenceView, TypedCloneable<Presence> {

public static final String ELEMENT = "presence";

@@ -81,7 +82,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* Creates a new presence update. Status, priority, and mode are left un-set.
*
* @param type the type.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Type type) {
// Ensure that the stanza ID is set by calling super().
super();
@@ -94,7 +98,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param to the recipient.
* @param type the type.
* @since 4.2
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Jid to, Type type) {
this(type);
setTo(to);
@@ -107,7 +114,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param status a text message describing the presence update.
* @param priority the priority of this presence update.
* @param mode the mode type for this presence update.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Presence(Type type, String status, int priority, Mode mode) {
// Ensure that the stanza ID is set by calling super().
super();
@@ -117,6 +127,14 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
setMode(mode);
}

Presence(PresenceBuilder presenceBuilder) {
super(presenceBuilder);
type = presenceBuilder.type;
status = presenceBuilder.status;
priority = presenceBuilder.priority;
mode = presenceBuilder.mode;
}

/**
* Copy constructor.
* <p>
@@ -163,11 +181,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd);
}

/**
* Returns the type of this presence packet.
*
* @return the type of the presence packet.
*/
@Override
public Type getType() {
return type;
}
@@ -176,18 +190,15 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* Sets the type of the presence packet.
*
* @param type the type of the presence packet.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setType(Type type) {
this.type = Objects.requireNonNull(type, "Type cannot be null");
}

/**
* Returns the status message of the presence update, or <code>null</code> if there
* is not a status. The status is free-form text describing a user's presence
* (i.e., "gone to lunch").
*
* @return the status message.
*/
@Override
public String getStatus() {
return status;
}
@@ -197,18 +208,21 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* describing a user's presence (i.e., "gone to lunch").
*
* @param status the status message.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setStatus(String status) {
this.status = status;
}

/**
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
@Override
public int getPriority() {
return getPriorityByte();
}

@Override
public byte getPriorityByte() {
if (priority == null) {
return 0;
}
@@ -221,7 +235,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param priority the priority of the presence.
* @throws IllegalArgumentException if the priority is outside the valid range.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setPriority(int priority) {
if (priority < -128 || priority > 127) {
throw new IllegalArgumentException("Priority value " + priority +
@@ -234,11 +251,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
this.priority = priority;
}

/**
* Returns the mode of the presence update.
*
* @return the mode.
*/
@Override
public Mode getMode() {
if (mode == null) {
return Mode.available;
@@ -251,7 +264,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* to be the same thing as {@link Presence.Mode#available}.
*
* @param mode the mode.
* @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setMode(Mode mode) {
this.mode = mode;
}
@@ -261,6 +277,10 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
return ELEMENT;
}

public PresenceBuilder asBuilder() {
return StanzaBuilder.buildPresenceFrom(this, getStanzaId());
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -326,7 +346,7 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
*/
public Presence cloneWithNewId() {
Presence clone = clone();
clone.setStanzaId(StanzaIdUtil.newStanzaId());
clone.setNewStanzaId();
return clone;
}



+ 140
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/PresenceBuilder.java View File

@@ -0,0 +1,140 @@
/**
*
* 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.packet;

import org.jivesoftware.smack.packet.Presence.Mode;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ToStringUtil;

public final class PresenceBuilder extends StanzaBuilder<PresenceBuilder> implements PresenceView {
static final PresenceBuilder EMPTY = new PresenceBuilder(() -> {
return null;
});

Presence.Type type = Presence.Type.available;

String status;

Byte priority;

Presence.Mode mode;

PresenceBuilder(Presence presence, String stanzaId) {
super(presence, stanzaId);
copyFromPresence(presence);
}

PresenceBuilder(Presence presence, StanzaIdSource stanzaIdSource) {
super(presence, stanzaIdSource);
copyFromPresence(presence);
}

PresenceBuilder(StanzaIdSource stanzaIdSource) {
super(stanzaIdSource);
}

PresenceBuilder(String stanzaId) {
super(stanzaId);
}

private void copyFromPresence(Presence presence) {
type = presence.getType();
status = presence.getStatus();
priority = presence.getPriorityByte();
mode = presence.getMode();
}

@Override
protected void addStanzaSpecificAttributes(ToStringUtil.Builder builder) {
builder.addValue("type", type)
.addValue("mode", mode)
.addValue("priority", priority)
.addValue("status", status)
;
}

public PresenceBuilder ofType(Presence.Type type) {
this.type = Objects.requireNonNull(type, "Type cannot be null");
return getThis();
}

public PresenceBuilder setStatus(String status) {
this.status = status;
return getThis();
}

public PresenceBuilder setPriority(int priority) {
if (priority < -128 || priority > 127) {
throw new IllegalArgumentException("Priority value " + priority +
" is not valid. Valid range is -128 through 127.");
}
Byte priorityByte = (byte) priority;
return setPriority(priorityByte);
}

public PresenceBuilder setPriority(Byte priority) {
this.priority = priority;
return getThis();
}

public PresenceBuilder setMode(Presence.Mode mode) {
this.mode = mode;
return getThis();
}

@Override
public PresenceBuilder getThis() {
return this;
}

public Presence build() {
return new Presence(this);
}

@Override
public Presence.Type getType() {
return type;
}

@Override
public String getStatus() {
return status;
}

@Override
public int getPriority() {
return getPriorityByte();
}

@Override
public byte getPriorityByte() {
if (priority == null) {
return 0;
}
return priority;
}

@Override
public Presence.Mode getMode() {
if (mode == null) {
return Mode.available;
}
return mode;
}

}

+ 59
- 0
smack-core/src/main/java/org/jivesoftware/smack/packet/PresenceView.java View File

@@ -0,0 +1,59 @@
/**
*
* 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.packet;

public interface PresenceView extends StanzaView {

/**
* Returns the type of this presence stanza.
*
* @return the type of the presence stanza.
*/
Presence.Type getType();

/**
* Returns the status message of the presence update, or <code>null</code> if there
* is not a status. The status is free-form text describing a user's presence
* (i.e., "gone to lunch").
*
* @return the status message.
*/
String getStatus();

/**
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
int getPriority();

/**
* Returns the priority of the presence.
*
* @return the priority.
* @see <a href="https://tools.ietf.org/html/rfc6121#section-4.7.2.3">RFC 6121 § 4.7.2.3. Priority Element</a>
*/
byte getPriorityByte();

/**
* Returns the mode of the presence update.
*
* @return the mode.
*/
Presence.Mode getMode();
}

+ 5
- 1
smack-core/src/main/java/org/jivesoftware/smack/packet/SimpleIQ.java View File

@@ -1,6 +1,6 @@
/**
*
* Copyright © 2014 Florian Schmaus
* Copyright © 2014-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.
@@ -29,6 +29,10 @@ public abstract class SimpleIQ extends IQ {
super(childElementName, childElementNamespace);
}

protected SimpleIQ(IqBuilder iqBuilder, String childElementName, String childElementNamespace) {
super(iqBuilder, childElementName, childElementNamespace);
}

@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.setEmptyElement();


+ 138
- 75
smack-core/src/main/java/org/jivesoftware/smack/packet/Stanza.java View File

@@ -20,15 +20,19 @@ package org.jivesoftware.smack.packet;
import static org.jivesoftware.smack.util.StringUtils.requireNotNullNorEmpty;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import javax.xml.namespace.QName;

import org.jivesoftware.smack.packet.id.StanzaIdUtil;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smack.util.XmppElementUtil;

import org.jxmpp.jid.Jid;

@@ -43,12 +47,16 @@ import org.jxmpp.jid.Jid;
* XMPP Stanzas are {@link Message}, {@link IQ} and {@link Presence}. Which therefore subclass this
* class. <b>If you think you need to subclass this class, then you are doing something wrong.</b>
* </p>
* <p>
* Use {@link StanzaBuilder} to construct a stanza instance. All instance mutating methods of this
* class are deprecated, although not all of them are currently marked as such, and must not be used.
* </p>
*
* @author Matt Tucker
* @author Florian Schmaus
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#stanzas">RFC 6120 § 8. XML Stanzas</a>
*/
public abstract class Stanza implements TopLevelStreamElement {
public abstract class Stanza implements StanzaView, TopLevelStreamElement {

public static final String TEXT = "text";
public static final String ITEM = "item";
@@ -56,12 +64,14 @@ public abstract class Stanza implements TopLevelStreamElement {
protected static final String DEFAULT_LANGUAGE =
java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US);

private final MultiMap<QName, ExtensionElement> extensionElements = new MultiMap<>();
private final MultiMap<QName, ExtensionElement> extensionElements;

// Assume that all stanzas Smack handles are in the client namespace, since Smack is an XMPP client library. We can
// change this behavior later if it is required.
private final String namespace = StreamOpen.CLIENT_NAMESPACE;

private final StanzaIdSource usedStanzaIdSource;

private String id = null;
private Jid to;
private Jid from;
@@ -80,30 +90,46 @@ public abstract class Stanza implements TopLevelStreamElement {
protected String language;

protected Stanza() {
this(StanzaIdUtil.newStanzaId());
}
extensionElements = new MultiMap<>();
usedStanzaIdSource = null;
id = StandardStanzaIdSource.DEFAULT.getNewStanzaId();
}

protected Stanza(StanzaBuilder<?> stanzaBuilder) {
if (stanzaBuilder.stanzaIdSource != null) {
id = stanzaBuilder.stanzaIdSource.getNewStanzaId();
// Note that some stanza ID sources, e.g. StanzaBuilder.PresenceBuilder.EMPTY return null here. Hence we
// only check that the returned string is not empty.
assert StringUtils.isNullOrNotEmpty(id);
usedStanzaIdSource = stanzaBuilder.stanzaIdSource;
} else {
// N.B. It is ok if stanzaId here is null.
id = stanzaBuilder.stanzaId;
usedStanzaIdSource = null;
}

to = stanzaBuilder.to;
from = stanzaBuilder.from;

protected Stanza(String stanzaId) {
setStanzaId(stanzaId);
error = stanzaBuilder.stanzaError;

language = stanzaBuilder.language;

extensionElements = stanzaBuilder.extensionElements.clone();
}

protected Stanza(Stanza p) {
usedStanzaIdSource = p.usedStanzaIdSource;

id = p.getStanzaId();
to = p.getTo();
from = p.getFrom();
error = p.error;

// Copy extensions
for (ExtensionElement pe : p.getExtensions()) {
addExtension(pe);
}
extensionElements = p.extensionElements.clone();
}

/**
* Returns the unique ID of the stanza. The returned value could be <code>null</code>.
*
* @return the packet's unique ID or <code>null</code> if the id is not available.
*/
@Override
public String getStanzaId() {
return id;
}
@@ -138,56 +164,50 @@ public abstract class Stanza implements TopLevelStreamElement {
*
* @return the stanza id.
* @since 4.2
* @deprecated use {@link #setNewStanzaId()} instead.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public String setStanzaId() {
return ensureStanzaIdSet();
if (!hasStanzaIdSet()) {
setNewStanzaId();
}
return getStanzaId();
}

/**
* Set a new stanza ID even if there is already one set.
* Throws an {@link IllegalArgumentException} if this stanza has no stanza ID set.
*
* @return the stanza id.
* @since 4.4
* @throws IllegalArgumentException if this stanza has no stanza ID set.
* @since 4.4.
*/
public String setNewStanzaId() {
return ensureStanzaIdSet(true);
}
public final void throwIfNoStanzaId() {
if (hasStanzaIdSet()) {
return;
}

/**
* Ensure a stanza id is set.
*
* @return the stanza id.
* @since 4.4
*/
public String ensureStanzaIdSet() {
return ensureStanzaIdSet(false);
throw new IllegalArgumentException("The stanza has no RFC stanza ID set, although one is required");
}

/**
* Ensure that a stanza ID is set.
*
* @param forceNew force a new ID even if there is already one set.
* @return the stanza ID.
* @since 4.4
*/
private String ensureStanzaIdSet(boolean forceNew) {
if (forceNew || !hasStanzaIdSet()) {
setStanzaId(StanzaIdUtil.newStanzaId());
// TODO: Remove this method once StanzaBuilder is ready.
protected String setNewStanzaId() {
if (usedStanzaIdSource != null) {
id = usedStanzaIdSource.getNewStanzaId();
}
else {
id = StandardStanzaIdSource.DEFAULT.getNewStanzaId();
}

return getStanzaId();
}

/**
* Returns who the stanza is being sent "to", or <code>null</code> if
* the value is not set. The XMPP protocol often makes the "to"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the stanza is being sent to, or <code>null</code> if the
* value has not been set.
*/
@Override
public Jid getTo() {
return to;
}
@@ -198,18 +218,12 @@ public abstract class Stanza implements TopLevelStreamElement {
*
* @param to who the packet is being sent to.
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setTo(Jid to) {
this.to = to;
}

/**
* Returns who the stanza is being sent "from" or <code>null</code> if
* the value is not set. The XMPP protocol often makes the "from"
* attribute optional, so it does not always need to be set.<p>
*
* @return who the stanza is being sent from, or <code>null</code> if the
* value has not been set.
*/
@Override
public Jid getFrom() {
return from;
}
@@ -221,16 +235,12 @@ public abstract class Stanza implements TopLevelStreamElement {
*
* @param from who the packet is being sent to.
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setFrom(Jid from) {
this.from = from;
}

/**
* Returns the error associated with this packet, or <code>null</code> if there are
* no errors.
*
* @return the error sub-packet or <code>null</code> if there isn't an error.
*/
@Override
public StanzaError getError() {
return error;
}
@@ -238,14 +248,22 @@ public abstract class Stanza implements TopLevelStreamElement {
/**
* Sets the error for this stanza.
*
* @param xmppErrorBuilder the error to associate with this stanza.
* @param stanzaError the error that this stanza carries and hence signals.
*/
public void setError(StanzaError.Builder xmppErrorBuilder) {
if (xmppErrorBuilder == null) {
return;
}
xmppErrorBuilder.setStanza(this);
error = xmppErrorBuilder.build();
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void setError(StanzaError stanzaError) {
error = stanzaError;
}

/**
* Deprecated.
* @param stanzaError the stanza error.
* @deprecated use {@link StanzaBuilder} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setError(StanzaError.Builder stanzaError) {
setError(stanzaError.build());
}

@Override
@@ -257,16 +275,15 @@ public abstract class Stanza implements TopLevelStreamElement {
* Sets the xml:lang of this Stanza.
*
* @param language the xml:lang of this Stanza.
* @deprecated use {@link StanzaBuilder#setLanguage(String)} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public void setLanguage(String language) {
this.language = language;
}

/**
* Returns a list of all extension elements of this stanza.
*
* @return a list of all extension elements of this stanza.
*/
@Override
public List<ExtensionElement> getExtensions() {
synchronized (extensionElements) {
// No need to create a new list, values() will already create a new one for us
@@ -274,6 +291,16 @@ public abstract class Stanza implements TopLevelStreamElement {
}
}

public final MultiMap<QName, ExtensionElement> getExtensionsMap() {
return cloneExtensionsMap();
}

final MultiMap<QName, ExtensionElement> cloneExtensionsMap() {
synchronized (extensionElements) {
return extensionElements.clone();
}
}

/**
* Return a list of all extensions with the given element name <em>and</em> namespace.
* <p>
@@ -289,7 +316,23 @@ public abstract class Stanza implements TopLevelStreamElement {
requireNotNullNorEmpty(elementName, "elementName must not be null nor empty");
requireNotNullNorEmpty(namespace, "namespace must not be null nor empty");
QName key = new QName(namespace, elementName);
return extensionElements.getAll(key);
return getExtensions(key);
}

@Override
public List<ExtensionElement> getExtensions(QName qname) {
List<ExtensionElement> res;
synchronized (extensionElements) {
res = extensionElements.getAll(qname);
}
return Collections.unmodifiableList(res);
}

@Override
public <E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass) {
synchronized (extensionElements) {
return XmppElementUtil.getElementsFrom(extensionElements, extensionElementClass);
}
}

/**
@@ -322,21 +365,31 @@ public abstract class Stanza implements TopLevelStreamElement {
return null;
}
QName key = new QName(namespace, elementName);
ExtensionElement packetExtension;
synchronized (extensionElements) {
packetExtension = extensionElements.getFirst(key);
}
ExtensionElement packetExtension = getExtension(key);
if (packetExtension == null) {
return null;
}
return (PE) packetExtension;
}

@SuppressWarnings("unchecked")
@Override
public final <E extends ExtensionElement> E getExtension(QName qname) {
synchronized (extensionElements) {
return (E) extensionElements.getFirst(qname);
}
}

/**
* Adds a stanza extension to the packet. Does nothing if extension is null.
* <p>
* Please note that although this method is not yet marked as deprecated, it is recommended to use
* {@link StanzaBuilder#addExtension(ExtensionElement)} instead.
* </p>
*
* @param extension a stanza extension.
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void addExtension(ExtensionElement extension) {
if (extension == null) return;
QName key = extension.getQName();
@@ -348,11 +401,16 @@ public abstract class Stanza implements TopLevelStreamElement {
/**
* Add the given extension and override eventually existing extensions with the same name and
* namespace.
* <p>
* Please note that although this method is not yet marked as deprecated, it is recommended to use
* {@link StanzaBuilder#overrideExtension(ExtensionElement)} instead.
* </p>
*
* @param extension the extension element to add.
* @return one of the removed extensions or <code>null</code> if there are none.
* @since 4.1.2
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public ExtensionElement overrideExtension(ExtensionElement extension) {
if (extension == null) return null;
synchronized (extensionElements) {
@@ -370,6 +428,7 @@ public abstract class Stanza implements TopLevelStreamElement {
*
* @param extensions a collection of stanza extensions
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public void addExtensions(Collection<ExtensionElement> extensions) {
if (extensions == null) return;
for (ExtensionElement packetExtension : extensions) {
@@ -421,6 +480,7 @@ public abstract class Stanza implements TopLevelStreamElement {
* @param namespace TODO javadoc me please
* @return the removed stanza extension or null.
*/
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
public ExtensionElement removeExtension(String elementName, String namespace) {
QName key = new QName(namespace, elementName);
synchronized (extensionElements) {