Source for org.jfree.chart.block.AbstractBlock

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * ------------------
  28:  * AbstractBlock.java
  29:  * ------------------
  30:  * (C) Copyright 2004, 2005, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: AbstractBlock.java,v 1.12.2.2 2006/08/04 11:37:50 mungady Exp $
  36:  *
  37:  * Changes:
  38:  * --------
  39:  * 22-Oct-2004 : Version 1 (DG);
  40:  * 02-Feb-2005 : Added accessor methods for margin (DG);
  41:  * 04-Feb-2005 : Added equals() method and implemented Serializable (DG);
  42:  * 03-May-2005 : Added null argument checks (DG);
  43:  * 06-May-2005 : Added convenience methods for setting margin, border and 
  44:  *               padding (DG);
  45:  * 
  46:  */
  47: 
  48: package org.jfree.chart.block;
  49: 
  50: import java.awt.Graphics2D;
  51: import java.awt.geom.Rectangle2D;
  52: import java.io.IOException;
  53: import java.io.ObjectInputStream;
  54: import java.io.ObjectOutputStream;
  55: import java.io.Serializable;
  56: 
  57: import org.jfree.data.Range;
  58: import org.jfree.io.SerialUtilities;
  59: import org.jfree.ui.RectangleInsets;
  60: import org.jfree.ui.Size2D;
  61: 
  62: /**
  63:  * A convenience class for creating new classes that implement 
  64:  * the {@link Block} interface.
  65:  */
  66: public class AbstractBlock implements Serializable {
  67: 
  68:     /** For serialization. */
  69:     private static final long serialVersionUID = 7689852412141274563L;
  70:     
  71:     /** The id for the block. */
  72:     private String id;
  73:     
  74:     /** The margin around the outside of the block. */
  75:     private RectangleInsets margin;
  76:     
  77:     /** The border for the block. */
  78:     private BlockBorder border;
  79: 
  80:     /** The padding between the block content and the border. */
  81:     private RectangleInsets padding;
  82:     
  83:     /** 
  84:      * The natural width of the block (may be overridden if there are 
  85:      * constraints in sizing).
  86:      */
  87:     private double width;
  88:     
  89:     /** 
  90:      * The natural height of the block (may be overridden if there are 
  91:      * constraints in sizing).
  92:      */
  93:     private double height;
  94:     
  95:     /**
  96:      * The current bounds for the block (position of the block in Java2D space).
  97:      */
  98:     private transient Rectangle2D bounds;
  99:     
 100:     /**
 101:      * Creates a new block.
 102:      */
 103:     protected AbstractBlock() {
 104:         this.id = null;
 105:         this.width = 0.0;
 106:         this.height = 0.0;
 107:         this.bounds = new Rectangle2D.Float();
 108:         this.margin = RectangleInsets.ZERO_INSETS;
 109:         this.border = BlockBorder.NONE; 
 110:         this.padding = RectangleInsets.ZERO_INSETS;
 111:     }
 112:     
 113:     /**
 114:      * Returns the id.
 115:      * 
 116:      * @return The id (possibly <code>null</code>).
 117:      */
 118:     public String getID() {
 119:         return this.id;   
 120:     }
 121:     
 122:     /**
 123:      * Sets the id for the block.
 124:      * 
 125:      * @param id  the id (<code>null</code> permitted).
 126:      */
 127:     public void setID(String id) {
 128:         this.id = id;   
 129:     }
 130:     
 131:     /**
 132:      * Returns the natural width of the block, if this is known in advance.
 133:      * The actual width of the block may be overridden if layout constraints
 134:      * make this necessary.  
 135:      * 
 136:      * @return The width.
 137:      */
 138:     public double getWidth() {
 139:         return this.width;
 140:     }
 141:     
 142:     /**
 143:      * Sets the natural width of the block, if this is known in advance.
 144:      * 
 145:      * @param width  the width (in Java2D units)
 146:      */
 147:     public void setWidth(double width) {
 148:         this.width = width;
 149:     }
 150:     
 151:     /**
 152:      * Returns the natural height of the block, if this is known in advance.
 153:      * The actual height of the block may be overridden if layout constraints
 154:      * make this necessary.  
 155:      * 
 156:      * @return The height.
 157:      */
 158:     public double getHeight() {
 159:         return this.height;
 160:     }
 161:     
 162:     /**
 163:      * Sets the natural width of the block, if this is known in advance.
 164:      * 
 165:      * @param height  the width (in Java2D units)
 166:      */
 167:     public void setHeight(double height) {
 168:         this.height = height;
 169:     }
 170:     
 171:     /**
 172:      * Returns the margin.
 173:      * 
 174:      * @return The margin (never <code>null</code>).
 175:      */
 176:     public RectangleInsets getMargin() {
 177:         return this.margin;
 178:     }
 179:         
 180:     /**
 181:      * Sets the margin (use {@link RectangleInsets#ZERO_INSETS} for no 
 182:      * padding).
 183:      * 
 184:      * @param margin  the margin (<code>null</code> not permitted).
 185:      */
 186:     public void setMargin(RectangleInsets margin) {
 187:         if (margin == null) {
 188:             throw new IllegalArgumentException("Null 'margin' argument.");   
 189:         }
 190:         this.margin = margin;
 191:     }
 192: 
 193:     /**
 194:      * Sets the margin.
 195:      * 
 196:      * @param top  the top margin.
 197:      * @param left  the left margin.
 198:      * @param bottom  the bottom margin.
 199:      * @param right  the right margin.
 200:      */
 201:     public void setMargin(double top, double left, double bottom, 
 202:                           double right) {
 203:         setMargin(new RectangleInsets(top, left, bottom, right));
 204:     }
 205: 
 206:     /**
 207:      * Returns the border.
 208:      * 
 209:      * @return The border (never <code>null</code>).
 210:      */
 211:     public BlockBorder getBorder() {
 212:         return this.border;
 213:     }
 214:     
 215:     /**
 216:      * Sets the border for the block (use {@link BlockBorder#NONE} for
 217:      * no border).
 218:      * 
 219:      * @param border  the border (<code>null</code> not permitted).
 220:      */
 221:     public void setBorder(BlockBorder border) {
 222:         if (border == null) {
 223:             throw new IllegalArgumentException("Null 'border' argument.");   
 224:         }
 225:         this.border = border;
 226:     }
 227:     
 228:     /**
 229:      * Sets a black border with the specified line widths.
 230:      * 
 231:      * @param top  the top border line width.
 232:      * @param left  the left border line width.
 233:      * @param bottom  the bottom border line width.
 234:      * @param right  the right border line width.
 235:      */
 236:     public void setBorder(double top, double left, double bottom, 
 237:                           double right) {
 238:         setBorder(new BlockBorder(top, left, bottom, right));
 239:     }
 240:     
 241:     /**
 242:      * Returns the padding.
 243:      * 
 244:      * @return The padding (never <code>null</code>).
 245:      */
 246:     public RectangleInsets getPadding() {
 247:         return this.padding;
 248:     }
 249:     
 250:     /**
 251:      * Sets the padding (use {@link RectangleInsets#ZERO_INSETS} for no 
 252:      * padding).
 253:      * 
 254:      * @param padding  the padding (<code>null</code> not permitted).
 255:      */
 256:     public void setPadding(RectangleInsets padding) {
 257:         if (padding == null) {
 258:             throw new IllegalArgumentException("Null 'padding' argument.");   
 259:         }
 260:         this.padding = padding;
 261:     }
 262: 
 263:     /**
 264:      * Returns the x-offset for the content within the block.
 265:      * 
 266:      * @return The x-offset.
 267:      */
 268:     public double getContentXOffset() {
 269:         return this.margin.getLeft() + this.border.getInsets().getLeft() 
 270:             + this.padding.getLeft();    
 271:     }
 272:     
 273:     /**
 274:      * Returns the y-offset for the content within the block.
 275:      * 
 276:      * @return The y-offset.
 277:      */
 278:     public double getContentYOffset() {
 279:         return this.margin.getTop() + this.border.getInsets().getTop() 
 280:             + this.padding.getTop();   
 281:     }
 282:     
 283:     /**
 284:      * Sets the padding.
 285:      * 
 286:      * @param top  the top padding.
 287:      * @param left  the left padding.
 288:      * @param bottom  the bottom padding.
 289:      * @param right  the right padding.
 290:      */
 291:     public void setPadding(double top, double left, double bottom, 
 292:                            double right) {
 293:         setPadding(new RectangleInsets(top, left, bottom, right));
 294:     }
 295:     
 296:     /**
 297:      * Arranges the contents of the block, with no constraints, and returns 
 298:      * the block size.
 299:      * 
 300:      * @param g2  the graphics device.
 301:      * 
 302:      * @return The block size (in Java2D units, never <code>null</code>).
 303:      */
 304:     public Size2D arrange(Graphics2D g2) {  
 305:         return arrange(g2, RectangleConstraint.NONE);
 306:     }
 307: 
 308:     /**
 309:      * Arranges the contents of the block, within the given constraints, and 
 310:      * returns the block size.
 311:      * 
 312:      * @param g2  the graphics device.
 313:      * @param constraint  the constraint (<code>null</code> not permitted).
 314:      * 
 315:      * @return The block size (in Java2D units, never <code>null</code>).
 316:      */
 317:     public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
 318:         Size2D base = new Size2D(getWidth(), getHeight());
 319:         return constraint.calculateConstrainedSize(base);
 320:     }
 321: 
 322:     /**
 323:      * Returns the current bounds of the block.
 324:      * 
 325:      * @return The bounds.
 326:      */
 327:     public Rectangle2D getBounds() {
 328:         return this.bounds;
 329:     }
 330:     
 331:     /**
 332:      * Sets the bounds of the block.
 333:      * 
 334:      * @param bounds  the bounds (<code>null</code> not permitted).
 335:      */
 336:     public void setBounds(Rectangle2D bounds) {
 337:         if (bounds == null) {
 338:             throw new IllegalArgumentException("Null 'bounds' argument.");
 339:         }
 340:         this.bounds = bounds;
 341:     }
 342:     
 343:     /**
 344:      * Calculate the width available for content after subtracting 
 345:      * the margin, border and padding space from the specified fixed 
 346:      * width.
 347:      * 
 348:      * @param fixedWidth  the fixed width.
 349:      * 
 350:      * @return The available space.
 351:      */
 352:     protected double trimToContentWidth(double fixedWidth) {
 353:         double result = this.margin.trimWidth(fixedWidth);
 354:         result = this.border.getInsets().trimWidth(result);
 355:         result = this.padding.trimWidth(result);
 356:         return Math.max(result, 0.0);
 357:     }
 358: 
 359:     /**
 360:      * Calculate the height available for content after subtracting 
 361:      * the margin, border and padding space from the specified fixed 
 362:      * height.
 363:      * 
 364:      * @param fixedHeight  the fixed height.
 365:      * 
 366:      * @return The available space.
 367:      */
 368:     protected double trimToContentHeight(double fixedHeight) {
 369:         double result = this.margin.trimHeight(fixedHeight);
 370:         result = this.border.getInsets().trimHeight(result);
 371:         result = this.padding.trimHeight(result);
 372:         return Math.max(result, 0.0);
 373:     }
 374:     
 375:     /**
 376:      * Returns a constraint for the content of this block that will result in
 377:      * the bounds of the block matching the specified constraint.
 378:      * 
 379:      * @param c  the outer constraint (<code>null</code> not permitted).
 380:      * 
 381:      * @return The content constraint.
 382:      */
 383:     protected RectangleConstraint toContentConstraint(RectangleConstraint c) {
 384:         if (c == null) {
 385:             throw new IllegalArgumentException("Null 'c' argument.");
 386:         }
 387:         if (c.equals(RectangleConstraint.NONE)) {
 388:             return c;
 389:         }
 390:         double w = c.getWidth();
 391:         Range wr = c.getWidthRange();
 392:         double h = c.getHeight();
 393:         Range hr = c.getHeightRange();
 394:         double ww = trimToContentWidth(w);
 395:         double hh = trimToContentHeight(h);
 396:         Range wwr = trimToContentWidth(wr);
 397:         Range hhr = trimToContentHeight(hr);
 398:         return new RectangleConstraint(
 399:             ww, wwr, c.getWidthConstraintType(), 
 400:             hh, hhr, c.getHeightConstraintType()
 401:         );
 402:     }
 403: 
 404:     private Range trimToContentWidth(Range r) {
 405:         if (r == null) {
 406:             return null;   
 407:         }
 408:         double lowerBound = 0.0;
 409:         double upperBound = Double.POSITIVE_INFINITY;
 410:         if (r.getLowerBound() > 0.0) {
 411:             lowerBound = trimToContentWidth(r.getLowerBound());   
 412:         }
 413:         if (r.getUpperBound() < Double.POSITIVE_INFINITY) {
 414:             upperBound = trimToContentWidth(r.getUpperBound());
 415:         }
 416:         return new Range(lowerBound, upperBound);
 417:     }
 418:     
 419:     private Range trimToContentHeight(Range r) {
 420:         if (r == null) {
 421:             return null;   
 422:         }
 423:         double lowerBound = 0.0;
 424:         double upperBound = Double.POSITIVE_INFINITY;
 425:         if (r.getLowerBound() > 0.0) {
 426:             lowerBound = trimToContentHeight(r.getLowerBound());   
 427:         }
 428:         if (r.getUpperBound() < Double.POSITIVE_INFINITY) {
 429:             upperBound = trimToContentHeight(r.getUpperBound());
 430:         }
 431:         return new Range(lowerBound, upperBound);
 432:     }
 433:     
 434:     /**
 435:      * Adds the margin, border and padding to the specified content width.
 436:      * 
 437:      * @param contentWidth  the content width.
 438:      * 
 439:      * @return The adjusted width.
 440:      */
 441:     protected double calculateTotalWidth(double contentWidth) {
 442:         double result = contentWidth;
 443:         result = this.padding.extendWidth(result);
 444:         result = this.border.getInsets().extendWidth(result);
 445:         result = this.margin.extendWidth(result);
 446:         return result;
 447:     }
 448: 
 449:     /**
 450:      * Adds the margin, border and padding to the specified content height.
 451:      * 
 452:      * @param contentHeight  the content height.
 453:      * 
 454:      * @return The adjusted height.
 455:      */
 456:     protected double calculateTotalHeight(double contentHeight) {
 457:         double result = contentHeight;
 458:         result = this.padding.extendHeight(result);
 459:         result = this.border.getInsets().extendHeight(result);
 460:         result = this.margin.extendHeight(result);
 461:         return result;
 462:     }
 463: 
 464:     /**
 465:      * Reduces the specified area by the amount of space consumed 
 466:      * by the margin.
 467:      * 
 468:      * @param area  the area (<code>null</code> not permitted).
 469:      * 
 470:      * @return The trimmed area.
 471:      */
 472:     protected Rectangle2D trimMargin(Rectangle2D area) {
 473:         // defer argument checking...
 474:         this.margin.trim(area);
 475:         return area;
 476:     }
 477:     
 478:     /**
 479:      * Reduces the specified area by the amount of space consumed 
 480:      * by the border.
 481:      * 
 482:      * @param area  the area (<code>null</code> not permitted).
 483:      * 
 484:      * @return The trimmed area.
 485:      */
 486:     protected Rectangle2D trimBorder(Rectangle2D area) {
 487:         // defer argument checking...
 488:         this.border.getInsets().trim(area);
 489:         return area;
 490:     }
 491: 
 492:     /**
 493:      * Reduces the specified area by the amount of space consumed 
 494:      * by the padding.
 495:      * 
 496:      * @param area  the area (<code>null</code> not permitted).
 497:      * 
 498:      * @return The trimmed area.
 499:      */
 500:     protected Rectangle2D trimPadding(Rectangle2D area) {
 501:         // defer argument checking...
 502:         this.padding.trim(area);
 503:         return area;
 504:     }
 505: 
 506:     /**
 507:      * Draws the border around the perimeter of the specified area.
 508:      * 
 509:      * @param g2  the graphics device.
 510:      * @param area  the area.
 511:      */
 512:     protected void drawBorder(Graphics2D g2, Rectangle2D area) {
 513:         this.border.draw(g2, area);
 514:     }
 515:     
 516:     /**
 517:      * Tests this block for equality with an arbitrary object.
 518:      * 
 519:      * @param obj  the object (<code>null</code> permitted).
 520:      * 
 521:      * @return A boolean.
 522:      */
 523:     public boolean equals(Object obj) {
 524:         if (obj == this) {
 525:             return true;   
 526:         }
 527:         if (!(obj instanceof AbstractBlock)) {
 528:             return false;   
 529:         }
 530:         AbstractBlock that = (AbstractBlock) obj;
 531:         if (!this.border.equals(that.border)) {
 532:             return false;   
 533:         }
 534:         if (!this.bounds.equals(that.bounds)) {
 535:             return false;   
 536:         }
 537:         if (!this.margin.equals(that.margin)) {
 538:             return false;   
 539:         }
 540:         if (!this.padding.equals(that.padding)) {
 541:             return false;   
 542:         }
 543:         if (this.height != that.height) {
 544:             return false;   
 545:         }
 546:         if (this.width != that.width) {
 547:             return false;   
 548:         }
 549:         return true;
 550:     }
 551:     
 552:     /**
 553:      * Provides serialization support.
 554:      *
 555:      * @param stream  the output stream.
 556:      *
 557:      * @throws IOException if there is an I/O error.
 558:      */
 559:     private void writeObject(ObjectOutputStream stream) throws IOException {
 560:         stream.defaultWriteObject();
 561:         SerialUtilities.writeShape(this.bounds, stream);
 562:     }
 563: 
 564:     /**
 565:      * Provides serialization support.
 566:      *
 567:      * @param stream  the input stream.
 568:      *
 569:      * @throws IOException  if there is an I/O error.
 570:      * @throws ClassNotFoundException  if there is a classpath problem.
 571:      */
 572:     private void readObject(ObjectInputStream stream) 
 573:         throws IOException, ClassNotFoundException {
 574:         stream.defaultReadObject();
 575:         this.bounds = (Rectangle2D) SerialUtilities.readShape(stream);
 576:     }
 577: 
 578: }