| /* |
| * Copyright 2008 ZXing authors |
| * |
| * 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 com.google.zxing.client.result; |
| |
| import java.text.DateFormat; |
| import java.text.ParseException; |
| import java.text.SimpleDateFormat; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.Locale; |
| import java.util.TimeZone; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * @author Sean Owen |
| */ |
| public final class CalendarParsedResult extends ParsedResult { |
| |
| private static final Pattern RFC2445_DURATION = |
| Pattern.compile("P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?"); |
| private static final long[] RFC2445_DURATION_FIELD_UNITS = { |
| 7 * 24 * 60 * 60 * 1000L, // 1 week |
| 24 * 60 * 60 * 1000L, // 1 day |
| 60 * 60 * 1000L, // 1 hour |
| 60 * 1000L, // 1 minute |
| 1000L, // 1 second |
| }; |
| |
| private static final Pattern DATE_TIME = Pattern.compile("[0-9]{8}(T[0-9]{6}Z?)?"); |
| |
| private final String summary; |
| private final Date start; |
| private final boolean startAllDay; |
| private final Date end; |
| private final boolean endAllDay; |
| private final String location; |
| private final String organizer; |
| private final String[] attendees; |
| private final String description; |
| private final double latitude; |
| private final double longitude; |
| |
| public CalendarParsedResult(String summary, |
| String startString, |
| String endString, |
| String durationString, |
| String location, |
| String organizer, |
| String[] attendees, |
| String description, |
| double latitude, |
| double longitude) { |
| super(ParsedResultType.CALENDAR); |
| this.summary = summary; |
| |
| try { |
| this.start = parseDate(startString); |
| } catch (ParseException pe) { |
| throw new IllegalArgumentException(pe.toString()); |
| } |
| |
| if (endString == null) { |
| long durationMS = parseDurationMS(durationString); |
| end = durationMS < 0L ? null : new Date(start.getTime() + durationMS); |
| } else { |
| try { |
| this.end = parseDate(endString); |
| } catch (ParseException pe) { |
| throw new IllegalArgumentException(pe.toString()); |
| } |
| } |
| |
| this.startAllDay = startString.length() == 8; |
| this.endAllDay = endString != null && endString.length() == 8; |
| |
| this.location = location; |
| this.organizer = organizer; |
| this.attendees = attendees; |
| this.description = description; |
| this.latitude = latitude; |
| this.longitude = longitude; |
| } |
| |
| public String getSummary() { |
| return summary; |
| } |
| |
| /** |
| * @return start time |
| */ |
| public Date getStart() { |
| return start; |
| } |
| |
| /** |
| * @return true if start time was specified as a whole day |
| */ |
| public boolean isStartAllDay() { |
| return startAllDay; |
| } |
| |
| /** |
| * @return event end {@link Date}, or {@code null} if event has no duration |
| * @see #getStart() |
| */ |
| public Date getEnd() { |
| return end; |
| } |
| |
| /** |
| * @return true if end time was specified as a whole day |
| */ |
| public boolean isEndAllDay() { |
| return endAllDay; |
| } |
| |
| public String getLocation() { |
| return location; |
| } |
| |
| public String getOrganizer() { |
| return organizer; |
| } |
| |
| public String[] getAttendees() { |
| return attendees; |
| } |
| |
| public String getDescription() { |
| return description; |
| } |
| |
| public double getLatitude() { |
| return latitude; |
| } |
| |
| public double getLongitude() { |
| return longitude; |
| } |
| |
| @Override |
| public String getDisplayResult() { |
| StringBuilder result = new StringBuilder(100); |
| maybeAppend(summary, result); |
| maybeAppend(format(startAllDay, start), result); |
| maybeAppend(format(endAllDay, end), result); |
| maybeAppend(location, result); |
| maybeAppend(organizer, result); |
| maybeAppend(attendees, result); |
| maybeAppend(description, result); |
| return result.toString(); |
| } |
| |
| /** |
| * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) |
| * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). |
| * |
| * @param when The string to parse |
| * @throws ParseException if not able to parse as a date |
| */ |
| private static Date parseDate(String when) throws ParseException { |
| if (!DATE_TIME.matcher(when).matches()) { |
| throw new ParseException(when, 0); |
| } |
| if (when.length() == 8) { |
| // Show only year/month/day |
| return buildDateFormat().parse(when); |
| } else { |
| // The when string can be local time, or UTC if it ends with a Z |
| Date date; |
| if (when.length() == 16 && when.charAt(15) == 'Z') { |
| date = buildDateTimeFormat().parse(when.substring(0, 15)); |
| Calendar calendar = new GregorianCalendar(); |
| long milliseconds = date.getTime(); |
| // Account for time zone difference |
| milliseconds += calendar.get(Calendar.ZONE_OFFSET); |
| // Might need to correct for daylight savings time, but use target time since |
| // now might be in DST but not then, or vice versa |
| calendar.setTime(new Date(milliseconds)); |
| milliseconds += calendar.get(Calendar.DST_OFFSET); |
| date = new Date(milliseconds); |
| } else { |
| date = buildDateTimeFormat().parse(when); |
| } |
| return date; |
| } |
| } |
| |
| private static String format(boolean allDay, Date date) { |
| if (date == null) { |
| return null; |
| } |
| DateFormat format = allDay |
| ? DateFormat.getDateInstance(DateFormat.MEDIUM) |
| : DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); |
| return format.format(date); |
| } |
| |
| private static long parseDurationMS(CharSequence durationString) { |
| if (durationString == null) { |
| return -1L; |
| } |
| Matcher m = RFC2445_DURATION.matcher(durationString); |
| if (!m.matches()) { |
| return -1L; |
| } |
| long durationMS = 0L; |
| for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) { |
| String fieldValue = m.group(i + 1); |
| if (fieldValue != null) { |
| durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Integer.parseInt(fieldValue); |
| } |
| } |
| return durationMS; |
| } |
| |
| private static DateFormat buildDateFormat() { |
| DateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); |
| // For dates without a time, for purposes of interacting with Android, the resulting timestamp |
| // needs to be midnight of that day in GMT. See: |
| // http://code.google.com/p/android/issues/detail?id=8330 |
| format.setTimeZone(TimeZone.getTimeZone("GMT")); |
| return format; |
| } |
| |
| private static DateFormat buildDateTimeFormat() { |
| return new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH); |
| } |
| |
| } |