package com.onaro.sanscreen.client.view.tabular.value; import java.awt.Color; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.HashMap; import java.util.Map; import javax.swing.Icon; import org.apache.commons.lang3.StringUtils; import com.onaro.commons.text.SmallDoubleFormat; public class NumberValue implements Comparable, ValueDecoration, Serializable { private static final long serialVersionUID = 1L; protected double number; private String cachedStringValue; private String format; private static final Map formatters; public static final String FORMAT_DEFAULT = "default"; //$NON-NLS-1$ public static final String FORMAT_DOUBLE = "double"; //$NON-NLS-1$ public static final String FORMAT_SINGLE_DIGIT_DOUBLE = "double.single.digit"; //$NON-NLS-1$ public static final String FORMAT_PERCENT = "percent"; //$NON-NLS-1$ /** * This format is used to display a percentage value without multiplying by 100 (which is the behavior of * FORMAT_PERCENT) */ public static final String FORMAT_SIMPLE_PERCENT = "simple.percent"; //$NON-NLS-1$ /** * This format is used to display a whole-number percentage, unlike FORMAT_PERCENT which shows one * fractional digit and FORMAT_SIMPLE_PERCENT which shows two fractional digits. */ public static final String FORMAT_ROUNDED_PERCENT = "rounded.percent"; //$NON-NLS-1$ public static final String FORMAT_INTEGER = "integer"; //$NON-NLS-1$ static { formatters = new HashMap(); formatters.put(FORMAT_DEFAULT, DecimalFormat.getInstance()); formatters.put(FORMAT_DOUBLE, new SmallDoubleFormat()); formatters.put(FORMAT_INTEGER, NumberFormat.getIntegerInstance()); formatters.put(FORMAT_ROUNDED_PERCENT, NumberFormat.getPercentInstance()); NumberFormat percentInstance = DecimalFormat.getPercentInstance(); percentInstance.setMinimumFractionDigits(1); percentInstance.setMaximumFractionDigits(1); formatters.put(FORMAT_PERCENT, percentInstance); // TODO: I18N this number format!!! NumberFormat simplePercentInstance = new DecimalFormat("##0.00'%'"); //$NON-NLS-1$ formatters.put(FORMAT_SIMPLE_PERCENT, simplePercentInstance); NumberFormat doubleSingleDigitInstance = DecimalFormat.getInstance(); doubleSingleDigitInstance.setMinimumFractionDigits(1); doubleSingleDigitInstance.setMaximumFractionDigits(1); formatters.put(FORMAT_SINGLE_DIGIT_DOUBLE, doubleSingleDigitInstance); } /** * Optional identifier. If specified, it will be used by {@link com.onaro.sanscreen.client.view.tabular.value.NumberValue#equals} and * {@link com.onaro.sanscreen.client.view.tabular.value.NumberValue#hashCode} ao two nodes having the same identifier will be considered equals. */ private String identifier; private String unitsText; private ValueDecoration valueDecoration = ValueDecoration.NONE; public NumberValue(double number, String identifier, String format) { if (! isValidFormat (format)) { throw new IllegalArgumentException ("Invalid format '" + format + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } this.number = number; this.identifier = identifier; this.format = format; } public NumberValue(double number, String unitsText, String identifer, String format, String valueDecoration) { this(number, unitsText, identifer, format); this.valueDecoration = valueDecoration != null ? new ValueDecorationImpl(valueDecoration) : ValueDecoration.NONE; } public NumberValue(double number, String unitsText, String identifier, String format) { if (! isValidFormat (format)) { throw new IllegalArgumentException ("Invalid format '" + format + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } this.number = number; this.identifier = identifier; this.format = format; if (unitsText != null && unitsText.trim().length() == 0) { this.unitsText = null; } else { this.unitsText = unitsText; } } public NumberValue(double number, String identifer) { this(number, identifer, FORMAT_DEFAULT); } public NumberValue(double number) { this(number, null, FORMAT_DEFAULT); } protected void setNumber (double newNumber) { number = newNumber; } protected void setFormat (String newFormat) { if (! isValidFormat (newFormat)) { throw new IllegalArgumentException ("Invalid format '" + newFormat + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } format = newFormat; } public int compareTo(NumberValue o) { if(o != null){ return Double.compare(getNumber(), o.getNumber()); }else{ return 1; } } public boolean isPercentFormat() { return isPercentFormat (format); } public static boolean isPercentFormat (String format) { return (NumberValue.FORMAT_PERCENT.equals (format) || NumberValue.FORMAT_SIMPLE_PERCENT.equals (format) || NumberValue.FORMAT_ROUNDED_PERCENT.equals (format)); } // Get value as a Number. Simple percentages (0 - 100%) are converted to ratios (0 - 1.0). public Number getNumberObject() { // Check for out of range values. if (Double.isNaN (number) || Double.isInfinite (number)) { return new Double (number); } // Check for rounding errors. if (number <= Float.MIN_NORMAL && number >= -Float.MIN_NORMAL) { number = 0.0; } // Return integer values as longs. if (NumberValue.FORMAT_INTEGER.equals (format)) { return new Long (Math.round (number)); } // Convert "simple percent" values (e.g., 50%) to ratios (e.g., 0.5). if (NumberValue.FORMAT_SIMPLE_PERCENT.equals (format) && number != 0.0) { return new Double (number / 100.0); } // Other values and formats require no conversion. return new Double (number); } public double getNumber() { return number; } public String toString() { if(cachedStringValue == null){ if (unitsText != null) { StringBuilder sb = new StringBuilder(format(format, getNumber())); sb.append(" ").append(unitsText); //$NON-NLS-1$ cachedStringValue = sb.toString(); } else cachedStringValue = format(format, getNumber()); } return cachedStringValue; } public static String format(String formatName, double number) { NumberFormat formatter = getFormat (formatName); return formatter == null? StringUtils.EMPTY : formatter.format (number); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NumberValue)) return false; final NumberValue numberValue = (NumberValue) o; if (identifier != null || numberValue.identifier != null) { if (identifier != null ? !identifier.equals(numberValue.identifier) : numberValue.identifier != null) return false; else return true; } return number == numberValue.number; } public int hashCode() { if (identifier != null) { return identifier.hashCode(); } final long temp = number != +0.0d ? Double.doubleToLongBits(number) : 0l; return (int) (temp ^ (temp >>> 32)); } public String getUnitsText() { return unitsText; } public static boolean isValidFormat(String formatName) { assert formatName != null : "Illegal (null) format name"; //$NON-NLS-1$ assert formatters.containsKey (formatName) : "Illegal format name '" + formatName + "'"; //$NON-NLS-1$ //$NON-NLS-2$ return formatters.containsKey(formatName); } public static NumberFormat getFormat(String formatName) { assert isValidFormat (formatName); return formatters.get(formatName); } public Color getBackground() { return valueDecoration.getBackground(); } public Color getForeground() { return valueDecoration.getForeground(); } public Icon getIcon() { return valueDecoration.getIcon(); } }