/* * $Id: BoundingBox.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ * * $Date: 2007/08/26 18:56:35 $ * * Copyright (c) Eric Z. Beard, ericzbeard@hotmail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package gnu.jpdf; import java.awt.*; import java.util.*; /** * <p>This class simplifies the placement of Strings within * a canvas area where the placement of objects is absolute</p> * * <p>A <code>BoundingBox</code> is just a Rectangle that knows how to * find the coordinates for a String based on the desired alignment and * <code>FontMetrics</code>. For each new String, a new child * <code>BoundingBox</code> is made that can be subtracted from the * original box so new Strings can be added</p> * * <p>One of the more helpful features of this class is the string wrap * feature of <code>getStringBounds</code>. The box returned by that method * will contain an array of strings that have been broken down to fit the * box. The box's coordinates and size will reflect the size of the * entire group of strings if it is laid out as expected. Using the * returned box and iterating through the array of strings from top to * bottom, getting new bounding boxes for each one (with upper left * alignment and no padding) will result in the correct string wrap.</p> * * <p>Note that you will need to have Xvfb running on a Unix server to * use this class</p> * * @author Eric Z. Beard, ericzbeard@hotmail.com * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ */ public class BoundingBox extends Rectangle { /** Percent f line height to space lines */ public static final int LINE_SPACING_PERCENTAGE = 20; /** Used to a align a String centered vertically */ public static final int VERT_ALIGN_CENTER = 0; /** Used to align a String at the top of the box */ public static final int VERT_ALIGN_TOP = 1; /** Used to align a String at the bottom of the box */ public static final int VERT_ALIGN_BOTTOM = 2; /** Used to align a String horizontally in the center of the box */ public static final int HORIZ_ALIGN_CENTER = 3; /** Used to align a String to the left in the box */ public static final int HORIZ_ALIGN_LEFT = 4; /** Used to aling a String to the right in a box */ public static final int HORIZ_ALIGN_RIGHT = 5; /** Used to subtract a child from a box, *leaving* the top portion */ public static final int SUBTRACT_FROM_TOP = 6; /** Used to subtract a child from a box, *leaving* the bottom portion */ public static final int SUBTRACT_FROM_BOTTOM = 7; /** Used to subtract a child from a box, *leaving* the left portion */ public static final int SUBTRACT_FROM_LEFT = 8; /** Used to subtract a child from a box, *leaving" the right portion */ public static final int SUBTRACT_FROM_RIGHT = 9; private static final int[] VERT_ALIGNS = {VERT_ALIGN_CENTER, VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM}; private static final int[] HORIZ_ALIGNS = {HORIZ_ALIGN_CENTER, HORIZ_ALIGN_LEFT, HORIZ_ALIGN_RIGHT}; private static final int[] SUBTRACTS = {SUBTRACT_FROM_TOP, SUBTRACT_FROM_BOTTOM, SUBTRACT_FROM_LEFT, SUBTRACT_FROM_RIGHT}; /** The point to use for Graphics.drawString() */ private Point drawingPoint; /** The absolute, world location of the box */ private Point absoluteLocation; /** Link to parent box */ private BoundingBox parent; /** * If this box was the result of a getStringBounds call, this * array will hold the broken strings */ private String[] stringArray; /** The string specified in getStringBounds */ private String fullString; /** * Creates a new <code>BoundingBox</code> instance. * * @param p a <code>Point</code>, upper left coords * @param d a <code>Dimension</code>, used to determine height and width */ public BoundingBox(Point p, Dimension d) { super(p, d); this.drawingPoint = this.getLocation(); this.absoluteLocation = this.getLocation(); } /** * <p>Returns true if this box has a parent. The 'world', or * enclosing canvas is not considered a parent</p> * * @return a <code>boolean</code> value */ public boolean hasParent() { return parent != null; } /** * <p>Get this box's parent box</p> * * @return a <code>BoundingBox</code> value */ public BoundingBox getParent() { return parent; } /** * <p>Make the specified box this box's child. Equivalent to * <code>child.setParent(parent)</code> where the specified 'parent' is * this instance</p> * * @param child a <code>BoundingBox</code>, any box that can fit inside * this one. The results of calling * <code>getAbsoluteLocation()</code> on the child will be * altered after this to take into account the child's * new location in the 'world' * */ public void add(BoundingBox child) { child.setParent(this); } /** * <p>Make the specified box this box's parent</p> * * @param parent a <code>BoundingBox</code> value */ public void setParent(BoundingBox parent) { // Prevent infinite recursion if (this == parent) { return; } this.parent = parent; // If this box was created empty, without a String inside, // determine its absolute location if (this.getLocation().equals(this.getAbsoluteLocation())) { int ancestorTranslateX = 0; int ancestorTranslateY = 0; BoundingBox ancestor = this; while (ancestor.hasParent()) { BoundingBox oldRef = ancestor; ancestor = ancestor.getParent(); // Prevent infinite recursion if (ancestor == oldRef) { break; } ancestorTranslateX += (int)ancestor.getLocation().getX(); ancestorTranslateY += (int)ancestor.getLocation().getY(); } this.getAbsoluteLocation().translate(ancestorTranslateX, ancestorTranslateY); } // end if } // end setParent /** * <p>Get the wrapped strings if this box was from a call to getStringBounds, * otherwise this method returns null</p> * * @return a <code>String[]</code> array of strings, top to bottom in layout */ public String[] getStringArray() { return stringArray; } // end getStringArray /** * <p>Set the value of the string array</p> * * @param strArray a <code>String</code> array * */ public void setStringArray(String[] strArray) { this.stringArray = strArray; } /** * <p>Set the absolute upper left world location point for this box</p> * * @param point a <code>Point</code> value */ public void setAbsoluteLocation(Point point) { this.absoluteLocation = point; } /** * <p>Returns false if for any reason this box has negative dimensions</p> * * @return true if the box has positive height and width, false otherwise. */ public boolean boxExists() { return (this.getHeight() > 0 && this.getWidth() > 0); } // end boxExists /** * <p>Get the absolute upper left location point for this box</p> * * @return a <code>Point</code> value */ public Point getAbsoluteLocation() { return absoluteLocation; } /** * <p>Returns the full string associated with a call to * <code>getStringBounds</code></p> * * @return the full string. */ public String getFullString() { return fullString; } /** * <p>Sets the full string associated with <code>getStringBounds</code></p> * * @param string a <code>String</code> */ public void setFullString(String string) { this.fullString = string; } /** * <p>Gets the location of a String after it is adjusted for * alignment within this box. The point's coordinates are * either within this box or within the enclosing area.</p> * * @param string a <code>String</code>, the String to be placed * @param hAlign an <code>int</code>, HORIZ_ALIGN_CENTER, * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT * @param vAlign an <code>int</code>, VERT_ALIGN_CENTER, * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM * @param fm a <code>FontMetrics</code> object for this String * @param padding an <code>int</code>, the padding around the String * @param enforce a <code>boolean</code>, if true the method will throw * an exception when the string is too big, if not true it will break * the string down and overrun the bottom of the box. If the box * is too small for even one word, the exception will be thrown * @return a <code>Point</code>, the coords to use in drawString() * @see #HORIZ_ALIGN_LEFT * @see #HORIZ_ALIGN_CENTER * @see #HORIZ_ALIGN_RIGHT * @see #VERT_ALIGN_TOP * @see #VERT_ALIGN_CENTER * @see #VERT_ALIGN_BOTTOM * @throws IllegalArgumentException if the args are invalid * @throws StringTooLongException if the string won't fit * and enforce is set to true. The exception can still be thrown * if enforce is false, but only in cases such as the box having * no height or width */ public BoundingBox getStringBounds(String string, int hAlign, int vAlign, FontMetrics fm, int padding, boolean enforce) throws IllegalArgumentException, StringTooLongException { // Check to make sure the values passed in are valid if (!checkHAlign(hAlign)) { throw new IllegalArgumentException("BoundingBox.getStringBounds, " + "hAlign invalid : " + hAlign); } if (!checkVAlign(vAlign)) { throw new IllegalArgumentException("BoundingBox.getStringBounds, " + "vAlign invalid : " + hAlign); } if (fm == null) { throw new IllegalArgumentException("BoundingBox.getStringBounds, " + "FontMetrics null"); } if (string == null) { throw new IllegalArgumentException("BoundingBox.getStringBounds, " + "String null"); } // NOTE: For this portion of the method, parent refers // to this object and child refers to the object about // to be created. When the absolute point for drawing the // String is determined, this object's ancestors are checked. Dimension parentSize = this.getSize(); Point childLocation; Dimension childSize; // String ascent, width, height, parent, child width, height int sa, sw, sh, pw, ph, cw, ch; // Child, parent x, y coords for upper left int cx, cy, px, py; sa = fm.getMaxAscent(); sw = fm.stringWidth(string); sh = sa + fm.getMaxDescent(); pw = (int)parentSize.getWidth(); ph = (int)parentSize.getHeight(); if (pw < 0) { throw new StringTooLongException("The parent box has a negative width " + " (" + pw + ")"); } if (ph < 0) { throw new StringTooLongException("The parent box has a negative height"+ " (" + ph + ")"); } cw = sw + padding*2; ch = sh + padding*2; px = (int)this.getX(); py = (int)this.getY(); String[] childStrArray = null; if ((cw > pw) || (string.indexOf("\n") != -1)) { cw = pw - (padding * 2); childStrArray = createStringArray(string, fm, padding, pw); ch = getWrappedHeight(childStrArray, fm, padding); if (ch > ph) { // If enforce is not true, it means we want the box to // be returned anyway (along with the strings in the array) // so we can chop them manually and try again if (enforce) { throw new StringTooLongException("The wrapped strings do not " + "fit into the parent box, pw=" + pw + ", ph=" + ph + ", ch=" + ch + ", cw=" + cw + ", string: " + string); } } } // Need to have child width and height, and string array set // Child location is relative to this (parent) box, not the world if (vAlign == VERT_ALIGN_TOP) { cy = 0; } else if (vAlign == VERT_ALIGN_CENTER) { cy = (ph/2) - (ch/2); } else { cy = ph - ch; } if (hAlign == HORIZ_ALIGN_LEFT) { cx = 0; } else if (hAlign == HORIZ_ALIGN_CENTER) { cx = (pw/2) - (cw/2); } else { cx = pw - cw; } childLocation = new Point(cx, cy); childSize = new Dimension(cw, ch); // Drawing location is based on the baseline of the String, and // relative to the world, not this box. The drawing point differs // from the absolute box location because of padding and ascent int dpx, dpy, abx, aby; // If this object also has a parent (maybe grandparents), iterate // through them and find the absolute 'world' location int ancestorTranslateX = 0; int ancestorTranslateY = 0; BoundingBox ancestor = this; while (ancestor.hasParent()) { BoundingBox oldRef = ancestor; ancestor = ancestor.getParent(); // Prevent infinite recursion if (ancestor == oldRef) { break; } ancestorTranslateX += (int)ancestor.getLocation().getX(); ancestorTranslateY += (int)ancestor.getLocation().getY(); } // Determine the absolute location for the box abx = px + cx + ancestorTranslateX; aby = py + cy + ancestorTranslateY; // Determine the absolute drawing point for the String dpx = abx + padding; dpy = aby + padding + sa; Point drawingPoint = new Point(dpx, dpy); BoundingBox returnChild = new BoundingBox(childLocation, childSize, drawingPoint, new Point(abx, aby)); this.add(returnChild); returnChild.setFullString(string); returnChild.setStringArray(childStrArray); return returnChild; } // end getStringBounds /** * <p>Gets the location of a String after it is adjusted for * alignment within this box. The point's coordinates are * either within this box or within the enclosing area.</p> * * <p>By default, this method enforces string length and throws the * exception if it is too long</p> * * @param string a <code>String</code>, the String to be placed * @param hAlign an <code>int</code>, HORIZ_ALIGN_CENTER, * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT * @param vAlign an <code>int</code>, VERT_ALIGN_CENTER, * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM * @param fm a <code>FontMetrics</code> object for this String * @param padding an <code>int</code>, the padding around the String * @return a <code>Point</code>, the coords to use in drawString() * @throws IllegalArgumentException if the args are invalid * @throws StringTooLongException if the string won't fit */ public BoundingBox getStringBounds(String string, int hAlign, int vAlign, FontMetrics fm, int padding) throws StringTooLongException, IllegalArgumentException { return getStringBounds(string, hAlign, vAlign, fm, padding, true); } // end getStringBounds (enforce true by default) /** * <p>This method is called after getting the box by calling * <code>getStringBounds</code> on the parent. Wraps the string at * word boundaries and draws it to the specified <code>Graphics</code> * context. Make sure padding is the same as specified for the * <code>getStringBounds</code> call, or you may get an unexpected * {@link gnu.jpdf.StringTooLongException}</p> * * @param g the <code>Graphics</code> object * @param fm the <code>FontMetrics</code> to use for sizing * @param padding an int, the padding around the strings * @param hAlign the <code>int</code> horizontal alignment * @throws IllegalArgumentException if the args are invalid * @throws StringTooLongException if the string * won't fit this will only happen if the fm or padding has * been changed since getStringBounds was called succesfully */ public void drawWrappedString(Graphics g, FontMetrics fm, int padding, int hAlign) throws IllegalArgumentException, StringTooLongException { if (getStringArray() == null) { Point p = getDrawingPoint(); int xx = (int)p.getX(); int yy = (int)p.getY(); g.drawString(getFullString(), xx, yy); } else { int len = stringArray.length; for (int i = 0; i < len; i++) { BoundingBox wrappedBox = null; wrappedBox = getStringBounds(stringArray[i], hAlign, BoundingBox.VERT_ALIGN_TOP, fm, 0); Point pp = wrappedBox.getDrawingPoint(); int xx = (int)pp.getX(); if (hAlign == BoundingBox.HORIZ_ALIGN_RIGHT) { xx -= padding; } if (hAlign == BoundingBox.HORIZ_ALIGN_LEFT) { xx += padding; } int yy = (int)pp.getY() + padding; g.drawString(stringArray[i], xx, yy); subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM); } } } // end drawWrappedString /** * <p>Draws lines from the wrapped string until there is no more room and * then stops. If there is no string or the box is too small for * anything to be drawn, does nothing</p> * * @param g the <code>Graphics</code> object to draw to * @param fm the <code>FontMetrics</code> object to use for string sizing * @param padding the <code>int</code> amount of padding around the string * @param hAlign the <code>int</code> horizontal alignment * */ public void drawWrappedStringTruncate(Graphics g, FontMetrics fm, int padding, int hAlign) { if (getStringArray() == null) { Point p = getDrawingPoint(); int xx = (int)p.getX(); int yy = (int)p.getY(); if (getFullString() != null) { g.drawString(getFullString(), xx, yy); } else { System.err.println("getStringArray and getFullString are null"); } } else { int totalHeight = 0; int len = stringArray.length; for (int i = 0; i < len; i++) { BoundingBox wrappedBox = null; try { wrappedBox = getStringBounds(stringArray[i], hAlign, BoundingBox.VERT_ALIGN_TOP, fm, 0, false); totalHeight += (int)wrappedBox.getHeight(); if (getParent() != null) { if (totalHeight > (int)(getParent().getHeight())) { return; } } } catch (StringTooLongException stle) { stle.printStackTrace(); return; } wrappedBox.drawChoppedString(g, fm, padding, hAlign); subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM); } } } // end drawWrappedStringTruncate /** * <p>Take the first line of the string (if it is wrapped, otherwise just * take the whole string) and chop the end of it off to make it fit in the * box. If the box is smaller than one letter, draw nothing</p> * * @param g the <code>Graphics</code> object to draw to * @param fm the <code>FontMetrics</code> object to use for string sizing * @param padding the <code>int</code> amount of padding around the string * @param hAlign the <code>int</code> horizontal alignment */ public void drawChoppedString(Graphics g, FontMetrics fm, int padding, int hAlign) { String string = ""; if (getStringArray() != null) { string = new String(getStringArray()[0]); } else { string = new String(getFullString()); } BoundingBox choppedBox = null; try { choppedBox = getStringBounds(string, hAlign, VERT_ALIGN_TOP, fm, padding); Point p = choppedBox.getDrawingPoint(); int x = (int)p.getX(); int y = (int)p.getY(); g.drawString(string, x, y); } catch (StringTooLongException stle) { // Doesn't fit - start cutting from the end until it does StringBuffer buf = new StringBuffer().append(string); if (buf.length() == 0) { System.out.println("BoundingBox.drawChoppedString, buf len 0 ??"); //return; throw new RuntimeException(); } buf.deleteCharAt(buf.length()-1); while ((fm.stringWidth(buf.toString()) > (int)getWidth()) && (buf.length() > 0)) { buf.deleteCharAt(buf.length()-1); } try { choppedBox = getStringBounds(buf.toString(), hAlign, VERT_ALIGN_TOP, fm, padding); Point pp = choppedBox.getDrawingPoint(); int xx = (int)pp.getX(); int yy = (int)pp.getY(); g.drawString(string, xx, yy); } catch (StringTooLongException sstle) { // Must be a really small box! sstle.printStackTrace(); } } } // end drawChoppedString /** * <p>Get the total height of the box needed to contain the strings in * the specified array</p> */ private int getWrappedHeight(String[] strings, FontMetrics fm, int padding) { int ma = fm.getMaxAscent(); int md = fm.getMaxDescent(); int sh = ma + md; int hPad = sh / LINE_SPACING_PERCENTAGE; sh += hPad; int total = sh * strings.length; return total + (padding*2); } // end getWrappedHeight /** * * <p>Make a string array from a string, wrapped to fit the box</p> * * <p>If the line width is too short, the array is just a * tokenized version of the string</p> * * @param string - the <code>String</code> to convert to an array * @param */ private String[] createStringArray(String string, FontMetrics fm, int padding, int pw) { if (string == null) { System.err.println("Tried createStringArray with null String"); return null; } if (fm == null) { System.err.println("Tried createStringArray with null FontMetrics"); } int lw = pw - (padding*2); Vector<String> returnVector = new Vector<String>(); // Return delimiters as tokens StringTokenizer st = new StringTokenizer(string, " \t\n\r\f", true); StringBuffer tempBuffer = new StringBuffer(); StringBuffer finalBuffer = new StringBuffer(); while(st.hasMoreTokens()) { // Get the next word and add a space after it String tempString = st.nextToken(); tempBuffer.append(tempString); // If we haven't reached the width with our current // line, keep adding tokens. Also, check for hard returns if ((fm.stringWidth(tempBuffer.toString()) < lw) && (tempBuffer.toString() .charAt(tempBuffer.toString().length() - 1) != '\n') && (tempBuffer.toString() .charAt(tempBuffer.toString().length() - 1) != '\r')) { finalBuffer.append(tempString); continue; } returnVector.addElement(finalBuffer.toString()); finalBuffer.delete(0, finalBuffer.length()); tempBuffer.delete(0, tempBuffer.length()); if ((tempString.charAt(0) != '\n') && (tempString.charAt(0) != '\r')) { tempBuffer.append(tempString); finalBuffer.append(tempString); } continue; } // end while returnVector.addElement(finalBuffer.toString()); int len = returnVector.size(); // Init the class member field stringArray String[] childStrArray = new String[len]; for (int i = 0; i < len; i++) { String curStr = (String)returnVector.get(i); childStrArray[i] = curStr; } return childStrArray; } // end createStringArray /** * <p>Removes the child box from this parent box. The child must * have this object as its parent or the method does nothing. * The BoundingBox returned will be cut by an area equal to * the child area plus the horizontal or vertical strip in * which it sits, depending on the 'subtractFrom' value passed * in</p> * * @param child a <code>BoundingBox</code> value * @param subtractFrom an <code>int</code>, SUBTRACT_FROM_LEFT, SUBTRACT_FROM_RIGHT, SUBTRACT_FROM_TOP, SUBTRACT_FROM_BOTTOM * @return a <code>BoundingBox</code> value * @see #SUBTRACT_FROM_LEFT * @see #SUBTRACT_FROM_RIGHT * @see #SUBTRACT_FROM_TOP * @see #SUBTRACT_FROM_BOTTOM */ public BoundingBox subtract(BoundingBox child, int subtractFrom) { // First, check to see if the params are valid if (child == null) { throw new IllegalArgumentException("BoundingBox.subtract, " + "BoundingBox child is null"); } if (!child.hasParent()) { throw new IllegalArgumentException("BoundingBox.subtract, " + "BoundingBox child has no parent"); } if (!(child.getParent() == this)) { throw new IllegalArgumentException("BoundingBox.subtract, " + "this is not BoundingBox child's parent"); } // Now that we know the child is this object's child, we continue // and check the subtractFrom param int len = SUBTRACTS.length; boolean valid = false; for (int i = 0; i < len; i++) { if (subtractFrom == SUBTRACTS[i]) { valid = true; } } if (!valid) { throw new IllegalArgumentException("BoundingBox.subtract, " + "subtractFrom invalid: " + subtractFrom); } // Now we know the child is valid, and if the subtractFrom // preference was invalid, we subtract from the bottom // The child should no longer be used, since the parent // reference will be invalid child.setParent(null); int cx = (int) child.getLocation().getX(); int cy = (int) child.getLocation().getY(); int cw = (int) child.getSize().getWidth(); int ch = (int) child.getSize().getHeight(); int px = (int) this.getLocation().getX(); int py = (int) this.getLocation().getY(); int pw = (int) this.getSize().getWidth(); int ph = (int) this.getSize().getHeight(); switch (subtractFrom) { case SUBTRACT_FROM_LEFT: // This will be useful for right-justified Strings in tables pw = cx; this.setSize(new Dimension(pw, ph)); return this; case SUBTRACT_FROM_RIGHT: // This will be useful for left justified Strings in tables px = px + cw + cx; pw = pw - cw - cx; this.setLocation(new Point(px, py)); this.setSize(new Dimension(pw, ph)); return this; case SUBTRACT_FROM_BOTTOM: py = py + ch + cy; ph = ph - ch - cy; this.setLocation(new Point(px, py)); this.setSize(new Dimension(pw, ph)); return this; case SUBTRACT_FROM_TOP: ph = cy; this.setSize(new Dimension(pw, ph)); return this; default: // Should never happen break; } // end switch return this; } // end subtract /** * <p> * Gets the drawing point to use in Graphics drawing methods. After getting * a new BoundingBox with getStringBounds(), calling this method will give * you an absolute point, accounting for alignment and padding, etc, from * which to start drawing the String * </p> * * <p> * If getStringBounds was not called (this is a parent box), the upper left * coordinates will be returned (this.getLocation()) * </p> * * @return a <code>Point</code> */ public Point getDrawingPoint() { return drawingPoint; } // main method is for testing ///////////////// /** * For testing * * @param args a <code>String[]</code> value */ public static void main(String[] args) { Point upperLeft = new Point(5, 5); Dimension bounds = new Dimension(100, 100); BoundingBox parent = new BoundingBox(upperLeft, bounds); String string = "Hello World!"; Font font = new Font("SansSerif", Font.PLAIN, 12); Frame frame = new Frame(); frame.addNotify(); try { Image image = frame.createImage(100, 100); if (image == null) { System.err.println("image is null"); } Graphics graphics = image.getGraphics(); FontMetrics fm = graphics.getFontMetrics(font); BoundingBox child = parent .getStringBounds(string, BoundingBox.HORIZ_ALIGN_LEFT, BoundingBox.VERT_ALIGN_TOP, fm, 5); System.out.println("Drawing Point: " + child.getDrawingPoint().toString()); System.out.println("Now testing subtract() method..."); parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); System.out.println("parent: " + parent.toString()); child = new BoundingBox(new Point(90, 110), new Dimension(100, 100)); parent.add(child); System.out.println("child: " + child.toString()); System.out.println(); System.out.println("subtracting the child from the parent"); System.out.println("SUBTRACT_FROM_TOP: "); parent = parent.subtract(child, SUBTRACT_FROM_TOP); System.out.println("new parent: " + parent.toString()); System.out.println(); System.out.println("Resetting parent"); parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); parent.add(child); System.out.println("SUBTRACT_FROM_BOTTOM"); parent.subtract(child, SUBTRACT_FROM_BOTTOM); System.out.println("new parent: " + parent.toString()); System.out.println(); System.out.println("Resetting parent"); parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); parent.add(child); System.out.println("SUBTRACT_FROM_LEFT"); parent.subtract(child, SUBTRACT_FROM_LEFT); System.out.println("new parent: " + parent.toString()); System.out.println(); System.out.println("Resetting parent"); parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); parent.add(child); System.out.println("SUBTRACT_FROM_RIGHT"); parent.subtract(child, SUBTRACT_FROM_RIGHT); System.out.println("new parent: " + parent.toString()); System.out.println(); System.exit(0); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } // Private methods ///////////////////////////// /** * Creates a new <code>BoundingBox</code> instance. * * @param p a <code>Point</code> value * @param d a <code>Dimension</code> value * @param drawingPoint a <code>Point</code> value */ private BoundingBox(Point p, Dimension d, Point drawingPoint, Point absolute) { super(p, d); this.drawingPoint = drawingPoint; this.absoluteLocation = absolute; } /** * <p>Checks the horizontal alignment passed into a * method to make sure it is one of the valid values</p> * * @param hAlign an <code>int</code> value * @return a <code>boolean</code> value */ private boolean checkHAlign(int hAlign) { int len = HORIZ_ALIGNS.length; for (int i = 0; i < len; i++) { if (hAlign == HORIZ_ALIGNS[i]) { return true; } } return false; } /** * <p>Checks the vertical alignment passed into a * method to make sure it is one of the valid values</p> * * @param vAlign an <code>int</code> value * @return a <code>boolean</code> value */ private boolean checkVAlign(int vAlign) { int len = VERT_ALIGNS.length; for (int i = 0; i < len; i++) { if (vAlign == VERT_ALIGNS[i]) { return true; } } return false; } } // end class BoundingBox