001: package jimm.datavision.field;
002:
003: import jimm.datavision.Writeable;
004: import jimm.util.XMLWriter;
005: import java.awt.Color;
006: import java.awt.Font;
007: import java.util.Observable;
008:
009: /**
010: * A format describes how to display a field. It specifies font family name,
011: * size, attributes (bold, italic, underline, wrap), alignment, and print
012: * format.
013: * <p>
014: * If a field's value is <code>null</code>, then the getter returns the value
015: * of the report's default field's format (which will never be
016: * <code>null</code>.
017: *
018: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
019: */
020: public class Format extends Observable implements Writeable, Cloneable {
021:
022: protected static final String DEFAULT_FONT_FAMILY_NAME = "Times New Roman";
023:
024: protected static final int DEFAULT_SIZE = 11;
025: protected static final Color DEFAULT_COLOR = Color.black;
026:
027: /** Used to specify left alignment. */
028: public static final int ALIGN_LEFT = 0;
029: /** Used to specify center alignment. */
030: public static final int ALIGN_CENTER = 1;
031: /** Used to specify right alignment. */
032: public static final int ALIGN_RIGHT = 2;
033:
034: protected Field field;
035: protected String fontFamilyName;
036: protected Double size; // In points
037: protected Boolean bold;
038: protected Boolean italic;
039: protected Boolean underline;
040: protected Boolean wrap;
041: protected Integer align; // ALIGN_{LEFT,CENTER,RIGHT}
042: protected String format; // Output formatting string
043: protected Color color;
044: protected Font font; // Lazily instantiated
045:
046: /**
047: * Returns an <code>ALIGN_*</code> constant, given one of "left",
048: * "center", or "right". If the specified string is null or is not one
049: * of these values, <code>ALIGN_LEFT</code> is returned.
050: *
051: * @param s the string "left", "center", or "right" (case is not
052: * significant)
053: * @return one of <code>ALIGN_LEFT</code>, <code>ALIGN_CENTER</code>,
054: * or <code>ALIGN_RIGHT</code>
055: */
056: public static int alignFromString(String s) {
057: if (s == null)
058: return ALIGN_LEFT;
059: String copy = s.toLowerCase();
060: if ("center".equals(copy))
061: return ALIGN_CENTER;
062: if ("right".equals(copy))
063: return ALIGN_RIGHT;
064: return ALIGN_LEFT;
065: }
066:
067: /**
068: * Given an <code>ALIGN_*</code> constant, return the string used
069: * to represent that value in a report XML file. If <i>align</i> is not
070: * one of those values, returns "left".
071: *
072: * @param align one of "left", "center", or "right"
073: */
074: public static String alignToString(int align) {
075: switch (align) {
076: case ALIGN_RIGHT:
077: return "right";
078: case ALIGN_CENTER:
079: return "center";
080: default:
081: return "left";
082: }
083: }
084:
085: public static Format createEmptyFormat() {
086: return new Format();
087: }
088:
089: public static Format createDefaultFormat() {
090: return new DefaultFormat();
091: }
092:
093: /**
094: * Constructor. Creates an empty format.
095: */
096: Format() {
097: }
098:
099: /**
100: * Normally you don't need to call this, because {@link Field#setFormat}
101: * calls this.
102: *
103: * @param f a field
104: */
105: void setField(Field f) {
106: field = f;
107: }
108:
109: /**
110: * Returns a clone of this format.
111: */
112: public Object clone() {
113: Format f = new Format();
114: fillClonedField(f);
115: return f;
116: }
117:
118: protected void fillClonedField(Format f) {
119: f.field = field;
120: f.setFontFamilyName(getFontFamilyName());
121: f.setSize(getSize());
122: f.setBold(isBold());
123: f.setItalic(isItalic());
124: f.setUnderline(isUnderline());
125: f.setWrap(isWrap());
126: f.setAlign(getAlign());
127: f.setFormat(getFormat());
128: f.setColor(getColor());
129: }
130:
131: public boolean equals(Object obj) {
132: if (obj == null || !(obj instanceof Format))
133: return false;
134: if (obj == this )
135: return true;
136:
137: Format f = (Format) obj;
138: // These getters never return null, because if our ivars are null we look
139: // up to the default field's format for its value.
140: return getSize() == f.getSize()
141: && isBold() == f.isBold()
142: && isItalic() == f.isItalic()
143: && isUnderline() == f.isUnderline()
144: && isWrap() == f.isWrap()
145: && getAlign() == f.getAlign()
146: && (format == f.getFormat() || (format != null && format
147: .equals(f.getFormat())))
148: && getColor().equals(f.getColor());
149: }
150:
151: public int hashCode() {
152: String formatString = getFormat();
153: return (color == null ? 0 : color.hashCode())
154: + (formatString == null ? 0 : formatString.hashCode())
155: + (int) getSize() + (isBold() ? 101 : 100)
156: + (isItalic() ? 501 : 500)
157: + (isUnderline() ? 1001 : 1000)
158: + (isWrap() ? 5001 : 5000) + (getAlign() + 10000);
159: }
160:
161: /**
162: * Returns this field's report's default field's format (*whew*).
163: */
164: public Format getDefaultFormat() {
165: return field.getReport().getDefaultField().getFormat();
166: }
167:
168: /**
169: * Returns the font family name for this format.
170: *
171: * @return the font family name
172: */
173: public String getFontFamilyName() {
174: return fontFamilyName == null ? getDefaultFormat()
175: .getFontFamilyName() : fontFamilyName;
176: }
177:
178: /**
179: * Sets the font family name
180: *
181: * @param newFontFamilyName the new font family name
182: */
183: public void setFontFamilyName(String newFontFamilyName) {
184: if (newFontFamilyName != null) {
185: newFontFamilyName = newFontFamilyName.trim();
186: if (newFontFamilyName.length() == 0)
187: newFontFamilyName = null;
188: }
189:
190: if (fontFamilyName != newFontFamilyName
191: && (fontFamilyName == null || !fontFamilyName
192: .equals(newFontFamilyName))) {
193: fontFamilyName = newFontFamilyName;
194: font = null;
195: setChanged();
196: notifyObservers();
197: }
198: }
199:
200: /**
201: * Based on our font family name, alignment flags, and size, return a
202: * font. Never returns <code>null</code>.
203: *
204: * @return a font; never <code>null</code>
205: */
206: public Font getFont() {
207: if (font == null) {
208: String name = getFontFamilyName();
209:
210: int style = 0;
211: if (isBold())
212: style = Font.BOLD;
213: if (isItalic())
214: style |= Font.ITALIC;
215: if (style == 0)
216: style = Font.PLAIN;
217:
218: font = new Font(name, style, (int) getSize());
219: }
220: return font;
221: }
222:
223: /** Clears the font we may be holding on to. */
224: public void clearFontCache() {
225: font = null;
226: }
227:
228: /**
229: * Returns the size for this format.
230: *
231: * @return the size
232: */
233: public double getSize() {
234: return size == null ? getDefaultFormat().getSize() : size
235: .doubleValue();
236: }
237:
238: /**
239: * Sets the size
240: *
241: * @param newSize the new size
242: */
243: public void setSize(double newSize) {
244: Double newSizeObj = new Double(newSize);
245: if (size != newSizeObj
246: && (size == null || !size.equals(newSizeObj))) {
247: size = newSizeObj;
248: setChanged();
249: notifyObservers();
250: }
251: }
252:
253: /**
254: * Returns the bold state.
255: *
256: * @return the bold state
257: */
258: public boolean isBold() {
259: return bold == null ? getDefaultFormat().isBold() : bold
260: .booleanValue();
261: }
262:
263: /**
264: * Sets the bold state.
265: *
266: * @param newBold the new value
267: */
268: public void setBold(boolean newBold) {
269: Boolean newBoldObj = newBold ? Boolean.TRUE : Boolean.FALSE;
270: if (bold != newBoldObj
271: && (bold == null || !bold.equals(newBoldObj))) {
272: bold = newBoldObj;
273: setChanged();
274: notifyObservers();
275: }
276: }
277:
278: /**
279: * Returns the italic state.
280: *
281: * @return the italic state
282: */
283: public boolean isItalic() {
284: return italic == null ? getDefaultFormat().isItalic() : italic
285: .booleanValue();
286: }
287:
288: /**
289: * Sets the italic state.
290: *
291: * @param newItalic the new value
292: */
293: public void setItalic(boolean newItalic) {
294: Boolean newItalicObj = newItalic ? Boolean.TRUE : Boolean.FALSE;
295: if (italic != newItalicObj
296: && (italic == null || !italic.equals(newItalicObj))) {
297: italic = newItalicObj;
298: setChanged();
299: notifyObservers();
300: }
301: }
302:
303: /**
304: * Returns the underline state.
305: *
306: * @return the underline state
307: */
308: public boolean isUnderline() {
309: return underline == null ? getDefaultFormat().isUnderline()
310: : underline.booleanValue();
311: }
312:
313: /**
314: * Sets the underline state.
315: *
316: * @param newUnderline the new underline state
317: */
318: public void setUnderline(boolean newUnderline) {
319: Boolean newUnderlineObj = newUnderline ? Boolean.TRUE
320: : Boolean.FALSE;
321: if (underline != newUnderlineObj
322: && (underline == null || !underline
323: .equals(newUnderlineObj))) {
324: underline = newUnderlineObj;
325: setChanged();
326: notifyObservers();
327: }
328: }
329:
330: /**
331: * Returns the wrap state.
332: *
333: * @return the wrap state
334: */
335: public boolean isWrap() {
336: return wrap == null ? getDefaultFormat().isWrap() : wrap
337: .booleanValue();
338: }
339:
340: /**
341: * Sets the wrap state.
342: *
343: * @param newWrap the new wrap state
344: */
345: public void setWrap(boolean newWrap) {
346: Boolean newWrapObj = newWrap ? Boolean.TRUE : Boolean.FALSE;
347: if (wrap != newWrapObj
348: && (wrap == null || !wrap.equals(newWrapObj))) {
349: wrap = newWrapObj;
350: setChanged();
351: notifyObservers();
352: }
353: }
354:
355: /**
356: * Returns the alignment.
357: *
358: * @return one of the <code>ALIGN_*</code> values
359: */
360: public int getAlign() {
361: return align == null ? getDefaultFormat().getAlign() : align
362: .intValue();
363: }
364:
365: /**
366: * Sets the alignment.
367: *
368: * @param newAlign one of the <code>ALIGN_*</code> values
369: */
370: public void setAlign(int newAlign) {
371: Integer newAlignObj = new Integer(newAlign);
372: if (align != newAlignObj
373: && (align == null || !align.equals(newAlignObj))) {
374: align = newAlignObj;
375: setChanged();
376: notifyObservers();
377: }
378: }
379:
380: /**
381: * Returns the format string for this field. May return <code>null</code>.
382: *
383: * @return the format string, possibly <code>null</code>
384: */
385: public String getFormat() {
386: return format == null ? getDefaultFormat().getFormat() : format;
387: }
388:
389: /**
390: * Sets the format string.
391: *
392: * @param newFormat the new format string
393: */
394: public void setFormat(String newFormat) {
395: if (format != newFormat
396: && (format == null || !format.equals(newFormat))) {
397: format = newFormat;
398: setChanged();
399: notifyObservers();
400: }
401: }
402:
403: /**
404: * Returns the color for this format.
405: *
406: * @return the color
407: */
408: public Color getColor() {
409: return color == null ? getDefaultFormat().getColor() : color;
410: }
411:
412: /**
413: * Sets the color
414: *
415: * @param newColor the new color
416: */
417: public void setColor(Color newColor) {
418: if (color != newColor) {
419: color = newColor;
420: setChanged();
421: notifyObservers();
422: }
423: }
424:
425: /**
426: * Returns a string representation of this format. Mainly used for
427: * debugging.
428: *
429: * @return pretty string, inn't it?
430: */
431: public String toString() {
432: return "Format[size="
433: + size
434: + ", bold="
435: + bold
436: + ", italic="
437: + italic
438: + ", underline="
439: + underline
440: + ", wrap="
441: + wrap
442: + ", align="
443: + (align == null ? "null" : alignToString(align
444: .intValue())) + ", format=" + format
445: + ", color=" + color + ", font=" + fontFamilyName + "]";
446: }
447:
448: /**
449: * Writes this format as an XML tag. Only writes the differences between this
450: * format and the default one.
451: *
452: * @param out a writer that knows how to write XML
453: */
454: public void writeXML(XMLWriter out) {
455: Format def = getDefaultFormat();
456:
457: if (this .equals(def)) // Don't write anything if there are no diffs
458: return;
459:
460: out.startElement("format");
461: if (fontFamilyName != null
462: && !def.getFontFamilyName().equals(fontFamilyName))
463: out.attr("font", fontFamilyName);
464: if (size != null && size.doubleValue() != def.getSize())
465: out.attr("size", size);
466: if (bold != null && bold.booleanValue() != def.isBold())
467: out.attr("bold", bold);
468: if (italic != null && italic.booleanValue() != def.isItalic())
469: out.attr("italic", italic);
470: if (underline != null
471: && underline.booleanValue() != def.isUnderline())
472: out.attr("underline", underline);
473: if (wrap != null && wrap.booleanValue() != def.isWrap())
474: out.attr("wrap", wrap);
475: if (align != null && align.intValue() != def.getAlign())
476: out.attr("align", alignToString(align.intValue()));
477: if (color != null && !color.equals(def.getColor()))
478: out.attr("color", color);
479: if (format != null && !format.equals(def.getFormat())
480: && format.length() > 0)
481: out.attr("format", format);
482: out.endElement();
483: }
484:
485: }
486:
487: /** Only used by the report. */
488: class DefaultFormat extends Format {
489:
490: /**
491: * Creates a format with all default values filled in.
492: */
493: DefaultFormat() {
494: fontFamilyName = DEFAULT_FONT_FAMILY_NAME;
495: size = new Double(DEFAULT_SIZE);
496: bold = italic = underline = Boolean.FALSE;
497: wrap = Boolean.TRUE;
498: align = new Integer(ALIGN_LEFT);
499: color = DEFAULT_COLOR;
500: }
501:
502: public String getFormat() {
503: return format;
504: }
505:
506: /**
507: * Returns a clone of this format.
508: */
509: public Object clone() {
510: DefaultFormat f = new DefaultFormat();
511: fillClonedField(f);
512: return f;
513: }
514:
515: /**
516: * Writes this format as an XML tag.
517: *
518: * @param out a writer that knows how to write XML
519: */
520: public void writeXML(XMLWriter out) {
521: out.startElement("format");
522: out.attr("font", fontFamilyName);
523: out.attr("size", size);
524: out.attr("bold", bold);
525: out.attr("italic", italic);
526: out.attr("underline", underline);
527: out.attr("wrap", wrap);
528: out.attr("align", alignToString(align.intValue()));
529: out.attr("color", color);
530: if (format != null && format.length() > 0)
531: out.attr("format", format);
532: out.endElement();
533: }
534:
535: }
|