diff --git a/source/org/jivesoftware/smack/util/StringUtils.java b/source/org/jivesoftware/smack/util/StringUtils.java
index 628f4b57e..2c0bc3eec 100644
--- a/source/org/jivesoftware/smack/util/StringUtils.java
+++ b/source/org/jivesoftware/smack/util/StringUtils.java
@@ -23,19 +23,63 @@ package org.jivesoftware.smack.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.Random;
+import java.util.TimeZone;
/**
* A collection of utility methods for String objects.
*/
public class StringUtils {
+ /**
+ * Date format as defined in XEP-0082 - XMPP Date and Time Profiles. The time zone is set to
+ * UTC.
+ *
+ * Date formats are not synchronized. Since multiple threads access the format concurrently, it
+ * must be synchronized externally or you can use the convenience methods
+ * {@link #parseXEP0082Date(String)} and {@link #formatXEP0082Date(Date)}.
+ */
+ public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ static {
+ XEP_0082_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
private static final char[] QUOTE_ENCODE = """.toCharArray();
private static final char[] APOS_ENCODE = "'".toCharArray();
private static final char[] AMP_ENCODE = "&".toCharArray();
private static final char[] LT_ENCODE = "<".toCharArray();
private static final char[] GT_ENCODE = ">".toCharArray();
+ /**
+ * Parses the given date string in the XEP-0082 - XMPP Date and Time Profiles format.
+ *
+ * @param dateString the date string to parse
+ * @return the parsed Date
+ * @throws ParseException if the specified string cannot be parsed
+ */
+ public static Date parseXEP0082Date(String dateString) throws ParseException {
+ synchronized (XEP_0082_UTC_FORMAT) {
+ return XEP_0082_UTC_FORMAT.parse(dateString);
+ }
+ }
+
+ /**
+ * Formats a Date into a XEP-0082 - XMPP Date and Time Profiles string.
+ *
+ * @param date the time value to be formatted into a time string
+ * @return the formatted time string in XEP-0082 format
+ */
+ public static String formatXEP0082Date(Date date) {
+ synchronized (XEP_0082_UTC_FORMAT) {
+ return XEP_0082_UTC_FORMAT.format(date);
+ }
+ }
+
/**
* Returns the name portion of a XMPP address. For example, for the
* address "matt@jivesoftware.com/Smack", "matt" would be returned. If no
diff --git a/source/org/jivesoftware/smackx/packet/DelayInfo.java b/source/org/jivesoftware/smackx/packet/DelayInfo.java
index 6026db03e..f5ba78fa1 100644
--- a/source/org/jivesoftware/smackx/packet/DelayInfo.java
+++ b/source/org/jivesoftware/smackx/packet/DelayInfo.java
@@ -15,12 +15,12 @@ package org.jivesoftware.smackx.packet;
import java.util.Date;
-import org.jivesoftware.smackx.packet.DelayInformation;
+import org.jivesoftware.smack.util.StringUtils;
/**
* A decorator for the {@link DelayInformation} class to transparently support
* both the new Delay Delivery specification XEP-0203 and
- * the old one XEP-0091.
+ * the old one XEP-0091.
*
* Existing code can be backward compatible.
*
@@ -28,8 +28,13 @@ import org.jivesoftware.smackx.packet.DelayInformation;
*/
public class DelayInfo extends DelayInformation
{
+
DelayInformation wrappedInfo;
+ /**
+ * Creates a new instance with given delay information.
+ * @param delay the delay information
+ */
public DelayInfo(DelayInformation delay)
{
super(delay.getStamp());
@@ -84,9 +89,7 @@ public class DelayInfo extends DelayInformation
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
"\"");
buf.append(" stamp=\"");
- synchronized (NEW_UTC_FORMAT) {
- buf.append(NEW_UTC_FORMAT.format(getStamp()));
- }
+ buf.append(StringUtils.formatXEP0082Date(getStamp()));
buf.append("\"");
if (getFrom() != null && getFrom().length() > 0) {
buf.append(" from=\"").append(getFrom()).append("\"");
diff --git a/source/org/jivesoftware/smackx/packet/DelayInformation.java b/source/org/jivesoftware/smackx/packet/DelayInformation.java
index b03f5cd2c..b9ab48518 100644
--- a/source/org/jivesoftware/smackx/packet/DelayInformation.java
+++ b/source/org/jivesoftware/smackx/packet/DelayInformation.java
@@ -20,36 +20,38 @@
package org.jivesoftware.smackx.packet;
-import org.jivesoftware.smack.packet.PacketExtension;
-
+import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
+import org.jivesoftware.smack.packet.PacketExtension;
+
/**
* Represents timestamp information about data stored for later delivery. A DelayInformation will
* always includes the timestamp when the packet was originally sent and may include more
* information such as the JID of the entity that originally sent the packet as well as the reason
- * for the dealy.
+ * for the delay.
*
- * For more information see JEP-91.
+ * For more information see XEP-0091
+ * and XEP-0203.
*
* @author Gaston Dombiak
*/
public class DelayInformation implements PacketExtension {
- public static SimpleDateFormat UTC_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
/**
- * New date format based on JEP-82 that some clients may use when sending delayed dates.
- * JEP-91 is using a SHOULD other servers or clients may be using this format instead of the
- * old UTC format.
+ * Date format according to the obsolete XEP-0091 specification.
+ * XEP-0091 recommends to use this old format for date-time instead of
+ * the one specified in XEP-0082.
+ *
+ * Date formats are not synchronized. Since multiple threads access the format concurrently,
+ * it must be synchronized externally.
*/
- public static SimpleDateFormat NEW_UTC_FORMAT =
- new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-
+ public static final DateFormat XEP_0091_UTC_FORMAT = new SimpleDateFormat(
+ "yyyyMMdd'T'HH:mm:ss");
static {
- UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+0"));
- NEW_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ XEP_0091_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
private Date stamp;
@@ -58,6 +60,7 @@ public class DelayInformation implements PacketExtension {
/**
* Creates a new instance with the specified timestamp.
+ * @param stamp the timestamp
*/
public DelayInformation(Date stamp) {
super();
@@ -86,10 +89,10 @@ public class DelayInformation implements PacketExtension {
}
/**
- * Returns the timstamp when the packet was originally sent. The returned Date is
+ * Returns the timestamp when the packet was originally sent. The returned Date is
* be understood as UTC.
*
- * @return the timstamp when the packet was originally sent.
+ * @return the timestamp when the packet was originally sent.
*/
public Date getStamp() {
return stamp;
@@ -128,8 +131,8 @@ public class DelayInformation implements PacketExtension {
buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
"\"");
buf.append(" stamp=\"");
- synchronized (UTC_FORMAT) {
- buf.append(UTC_FORMAT.format(stamp));
+ synchronized (XEP_0091_UTC_FORMAT) {
+ buf.append(XEP_0091_UTC_FORMAT.format(stamp));
}
buf.append("\"");
if (from != null && from.length() > 0) {
diff --git a/source/org/jivesoftware/smackx/packet/StreamInitiation.java b/source/org/jivesoftware/smackx/packet/StreamInitiation.java
index 876f3718f..10c1452c6 100644
--- a/source/org/jivesoftware/smackx/packet/StreamInitiation.java
+++ b/source/org/jivesoftware/smackx/packet/StreamInitiation.java
@@ -19,12 +19,12 @@
*/
package org.jivesoftware.smackx.packet;
+import java.util.Date;
+
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.util.StringUtils;
-import java.util.Date;
-
/**
* The process by which two entities initiate a stream.
*
@@ -347,7 +347,7 @@ public class StreamInitiation extends IQ {
}
if (getDate() != null) {
- buffer.append("date=\"").append(DelayInformation.UTC_FORMAT.format(date)).append("\" ");
+ buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
}
if (getHash() != null) {
diff --git a/source/org/jivesoftware/smackx/provider/DelayInfoProvider.java b/source/org/jivesoftware/smackx/provider/DelayInfoProvider.java
index a5bc06f39..9d1dcd8f4 100644
--- a/source/org/jivesoftware/smackx/provider/DelayInfoProvider.java
+++ b/source/org/jivesoftware/smackx/provider/DelayInfoProvider.java
@@ -21,8 +21,9 @@ import org.xmlpull.v1.XmlPullParser;
/**
* This provider simply creates a {@link DelayInfo} decorator for the {@link DelayInformation} that
- * is returned by the superclass. This allows the new code using Delay Information XEP-0203 to be backward compatible
- * with XEP-0091.
+ * is returned by the superclass. This allows the new code using
+ * Delay Information XEP-0203 to be
+ * backward compatible with XEP-0091.
*
*
This provider must be registered in the smack.properties file for the element
* delay with namespace urn:xmpp:delay
diff --git a/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java b/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java
index 995b9eff8..d20c22d53 100644
--- a/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java
+++ b/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java
@@ -20,56 +20,185 @@
package org.jivesoftware.smackx.provider;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.packet.DelayInformation;
import org.xmlpull.v1.XmlPullParser;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
-
/**
* The DelayInformationProvider parses DelayInformation packets.
- *
+ *
* @author Gaston Dombiak
+ * @author Henning Staib
*/
public class DelayInformationProvider implements PacketExtensionProvider {
+ /*
+ * Date format used to parse dates in the XEP-0091 format but missing leading
+ * zeros for month and day.
+ */
+ private static final SimpleDateFormat XEP_0091_UTC_FALLBACK_FORMAT = new SimpleDateFormat(
+ "yyyyMd'T'HH:mm:ss");
+ static {
+ XEP_0091_UTC_FALLBACK_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ /*
+ * Date format used to parse dates in the XEP-0082 format but missing milliseconds.
+ */
+ private static final SimpleDateFormat XEP_0082_UTC_FORMAT_WITHOUT_MILLIS = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss'Z'");
+ static {
+ XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ /*
+ * Maps a regular expression for a date format to the date format parser.
+ */
+ private static Map formats = new HashMap();
+ static {
+ formats.put("^\\d+T\\d+:\\d+:\\d+$", DelayInformation.XEP_0091_UTC_FORMAT);
+ formats.put("^\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d+Z$", StringUtils.XEP_0082_UTC_FORMAT);
+ formats.put("^\\d+-\\d+-\\d+T\\d+:\\d+:\\d+Z$", XEP_0082_UTC_FORMAT_WITHOUT_MILLIS);
+ }
+
/**
- * Creates a new DeliveryInformationProvider.
- * ProviderManager requires that every PacketExtensionProvider has a public, no-argument
- * constructor
+ * Creates a new DeliveryInformationProvider. ProviderManager requires that
+ * every PacketExtensionProvider has a public, no-argument constructor
*/
public DelayInformationProvider() {
}
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+ String stampString = (parser.getAttributeValue("", "stamp"));
Date stamp = null;
- try {
- synchronized (DelayInformation.UTC_FORMAT) {
- stamp = DelayInformation.UTC_FORMAT.parse(parser.getAttributeValue("", "stamp"));
- }
- } catch (ParseException e) {
- // Try again but assuming that the date follows JEP-82 format
- // (Jabber Date and Time Profiles)
- try {
- synchronized (DelayInformation.NEW_UTC_FORMAT) {
- stamp = DelayInformation.NEW_UTC_FORMAT
- .parse(parser.getAttributeValue("", "stamp"));
+ DateFormat format = null;
+
+ for (String regexp : formats.keySet()) {
+ if (stampString.matches(regexp)) {
+ try {
+ format = formats.get(regexp);
+ synchronized (format) {
+ stamp = format.parse(stampString);
+ }
}
- } catch (ParseException e1) {
- // Last attempt. Try parsing the date assuming that it does not include milliseconds
- SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
- formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- stamp = formatter.parse(parser.getAttributeValue("", "stamp"));
+ catch (ParseException e) {
+ // do nothing, format is still set
+ }
+
+ // break because only one regexp can match
+ break;
}
}
+
+ /*
+ * if date is in XEP-0091 format handle ambiguous dates missing the
+ * leading zero in month and day
+ */
+ if (format == DelayInformation.XEP_0091_UTC_FORMAT
+ && stampString.split("T")[0].length() < 8) {
+ stamp = handleDateWithMissingLeadingZeros(stampString);
+ }
+
+ /*
+ * if date could not be parsed but XML is valid, don't shutdown
+ * connection by throwing an exception instead set timestamp to current
+ * time
+ */
+ if (stamp == null) {
+ stamp = new Date();
+ }
+
DelayInformation delayInformation = new DelayInformation(stamp);
delayInformation.setFrom(parser.getAttributeValue("", "from"));
- delayInformation.setReason(parser.nextText());
+ String reason = parser.nextText();
+
+ /*
+ * parser.nextText() returns empty string if there is no reason.
+ * DelayInformation API specifies that null should be returned in that
+ * case.
+ */
+ reason = "".equals(reason) ? null : reason;
+ delayInformation.setReason(reason);
+
return delayInformation;
}
+ /**
+ * Parses the given date string in different ways and returns the date that
+ * lies in the past and/or is nearest to the current date-time.
+ *
+ * @param stampString date in string representation
+ * @return the parsed date
+ */
+ private Date handleDateWithMissingLeadingZeros(String stampString) {
+ Calendar now = new GregorianCalendar();
+ Calendar xep91 = null;
+ Calendar xep91Fallback = null;
+
+ xep91 = parseXEP91Date(stampString, DelayInformation.XEP_0091_UTC_FORMAT);
+ xep91Fallback = parseXEP91Date(stampString, XEP_0091_UTC_FALLBACK_FORMAT);
+
+ List dates = filterDatesBefore(now, xep91, xep91Fallback);
+
+ if (!dates.isEmpty()) {
+ return determineNearestDate(now, dates).getTime();
+ }
+ return null;
+ }
+
+ private Calendar parseXEP91Date(String stampString, DateFormat dateFormat) {
+ try {
+ synchronized (dateFormat) {
+ dateFormat.parse(stampString);
+ return dateFormat.getCalendar();
+ }
+ }
+ catch (ParseException e) {
+ return null;
+ }
+ }
+
+ private List filterDatesBefore(Calendar now, Calendar... dates) {
+ List result = new ArrayList();
+
+ for (Calendar calendar : dates) {
+ if (calendar != null && calendar.before(now)) {
+ result.add(calendar);
+ }
+ }
+
+ return result;
+ }
+
+ private Calendar determineNearestDate(final Calendar now, List dates) {
+
+ Collections.sort(dates, new Comparator() {
+
+ public int compare(Calendar o1, Calendar o2) {
+ Long diff1 = new Long(now.getTimeInMillis() - o1.getTimeInMillis());
+ Long diff2 = new Long(now.getTimeInMillis() - o2.getTimeInMillis());
+ return diff1.compareTo(diff2);
+ }
+
+ });
+
+ return dates.get(0);
+ }
+
}
diff --git a/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java b/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
index 4a7c1adba..c48d9b285 100644
--- a/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
+++ b/source/org/jivesoftware/smackx/provider/StreamInitiationProvider.java
@@ -19,10 +19,13 @@
*/
package org.jivesoftware.smackx.provider;
+import java.text.ParseException;
+import java.util.Date;
+
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.packet.DataForm;
-import org.jivesoftware.smackx.packet.DelayInformation;
import org.jivesoftware.smackx.packet.StreamInitiation;
import org.jivesoftware.smackx.packet.StreamInitiation.File;
import org.xmlpull.v1.XmlPullParser;
@@ -90,10 +93,19 @@ public class StreamInitiationProvider implements IQProvider {
e.printStackTrace();
}
}
+
+ Date fileDate = new Date();
+ if (date != null) {
+ try {
+ fileDate = StringUtils.parseXEP0082Date(date);
+ } catch (ParseException e) {
+ // couldn't parse date, use current date-time
+ }
+ }
+
File file = new File(name, fileSize);
file.setHash(hash);
- if (date != null)
- file.setDate(DelayInformation.UTC_FORMAT.parse(date));
+ file.setDate(fileDate);
file.setDesc(desc);
file.setRanged(isRanged);
initiation.setFile(file);
diff --git a/test-unit/org/jivesoftware/smackx/provider/DelayInformationTest.java b/test-unit/org/jivesoftware/smackx/provider/DelayInformationTest.java
new file mode 100644
index 000000000..44a82b61f
--- /dev/null
+++ b/test-unit/org/jivesoftware/smackx/provider/DelayInformationTest.java
@@ -0,0 +1,248 @@
+/**
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.provider;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.packet.DelayInfo;
+import org.jivesoftware.smackx.packet.DelayInformation;
+import org.junit.Test;
+import org.xmlpull.mxp1.MXParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public class DelayInformationTest {
+
+ private static Properties outputProperties = new Properties();
+ static {
+ outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
+ }
+
+ @Test
+ public void delayInformationTest() throws Exception {
+ DelayInformationProvider p = new DelayInformationProvider();
+ DelayInformation delayInfo;
+ XmlPullParser parser;
+ String control;
+ GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date date = calendar.getTime();
+
+ control = XMLBuilder.create("x")
+ .a("xmlns", "jabber:x:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25Z")
+ .t("Offline Storage")
+ .asString(outputProperties);
+
+ parser = getParser(control, "x");
+ delayInfo = (DelayInformation) p.parseExtension(parser);
+
+ assertEquals("capulet.com", delayInfo.getFrom());
+ assertEquals(date, delayInfo.getStamp());
+ assertEquals("Offline Storage", delayInfo.getReason());
+
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("x", parser.getName());
+
+ control = XMLBuilder.create("x")
+ .a("xmlns", "jabber:x:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25Z")
+ .asString(outputProperties);
+
+ parser = getParser(control, "x");
+ delayInfo = (DelayInformation) p.parseExtension(parser);
+
+ assertEquals("capulet.com", delayInfo.getFrom());
+ assertEquals(date, delayInfo.getStamp());
+ assertNull(delayInfo.getReason());
+
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("x", parser.getName());
+
+ }
+
+ @Test
+ public void delayInfoTest() throws Exception {
+ DelayInformationProvider p = new DelayInfoProvider();
+ DelayInfo delayInfo;
+ XmlPullParser parser;
+ String control;
+ GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date date = calendar.getTime();
+
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25Z")
+ .t("Offline Storage")
+ .asString(outputProperties);
+
+ parser = getParser(control, "delay");
+ delayInfo = (DelayInfo) p.parseExtension(parser);
+
+ assertEquals("capulet.com", delayInfo.getFrom());
+ assertEquals(date, delayInfo.getStamp());
+ assertEquals("Offline Storage", delayInfo.getReason());
+
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("delay", parser.getName());
+
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25Z")
+ .asString(outputProperties);
+
+ parser = getParser(control, "delay");
+ delayInfo = (DelayInfo) p.parseExtension(parser);
+
+ assertEquals("capulet.com", delayInfo.getFrom());
+ assertEquals(date, delayInfo.getStamp());
+ assertNull(delayInfo.getReason());
+
+ assertEquals(XmlPullParser.END_TAG, parser.getEventType());
+ assertEquals("delay", parser.getName());
+
+ }
+
+ @Test
+ public void dateFormatsTest() throws Exception {
+ DelayInformationProvider p = new DelayInfoProvider();
+ DelayInfo delayInfo;
+ String control;
+ GregorianCalendar calendar = new GregorianCalendar(2002, 9 - 1, 10, 23, 8, 25);
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ // XEP-0082 date format
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25.12Z")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ GregorianCalendar cal = (GregorianCalendar) calendar.clone();
+ cal.add(Calendar.MILLISECOND, 12);
+ assertEquals(cal.getTime(), delayInfo.getStamp());
+
+ // XEP-0082 date format without milliseconds
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-09-10T23:08:25Z")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ assertEquals(calendar.getTime(), delayInfo.getStamp());
+
+ // XEP-0082 date format without milliseconds and leading 0 in month
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "2002-9-10T23:08:25Z")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ assertEquals(calendar.getTime(), delayInfo.getStamp());
+
+ // XEP-0091 date format
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "20020910T23:08:25")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ assertEquals(calendar.getTime(), delayInfo.getStamp());
+
+ // XEP-0091 date format without leading 0 in month
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMd'T'HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ GregorianCalendar dateInPast = new GregorianCalendar();
+ if (dateInPast.get(Calendar.MONTH) >= 10) {
+ dateInPast.set(Calendar.MONTH, dateInPast.get(Calendar.MONTH) - 3);
+ }
+ dateInPast.add(Calendar.DAY_OF_MONTH, -3);
+ dateInPast.set(Calendar.MILLISECOND, 0);
+
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", dateFormat.format(dateInPast.getTime()))
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ assertEquals(dateInPast.getTime(), delayInfo.getStamp());
+
+ // XEP-0091 date format from SMACK-243
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "200868T09:16:20")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+ Date controlDate = StringUtils.parseXEP0082Date("2008-06-08T09:16:20.0Z");
+
+ assertEquals(controlDate, delayInfo.getStamp());
+
+ // invalid date format
+ control = XMLBuilder.create("delay")
+ .a("xmlns", "urn:xmpp:delay")
+ .a("from", "capulet.com")
+ .a("stamp", "yesterday")
+ .asString(outputProperties);
+
+ delayInfo = (DelayInfo) p.parseExtension(getParser(control, "delay"));
+
+ assertNotNull(delayInfo.getStamp());
+
+ }
+
+ private XmlPullParser getParser(String control, String startTag)
+ throws XmlPullParserException, IOException {
+ XmlPullParser parser = new MXParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new StringReader(control));
+
+ while (true) {
+ if (parser.next() == XmlPullParser.START_TAG
+ && parser.getName().equals(startTag)) {
+ break;
+ }
+ }
+ return parser;
+ }
+
+}