/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.pattern.observer; import com.espertech.esper.client.util.TimePeriod; import com.espertech.esper.schedule.ScheduleParameterException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.datatype.DatatypeConfigurationException; import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Factory for ISO8601 repeating interval observers that indicate truth when a time point was reached. */ public class TimerScheduleISO8601Parser { private static final Logger log = LoggerFactory.getLogger(TimerScheduleISO8601Parser.class); public static TimerScheduleSpec parse(String iso) throws ScheduleParameterException { if (iso == null) { throw new ScheduleParameterException("Received a null value"); } iso = iso.trim(); if (iso.isEmpty()) { throw new ScheduleParameterException("Received an empty string"); } String[] split = iso.split("/"); Long optionalRepeats = null; Calendar optionalDate = null; TimePeriod optionalTimePeriod = null; try { if (iso.equals("/")) { throw new ScheduleParameterException("Invalid number of parts"); } if (iso.endsWith("/")) { throw new ScheduleParameterException("Missing the period part"); } if (split.length == 3) { optionalRepeats = parseRepeat(split[0]); optionalDate = parseDate(split[1]); optionalTimePeriod = parsePeriod(split[2]); } else if (split.length == 2) { // there are two forms: // partial-form-1: "R<?>/P<period>" // partial-form-2: "<date>/P<period>" if (split[0].isEmpty()) { throw new ScheduleParameterException("Expected either a recurrence or a date but received an empty string"); } if (split[0].charAt(0) == 'R') { optionalRepeats = parseRepeat(split[0]); } else { optionalDate = parseDate(split[0]); } optionalTimePeriod = parsePeriod(split[1]); } else if (split.length == 1) { // there are two forms: // just date: "<date>" // just period: "P<period>" if (split[0].charAt(0) == 'P') { optionalTimePeriod = parsePeriod(split[0]); } else { optionalDate = parseDate(split[0]); } } } catch (Exception ex) { throw new ScheduleParameterException("Failed to parse '" + iso + "': " + ex.getMessage(), ex); } // parse repeating interval return new TimerScheduleSpec(optionalDate, null, optionalRepeats, optionalTimePeriod); } public static Calendar parseDate(String dateText) throws ScheduleParameterException { try { return javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(dateText).toGregorianCalendar(); } catch (RuntimeException e) { String message = "Exception parsing date '" + dateText + "', the date is not a supported ISO 8601 date"; log.debug(message, e); throw new ScheduleParameterException(message); } catch (DatatypeConfigurationException e) { throw new ScheduleParameterException("Exception parsing date '" + dateText + "': " + e.getMessage(), e); } } private static long parseRepeat(String repeat) throws ScheduleParameterException { if (repeat.charAt(0) != 'R') { throw new ScheduleParameterException("Invalid repeat '" + repeat + "', expecting 'R' but received '" + repeat.charAt(0) + "'"); } long numRepeats = -1; if (repeat.length() > 1) { try { numRepeats = Long.parseLong(repeat.substring(1)); } catch (RuntimeException ex) { String message = "Invalid repeat '" + repeat + "', expecting an long-typed value but received '" + repeat.substring(1) + "'"; log.debug(message, ex); throw new ScheduleParameterException(message); } } return numRepeats; } private static TimePeriod parsePeriod(String period) throws ScheduleParameterException { Pattern p = Pattern.compile("P((\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?"); Matcher matcher = p.matcher(period); if (!matcher.matches()) { throw new ScheduleParameterException("Invalid period '" + period + "'"); } TimePeriod timePeriod = new TimePeriod(); int indexOfT = period.indexOf("T"); if (indexOfT < 1) { parsePeriodDatePart(period.substring(1), timePeriod); } else { parsePeriodDatePart(period.substring(1, indexOfT), timePeriod); parsePeriodTimePart(period.substring(indexOfT + 1), timePeriod); } Integer largestAbsolute = timePeriod.largestAbsoluteValue(); if (largestAbsolute == null || largestAbsolute == 0) { throw new ScheduleParameterException("Invalid period '" + period + "'"); } return timePeriod; } private static void parsePeriodDatePart(String datePart, TimePeriod timePeriod) throws ScheduleParameterException { Pattern pattern = Pattern.compile("(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?"); Matcher matcher = pattern.matcher(datePart); if (!matcher.matches()) { throw new IllegalStateException(); } for (int i = 0; i < matcher.groupCount(); i++) { String group = matcher.group(i + 1); if (group == null) { } else if (group.endsWith("Y")) { timePeriod.setYears(safeParsePrefixedInt(group)); } else if (group.endsWith("M")) { timePeriod.setMonths(safeParsePrefixedInt(group)); } else if (group.endsWith("D")) { timePeriod.setDays(safeParsePrefixedInt(group)); } else if (group.endsWith("W")) { timePeriod.setWeeks(safeParsePrefixedInt(group)); } } } private static Integer safeParsePrefixedInt(String group) { return Integer.parseInt(group.substring(0, group.length() - 1)); } private static void parsePeriodTimePart(String timePart, TimePeriod timePeriod) throws ScheduleParameterException { Pattern pattern = Pattern.compile("(\\d+H)?(\\d+M)?(\\d+S)?"); Matcher matcher = pattern.matcher(timePart); if (!matcher.matches()) { throw new IllegalStateException(); } for (int i = 0; i < matcher.groupCount(); i++) { String group = matcher.group(i + 1); if (group == null) { } else if (group.endsWith("H")) { timePeriod.setHours(safeParsePrefixedInt(group)); } else if (group.endsWith("M")) { timePeriod.setMinutes(safeParsePrefixedInt(group)); } else if (group.endsWith("S")) { timePeriod.setSeconds(safeParsePrefixedInt(group)); } } } }