From 469d4fb0dc0a0118335bef2df3d48b69fdc58b7d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 2 May 2021 17:14:15 +0200 Subject: [PATCH] [time] Use IqBuilder pattern and improve API --- .../smackx/time/EntityTimeManager.java | 28 ++-- .../jivesoftware/smackx/time/packet/Time.java | 121 +++++++++--------- .../smackx/time/packet/TimeBuilder.java | 94 ++++++++++++++ .../smackx/time/packet/TimeView.java | 37 ++++++ .../smackx/time/provider/TimeProvider.java | 51 +++++++- .../smackx/time/packet/TimeTest.java | 18 ++- 6 files changed, 265 insertions(+), 84 deletions(-) create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeBuilder.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeView.java diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/EntityTimeManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/EntityTimeManager.java index 11d195b67..4262d8213 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/EntityTimeManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/EntityTimeManager.java @@ -21,6 +21,8 @@ import java.util.WeakHashMap; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.FeatureNotSupportedException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; @@ -75,12 +77,13 @@ public final class EntityTimeManager extends Manager { Mode.async) { @Override public IQ handleIQRequest(IQ iqRequest) { - if (enabled) { - return Time.createResponse(iqRequest); - } - else { + if (!enabled) { return IQ.createErrorResponse(iqRequest, Condition.not_acceptable); } + + Time timeRequest = (Time) iqRequest; + Time timeResponse = Time.builder(timeRequest).build(); + return timeResponse; } }); } @@ -105,13 +108,16 @@ public final class EntityTimeManager extends Manager { return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, Time.NAMESPACE); } - public Time getTime(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - if (!isTimeSupported(jid)) - return null; + public Time getTime(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, + InterruptedException, FeatureNotSupportedException { + if (!isTimeSupported(jid)) { + throw new SmackException.FeatureNotSupportedException(Time.NAMESPACE); + } - Time request = new Time(); - // TODO Add Time(Jid) constructor and use this constructor instead - request.setTo(jid); - return connection().createStanzaCollectorAndSend(request).nextResultOrThrow(); + XMPPConnection connection = connection(); + Time request = Time.builder(connection) + .to(jid) + .build(); + return connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java index 98189c312..bb7632210 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/Time.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2014 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2014-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,10 @@ import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IqData; +import org.jivesoftware.smack.util.StringUtils; import org.jxmpp.util.XmppDateTime; @@ -33,31 +36,41 @@ import org.jxmpp.util.XmppDateTime; * @see XEP-202 * @author Florian Schmaus */ -public class Time extends IQ { +public class Time extends IQ implements TimeView { + public static final String NAMESPACE = "urn:xmpp:time"; public static final String ELEMENT = "time"; private static final Logger LOGGER = Logger.getLogger(Time.class.getName()); - private String utc; - private String tzo; + private final String utc; + private final String tzo; - public Time() { - super(ELEMENT, NAMESPACE); - setType(Type.get); - } + public Time(TimeBuilder timeBuilder) { + super(timeBuilder, ELEMENT, NAMESPACE); + utc = timeBuilder.getUtc(); + tzo = timeBuilder.getTzo(); - /** - * Creates a new Time instance using the specified calendar instance as - * the time value to send. - * - * @param cal the time value. - */ - public Time(Calendar cal) { - super(ELEMENT, NAMESPACE); - tzo = XmppDateTime.asString(cal.getTimeZone()); - // Convert local time to the UTC time. - utc = XmppDateTime.formatXEP0082Date(cal.getTime()); + Type type = getType(); + switch (type) { + case get: + if (utc != null) { + throw new IllegalArgumentException("Time requests must not have utc set"); + } + if (tzo != null) { + throw new IllegalArgumentException("Time requests must not have tzo set"); + } + break; + case result: + StringUtils.requireNotNullNorEmpty(utc, "Must have set a utc value"); + StringUtils.requireNotNullNorEmpty(tzo, "Must have set a tzo value"); + break; + case error: + // Nothing to check. + break; + case set: + throw new IllegalArgumentException("Invalid IQ type"); + } } /** @@ -79,67 +92,47 @@ public class Time extends IQ { return date; } - /** - * Sets the time using the local time. - * - * @param time the current local time. - */ - public void setTime(Date time) { - } - - /** - * Returns the time as a UTC formatted String using the format CCYY-MM-DDThh:mm:ssZ. - * - * @return the time as a UTC formatted String. - */ + @Override public String getUtc() { return utc; } - /** - * Sets the time using UTC formatted String in the format CCYY-MM-DDThh:mm:ssZ. - * - * @param utc the time using a formatted String. - */ - public void setUtc(String utc) { - this.utc = utc; - } - - /** - * Returns the time zone. - * - * @return the time zone. - */ + @Override public String getTzo() { return tzo; } - /** - * Sets the time zone offset. - * - * @param tzo the time zone offset. - */ - public void setTzo(String tzo) { - this.tzo = tzo; - } - - public static Time createResponse(IQ request) { - Time time = new Time(Calendar.getInstance()); - time.setType(Type.result); - time.setTo(request.getFrom()); - return time; - } - @Override protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) { if (utc != null) { buf.rightAngleBracket(); - buf.append("").append(utc).append(""); - buf.append("").append(tzo).append(""); + buf.element("utc", utc); + buf.element("tzo", tzo); } else { buf.setEmptyElement(); } return buf; } + + public static TimeBuilder builder(XMPPConnection connection) { + return new TimeBuilder(connection); + } + + public static TimeBuilder builder(IqData iqData) { + return new TimeBuilder(iqData); + } + + public static TimeBuilder builder(String stanzaId) { + return new TimeBuilder(stanzaId); + } + + public static TimeBuilder builder(Time timeRequest, Calendar calendar) { + IqData iqData = IqData.createResponseData(timeRequest); + return builder(iqData).setTime(calendar); + } + + public static TimeBuilder builder(Time timeRequest) { + return builder(timeRequest, Calendar.getInstance()); + } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeBuilder.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeBuilder.java new file mode 100644 index 000000000..6b3332085 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeBuilder.java @@ -0,0 +1,94 @@ +/** + * + * Copyright 2021 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.smackx.time.packet; + +import java.text.ParseException; +import java.util.Calendar; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.IqBuilder; +import org.jivesoftware.smack.packet.IqData; +import org.jivesoftware.smack.util.StringUtils; + +import org.jxmpp.util.XmppDateTime; + +// TODO: Use java.time.ZonedDataTime once Smack's minimum Android SDK API level is 26 or higher. +public class TimeBuilder extends IqBuilder implements TimeView { + + private String utc; + private String tzo; + + TimeBuilder(IqData iqCommon) { + super(iqCommon); + } + + TimeBuilder(XMPPConnection connection) { + super(connection); + } + + TimeBuilder(String stanzaId) { + super(stanzaId); + } + + /** + * Sets the time using UTC formatted String, in the format CCYY-MM-DDThh:mm:ssZ, and the provided timezone + * definition in the format (+|-)hh:mm. + * + * @param utc the time using a formatted String. + * @param tzo the time zone definition. + * @return a reference to this builder. + * @throws ParseException if the provided string is not parsable (e.g. because it does not follow the expected + * format). + */ + public TimeBuilder setUtcAndTzo(String utc, String tzo) throws ParseException { + this.utc = StringUtils.requireNotNullNorEmpty(utc, "Must provide utc argument"); + // Sanity check the provided string. + XmppDateTime.parseDate(utc); + + this.tzo = StringUtils.requireNotNullNorEmpty(tzo, "Must provide tzo argument"); + + return getThis(); + } + + public TimeBuilder setTime(Calendar calendar) { + // Convert local time to the UTC time. + utc = XmppDateTime.formatXEP0082Date(calendar.getTime()); + tzo = XmppDateTime.asString(calendar.getTimeZone()); + + return getThis(); + } + + @Override + public String getUtc() { + return utc; + } + + @Override + public String getTzo() { + return tzo; + } + + @Override + public Time build() { + return new Time(this); + } + + @Override + public TimeBuilder getThis() { + return this; + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeView.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeView.java new file mode 100644 index 000000000..7ce47adce --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/packet/TimeView.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2021 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.smackx.time.packet; + +import org.jivesoftware.smack.packet.IqView; + +public interface TimeView extends IqView { + + /** + * Returns the time as a UTC formatted String using the format CCYY-MM-DDThh:mm:ssZ. + * + * @return the time as a UTC formatted String. + */ + String getUtc(); + + /** + * Returns the time zone. + * + * @return the time zone. + */ + String getTzo(); + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/provider/TimeProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/provider/TimeProvider.java index 09ff3e4d2..d6c68e33c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/time/provider/TimeProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/time/provider/TimeProvider.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014 Florian Schmaus + * Copyright © 2014-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,55 @@ */ package org.jivesoftware.smackx.time.provider; -import org.jivesoftware.smack.provider.IntrospectionProvider.IQIntrospectionProvider; +import java.io.IOException; +import java.text.ParseException; + +import org.jivesoftware.smack.packet.IqData; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.provider.IqProvider; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.time.packet.Time; +import org.jivesoftware.smackx.time.packet.TimeBuilder; -public class TimeProvider extends IQIntrospectionProvider