1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.utils.strings.format;
\r
14 import java.io.Serializable;
\r
15 import java.text.DecimalFormat;
\r
16 import java.util.IllegalFormatConversionException;
\r
17 import java.util.IllegalFormatException;
\r
20 * Value metrics data
\r
22 * Use 'S' Prefix to use String formatting rules http://java.sun.com/javase/6/docs/api/java/util/Formatter.html#syntax
\r
23 * Use 'D' Prefix to use DecimalFormat rules http://java.sun.com/javase/6/docs/api/java/text/DecimalFormat.html
\r
25 * If no prefix is detected, String rules are used.
\r
27 * @author Toni Kalajainen
\r
28 * @Author Marko Luukkainen
\r
30 public class MetricsFormat implements Serializable {
\r
33 * Define serial version of this class
\r
35 private static final long serialVersionUID = 8036591251300342995L;
\r
38 /** Presentation format */
\r
39 private String pattern;
\r
41 /** Presentation format */
\r
42 private String internalPattern;
\r
44 /** Presentation format */
\r
45 private String[] internalPatternDivided;
\r
47 /** flag if format can adjust decimals */
\r
48 private boolean canUseDecimals = false;
\r
51 private double scale;
\r
54 private String name;
\r
59 /** Is double or long */
\r
60 private boolean isDouble = true;
\r
62 private boolean useDecimalFormat = false;
\r
64 /** Array reserved for formatter use */
\r
65 private Object args[] = new Object[1];
\r
68 public String getName() {
\r
72 public MetricsFormat(String pattern, double scale, String name)
\r
73 throws IllegalArgumentException
\r
76 pattern = setPattern(pattern);
\r
77 this.internalPattern = formatPattern( pattern );
\r
78 this.internalPatternDivided = formatPatternDivided( pattern );
\r
80 this.hash = makeHash();
\r
83 public MetricsFormat(String pattern, double scale)
\r
84 throws IllegalArgumentException
\r
87 pattern = setPattern(pattern);
\r
88 this.internalPattern = formatPattern( pattern );
\r
89 this.internalPatternDivided = formatPatternDivided( pattern );
\r
91 this.hash = makeHash();
\r
94 public MetricsFormat(String pattern)
\r
95 throws IllegalArgumentException
\r
98 pattern = setPattern(pattern);
\r
99 this.internalPattern = formatPattern( pattern );
\r
100 this.internalPatternDivided = formatPatternDivided( pattern );
\r
102 this.hash = makeHash();
\r
106 private String setPattern(String pattern) {
\r
107 this.pattern = pattern;
\r
108 if (pattern.startsWith("D")) {
\r
109 pattern = pattern.substring(1);
\r
110 useDecimalFormat = true;
\r
112 if (pattern.startsWith("S"))
\r
113 pattern = pattern.substring(1);
\r
114 useDecimalFormat = false;
\r
120 * Replacs all % with %1$ unless % precedes with \
\r
125 private String formatPattern(String pattern) {
\r
127 if (!useDecimalFormat) {
\r
128 while ( (pos = pattern.indexOf('%', pos)) >= 0 ) {
\r
129 if (pos==0 || pattern.indexOf(pos-1)!='\\') {
\r
130 pattern = pattern.substring(0, pos+1) + "1$" + pattern.substring(pos+1, pattern.length());
\r
139 if (useDecimalFormat) {
\r
141 useDecimalFormat = true;
\r
142 canUseDecimals = false;
\r
143 DecimalFormat format = new DecimalFormat(pattern);
\r
144 format.format(value);
\r
149 useDecimalFormat = false;
\r
151 String.format(pattern, args);
\r
152 } catch(IllegalFormatConversionException e1) {
\r
155 useDecimalFormat = false;
\r
156 args[0] = (long)value;
\r
157 String.format(pattern, args);
\r
158 } catch (Exception e2) {
\r
168 * Replacs all % with %1$ unless % precedes with \
\r
170 * Divides formatString into half so that
\r
171 * formats precision can be adjusted with .(n)<br>
\r
172 * Currently only format which supports adjustable
\r
173 * decimals is f/F (floating point as decimal number)
\r
179 private String[] formatPatternDivided(String pattern) {
\r
180 String divPattern[] = new String[]{"",""};
\r
181 if (useDecimalFormat)
\r
184 while ( (pos = pattern.indexOf('%', pos)) >= 0 ) {
\r
185 if (pos==0 || pattern.indexOf(pos-1)!='\\') {
\r
186 divPattern[0] = pattern.substring(0, pos+1) + "1$";
\r
187 divPattern[1] = pattern.substring(pos+1, pattern.length());
\r
191 char c = divPattern[1].charAt(pos2);
\r
199 Character.isDigit(c))
\r
204 // get rid of possible precisision setting
\r
205 // TODO : maybe we should let the user to set the precision
\r
206 // and then use user given precision instead of dynamic precision
\r
209 char c = divPattern[1].charAt(pos3);
\r
210 if (c == '.' || Character.isDigit(c)) {
\r
212 } else if (c == 'f'|| c == 'F') {
\r
213 this.canUseDecimals = true;
\r
216 this.canUseDecimals = false;
\r
220 if (pos2 > 0 || pos3 > 0) {
\r
221 divPattern[0] += divPattern[1].substring(0,pos2);
\r
222 divPattern[1] = divPattern[1].substring(pos3, divPattern[1].length());
\r
225 // divided patterns can have only one %
\r
233 // checking if format can be used
\r
235 String.format(divPattern[0] + ".2" + divPattern[1] , args);
\r
236 } catch (IllegalFormatException e1) {
\r
238 pattern = formatPattern(pattern);
\r
240 args[0] = (long)value;
\r
241 String.format(pattern, args);
\r
242 // if we get this far by not using decimals something strange has happened...
\r
243 divPattern[0] = null;
\r
244 divPattern[1] = null;
\r
245 canUseDecimals = false;
\r
246 } catch(Exception e2) {
\r
259 public String formatValue(double value) {
\r
260 return _formatValue(internalPattern, value);
\r
265 * Note: cannot DecimalFormatter syntax does not work with this method
\r
267 * @param numDecimals
\r
270 public String formatValue(double value, int numDecimals) {
\r
271 if (canUseDecimals) {
\r
272 if (numDecimals < 0)
\r
274 return _formatValue(value, numDecimals);
\r
276 return _formatValue(internalPattern, value);
\r
282 private String _formatValue(String pattern, double value) {
\r
284 if (!useDecimalFormat) {
\r
287 return String.format(pattern, args);
\r
289 args[0] = new Long((long)value);
\r
290 return String.format(pattern, args);
\r
293 DecimalFormat format = new DecimalFormat(pattern);
\r
294 return format.format(value);
\r
298 private String _formatValue(double value, int decimals) {
\r
301 return String.format(internalPatternDivided[0] + "." + decimals + internalPatternDivided[1], args);
\r
305 public String getPattern() {
\r
309 public double getScale() {
\r
313 public String toString() {
\r
319 public boolean equals(Object obj) {
\r
320 if (this == obj) return true;
\r
321 if (obj == null) return false;
\r
322 if (!(obj instanceof MetricsFormat)) return false;
\r
323 MetricsFormat other = (MetricsFormat) obj;
\r
324 if (other.hash!=hash) return false;
\r
325 return other.pattern.equals(pattern) && other.scale==scale && other.name.equals(name);
\r
328 private int makeHash() {
\r
329 long longBits = Double.doubleToLongBits(scale);
\r
330 int scaleHash = ((int)longBits)^(int)(longBits<<16);
\r
331 return pattern.hashCode() ^ scaleHash ^ name.hashCode();
\r
334 public int hashCode() {
\r
339 private void changeContent(MetricsFormat newContent) {
\r
340 this.name = newContent.name;
\r
341 this.pattern = newContent.pattern;
\r
342 this.scale = newContent.scale;
\r
343 this.internalPattern = newContent.internalPattern;
\r
344 this.hash = newContent.hash;
\r