com.github.kagkarlsson.shaded.cronutils.mapper.CronMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of db-scheduler Show documentation
Show all versions of db-scheduler Show documentation
Simple persistent scheduler for scheduled tasks, recurring or ad-hoc.
/*
* Copyright 2014 jmrozanec
* 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.github.kagkarlsson.shaded.cronutils.mapper;
import com.github.kagkarlsson.shaded.cronutils.Function;
import com.github.kagkarlsson.shaded.cronutils.model.Cron;
import com.github.kagkarlsson.shaded.cronutils.model.CronType;
import com.github.kagkarlsson.shaded.cronutils.model.RebootCron;
import com.github.kagkarlsson.shaded.cronutils.model.SingleCron;
import com.github.kagkarlsson.shaded.cronutils.model.definition.CronDefinition;
import com.github.kagkarlsson.shaded.cronutils.model.definition.CronDefinitionBuilder;
import com.github.kagkarlsson.shaded.cronutils.model.definition.CronNicknames;
import com.github.kagkarlsson.shaded.cronutils.model.field.CronField;
import com.github.kagkarlsson.shaded.cronutils.model.field.CronFieldName;
import com.github.kagkarlsson.shaded.cronutils.model.field.constraint.FieldConstraints;
import com.github.kagkarlsson.shaded.cronutils.model.field.constraint.FieldConstraintsBuilder;
import com.github.kagkarlsson.shaded.cronutils.model.field.definition.DayOfWeekFieldDefinition;
import com.github.kagkarlsson.shaded.cronutils.model.field.definition.FieldDefinition;
import com.github.kagkarlsson.shaded.cronutils.model.field.expression.*;
import com.github.kagkarlsson.shaded.cronutils.model.field.expression.visitor.FieldExpressionVisitorAdaptor;
import com.github.kagkarlsson.shaded.cronutils.model.field.expression.visitor.ValueMappingFieldExpressionVisitor;
import com.github.kagkarlsson.shaded.cronutils.model.field.value.FieldValue;
import com.github.kagkarlsson.shaded.cronutils.model.field.value.IntegerFieldValue;
import com.github.kagkarlsson.shaded.cronutils.model.field.value.SpecialChar;
import com.github.kagkarlsson.shaded.cronutils.utils.Preconditions;
import com.github.kagkarlsson.shaded.cronutils.utils.VisibleForTesting;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import static com.github.kagkarlsson.shaded.cronutils.model.field.expression.FieldExpression.always;
import static com.github.kagkarlsson.shaded.cronutils.model.field.expression.FieldExpression.questionMark;
public class CronMapper {
private final Map> mappings;
private final Function cronRules;
private final CronDefinition to;
/**
* Constructor.
*
* @param from - source CronDefinition;
* if null a NullPointerException will be raised
* @param to - target CronDefinition;
* if null a NullPointerException will be raised
* @param cronRules - cron rules
*/
public CronMapper(final CronDefinition from, final CronDefinition to, final Function cronRules) {
Preconditions.checkNotNull(from, "Source CronDefinition must not be null");
this.to = Preconditions.checkNotNull(to, "Destination CronDefinition must not be null");
this.cronRules = Preconditions.checkNotNull(cronRules, "CronRules must not be null");
mappings = new EnumMap<>(CronFieldName.class);
buildMappings(from, to);
}
/**
* Maps given cron to target cron definition.
*
* @param cron - Instance to be mapped;
* if null a NullPointerException will be raised
* @return new Cron instance, never null;
*/
public Cron map(final Cron cron) {
Preconditions.checkNotNull(cron, "Cron must not be null");
if(cron instanceof RebootCron){
if(this.to.getCronNicknames().contains(CronNicknames.REBOOT)){
return new RebootCron(this.to);
} else {
throw new IllegalArgumentException("The target cron definition does not support @reboot nickname");
}
}
final List fields = new ArrayList<>();
for (final CronFieldName name : CronFieldName.values()) {
if (mappings.containsKey(name)) {
final CronField field = mappings.get(name).apply(cron.retrieve(name));
if (field != null) {
fields.add(field);
}
}
}
return cronRules.apply(new SingleCron(to, fields)).validate();
}
/**
* Creates a CronMapper that maps a cron4j expression to a quartz expression.
* @return a CronMapper for mapping from cron4j to quartz
*/
public static CronMapper fromCron4jToQuartz() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J),
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
setQuestionMark()
);
}
public static CronMapper fromQuartzToCron4j() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
CronDefinitionBuilder.instanceDefinitionFor(CronType.CRON4J),
sameCron()
);
}
public static CronMapper fromQuartzToUnix() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX),
sameCron()
);
}
public static CronMapper fromUnixToQuartz() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX),
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
setQuestionMark()
);
}
public static CronMapper fromQuartzToSpring() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING),
setQuestionMark()
);
}
public static CronMapper fromSpringToQuartz() {
return new CronMapper(
CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING),
CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ),
setQuestionMark()
);
}
public static CronMapper sameCron(final CronDefinition cronDefinition) {
return new CronMapper(cronDefinition, cronDefinition, sameCron());
}
private static Function sameCron() {
return cron -> cron;
}
private static Function setQuestionMark() {
return cron -> {
final CronField dow = cron.retrieve(CronFieldName.DAY_OF_WEEK);
final CronField dom = cron.retrieve(CronFieldName.DAY_OF_MONTH);
if (dow == null && dom == null) {
return cron;
}
if (dow.getExpression() instanceof QuestionMark || dom.getExpression() instanceof QuestionMark) {
return cron;
}
final Map fields = new EnumMap<>(CronFieldName.class);
fields.putAll(cron.retrieveFieldsAsMap());
if (dow.getExpression() instanceof Always) {
fields.put(CronFieldName.DAY_OF_WEEK,
new CronField(CronFieldName.DAY_OF_WEEK, questionMark(), fields.get(CronFieldName.DAY_OF_WEEK).getConstraints()));
} else {
if (dom.getExpression() instanceof Always) {
fields.put(CronFieldName.DAY_OF_MONTH,
new CronField(CronFieldName.DAY_OF_MONTH, questionMark(), fields.get(CronFieldName.DAY_OF_MONTH).getConstraints()));
} else {
cron.validate();
}
}
return new SingleCron(cron.getCronDefinition(), new ArrayList<>(fields.values()));
};
}
/**
* Builds functions that map the fields from source CronDefinition to target.
*
* @param from - source CronDefinition
* @param to - target CronDefinition
*/
private void buildMappings(final CronDefinition from, final CronDefinition to) {
final Map sourceFieldDefinitions = getFieldDefinitions(from);
final Map destFieldDefinitions = getFieldDefinitions(to);
boolean startedDestMapping = false;
boolean startedSourceMapping = false;
for (final CronFieldName name : CronFieldName.values()) {
final FieldDefinition destinationFieldDefinition = destFieldDefinitions.get(name);
final FieldDefinition sourceFieldDefinition = sourceFieldDefinitions.get(name);
if (destinationFieldDefinition != null) {
startedDestMapping = true;
}
if (sourceFieldDefinition != null) {
startedSourceMapping = true;
}
if (startedDestMapping && destinationFieldDefinition == null) {
break;
}
//destination has fields before source definition starts. We default them to zero.
if (!startedSourceMapping && destinationFieldDefinition != null) {
mappings.put(name, returnOnZeroExpression(name));
}
//destination has fields after source definition was processed. We default them to always.
if (startedSourceMapping && sourceFieldDefinition == null && destinationFieldDefinition != null) {
mappings.put(name, returnAlwaysExpression(name));
}
if (sourceFieldDefinition == null || destinationFieldDefinition == null) {
continue;
}
if (CronFieldName.DAY_OF_WEEK.equals(name)) {
mappings.put(name, dayOfWeekMapping((DayOfWeekFieldDefinition) sourceFieldDefinition, (DayOfWeekFieldDefinition) destinationFieldDefinition));
} else if (CronFieldName.DAY_OF_MONTH.equals(name)) {
mappings.put(name, dayOfMonthMapping(sourceFieldDefinition, destinationFieldDefinition));
} else {
mappings.put(name, returnSameExpression());
}
}
}
private Map getFieldDefinitions(final CronDefinition from) {
final Map result = new EnumMap<>(CronFieldName.class);
for (final FieldDefinition fieldDefinition : from.getFieldDefinitions()) {
result.put(fieldDefinition.getFieldName(), fieldDefinition);
}
return result;
}
/**
* Creates a Function that returns same field.
*
* @return CronField -> CronField instance, never null
*/
@VisibleForTesting
static Function returnSameExpression() {
return field -> field;
}
/**
* Creates a Function that returns a On instance with zero value.
*
* @param name - Cron field name
* @return new CronField -> CronField instance, never null
*/
@VisibleForTesting
static Function returnOnZeroExpression(final CronFieldName name) {
return field -> {
final FieldConstraints constraints = FieldConstraintsBuilder.instance().forField(name).createConstraintsInstance();
return new CronField(name, new On(new IntegerFieldValue(0)), constraints);
};
}
/**
* Creates a Function that returns an Always instance.
*
* @param name - Cron field name
* @return new CronField -> CronField instance, never null
*/
@VisibleForTesting
static Function returnAlwaysExpression(final CronFieldName name) {
return field -> new CronField(name, always(), FieldConstraintsBuilder.instance().forField(name).createConstraintsInstance());
}
private static IntegerFieldValue mapDayOfWeek(DayOfWeekFieldDefinition sourceDef, DayOfWeekFieldDefinition targetDef, IntegerFieldValue fieldValue) {
return new IntegerFieldValue(ConstantsMapper.weekDayMapping(sourceDef.getMondayDoWValue(), targetDef.getMondayDoWValue(), fieldValue.getValue()));
}
private static FieldValue> mapDayOfWeek(DayOfWeekFieldDefinition sourceDef, DayOfWeekFieldDefinition targetDef, FieldValue> fieldValue) {
if (fieldValue instanceof IntegerFieldValue) {
return mapDayOfWeek(sourceDef, targetDef, (IntegerFieldValue) fieldValue);
}
return fieldValue;
}
@VisibleForTesting
static Function dayOfWeekMapping(final DayOfWeekFieldDefinition sourceDef, final DayOfWeekFieldDefinition targetDef) {
return field -> {
final FieldExpression expression = field.getExpression();
FieldExpression dest = null;
dest = expression.accept(new FieldExpressionVisitorAdaptor() {
public FieldExpression visit(Every every) {
return new Every(every.getExpression().accept(this), every.getPeriod());
}
public FieldExpression visit(On on) {
return new On(mapDayOfWeek(sourceDef, targetDef, on.getTime()), on.getSpecialChar());
}
@Override
public FieldExpression visit(Between between) {
return new Between(mapDayOfWeek(sourceDef, targetDef, between.getFrom()), mapDayOfWeek(sourceDef, targetDef, between.getTo()));
}
@Override
public FieldExpression visit(And and) {
And newAnd = new And();
for (FieldExpression expr : and.getExpressions()) {
newAnd.and(expr.accept(this));
}
return newAnd;
}
});
if (expression instanceof QuestionMark && !targetDef.getConstraints().getSpecialChars().contains(SpecialChar.QUESTION_MARK)) {
dest = always();
}
return new CronField(CronFieldName.DAY_OF_WEEK, dest, targetDef.getConstraints());
};
}
@VisibleForTesting
static Function dayOfMonthMapping(final FieldDefinition sourceDef, final FieldDefinition targetDef) {
return field -> {
final FieldExpression expression = field.getExpression();
FieldExpression dest = expression;
if (expression instanceof QuestionMark && !targetDef.getConstraints().getSpecialChars().contains(SpecialChar.QUESTION_MARK)) {
dest = always();
}
return new CronField(CronFieldName.DAY_OF_MONTH, dest, targetDef.getConstraints());
};
}
}