changeset 885:7c97a7125993

Replace DisplayableValues with a Quantity pattern implementation The existing DisplayableValues class does a few things in a rather haphazard way with responsibilities split strangely between DisplayableValues and Scale classes. Replace it with a new class called Size that implements the Quantity pattern and replace all uses of DisplayableValues with that. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-January/004972.html
author Omair Majid <omajid@redhat.com>
date Thu, 03 Jan 2013 10:23:48 -0500
parents 4b4233c91ca1
children e8357a536e22
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/core/src/main/java/com/redhat/thermostat/client/ui/BytesTickUnit.java common/core/src/main/java/com/redhat/thermostat/common/Size.java common/core/src/main/java/com/redhat/thermostat/common/locale/LocaleResources.java common/core/src/main/java/com/redhat/thermostat/common/utils/DisplayableValues.java common/core/src/main/resources/com/redhat/thermostat/common/locale/strings.properties common/core/src/test/java/com/redhat/thermostat/common/SizeTest.java common/core/src/test/java/com/redhat/thermostat/common/utils/DisplayableValuesTest.java eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java host-memory/client-swing/src/main/java/com/redhat/thermostat/host/memory/client/swing/internal/HostMemoryPanel.java host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/internal/HostOverviewController.java system-backend/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java system-backend/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/Payload.java vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/PayloadTest.java
diffstat 21 files changed, 425 insertions(+), 287 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Thu Jan 03 10:23:48 2013 -0500
@@ -42,8 +42,6 @@
 
     MISSING_INFO,
 
-    VALUE_AND_UNIT,
-
     HOST_SERVICE_UNAVAILABLE,
     VM_SERVICE_UNAVAILABLE,
     VM_CPU_SERVICE_NOT_AVAILABLE,
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java	Thu Jan 03 10:23:48 2013 -0500
@@ -46,12 +46,12 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.cli.TableRenderer;
 import com.redhat.thermostat.common.dao.VmCpuStatDAO;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
 import com.redhat.thermostat.storage.model.TimeStampedPojoComparator;
 import com.redhat.thermostat.storage.model.TimeStampedPojoCorrelator;
@@ -160,8 +160,7 @@
         int i = 0;
         for (VmMemoryStat.Generation gen : vmMemoryStat.getGenerations()) {
             for (VmMemoryStat.Space space : gen.getSpaces()) {
-                String[] displayableSize = DisplayableValues.bytes(space.getUsed());
-                memoryUsage[i] = translator.localize(LocaleResources.VALUE_AND_UNIT, displayableSize[0], displayableSize[1]);
+                memoryUsage[i] = Size.bytes(space.getUsed()).toString();
                 i++;
             }
         }
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Wed Jan 02 09:47:39 2013 -0500
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Thu Jan 03 10:23:48 2013 -0500
@@ -1,7 +1,5 @@
 MISSING_INFO = Missing Information
 
-VALUE_AND_UNIT = {0} {1}
-
 HOST_SERVICE_UNAVAILABLE = Unable to get host information (HostInfoDAO is unavailable)
 VM_SERVICE_UNAVAILABLE = Unable to get vm information (VmInfoDAO is unavailable)
 VM_CPU_SERVICE_NOT_AVAILABLE = Unable to access vm cpu information (VmCpuStats not available)
--- a/client/core/src/main/java/com/redhat/thermostat/client/ui/BytesTickUnit.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/client/core/src/main/java/com/redhat/thermostat/client/ui/BytesTickUnit.java	Thu Jan 03 10:23:48 2013 -0500
@@ -39,7 +39,7 @@
 import org.jfree.chart.axis.NumberTickUnit;
 import org.jfree.chart.axis.TickUnit;
 
-import com.redhat.thermostat.common.utils.DisplayableValues;
+import com.redhat.thermostat.common.Size;
 
 /**
  * A {@link TickUnit} that displays a byte value with an appropriate
@@ -54,7 +54,6 @@
 
     @Override
     public String valueToString(double value) {
-        String[] displayable = DisplayableValues.bytes((long) value);
-        return displayable[0] + " " + displayable[1];
+        return Size.bytes((long)value).toString();
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/Size.java	Thu Jan 03 10:23:48 2013 -0500
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+import java.util.Objects;
+
+import com.redhat.thermostat.common.locale.LocaleResources;
+import com.redhat.thermostat.common.locale.Translate;
+
+/**
+ * Represents a size (of a file, memory, or disk) with a number and a unit.
+ * <p>
+ * Once created, an instance of this class is immutable. All operations that
+ * modify this will return new objects.
+ */
+public class Size {
+
+    /* This is the Quantity pattern. */
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private static final String DOUBLE_FORMAT_STRING = "%.1f";
+
+    private static final long BYTES_IN_KiB = 1024;
+    private static final long BYTES_IN_MiB = 1024 * BYTES_IN_KiB;
+    private static final long BYTES_IN_GiB = 1024 * BYTES_IN_MiB;
+    private static final long BYTES_IN_TiB = 1024 * BYTES_IN_GiB;
+
+    public enum Unit {
+        B(1),
+        KiB(BYTES_IN_KiB),
+        MiB(BYTES_IN_MiB),
+        GiB(BYTES_IN_GiB),
+        TiB(BYTES_IN_TiB);
+
+        private long numBytes;
+
+        private Unit(long numBytes) {
+            this.numBytes = numBytes;
+        }
+
+        private long getNumBytes() {
+            return numBytes;
+        }
+
+        public static Unit getBestUnit(long bytes) {
+            if (bytes < BYTES_IN_KiB) {
+                return Unit.B;
+            } else if (bytes < BYTES_IN_MiB) {
+                return Unit.KiB;
+            } else if (bytes < BYTES_IN_GiB) {
+                return Unit.MiB;
+            } else if (bytes < BYTES_IN_TiB) {
+                return Unit.GiB;
+            } else {
+                return Unit.TiB;
+            }
+        }
+    }
+
+    private final double amount;
+    private final Unit unit;
+
+    public Size(double amount, Unit unit) {
+        this.amount = amount;
+        this.unit = Objects.requireNonNull(unit);
+    }
+
+    public double getValue() {
+        return amount;
+    }
+
+    public Unit getUnit() {
+        return unit;
+    }
+
+    public Size convertTo(Unit target) {
+        return new Size(1.0 * amount * unit.getNumBytes() / target.getNumBytes(), target);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(amount, unit);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Size)) {
+            return false;
+        }
+        Size other = (Size) obj;
+        return this.getValue() == other.convertTo(this.getUnit()).getValue();
+    }
+
+    // TODO consider implementing these operations:
+    //
+    // public int compareTo(Object other)
+    //
+    // public boolean greaterThan(Size other) { }
+    //
+    // public boolean lessThan(Size other) { }
+    //
+    // public Size add(Size toAdd) { }
+    //
+    // public Size subtract(Size toSubtract) { }
+    //
+    // public Size multiply(double by) { }
+    //
+    // public Size divide(double by) { }
+
+    /**
+     * Returns a simplified and human-readable version of this Size
+     */
+    @Override
+    public String toString() {
+        String[] parts = toStringParts();
+        return translator.localize(LocaleResources.VALUE_AND_UNIT, parts[0], parts[1]);
+    }
+
+    /**
+     * Returns a human-readable version of this Size, appropriate for localization.
+     *
+     * @return a two-element string array. The first element is the amount. The second element is the unit.
+     */
+    public String[] toStringParts() {
+        long amountInBytes = (long) (amount * unit.getNumBytes());
+        if (amountInBytes < BYTES_IN_KiB) {
+            // No decimal units in plain bytes
+            return new String[] { String.valueOf(amountInBytes), Unit.B.name() };
+        } else if (amountInBytes < BYTES_IN_MiB) {
+            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) amountInBytes / BYTES_IN_KiB), Unit.KiB.name() };
+        } else if (amountInBytes < BYTES_IN_GiB) {
+            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) amountInBytes / BYTES_IN_MiB), Unit.MiB.name() };
+        } else if (amountInBytes < BYTES_IN_TiB) {
+            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) amountInBytes / BYTES_IN_GiB), Unit.GiB.name() };
+        } else {
+            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) amountInBytes / BYTES_IN_TiB), Unit.TiB.name() };
+        }
+    }
+
+    public static Size bytes(long bytes) {
+        return new Size(bytes, Unit.B);
+    }
+
+    /**
+     * Parses a string (such as "1.0 KiB") into a size.
+     */
+    public static Size parse(String toParse) {
+        String[] parts = toParse.split(" +");
+        String value = toParse;
+        String units = null;
+        if (parts.length > 1) {
+            value = parts[0];
+            units = parts[1];
+        }
+
+        double result = Double.NaN;
+        try {
+            result = Double.parseDouble(value);
+            if (units != null) {
+                Unit parsedUnit = Unit.valueOf(units.trim());
+                return new Size(result, parsedUnit);
+            }
+            return new Size(result, Unit.B);
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("unable to parse '" + toParse + "'", nfe);
+        }
+    }
+
+}
--- a/common/core/src/main/java/com/redhat/thermostat/common/locale/LocaleResources.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/common/core/src/main/java/com/redhat/thermostat/common/locale/LocaleResources.java	Thu Jan 03 10:23:48 2013 -0500
@@ -40,6 +40,8 @@
 
     MISSING_INFO,
 
+    VALUE_AND_UNIT,
+
     APPLICATION_INFO_LICENSE,
     APPLICATION_INFO_DESCRIPTION,
     APPLICATION_VERSION_INFO,
--- a/common/core/src/main/java/com/redhat/thermostat/common/utils/DisplayableValues.java	Wed Jan 02 09:47:39 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.utils;
-
-public class DisplayableValues {
-
-    private static final long BYTES_IN_KB = 1024;
-    private static final long BYTES_IN_MB = 1024*BYTES_IN_KB;
-    private static final long BYTES_IN_GB = 1024*BYTES_IN_MB;
-    private static final long BYTES_IN_TB = 1024*BYTES_IN_GB;
-    
-    public enum Scale {
-        B(1),
-        KiB(BYTES_IN_KB),
-        MiB(BYTES_IN_MB),
-        GiB(BYTES_IN_GB),
-        TiB(BYTES_IN_TB);
-
-        private long numBytes;
-
-        private Scale(long numBytes) {
-            this.numBytes = numBytes;
-        }
-
-        public long getNumBytes() {
-            return numBytes;
-        }
-
-        public static double convertTo(Scale scale, final long bytes) {
-            
-            return ((double) bytes) / ((double) scale.numBytes);
-        }
-        
-        public static double convertTo(Scale scale, final long bytes, long roundTo) {
-            
-            double result = ((double) bytes) / ((double) scale.numBytes);
-            result = ((double) Math.round(result * roundTo)) / roundTo;
-            
-            return result;
-        }
-
-        public static long convertToBytes(final long originalValue, Scale originalScale) {
-            return originalValue * originalScale.numBytes;
-        }
-
-        public static Scale getScale(final long bytes) {
-            if (bytes < BYTES_IN_KB) {
-                return Scale.B;
-            } else if (bytes < BYTES_IN_MB) {
-                return Scale.KiB;
-            } else if (bytes < BYTES_IN_GB) {
-                return Scale.MiB;
-            } else if (bytes < BYTES_IN_TB) {
-                return Scale.GiB;
-            } else {
-                return Scale.TiB;
-            }
-        }
-    }
-    
-    private static final String DOUBLE_FORMAT_STRING = "%.1f";
-
-    private DisplayableValues() {} // Not to be instantiated.
-
-    public static String[] bytes(final long bytes) {
-        if (bytes < BYTES_IN_KB) {
-            return new String[] { String.valueOf(bytes), Scale.B.name() };
-        } else if (bytes < BYTES_IN_MB) {
-            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_KB), Scale.KiB.name() };
-        } else if (bytes < BYTES_IN_GB) {
-            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_MB), Scale.MiB.name() };
-        } else if (bytes < BYTES_IN_TB) {
-            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_GB), Scale.GiB.name() };
-        } else {
-            return new String[] { String.format(DOUBLE_FORMAT_STRING, (double) bytes/BYTES_IN_TB), Scale.TiB.name() };
-        }
-    }
-}
--- a/common/core/src/main/resources/com/redhat/thermostat/common/locale/strings.properties	Wed Jan 02 09:47:39 2013 -0500
+++ b/common/core/src/main/resources/com/redhat/thermostat/common/locale/strings.properties	Thu Jan 03 10:23:48 2013 -0500
@@ -1,5 +1,7 @@
 MISSING_INFO = Missing Information
 
+VALUE_AND_UNIT = {0} {1}
+
 APPLICATION_INFO_DESCRIPTION = A monitoring and serviceability tool for OpenJDK
 APPLICATION_INFO_LICENSE = Licensed under GPLv2+ with Classpath exception
 # First parameter is the application name
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/common/SizeTest.java	Thu Jan 03 10:23:48 2013 -0500
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Locale;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.Size.Unit;
+
+public class SizeTest {
+    private static Locale defaultLocale;
+
+    @BeforeClass
+    public static void setUp() {
+        defaultLocale = Locale.getDefault();
+        Locale.setDefault(Locale.US);
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        Locale.setDefault(defaultLocale);
+    }
+
+    @Test
+    public void testAccessorMethods() {
+        Size size = new Size(1, Size.Unit.B);
+        assertEquals(1, size.getValue(), 0.00001);
+        assertEquals(Size.Unit.B, size.getUnit());
+    }
+
+    @Test
+    public void testHashing() {
+        Size size1 = new Size(1024, Size.Unit.B);
+        Size size2 = new Size(1, Size.Unit.B);
+
+        assertTrue(size1.hashCode() != size2.hashCode());
+    }
+
+    @Test
+    public void testEquality() {
+        Size size1 = new Size(1024, Size.Unit.B);
+        Size size2 = new Size(1, Size.Unit.KiB);
+        Size size3 = new Size(2, Size.Unit.KiB);
+
+        assertEquals(size1, size2);
+        assertEquals(size2, size1);
+        assertFalse(size1.equals(size3));
+
+        assertFalse(size1.equals(null));
+        assertFalse(size1.equals(new Object()));
+    }
+
+    @Test
+    public void testUnitConversion() {
+        Size size = new Size(1024, Size.Unit.KiB);
+
+        Size sizeInBytes = size.convertTo(Size.Unit.B);
+        assertEquals(1024 * 1024, sizeInBytes.getValue(), 0.001);
+        assertEquals(Size.Unit.B, sizeInBytes.getUnit());
+
+        Size sizeInMegaBytes = size.convertTo(Size.Unit.MiB);
+        assertEquals(1, sizeInMegaBytes.getValue(), 0.001);
+        assertEquals(Size.Unit.MiB, sizeInMegaBytes.getUnit());
+
+        assertEquals(size, sizeInBytes);
+        assertEquals(size, sizeInMegaBytes);
+
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("1 B", Size.bytes(1).toString());
+        assertEquals("1023 B", Size.bytes(1023).toString());
+        assertEquals("1.0 KiB", Size.bytes(1024).toString());
+        assertEquals("1024.0 KiB", Size.bytes(1_048_575).toString());
+        assertEquals("1.0 MiB", Size.bytes(1_048_576).toString());
+        assertEquals("10.0 MiB", Size.bytes(10_480_000).toString());
+        assertEquals("42.0 MiB", Size.bytes(44_040_000).toString());
+        assertEquals("99.9 MiB", Size.bytes(104_752_742).toString());
+        assertEquals("100.0 MiB", Size.bytes(104_857_600).toString());
+        assertEquals("500.0 MiB", Size.bytes(524_288_000).toString());
+        assertEquals("900.0 MiB", Size.bytes(943_718_400).toString());
+        assertEquals("999.9 MiB", Size.bytes(1_048_471_000).toString());
+        assertEquals("1.0 GiB", Size.bytes(1_073_741_824).toString());
+        assertEquals("1.1 GiB", Size.bytes(1_181_116_000).toString());
+        assertEquals("9.9 GiB", Size.bytes(10_630_044_000l).toString());
+        assertEquals("99.9 GiB", Size.bytes(107_266_808_000l).toString());
+        assertEquals("1.0 TiB", Size.bytes(1_099_511_627_776l).toString());
+    }
+
+    @Test
+    public void testParsingNumber() {
+        assertEquals(Size.bytes(1), Size.parse("1"));
+    }
+
+    @Test
+    public void testParsingNumberAndUnit() {
+        assertEquals(Size.bytes(1), Size.parse("1 B"));
+
+        assertEquals(new Size(1024, Unit.KiB), Size.parse("1024.0 KiB"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testParsingString() {
+        Size.parse("B");
+    }
+
+    @Test
+    public void testGetBestUnit() {
+        assertEquals(Size.Unit.KiB, Size.Unit.getBestUnit(1024));
+        assertEquals(Size.Unit.B, Size.Unit.getBestUnit(999));
+
+        assertEquals(Size.Unit.MiB, Size.Unit.getBestUnit(943_718_400));
+        assertEquals(Size.Unit.MiB, Size.Unit.getBestUnit(1_048_471_000));
+        assertEquals(Size.Unit.GiB, Size.Unit.getBestUnit(1_073_741_824));
+        assertEquals(Size.Unit.GiB, Size.Unit.getBestUnit(107_266_808_000l));
+
+        assertEquals(Size.Unit.TiB, Size.Unit.getBestUnit(1_099_511_627_776l));
+    }
+
+}
--- a/common/core/src/test/java/com/redhat/thermostat/common/utils/DisplayableValuesTest.java	Wed Jan 02 09:47:39 2013 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/*
- * Copyright 2012 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.common.utils;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Locale;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
-
-public class DisplayableValuesTest {
-
-    private static Locale defaultLocale;
-
-    @BeforeClass
-    public static void setUp() {
-        defaultLocale = Locale.getDefault();
-        Locale.setDefault(Locale.US);
-    }
-
-    @AfterClass
-    public static void tearDown() {
-        Locale.setDefault(defaultLocale);
-    }
-
-    @Test
-    public void testBytes() {
-        testBytesOutput("1", "B", DisplayableValues.bytes(1));
-        testBytesOutput("1023", "B", DisplayableValues.bytes(1023));
-        testBytesOutput("1.0", "KiB", DisplayableValues.bytes(1024));
-        testBytesOutput("1024.0", "KiB", DisplayableValues.bytes(1_048_575));
-        testBytesOutput("1.0", "MiB", DisplayableValues.bytes(1_048_576));
-        testBytesOutput("10.0", "MiB", DisplayableValues.bytes(10_480_000));
-        testBytesOutput("42.0", "MiB", DisplayableValues.bytes(44_040_000));
-        testBytesOutput("99.9", "MiB", DisplayableValues.bytes(104_752_742));
-        testBytesOutput("100.0", "MiB", DisplayableValues.bytes(104_857_600));
-        testBytesOutput("500.0", "MiB", DisplayableValues.bytes(524_288_000));
-        testBytesOutput("900.0", "MiB", DisplayableValues.bytes(943_718_400));
-        testBytesOutput("999.9", "MiB", DisplayableValues.bytes(1_048_471_000));
-        testBytesOutput("1.0", "GiB", DisplayableValues.bytes(1_073_741_824));
-        testBytesOutput("1.1", "GiB", DisplayableValues.bytes(1_181_116_000));
-        testBytesOutput("9.9", "GiB", DisplayableValues.bytes(10_630_044_000l));
-        testBytesOutput("99.9", "GiB", DisplayableValues.bytes(107_266_808_000l));
-        testBytesOutput("1.0", "TiB", DisplayableValues.bytes(1_099_511_627_776l));
-    }
-
-    private void testBytesOutput(String number, String units, String[] output) {
-        assertEquals(2, output.length);
-        assertEquals(number, output[0]);
-        assertEquals(units, output[1]);
-    }
-    
-    @Test
-    public void testScales() {
-        
-        double value = Scale.convertTo(Scale.KiB, 1024);
-        assertEquals(1, value, 0);
-        
-        value = Scale.convertTo(Scale.KiB, 2048);
-        assertEquals(2, value, 0);
-        
-        value = Scale.convertTo(Scale.KiB, 524_288);
-        assertEquals(512, value, 0);
-        
-        value = Scale.convertTo(Scale.MiB, 524_288_000);
-        assertEquals(500, value, 0);
-
-        long longValue = Scale.convertToBytes(1, Scale.MiB);
-        assertEquals(1024l * 1024, longValue);
-    }
-}
--- a/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/eclipse/com.redhat.thermostat.eclipse.chart.common/src/com/redhat/thermostat/eclipse/chart/common/SWTHostMemoryView.java	Thu Jan 03 10:23:48 2013 -0500
@@ -70,9 +70,8 @@
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView;
 import com.redhat.thermostat.host.memory.client.locale.LocaleResources;
 import com.redhat.thermostat.client.ui.ChartColors;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues;
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
 import com.redhat.thermostat.eclipse.SWTComponent;
 import com.redhat.thermostat.eclipse.ThermostatConstants;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
@@ -162,7 +161,7 @@
         JFreeChart chart = ChartFactory.createTimeSeriesChart(
                 null, // Title
                 translator.localize(LocaleResources.HOST_MEMORY_CHART_TIME_LABEL), // x-axis Label
-                translator.localize(LocaleResources.HOST_MEMORY_CHART_SIZE_LABEL, Scale.MiB.name()), // y-axis Label
+                translator.localize(LocaleResources.HOST_MEMORY_CHART_SIZE_LABEL, Size.Unit.MiB.name()), // y-axis Label
                 memoryCollection, // Dataset
                 false, // Show Legend
                 false, // Use tooltips
@@ -258,7 +257,7 @@
                     RegularTimePeriod period = new FixedMillisecond(timeData.getTimeInMillis());
                     if (series.getDataItem(period) == null) {
                         Long sizeInBytes = (Long) timeData.getData();
-                        Double sizeInMegaBytes = DisplayableValues.Scale.convertTo(Scale.MiB, sizeInBytes);
+                        Double sizeInMegaBytes = Size.bytes(sizeInBytes).convertTo(Size.Unit.MiB).getValue();
                         series.add(new FixedMillisecond(timeData.getTimeInMillis()), sizeInMegaBytes, false);
                     }
                 }
--- a/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/internal/HostMemoryController.java	Thu Jan 03 10:23:48 2013 -0500
@@ -47,13 +47,13 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.MemoryStatDAO;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryViewProvider;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView.GraphVisibilityChangeListener;
@@ -113,8 +113,7 @@
             @Override
             public void run() {
                 long memorySize = HostMemoryController.this.hostInfoDAO.getHostInfo(ref).getTotalMemory();
-                String[] memorySizeParts = DisplayableValues.bytes(memorySize);
-                view.setTotalMemory(translator.localize(LocaleResources.NUMBER_AND_UNIT, memorySizeParts[0], memorySizeParts[1]));
+                view.setTotalMemory(Size.bytes(memorySize).toString());
                 doMemoryChartUpdate();
             }
         });
--- a/host-memory/client-swing/src/main/java/com/redhat/thermostat/host/memory/client/swing/internal/HostMemoryPanel.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/host-memory/client-swing/src/main/java/com/redhat/thermostat/host/memory/client/swing/internal/HostMemoryPanel.java	Thu Jan 03 10:23:48 2013 -0500
@@ -72,9 +72,8 @@
 import com.redhat.thermostat.client.ui.ComponentVisibleListener;
 import com.redhat.thermostat.client.ui.RecentTimeSeriesChartController;
 import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues;
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
 import com.redhat.thermostat.host.memory.client.core.HostMemoryView;
 import com.redhat.thermostat.host.memory.client.locale.LocaleResources;
 import com.redhat.thermostat.storage.model.DiscreteTimeData;
@@ -210,7 +209,7 @@
                     RegularTimePeriod period = new FixedMillisecond(timeData.getTimeInMillis());
                     if (series.getDataItem(period) == null) {
                         Long sizeInBytes = (Long) timeData.getData();
-                        Double sizeInMegaBytes = DisplayableValues.Scale.convertTo(Scale.MiB, sizeInBytes);
+                        Double sizeInMegaBytes = Size.bytes(sizeInBytes).convertTo(Size.Unit.MiB).getValue();
                         series.add(new FixedMillisecond(timeData.getTimeInMillis()), sizeInMegaBytes, false);
                     }
                 }
@@ -303,7 +302,7 @@
         JFreeChart chart = ChartFactory.createTimeSeriesChart(
                 translator.localize(LocaleResources.HOST_MEMORY_CHART_TITLE), // Title
                 translator.localize(LocaleResources.HOST_MEMORY_CHART_TIME_LABEL), // x-axis Label
-                translator.localize(LocaleResources.HOST_MEMORY_CHART_SIZE_LABEL, Scale.MiB.name()), // y-axis Label
+                translator.localize(LocaleResources.HOST_MEMORY_CHART_SIZE_LABEL, Size.Unit.MiB.name()), // y-axis Label
                 memoryCollection, // Dataset
                 false, // Show Legend
                 false, // Use tooltips
--- a/host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/internal/HostOverviewController.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/internal/HostOverviewController.java	Thu Jan 03 10:23:48 2013 -0500
@@ -48,13 +48,13 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.dao.HostInfoDAO;
 import com.redhat.thermostat.common.dao.HostRef;
 import com.redhat.thermostat.common.dao.NetworkInterfaceInfoDAO;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues;
 import com.redhat.thermostat.host.overview.client.core.HostOverviewView;
 import com.redhat.thermostat.host.overview.client.core.HostOverviewViewProvider;
 import com.redhat.thermostat.host.overview.client.locale.LocaleResources;
@@ -94,8 +94,7 @@
                 view.setCpuModel(hostInfo.getCpuModel());
                 view.setCpuCount(String.valueOf(hostInfo.getCpuCount()));
 
-                String[] parts = DisplayableValues.bytes(hostInfo.getTotalMemory());
-                String readableTotalMemory = translator.localize(LocaleResources.NUMBER_AND_UNIT, parts[0], parts[1]);
+                String readableTotalMemory = new Size(hostInfo.getTotalMemory(), Size.Unit.B).toString();
                 view.setTotalMemory(readableTotalMemory);
 
                 List<NetworkInterfaceInfo> networkInfo =
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/HostInfoBuilder.java	Thu Jan 03 10:23:48 2013 -0500
@@ -43,7 +43,8 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
+import com.redhat.thermostat.common.Size;
+import com.redhat.thermostat.common.Size.Unit;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.HostInfo;
 import com.redhat.thermostat.utils.ProcDataSource;
@@ -76,9 +77,9 @@
     }
 
     static class HostMemoryInfo {
-        public final long totalMemory;
+        public final Size totalMemory;
 
-        public HostMemoryInfo(long totalMemory) {
+        public HostMemoryInfo(Size totalMemory) {
             this.totalMemory = totalMemory;
         }
     }
@@ -93,9 +94,10 @@
         String hostname = getHostName();
         HostCpuInfo cpuInfo = getCpuInfo();
         HostMemoryInfo memoryInfo = getMemoryInfo();
+        long totalMemorySize = (long) memoryInfo.totalMemory.convertTo(Unit.B).getValue();
         HostOsInfo osInfo = getOsInfo();
 
-        return new HostInfo(hostname, osInfo.distribution, osInfo.kernel, cpuInfo.model, cpuInfo.count, memoryInfo.totalMemory);
+        return new HostInfo(hostname, osInfo.distribution, osInfo.kernel, cpuInfo.model, cpuInfo.count, totalMemorySize);
     }
 
     HostCpuInfo getCpuInfo() {
@@ -123,19 +125,19 @@
     }
 
     HostMemoryInfo getMemoryInfo() {
-        long totalMemory = -1;
+        Size totalMemory = null;
         try (BufferedReader bufferedReader = new BufferedReader(dataSource.getMemInfoReader())) {
             String[] memTotalParts = bufferedReader.readLine().split(" +");
             long data = Long.valueOf(memTotalParts[1]);
             String units = memTotalParts[2];
             if (units.equals("kB")) {
-                totalMemory = Scale.convertToBytes(data, Scale.KiB);
+                totalMemory = new Size(data, Size.Unit.KiB);
             }
         } catch (IOException ioe) {
             logger.log(Level.WARNING, "unable to read memory info");
         }
 
-        logger.log(Level.FINEST, "totalMemory: " + totalMemory + " bytes");
+        logger.log(Level.FINEST, "totalMemory: " + totalMemory.toString());
         return new HostMemoryInfo(totalMemory);
     }
 
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/MemoryStatBuilder.java	Thu Jan 03 10:23:48 2013 -0500
@@ -42,8 +42,7 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.common.NotImplementedException;
-import com.redhat.thermostat.common.utils.DisplayableValues;
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.model.MemoryStat;
 import com.redhat.thermostat.utils.ProcDataSource;
@@ -126,8 +125,9 @@
         try {
             result = Long.parseLong(value);
             if (units != null) {
+                // /proc/meminfo uses kB instead of KiB, incorrectly
                 if (units.equals("kB") || units.equals("KB")) {
-                    result = Scale.convertToBytes(result, Scale.KiB);
+                    result = (long) new Size(result, Size.Unit.KiB).convertTo(Size.Unit.B).getValue();
                 } else {
                     throw new NotImplementedException("unit conversion from " + units + " not implemented");
                 }
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/HostInfoBuilderTest.java	Thu Jan 03 10:23:48 2013 -0500
@@ -51,6 +51,7 @@
 import com.redhat.thermostat.backend.system.HostInfoBuilder.HostCpuInfo;
 import com.redhat.thermostat.backend.system.HostInfoBuilder.HostMemoryInfo;
 import com.redhat.thermostat.backend.system.HostInfoBuilder.HostOsInfo;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.storage.model.HostInfo;
 import com.redhat.thermostat.utils.ProcDataSource;
 
@@ -94,7 +95,7 @@
 
         HostMemoryInfo memoryInfo = new HostInfoBuilder(dataSource).getMemoryInfo();
         assertNotNull(memoryInfo);
-        assertEquals(12345 * KILOBYTES_TO_BYTES, memoryInfo.totalMemory);
+        assertEquals(Size.bytes(12345 * KILOBYTES_TO_BYTES), memoryInfo.totalMemory);
         verify(dataSource).getMemInfoReader();
 
     }
--- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java	Thu Jan 03 10:23:48 2013 -0500
@@ -50,13 +50,13 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.cli.CommandException;
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
 import com.redhat.thermostat.storage.model.HeapInfo;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
 import com.redhat.thermostat.storage.model.VmMemoryStat.Generation;
@@ -277,14 +277,9 @@
                     }
                 }
                 model.addData(memoryStats.getTimeStamp(), used, capacity);
-                
-                NumberFormat formatter = DecimalFormat.getInstance();
 
-                double res = Scale.convertTo(Scale.B, used);
-                String _used = formatter.format(res) + " " + Scale.B;
-                
-                res = Scale.convertTo(Scale.B, capacity);
-                String _capacity= formatter.format(capacity) + " " + Scale.B;
+                String _used = Size.bytes(used).toString();
+                String _capacity= Size.bytes(capacity).toString();
                 
                 view.updateUsedAndCapacity(_used, _capacity);
                 desiredUpdateTimeStamp = Math.max(desiredUpdateTimeStamp, memoryStats.getTimeStamp());
--- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/Payload.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/Payload.java	Thu Jan 03 10:23:48 2013 -0500
@@ -36,7 +36,7 @@
 
 package com.redhat.thermostat.vm.memory.client.core;
 
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
+import com.redhat.thermostat.common.Size;
 
 public class Payload implements Cloneable {
 
@@ -48,8 +48,8 @@
     private double maxUsed;
     private double used;
     
-    private Scale usedUnit;
-    private Scale capacityUnit;
+    private Size.Unit usedUnit;
+    private Size.Unit capacityUnit;
     
     private StatsModel model;
     
@@ -61,19 +61,19 @@
         return model;
     }
     
-    public void setCapacityUnit(Scale capacityUnit) {
+    public void setCapacityUnit(Size.Unit capacityUnit) {
         this.capacityUnit = capacityUnit;
     }
     
-    public Scale getCapacityUnit() {
+    public Size.Unit getCapacityUnit() {
         return capacityUnit;
     }
     
-    public void setUsedUnit(Scale usedUnit) {
+    public void setUsedUnit(Size.Unit usedUnit) {
         this.usedUnit = usedUnit;
     }
     
-    public Scale getUsedUnit() {
+    public Size.Unit getUsedUnit() {
         return usedUnit;
     }
     
--- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/internal/MemoryStatsController.java	Thu Jan 03 10:23:48 2013 -0500
@@ -48,7 +48,9 @@
 import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Size.Unit;
 import com.redhat.thermostat.common.Timer.SchedulingType;
 import com.redhat.thermostat.common.command.Request;
 import com.redhat.thermostat.common.command.RequestResponseListener;
@@ -59,7 +61,6 @@
 import com.redhat.thermostat.common.dao.VmMemoryStatDAO;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.locale.Translate;
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
 import com.redhat.thermostat.gc.remote.common.GCRequest;
 import com.redhat.thermostat.gc.remote.common.command.GCCommand;
 import com.redhat.thermostat.storage.model.VmMemoryStat;
@@ -104,17 +105,17 @@
                             payload.setName(space.getName());
                         }
 
-                        Scale usedScale = normalizeScale(space.getUsed(), space.getCapacity());
-                        double used = Scale.convertTo(usedScale, space.getUsed(), 100);
-                        double maxUsed = Scale.convertTo(usedScale, space.getCapacity(), 100);
+                        Size.Unit usedScale = bestUnitForRange(space.getUsed(), space.getCapacity());
+                        double used = Size.bytes(space.getUsed()).convertTo(usedScale).getValue();
+                        double maxUsed = Size.bytes(space.getCapacity()).convertTo(usedScale).getValue();
                         
                         payload.setUsed(used);
                         payload.setMaxUsed(maxUsed);
                         payload.setUsedUnit(usedScale);
                         
-                        Scale maxScale = normalizeScale(space.getCapacity(), space.getMaxCapacity());
-                        double capacity = Scale.convertTo(maxScale, space.getCapacity(), 100);
-                        double maxCapacity = Scale.convertTo(maxScale, space.getMaxCapacity(), 100);
+                        Size.Unit maxScale = bestUnitForRange(space.getCapacity(), space.getMaxCapacity());
+                        double capacity = Size.bytes(space.getCapacity()).convertTo(maxScale).getValue();
+                        double maxCapacity = Size.bytes(space.getMaxCapacity()).convertTo(maxScale).getValue();
                         
                         payload.setCapacity(capacity);
                         payload.setMaxCapacity(maxCapacity);
@@ -134,8 +135,7 @@
                         }
                         
                         // normalize this always in the same unit
-                        model.addData(memoryStats.getTimeStamp(),
-                                      Scale.convertTo(Scale.MiB, space.getUsed(), 100));
+                        model.addData(memoryStats.getTimeStamp(), Size.bytes(space.getUsed()).convertTo(Unit.MiB).getValue());
                         
                         payload.setModel(model);
                         if (regions.containsKey(space.getName())) {
@@ -224,13 +224,13 @@
         return regions;
     }
     
-    private Scale normalizeScale(long min, long max) {
+    private Size.Unit bestUnitForRange(long min, long max) {
         // FIXME: this is very dumb and very inefficient
         // needs cleanup
-        Scale minScale = Scale.getScale(min);
-        Scale maxScale = Scale.getScale(max);
+        Size.Unit minScale = Size.Unit.getBestUnit(min);
+        Size.Unit maxScale = Size.Unit.getBestUnit(max);
         
-        Scale[] scales = Scale.values();
+        Size.Unit[] scales = Size.Unit.values();
         int maxID = 0;
         int minID = 0;
         for (int i = 0; i < scales.length; i++) {
--- a/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/PayloadTest.java	Wed Jan 02 09:47:39 2013 -0500
+++ b/vm-memory/client-core/src/test/java/com/redhat/thermostat/vm/memory/client/core/PayloadTest.java	Thu Jan 03 10:23:48 2013 -0500
@@ -41,7 +41,7 @@
 
 import org.junit.Test;
 
-import com.redhat.thermostat.common.utils.DisplayableValues.Scale;
+import com.redhat.thermostat.common.Size;
 
 public class PayloadTest {
 
@@ -57,11 +57,11 @@
         Payload source = new Payload();
         source.setCapacity(10.0);
         source.setName("fluff");
-        source.setCapacityUnit(Scale.GiB);
+        source.setCapacityUnit(Size.Unit.GiB);
         source.setMaxCapacity(100.0);
         source.setMaxUsed(5.0);
         source.setUsed(3.0);
-        source.setUsedUnit(Scale.MiB);
+        source.setUsedUnit(Size.Unit.MiB);
         source.setModel(model);
         source.setTooltip("fluffTooltip");