]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils/src/org/simantics/utils/strings/format/MetricsFormat.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.utils / src / org / simantics / utils / strings / format / MetricsFormat.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.utils.strings.format;
13
14 import java.io.Serializable;
15 import java.text.DecimalFormat;
16 import java.util.IllegalFormatConversionException;
17 import java.util.IllegalFormatException;
18
19 /**
20  * Value metrics data
21  * 
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
24  * 
25  * If no prefix is detected, String rules are used.
26  * 
27  * @author Toni Kalajainen
28  * @Author Marko Luukkainen
29  */
30 public class MetricsFormat implements Serializable {
31
32     /**
33      * Define serial version of this class
34      */
35     private static final long serialVersionUID = 8036591251300342995L;
36
37     
38     /** Presentation format */
39     private String pattern;
40         
41     /** Presentation format */
42     private String internalPattern;
43     
44     /** Presentation format */
45     private String[] internalPatternDivided;   
46     
47     /** flag if format can adjust decimals */
48     private boolean canUseDecimals = false;
49         
50     /** scale */
51     private double scale;
52     
53     /** name */
54     private String name;
55     
56     /** hash */
57     private int hash;
58     
59     /** Is double or long */
60     private boolean isDouble = true;
61     
62     private boolean useDecimalFormat = false;
63     
64     /** Array reserved for formatter use */
65     private Object args[] = new Object[1];
66     
67     
68     public String getName() {
69         return name;
70     }
71
72     public MetricsFormat(String pattern, double scale, String name)
73     throws IllegalArgumentException
74     {
75         this.scale  = scale;
76         pattern = setPattern(pattern);
77         this.internalPattern = formatPattern( pattern );
78         this.internalPatternDivided = formatPatternDivided( pattern );
79         this.name = name;
80         this.hash = makeHash();
81     }
82
83     public MetricsFormat(String pattern, double scale) 
84     throws IllegalArgumentException
85     {
86         this.scale  = scale;
87         pattern = setPattern(pattern);
88         this.internalPattern = formatPattern( pattern );
89         this.internalPatternDivided = formatPatternDivided( pattern );
90         this.name = "";
91         this.hash = makeHash();
92     }
93     
94     public MetricsFormat(String pattern) 
95     throws IllegalArgumentException
96     {
97         this.scale  = 1.0f;
98         pattern = setPattern(pattern);
99         this.internalPattern = formatPattern( pattern );
100         this.internalPatternDivided = formatPatternDivided( pattern );
101         this.name = "";
102         this.hash = makeHash();
103         
104     }
105     
106     private String setPattern(String pattern) {
107         this.pattern = pattern;
108         if (pattern.startsWith("D")) {
109                 pattern = pattern.substring(1);
110                 useDecimalFormat = true;
111         } else {
112                 if (pattern.startsWith("S"))
113                         pattern = pattern.substring(1);
114                 useDecimalFormat = false;
115         }
116         return pattern;
117     }
118     
119     /**
120      * Replacs all % with %1$ unless % precedes with \ 
121      * 
122      * @param pattern
123      * @return
124      */
125     private String formatPattern(String pattern) {
126         int pos = 0;
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());
131                     pos += 3;
132                     continue;
133                 }
134                 pos++;    
135             }
136         }
137                 
138         double value = 0;
139         if (useDecimalFormat) {
140                 isDouble = true;
141                 useDecimalFormat = true;
142                 canUseDecimals = false;
143                 DecimalFormat format = new DecimalFormat(pattern);
144                 format.format(value);
145             
146         } else {
147             try {
148                 isDouble = true;
149                 useDecimalFormat = false;
150                 args[0] = value;
151                 String.format(pattern, args);           
152             } catch(IllegalFormatConversionException e1) {
153                 try {
154                         isDouble = false;
155                         useDecimalFormat = false;
156                     args[0] = (long)value;
157                     String.format(pattern, args); 
158                 } catch (Exception e2) {
159                         throw e1;
160                 }
161             }
162         }
163         
164         return pattern;
165     }
166     
167     /**
168      * Replacs all % with %1$ unless % precedes with \ 
169      * 
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)
174      * 
175      * 
176      * @param pattern
177      * @return
178      */
179     private String[] formatPatternDivided(String pattern) {
180         String divPattern[] = new String[]{"",""};
181         if (useDecimalFormat)
182                 return divPattern;
183         int pos = 0;
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());
188                 // parse flags
189                 int pos2 = 0;
190                 while(true) {
191                     char c = divPattern[1].charAt(pos2);
192                     if (c == '-' ||
193                         c == '#' ||
194                         c == '+' ||
195                         c == ' ' ||
196                         c == '0' ||
197                         c == ',' ||
198                         c == '(' ||
199                         Character.isDigit(c))
200                         pos2++;
201                     else
202                         break;
203                 }
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
207                 int pos3 = pos2;
208                 while(true) {
209                     char c = divPattern[1].charAt(pos3);
210                     if (c == '.' || Character.isDigit(c)) {
211                         pos3++;
212                     } else if (c == 'f'|| c == 'F') {
213                         this.canUseDecimals = true;
214                         break;
215                     } else {
216                         this.canUseDecimals = false;
217                         break;
218                     }
219                 }
220                 if (pos2 > 0 || pos3 > 0) {
221                     divPattern[0] += divPattern[1].substring(0,pos2);
222                     divPattern[1] = divPattern[1].substring(pos3, divPattern[1].length());
223                 }
224                 pos += 3;
225                 // divided patterns can have only one %
226                 break;
227             }
228             pos++;    
229         }
230                 
231         double value = 0;
232         try {
233             // checking if format can be used
234             args[0] = value;
235             String.format(divPattern[0] + ".2" + divPattern[1] , args);
236         } catch (IllegalFormatException e1) {
237             try {
238                 pattern = formatPattern(pattern);
239                 isDouble = false;
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) {
247                 throw e1;
248             }
249         }
250         
251         return divPattern;
252     }
253     
254     /**
255      * Formats value
256      * @param value
257      * @return
258      */
259     public String formatValue(double value) {
260         return _formatValue(internalPattern, value);
261     }
262     
263     /**
264      * Format value
265      * Note: cannot DecimalFormatter syntax does not work with this method
266      * @param value
267      * @param numDecimals
268      * @return
269      */
270     public String formatValue(double value, int numDecimals) {
271         if (canUseDecimals) {
272             if (numDecimals < 0) 
273                 numDecimals = 0;
274             return _formatValue(value, numDecimals);
275         } else {
276             return _formatValue(internalPattern, value);
277         }
278     
279     }
280
281
282     private String _formatValue(String pattern, double value) {
283         //value *= scale;
284         if (!useDecimalFormat) {
285                 if (isDouble) {
286                         args[0] = value;
287                         return String.format(pattern, args);
288                 } else {
289                         args[0] = new Long((long)value);
290                         return String.format(pattern, args);                
291                 }
292         } else {
293                 DecimalFormat format = new DecimalFormat(pattern);
294                 return format.format(value);
295         }
296     }
297     
298     private String _formatValue(double value, int decimals) {
299         //value *= scale;
300         args[0] = value;
301         return String.format(internalPatternDivided[0] + "." + decimals + internalPatternDivided[1], args);
302              
303     }
304
305     public String getPattern() {
306         return pattern;
307     }
308
309     public double getScale() {
310         return scale;
311     }
312     
313     public String toString() {
314         return name;
315     }
316     
317    
318     
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);
326     }
327     
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();
332     }
333     
334     public int hashCode() {
335         return hash;
336     }
337
338     /*
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;
345     }*/
346     
347      
348     
349 }