spring BitsCronField 源码

  • 2022-08-08
  • 浏览 (745)

spring BitsCronField 代码

文件路径:/spring-context/src/main/java/org/springframework/scheduling/support/BitsCronField.java

/*
 * Copyright 2002-2021 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.scheduling.support;

import java.time.DateTimeException;
import java.time.temporal.Temporal;
import java.time.temporal.ValueRange;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * Efficient bitwise-operator extension of {@link CronField}.
 * Created using the {@code parse*} methods.
 *
 * @author Arjen Poutsma
 * @since 5.3
 */
final class BitsCronField extends CronField {

	private static final long MASK = 0xFFFFFFFFFFFFFFFFL;


	@Nullable
	private static BitsCronField zeroNanos = null;


	// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
	private long bits;


	private BitsCronField(Type type) {
		super(type);
	}

	/**
	 * Return a {@code BitsCronField} enabled for 0 nanoseconds.
	 */
	public static BitsCronField zeroNanos() {
		if (zeroNanos == null) {
			BitsCronField field = new BitsCronField(Type.NANO);
			field.setBit(0);
			zeroNanos = field;
		}
		return zeroNanos;
	}

	/**
	 * Parse the given value into a seconds {@code BitsCronField}, the first entry of a cron expression.
	 */
	public static BitsCronField parseSeconds(String value) {
		return parseField(value, Type.SECOND);
	}

	/**
	 * Parse the given value into a minutes {@code BitsCronField}, the second entry of a cron expression.
	 */
	public static BitsCronField parseMinutes(String value) {
		return BitsCronField.parseField(value, Type.MINUTE);
	}

	/**
	 * Parse the given value into an hours {@code BitsCronField}, the third entry of a cron expression.
	 */
	public static BitsCronField parseHours(String value) {
		return BitsCronField.parseField(value, Type.HOUR);
	}

	/**
	 * Parse the given value into a days of months {@code BitsCronField}, the fourth entry of a cron expression.
	 */
	public static BitsCronField parseDaysOfMonth(String value) {
		return parseDate(value, Type.DAY_OF_MONTH);
	}

	/**
	 * Parse the given value into a month {@code BitsCronField}, the fifth entry of a cron expression.
	 */
	public static BitsCronField parseMonth(String value) {
		return BitsCronField.parseField(value, Type.MONTH);
	}

	/**
	 * Parse the given value into a days of week {@code BitsCronField}, the sixth entry of a cron expression.
	 */
	public static BitsCronField parseDaysOfWeek(String value) {
		BitsCronField result = parseDate(value, Type.DAY_OF_WEEK);
		if (result.getBit(0)) {
			// cron supports 0 for Sunday; we use 7 like java.time
			result.setBit(7);
			result.clearBit(0);
		}
		return result;
	}


	private static BitsCronField parseDate(String value, BitsCronField.Type type) {
		if (value.equals("?")) {
			value = "*";
		}
		return BitsCronField.parseField(value, type);
	}

	private static BitsCronField parseField(String value, Type type) {
		Assert.hasLength(value, "Value must not be empty");
		Assert.notNull(type, "Type must not be null");
		try {
			BitsCronField result = new BitsCronField(type);
			String[] fields = StringUtils.delimitedListToStringArray(value, ",");
			for (String field : fields) {
				int slashPos = field.indexOf('/');
				if (slashPos == -1) {
					ValueRange range = parseRange(field, type);
					result.setBits(range);
				}
				else {
					String rangeStr = field.substring(0, slashPos);
					String deltaStr = field.substring(slashPos + 1);
					ValueRange range = parseRange(rangeStr, type);
					if (rangeStr.indexOf('-') == -1) {
						range = ValueRange.of(range.getMinimum(), type.range().getMaximum());
					}
					int delta = Integer.parseInt(deltaStr);
					if (delta <= 0) {
						throw new IllegalArgumentException("Incrementer delta must be 1 or higher");
					}
					result.setBits(range, delta);
				}
			}
			return result;
		}
		catch (DateTimeException | IllegalArgumentException ex) {
			String msg = ex.getMessage() + " '" + value + "'";
			throw new IllegalArgumentException(msg, ex);
		}
	}

	private static ValueRange parseRange(String value, Type type) {
		if (value.equals("*")) {
			return type.range();
		}
		else {
			int hyphenPos = value.indexOf('-');
			if (hyphenPos == -1) {
				int result = type.checkValidValue(Integer.parseInt(value));
				return ValueRange.of(result, result);
			}
			else {
				int min = Integer.parseInt(value, 0, hyphenPos, 10);
				int max = Integer.parseInt(value, hyphenPos + 1, value.length(), 10);
				min = type.checkValidValue(min);
				max = type.checkValidValue(max);
				if (type == Type.DAY_OF_WEEK && min == 7) {
					// If used as a minimum in a range, Sunday means 0 (not 7)
					min = 0;
				}
				return ValueRange.of(min, max);
			}
		}
	}

	@Nullable
	@Override
	public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
		int current = type().get(temporal);
		int next = nextSetBit(current);
		if (next == -1) {
			temporal = type().rollForward(temporal);
			next = nextSetBit(0);
		}
		if (next == current) {
			return temporal;
		}
		else {
			int count = 0;
			current = type().get(temporal);
			while (current != next && count++ < CronExpression.MAX_ATTEMPTS) {
				temporal = type().elapseUntil(temporal, next);
				current = type().get(temporal);
				next = nextSetBit(current);
				if (next == -1) {
					temporal = type().rollForward(temporal);
					next = nextSetBit(0);
				}
			}
			if (count >= CronExpression.MAX_ATTEMPTS) {
				return null;
			}
			return type().reset(temporal);
		}
	}

	boolean getBit(int index) {
		return (this.bits & (1L << index)) != 0;
	}

	private int nextSetBit(int fromIndex) {
		long result = this.bits & (MASK << fromIndex);
		if (result != 0) {
			return Long.numberOfTrailingZeros(result);
		}
		else {
			return -1;
		}

	}

	private void setBits(ValueRange range) {
		if (range.getMinimum() == range.getMaximum()) {
			setBit((int) range.getMinimum());
		}
		else {
			long minMask = MASK << range.getMinimum();
			long maxMask = MASK >>> - (range.getMaximum() + 1);
			this.bits |= (minMask & maxMask);
		}
	}

	private void setBits(ValueRange range, int delta) {
		if (delta == 1) {
			setBits(range);
		}
		else {
			for (int i = (int) range.getMinimum(); i <= range.getMaximum(); i += delta) {
				setBit(i);
			}
		}
	}

	private void setBit(int index) {
		this.bits |= (1L << index);
	}

	private void clearBit(int index) {
		this.bits &=  ~(1L << index);
	}

	@Override
	public int hashCode() {
		return Long.hashCode(this.bits);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof BitsCronField other)) {
			return false;
		}
		return type() == other.type() && this.bits == other.bits;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder(type().toString());
		builder.append(" {");
		int i = nextSetBit(0);
		if (i != -1) {
			builder.append(i);
			i = nextSetBit(i+1);
			while (i != -1) {
				builder.append(", ");
				builder.append(i);
				i = nextSetBit(i+1);
			}
		}
		builder.append('}');
		return builder.toString();
	}

}

相关信息

spring 源码目录

相关文章

spring CompositeCronField 源码

spring CronExpression 源码

spring CronField 源码

spring CronSequenceGenerator 源码

spring CronTrigger 源码

spring DelegatingErrorHandlingRunnable 源码

spring MethodInvokingRunnable 源码

spring PeriodicTrigger 源码

spring QuartzCronField 源码

spring ScheduledMethodRunnable 源码

0  赞