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