All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.easybeans.component.quartz.ScheduleExpressionParser Maven / Gradle / Ivy

/**
 * EasyBeans
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.easybeans.component.quartz;


import static org.ow2.easybeans.component.quartz.SchedulePatterns.LAST;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_DAY_OF_MONTH;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_DAY_OF_WEEK;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_DAY_OF_WEEK_0_7;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_DAY_OF_WEEK_VALUES;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_HOUR;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_INCREMENTS;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_MINUTE;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_MONTH;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_MONTH_1_12;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_MONTH_VALUES;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_NDAYS;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_RANGE;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_SECOND;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.PATTERN_YEAR;
import static org.ow2.easybeans.component.quartz.SchedulePatterns.WILDCARD_CHARACTER;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import javax.ejb.ScheduleExpression;

import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 *
 * @author Florent Benoit
 */
public class ScheduleExpressionParser {

    /**
     * Logger.
     */
    private static final Log LOGGER = LogFactory.getLog(ScheduleExpressionParser.class);


    /**
     * Allows to convert a month name to its int counterpart value.
     */
    private Map monthNameToInt = null;

    /**
     * Allows to convert a dayOfWeek day to its int counterpart value.
     */
    private Map dayOfWeeksToInt = null;

    /**
     * Init the parser and the converters.
     */
    @SuppressWarnings("boxing")
    public ScheduleExpressionParser() {

        // Initialize month name to int values
        this.monthNameToInt = new HashMap();
        this.monthNameToInt.put("jan", Calendar.JANUARY);
        this.monthNameToInt.put("feb", Calendar.FEBRUARY);
        this.monthNameToInt.put("mar", Calendar.MARCH);
        this.monthNameToInt.put("apr", Calendar.APRIL);
        this.monthNameToInt.put("may", Calendar.MAY);
        this.monthNameToInt.put("jun", Calendar.JUNE);
        this.monthNameToInt.put("jul", Calendar.JULY);
        this.monthNameToInt.put("aug", Calendar.AUGUST);
        this.monthNameToInt.put("sep", Calendar.SEPTEMBER);
        this.monthNameToInt.put("oct", Calendar.OCTOBER);
        this.monthNameToInt.put("nov", Calendar.NOVEMBER);
        this.monthNameToInt.put("dec", Calendar.DECEMBER);



        // -1 to all values as for EJB spec sunday = 0;
        this.dayOfWeeksToInt = new HashMap();
        this.dayOfWeeksToInt.put("sun", Calendar.SUNDAY - 1);
        this.dayOfWeeksToInt.put("mon", Calendar.MONDAY - 1);
        this.dayOfWeeksToInt.put("tue", Calendar.TUESDAY - 1);
        this.dayOfWeeksToInt.put("wed", Calendar.WEDNESDAY - 1);
        this.dayOfWeeksToInt.put("thu", Calendar.THURSDAY - 1);
        this.dayOfWeeksToInt.put("fri", Calendar.FRIDAY - 1);
        this.dayOfWeeksToInt.put("sat", Calendar.SATURDAY - 1);

    }

    /**
     * Cleanup the given string by removing all spaces and by converting the values to lowercase.
     * @param input the value to treat
     * @return the cleanup values
     */
    protected String cleanupInput(final String input) {
        if (input == null) {
            return null;
        }
        // remove the whitespaces ( ? 18.2.1.2)
        String updatedInput = input.replaceAll("\\s+", "");

        // Use tolower as we've to be case insensitive ( ? 18.2.1.2)
        return updatedInput.toLowerCase();
    }

    /**
     * Convert the EJB Schedule Expression to our ScheduleValues expression.
     * @param scheduleExpression the EJB expression
     * @return an updated quartz expression
     */
    public EasyBeansScheduleExpression parse(final ScheduleExpression scheduleExpression) {
        LOGGER.debug("Parsing expression ''{0}''", scheduleExpression);
        EasyBeansScheduleExpression quartzExpression = new EasyBeansScheduleExpression();

        String seconds = scheduleExpression.getSecond();
        String minutes = scheduleExpression.getMinute();
        String hours = scheduleExpression.getHour();
        String dayOfMonth = scheduleExpression.getDayOfMonth();
        String month = scheduleExpression.getMonth();
        String dayOfWeek = scheduleExpression.getDayOfWeek();
        String year = scheduleExpression.getYear();

        // Update each field
        quartzExpression.setSecond(getScheduleValue(seconds, Calendar.SECOND));
        quartzExpression.setMinute(getScheduleValue(minutes, Calendar.MINUTE));
        quartzExpression.setHour(getScheduleValue(hours, Calendar.HOUR_OF_DAY));
        quartzExpression.setDayOfMonth(getScheduleValue(dayOfMonth, Calendar.DAY_OF_MONTH));
        quartzExpression.setMonth(getScheduleValue(month, Calendar.MONTH));
        quartzExpression.setDayOfWeek(getScheduleValue(dayOfWeek, Calendar.DAY_OF_WEEK));
        quartzExpression.setYear(getScheduleValue(year, Calendar.YEAR));

        return quartzExpression;
    }

    /**
     * Extract the right schedule value by analyzing the given input value for a given calendar field.
     * @param inputValue the value to handle
     * @param calendarField the calendar field.
     * @return  the found schedulevalue
     */
    public ScheduleValue getScheduleValue(final String inputValue, final int calendarField) {

        String value = cleanupInput(inputValue);

        // First, validate the given value
        if (!validate(value, calendarField)) {
            throw new IllegalArgumentException("Unable to validate the field '" + inputValue + "' for calendar field '"
                    + calendarField + "'.");
        }

        // It has been validated, now get value

        // It's a list ?
        String[] commaSeparatedList = value.split(",");
        if (commaSeparatedList.length > 1) {
            List elements = new ArrayList();

            // First, remove any duplicates that can be present
            for (int i = 0; i < commaSeparatedList.length; i++) {
                String simple = commaSeparatedList[i];
                if (!elements.contains(simple)) {
                    elements.add(simple);
                }
            }

            // We've at least 2 elements
            if (elements.size() > 1) {

                // Now, for each expression, gets the schedule value
                ScheduleValueList scheduleValueList = new ScheduleValueList(calendarField);
                for (String element : elements) {
                    scheduleValueList.add(getScheduleValue(element, calendarField));
                }

                return scheduleValueList;
            }

            // Only one element, transform it to a single value
            return getScheduleValue(elements.get(0), calendarField);
        }

        Matcher incrementMatcher = PATTERN_INCREMENTS.matcher(value);
        if (incrementMatcher.matches()) {
            return new ScheduleValueIncrements(incrementMatcher.group(1), Integer.parseInt(incrementMatcher.group(2)),
                    calendarField);
        }


        Matcher rangeMatcher = PATTERN_RANGE.matcher(value);
        if (rangeMatcher.matches()) {

            String leftRange = rangeMatcher.group(1);
            String rightRange = rangeMatcher.group(2);

            // Handle special case DAY_OF_WEEK 0-7 as it's a wildcard (see spec EJB 3.1
            if (Calendar.DAY_OF_WEEK == calendarField) {
                if ("0".equals(leftRange) && "7".equals(rightRange)) {
                    return new ScheduleValueWildCard(calendarField);
                }
            }
            String left = convertToSimple(leftRange, calendarField);
            String right = convertToSimple(rightRange, calendarField);

            return new ScheduleValueRange(left, right, calendarField);

        }

        // Wildcard ?
        if (WILDCARD_CHARACTER.equals(value)) {
            return new ScheduleValueWildCard(calendarField);
        }


        // Last ?
        if (LAST.equals(value)) {
            return new ScheduleValueLast(calendarField);
        }

        // Update value to a simple value
        value = convertToSimple(value, calendarField);


        // NTh day ?
        if (PATTERN_NDAYS.matcher(value).matches()) {
            return new ScheduleValueAttributeNDays(value, calendarField);
        }

        // Simple attribute as it's the only remaining case.
        return new ScheduleValueAttribute(Integer.parseInt(value), calendarField);
    }


    /**
     * Makes simpler the expression receive.
     * @param inputValue the value to update
     * @param calendarField the calendar field to use
     * @return the updated value
     */
    public String convertToSimple(final String inputValue, final int calendarField) {
        String value = cleanupInput(inputValue);

        switch (calendarField) {

        // For month we've to handle the EJB spec which start month at 1
        case Calendar.MONTH:

            if (PATTERN_MONTH_VALUES.matcher(value).matches()) {
                Integer monthVal = this.monthNameToInt.get(value);
                return String.valueOf(monthVal.intValue());
            }

            if (PATTERN_MONTH_1_12.matcher(value).matches()) {
                // Needs to decrement as month are for EJB spec from 1 to 12
                return String.valueOf(Integer.parseInt(value) - 1);
            }
            break;

        // Update DayOfWeek
        case Calendar.DAY_OF_WEEK:
            if (PATTERN_DAY_OF_WEEK_VALUES.matcher(value).matches()) {
                Integer dayOfweekVal = this.dayOfWeeksToInt.get(value);
                return String.valueOf(dayOfweekVal.intValue());
            }
            if (PATTERN_DAY_OF_WEEK_0_7.matcher(value).matches()) {
                return String.valueOf(Integer.parseInt(value));
            }

            break;
        default:
            return value;
        }

        return value;
    }

    /**
     * Validate the given value for the given calendar field.
     * @param inputValue the value to handle
     * @param calendarField the calendar field to use
     * @return true if the value is OK or false if it's incorrect.
     */
    protected boolean validate(final String inputValue, final int calendarField) {

        // Null values not allowed
        if (inputValue == null) {
            return false;
        }

        String value = cleanupInput(inputValue);

        // It's a list ?
        String[] commaSeparatedList = value.split(",");
        if (commaSeparatedList.length > 1) {
            // got a list
            boolean validated = true;
            for (int i = 0; i < commaSeparatedList.length; i++) {

                // Wilcard not allowed in a list
                if (WILDCARD_CHARACTER.equals(commaSeparatedList[i])) {
                    return false;
                }

                // Increments not allowed in a list
                if (PATTERN_INCREMENTS.matcher(commaSeparatedList[i]).matches()) {
                    return false;
                }

                validated = validated && validate(commaSeparatedList[i], calendarField);
            }
            return validated;
        }

        // Match increments for second, minute and hour ?
        Matcher incrementMatcher = PATTERN_INCREMENTS.matcher(value);

        // Increments ? only for second/minute/hour
        if (incrementMatcher.matches()) {

            if (!(Calendar.SECOND == calendarField
               || Calendar.MINUTE == calendarField
               || Calendar.HOUR_OF_DAY == calendarField)) {
                throw new IllegalArgumentException(
                        "Cannot use increments pattern for an other type than Second/Minute/Hour for the value '" + value
                                + "' for the type '" + calendarField + "'.");
            }

            String leftIncrement = incrementMatcher.group(1);
            String rightIncrement = incrementMatcher.group(2);

            // Update wildcard character (* needs to be replaced by 0)
            if (WILDCARD_CHARACTER.equals(leftIncrement)) {
                leftIncrement = "0";
            }

            // Validate both cases
            return validate(leftIncrement, calendarField) && validate(rightIncrement, calendarField);

        }

        Matcher rangeMatcher = PATTERN_RANGE.matcher(value);
        if (rangeMatcher.matches()) {

            String leftRange = rangeMatcher.group(1);
            String rightRange = rangeMatcher.group(2);

            return validate(leftRange, calendarField) && validate(rightRange, calendarField);

        }

        switch (calendarField) {
        case Calendar.SECOND:
            return PATTERN_SECOND.matcher(value).matches();
        case Calendar.MINUTE:
            return PATTERN_MINUTE.matcher(value).matches();
        case Calendar.HOUR_OF_DAY:
            return PATTERN_HOUR.matcher(value).matches();
        case Calendar.DAY_OF_MONTH:
            return PATTERN_DAY_OF_MONTH.matcher(value).matches();
        case Calendar.MONTH:
            return PATTERN_MONTH.matcher(value).matches();
        case Calendar.DAY_OF_WEEK:
            return PATTERN_DAY_OF_WEEK.matcher(value).matches();
        case Calendar.YEAR:
            return PATTERN_YEAR.matcher(value).matches();
        default:
            throw new IllegalStateException("Unknown Calendar Field '" + calendarField + "'");
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy