You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/05/07 02:12:25 UTC

[james-project] 01/22: JAMES-3140 Add a way of parsing Size units

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 <bt...@linagora.com>
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: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org