75e4b0a8d5a4e9ae4901e0e618ca6274f79a60e2
[deliverable/tracecompass.git] / common / org.eclipse.tracecompass.common.core / src / org / eclipse / tracecompass / common / core / format / DecimalUnitFormat.java
1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson and others
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9
10 package org.eclipse.tracecompass.common.core.format;
11
12 import java.math.BigDecimal;
13 import java.text.DecimalFormat;
14 import java.text.FieldPosition;
15 import java.text.Format;
16 import java.text.NumberFormat;
17 import java.text.ParsePosition;
18 import java.util.Map;
19
20 import com.google.common.collect.ImmutableMap;
21
22 /**
23 * Provides a formatter for decimal numbers with International System of Units
24 * prefixes up to peta (quadrillion). It receives a number and formats it in the
25 * closest thousand's unit, with at most 1 decimal.
26 *
27 * @author Michael Jeanson
28 * @since 2.0
29 */
30 public class DecimalUnitFormat extends Format {
31
32 private static final long serialVersionUID = 3650332020346870384L;
33
34 /* International System of Units prefixes */
35 private static final String KILO_PREFIX = "k"; //$NON-NLS-1$
36 private static final String MEGA_PREFIX = "M"; //$NON-NLS-1$
37 private static final String GIGA_PREFIX = "G"; //$NON-NLS-1$
38 private static final String TERA_PREFIX = "T"; //$NON-NLS-1$
39 private static final String PETA_PREFIX = "P"; //$NON-NLS-1$
40
41 private static final String MILLI_PREFIX = "m"; //$NON-NLS-1$
42 private static final String MICRO_PREFIX = "ยต"; //$NON-NLS-1$
43 private static final String NANO_PREFIX = "n"; //$NON-NLS-1$
44 private static final String PICO_PREFIX = "p"; //$NON-NLS-1$
45
46 private static final long KILO = 1000L;
47 private static final long MEGA = 1000000L;
48 private static final long GIGA = 1000000000L;
49 private static final long TERA = 1000000000000L;
50 private static final long PETA = 1000000000000000L;
51
52 private static final double MILLI = 0.001;
53 private static final double MICRO = 0.000001;
54 private static final double NANO = 0.000000001;
55 private static final double PICO = 0.000000000001;
56
57 /* Map of prefix to exponent */
58 private static final Map<String, Integer> PREFIX_MAP = ImmutableMap.<String, Integer>builder()
59 .put(KILO_PREFIX, +3)
60 .put(MEGA_PREFIX, +6)
61 .put(GIGA_PREFIX, +9)
62 .put(TERA_PREFIX, +12)
63 .put(PETA_PREFIX, +15)
64 .put(MILLI_PREFIX, -3)
65 .put(MICRO_PREFIX, -6)
66 .put(NANO_PREFIX, -9)
67 .put(PICO_PREFIX, -12)
68 .build();
69 private static final Format FORMAT = new DecimalFormat("#.#"); //$NON-NLS-1$
70 private final double fFactor;
71
72
73 /**
74 * Default constructor.
75 */
76 public DecimalUnitFormat() {
77 super();
78 fFactor = 1.0;
79 }
80
81 /**
82 * Constructor with multiplication factor.
83 *
84 * @param factor Multiplication factor to apply to the value
85 */
86 public DecimalUnitFormat(double factor) {
87 super();
88 fFactor = factor;
89 }
90
91 @Override
92 public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
93 if (obj instanceof Number) {
94 Number num = (Number) obj;
95
96 /* Apply the multiplication factor before formatting */
97 double value = num.doubleValue() * fFactor;
98
99 double abs = Math.abs(value);
100
101 if (Double.isInfinite(value) || Double.isNaN(value) || abs < PICO) {
102 return toAppendTo.append(FORMAT.format(value));
103 }
104
105 if (abs >= 1) {
106 if (abs > Long.MAX_VALUE) {
107 return toAppendTo.append(num);
108 }
109 if (abs >= PETA) {
110 return toAppendTo.append(FORMAT.format(value / PETA)).append(' ').append(PETA_PREFIX);
111 }
112 if (abs >= TERA) {
113 return toAppendTo.append(FORMAT.format(value / TERA)).append(' ').append(TERA_PREFIX);
114 }
115 if (abs >= GIGA) {
116 return toAppendTo.append(FORMAT.format(value / GIGA)).append(' ').append(GIGA_PREFIX);
117 }
118 if (abs >= MEGA) {
119 return toAppendTo.append(FORMAT.format(value / MEGA)).append(' ').append(MEGA_PREFIX);
120 }
121 if (abs >= KILO) {
122 return toAppendTo.append(FORMAT.format(value / KILO)).append(' ').append(KILO_PREFIX);
123 }
124
125 return toAppendTo.append(FORMAT.format(value));
126 }
127
128 if (abs < NANO) {
129 return toAppendTo.append(FORMAT.format(value * TERA)).append(' ').append(PICO_PREFIX);
130 }
131 if (abs < MICRO) {
132 return toAppendTo.append(FORMAT.format(value * GIGA)).append(' ').append(NANO_PREFIX);
133 }
134 if (abs < MILLI) {
135 return toAppendTo.append(FORMAT.format(value * MEGA)).append(' ').append(MICRO_PREFIX);
136 }
137
138 return toAppendTo.append(FORMAT.format(value * KILO)).append(' ').append(MILLI_PREFIX);
139 }
140
141 throw new IllegalArgumentException("Cannot format given Object as a Number: " + obj); //$NON-NLS-1$
142 }
143
144 /**
145 * @since 2.1
146 */
147 @Override
148 public Number parseObject(String source, ParsePosition pos) {
149 Number number = NumberFormat.getInstance().parse(source, pos);
150 if (number == null) {
151 return null;
152 }
153 String unit = source.substring(pos.getIndex()).trim();
154 Integer exponent = null;
155 if (!unit.isEmpty()) {
156 String prefix = unit.substring(0, 1);
157 exponent = PREFIX_MAP.get(prefix);
158 }
159 if (exponent != null && Double.isFinite(number.doubleValue())) {
160 BigDecimal bd = new BigDecimal(number.toString());
161 bd = bd.movePointRight(exponent.intValue());
162 if (bd.remainder(BigDecimal.ONE).equals(BigDecimal.ZERO) &&
163 bd.abs().compareTo(new BigDecimal(Long.MAX_VALUE)) < 0) {
164 return bd.longValue();
165 }
166 return bd.doubleValue();
167 }
168 return number;
169 }
170 }
This page took 0.033018 seconds and 4 git commands to generate.