001/* 002 * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved. 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * o Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 010 * o Redistributions in binary form must reproduce the above copyright notice, 011 * this list of conditions and the following disclaimer in the documentation 012 * and/or other materials provided with the distribution. 013 * 014 * o Neither the name of JGoodies Karsten Lentzsch nor the names of 015 * its contributors may be used to endorse or promote products derived 016 * from this software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031package com.jgoodies.forms.layout; 032 033import java.awt.Component; 034import java.awt.Container; 035import java.io.Serializable; 036import java.util.List; 037import java.util.Locale; 038 039/** 040 * An implementation of the {@link Size} interface that represents constant 041 * sizes described by a value and unit, for example: 042 * 10 pixel, 15 point or 4 dialog units. 043 * You can get instances of <code>ConstantSize</code> using 044 * the factory methods and constants in the {@link Sizes} class. 045 * Logical constant sizes that vary with the current layout style 046 * are delivered by the {@link com.jgoodies.forms.util.LayoutStyle} class.<p> 047 * 048 * This class supports different size units: 049 * <table> 050 * <caption>Size units</caption> 051 * <tr><td><b>Unit</b> 052 * </td><td> <b>Abbreviation</b> </td><td> 053 * <b>Size</b></td></tr> 054 * <tr><td>Millimeter</td><td>mm</td><td>0.1 cm</td></tr> 055 * <tr><td>Centimeter</td><td>cm</td><td>10.0 mm</td></tr> 056 * <tr><td>Inch</td><td>in</td><td>25.4 mm</td></tr> 057 * <tr><td>DTP Point</td><td>pt</td><td>1/72 in</td></tr> 058 * <tr><td>Pixel</td><td>px</td><td>1/(resolution in dpi) in</td></tr> 059 * <tr><td>Dialog Unit</td><td>dlu</td><td>honors l&f, resolution, and 060 * dialog font size</td></tr> 061 * </table><p> 062 * 063 * <strong>Examples:</strong><pre> 064 * Sizes.ZERO; 065 * Sizes.DLUX9; 066 * Sizes.dluX(42); 067 * Sizes.pixel(99); 068 * </pre> 069 * 070 * @author Karsten Lentzsch 071 * @version $Revision$ 072 * 073 * @see Size 074 * @see Sizes 075 */ 076 077@SuppressWarnings("serial") 078public final class ConstantSize implements Size, Serializable { 079 080 // Public Units ********************************************************* 081 082 public static final Unit PIXEL = new Unit("Pixel", "px", true); 083 public static final Unit POINT = new Unit("Point", "pt", true); 084 public static final Unit DIALOG_UNITS_X = new Unit("Dialog units X", "dluX", 085 true); 086 public static final Unit DLUX = DIALOG_UNITS_X; 087 public static final Unit DIALOG_UNITS_Y = new Unit("Dialog units Y", "dluY", 088 true); 089 public static final Unit DLUY = DIALOG_UNITS_Y; 090 public static final Unit MILLIMETER = new Unit("Millimeter", "mm", false); 091 public static final Unit MM = MILLIMETER; 092 public static final Unit CENTIMETER = new Unit("Centimeter", "cm", false); 093 public static final Unit CM = CENTIMETER; 094 public static final Unit INCH = new Unit("Inch", "in", false); 095 public static final Unit IN = INCH; 096 097 /** 098 * An array of all enumeration values used to canonicalize 099 * deserialized units. 100 */ 101 private static final Unit[] VALUES = { PIXEL, POINT, DIALOG_UNITS_X, 102 DIALOG_UNITS_Y, MILLIMETER, CENTIMETER, INCH }; 103 104 // Fields *************************************************************** 105 106 private final double value; 107 private final Unit unit; 108 109 // Instance Creation **************************************************** 110 111 /** 112 * Constructs an instance of <code>ConstantSize</code> from the given 113 * encoded size and unit description. 114 * 115 * @param value the size value interpreted in the given units 116 * @param unit the size's unit 117 */ 118 ConstantSize(int value, Unit unit) { 119 this.value = value; 120 this.unit = unit; 121 } 122 123 /** 124 * Constructs an instance of <code>ConstantSize</code> from the given 125 * encoded size and unit description. 126 * 127 * @param value the size value interpreted in the given units 128 * @param unit the size's unit 129 */ 130 ConstantSize(double value, Unit unit) { 131 this.value = value; 132 this.unit = unit; 133 } 134 135 /** 136 * Constructs an instance of <code>ConstantSize</code> from the given 137 * encoded size and unit description. 138 * 139 * @param encodedValueAndUnit the size's value and unit as string 140 * @param horizontal true for horizontal, false for vertical 141 * @return a constant size for the given encoding and unit description 142 * @exception IllegalArgumentException if the unit requires integer 143 * but the value is not an integer 144 */ 145 static ConstantSize valueOf(String encodedValueAndUnit, 146 boolean horizontal) { 147 String split[] = ConstantSize.splitValueAndUnit(encodedValueAndUnit); 148 String encodedValue = split[0]; 149 String encodedUnit = split[1]; 150 Unit unit = Unit.valueOf(encodedUnit, horizontal); 151 double value = Double.parseDouble(encodedValue); 152 if (unit.requiresIntegers) { 153 if (value != (int) value) { 154 throw new IllegalArgumentException(unit.toString() + " value " 155 + encodedValue + " must be an integer."); 156 } 157 } 158 return new ConstantSize(value, unit); 159 } 160 161 /** 162 * Returns an instance of <code>Size</code> for the specified value 163 * in horizontal dialog units. 164 * 165 * @param value size value in horizontal dialog units 166 * @return the associated Size instance 167 */ 168 static ConstantSize dluX(int value) { 169 return new ConstantSize(value, DLUX); 170 } 171 172 /** 173 * Returns an instance of <code>Size</code> for the specified value 174 * in vertical dialog units. 175 * 176 * @param value size value in vertical dialog units 177 * @return the associated Size instance 178 */ 179 static ConstantSize dluY(int value) { 180 return new ConstantSize(value, DLUY); 181 } 182 183 // Accessing the Value ************************************************** 184 185 /** 186 * Converts the size if necessary and returns the value in pixels. 187 * 188 * @param component the associated component 189 * @return the size in pixels 190 */ 191 public int getPixelSize(Component component) { 192 if (unit == PIXEL) { 193 return intValue(); 194 } else if (unit == POINT) { 195 return Sizes.pointAsPixel(intValue(), component); 196 } else if (unit == INCH) { 197 return Sizes.inchAsPixel(value, component); 198 } else if (unit == MILLIMETER) { 199 return Sizes.millimeterAsPixel(value, component); 200 } else if (unit == CENTIMETER) { 201 return Sizes.centimeterAsPixel(value, component); 202 } else if (unit == DIALOG_UNITS_X) { 203 return Sizes.dialogUnitXAsPixel(intValue(), component); 204 } else if (unit == DIALOG_UNITS_Y) { 205 return Sizes.dialogUnitYAsPixel(intValue(), component); 206 } else { 207 throw new IllegalStateException("Invalid unit " + unit); 208 } 209 } 210 211 // Implementing the Size Interface ************************************** 212 213 /** 214 * Returns this size as pixel size. Neither requires the component 215 * list nor the specified measures.<p> 216 * 217 * Invoked by {@link com.jgoodies.forms.layout.FormSpec} to determine 218 * the size of a column or row. 219 * 220 * @param container the layout container 221 * @param components the list of components used to compute the size 222 * @param minMeasure the measure that determines the minimum sizes 223 * @param prefMeasure the measure that determines the preferred sizes 224 * @param defaultMeasure the measure that determines the default sizes 225 * @return the computed maximum size in pixel 226 */ 227 @Override 228 public int maximumSize(Container container, List components, 229 FormLayout.Measure minMeasure, FormLayout.Measure prefMeasure, 230 FormLayout.Measure defaultMeasure) { 231 return getPixelSize(container); 232 } 233 234 // Overriding Object Behavior ******************************************* 235 236 /** 237 * Indicates whether some other ConstantSize is "equal to" this one. 238 * 239 * @param o the Object with which to compare 240 * @return <code>true</code> if this object is the same as the obj 241 * argument; <code>false</code> otherwise. 242 * @see java.lang.Object#hashCode() 243 * @see java.util.Hashtable 244 */ 245 @Override 246 public boolean equals(Object o) { 247 if (this == o) { 248 return true; 249 } 250 if (!(o instanceof ConstantSize)) { 251 return false; 252 } 253 ConstantSize size = (ConstantSize) o; 254 return this.value == size.value && this.unit == size.unit; 255 } 256 257 /** 258 * Returns a hash code value for the object. This method is 259 * supported for the benefit of hashtables such as those provided by 260 * <code>java.util.Hashtable</code>. 261 * 262 * @return a hash code value for this object. 263 * @see java.lang.Object#equals(java.lang.Object) 264 * @see java.util.Hashtable 265 */ 266 @Override 267 public int hashCode() { 268 return Double.valueOf(value).hashCode() + 37 * unit.hashCode(); 269 } 270 271 /** 272 * Returns a string representation of this size object. 273 * 274 * <strong>Note:</strong> The string representation may change 275 * at any time. It is strongly recommended to not use this string 276 * for parsing purposes. 277 * 278 * @return a string representation of the constant size 279 */ 280 @Override 281 public String toString() { 282 return value == intValue() 283 ? Integer.toString(intValue()) + unit.abbreviation() 284 : Double.toString(value) + unit.abbreviation(); 285 } 286 287 // Helper Code ********************************************************** 288 289 private int intValue() { 290 return (int) Math.round(value); 291 } 292 293 /** 294 * Splits a string that encodes size with unit into the size and unit 295 * substrings. Returns an array of two strings. 296 * 297 * @param encodedValueAndUnit a strings that represents a size with unit 298 * @return the first element is size, the second is unit 299 */ 300 static String[] splitValueAndUnit(String encodedValueAndUnit) { 301 String[] result = new String[2]; 302 int len = encodedValueAndUnit.length(); 303 int firstLetterIndex = len; 304 while (firstLetterIndex > 0 && Character 305 .isLetter(encodedValueAndUnit.charAt(firstLetterIndex - 1))) { 306 firstLetterIndex--; 307 } 308 result[0] = encodedValueAndUnit.substring(0, firstLetterIndex); 309 result[1] = encodedValueAndUnit.substring(firstLetterIndex); 310 return result; 311 } 312 313 // Helper Class ********************************************************* 314 315 /** 316 * An ordinal-based serializable typesafe enumeration for units 317 * as used in instances of {@link ConstantSize}. 318 */ 319 public static final class Unit implements Serializable { 320 321 private final transient String name; 322 private final transient String abbreviation; 323 final transient boolean requiresIntegers; 324 325 private Unit(String name, String abbreviation, 326 boolean requiresIntegers) { 327 this.name = name; 328 this.abbreviation = abbreviation; 329 this.requiresIntegers = requiresIntegers; 330 } 331 332 /** 333 * Returns an instance of <code>Unit</code> that corresponds to the 334 * specified string. 335 * 336 * @param str the encoded unit 337 * @param horizontal true for a horizontal unit, false for vertical 338 * @return the corresponding Unit 339 * @exception IllegalArgumentException if no Unit exists for the string 340 */ 341 static Unit valueOf(String str, boolean horizontal) { 342 String lowerCase = str.toLowerCase(Locale.ENGLISH); 343 if (lowerCase.equals("px") || lowerCase.length() == 0) { 344 return PIXEL; 345 } else if (lowerCase.equals("dlu")) { 346 return horizontal ? DIALOG_UNITS_X : DIALOG_UNITS_Y; 347 } else if (lowerCase.equals("pt")) { 348 return POINT; 349 } else if (lowerCase.equals("in")) { 350 return INCH; 351 } else if (lowerCase.equals("mm")) { 352 return MILLIMETER; 353 } else if (lowerCase.equals("cm")) { 354 return CENTIMETER; 355 } else { 356 throw new IllegalArgumentException("Invalid unit name '" + str 357 + "'. Must be one of: " + "px, dlu, pt, mm, cm, in"); 358 } 359 } 360 361 @Override 362 public String toString() { 363 return name; 364 } 365 366 /** 367 * Returns the first character of this Unit's name. 368 * Used to identify it in short format strings. 369 * 370 * @return the first character of this Unit's name. 371 */ 372 public String abbreviation() { 373 return abbreviation; 374 } 375 376 // Serialization ***************************************************** 377 378 private static int nextOrdinal = 0; 379 380 private final int ordinal = nextOrdinal++; 381 382 private Object readResolve() { 383 return VALUES[ordinal]; // Canonicalize 384 } 385 386 } 387 388}