001: package jimm.datavision.field;
002:
003: import jimm.util.StringUtils;
004: import java.awt.FontMetrics;
005: import java.util.*;
006: import java.text.SimpleDateFormat;
007: import java.text.DecimalFormat;
008: import javax.swing.JLabel;
009:
010: /**
011: * Helps avoid multiple expensive formatting and font size calculations. Each
012: * {@link Field} holds on to one of these and asks it for the formatted
013: * (wrapped) version of its value or the height needed to output the formatted
014: * value.
015: *
016: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
017: */
018: class FormattedValueCache implements Observer {
019:
020: protected static final double LINE_SIZE_FUDGE_FACTOR = 1.2;
021: protected static final HashMap decimalFormatters = new HashMap();
022: protected static final HashMap dateFormatters = new HashMap();
023: protected static JLabel wrappingCalculationsLabel;
024:
025: protected Field field;
026: protected Object value;
027: protected String formatted;
028: protected double height;
029:
030: FormattedValueCache(Field f) {
031: field = f;
032: if (field != null) {
033: field.addObserver(this );
034: if (field.getFormat() != null)
035: field.getFormat().addObserver(this );
036: }
037: }
038:
039: protected void finalize() throws Throwable {
040: if (field != null) {
041: field.deleteObserver(this );
042: if (field.getFormat() != null)
043: field.getFormat().deleteObserver(this );
044: }
045: }
046:
047: /** When format changes, erase value so we recalculate it */
048: public void update(Observable o, Object arg) {
049: if (field != null && field.getFormat() != null)
050: value = null;
051: }
052:
053: String getFormattedString(Object val) {
054: if (notSameAs(val)) {
055: value = val;
056: calcValues();
057: }
058: return formatted;
059: }
060:
061: double getOutputHeight(Object val) {
062: if (notSameAs(val)) {
063: value = val;
064: calcValues();
065: }
066: return height;
067: }
068:
069: protected boolean notSameAs(Object otherValue) {
070: if (value == null)
071: return otherValue != null;
072: else
073: return !value.equals(otherValue);
074: }
075:
076: /**
077: * Cacluates formatted (wrapped) string and output height.
078: */
079: void calcValues() {
080: if (value == null) {
081: formatted = null;
082: height = 0;
083: return;
084: }
085:
086: Format format = field.getFormat();
087: String fmtStr = format.getFormat();
088:
089: if (value instanceof Number) {
090: if (fmtStr == null)
091: formatted = value.toString();
092: else
093: formatted = getNumberFormatterFor(fmtStr).format(
094: (Number) value);
095: } else if (value instanceof Date) {
096: if (fmtStr == null)
097: formatted = value.toString();
098: else
099: formatted = getDateFormatterFor(fmtStr).format(
100: (Date) value);
101: } else {
102: formatted = value.toString();
103: if (format.isWrap()) {
104: FontMetrics fm = getWrappingCalcsLabel()
105: .getFontMetrics(format.getFont());
106: formatted = StringUtils.join(StringUtils.wrap(
107: formatted, fm, (int) field.getBounds().width),
108: "\n");
109: }
110: }
111:
112: // Height calculation. Field's bounds height is the minimum height.
113: height = field.bounds.height;
114: if (formatted.length() != 0) {
115: List lines = StringUtils.splitIntoLines(formatted);
116: double h = lines.size() * format.getSize()
117: * LINE_SIZE_FUDGE_FACTOR;
118: if (h > height)
119: height = h;
120: }
121: }
122:
123: protected DecimalFormat getNumberFormatterFor(String formatString) {
124: DecimalFormat formatter = (DecimalFormat) decimalFormatters
125: .get(formatString);
126: if (formatter == null) {
127: formatter = new DecimalFormat(formatString);
128: decimalFormatters.put(formatString, formatter);
129: }
130: return formatter;
131: }
132:
133: protected SimpleDateFormat getDateFormatterFor(String formatString) {
134: SimpleDateFormat formatter = (SimpleDateFormat) dateFormatters
135: .get(formatString);
136: if (formatter == null) {
137: formatter = new SimpleDateFormat(formatString);
138: dateFormatters.put(formatString, formatter);
139: }
140: return formatter;
141: }
142:
143: protected static JLabel getWrappingCalcsLabel() {
144: if (wrappingCalculationsLabel == null) // Lazy instantiation
145: wrappingCalculationsLabel = new JLabel();
146: return wrappingCalculationsLabel;
147: }
148: }
|