This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit e1e8c1234327da80a7dc22f5a0ddc11f1e4c0475 Author: Benoit Tellier <[email protected]> AuthorDate: Tue Apr 28 08:42:20 2020 +0700 JAMES-3140 Add a way of parsing Size units Reuse amount/unit separation of DurationParser. --- .../java/org/apache/james/util/DurationParser.java | 35 ++---------- .../java/org/apache/james/util/SizeFormat.java | 25 ++++++++- .../java/org/apache/james/util/UnitParser.java | 64 ++++++++++++++++++++++ .../java/org/apache/james/util/SizeFormatTest.java | 60 ++++++++++++++++++++ 4 files changed, 153 insertions(+), 31 deletions(-) diff --git a/server/container/util/src/main/java/org/apache/james/util/DurationParser.java b/server/container/util/src/main/java/org/apache/james/util/DurationParser.java index a0d61a6..6194501 100644 --- a/server/container/util/src/main/java/org/apache/james/util/DurationParser.java +++ b/server/container/util/src/main/java/org/apache/james/util/DurationParser.java @@ -23,22 +23,11 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; public class DurationParser { - - private static final String PATTERN_STRING = "\\s*(-?[0-9]+)\\s*([a-z,A-Z]*)\\s*"; - private static final int AMOUNT = 1; - private static final int UNIT = 2; - - private static Pattern PATTERN = Pattern.compile(PATTERN_STRING); - private enum Unit { MILLI_SECONDS(ImmutableList.of("ms", "msec", "msecs"), ChronoUnit.MILLIS), SECONDS(ImmutableList.of("s", "sec", "secs", "second", "seconds"), ChronoUnit.SECONDS), @@ -86,18 +75,11 @@ public class DurationParser { } public static Duration parse(String rawString, ChronoUnit defaultUnit) throws NumberFormatException { - Matcher res = PATTERN.matcher(rawString); - if (res.matches()) { - String unitAsString = res.group(UNIT); - String amountAsString = res.group(AMOUNT); - if (amountAsString != null && unitAsString != null) { - long time = Integer.parseInt(res.group(AMOUNT).trim()); - Duration unitAsDuration = parseUnitAsDuration(unitAsString).orElse(defaultUnit.getDuration()); - - return computeDuration(unitAsDuration, time); - } - } - throw new NumberFormatException("The supplied String is not a supported format " + rawString); + UnitParser.ParsingResult parsingResult = UnitParser.parse(rawString); + Duration unitAsDuration = parsingResult.getUnit() + .map(s -> Unit.parse(s).getDuration()) + .orElse(defaultUnit.getDuration()); + return computeDuration(unitAsDuration, parsingResult.getNumber()); } private static Duration computeDuration(Duration unitAsDuration, long time) { @@ -105,11 +87,4 @@ public class DurationParser { return unitAsDuration.multipliedBy(time); } - - private static Optional<Duration> parseUnitAsDuration(String unit) { - if (Strings.isNullOrEmpty(unit)) { - return Optional.empty(); - } - return Optional.of(Unit.parse(unit).getDuration()); - } } diff --git a/server/container/util/src/main/java/org/apache/james/util/SizeFormat.java b/server/container/util/src/main/java/org/apache/james/util/SizeFormat.java index c5293a8..c71ad3c 100644 --- a/server/container/util/src/main/java/org/apache/james/util/SizeFormat.java +++ b/server/container/util/src/main/java/org/apache/james/util/SizeFormat.java @@ -24,7 +24,9 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.Arrays; import java.util.Locale; +import java.util.Optional; import org.apache.commons.io.FileUtils; @@ -59,6 +61,12 @@ public class SizeFormat { private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.US); private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.##", DECIMAL_FORMAT_SYMBOLS); + public static Optional<Unit> of(String rawValue) { + return Arrays.stream(values()) + .filter(unit -> unit.notation.equals(rawValue)) + .findAny(); + } + private final BigInteger bytesCount; private final String notation; @@ -67,6 +75,11 @@ public class SizeFormat { this.notation = notation; } + public long toByteCount(long value) { + return bytesCount.multiply(BigInteger.valueOf(value)) + .longValueExact(); + } + public String format(long size) { return format(new BigDecimal(size)); } @@ -76,7 +89,7 @@ public class SizeFormat { } public BigDecimal scaleToUnit(BigDecimal sizeAsDecimal) { - return sizeAsDecimal.divide(new BigDecimal((bytesCount)), SCALE, RoundingMode.FLOOR); + return sizeAsDecimal.divide(new BigDecimal(bytesCount), SCALE, RoundingMode.FLOOR); } private String asString(BigDecimal bigDecimal) { @@ -90,4 +103,14 @@ public class SizeFormat { return Unit.locateUnit(bytesCount) .format(bytesCount); } + + public static long parseAsByteCount(String bytesWithUnit) { + UnitParser.ParsingResult parsingResult = UnitParser.parse(bytesWithUnit); + Unit unit = parsingResult.getUnit() + .map(rawValue -> Unit.of(rawValue) + .orElseThrow(() -> new IllegalArgumentException("Unknown unit " + rawValue))) + .orElse(Unit.Byte); + + return unit.toByteCount(parsingResult.getNumber()); + } } diff --git a/server/container/util/src/main/java/org/apache/james/util/UnitParser.java b/server/container/util/src/main/java/org/apache/james/util/UnitParser.java new file mode 100644 index 0000000..2f6e7d7 --- /dev/null +++ b/server/container/util/src/main/java/org/apache/james/util/UnitParser.java @@ -0,0 +1,64 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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 org.apache.james.util; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.base.Strings; + +class UnitParser { + static class ParsingResult { + private final long number; + private final Optional<String> unit; + + ParsingResult(long number, Optional<String> unit) { + this.number = number; + this.unit = unit; + } + + public long getNumber() { + return number; + } + + public Optional<String> getUnit() { + return unit; + } + } + + private static final String PATTERN_STRING = "\\s*(-?[0-9]+)\\s*([a-z,A-Z]*)\\s*"; + private static final int AMOUNT = 1; + private static final int UNIT = 2; + + private static Pattern PATTERN = Pattern.compile(PATTERN_STRING); + + static ParsingResult parse(String rawString) throws NumberFormatException { + Matcher res = PATTERN.matcher(rawString); + if (res.matches()) { + String unitAsString = res.group(UNIT); + String amountAsString = res.group(AMOUNT); + long amount = Integer.parseInt(amountAsString.trim()); + + return new ParsingResult(amount, Optional.of(unitAsString).filter(s -> !Strings.isNullOrEmpty(s))); + } + throw new NumberFormatException("Supplied value do not follow the unit format (number optionally suffixed with a string representing the unit"); + } +} diff --git a/server/container/util/src/test/java/org/apache/james/util/SizeFormatTest.java b/server/container/util/src/test/java/org/apache/james/util/SizeFormatTest.java index 03c5a24..436e0c5 100644 --- a/server/container/util/src/test/java/org/apache/james/util/SizeFormatTest.java +++ b/server/container/util/src/test/java/org/apache/james/util/SizeFormatTest.java @@ -86,4 +86,64 @@ class SizeFormatTest { .isEqualTo("1 TiB"); } + @Test + void parseAsByteCountShouldReturnCountWhenNoUnit() { + assertThat(SizeFormat.parseAsByteCount("36")) + .isEqualTo(36); + } + + @Test + void parseAsByteCountShouldAcceptKiB() { + assertThat(SizeFormat.parseAsByteCount("36 KiB")) + .isEqualTo(36 * 1024); + } + + @Test + void parseAsByteCountShouldAcceptZero() { + assertThat(SizeFormat.parseAsByteCount("0 KiB")) + .isEqualTo(0); + } + + @Test + void parseAsByteCountShouldThrowOnInvalidUnit() { + assertThatThrownBy(() -> SizeFormat.parseAsByteCount("0 invalid")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void parseAsByteCountShouldThrowOnMissingAmount() { + assertThatThrownBy(() -> SizeFormat.parseAsByteCount("KiB")) + .isInstanceOf(NumberFormatException.class); + } + + @Test + void parseAsByteCountShouldThrowWhenEmpty() { + assertThatThrownBy(() -> SizeFormat.parseAsByteCount("")) + .isInstanceOf(NumberFormatException.class); + } + + @Test + void parseAsByteCountShouldThrowWhenUnitDoesNotMatchCase() { + assertThatThrownBy(() -> SizeFormat.parseAsByteCount("12 KIB")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void parseAsByteCountShouldAcceptNegativeValue() { + assertThat(SizeFormat.parseAsByteCount("-36 KiB")) + .isEqualTo(-36 * 1024); + } + + @Test + void parseAsByteCountShouldAcceptMiB() { + assertThat(SizeFormat.parseAsByteCount("36 MiB")) + .isEqualTo(36 * 1024 * 1024); + } + + @Test + void parseAsByteCountShouldAcceptGiB() { + assertThat(SizeFormat.parseAsByteCount("36 GiB")) + .isEqualTo(36L * 1024L * 1024L * 1024L); + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
