spring PeriodStyle 源码

  • 2022-08-12
  • 浏览 (498)

springboot PeriodStyle 代码

文件路径:/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java

/*
 * Copyright 2002-2020 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.boot.convert;

import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.util.Assert;

/**
 * A standard set of {@link Period} units.
 *
 * @author Eddú Meléndez
 * @author Edson Chávez
 * @since 2.3.0
 * @see Period
 */
public enum PeriodStyle {

	/**
	 * Simple formatting, for example '1d'.
	 */
	SIMPLE("^" + "(?:([-+]?[0-9]+)Y)?" + "(?:([-+]?[0-9]+)M)?" + "(?:([-+]?[0-9]+)W)?" + "(?:([-+]?[0-9]+)D)?" + "$",
			Pattern.CASE_INSENSITIVE) {

		@Override
		public Period parse(String value, ChronoUnit unit) {
			try {
				if (NUMERIC.matcher(value).matches()) {
					return Unit.fromChronoUnit(unit).parse(value);
				}
				Matcher matcher = matcher(value);
				Assert.state(matcher.matches(), "Does not match simple period pattern");
				Assert.isTrue(hasAtLeastOneGroupValue(matcher), () -> "'" + value + "' is not a valid simple period");
				int years = parseInt(matcher, 1);
				int months = parseInt(matcher, 2);
				int weeks = parseInt(matcher, 3);
				int days = parseInt(matcher, 4);
				return Period.of(years, months, Math.addExact(Math.multiplyExact(weeks, 7), days));
			}
			catch (Exception ex) {
				throw new IllegalArgumentException("'" + value + "' is not a valid simple period", ex);
			}
		}

		boolean hasAtLeastOneGroupValue(Matcher matcher) {
			for (int i = 0; i < matcher.groupCount(); i++) {
				if (matcher.group(i + 1) != null) {
					return true;
				}
			}
			return false;
		}

		private int parseInt(Matcher matcher, int group) {
			String value = matcher.group(group);
			return (value != null) ? Integer.parseInt(value) : 0;
		}

		@Override
		protected boolean matches(String value) {
			return NUMERIC.matcher(value).matches() || matcher(value).matches();
		}

		@Override
		public String print(Period value, ChronoUnit unit) {
			if (value.isZero()) {
				return Unit.fromChronoUnit(unit).print(value);
			}
			StringBuilder result = new StringBuilder();
			append(result, value, Unit.YEARS);
			append(result, value, Unit.MONTHS);
			append(result, value, Unit.DAYS);
			return result.toString();
		}

		private void append(StringBuilder result, Period value, Unit unit) {
			if (!unit.isZero(value)) {
				result.append(unit.print(value));
			}
		}

	},

	/**
	 * ISO-8601 formatting.
	 */
	ISO8601("^[+-]?P.*$", 0) {

		@Override
		public Period parse(String value, ChronoUnit unit) {
			try {
				return Period.parse(value);
			}
			catch (Exception ex) {
				throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 period", ex);
			}
		}

		@Override
		public String print(Period value, ChronoUnit unit) {
			return value.toString();
		}

	};

	private static final Pattern NUMERIC = Pattern.compile("^[-+]?[0-9]+$");

	private final Pattern pattern;

	PeriodStyle(String pattern, int flags) {
		this.pattern = Pattern.compile(pattern, flags);
	}

	protected boolean matches(String value) {
		return this.pattern.matcher(value).matches();
	}

	protected final Matcher matcher(String value) {
		return this.pattern.matcher(value);
	}

	/**
	 * Parse the given value to a Period.
	 * @param value the value to parse
	 * @return a period
	 */
	public Period parse(String value) {
		return parse(value, null);
	}

	/**
	 * Parse the given value to a period.
	 * @param value the value to parse
	 * @param unit the period unit to use if the value doesn't specify one ({@code null}
	 * will default to d)
	 * @return a period
	 */
	public abstract Period parse(String value, ChronoUnit unit);

	/**
	 * Print the specified period.
	 * @param value the value to print
	 * @return the printed result
	 */
	public String print(Period value) {
		return print(value, null);
	}

	/**
	 * Print the specified period using the given unit.
	 * @param value the value to print
	 * @param unit the value to use for printing
	 * @return the printed result
	 */
	public abstract String print(Period value, ChronoUnit unit);

	/**
	 * Detect the style then parse the value to return a period.
	 * @param value the value to parse
	 * @return the parsed period
	 * @throws IllegalArgumentException if the value is not a known style or cannot be
	 * parsed
	 */
	public static Period detectAndParse(String value) {
		return detectAndParse(value, null);
	}

	/**
	 * Detect the style then parse the value to return a period.
	 * @param value the value to parse
	 * @param unit the period unit to use if the value doesn't specify one ({@code null}
	 * will default to ms)
	 * @return the parsed period
	 * @throws IllegalArgumentException if the value is not a known style or cannot be
	 * parsed
	 */
	public static Period detectAndParse(String value, ChronoUnit unit) {
		return detect(value).parse(value, unit);
	}

	/**
	 * Detect the style from the given source value.
	 * @param value the source value
	 * @return the period style
	 * @throws IllegalArgumentException if the value is not a known style
	 */
	public static PeriodStyle detect(String value) {
		Assert.notNull(value, "Value must not be null");
		for (PeriodStyle candidate : values()) {
			if (candidate.matches(value)) {
				return candidate;
			}
		}
		throw new IllegalArgumentException("'" + value + "' is not a valid period");
	}

	private enum Unit {

		/**
		 * Days, represented by suffix {@code d}.
		 */
		DAYS(ChronoUnit.DAYS, "d", Period::getDays, Period::ofDays),

		/**
		 * Weeks, represented by suffix {@code w}.
		 */
		WEEKS(ChronoUnit.WEEKS, "w", null, Period::ofWeeks),

		/**
		 * Months, represented by suffix {@code m}.
		 */
		MONTHS(ChronoUnit.MONTHS, "m", Period::getMonths, Period::ofMonths),

		/**
		 * Years, represented by suffix {@code y}.
		 */
		YEARS(ChronoUnit.YEARS, "y", Period::getYears, Period::ofYears);

		private final ChronoUnit chronoUnit;

		private final String suffix;

		private final Function<Period, Integer> intValue;

		private final Function<Integer, Period> factory;

		Unit(ChronoUnit chronoUnit, String suffix, Function<Period, Integer> intValue,
				Function<Integer, Period> factory) {
			this.chronoUnit = chronoUnit;
			this.suffix = suffix;
			this.intValue = intValue;
			this.factory = factory;
		}

		private Period parse(String value) {
			return this.factory.apply(Integer.parseInt(value));
		}

		private String print(Period value) {
			return intValue(value) + this.suffix;
		}

		private boolean isZero(Period value) {
			return intValue(value) == 0;
		}

		private int intValue(Period value) {
			Assert.notNull(this.intValue, () -> "intValue cannot be extracted from " + this.name());
			return this.intValue.apply(value);
		}

		private static Unit fromChronoUnit(ChronoUnit chronoUnit) {
			if (chronoUnit == null) {
				return Unit.DAYS;
			}
			for (Unit candidate : values()) {
				if (candidate.chronoUnit == chronoUnit) {
					return candidate;
				}
			}
			throw new IllegalArgumentException("Unsupported unit " + chronoUnit);
		}

	}

}

相关信息

spring 源码目录

相关文章

spring ApplicationConversionService 源码

spring ArrayToDelimitedStringConverter 源码

spring CharArrayFormatter 源码

spring CharSequenceToObjectConverter 源码

spring CollectionToDelimitedStringConverter 源码

spring DataSizeUnit 源码

spring DelimitedStringToArrayConverter 源码

spring DelimitedStringToCollectionConverter 源码

spring Delimiter 源码

spring DurationFormat 源码

0  赞