Greetings,
I've had to implement a quartz cron validator :
format.
I decided to split the parts and validate each one separately. My tests seem to confirm that the results are accepatable but I feel that the approach may not be optimal. My regex is not all that good either.
I need help in improving
any aspects of this code.
Critiques are also welcome and you don't have to hold back.
package cib.ibot.core.util;
import java.util.regex.Pattern;
public class CronValidate {
enum Day {
MON, TUE, WED, THU, FRI, SAT, SUN
}
enum Month {
JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
}
public static boolean validQuartzCron(String cronExpression) {
if (cronExpression == null || cronExpression.trim().equals("")) {
return false;
}
String[] parts = cronExpression.split(" ");
if (parts.length != 6 && parts.length != 7) {
return false;
}
// seconds
if (!CronValidate.validateField(parts[0], CronValidate.zero59(), false)) {
return false;
}
// minutes
if (!CronValidate.validateField(parts[1], CronValidate.zero59(), false)) {
return false;
}
// hours
if (!CronValidate.validateField(parts[2], CronValidate.zero23(), false)) {
return false;
}
// Day of Month
if ((parts[3].contains("L") || parts[3].contains("W"))) {
return CronValidate.validateLWMonth(parts[3]);
}
else if (!CronValidate.validateField(parts[3], CronValidate.one31(), true)) {
return false;
}
// Month
if (!CronValidate.validateField(CronValidate.convertToAllNumbers(parts[4]), CronValidate.one12(), false)) {
return false;
}
// Day of Week
if ((parts[5].contains("L") || parts[5].contains("#"))) {
return CronValidate.validateLHashDayOfWeek(parts[5]);
}
else if (!CronValidate.validateField(CronValidate.convertToAllNumbers(parts[5]), CronValidate.one7(), false)) {
return false;
}
// year
if (parts.length == 7 && !CronValidate.validYear(parts[6])) {
return false;
}
// if you got this far you are ok.
return true;
}
public static boolean validateField(String cronExpression, String basicFieldRegex, boolean allowQMark) {
if (cronExpression.toString().equals("")) {
return false;
}
if (cronExpression.equals("*")) {
return true;
}
if (allowQMark && cronExpression.equals("?")) {
return true;
}
if (CronValidate.validateRegex(basicFieldRegex, cronExpression)) {
return true;
}
if (!cronExpression.contains(",") && !cronExpression.contains("/") && !cronExpression.contains("-")) {
return CronValidate.validateRegex(basicFieldRegex, cronExpression);
}
if (cronExpression.contains(",")) {
String[] parts = cronExpression.split(",");
for (String part : parts) {
if (part.contains("-")) {
String[] pieces = part.split("\\-");
for (String piece : pieces) {
if (!CronValidate.validateRegex(basicFieldRegex, piece)) {
return false;
}
}
} else if (part.contains("/")) {
String[] pieces = part.split("/");
if (pieces.length != 2) {
return false;
}
if (!CronValidate.validateRegex(basicFieldRegex, pieces[0]) || !CronValidate.validateRegex(basicFieldRegex, pieces[1])) {
return false;
}
}
else if (!CronValidate.validateRegex(basicFieldRegex, part)) {
return false;
}
}
return true;
}
if (cronExpression.contains("/")) {
String[] parts = cronExpression.split("/");
if (parts.length != 2) {
return false;
}
if (!CronValidate.validateRegex(basicFieldRegex, parts[0]) || !CronValidate.validateRegex(basicFieldRegex, parts[1])) {
return false;
}
}
if (cronExpression.contains("-")) {
String[] pieces = cronExpression.split("\\-");
for (String piece : pieces) {
if (!CronValidate.validateRegex(basicFieldRegex, piece)) {
return false;
}
}
}
return true;
}
// exceptionals
private static boolean validateLHashDayOfWeek(String dayOfWeekCron) {
if (dayOfWeekCron.equals("L") || dayOfWeekCron.matches("^([1-7])L$") || dayOfWeekCron.matches("^([1-7]#[1-7])$")) {
return true;
}
return false;
}
private static boolean validateLWMonth(String dayOfMonthCron) {
if (dayOfMonthCron.equals("L") || dayOfMonthCron.equals("LW") || dayOfMonthCron.equals("W") || dayOfMonthCron.matches("^([1-9]|1[0-9]|2[0-9]|3[0-1])W$")) {
return true;
}
return false;
}
private static boolean validYear(String yearCron) {
if (yearCron.equals("*")) {
return true;
}
if (yearCron.matches(CronValidate.yearRange())) {
return true;
}
if (yearCron.matches(CronValidate.year())) {
return true;
}
if(yearCron.contains(",")) {
String[] parts = yearCron.split(",");
// each part must be a year range or a simple year
for(String part : parts) {
if (part.matches(CronValidate.yearRange()) || part.matches(CronValidate.year())) {
return true;
}
}
}
return false;
}
// utilities
public static String convertToAllNumbers(String cron) {
for (Day day : Day.values()) {
if (cron.contains(day.name())) {
cron = cron.replaceAll(day.name(), "" + (day.ordinal() + 1));
}
}
for (Month month : Month.values()) {
if (cron.contains(month.name())) {
cron = cron.replaceAll(month.name(), "" + (month.ordinal() + 1));
}
}
return cron;
}
public static boolean validateRegex(String regex, String input) {
return Pattern.matches(regex, input);
}
// Regexes
private static String zero59() {
return "^([0-5]?[0-9])$";
}
private static String zero23() {
return "^([0-9]|1[0-9]|2[0-3])$";
}
private static String one31() {
return "^([1-9]|1[0-9]|2[0-9]|3[0-1])$";
}
private static String one12() {
return "^([1-9]|1[0-2])$";
}
private static String one7() {
return "^([1-7])$";
}
private static String yearRange() {
return "^[0-9]{1,4}-[0-9]{1,4}$";
}
private static String year() {
return "[0-9]{1,4}";
}
}
It looks like a lot of code but that's just because I split the stuff into many smaller methods to keep things clear in my head.
I'll post the test cases I used just now.