content: package themeengine.include.net.miginfocom.layout;
  /*
   * License (BSD):
   * ==============
   *
   * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   * Redistributions of source code must retain the above copyright notice, this list
   * of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this
   * list of conditions and the following disclaimer in the documentation and/or other
   * materials provided with the distribution.
   * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
   * used to endorse or promote products derived from this software without specific
   * prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
   * OF SUCH DAMAGE.
   *
   * @version 1.0
   * @author Mikael Grev, MiG InfoCom AB
   *         Date: 2006-sep-08
   */
  
  import java.beans.Encoder;
  import java.beans.Expression;
  import java.beans.PersistenceDelegate;
  import java.io.*;
  import java.util.ArrayList;
  import java.util.HashMap;
  
  public final class UnitValue implements Serializable
  {
  	private static final HashMap<String, Integer> UNIT_MAP = new HashMap<String, Integer>(32);
  
  	private static final ArrayList<UnitConverter> CONVERTERS = new ArrayList<UnitConverter>();
  
  	/** An operation indicating a static value.
  	 */
  	public static final int STATIC = 100;
  
  	/** An operation indicating a addition of two sub units.
  	 */
  	public static final int ADD = 101; // Must have "sub-unit values"
  
  	/** An operation indicating a subtraction of two sub units
  	 */
  	public static final int SUB = 102; // Must have "sub-unit values"
  
  	/** An operation indicating a multiplication of two sub units.
  	 */
  	public static final int MUL = 103; // Must have "sub-unit values"
  
  	/** An operation indicating a division of two sub units.
  	 */
  	public static final int DIV = 104; // Must have "sub-unit values"
  
  	/** An operation indicating the minimum of two sub units
  	 */
  	public static final int MIN = 105; // Must have "sub-unit values"
  
  	/** An operation indicating the maximum of two sub units
  	 */
  	public static final int MAX = 106; // Must have "sub-unit values"
  
  	/** An operation indicating the middle value of two sub units
  	 */
  	public static final int MID = 107; // Must have "sub-unit values"
  
  
  
  
  	/** A unit indicating pixels.
  	 */
  	public static final int PIXEL = 0;
  
  	/** A unit indicating logical horizontal pixels.
  	 */
  	public static final int LPX = 1;
  
  	/** A unit indicating logical vertical pixels.
  	 */
  	public static final int LPY = 2;
  
  	/** A unit indicating millimeters.
  	 */
  	public static final int MM = 3;
  
  	/** A unit indicating centimeters.
  	 */
  	public static final int CM = 4;
  
  	/** A unit indicating inches.
  	 */
  	public static final int INCH = 5;
  
  	/** A unit indicating percent.
  	 */
  	public static final int PERCENT = 6;
  
  	/** A unit indicating points.
  	 */
  	public static final int PT = 7;
  
  	/** A unit indicating screen percentage width.
  	 */
  	public static final int SPX = 8;
  
  	/** A unit indicating screen percentage height.
  	 */
  	public static final int SPY = 9;
  
  	/** A unit indicating alignment.
  	 */
  	public static final int ALIGN = 12;
  
  	/** A unit indicating minimum size.
  	 */
  	public static final int MIN_SIZE = 13;
  
  	/** A unit indicating preferred size.
  	 */
  	public static final int PREF_SIZE = 14;
  
  	/** A unit indicating maximum size.
  	 */
  	public static final int MAX_SIZE = 15;
  
  	/** A unit indicating button size.
  	 */
  	public static final int BUTTON = 16;
  
  	/** A unit indicating linking to x.
  	 */
  	public static final int LINK_X = 18;   // First link
  
  	/** A unit indicating linking to y.
  	 */
  	public static final int LINK_Y = 19;
  
  	/** A unit indicating linking to width.
  	 */
  	public static final int LINK_W = 20;
  
  	/** A unit indicating linking to height.
  	 */
  	public static final int LINK_H = 21;
  
  	/** A unit indicating linking to x2.
  	 */
  	public static final int LINK_X2 = 22;
  
  	/** A unit indicating linking to y2.
  	 */
  	public static final int LINK_Y2 = 23;
  
  	/** A unit indicating linking to x position on screen.
  	 */
  	public static final int LINK_XPOS = 24;
  
  	/** A unit indicating linking to y position on screen.
  	 */
  	public static final int LINK_YPOS = 25;    // Last link
  
  	/** A unit indicating a lookup.
  	 */
  	public static final int LOOKUP = 26;
  
  	/** A unit indicating label alignment.
  	 */
  	public static final int LABEL_ALIGN = 27;
  
  	private static final int IDENTITY = -1;
  
  	static {
  		UNIT_MAP.put("px", PIXEL);
  		UNIT_MAP.put("lpx", LPX);
  		UNIT_MAP.put("lpy", LPY);
  		UNIT_MAP.put("%", PERCENT);
  		UNIT_MAP.put("cm", CM);
  		UNIT_MAP.put("in", INCH);
  		UNIT_MAP.put("spx", SPX);
  		UNIT_MAP.put("spy", SPY);
  		UNIT_MAP.put("al", ALIGN);
  		UNIT_MAP.put("mm", MM);
  		UNIT_MAP.put("pt", PT);
  		UNIT_MAP.put("min", MIN_SIZE);
  		UNIT_MAP.put("minimum", MIN_SIZE);
  		UNIT_MAP.put("p", PREF_SIZE);
  		UNIT_MAP.put("pref", PREF_SIZE);
  		UNIT_MAP.put("max", MAX_SIZE);
  		UNIT_MAP.put("maximum", MAX_SIZE);
  		UNIT_MAP.put("button", BUTTON);
  		UNIT_MAP.put("label", LABEL_ALIGN);
  	}
  
  	static final UnitValue ZERO = new UnitValue(0, null, PIXEL, true, STATIC, null, null, "0px");
  	static final UnitValue TOP = new UnitValue(0, null, PERCENT, false, STATIC, null, null, "top");
  	static final UnitValue LEADING = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "leading");
  	static final UnitValue LEFT = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "left");
  	static final UnitValue CENTER = new UnitValue(50, null, PERCENT, true, STATIC, null, null, "center");
  	static final UnitValue TRAILING = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "trailing");
  	static final UnitValue RIGHT = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "right");
  	static final UnitValue BOTTOM = new UnitValue(100, null, PERCENT, false, STATIC, null, null, "bottom");
  	static final UnitValue LABEL = new UnitValue(0, null, LABEL_ALIGN, false, STATIC, null, null, "label");
  
  	static final UnitValue INF = new UnitValue(LayoutUtil.INF, null, PIXEL, true, STATIC, null, null, "inf");
  
  	static final UnitValue BASELINE_IDENTITY = new UnitValue(0, null, IDENTITY, false, STATIC, null, null, "baseline");
  
  	private final transient float value;
  	private final transient int unit;
  	private final transient int oper;
  	private final transient String unitStr;
  	private transient String linkId = null; // Should be final, but initializes in a sub method.
  	private final transient boolean isHor;
  	private final transient UnitValue[] subUnits;
  
  	// Pixel
  	public UnitValue(float value)  // If hor/ver does not matter.
  	{
  		this(value, null, PIXEL, true, STATIC, null, null, value + "px");
  	}
  
  	public UnitValue(float value, int unit, String createString)  // If hor/ver does not matter.
  	{
  		this(value, null, unit, true, STATIC, null, null, createString);
  	}
  
          public UnitValue(float value, String unitStr, boolean isHor, int oper, String createString)
  	{
  		this(value, unitStr, -1, isHor, oper, null, null, createString);
  	}
  
  	UnitValue(boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
  	{
  		this(0, "", -1, isHor, oper, sub1, sub2, createString);
  		if (sub1 == null || sub2 == null)
  			throw new IllegalArgumentException("Sub units is null!");
  	}
  
  	private UnitValue(float value, String unitStr, int unit, boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
  	{
  		if (oper < STATIC || oper > MID)
  			throw new IllegalArgumentException("Unknown Operation: " + oper);
  
  		if (oper >= ADD && oper <= MID && (sub1 == null || sub2 == null))
  			throw new IllegalArgumentException(oper + " Operation may not have null sub-UnitValues.");
  
  		this.value = value;
  		this.oper = oper;
  		this.isHor = isHor;
  		this.unitStr = unitStr;
  		this.unit = unitStr != null ? parseUnitString() : unit;
  		this.subUnits = sub1 != null && sub2 != null ? new UnitValue[] {sub1, sub2} : null;
  
  		LayoutUtil.putCCString(this, createString);    // "this" escapes!! Safe though.
  	}
  
  	/** Returns the size in pixels rounded.
  	 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
  	 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
  	 * required to return any usable value if <code>null</code>.
  	 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
  	 * connected to any component.
  	 * @return The size in pixels.
  	 */
  	public final int getPixels(float refValue, ContainerWrapper parent, ComponentWrapper comp)
  	{
  		return Math.round(getPixelsExact(refValue, parent, comp));
  	}
  
  	private static final float[] SCALE = new float[] {25.4f, 2.54f, 1f, 0f, 72f};
  	/** Returns the size in pixels.
  	 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
  	 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
  	 * required to return any usable value if <code>null</code>.
  	 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
  	 * connected to any component.
  	 * @return The size in pixels.
  	 */
  	public final float getPixelsExact(float refValue, ContainerWrapper parent, ComponentWrapper comp)
  	{
  		if (parent == null)
  			return 1;
  
  		if (oper == STATIC) {
  			switch (unit) {
  				case PIXEL:
  					return value;
  
  				case LPX:
  				case LPY:
  					return parent.getPixelUnitFactor(unit == LPX) * value;
  
  				case MM:
  				case CM:
  				case INCH:
  				case PT:
  					float f = SCALE[unit - MM];
  					Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
  					if (s != null)
  						f *= s;
  
  					return (isHor ? parent.getHorizontalScreenDPI() : parent.getVerticalScreenDPI()) * value / f;
  
  				case PERCENT:
  					return value * refValue * 0.01f;
  
  				case SPX:
  				case SPY:
  					return (unit == SPX ? parent.getScreenWidth() : parent.getScreenHeight()) * value * 0.01f;
  
  				case ALIGN:
  					Integer st = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.X : LinkHandler.Y);
  					Integer sz = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.WIDTH : LinkHandler.HEIGHT);
  					if (st == null || sz == null)
  						return 0;
  					return value * (Math.max(0, sz.intValue()) - refValue) + st;
  
  				case MIN_SIZE:
  					if (comp == null)
  						return 0;
  					return isHor ? comp.getMinimumWidth(comp.getHeight()) : comp.getMinimumHeight(comp.getWidth());
  
  				case PREF_SIZE:
  					if (comp == null)
  						return 0;
  					return isHor ? comp.getPreferredWidth(comp.getHeight()) : comp.getPreferredHeight(comp.getWidth());
  
  				case MAX_SIZE:
  					if (comp == null)
  						return 0;
  					return isHor ? comp.getMaximumWidth(comp.getHeight()) : comp.getMaximumHeight(comp.getWidth());
  
  				case BUTTON:
  					return PlatformDefaults.getMinimumButtonWidthIncludingPadding(refValue, parent, comp);
  
  				case LINK_X:
  				case LINK_Y:
  				case LINK_W:
  				case LINK_H:
  				case LINK_X2:
  				case LINK_Y2:
  				case LINK_XPOS:
  				case LINK_YPOS:
  					Integer v = LinkHandler.getValue(parent.getLayout(), getLinkTargetId(), unit - (unit >= LINK_XPOS ? LINK_XPOS : LINK_X));
  					if (v == null)
  						return 0;
  
  					if (unit == LINK_XPOS)
  						return parent.getScreenLocationX() + v;
  					if (unit == LINK_YPOS)
  						return parent.getScreenLocationY() + v;
  
  					return v;
  
  				case LOOKUP:
  					float res = lookup(refValue, parent, comp);
  					if (res != UnitConverter.UNABLE)
  						return res;
  
  				case LABEL_ALIGN:
  					return PlatformDefaults.getLabelAlignPercentage() * refValue;
  
  				case IDENTITY:
  			}
  			throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
  		}
  
  		if (subUnits != null && subUnits.length == 2) {
  			float r1 = subUnits[0].getPixelsExact(refValue, parent, comp);
  			float r2 = subUnits[1].getPixelsExact(refValue, parent, comp);
  			switch (oper) {
  				case ADD:
  					return r1 + r2;
  				case SUB:
  					return r1 - r2;
  				case MUL:
  					return r1 * r2;
  				case DIV:
  					return r1 / r2;
  				case MIN:
  					return r1 < r2 ? r1 : r2;
  				case MAX:
  					return r1 > r2 ? r1 : r2;
  				case MID:
  					return (r1 + r2) * 0.5f;
  			}
  		}
  
  		throw new IllegalArgumentException("Internal: Unknown Oper: " + oper);
  	}
  
  	private float lookup(float refValue, ContainerWrapper parent, ComponentWrapper comp)
  	{
  		float res = UnitConverter.UNABLE;
  		for (int i = CONVERTERS.size() - 1; i >= 0; i--) {
  			res = CONVERTERS.get(i).convertToPixels(value, unitStr, isHor, refValue, parent, comp);
  			if (res != UnitConverter.UNABLE)
  				return res;
  		}
  		return PlatformDefaults.convertToPixels(value, unitStr, isHor, refValue, parent, comp);
  	}
  
  	private int parseUnitString()
  	{
  		int len = unitStr.length();
  		if (len == 0)
  			return isHor ? PlatformDefaults.getDefaultHorizontalUnit() : PlatformDefaults.getDefaultVerticalUnit();
  
  		Integer u = UNIT_MAP.get(unitStr);
  		if (u != null) {
  			if (!isHor && (u == BUTTON || u == LABEL_ALIGN))
  				throw new IllegalArgumentException("Not valid in vertical contexts: '" + unitStr + "'");
  
  			return u;
  		}
  
  		if (unitStr.equals("lp"))
  			return isHor ? LPX : LPY;
  
  		if (unitStr.equals("sp"))
  			return isHor ? SPX : SPY;
  
  		if (lookup(0, null, null) != UnitConverter.UNABLE)    // To test so we can fail fast
  			return LOOKUP;
  
  		// Only link left. E.g. "otherID.width"
  
  		int pIx = unitStr.indexOf('.');
  		if (pIx != -1) {
  			linkId = unitStr.substring(0, pIx);
  			String e = unitStr.substring(pIx + 1);
  
  			if (e.equals("x"))
  				return LINK_X;
  			if (e.equals("y"))
  				return LINK_Y;
  			if (e.equals("w") || e.equals("width"))
  				return LINK_W;
  			if (e.equals("h") || e.equals("height"))
  				return LINK_H;
  			if (e.equals("x2"))
  				return LINK_X2;
  			if (e.equals("y2"))
  				return LINK_Y2;
  			if (e.equals("xpos"))
  				return LINK_XPOS;
  			if (e.equals("ypos"))
  				return LINK_YPOS;
  		}
  
  		throw new IllegalArgumentException("Unknown keyword: " + unitStr);
  	}
  
  	final boolean isAbsolute()
  	{
  		switch (unit) {
  			case PIXEL:
  			case LPX:
  			case LPY:
  			case MM:
  			case CM:
  			case INCH:
  			case PT:
  				return true;
  
  			case SPX:
  			case SPY:
  			case PERCENT:
  			case ALIGN:
  			case MIN_SIZE:
  			case PREF_SIZE:
  			case MAX_SIZE:
  			case BUTTON:
  			case LINK_X:
  			case LINK_Y:
  			case LINK_W:
  			case LINK_H:
  			case LINK_X2:
  			case LINK_Y2:
  			case LINK_XPOS:
  			case LINK_YPOS:
  			case LOOKUP:
  			case LABEL_ALIGN:
  				return false;
  
  			case IDENTITY:
  		}
  		throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
  	}
  
  	final boolean isAbsoluteDeep()
  	{
  		if (subUnits != null) {
  			for (UnitValue subUnit : subUnits) {
  				if (subUnit.isAbsoluteDeep())
  					return true;
  			}
  		}
  		return isAbsolute();
  	}
  
  	final boolean isLinked()
  	{
  		return linkId != null;
  	}
  
  	final boolean isLinkedDeep()
  	{
  		if (subUnits != null) {
  			for (UnitValue subUnit : subUnits) {
  				if (subUnit.isLinkedDeep())
  					return true;
  			}
  		}
  		return isLinked();
  	}
  
  	final String getLinkTargetId()
  	{
  		return linkId;
  	}
  
  	final UnitValue getSubUnitValue(int i)
  	{
  		return subUnits[i];
  	}
  
  	final int getSubUnitCount()
  	{
  		return subUnits != null ? subUnits.length : 0;
  	}
  
  	public final UnitValue[] getSubUnits()
  	{
  		return subUnits != null ? subUnits.clone() : null;
  	}
  
  	public final int getUnit()
  	{
  		return unit;
  	}
  
  	public final String getUnitString()
  	{
  		return unitStr;
  	}
  
  	public final int getOperation()
  	{
  		return oper;
  	}
  
  	public final float getValue()
  	{
  		return value;
  	}
  
  	public final boolean isHorizontal()
  	{
  		return isHor;
  	}
  
  	@Override
  	final public String toString()
  	{
  		return getClass().getName() + ". Value=" + value + ", unit=" + unit + ", unitString: " + unitStr + ", oper=" + oper + ", isHor: " + isHor;
  	}
  
  	/** Returns the creation string for this object. Note that {@link LayoutUtil#setDesignTime(ContainerWrapper, boolean)} must be
  	 * set to <code>true</code> for the creation strings to be stored.
  	 * @return The constraint string or <code>null</code> if none is registered.
  	 */
  	public final String getConstraintString()
  	{
  		return LayoutUtil.getCCString(this);
  	}
  
  	@Override
  	public final int hashCode()
  	{
  		return (int) (value * 12345) + (oper >>> 5) + unit >>> 17;
  	}
  
  	/** Adds a global unit converter that can convert from some <code>unit</code> to pixels.
  	 * <p>
  	 * This converter will be asked before the platform converter so the values for it (e.g. "related" and "unrelated")
  	 * can be overridden. It is however not possible to override the built in ones (e.g. "mm", "pixel" or "lp").
  	 * @param conv The converter. Not <code>null</code>.
  	 */
  	public synchronized static void addGlobalUnitConverter(UnitConverter conv)
  	{
  		if (conv == null)
  			throw new NullPointerException();
  		CONVERTERS.add(conv);
  	}
  
  	/** Removed the converter.
  	 * @param unit The converter.
  	 * @return If there was a converter found and thus removed.
  	 */
  	public synchronized static boolean removeGlobalUnitConverter(UnitConverter unit)
  	{
  		return CONVERTERS.remove(unit);
  	}
  
  	/** Returns the global converters currently registered. The platform converter will not be in this list.
  	 * @return The converters. Never <code>null</code>.
  	 */
  	public synchronized static UnitConverter[] getGlobalUnitConverters()
  	{
  		return CONVERTERS.toArray(new UnitConverter[CONVERTERS.size()]);
  	}
  
  	/** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
  	 * @return The current default unit.
  	 * @see #PIXEL
  	 * @see #LPX
  	 * @deprecated Use {@link PlatformDefaults#getDefaultHorizontalUnit()} and {@link PlatformDefaults#getDefaultVerticalUnit()} instead.
  	 */
  	public static int getDefaultUnit()
  	{
  		return PlatformDefaults.getDefaultHorizontalUnit();
  	}
  
  	/** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
  	 * @param unit The new default unit.
  	 * @see #PIXEL
  	 * @see #LPX
  	 * @deprecated Use {@link PlatformDefaults#setDefaultHorizontalUnit(int)} and {@link PlatformDefaults#setDefaultVerticalUnit(int)} instead.
  	 */
  	public static void setDefaultUnit(int unit)
  	{
  		PlatformDefaults.setDefaultHorizontalUnit(unit);
  		PlatformDefaults.setDefaultVerticalUnit(unit);
  	}
  
  	static {
          if(LayoutUtil.HAS_BEANS){
              LayoutUtil.setDelegate(UnitValue.class, new PersistenceDelegate() {
                  @Override
                  protected Expression instantiate(Object oldInstance, Encoder out)
                  {
                      UnitValue uv = (UnitValue) oldInstance;
                      String cs = uv.getConstraintString();
                      if (cs == null)
                          throw new IllegalStateException("Design time must be on to use XML persistence. See LayoutUtil.");
  
                      return new Expression(oldInstance, ConstraintParser.class, "parseUnitValueOrAlign", new Object[] {
                              uv.getConstraintString(), (uv.isHorizontal() ? Boolean.TRUE : Boolean.FALSE), null
                      });
                  }
              });
          }
  	}
  
  	// ************************************************
  	// Persistence Delegate and Serializable combined.
  	// ************************************************
  
  	private static final long serialVersionUID = 1L;
  
  	private Object readResolve() throws ObjectStreamException
  	{
  		return LayoutUtil.getSerializedObject(this);
  	}
  
  	private void writeObject(ObjectOutputStream out) throws IOException
  	{
  		if (getClass() == UnitValue.class)
  			LayoutUtil.writeAsXML(out, this);
  	}
  
  	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
  	{
  		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
  	}
  }
  