mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-10 18:59:41 +02:00
improved Delay Information Parser (fixes SMACK-243)
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@11823 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
8b54f34153
commit
7e01c66374
8 changed files with 496 additions and 56 deletions
|
@ -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<String, DateFormat> formats = new HashMap<String, DateFormat>();
|
||||
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<Calendar> 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<Calendar> filterDatesBefore(Calendar now, Calendar... dates) {
|
||||
List<Calendar> result = new ArrayList<Calendar>();
|
||||
|
||||
for (Calendar calendar : dates) {
|
||||
if (calendar != null && calendar.before(now)) {
|
||||
result.add(calendar);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Calendar determineNearestDate(final Calendar now, List<Calendar> dates) {
|
||||
|
||||
Collections.sort(dates, new Comparator<Calendar>() {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue