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

com.xlrit.gears.base.util.PeriodDurationHelper Maven / Gradle / Ivy

There is a newer version: 1.17.5
Show newest version
package com.xlrit.gears.base.util;

import java.time.Duration;
import java.time.Period;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.threeten.extra.PeriodDuration;

import static com.google.common.base.Preconditions.checkArgument;

public class PeriodDurationHelper {
	public static final String FORMAT_DEFAULT = "long";

	private final Pattern pattern;
	private final Map unitToIndex;
	private final String[] unitsShort;
	private final String[] unitsPlural;
	private final String[] unitsSingular;
	private final String join;

	private PeriodDurationHelper(Locale locale) {
		this.pattern = createPattern(locale);
		this.unitToIndex = createUnitToIndexMap(locale);
		this.unitsShort = PeriodDurationHelper.getUnitsShort(locale);
		this.unitsPlural = PeriodDurationHelper.getUnitsPlural(locale);
		this.unitsSingular = PeriodDurationHelper.getUnitsSingular(locale);
		this.join = getJoin(locale);
	}

	public boolean isValid(String input) {
		Matcher matcher = pattern.matcher(input);
		String unmatched = matcher.replaceAll("");
		return unmatched
			.replaceAll(" " + join + " ", "")
			.replaceAll("\\s*", "")
			.isEmpty();
	}

	public PeriodDuration parse(String input) {
		if (input.startsWith("P")) {
			return PeriodDuration.parse(input);
		}

		if (!isValid(input))
			throw new DateTimeParseException("Text cannot be parsed to PeriodDuration", input, 0);

		int[] values = new int[NR_UNITS];
		Matcher matcher = pattern.matcher(input);
		while (matcher.find()) {
			String value = matcher.group(1);
			String unit  = matcher.group(2);

			try {
				Integer index = unitToIndex.get(unit);
				values[index] = Integer.parseInt(value);
			}
			catch (NumberFormatException e) {
				throw new DateTimeParseException("Text cannot be parsed to PeriodDuration", input, 0);
			}
		}

		return create(values);
	}

	public String format(PeriodDuration value) {
		return format(value, FORMAT_DEFAULT);
	}

	public String format(PeriodDuration value, String format) {
		return switch (format) {
			case "short" -> formatShort(value);
			case "long"  -> formatLong(value);
			default      -> throw new IllegalArgumentException("Unsupported format '" + format + "'");
		};
	}

	public String formatShort(PeriodDuration value) {
		Period period = value.getPeriod();
		Duration duration = value.getDuration();

		Stream partStream = Stream.of(
			formatWithShortUnit(YEAR,   period.getYears()),
			formatWithShortUnit(MONTH,  period.getMonths()),
			formatWithShortUnit(DAY,    period.getDays()),
			formatWithShortUnit(HOUR,   duration.toHours()),
			formatWithShortUnit(MINUTE, duration.toMinutesPart()),
			formatWithShortUnit(SECOND, duration.toSecondsPart()),
			formatWithShortUnit(MILLIS, duration.toMillisPart())
		);

		List parts = partStream.filter(periodStr -> !periodStr.isEmpty()).toList();
		return String.join(" ", parts);
	}

	public String formatLong(PeriodDuration value) {
		Period period = value.getPeriod();
		Duration duration = value.getDuration();

		Stream partStream = Stream.of(
			formatWithLongUnit(YEAR,   period.getYears()),
			formatWithLongUnit(MONTH,  period.getMonths()),
			formatWithLongUnit(DAY,    period.getDays()),
			formatWithLongUnit(HOUR,   duration.toHours()),
			formatWithLongUnit(MINUTE, duration.toMinutesPart()),
			formatWithLongUnit(SECOND, duration.toSecondsPart()),
			formatWithLongUnit(MILLIS, duration.toMillisPart())
		);

		List parts = partStream.filter(periodStr -> !periodStr.isEmpty()).toList();

		if (parts.size() > 1) {
			int lastIndex = parts.size() - 1;
			String initial = String.join(" ", parts.subList(0, lastIndex));
			String last = parts.get(lastIndex);
			return initial + " " + join + " " + last;
		}
		else
			return String.join(" ", parts);
	}

	private String formatWithShortUnit(int unitIndex, long value) {
		return value == 0 ? "" : value + unitsShort[unitIndex]; // note no space between value and unit
	}

	private String formatWithLongUnit(int unitIndex, long value) {
		if (value == 0) return "";
		if (value == 1) return value + " " + unitsSingular[unitIndex];
		return value + " " + unitsPlural[unitIndex];
	}

	// ================================================================================================================ //

	public static PeriodDurationHelper forLanguageTag(String languageTag) {
		return new PeriodDurationHelper(StringUtils.toLocale(languageTag));
	}

	public static PeriodDuration create(int[] values) {
		checkArgument(values.length == NR_UNITS);
		return create(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]);
	}

	public static PeriodDuration create(int years, int months, int weeks, int days, int hours, int minutes, int seconds, int millis) {
		Period period = Period.of(years, months, weeks * 7 + days);
		long totalSeconds = hours * 3600L + minutes * 60L + seconds;
		long totalMillis = totalSeconds * 1000L + millis;
		Duration duration = Duration.ofMillis(totalMillis);
		return PeriodDuration.of(period, duration);
	}

	public static String getJoin(Locale locale) {
		return switch (locale.getLanguage()) {
			case "en" -> JOIN_EN;
			case "nl" -> JOIN_NL;
			default   -> throw new IllegalArgumentException(String.format("The period parser does not support language: %s", locale.getLanguage()));
		};
	}

	public static String[] getUnits(Locale locale) {
		return switch (locale.getLanguage()) {
			case "en" -> UNITS_ALL_EN;
			case "nl" -> UNITS_ALL_NL;
			default   -> throw new IllegalArgumentException(String.format("The period parser does not support language: %s", locale.getLanguage()));
		};
	}

	public static String[] getUnitsShort(Locale locale) {
		return switch (locale.getLanguage()) {
			case "en" -> UNITS_SHORT_EN;
			case "nl" -> UNITS_SHORT_NL;
			default   -> throw new IllegalArgumentException(String.format("The period parser does not support language: %s", locale.getLanguage()));
		};
	}

	public static String[] getUnitsSingular(Locale locale) {
		return switch (locale.getLanguage()) {
			case "en" -> UNITS_SINGULAR_EN;
			case "nl" -> UNITS_SINGULAR_NL;
			default   -> throw new IllegalArgumentException(String.format("The period parser does not support language: %s", locale.getLanguage()));
		};
	}

	public static String[] getUnitsPlural(Locale locale) {
		return switch (locale.getLanguage()) {
			case "en" -> UNITS_PLURAL_EN;
			case "nl" -> UNITS_PLURAL_NL;
			default   -> throw new IllegalArgumentException(String.format("The period parser does not support language: %s", locale.getLanguage()));
		};
	}

	private static Map createUnitToIndexMap(Locale locale) {
		String[] sjort    = getUnitsShort(locale);
		String[] singular = getUnitsSingular(locale);
		String[] plural   = getUnitsPlural(locale);

		if (!(sjort.length == singular.length && singular.length == plural.length))
			throw new IllegalStateException("Unit arrays must all have the same length");

		Map result = new HashMap<>();
		for (int i = 0; i < singular.length; i++) {
			result.put(sjort[i], i);
			result.put(singular[i], i);
			result.put(plural[i], i);
		}
		return result;
	}

	private static Pattern createPattern(Locale locale) {
		String[] units = getUnits(locale);
		String pattern = "(-?[0-9]+)\\s*" + "(" + String.join("|", units) + ")\\b";
		return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
	}

	private static final String[] UNITS_SHORT_EN =    { "y", "mo", "wk", "d", "h", "min", "s", "ms" };
	private static final String[] UNITS_SINGULAR_EN = { "year", "month", "week", "day", "hour", "minute", "second", "millisecond" };
	private static final String[] UNITS_PLURAL_EN   = { "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds" };
	private static final String[] UNITS_ALL_EN      = Stream.of(UNITS_SHORT_EN, UNITS_SINGULAR_EN, UNITS_PLURAL_EN).flatMap(Stream::of).toArray(String[]::new);
	private static final String   JOIN_EN           = "and";
	private static final String[] UNITS_SHORT_NL =    { "jr", "mnd", "wk", "d", "u", "min", "s", "ms" };
	private static final String[] UNITS_SINGULAR_NL = { "jaar", "maand", "week", "dag", "uur", "minuut", "seconde", "milliseconde" };
	private static final String[] UNITS_PLURAL_NL   = { "jaar", "maanden", "weken", "dagen", "uren", "minuten", "seconden", "milliseconden" };
	private static final String[] UNITS_ALL_NL      = Stream.of(UNITS_SHORT_NL, UNITS_SINGULAR_NL, UNITS_PLURAL_NL).flatMap(Stream::of).toArray(String[]::new);
	private static final String   JOIN_NL           = "en";

	public static final int YEAR     = 0;
	public static final int MONTH    = 1;
	public static final int WEEK     = 2;
	public static final int DAY      = 3;
	public static final int HOUR     = 4;
	public static final int MINUTE   = 5;
	public static final int SECOND   = 6;
	public static final int MILLIS   = 7;
	public static final int NR_UNITS = 8;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy