diff --git a/source/org/jivesoftware/smack/packet/Presence.java b/source/org/jivesoftware/smack/packet/Presence.java index 97402a005..84fcfef50 100644 --- a/source/org/jivesoftware/smack/packet/Presence.java +++ b/source/org/jivesoftware/smack/packet/Presence.java @@ -209,7 +209,7 @@ public class Presence extends Packet { * @return the xml:lang of this Presence, or null if one has not been set. * @since 3.0.2 */ - private String getLanguage() { + public String getLanguage() { return language; } diff --git a/source/org/jivesoftware/smack/util/StringUtils.java b/source/org/jivesoftware/smack/util/StringUtils.java index 2783a6a5b..ceba26b38 100644 --- a/source/org/jivesoftware/smack/util/StringUtils.java +++ b/source/org/jivesoftware/smack/util/StringUtils.java @@ -26,27 +26,87 @@ import java.security.NoSuchAlgorithmException; 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.List; import java.util.Random; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 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)}. + * @deprecated This public version will be removed in favor of using the methods defined within this class. */ - public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat( - "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + public static final DateFormat XEP_0082_UTC_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + /* + * private version to use internally so we don't have to be concerned with thread safety. + */ + private static final DateFormat dateFormatter = DateFormatType.XEP_0082_DATE_PROFILE.createFormatter(); + private static final Pattern datePattern = Pattern.compile("^\\d+-\\d+-\\d+$"); + + private static final DateFormat timeFormatter = DateFormatType.XEP_0082_TIME_MILLIS_ZONE_PROFILE.createFormatter(); + private static final Pattern timePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))$"); + private static final DateFormat timeNoZoneFormatter = DateFormatType.XEP_0082_TIME_MILLIS_PROFILE.createFormatter(); + private static final Pattern timeNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+$"); + + private static final DateFormat timeNoMillisFormatter = DateFormatType.XEP_0082_TIME_ZONE_PROFILE.createFormatter(); + private static final Pattern timeNoMillisPattern = Pattern.compile("^(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))$"); + private static final DateFormat timeNoMillisNoZoneFormatter = DateFormatType.XEP_0082_TIME_PROFILE.createFormatter(); + private static final Pattern timeNoMillisNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+$"); + + private static final DateFormat dateTimeFormatter = DateFormatType.XEP_0082_DATETIME_MILLIS_PROFILE.createFormatter(); + private static final Pattern dateTimePattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))?$"); + private static final DateFormat dateTimeNoMillisFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private static final Pattern dateTimeNoMillisPattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))?$"); + + private static final DateFormat xep0091Formatter = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"); + private static final DateFormat xep0091Date6DigitFormatter = new SimpleDateFormat("yyyyMd'T'HH:mm:ss"); + private static final DateFormat xep0091Date7Digit1MonthFormatter = new SimpleDateFormat("yyyyMdd'T'HH:mm:ss"); + private static final DateFormat xep0091Date7Digit2MonthFormatter = new SimpleDateFormat("yyyyMMd'T'HH:mm:ss"); + private static final Pattern xep0091Pattern = Pattern.compile("^\\d+T\\d+:\\d+:\\d+$"); + + private static final List couplings = new ArrayList(); + static { - XEP_0082_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + TimeZone utc = TimeZone.getTimeZone("UTC"); + XEP_0082_UTC_FORMAT.setTimeZone(utc); + dateFormatter.setTimeZone(utc); + timeFormatter.setTimeZone(utc); + timeNoZoneFormatter.setTimeZone(utc); + timeNoMillisFormatter.setTimeZone(utc); + timeNoMillisNoZoneFormatter.setTimeZone(utc); + dateTimeFormatter.setTimeZone(utc); + dateTimeNoMillisFormatter.setTimeZone(utc); + + xep0091Formatter.setTimeZone(utc); + xep0091Date6DigitFormatter.setTimeZone(utc); + xep0091Date7Digit1MonthFormatter.setTimeZone(utc); + xep0091Date7Digit1MonthFormatter.setLenient(false); + xep0091Date7Digit2MonthFormatter.setTimeZone(utc); + xep0091Date7Digit2MonthFormatter.setLenient(false); + + couplings.add(new PatternCouplings(datePattern, dateFormatter)); + couplings.add(new PatternCouplings(dateTimePattern, dateTimeFormatter)); + couplings.add(new PatternCouplings(dateTimeNoMillisPattern, dateTimeNoMillisFormatter)); + couplings.add(new PatternCouplings(timePattern, timeFormatter)); + couplings.add(new PatternCouplings(timeNoZonePattern, timeNoZoneFormatter)); + couplings.add(new PatternCouplings(timeNoMillisPattern, timeNoMillisFormatter)); + couplings.add(new PatternCouplings(timeNoMillisNoZonePattern, timeNoMillisNoZoneFormatter)); } private static final char[] QUOTE_ENCODE = """.toCharArray(); @@ -56,18 +116,139 @@ public class StringUtils { private static final char[] GT_ENCODE = ">".toCharArray(); /** - * Parses the given date string in the XEP-0082 - XMPP Date and Time Profiles format. + * Parses the given date string in the XEP-0082 - XMPP Date and Time Profiles. * * @param dateString the date string to parse * @return the parsed Date * @throws ParseException if the specified string cannot be parsed + * @deprecated Use {@link #parseDate(String)} instead. + * */ public static Date parseXEP0082Date(String dateString) throws ParseException { - synchronized (XEP_0082_UTC_FORMAT) { - return XEP_0082_UTC_FORMAT.parse(dateString); + return parseDate(dateString); + } + + /** + * Parses the given date string in either of the three profiles of XEP-0082 - XMPP Date and Time Profiles + * or XEP-0091 - Legacy Delayed Delivery format. + *

+ * This method uses internal date formatters and is thus threadsafe. + * @param dateString the date string to parse + * @return the parsed Date + * @throws ParseException if the specified string cannot be parsed + */ + public static Date parseDate(String dateString) throws ParseException { + Matcher matcher = xep0091Pattern.matcher(dateString); + + /* + * if date is in XEP-0091 format handle ambiguous dates missing the + * leading zero in month and day + */ + if (matcher.matches()) { + int length = dateString.split("T")[0].length(); + + if (length < 8) { + Date date = handleDateWithMissingLeadingZeros(dateString, length); + + if (date != null) + return date; + } + else { + synchronized (xep0091Formatter) { + return xep0091Formatter.parse(dateString); + } + } + } + else { + for (PatternCouplings coupling : couplings) { + matcher = coupling.pattern.matcher(dateString); + + if (matcher.matches()) + { + synchronized (coupling.formatter) { + return coupling.formatter.parse(dateString); + } + } + } + } + + /* + * We assume it is the XEP-0082 DateTime profile with no milliseconds at this point. If it isn't, is is just not parseable, then we attempt + * to parse it regardless and let it throw the ParseException. + */ + synchronized (dateTimeNoMillisFormatter) { + return dateTimeNoMillisFormatter.parse(dateString); } } + /** + * 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 + * @param dateLength + * @param noFuture + * @return the parsed date + * @throws ParseException The date string was of an unknown format + */ + private static Date handleDateWithMissingLeadingZeros(String stampString, int dateLength) throws ParseException { + if (dateLength == 6) { + synchronized (xep0091Date6DigitFormatter) { + return xep0091Date6DigitFormatter.parse(stampString); + } + } + Calendar now = Calendar.getInstance(); + + Calendar oneDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit1MonthFormatter); + Calendar twoDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit2MonthFormatter); + + List dates = filterDatesBefore(now, oneDigitMonth, twoDigitMonth); + + if (!dates.isEmpty()) { + return determineNearestDate(now, dates).getTime(); + } + return null; + } + + private static Calendar parseXEP91Date(String stampString, DateFormat dateFormat) { + try { + synchronized (dateFormat) { + dateFormat.parse(stampString); + return dateFormat.getCalendar(); + } + } + catch (ParseException e) { + return null; + } + } + + private static 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 static 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); + } + /** * Formats a Date into a XEP-0082 - XMPP Date and Time Profiles string. * @@ -75,10 +256,15 @@ public class StringUtils { * @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); + synchronized (dateTimeFormatter) { + return dateTimeFormatter.format(date); } } + + public static String formatDate(Date toFormat, DateFormatType type) + { + return null; + } /** * Returns the name portion of a XMPP address. For example, for the @@ -562,4 +748,15 @@ public class StringUtils { private StringUtils() { // Not instantiable. } + + private static class PatternCouplings { + Pattern pattern; + DateFormat formatter; + + public PatternCouplings(Pattern datePattern, DateFormat dateFormat) { + pattern = datePattern; + formatter = dateFormat; + } + } + } diff --git a/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java b/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java index d20c22d53..77bf6114f 100644 --- a/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java +++ b/source/org/jivesoftware/smackx/provider/DelayInformationProvider.java @@ -20,19 +20,8 @@ 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; @@ -47,82 +36,25 @@ import org.xmlpull.v1.XmlPullParser; * @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 - */ - public DelayInformationProvider() { - } - public PacketExtension parseExtension(XmlPullParser parser) throws Exception { String stampString = (parser.getAttributeValue("", "stamp")); Date stamp = null; - 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 e) { - // do nothing, format is still set - } - - // break because only one regexp can match - break; + try { + stamp = StringUtils.parseDate(stampString); + } + catch (ParseException parseExc) { + /* + * if date could not be parsed but XML is valid, don't shutdown + * connection by throwing an exception instead set timestamp to epoch + * so that it is obviously wrong. + */ + if (stamp == null) { + stamp = new Date(0); } } - /* - * 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")); @@ -133,72 +65,9 @@ public class DelayInformationProvider implements PacketExtensionProvider { * DelayInformation API specifies that null should be returned in that * case. */ - reason = "".equals(reason) ? null : reason; + reason = reason.isEmpty() ? 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/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java index 068d37d26..e7c25161a 100644 --- a/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/test-unit/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -7,22 +7,30 @@ */ package org.jivesoftware.smack.util; -import static junit.framework.Assert.*; -import static org.custommonkey.xmlunit.XMLAssert.*; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLNotEqual; -import java.io.IOException; -import java.io.StringReader; +import java.util.Calendar; +import java.util.Date; import java.util.Locale; import java.util.Properties; +import java.util.TimeZone; import org.custommonkey.xmlunit.DetailedDiff; import org.custommonkey.xmlunit.Diff; +import org.jivesoftware.smack.TestUtils; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smackx.packet.DelayInformation; import org.junit.Ignore; import org.junit.Test; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import com.jamesmurty.utils.XMLBuilder; @@ -56,7 +64,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); Message message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertTrue(message.getBodyLanguages().isEmpty()); @@ -75,7 +83,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(otherLanguage, message.getBody()); assertTrue(message.getBodyLanguages().isEmpty()); @@ -93,7 +101,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertTrue(message.getBodyLanguages().isEmpty()); @@ -112,7 +120,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertTrue(message.getBodyLanguages().isEmpty()); @@ -133,7 +141,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getBody()); assertFalse(message.getBodyLanguages().isEmpty()); @@ -154,7 +162,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getBody()); assertFalse(message.getBodyLanguages().isEmpty()); @@ -175,7 +183,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getBody()); assertFalse(message.getBodyLanguages().isEmpty()); @@ -205,7 +213,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); Message message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertTrue(message.getSubjectLanguages().isEmpty()); @@ -224,7 +232,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(otherLanguage, message.getSubject()); assertTrue(message.getSubjectLanguages().isEmpty()); @@ -242,7 +250,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertTrue(message.getSubjectLanguages().isEmpty()); @@ -261,7 +269,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertTrue(message.getSubjectLanguages().isEmpty()); @@ -282,7 +290,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getSubject()); assertFalse(message.getSubjectLanguages().isEmpty()); @@ -303,7 +311,7 @@ public class PacketParserUtilsTest { .t(otherLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getSubject()); assertFalse(message.getSubjectLanguages().isEmpty()); @@ -324,7 +332,7 @@ public class PacketParserUtilsTest { .t(defaultLanguage) .asString(outputProperties); - message = (Message) PacketParserUtils.parseMessage(getParser(control)); + message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertNull(message.getSubject()); assertFalse(message.getSubjectLanguages().isEmpty()); @@ -359,7 +367,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertEquals(otherLanguage, message.getBody(otherLanguage)); @@ -384,7 +392,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody(defaultLanguage)); @@ -408,7 +416,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(otherLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody(defaultLanguage)); @@ -432,7 +440,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody(defaultLanguage)); @@ -455,7 +463,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody(defaultLanguage)); @@ -478,7 +486,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getBody()); assertEquals(defaultLanguage, message.getBody(defaultLanguage)); @@ -512,7 +520,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertEquals(otherLanguage, message.getSubject(otherLanguage)); @@ -537,7 +545,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); @@ -561,7 +569,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(otherLanguage, message.getSubject()); assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); @@ -585,7 +593,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); @@ -608,7 +616,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); @@ -631,7 +639,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); message = (Message) PacketParserUtils - .parseMessage(getParser(control)); + .parseMessage(TestUtils.getMessageParser(control)); assertEquals(defaultLanguage, message.getSubject()); assertEquals(defaultLanguage, message.getSubject(defaultLanguage)); @@ -657,7 +665,7 @@ public class PacketParserUtilsTest { .asString(outputProperties); try { - Message message = (Message) PacketParserUtils.parseMessage(getParser(control)); + Message message = (Message) PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); String body = "" + "Bad Message Body"; assertEquals(body, message.getBody()); @@ -689,7 +697,7 @@ public class PacketParserUtilsTest { String invalidControl = validControl.replace("Good Message Body", "Bad Body"); try { - PacketParserUtils.parseMessage(getParser(invalidControl)); + PacketParserUtils.parseMessage(TestUtils.getMessageParser(invalidControl)); fail("Exception should be thrown"); } catch(XmlPullParserException e) { assertTrue(e.getMessage().contains("end tag name ")); @@ -698,7 +706,7 @@ public class PacketParserUtilsTest { invalidControl = validControl.replace("Good Message Body", "Bad Body"); try { - PacketParserUtils.parseMessage(getParser(invalidControl)); + PacketParserUtils.parseMessage(TestUtils.getMessageParser(invalidControl)); fail("Exception should be thrown"); } catch(XmlPullParserException e) { assertTrue(e.getMessage().contains("end tag name ")); @@ -707,7 +715,7 @@ public class PacketParserUtilsTest { invalidControl = validControl.replace("Good Message Body", "Bad Body"); try { - PacketParserUtils.parseMessage(getParser(invalidControl)); + PacketParserUtils.parseMessage(TestUtils.getMessageParser(invalidControl)); fail("Exception should be thrown"); } catch(XmlPullParserException e) { assertTrue(e.getMessage().contains("end tag name ")); @@ -736,20 +744,94 @@ public class PacketParserUtilsTest { .t("This is a test of the emergency broadcast system, 3.") .asString(outputProperties); - Packet message = PacketParserUtils.parseMessage(getParser(control)); + Packet message = PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertXMLEqual(control, message.toXML()); } - - private XmlPullParser getParser(String control) throws XmlPullParserException, IOException { - XmlPullParser parser = new MXParser(); - parser.setInput(new StringReader(control)); - while(true) { - if(parser.next() == XmlPullParser.START_TAG - && parser.getName().equals("message")) { break; } - } - return parser; + + @Test + public void validateSimplePresence() throws Exception { + String stanza = ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + + assertXMLEqual(stanza, presence.toXML()); } + @Test + public void validatePresenceProbe() throws Exception { + String stanza = ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + + assertXMLEqual(stanza, presence.toXML()); + assertEquals(Presence.Type.unsubscribed, presence.getType()); + } + + @Test + public void validatePresenceOptionalElements() throws Exception { + String stanza = "" + + "dnd" + + "Wooing Juliet" + + "1" + + ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + assertXMLEqual(stanza, presence.toXML()); + assertEquals(Presence.Type.unsubscribed, presence.getType()); + assertEquals("dnd", presence.getMode().name()); + assertEquals("en", presence.getLanguage()); + assertEquals("Wooing Juliet", presence.getStatus()); + assertEquals(1, presence.getPriority()); + } + + @Test + public void validatePresenceWithDelayedDelivery() throws Exception { + String stanza = "" + + ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + + DelayInformation delay = (DelayInformation) presence.getExtension("urn:xmpp:delay"); + assertNotNull(delay); + Date date = StringUtils.parseDate("2002-09-10T23:41:07Z"); + assertEquals(date, delay.getStamp()); + } + + @Test + public void validatePresenceWithLegacyDelayed() throws Exception { + String stanza = "" + + ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + + DelayInformation delay = (DelayInformation) presence.getExtension("jabber:x:delay"); + assertNotNull(delay); + Date date = StringUtils.parseDate("20020910T23:41:07"); + Calendar cal = Calendar.getInstance(); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + cal.setTime(date); + assertEquals(cal.getTime(), delay.getStamp()); + } + + @Test + public void parsePresenceWithInvalidDelayedDelivery() throws Exception { + String stanza = "" + + ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + assertNull(presence.getExtension("urn:xmpp:delay")); + } + + @Test + public void parsePresenceWithInvalidLegacyDelayed() throws Exception { + String stanza = "" + + ""; + + Presence presence = PacketParserUtils.parsePresence(TestUtils.getPresenceParser(stanza)); + DelayInformation delay = (DelayInformation) presence.getExtension("urn:xmpp:delay"); + assertNull(delay); + } + private String determineNonDefaultLanguage() { String otherLanguage = "jp"; Locale[] availableLocales = Locale.getAvailableLocales(); diff --git a/test/org/jivesoftware/smack/util/StringUtilsTest.java b/test-unit/org/jivesoftware/smack/util/StringUtilsTest.java similarity index 63% rename from test/org/jivesoftware/smack/util/StringUtilsTest.java rename to test-unit/org/jivesoftware/smack/util/StringUtilsTest.java index f849cf81f..c2f892fdc 100644 --- a/test/org/jivesoftware/smack/util/StringUtilsTest.java +++ b/test-unit/org/jivesoftware/smack/util/StringUtilsTest.java @@ -52,13 +52,23 @@ package org.jivesoftware.smack.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.junit.Test; /** * A test case for the StringUtils class. */ -public class StringUtilsTest extends TestCase { - +public class StringUtilsTest { + @Test public void testEscapeForXML() { String input = null; @@ -98,6 +108,7 @@ public class StringUtilsTest extends TestCase { assertEquals("It's a good day today", StringUtils.escapeForXML(input)); } + @Test public void testHash() { // Test null // @TODO - should the StringUtils.hash(String) method be fixed to handle null input? @@ -148,6 +159,7 @@ public class StringUtilsTest extends TestCase { return valid; } + @Test public void testEncodeHex() { String input = ""; String output = ""; @@ -163,6 +175,7 @@ public class StringUtilsTest extends TestCase { /** * This method tests 2 StringUtil methods - encodeBase64(String) and encodeBase64(byte[]). */ + @Test public void testEncodeBase64() { String input = ""; String output = ""; @@ -204,6 +217,7 @@ public class StringUtilsTest extends TestCase { } */ + @Test public void testRandomString() { // Boundary test String result = StringUtils.randomString(-1); @@ -222,6 +236,7 @@ public class StringUtilsTest extends TestCase { assertTrue(result.length() == 128); } + @Test public void testParsing() { String error = "Error parsing node name"; assertEquals(error, "", StringUtils.parseName("yahoo.myjabber.net")); @@ -236,4 +251,144 @@ public class StringUtilsTest extends TestCase { assertEquals(error, result, StringUtils.parseServer("user@yahoo.myjabber.net/registred")); assertEquals(error, result, StringUtils.parseServer("user@yahoo.myjabber.net")); } + + @Test + public void parseXep0082DateProfile() throws Exception + { + Date date = StringUtils.parseDate("1971-07-21"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(1971, cal.get(Calendar.YEAR)); + assertEquals(6, cal.get(Calendar.MONTH)); + assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); + } + + @Test + public void parseXep0082TimeProfile() throws Exception + { + Date date = StringUtils.parseDate("02:56:15"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(56, cal.get(Calendar.MINUTE)); + assertEquals(15, cal.get(Calendar.SECOND)); + } + + @Test + public void parseXep0082TimeWithMillisProfile() throws Exception + { + Date date = StringUtils.parseDate("02:56:15.123"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(56, cal.get(Calendar.MINUTE)); + assertEquals(15, cal.get(Calendar.SECOND)); + assertEquals(123, cal.get(Calendar.MILLISECOND)); + } + + @Test + public void parseXep0082DateTimeProfile() throws Exception + { + Date date = StringUtils.parseDate("1971-07-21T02:56:15Z"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(1971, cal.get(Calendar.YEAR)); + assertEquals(6, cal.get(Calendar.MONTH)); + assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(2, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(56, cal.get(Calendar.MINUTE)); + assertEquals(15, cal.get(Calendar.SECOND)); + } + + @Test + public void parseXep0082DateTimeProfileWithMillis() throws Exception + { + Date date = StringUtils.parseDate("1971-07-21T02:56:15.123Z"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(1971, cal.get(Calendar.YEAR)); + assertEquals(6, cal.get(Calendar.MONTH)); + assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(2, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(56, cal.get(Calendar.MINUTE)); + assertEquals(15, cal.get(Calendar.SECOND)); + assertEquals(123, cal.get(Calendar.MILLISECOND)); + } + + @Test + public void parseXep0091() throws Exception + { + Date date = StringUtils.parseDate("20020910T23:08:25"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2002, cal.get(Calendar.YEAR)); + assertEquals(8, cal.get(Calendar.MONTH)); + assertEquals(10, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(23, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(8, cal.get(Calendar.MINUTE)); + assertEquals(25, cal.get(Calendar.SECOND)); + } + + @Test + public void parseXep0091NoLeading0() throws Exception + { + Date date = StringUtils.parseDate("200291T23:08:25"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2002, cal.get(Calendar.YEAR)); + assertEquals(8, cal.get(Calendar.MONTH)); + assertEquals(1, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(23, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(8, cal.get(Calendar.MINUTE)); + assertEquals(25, cal.get(Calendar.SECOND)); + } + + @Test + public void parseXep0091AmbiguousMonthDay() throws Exception + { + Date date = StringUtils.parseDate("2002101T23:08:25"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2002, cal.get(Calendar.YEAR)); + assertEquals(9, cal.get(Calendar.MONTH)); + assertEquals(1, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(23, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(8, cal.get(Calendar.MINUTE)); + assertEquals(25, cal.get(Calendar.SECOND)); + } + + @Test + public void parseXep0091SingleDigitMonth() throws Exception + { + Date date = StringUtils.parseDate("2002130T23:08:25"); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + assertEquals(2002, cal.get(Calendar.YEAR)); + assertEquals(0, cal.get(Calendar.MONTH)); + assertEquals(30, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(23, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(8, cal.get(Calendar.MINUTE)); + assertEquals(25, cal.get(Calendar.SECOND)); + } + + @Test (expected=ParseException.class) + public void parseNoMonthDay() throws Exception + { + StringUtils.parseDate("2002T23:08:25"); + } + + @Test (expected=ParseException.class) + public void parseNoYear() throws Exception + { + StringUtils.parseDate("130T23:08:25"); + } }