common: Support parsing in DecimalUnitFormat
authorPatrick Tasse <patrick.tasse@gmail.com>
Wed, 30 Nov 2016 20:27:51 +0000 (15:27 -0500)
committerPatrick Tasse <patrick.tasse@gmail.com>
Fri, 2 Dec 2016 23:16:44 +0000 (18:16 -0500)
This allows parsing strings which represent a number with an optional
unit and SI-prefix, such as "1.2 kb" (1200), "1.2" (1.2), "1.2 m"
(0.0012), "12 ns" (0.000000012), etc.

Change-Id: Icd00870903bd22da6d322237ab1302276072217a
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/86097
Reviewed-by: Hudson CI
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/format/DecimalUnitFormatErrorTest.java
common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/format/DecimalUnitFormatTest.java
common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/format/DecimalUnitFormat.java

index 6a76898c836b5098d606899a9cbb2746bd04a357..8450416a89bfbfb9c346480f35aba85c5b64c14a 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 EfficiOS inc, Michael Jeanson
+ * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -9,7 +9,10 @@
 
 package org.eclipse.tracecompass.common.core.tests.format;
 
+import static org.junit.Assert.assertEquals;
+
 import java.text.Format;
+import java.text.ParseException;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
@@ -25,10 +28,48 @@ public class DecimalUnitFormatErrorTest {
     private static final @NonNull Format FORMATTER = new DecimalUnitFormat();
 
     /**
-     * Test an illegal argument
+     * Test format with an illegal argument
      */
     @Test(expected = IllegalArgumentException.class)
-    public void testException() {
-        FORMATTER.format(new String("Toto"));
+    public void testFormatIllegalArgument() {
+        FORMATTER.format("Toto");
+    }
+
+    /**
+     * Test parsing a string that is not a number
+     * @throws ParseException if the string cannot be parsed
+     */
+    @Test(expected = ParseException.class)
+    public void testParseNotANumber() throws ParseException {
+        FORMATTER.parseObject("Toto");
+    }
+
+    /**
+     * Test parsing a number with a unit
+     * @throws ParseException if the string cannot be parsed
+     */
+    @Test
+    public void testParseWithUnit() throws ParseException {
+        FORMATTER.parseObject("1.2 s");
+    }
+
+    /**
+     * Test parsing a number with a prefix and a unit
+     * @throws ParseException if the string cannot be parsed
+     */
+    @Test
+    public void testParsePrefixWithUnitAndPrefix() throws ParseException {
+        assertEquals(0.0012, FORMATTER.parseObject("1.2 ms"));
+    }
+
+    /**
+     * Test parsing a special Double number with a prefix
+     * @throws ParseException if the string cannot be parsed
+     */
+    @Test
+    public void testParseSpecialWithPrefix() throws ParseException {
+        assertEquals(Double.POSITIVE_INFINITY, FORMATTER.parseObject("∞ k"));
+        assertEquals(Double.NEGATIVE_INFINITY, FORMATTER.parseObject("-∞ p"));
+        assertEquals(Double.NaN, FORMATTER.parseObject("�M"));
     }
 }
index d343979c701684ca08334360c2bb7e286d080634..11595b1039c5293597074fe2822e7e87be57816b 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 EfficiOS inc, Michael Jeanson
+ * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -12,6 +12,7 @@ package org.eclipse.tracecompass.common.core.tests.format;
 import static org.junit.Assert.assertEquals;
 
 import java.text.Format;
+import java.text.ParseException;
 import java.util.Arrays;
 
 import org.eclipse.jdt.annotation.NonNull;
@@ -32,19 +33,23 @@ public class DecimalUnitFormatTest {
     private static final @NonNull Format FORMATTER = new DecimalUnitFormat();
 
     private final @NonNull Number fNumValue;
-    private final @NonNull String fExpected;
+    private final @NonNull String fStringValue;
+    private final @NonNull Number fParseValue;
 
     /**
      * Constructor
      *
-     * @param value
-     *            The numeric value to format
-     * @param expected
-     *            The expected formatted result
+     * @param numValue
+     *            The numeric value
+     * @param stringValue
+     *            The string value
+     * @param parseValue
+     *            The parse value of the string value
      */
-    public DecimalUnitFormatTest(@NonNull Number value, @NonNull String expected) {
-        fNumValue = value;
-        fExpected = expected;
+    public DecimalUnitFormatTest(@NonNull Number numValue, @NonNull String stringValue, @NonNull Number parseValue) {
+        fNumValue = numValue;
+        fStringValue = stringValue;
+        fParseValue = parseValue;
     }
 
     /**
@@ -53,40 +58,43 @@ public class DecimalUnitFormatTest {
     @Parameters(name = "{index}: {0}")
     public static Iterable<Object[]> getParameters() {
         return Arrays.asList(new Object[][] {
-                { 0, "0" },
-                { 3, "3" },
-                { 975, "975" },
-                { 1000, "1 k" },
-                { 4000, "4 k" },
-                { -4000, "-4 k" },
-                { 4000L, "4 k" },
-                { 4000.0, "4 k" },
-                { 12345678, "12.3 M" },
-                { Integer.MAX_VALUE, "2.1 G" },
-                { Integer.MIN_VALUE, "-2.1 G" },
-                { Long.MAX_VALUE, "9223.4 P" },
-                { 98765432.123456, "98.8 M" },
-                { -98765432.123456, "-98.8 M" },
-                { 555555555555L, "555.6 G" },
-                { 555555555555555L, "555.6 T" },
-                { 100100000, "100.1 M" },
-                { 0.1, "100 m" },
-                { 0.001, "1 m" },
-                { 0.000001, "1 µ" },
-                { 0.000000001, "1 n" },
-                { 0.000000000001, "1 p" },
-                { -0.04, "-40 m" },
-                { 0.002, "2 m" },
-                { 0.0555, "55.5 m" },
-                { 0.0004928373928, "492.8 µ" },
-                { 0.000000251, "251 n"},
-                { 0.000000000043, "43 p"},
-                { 0.000000045643, "45.6 n"},
-                { Double.MAX_VALUE, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 P" },
-                { Double.POSITIVE_INFINITY, "∞" },
-                { Double.MIN_NORMAL, "0" },
-                { Double.NEGATIVE_INFINITY, "-∞" },
-                { Double.NaN, "�" },
+                { 3, "3", 3L },
+                { 5.6, "5.6", 5.6 },
+                { 1.234567, "1.2", 1.2 },
+                { 1.01, "1", 1L },
+                { 975, "975", 975L },
+                { 1000, "1 k", 1000L },
+                { 4000, "4 k", 4000L },
+                { -4000, "-4 k", -4000L },
+                { 4000L, "4 k", 4000L },
+                { 4000.0, "4 k", 4000L },
+                { 12345678, "12.3 M", 12300000L },
+                { Integer.MAX_VALUE, "2.1 G", 2100000000L },
+                { Integer.MIN_VALUE, "-2.1 G", -2100000000L },
+                { Long.MAX_VALUE, "9223.4 P", 9.2234E18 },
+                { 98765432.123456, "98.8 M", 98800000L },
+                { -98765432.123456, "-98.8 M", -98800000L },
+                { 555555555555L, "555.6 G", 555600000000L },
+                { 555555555555555L, "555.6 T", 555600000000000L },
+                { 100100000, "100.1 M", 100100000L },
+                { 0.1, "100 m", 0.1 },
+                { 0.001, "1 m", 0.001 },
+                { 0.000001, "1 µ", 0.000001 },
+                { 0.000000001, "1 n", 0.000000001 },
+                { 0.000000000001, "1 p", 0.000000000001 },
+                { 0.0000000000001, "0", 0L },
+                { -0.04, "-40 m", -0.04 },
+                { 0.002, "2 m", 0.002 },
+                { 0.0555, "55.5 m", 0.0555 },
+                { 0.0004928373928, "492.8 µ", 0.0004928 },
+                { 0.000000251, "251 n", 0.000000251 },
+                { 0.000000000043, "43 p", 0.000000000043 },
+                { 0.000000045643, "45.6 n", 0.0000000456 },
+                { Double.MAX_VALUE, "1.7976931348623157E308", 1.7976931348623157E308 },
+                { Double.POSITIVE_INFINITY, "∞", Double.POSITIVE_INFINITY },
+                { Double.MIN_NORMAL, "0", 0L },
+                { Double.NEGATIVE_INFINITY, "-∞", Double.NEGATIVE_INFINITY },
+                { Double.NaN, "�", Double.NaN }
         });
     }
 
@@ -95,6 +103,17 @@ public class DecimalUnitFormatTest {
      */
     @Test
     public void testFormat() {
-        assertEquals("format value", fExpected, FORMATTER.format(fNumValue));
+        assertEquals("format value", fStringValue, FORMATTER.format(fNumValue));
+    }
+
+    /**
+     * Test the {@link Format#parseObject(String)} method
+     *
+     * @throws ParseException
+     *             if the string cannot be parsed
+     */
+    @Test
+    public void testParseObject() throws ParseException {
+        assertEquals("parseObject value", fParseValue, FORMATTER.parseObject(fStringValue));
     }
 }
index 69f63539ff831e365b551028537057be682c0ccf..75e4b0a8d5a4e9ae4901e0e618ca6274f79a60e2 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson
+ * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
@@ -9,10 +9,15 @@
 
 package org.eclipse.tracecompass.common.core.format;
 
+import java.math.BigDecimal;
 import java.text.DecimalFormat;
 import java.text.FieldPosition;
 import java.text.Format;
+import java.text.NumberFormat;
 import java.text.ParsePosition;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
 
 /**
  * Provides a formatter for decimal numbers with International System of Units
@@ -49,6 +54,18 @@ public class DecimalUnitFormat extends Format {
     private static final double NANO  = 0.000000001;
     private static final double PICO  = 0.000000000001;
 
+    /* Map of prefix to exponent */
+    private static final Map<String, Integer> PREFIX_MAP = ImmutableMap.<String, Integer>builder()
+            .put(KILO_PREFIX, +3)
+            .put(MEGA_PREFIX, +6)
+            .put(GIGA_PREFIX, +9)
+            .put(TERA_PREFIX, +12)
+            .put(PETA_PREFIX, +15)
+            .put(MILLI_PREFIX, -3)
+            .put(MICRO_PREFIX, -6)
+            .put(NANO_PREFIX, -9)
+            .put(PICO_PREFIX, -12)
+            .build();
     private static final Format FORMAT = new DecimalFormat("#.#"); //$NON-NLS-1$
     private final double fFactor;
 
@@ -86,6 +103,9 @@ public class DecimalUnitFormat extends Format {
             }
 
             if (abs >= 1) {
+                if (abs > Long.MAX_VALUE) {
+                    return toAppendTo.append(num);
+                }
                 if (abs >= PETA) {
                     return toAppendTo.append(FORMAT.format(value / PETA)).append(' ').append(PETA_PREFIX);
                 }
@@ -121,8 +141,30 @@ public class DecimalUnitFormat extends Format {
         throw new IllegalArgumentException("Cannot format given Object as a Number: " + obj); //$NON-NLS-1$
     }
 
+    /**
+     * @since 2.1
+     */
     @Override
-    public Object parseObject(String source, ParsePosition pos) {
-        return (source == null ? "" : source); //$NON-NLS-1$
+    public Number parseObject(String source, ParsePosition pos) {
+        Number number = NumberFormat.getInstance().parse(source, pos);
+        if (number == null) {
+            return null;
+        }
+        String unit = source.substring(pos.getIndex()).trim();
+        Integer exponent = null;
+        if (!unit.isEmpty()) {
+            String prefix = unit.substring(0, 1);
+            exponent = PREFIX_MAP.get(prefix);
+        }
+        if (exponent != null && Double.isFinite(number.doubleValue())) {
+            BigDecimal bd = new BigDecimal(number.toString());
+            bd = bd.movePointRight(exponent.intValue());
+            if (bd.remainder(BigDecimal.ONE).equals(BigDecimal.ZERO) &&
+                    bd.abs().compareTo(new BigDecimal(Long.MAX_VALUE)) < 0) {
+                return bd.longValue();
+            }
+            return bd.doubleValue();
+        }
+        return number;
     }
 }
This page took 0.031255 seconds and 5 git commands to generate.