001: package jimm.datavision.field;
002:
003: import jimm.datavision.Section;
004: import jimm.datavision.Line;
005: import jimm.datavision.Point;
006: import jimm.datavision.Writeable;
007: import jimm.datavision.layout.LineDrawer;
008: import jimm.util.XMLWriter;
009: import java.awt.Color;
010: import java.util.Observer;
011: import java.util.Observable;
012:
013: /**
014: * A border is a visual decoration around a report field. Each of its four
015: * edges (top, left, bottom, and right) is a {@link BorderEdge} and may be
016: * <code>null</code>.
017: *
018: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
019: */
020: public class Border extends Observable implements Writeable, Cloneable,
021: Observer {
022:
023: protected static final int BORDER_LINE_SPACE_MULT = 3;
024: protected static final Color DEFAULT_COLOR = Color.black;
025:
026: protected Field field;
027: protected BorderEdge top, left, bottom, right; // May be null
028: protected Color color;
029:
030: /**
031: * Constructs a new border for the specified field.
032: *
033: * @param field a report field
034: */
035: public Border(Field field) {
036: this .field = field;
037: }
038:
039: /**
040: * Returns a clone of this border. All edges are cloned as well.
041: */
042: public Object clone() {
043: Border b = new Border(field);
044: BorderEdge e;
045:
046: if ((e = getTop()) == null || e.getNumber() == 0)
047: b.setTop(null);
048: else
049: b.setTop((BorderEdge) e.clone());
050:
051: if ((e = getBottom()) == null || e.getNumber() == 0)
052: b.setBottom(null);
053: else
054: b.setBottom((BorderEdge) e.clone());
055:
056: if ((e = getLeft()) == null || e.getNumber() == 0)
057: b.setLeft(null);
058: else
059: b.setLeft((BorderEdge) e.clone());
060:
061: if ((e = getRight()) == null || e.getNumber() == 0)
062: b.setRight(null);
063: else
064: b.setRight((BorderEdge) e.clone());
065:
066: b.setColor(getColor());
067:
068: return b;
069: }
070:
071: /**
072: * For testing only. Checks color and edges but not field.
073: *
074: * @see jimm.datavision.test.ReportTest#testCloning
075: */
076: public boolean equals(Object obj) {
077: if (obj == null || !(obj instanceof Border))
078: return false;
079: if (obj == this )
080: return true;
081:
082: Border b = (Border) obj;
083: if (color == null && b.color != null)
084: return false;
085: if (color != null && !color.equals(b.color))
086: return false;
087:
088: BorderEdge e, e2;
089: int n, n2;
090: e = top;
091: e2 = b.top;
092: n = e == null ? 0 : e.number;
093: n2 = e2 == null ? 0 : e2.number;
094: if (n != n2)
095: return false;
096:
097: e = left;
098: e2 = b.left;
099: n = e == null ? 0 : e.number;
100: n2 = e2 == null ? 0 : e2.number;
101: if (n != n2)
102: return false;
103:
104: e = bottom;
105: e2 = b.bottom;
106: n = e == null ? 0 : e.number;
107: n2 = e2 == null ? 0 : e2.number;
108: if (n != n2)
109: return false;
110:
111: e = right;
112: e2 = b.right;
113: n = e == null ? 0 : e.number;
114: n2 = e2 == null ? 0 : e2.number;
115: if (n != n2)
116: return false;
117:
118: return true;
119: }
120:
121: public int hashCode() {
122: int hc = 0;
123: if (color != null)
124: hc += color.hashCode();
125: if (top != null)
126: hc += top.hashCode();
127: if (left != null)
128: hc += left.hashCode();
129: if (bottom != null)
130: hc += bottom.hashCode();
131: if (right != null)
132: hc += right.hashCode();
133: return hc;
134: }
135:
136: /**
137: * Used only when cloning a field, this sets our field. Otherwise, don't
138: * call this.
139: *
140: * @param f the new field
141: */
142: public void setField(Field f) {
143: field = f;
144: }
145:
146: protected void finalize() throws Throwable {
147: if (top != null)
148: top.deleteObserver(this );
149: if (left != null)
150: left.deleteObserver(this );
151: if (bottom != null)
152: bottom.deleteObserver(this );
153: if (right != null)
154: right.deleteObserver(this );
155: }
156:
157: public void update(Observable o, Object arg) {
158: setChanged();
159: notifyObservers(arg);
160: }
161:
162: /**
163: * Returns the border's top edge. May return <code>null</code>.
164: *
165: * @return the top edge
166: */
167: public BorderEdge getTop() {
168: return top;
169: }
170:
171: /**
172: * Sets the top edge. <i>newTop</i> may be <code>null</code>.
173: *
174: * @param newTop the new edge
175: */
176: public void setTop(BorderEdge newTop) {
177: if (top != newTop) {
178: if (top != null)
179: top.deleteObserver(this );
180: top = newTop;
181: if (top != null)
182: top.addObserver(this );
183: setChanged();
184: notifyObservers();
185: }
186: }
187:
188: /**
189: * Returns the border's left edge. May return <code>null</code>.
190: *
191: * @return the left edge
192: */
193: public BorderEdge getLeft() {
194: return left;
195: }
196:
197: /**
198: * Sets the left edge. <i>newLeft</i> may be <code>null</code>.
199: *
200: * @param newLeft the new edge
201: */
202: public void setLeft(BorderEdge newLeft) {
203: if (left != newLeft) {
204: if (left != null)
205: left.deleteObserver(this );
206: left = newLeft;
207: if (left != null)
208: left.addObserver(this );
209: setChanged();
210: notifyObservers();
211: }
212: }
213:
214: /**
215: * Returns the border's bottom edge. May return <code>null</code>.
216: *
217: * @return the bottom edge
218: */
219: public BorderEdge getBottom() {
220: return bottom;
221: }
222:
223: /**
224: * Sets the bottom edge. <i>newBottom</i> may be <code>null</code>.
225: *
226: * @param newBottom the new edge
227: */
228: public void setBottom(BorderEdge newBottom) {
229: if (bottom != newBottom) {
230: if (bottom != null)
231: bottom.deleteObserver(this );
232: bottom = newBottom;
233: if (bottom != null)
234: bottom.addObserver(this );
235: setChanged();
236: notifyObservers();
237: }
238: }
239:
240: /**
241: * Returns the border's right edge. May return <code>null</code>.
242: *
243: * @return the right edge
244: */
245: public BorderEdge getRight() {
246: return right;
247: }
248:
249: /**
250: * Sets the right edge. <i>newRight</i> may be <code>null</code>.
251: *
252: * @param newRight the new edge
253: */
254: public void setRight(BorderEdge newRight) {
255: if (right != newRight) {
256: if (right != null)
257: right.deleteObserver(this );
258: right = newRight;
259: if (right != null)
260: right.addObserver(this );
261: setChanged();
262: notifyObservers();
263: }
264: }
265:
266: /**
267: * Retrieves the border color.
268: *
269: * @return the border color
270: */
271: public Color getColor() {
272: return color;
273: }
274:
275: /**
276: * Sets the border color.
277: *
278: * @param c the new color
279: */
280: public void setColor(Color c) {
281: if (color != c) {
282: color = c;
283: setChanged();
284: notifyObservers();
285: }
286: }
287:
288: /**
289: * For each edge, hand the lines that make up that edge to the specified
290: * line drawer's <code>drawLine</code> method.
291: *
292: * @param ld a line drawer
293: */
294: public void eachLine(LineDrawer ld, Object arg) {
295: Rectangle rect = field.getBounds();
296: Section section = field.getSection();
297: BorderEdge edge;
298: Line line;
299:
300: edge = top;
301: if (edge != null && edge.getThickness() > 0
302: && edge.getNumber() > 0) {
303: line = new Line(null, section, edge.getThickness(), null,
304: true, new Point(rect.x, rect.y), new Point(rect.x
305: + rect.width, rect.y));
306: for (int i = 0; i < edge.getNumber(); ++i) {
307: ld.drawLine(line, arg);
308: line.getPoint(0).y += BORDER_LINE_SPACE_MULT;
309: line.getPoint(1).y += BORDER_LINE_SPACE_MULT;
310: }
311: }
312:
313: edge = bottom;
314: if (edge != null && edge.getThickness() > 0
315: && edge.getNumber() > 0) {
316: line = new Line(
317: null,
318: section,
319: edge.getThickness(),
320: null,
321: true,
322: new Point(rect.x, rect.y + rect.height),
323: new Point(rect.x + rect.width, rect.y + rect.height));
324: for (int i = 0; i < edge.getNumber(); ++i) {
325: ld.drawLine(line, arg);
326: line.getPoint(0).y -= BORDER_LINE_SPACE_MULT;
327: line.getPoint(1).y -= BORDER_LINE_SPACE_MULT;
328: }
329: }
330:
331: edge = left;
332: if (edge != null && edge.getThickness() > 0
333: && edge.getNumber() > 0) {
334: line = new Line(null, section, edge.getThickness(), null,
335: true, new Point(rect.x, rect.y), new Point(rect.x,
336: rect.y + rect.height));
337: for (int i = 0; i < edge.getNumber(); ++i) {
338: ld.drawLine(line, arg);
339: line.getPoint(0).x += BORDER_LINE_SPACE_MULT;
340: line.getPoint(1).x += BORDER_LINE_SPACE_MULT;
341: }
342: }
343:
344: edge = right;
345: if (edge != null && edge.getThickness() > 0
346: && edge.getNumber() > 0) {
347: line = new Line(
348: null,
349: section,
350: edge.getThickness(),
351: null,
352: true,
353: new Point(rect.x + rect.width, rect.y),
354: new Point(rect.x + rect.width, rect.y + rect.height));
355: for (int i = 0; i < edge.getNumber(); ++i) {
356: ld.drawLine(line, arg);
357: line.getPoint(0).x -= BORDER_LINE_SPACE_MULT;
358: line.getPoint(1).x -= BORDER_LINE_SPACE_MULT;
359: }
360: }
361: }
362:
363: /**
364: * Returns <code>true</code> if this border's edges are all
365: * <code>null</code> or have zero count or width.
366: *
367: * @return <code>true</code> if this border's edges are all
368: * <code>null</code> or have zero count or width
369: */
370: public boolean isEmpty() {
371: return isEmptyEdge(top) && isEmptyEdge(bottom)
372: && isEmptyEdge(left) && isEmptyEdge(right);
373: }
374:
375: /**
376: * Returns <code>true</code> if <var>edge</var> is <code>null</code> or has
377: * zero count or width.
378: *
379: * @return <code>true</code> if <var>edge</var> is <code>null</code> or has
380: * zero count or width
381: */
382: protected boolean isEmptyEdge(BorderEdge edge) {
383: return edge == null || edge.getNumber() == 0
384: || edge.getThickness() == 0;
385: }
386:
387: /**
388: * Writes this border as an XML tag. Asks each edge to write itself as well.
389: * If {@link #isEmpty} returns <code>true</code>, returns without writing
390: * anything.
391: *
392: * @param out a writer that knows how to write XML
393: */
394: public void writeXML(XMLWriter out) {
395: if (isEmpty())
396: return;
397:
398: out.startElement("border");
399: if (color != null && !color.equals(DEFAULT_COLOR))
400: out.attr("color", color);
401:
402: if (top != null && top.getNumber() != 0)
403: top.writeXML(out, "top");
404: if (bottom != null && bottom.getNumber() != 0)
405: bottom.writeXML(out, "bottom");
406: if (left != null && left.getNumber() != 0)
407: left.writeXML(out, "left");
408: if (right != null && right.getNumber() != 0)
409: right.writeXML(out, "right");
410:
411: out.endElement();
412: }
413:
414: }
|