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

com.viaoa.process.OACron Maven / Gradle / Ivy

There is a newer version: 3.7.10
Show newest version
/*  Copyright 1999 Vince Via [email protected]
    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.viaoa.process;

import java.util.Arrays;
import java.util.Calendar;

import com.viaoa.hub.HubEvent;
import com.viaoa.util.OAArray;
import com.viaoa.util.OAConv;
import com.viaoa.util.OADate;
import com.viaoa.util.OADateTime;
import com.viaoa.util.OAString;


/**
 * Used to define and find the next time a Cron-like entry should be ran.

        *     *     *   *    *        command to be executed
        -     -     -   -    -
        |     |     |   |    |
        |     |     |   |    +----- day of week (0-6) (Sunday=0) - Java: 1-7 (Sunday=1)
        |     |     |   +---------- month (1-12) - Java: 0-11    
        |     |     +-------------- day of month (1-31), also allows "last"
        |     +-------------------- hour (0 - 23)
        +-------------------------- min (0 - 59)

 Each field needs to be separate by space or tab.
 Field values can use a single number, "-" for a range, and commas to separate more then one.

 see OACronProcessor# to register cron to be processed

 @author vvia
 */
public abstract class OACron {
    // NOTE: all values are stored as cron values
    private OADateTime dtFrom;
    private String strMins;
    private String strHours;
    private String strDayOfMonth;
    private String strMonth;
    private String strDayOfWeek;

    // store sorted cron values (not Java)
    private int[] mins;
    private int[] hrs;
    private int[] monthDays;
    private int[] daysOfWeek;
    private int[] months;

    private boolean bIncludeLastDayOfMonth;

    private boolean bValid;

    private String name;
    private String description;
    
    private boolean bEnabled = true;
    private OADateTime dtCreated;
    private OADateTime dtLast;
    

    public OACron(String strMins, String strHours, String strDayOfMonth, String strMonth, String strDayOfWeek) {
        this.dtCreated = new OADateTime();
        this.strMins = strMins;
        this.strHours = strHours;
        this.strDayOfMonth = strDayOfMonth;
        this.strMonth = strMonth;
        this.strDayOfWeek = strDayOfWeek;

        // set to default, can get changed when calling getInts
        bValid = true;
        bIncludeLastDayOfMonth = false;
        
        mins = getInts(strMins);
        hrs = getInts(strHours);
        monthDays = getInts(strDayOfMonth, true);
        daysOfWeek = getInts(strDayOfWeek);
        months = getInts(strMonth);

        if (bValid) {
            for (int x : mins) {
                if (x < 0 || x > 59) bValid = false;
            }
            for (int x : hrs) {
                if (x < 0 || x > 23) bValid = false;
            }
            for (int x : monthDays) {
                if (x < 1 || x > 31) bValid = false;
            }
            for (int x : daysOfWeek) {
                if (x < 0 || x > 6) bValid = false;
            }
            
            if (months == null || months.length == 0) { // any month
                months = new int[12];
                for (int i=0; i<12; i++) months[i] = i+1; // store as cron month is 1-12, java is 0-11
            }
            for (int x : months) {
                if (x < 1 || x > 12) bValid = false;
            }
        }
    }

    /**
     * Called when it's time to process.
     */
    public abstract void process(final boolean bManuallyCalled);
    
    
    public int[] getMinutes() {
        return mins;
    }
    public int[] getHours() {
        return hrs;
    }
    public int[] getMonthDays() {
        return monthDays;
    }
    public int[] getDaysOfWeek() {
        return daysOfWeek;
    }
    public int[] getMonths() {
        return months;
    }
    public boolean getIncludeLastDayOfMonth() {
        return bIncludeLastDayOfMonth;
    }

    public boolean isValid() {
        return bValid;
    }

    public String getDescription() {
        if (description != null) return description;
        description = "";
        int x;

        if (months.length == 12) {
            //if (description.length() > 0) description += "; and ";
            //else description += "all months";
        }
        else if (months.length > 0) {
            if (description.length() > 0) description += "; and";
            else description += "when";
            description += " month is ";

            for (int i=0; i 0) description += " or ";
                if (x < 1 || x > 12) {
                    description += "Invalid:"+x;
                }
                else {
                    OADate d = new OADate(2017, x-1, 1);
                    description += d.toString("MMM");
                }
            }
        }

        if (monthDays.length > 0 || bIncludeLastDayOfMonth) {
            if (description.length() > 0) description += "; and";
            else description += "when";
            description += " day of month is ";
            for (int i=0; i 0) description += " or ";
                if (x < 0 || x > 31) description += "Invalid:"+x;
                else description += ""+x;
            }
            if (bIncludeLastDayOfMonth) {
                if (monthDays.length > 0) description += " or ";
                description += "last day";
            }
        }

        if (daysOfWeek.length > 0) {
            if (description.length() > 0) description += "; and";
            else description += "when";
            description += " day of week is ";


            for (int i=0; i 0) description += " or ";
                if (x < 0 || x > 6) description += "Invalid:"+x;
                else {
                    String s;
                    for (int j=0; j<7; j++) {
                        OADate d = new OADate(2017, 0, 1+j);
                        if (d.getDayOfWeek() != x+1) continue;
                        description += d.toString("EEE");
                        break;
                    }
                }
            }
        }

        if (hrs.length > 0) {
            if (description.length() > 0) description += "; and";
            else description += "when";
            description += " hour is ";
            for (int i=0; i 0) description += " or ";
                if (x < 0 || x > 23) description += "Invalid:"+x;
                else description += ""+x;
            }
        }


        if (mins.length > 0) {
            if (description.length() > 0) description += "; and";
            else description += "when";
            description += " minute is ";
            for (int i=0; i 0) description += ", ";
                if (x < 0 || x > 59) description += "Invalid:"+x;
                else description += ""+x;
            }
        }
        else {
            if (description.length() > 0) description += "; and ";
            description += "every minute";
        }


        if (!isValid()) description = "INVALID: "+description;

        return description;
    }

    public OADateTime getLast() {
        return dtLast;
    }
    public void setLast(OADateTime dt) {
        this.dtLast = dt;
    }
    
    public OADateTime getNext() {
        return findNext(new OADateTime());
    }
    public OADateTime getNext(OADateTime dtFrom) {
        return findNext(dtFrom);
    }
    
    public OADateTime findNext() {
        return findNext(new OADateTime());
    }
    public OADateTime findNext(OADateTime dtFrom) {
        if (!isValid()) return null;
        if (dtFrom == null) dtFrom = new OADateTime();
        this.dtFrom = dtFrom;

        OADateTime dtFound = findNextMonth();
        return dtFound;
    }

    private OADateTime findNextMonth() {
        OADateTime dtFound = null;

        final int fromMonth = dtFrom.getMonth();

        OADateTime dtCheck = new OADateTime(dtFrom.getTime());
        for (int i=0; ;i++) {
            if (i > 0) dtCheck = dtCheck.addYears(1);
            for (int m : months) {
                m--; // cron month is 1-12, java is 0-11

                if (i == 0 && m < dtFrom.getMonth()) continue;

                dtCheck.setDay(1);
                dtCheck.setMonth(m);
                dtCheck.clearTime();
                if (dtFound != null && dtFound.before(dtCheck)) continue;
                OADateTime dtTo = dtCheck.addMonths(1);

                if (dtCheck.before(dtFrom)) dtCheck = new OADateTime(dtFrom);

                dtFound = findNextMonthDay(dtFound, dtTo, dtCheck);
            }
            if (dtFound != null) break;
        }
        if (dtFound != null) {
            dtFound.clearSecondAndMilliSecond();            
        }
        return dtFound;
    }
    private OADateTime findNextMonthDay(OADateTime dtFound, OADateTime dtTo, OADateTime dtCheck) {
        if (!bIncludeLastDayOfMonth && (monthDays == null || monthDays.length == 0)) {
            dtFound = findClosestDayOfWeek(dtFound, dtTo, dtCheck);
        }
        else {
            OADateTime dtx = new OADateTime(dtFrom);
            dtx.clearTime();

            int max = dtCheck.getDaysInMonth();
            for (int i=0; i<=monthDays.length; i++) {
                int md;
                if (i == monthDays.length) {
                    if (!bIncludeLastDayOfMonth) continue;
                    md = max;
                }
                else md = monthDays[i];

                if (md > max) continue;

                dtCheck.setDay(md);
                if (dtCheck.before(dtx)) continue;
                if (dtFound != null && dtFound.before(dtCheck)) continue;

                OADateTime dtTo2 = dtCheck.addDays(1);
                dtTo2.clearTime();

                dtFound = findClosestDayOfWeek(dtFound, dtTo2, dtCheck);
            }
        }
        return dtFound;
    }

    // Java Sunday=1, cron Sunday=0
    private OADateTime findClosestDayOfWeek(OADateTime dtFound, OADateTime dtTo, OADateTime dtCheck) {
        if (daysOfWeek == null || daysOfWeek.length == 0) { // any dayOfWeek
            for (;;) {
                dtFound = findClosestHour(dtFound, dtTo, dtCheck);
                if (dtFound != null) break;
                dtCheck = dtCheck.addDays(1);
                dtCheck.clearTime();
                if (dtTo != null && dtTo.compareTo(dtCheck) <= 0) break;
            }
        }
        else {
            int fromDayOfWeek = dtCheck.getDayOfWeek();
            OADateTime dtHold = dtCheck;

            int fromWd = dtHold.getDayOfWeek();
            for (int i=0; i<2; i++) {
                for (int wd : daysOfWeek) {
                    wd++;  // cron day is 0 based, java 1 based

                    int diff;
                    if (i > 0) {
                        if (wd != fromWd) continue;
                        diff = 7;
                    }
                    else {
                        if (fromWd > wd) diff = (wd+7) - fromWd;
                        else diff = wd - fromWd;
                    }

                    if (diff != 0) dtCheck = dtHold.addDays(diff);
                    dtCheck.clearTime();

                    if (dtTo != null && dtTo.compareTo(dtCheck) <= 0) continue;

                    if (dtFound != null && dtFound.before(dtCheck)) continue;

                    dtFound = findClosestHour(dtFound, dtTo, dtCheck);
                }
                if (dtFound != null) break;
            }
        }
        return dtFound;
    }
    private OADateTime findClosestHour(OADateTime dtFound, OADateTime dtTo, OADateTime dtCheck) {
        if (hrs == null || hrs.length == 0) { // any hour
            dtFound = findClosestMinute(dtFound, dtTo, dtCheck);
            if (dtFound == null) {
                dtCheck = dtCheck.addHours(1);
                if (dtTo == null || dtTo.compareTo(dtCheck) > 0) {
                    dtFound = findClosestMinute(dtFound, dtTo, dtCheck);
                }
            }
        }
        else {
            dtCheck.setMinute(0);
            dtCheck.clearSecondAndMilliSecond();            
            OADateTime dtx = new OADateTime(dtFrom);
            dtx.clearTime();
            dtx.set24Hour(dtFrom.get24Hour());

            for (int hr : hrs) {
                dtCheck.set24Hour(hr);
                if (dtCheck.before(dtx)) continue;
                if (dtFound != null && dtFound.before(dtCheck)) continue;
                dtFound = findClosestMinute(dtFound, dtTo, dtCheck);
            }
        }
        return dtFound;
    }
    private OADateTime findClosestMinute(OADateTime dtFound, OADateTime dtTo, OADateTime dtCheck) {
        if (mins == null || mins.length == 0) {
            if (dtFound == null || dtCheck.before(dtFound)) {
                dtFound = new OADateTime(dtCheck);
                if (dtFound.equals(dtFrom)) dtFound = dtFound.addMinutes(1);
            }
            return dtFound;
        }
        else {
            for (int m : mins) {
                dtCheck.setMinute(m);
                if (dtCheck.compareTo(dtFrom) <= 0) continue;
                if (dtFound != null && dtFound.before(dtCheck)) continue;
                if (dtCheck.before(dtFrom)) continue;
                if (dtFound == null || dtCheck.before(dtFound)) {
                    dtFound = new OADateTime(dtCheck);
                }
            }
        }
        return dtFound;
    }


    /**
     * parses values from String
     * @param line
     * @return values to include or [0]=all
     */
    private int[] getInts(String line) {
        return getInts(line, false);
    }
    private int[] getInts(String line, boolean bAllowLast) {
        if (OAString.isEmpty(line)) return new int[0];
        if (line.indexOf('*') >= 0) return new int[0]; // all

        line = line.replace(',', ' ');
        line = line.replace(':', ' ');

        int[] ints = new int[0];
        for (String s : line.split("\\s+")) {
            String[] ss = s.split("\\-");
            if (ss == null || ss.length == 0) continue;
            s = ss[0];
            if (!OAString.isInteger(s)) {
                if (bAllowLast && s.equalsIgnoreCase("last")) {
                    bIncludeLastDayOfMonth = true;
                }
                else bValid = false;
                continue;
            }
            int x = OAConv.toInt(s);

            if (ss.length == 2) {
                if (!OAString.isInteger(ss[1])) {
                    bValid = false;
                    if (!OAArray.contains(ints, x)) {
                        ints = OAArray.add(ints, x);
                    }
                    continue;
                }
                int x2 = OAConv.toInt(ss[1]);
                for (int i=x; i<=x2; i++) {
                    if (!OAArray.contains(ints, i)) {
                        ints = OAArray.add(ints, i);
                    }
                }
            }
            else {
                ints = OAArray.add(ints, x);
            }
        }

        Arrays.sort(ints);
        return ints;
    }

    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }

    public boolean getIsValid() {
        return bValid;
    }
    public void setEnabled(boolean b) {
        this.bEnabled = b;
    }
    public boolean getEnabled() {
        return bEnabled;
    }

    public OADateTime getCreated() {
        return dtCreated;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy