--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.utils.strings.format;\r
+\r
+import java.io.Serializable;\r
+import java.text.DecimalFormat;\r
+import java.util.IllegalFormatConversionException;\r
+import java.util.IllegalFormatException;\r
+\r
+/**\r
+ * Value metrics data\r
+ * \r
+ * Use 'S' Prefix to use String formatting rules http://java.sun.com/javase/6/docs/api/java/util/Formatter.html#syntax\r
+ * Use 'D' Prefix to use DecimalFormat rules http://java.sun.com/javase/6/docs/api/java/text/DecimalFormat.html\r
+ * \r
+ * If no prefix is detected, String rules are used.\r
+ * \r
+ * @author Toni Kalajainen\r
+ * @Author Marko Luukkainen\r
+ */\r
+public class MetricsFormat implements Serializable {\r
+\r
+ /**\r
+ * Define serial version of this class\r
+ */\r
+ private static final long serialVersionUID = 8036591251300342995L;\r
+\r
+ \r
+ /** Presentation format */\r
+ private String pattern;\r
+ \r
+ /** Presentation format */\r
+ private String internalPattern;\r
+ \r
+ /** Presentation format */\r
+ private String[] internalPatternDivided; \r
+ \r
+ /** flag if format can adjust decimals */\r
+ private boolean canUseDecimals = false;\r
+ \r
+ /** scale */\r
+ private double scale;\r
+ \r
+ /** name */\r
+ private String name;\r
+ \r
+ /** hash */\r
+ private int hash;\r
+ \r
+ /** Is double or long */\r
+ private boolean isDouble = true;\r
+ \r
+ private boolean useDecimalFormat = false;\r
+ \r
+ /** Array reserved for formatter use */\r
+ private Object args[] = new Object[1];\r
+ \r
+ \r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ public MetricsFormat(String pattern, double scale, String name)\r
+ throws IllegalArgumentException\r
+ {\r
+ this.scale = scale;\r
+ pattern = setPattern(pattern);\r
+ this.internalPattern = formatPattern( pattern );\r
+ this.internalPatternDivided = formatPatternDivided( pattern );\r
+ this.name = name;\r
+ this.hash = makeHash();\r
+ }\r
+\r
+ public MetricsFormat(String pattern, double scale) \r
+ throws IllegalArgumentException\r
+ {\r
+ this.scale = scale;\r
+ pattern = setPattern(pattern);\r
+ this.internalPattern = formatPattern( pattern );\r
+ this.internalPatternDivided = formatPatternDivided( pattern );\r
+ this.name = "";\r
+ this.hash = makeHash();\r
+ }\r
+ \r
+ public MetricsFormat(String pattern) \r
+ throws IllegalArgumentException\r
+ {\r
+ this.scale = 1.0f;\r
+ pattern = setPattern(pattern);\r
+ this.internalPattern = formatPattern( pattern );\r
+ this.internalPatternDivided = formatPatternDivided( pattern );\r
+ this.name = "";\r
+ this.hash = makeHash();\r
+ \r
+ }\r
+ \r
+ private String setPattern(String pattern) {\r
+ this.pattern = pattern;\r
+ if (pattern.startsWith("D")) {\r
+ pattern = pattern.substring(1);\r
+ useDecimalFormat = true;\r
+ } else {\r
+ if (pattern.startsWith("S"))\r
+ pattern = pattern.substring(1);\r
+ useDecimalFormat = false;\r
+ }\r
+ return pattern;\r
+ }\r
+ \r
+ /**\r
+ * Replacs all % with %1$ unless % precedes with \ \r
+ * \r
+ * @param pattern\r
+ * @return\r
+ */\r
+ private String formatPattern(String pattern) {\r
+ int pos = 0;\r
+ if (!useDecimalFormat) {\r
+ while ( (pos = pattern.indexOf('%', pos)) >= 0 ) {\r
+ if (pos==0 || pattern.indexOf(pos-1)!='\\') {\r
+ pattern = pattern.substring(0, pos+1) + "1$" + pattern.substring(pos+1, pattern.length());\r
+ pos += 3;\r
+ continue;\r
+ }\r
+ pos++; \r
+ }\r
+ }\r
+ \r
+ double value = 0;\r
+ if (useDecimalFormat) {\r
+ isDouble = true;\r
+ useDecimalFormat = true;\r
+ canUseDecimals = false;\r
+ DecimalFormat format = new DecimalFormat(pattern);\r
+ format.format(value);\r
+ \r
+ } else {\r
+ try {\r
+ isDouble = true;\r
+ useDecimalFormat = false;\r
+ args[0] = value;\r
+ String.format(pattern, args); \r
+ } catch(IllegalFormatConversionException e1) {\r
+ try {\r
+ isDouble = false;\r
+ useDecimalFormat = false;\r
+ args[0] = (long)value;\r
+ String.format(pattern, args); \r
+ } catch (Exception e2) {\r
+ throw e1;\r
+ }\r
+ }\r
+ }\r
+ \r
+ return pattern;\r
+ }\r
+ \r
+ /**\r
+ * Replacs all % with %1$ unless % precedes with \ \r
+ * \r
+ * Divides formatString into half so that\r
+ * formats precision can be adjusted with .(n)<br>\r
+ * Currently only format which supports adjustable\r
+ * decimals is f/F (floating point as decimal number)\r
+ * \r
+ * \r
+ * @param pattern\r
+ * @return\r
+ */\r
+ private String[] formatPatternDivided(String pattern) {\r
+ String divPattern[] = new String[]{"",""};\r
+ if (useDecimalFormat)\r
+ return divPattern;\r
+ int pos = 0;\r
+ while ( (pos = pattern.indexOf('%', pos)) >= 0 ) {\r
+ if (pos==0 || pattern.indexOf(pos-1)!='\\') {\r
+ divPattern[0] = pattern.substring(0, pos+1) + "1$";\r
+ divPattern[1] = pattern.substring(pos+1, pattern.length());\r
+ // parse flags\r
+ int pos2 = 0;\r
+ while(true) {\r
+ char c = divPattern[1].charAt(pos2);\r
+ if (c == '-' ||\r
+ c == '#' ||\r
+ c == '+' ||\r
+ c == ' ' ||\r
+ c == '0' ||\r
+ c == ',' ||\r
+ c == '(' ||\r
+ Character.isDigit(c))\r
+ pos2++;\r
+ else\r
+ break;\r
+ }\r
+ // get rid of possible precisision setting\r
+ // TODO : maybe we should let the user to set the precision\r
+ // and then use user given precision instead of dynamic precision\r
+ int pos3 = pos2;\r
+ while(true) {\r
+ char c = divPattern[1].charAt(pos3);\r
+ if (c == '.' || Character.isDigit(c)) {\r
+ pos3++;\r
+ } else if (c == 'f'|| c == 'F') {\r
+ this.canUseDecimals = true;\r
+ break;\r
+ } else {\r
+ this.canUseDecimals = false;\r
+ break;\r
+ }\r
+ }\r
+ if (pos2 > 0 || pos3 > 0) {\r
+ divPattern[0] += divPattern[1].substring(0,pos2);\r
+ divPattern[1] = divPattern[1].substring(pos3, divPattern[1].length());\r
+ }\r
+ pos += 3;\r
+ // divided patterns can have only one %\r
+ break;\r
+ }\r
+ pos++; \r
+ }\r
+ \r
+ double value = 0;\r
+ try {\r
+ // checking if format can be used\r
+ args[0] = value;\r
+ String.format(divPattern[0] + ".2" + divPattern[1] , args);\r
+ } catch (IllegalFormatException e1) {\r
+ try {\r
+ pattern = formatPattern(pattern);\r
+ isDouble = false;\r
+ args[0] = (long)value;\r
+ String.format(pattern, args); \r
+ // if we get this far by not using decimals something strange has happened...\r
+ divPattern[0] = null;\r
+ divPattern[1] = null;\r
+ canUseDecimals = false;\r
+ } catch(Exception e2) {\r
+ throw e1;\r
+ }\r
+ }\r
+ \r
+ return divPattern;\r
+ }\r
+ \r
+ /**\r
+ * Formats value\r
+ * @param value\r
+ * @return\r
+ */\r
+ public String formatValue(double value) {\r
+ return _formatValue(internalPattern, value);\r
+ }\r
+ \r
+ /**\r
+ * Format value\r
+ * Note: cannot DecimalFormatter syntax does not work with this method\r
+ * @param value\r
+ * @param numDecimals\r
+ * @return\r
+ */\r
+ public String formatValue(double value, int numDecimals) {\r
+ if (canUseDecimals) {\r
+ if (numDecimals < 0) \r
+ numDecimals = 0;\r
+ return _formatValue(value, numDecimals);\r
+ } else {\r
+ return _formatValue(internalPattern, value);\r
+ }\r
+ \r
+ }\r
+\r
+\r
+ private String _formatValue(String pattern, double value) {\r
+ //value *= scale;\r
+ if (!useDecimalFormat) {\r
+ if (isDouble) {\r
+ args[0] = value;\r
+ return String.format(pattern, args);\r
+ } else {\r
+ args[0] = new Long((long)value);\r
+ return String.format(pattern, args); \r
+ }\r
+ } else {\r
+ DecimalFormat format = new DecimalFormat(pattern);\r
+ return format.format(value);\r
+ }\r
+ }\r
+ \r
+ private String _formatValue(double value, int decimals) {\r
+ //value *= scale;\r
+ args[0] = value;\r
+ return String.format(internalPatternDivided[0] + "." + decimals + internalPatternDivided[1], args);\r
+ \r
+ }\r
+\r
+ public String getPattern() {\r
+ return pattern;\r
+ }\r
+\r
+ public double getScale() {\r
+ return scale;\r
+ }\r
+ \r
+ public String toString() {\r
+ return name;\r
+ }\r
+ \r
+ \r
+ \r
+ public boolean equals(Object obj) {\r
+ if (this == obj) return true;\r
+ if (obj == null) return false;\r
+ if (!(obj instanceof MetricsFormat)) return false;\r
+ MetricsFormat other = (MetricsFormat) obj;\r
+ if (other.hash!=hash) return false;\r
+ return other.pattern.equals(pattern) && other.scale==scale && other.name.equals(name);\r
+ }\r
+ \r
+ private int makeHash() {\r
+ long longBits = Double.doubleToLongBits(scale);\r
+ int scaleHash = ((int)longBits)^(int)(longBits<<16);\r
+ return pattern.hashCode() ^ scaleHash ^ name.hashCode();\r
+ }\r
+ \r
+ public int hashCode() {\r
+ return hash;\r
+ }\r
+\r
+ /*\r
+ private void changeContent(MetricsFormat newContent) {\r
+ this.name = newContent.name;\r
+ this.pattern = newContent.pattern;\r
+ this.scale = newContent.scale;\r
+ this.internalPattern = newContent.internalPattern;\r
+ this.hash = newContent.hash;\r
+ }*/\r
+ \r
+ \r
+ \r
+}\r