Commit | Line | Data |
---|---|---|
a78d0727 | 1 | /******************************************************************************* |
d64d4a57 | 2 | * Copyright (c) 2016 EfficiOS Inc., Michael Jeanson and others |
a78d0727 MJ |
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 | ||
d64d4a57 | 12 | import java.math.BigDecimal; |
a78d0727 MJ |
13 | import java.text.DecimalFormat; |
14 | import java.text.FieldPosition; | |
15 | import java.text.Format; | |
d64d4a57 | 16 | import java.text.NumberFormat; |
a78d0727 | 17 | import java.text.ParsePosition; |
d64d4a57 PT |
18 | import java.util.Map; |
19 | ||
20 | import com.google.common.collect.ImmutableMap; | |
a78d0727 MJ |
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 | ||
d64d4a57 PT |
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(); | |
a78d0727 MJ |
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) { | |
d64d4a57 PT |
106 | if (abs > Long.MAX_VALUE) { |
107 | return toAppendTo.append(num); | |
108 | } | |
a78d0727 MJ |
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 | ||
d64d4a57 | 144 | /** |
15a74be2 | 145 | * @since 2.2 |
d64d4a57 | 146 | */ |
a78d0727 | 147 | @Override |
d64d4a57 PT |
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; | |
a78d0727 MJ |
169 | } |
170 | } |