001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.app;
031:
032: import java.io.Serializable;
033:
034: /**
035: * A representation of a linear distance with units. <code>Extent</code>
036: * objects are immutable once constructed.
037: * <p>
038: * <strong>WARNING:</strong> Many <code>Component</code>s will have
039: * <code>Extent</code>-based properties that allow only certain types of
040: * units. Make certain to verify the API specification of any
041: * <code>Component</code> to ensure that you are using <code>Extent</code>s
042: * correctly with it. The <code>Extent</code>-based <code>getXXX()</code>
043: * and <code>setXXX()</code> property methods of a <code>Component</code>
044: * will explain what types of <code>Extent</code>s are allowed.
045: */
046: public class Extent implements Comparable, Serializable {
047:
048: /**
049: * Adds one <code>Extent</code> to another, returning the sum as a new
050: * <code>Extent</code>. Null is returned if the <code>Extent</code>s have
051: * incompatible units. If either provided <code>Extent</code> is null, the
052: * other is returned.
053: *
054: * @param a the first <code>Extent</code>
055: * @param b the second <code>Extent</code>
056: * @return the sum of the <code>Extent</code>s, if calculable
057: */
058: public static Extent add(Extent a, Extent b) {
059: if (a == null) {
060: return b;
061: }
062: if (b == null) {
063: return a;
064: }
065: if (a.getUnits() == b.getUnits()) {
066: return new Extent(a.getValue() + b.getValue(), a.getUnits());
067: }
068: if (a.isPrint() && b.isPrint()) {
069: if (a.isEnglish() && b.isEnglish()) {
070: return new Extent(a.toPoint() + b.toPoint(), PT);
071: }
072: return new Extent(a.toMm() + b.toMm(), MM);
073: }
074: return null;
075: }
076:
077: /**
078: * Validates that the specified <code>Extent</code> is acceptable for use
079: * in a particular environment, by ensuring that its units are of a
080: * supported type.
081: *
082: * @param value the <code>Extent</code> to validate
083: * @param validUnits a bitmask containing one or more of the unit constants
084: * (multiple unit constants may be ORed together)
085: * @throws IllegalArgumentException if the <code>Extent</code> is invalid
086: */
087: public static void validate(Extent value, int validUnits) {
088: if (value != null && (value.getUnits() & validUnits) == 0) {
089: throw new IllegalArgumentException(
090: "Specified units are unsupported in this context.");
091: }
092: }
093:
094: /**
095: * Pixel units.
096: */
097: public static final int PX = 1;
098:
099: /**
100: * Percentage units.
101: */
102: public static final int PERCENT = 2;
103:
104: /**
105: * Points (1pt = 1/72in).
106: */
107: public static final int PT = 4;
108:
109: /**
110: * Centimeter units.
111: */
112: public static final int CM = 8;
113:
114: /**
115: * Millimeter units.
116: */
117: public static final int MM = 16;
118:
119: /**
120: * Inch units.
121: */
122: public static final int IN = 32;
123:
124: /**
125: * Em units (height of font).
126: */
127: public static final int EM = 64;
128:
129: /**
130: * Ex units (height of character 'x' in font).
131: */
132: public static final int EX = 128;
133:
134: /**
135: * Picas (1pc = 12pt)
136: */
137: public static final int PC = 256;
138:
139: private int value;
140: private int units;
141:
142: /**
143: * Creates a new <code>Extent</code> with pixel units.
144: *
145: * @param value the value of the extent in pixels
146: */
147: public Extent(int value) {
148: this .value = value;
149: this .units = Extent.PX;
150: }
151:
152: /**
153: * Creates a new <code>Extent</code>.
154: *
155: * @param value the value of the extent
156: * @param units the units of the value, one of the following constants:
157: * <ul>
158: * <li><code>PC</code>: Pixels</li>
159: * <li><code>PERCENT</code>: Percent (of size of containing
160: * component)</li>
161: * <li><code>PT</code>: Points</li>
162: * <li><code>CM</code>: Centimeters</li>
163: * <li><code>MM</code>: Millimeters</li>
164: * <li><code>IN</code>: Inches</li>
165: * <li><code>EM</code>: Ems (height of 'M' character)</li>
166: * <li><code>EX</code>: Exs (height of 'x' character)</li>
167: * <li><code>PC</code>: Picas</li>
168: * </ul>
169: */
170: public Extent(int value, int units) {
171: this .value = value;
172: this .units = units;
173: }
174:
175: /**
176: * @see java.lang.Comparable#compareTo(java.lang.Object)
177: */
178: public int compareTo(Object o) {
179: Extent that = (Extent) o;
180: if (this .units == that.units) {
181: return this .value - that.value;
182: }
183: if (this .isPrint() && that.isPrint()) {
184: return this .toPoint() - that.toPoint();
185: }
186: return this .units - that.units;
187: }
188:
189: /**
190: * @see java.lang.Object#equals(java.lang.Object)
191: */
192: public boolean equals(Object o) {
193: if (this == o) {
194: return true;
195: } else if (o == null) {
196: return false;
197: } else if (o instanceof Extent) {
198: Extent that = (Extent) o;
199: return this .value == that.value && this .units == that.units;
200: } else {
201: return false;
202: }
203: }
204:
205: /**
206: * Returns the value of the <code>Extent</code>.
207: *
208: * @return The value of the <code>Extent</code>
209: */
210: public int getValue() {
211: return value;
212: }
213:
214: /**
215: * Returns the units of the <code>Extent</code>.
216: *
217: * @return The units of the <code>Extent</code>, one of the following
218: * constants:
219: * <ul>
220: * <li><code>PC</code>: Pixels</li>
221: * <li><code>PERCENT</code>: Percent (of size of containing
222: * component)</li>
223: * <li><code>PT</code>: Points</li>
224: * <li><code>CM</code>: Centimeters</li>
225: * <li><code>MM</code>: Millimeters</li>
226: * <li><code>IN</code>: Inches</li>
227: * <li><code>EM</code>: Ems (height of 'M' character)</li>
228: * <li><code>EX</code>: Exs (height of 'x' character)</li>
229: * <li><code>PC</code>: Picas</li>
230: * </ul>
231: */
232: public int getUnits() {
233: return units;
234: }
235:
236: /**
237: * Determines whether this <code>Extent</code> can be compared to another
238: * <code>Extent</code> to determine which is a greater length.
239: *
240: * @param that the <code>Extent</code> to test comparability to
241: * @return true if the <code>Extent</code>s can be compared
242: */
243: public boolean isComparableTo(Extent that) {
244: return this .units == that.units
245: || (this .isPrint() && that.isPrint());
246: }
247:
248: /**
249: * Determines if the <code>Extent</code> has English units, i.e., the
250: * units are of type <code>IN</code> (inches), <code>PC</code> (picas), or
251: * <code>PT</code> (points).
252: *
253: * @return true if this <code>Extent</code> has English units
254: */
255: public boolean isEnglish() {
256: return units == IN || units == PC || units == PT;
257: }
258:
259: /**
260: * Determines if the <code>Extent</code> has SI (Metric) units, i.e., the
261: * units are of type <code>MM</code> (millimeters) or <code>CM</code>
262: * (centimeters).
263: *
264: * @return true if this <code>Extent</code> has SI units
265: */
266: public boolean isSI() {
267: return units == MM || units == CM;
268: }
269:
270: /**
271: * Determines if the <code>Extent</code> has percentage-based units.
272: *
273: * @return true if the <code>Extent</code> has percentage-based units
274: */
275: public boolean isPercentage() {
276: return units == PERCENT;
277: }
278:
279: /**
280: * Determines if this <code>Extent</code> has 'print' based units, i.e.,
281: * the units are in real dimensions, such as SI or English values, rather
282: * than screen-based units such as pixels or percentages.
283: *
284: * @return true if this <code>Extent</code> has 'print' based units
285: */
286: public boolean isPrint() {
287: return units == IN || units == PC || units == PT || units == MM
288: || units == CM;
289: }
290:
291: /**
292: * Returns the value of the extent in millimeters.
293: *
294: * @return the value of the extent in millimeters
295: * @throws IllegalStateException if the value cannot be returned in
296: * millimeters.
297: * Verify that <code>isPrint()</code> returns true to avoid
298: * potentially receiving this exception.
299: */
300: public int toMm() {
301: switch (units) {
302: case MM:
303: return value;
304: case CM:
305: return value * 10;
306: case IN:
307: return (int) (value * 25.4);
308: case PT:
309: return (int) ((value / 72) * 25.4);
310: case PC:
311: return (int) ((value / 6) * 25.4);
312: }
313: throw new IllegalStateException("Cannot convert to mm.");
314: }
315:
316: /**
317: * Returns the value of the extent in points.
318: *
319: * @return the value of the extent in points
320: * @throws IllegalStateException if the value cannot be returned in points
321: * (verify that <code>isPrint()</code> returns true to avoid
322: * potentially receiving this exception).
323: */
324: public int toPoint() {
325: switch (units) {
326: case PT:
327: return value;
328: case PC:
329: return value * 12;
330: case IN:
331: return value * 72;
332: case MM:
333: return (int) ((value / 25.4) * 72);
334: case CM:
335: return (int) ((value / 2.54) * 72);
336: }
337: throw new IllegalStateException("Cannot convert to pt.");
338: }
339:
340: /**
341: * Returns a string describing the state of the Extent.
342: * For debugging purposes only, do not rely on formatting.
343: *
344: * @see java.lang.Object#toString()
345: */
346: public String toString() {
347: StringBuffer out = new StringBuffer();
348: out.append(value);
349: switch (units) {
350: case Extent.CM:
351: out.append("cm");
352: break;
353: case Extent.EM:
354: out.append("em");
355: break;
356: case Extent.EX:
357: out.append("ex");
358: break;
359: case Extent.IN:
360: out.append("in");
361: break;
362: case Extent.MM:
363: out.append("mm");
364: break;
365: case Extent.PC:
366: out.append("pc");
367: break;
368: case Extent.PERCENT:
369: out.append("%");
370: break;
371: case Extent.PT:
372: out.append("pt");
373: break;
374: case Extent.PX:
375: out.append("px");
376: break;
377: }
378: return out.toString();
379: }
380: }
|