001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ------------------
028: * AbstractBlock.java
029: * ------------------
030: * (C) Copyright 2004-2007, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: AbstractBlock.java,v 1.12.2.6 2007/04/04 09:14:20 mungady Exp $
036: *
037: * Changes:
038: * --------
039: * 22-Oct-2004 : Version 1 (DG);
040: * 02-Feb-2005 : Added accessor methods for margin (DG);
041: * 04-Feb-2005 : Added equals() method and implemented Serializable (DG);
042: * 03-May-2005 : Added null argument checks (DG);
043: * 06-May-2005 : Added convenience methods for setting margin, border and
044: * padding (DG);
045: * ------------- JFREECHART 1.0.x ---------------------------------------------
046: * 16-Mar-2007 : Changed border from BlockBorder to BlockFrame, updated
047: * equals(), and implemented Cloneable (DG);
048: *
049: */
050:
051: package org.jfree.chart.block;
052:
053: import java.awt.Graphics2D;
054: import java.awt.geom.Rectangle2D;
055: import java.io.IOException;
056: import java.io.ObjectInputStream;
057: import java.io.ObjectOutputStream;
058: import java.io.Serializable;
059:
060: import org.jfree.data.Range;
061: import org.jfree.io.SerialUtilities;
062: import org.jfree.ui.RectangleInsets;
063: import org.jfree.ui.Size2D;
064: import org.jfree.util.ObjectUtilities;
065: import org.jfree.util.PublicCloneable;
066: import org.jfree.util.ShapeUtilities;
067:
068: /**
069: * A convenience class for creating new classes that implement
070: * the {@link Block} interface.
071: */
072: public class AbstractBlock implements Cloneable, Serializable {
073:
074: /** For serialization. */
075: private static final long serialVersionUID = 7689852412141274563L;
076:
077: /** The id for the block. */
078: private String id;
079:
080: /** The margin around the outside of the block. */
081: private RectangleInsets margin;
082:
083: /** The frame (or border) for the block. */
084: private BlockFrame frame;
085:
086: /** The padding between the block content and the border. */
087: private RectangleInsets padding;
088:
089: /**
090: * The natural width of the block (may be overridden if there are
091: * constraints in sizing).
092: */
093: private double width;
094:
095: /**
096: * The natural height of the block (may be overridden if there are
097: * constraints in sizing).
098: */
099: private double height;
100:
101: /**
102: * The current bounds for the block (position of the block in Java2D space).
103: */
104: private transient Rectangle2D bounds;
105:
106: /**
107: * Creates a new block.
108: */
109: protected AbstractBlock() {
110: this .id = null;
111: this .width = 0.0;
112: this .height = 0.0;
113: this .bounds = new Rectangle2D.Float();
114: this .margin = RectangleInsets.ZERO_INSETS;
115: this .frame = BlockBorder.NONE;
116: this .padding = RectangleInsets.ZERO_INSETS;
117: }
118:
119: /**
120: * Returns the id.
121: *
122: * @return The id (possibly <code>null</code>).
123: *
124: * @see #setID(String)
125: */
126: public String getID() {
127: return this .id;
128: }
129:
130: /**
131: * Sets the id for the block.
132: *
133: * @param id the id (<code>null</code> permitted).
134: *
135: * @see #getID()
136: */
137: public void setID(String id) {
138: this .id = id;
139: }
140:
141: /**
142: * Returns the natural width of the block, if this is known in advance.
143: * The actual width of the block may be overridden if layout constraints
144: * make this necessary.
145: *
146: * @return The width.
147: *
148: * @see #setWidth(double)
149: */
150: public double getWidth() {
151: return this .width;
152: }
153:
154: /**
155: * Sets the natural width of the block, if this is known in advance.
156: *
157: * @param width the width (in Java2D units)
158: *
159: * @see #getWidth()
160: */
161: public void setWidth(double width) {
162: this .width = width;
163: }
164:
165: /**
166: * Returns the natural height of the block, if this is known in advance.
167: * The actual height of the block may be overridden if layout constraints
168: * make this necessary.
169: *
170: * @return The height.
171: *
172: * @see #setHeight(double)
173: */
174: public double getHeight() {
175: return this .height;
176: }
177:
178: /**
179: * Sets the natural width of the block, if this is known in advance.
180: *
181: * @param height the width (in Java2D units)
182: *
183: * @see #getHeight()
184: */
185: public void setHeight(double height) {
186: this .height = height;
187: }
188:
189: /**
190: * Returns the margin.
191: *
192: * @return The margin (never <code>null</code>).
193: *
194: * @see #getMargin()
195: */
196: public RectangleInsets getMargin() {
197: return this .margin;
198: }
199:
200: /**
201: * Sets the margin (use {@link RectangleInsets#ZERO_INSETS} for no
202: * padding).
203: *
204: * @param margin the margin (<code>null</code> not permitted).
205: *
206: * @see #getMargin()
207: */
208: public void setMargin(RectangleInsets margin) {
209: if (margin == null) {
210: throw new IllegalArgumentException(
211: "Null 'margin' argument.");
212: }
213: this .margin = margin;
214: }
215:
216: /**
217: * Sets the margin.
218: *
219: * @param top the top margin.
220: * @param left the left margin.
221: * @param bottom the bottom margin.
222: * @param right the right margin.
223: *
224: * @see #getMargin()
225: */
226: public void setMargin(double top, double left, double bottom,
227: double right) {
228: setMargin(new RectangleInsets(top, left, bottom, right));
229: }
230:
231: /**
232: * Returns the border.
233: *
234: * @return The border (never <code>null</code>).
235: *
236: * @deprecated Use {@link #getFrame()} instead.
237: */
238: public BlockBorder getBorder() {
239: if (this .frame instanceof BlockBorder) {
240: return (BlockBorder) this .frame;
241: } else {
242: return null;
243: }
244: }
245:
246: /**
247: * Sets the border for the block (use {@link BlockBorder#NONE} for
248: * no border).
249: *
250: * @param border the border (<code>null</code> not permitted).
251: *
252: * @see #getBorder()
253: *
254: * @deprecated Use {@link #setFrame(BlockFrame)} instead.
255: */
256: public void setBorder(BlockBorder border) {
257: setFrame(border);
258: }
259:
260: /**
261: * Sets a black border with the specified line widths.
262: *
263: * @param top the top border line width.
264: * @param left the left border line width.
265: * @param bottom the bottom border line width.
266: * @param right the right border line width.
267: */
268: public void setBorder(double top, double left, double bottom,
269: double right) {
270: setFrame(new BlockBorder(top, left, bottom, right));
271: }
272:
273: /**
274: * Returns the current frame (border).
275: *
276: * @return The frame.
277: *
278: * @since 1.0.5
279: * @see #setFrame(BlockFrame)
280: */
281: public BlockFrame getFrame() {
282: return this .frame;
283: }
284:
285: /**
286: * Sets the frame (or border).
287: *
288: * @param frame the frame (<code>null</code> not permitted).
289: *
290: * @since 1.0.5
291: * @see #getFrame()
292: */
293: public void setFrame(BlockFrame frame) {
294: if (frame == null) {
295: throw new IllegalArgumentException("Null 'frame' argument.");
296: }
297: this .frame = frame;
298: }
299:
300: /**
301: * Returns the padding.
302: *
303: * @return The padding (never <code>null</code>).
304: *
305: * @see #setPadding(RectangleInsets)
306: */
307: public RectangleInsets getPadding() {
308: return this .padding;
309: }
310:
311: /**
312: * Sets the padding (use {@link RectangleInsets#ZERO_INSETS} for no
313: * padding).
314: *
315: * @param padding the padding (<code>null</code> not permitted).
316: *
317: * @see #getPadding()
318: */
319: public void setPadding(RectangleInsets padding) {
320: if (padding == null) {
321: throw new IllegalArgumentException(
322: "Null 'padding' argument.");
323: }
324: this .padding = padding;
325: }
326:
327: /**
328: * Sets the padding.
329: *
330: * @param top the top padding.
331: * @param left the left padding.
332: * @param bottom the bottom padding.
333: * @param right the right padding.
334: */
335: public void setPadding(double top, double left, double bottom,
336: double right) {
337: setPadding(new RectangleInsets(top, left, bottom, right));
338: }
339:
340: /**
341: * Returns the x-offset for the content within the block.
342: *
343: * @return The x-offset.
344: *
345: * @see #getContentYOffset()
346: */
347: public double getContentXOffset() {
348: return this .margin.getLeft() + this .frame.getInsets().getLeft()
349: + this .padding.getLeft();
350: }
351:
352: /**
353: * Returns the y-offset for the content within the block.
354: *
355: * @return The y-offset.
356: *
357: * @see #getContentXOffset()
358: */
359: public double getContentYOffset() {
360: return this .margin.getTop() + this .frame.getInsets().getTop()
361: + this .padding.getTop();
362: }
363:
364: /**
365: * Arranges the contents of the block, with no constraints, and returns
366: * the block size.
367: *
368: * @param g2 the graphics device.
369: *
370: * @return The block size (in Java2D units, never <code>null</code>).
371: */
372: public Size2D arrange(Graphics2D g2) {
373: return arrange(g2, RectangleConstraint.NONE);
374: }
375:
376: /**
377: * Arranges the contents of the block, within the given constraints, and
378: * returns the block size.
379: *
380: * @param g2 the graphics device.
381: * @param constraint the constraint (<code>null</code> not permitted).
382: *
383: * @return The block size (in Java2D units, never <code>null</code>).
384: */
385: public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
386: Size2D base = new Size2D(getWidth(), getHeight());
387: return constraint.calculateConstrainedSize(base);
388: }
389:
390: /**
391: * Returns the current bounds of the block.
392: *
393: * @return The bounds.
394: *
395: * @see #setBounds(Rectangle2D)
396: */
397: public Rectangle2D getBounds() {
398: return this .bounds;
399: }
400:
401: /**
402: * Sets the bounds of the block.
403: *
404: * @param bounds the bounds (<code>null</code> not permitted).
405: *
406: * @see #getBounds()
407: */
408: public void setBounds(Rectangle2D bounds) {
409: if (bounds == null) {
410: throw new IllegalArgumentException(
411: "Null 'bounds' argument.");
412: }
413: this .bounds = bounds;
414: }
415:
416: /**
417: * Calculate the width available for content after subtracting
418: * the margin, border and padding space from the specified fixed
419: * width.
420: *
421: * @param fixedWidth the fixed width.
422: *
423: * @return The available space.
424: *
425: * @see #trimToContentHeight(double)
426: */
427: protected double trimToContentWidth(double fixedWidth) {
428: double result = this .margin.trimWidth(fixedWidth);
429: result = this .frame.getInsets().trimWidth(result);
430: result = this .padding.trimWidth(result);
431: return Math.max(result, 0.0);
432: }
433:
434: /**
435: * Calculate the height available for content after subtracting
436: * the margin, border and padding space from the specified fixed
437: * height.
438: *
439: * @param fixedHeight the fixed height.
440: *
441: * @return The available space.
442: *
443: * @see #trimToContentWidth(double)
444: */
445: protected double trimToContentHeight(double fixedHeight) {
446: double result = this .margin.trimHeight(fixedHeight);
447: result = this .frame.getInsets().trimHeight(result);
448: result = this .padding.trimHeight(result);
449: return Math.max(result, 0.0);
450: }
451:
452: /**
453: * Returns a constraint for the content of this block that will result in
454: * the bounds of the block matching the specified constraint.
455: *
456: * @param c the outer constraint (<code>null</code> not permitted).
457: *
458: * @return The content constraint.
459: */
460: protected RectangleConstraint toContentConstraint(
461: RectangleConstraint c) {
462: if (c == null) {
463: throw new IllegalArgumentException("Null 'c' argument.");
464: }
465: if (c.equals(RectangleConstraint.NONE)) {
466: return c;
467: }
468: double w = c.getWidth();
469: Range wr = c.getWidthRange();
470: double h = c.getHeight();
471: Range hr = c.getHeightRange();
472: double ww = trimToContentWidth(w);
473: double hh = trimToContentHeight(h);
474: Range wwr = trimToContentWidth(wr);
475: Range hhr = trimToContentHeight(hr);
476: return new RectangleConstraint(ww, wwr, c
477: .getWidthConstraintType(), hh, hhr, c
478: .getHeightConstraintType());
479: }
480:
481: private Range trimToContentWidth(Range r) {
482: if (r == null) {
483: return null;
484: }
485: double lowerBound = 0.0;
486: double upperBound = Double.POSITIVE_INFINITY;
487: if (r.getLowerBound() > 0.0) {
488: lowerBound = trimToContentWidth(r.getLowerBound());
489: }
490: if (r.getUpperBound() < Double.POSITIVE_INFINITY) {
491: upperBound = trimToContentWidth(r.getUpperBound());
492: }
493: return new Range(lowerBound, upperBound);
494: }
495:
496: private Range trimToContentHeight(Range r) {
497: if (r == null) {
498: return null;
499: }
500: double lowerBound = 0.0;
501: double upperBound = Double.POSITIVE_INFINITY;
502: if (r.getLowerBound() > 0.0) {
503: lowerBound = trimToContentHeight(r.getLowerBound());
504: }
505: if (r.getUpperBound() < Double.POSITIVE_INFINITY) {
506: upperBound = trimToContentHeight(r.getUpperBound());
507: }
508: return new Range(lowerBound, upperBound);
509: }
510:
511: /**
512: * Adds the margin, border and padding to the specified content width.
513: *
514: * @param contentWidth the content width.
515: *
516: * @return The adjusted width.
517: */
518: protected double calculateTotalWidth(double contentWidth) {
519: double result = contentWidth;
520: result = this .padding.extendWidth(result);
521: result = this .frame.getInsets().extendWidth(result);
522: result = this .margin.extendWidth(result);
523: return result;
524: }
525:
526: /**
527: * Adds the margin, border and padding to the specified content height.
528: *
529: * @param contentHeight the content height.
530: *
531: * @return The adjusted height.
532: */
533: protected double calculateTotalHeight(double contentHeight) {
534: double result = contentHeight;
535: result = this .padding.extendHeight(result);
536: result = this .frame.getInsets().extendHeight(result);
537: result = this .margin.extendHeight(result);
538: return result;
539: }
540:
541: /**
542: * Reduces the specified area by the amount of space consumed
543: * by the margin.
544: *
545: * @param area the area (<code>null</code> not permitted).
546: *
547: * @return The trimmed area.
548: */
549: protected Rectangle2D trimMargin(Rectangle2D area) {
550: // defer argument checking...
551: this .margin.trim(area);
552: return area;
553: }
554:
555: /**
556: * Reduces the specified area by the amount of space consumed
557: * by the border.
558: *
559: * @param area the area (<code>null</code> not permitted).
560: *
561: * @return The trimmed area.
562: */
563: protected Rectangle2D trimBorder(Rectangle2D area) {
564: // defer argument checking...
565: this .frame.getInsets().trim(area);
566: return area;
567: }
568:
569: /**
570: * Reduces the specified area by the amount of space consumed
571: * by the padding.
572: *
573: * @param area the area (<code>null</code> not permitted).
574: *
575: * @return The trimmed area.
576: */
577: protected Rectangle2D trimPadding(Rectangle2D area) {
578: // defer argument checking...
579: this .padding.trim(area);
580: return area;
581: }
582:
583: /**
584: * Draws the border around the perimeter of the specified area.
585: *
586: * @param g2 the graphics device.
587: * @param area the area.
588: */
589: protected void drawBorder(Graphics2D g2, Rectangle2D area) {
590: this .frame.draw(g2, area);
591: }
592:
593: /**
594: * Tests this block for equality with an arbitrary object.
595: *
596: * @param obj the object (<code>null</code> permitted).
597: *
598: * @return A boolean.
599: */
600: public boolean equals(Object obj) {
601: if (obj == this ) {
602: return true;
603: }
604: if (!(obj instanceof AbstractBlock)) {
605: return false;
606: }
607: AbstractBlock that = (AbstractBlock) obj;
608: if (!ObjectUtilities.equal(this .id, that.id)) {
609: return false;
610: }
611: if (!this .frame.equals(that.frame)) {
612: return false;
613: }
614: if (!this .bounds.equals(that.bounds)) {
615: return false;
616: }
617: if (!this .margin.equals(that.margin)) {
618: return false;
619: }
620: if (!this .padding.equals(that.padding)) {
621: return false;
622: }
623: if (this .height != that.height) {
624: return false;
625: }
626: if (this .width != that.width) {
627: return false;
628: }
629: return true;
630: }
631:
632: /**
633: * Returns a clone of this block.
634: *
635: * @return A clone.
636: *
637: * @throws CloneNotSupportedException if there is a problem creating the
638: * clone.
639: */
640: public Object clone() throws CloneNotSupportedException {
641: AbstractBlock clone = (AbstractBlock) super .clone();
642: clone.bounds = (Rectangle2D) ShapeUtilities.clone(this .bounds);
643: if (this .frame instanceof PublicCloneable) {
644: PublicCloneable pc = (PublicCloneable) this .frame;
645: clone.frame = (BlockFrame) pc.clone();
646: }
647: return clone;
648: }
649:
650: /**
651: * Provides serialization support.
652: *
653: * @param stream the output stream.
654: *
655: * @throws IOException if there is an I/O error.
656: */
657: private void writeObject(ObjectOutputStream stream)
658: throws IOException {
659: stream.defaultWriteObject();
660: SerialUtilities.writeShape(this .bounds, stream);
661: }
662:
663: /**
664: * Provides serialization support.
665: *
666: * @param stream the input stream.
667: *
668: * @throws IOException if there is an I/O error.
669: * @throws ClassNotFoundException if there is a classpath problem.
670: */
671: private void readObject(ObjectInputStream stream)
672: throws IOException, ClassNotFoundException {
673: stream.defaultReadObject();
674: this .bounds = (Rectangle2D) SerialUtilities.readShape(stream);
675: }
676:
677: }
|