/*
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
* modifications by Nikolaus Moll
* 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
*/
package org.jowidgets.impl.layout.miglayout;
import java.beans.Encoder;
import java.beans.Expression;
import java.beans.PersistenceDelegate;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
final class UnitValueCommon implements Serializable {
private static final long serialVersionUID = 1L;
private static final float[] SCALE = new float[] {25.4f, 2.54f, 1f, 0f, 72f};
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 UnitValueCommon[] subUnits;
// Pixel
UnitValueCommon(final float value) // If hor/ver does not matter.
{
this(value, null, UnitValueToolkitCommon.PIXEL, true, UnitValueToolkitCommon.STATIC, null, null, value + "px");
}
UnitValueCommon(final float value, final int unit, final String createString) // If hor/ver does not matter.
{
this(value, null, unit, true, UnitValueToolkitCommon.STATIC, null, null, createString);
}
UnitValueCommon(final float value, final String unitStr, final boolean isHor, final int oper, final String createString) {
this(value, unitStr, -1, isHor, oper, null, null, createString);
}
UnitValueCommon(
final boolean isHor,
final int oper,
final UnitValueCommon sub1,
final UnitValueCommon sub2,
final String createString) {
this(0, "", -1, isHor, oper, sub1, sub2, createString);
if (sub1 == null || sub2 == null) {
throw new IllegalArgumentException("Sub units is null!");
}
}
UnitValueCommon(
final float value,
final String unitStr,
final int unit,
final boolean isHor,
final int oper,
final UnitValueCommon sub1,
final UnitValueCommon sub2,
final String createString) {
if (oper < UnitValueToolkitCommon.STATIC || oper > UnitValueToolkitCommon.MID) {
throw new IllegalArgumentException("Unknown Operation: " + oper);
}
if (oper >= UnitValueToolkitCommon.ADD && oper <= UnitValueToolkitCommon.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 UnitValueCommon[] {sub1, sub2} : null;
final LayoutUtilCommon layoutUtil = MigLayoutToolkitImpl.getMigLayoutUtil();
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 int getPixels(final float refValue, final IContainerWrapperCommon parent, final IComponentWrapperCommon comp) {
return Math.round(getPixelsExact(refValue, parent, comp));
}
/**
* 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 float getPixelsExact(final float refValue, final IContainerWrapperCommon parent, final IComponentWrapperCommon comp) {
if (parent == null) {
return 1;
}
if (oper == UnitValueToolkitCommon.STATIC) {
switch (unit) {
case UnitValueToolkitCommon.PIXEL:
return value;
case UnitValueToolkitCommon.LPX:
case UnitValueToolkitCommon.LPY:
return parent.getPixelUnitFactor(unit == UnitValueToolkitCommon.LPX) * value;
case UnitValueToolkitCommon.MM:
case UnitValueToolkitCommon.CM:
case UnitValueToolkitCommon.INCH:
case UnitValueToolkitCommon.PT:
float f = SCALE[unit - UnitValueToolkitCommon.MM];
final Float s = isHor
? MigLayoutToolkitImpl.getMigPlatformDefaults().getHorizontalScaleFactor()
: MigLayoutToolkitImpl.getMigPlatformDefaults().getVerticalScaleFactor();
if (s != null) {
f *= s;
}
return (isHor ? parent.getHorizontalScreenDPI() : parent.getVerticalScreenDPI()) * value / f;
case UnitValueToolkitCommon.PERCENT:
return value * refValue * 0.01f;
case UnitValueToolkitCommon.SPX:
case UnitValueToolkitCommon.SPY:
return (unit == UnitValueToolkitCommon.SPX ? parent.getScreenWidth() : parent.getScreenHeight())
* value
* 0.01f;
case UnitValueToolkitCommon.ALIGN:
final Integer st = MigLayoutToolkitImpl.getMigLinkHandler()
.getValue(parent.getLayout(), "visual", isHor ? LinkHandlerCommon.X : LinkHandlerCommon.Y);
final Integer sz = MigLayoutToolkitImpl.getMigLinkHandler()
.getValue(parent.getLayout(), "visual", isHor ? LinkHandlerCommon.WIDTH : LinkHandlerCommon.HEIGHT);
if (st == null || sz == null) {
return 0;
}
return value * (Math.max(0, sz) - refValue) + st;
case UnitValueToolkitCommon.MIN_SIZE:
if (comp == null) {
return 0;
}
return isHor ? comp.getMinimumWidth(comp.getHeight()) : comp.getMinimumHeight(comp.getWidth());
case UnitValueToolkitCommon.PREF_SIZE:
if (comp == null) {
return 0;
}
return isHor ? comp.getPreferredWidth(comp.getHeight()) : comp.getPreferredHeight(comp.getWidth());
case UnitValueToolkitCommon.MAX_SIZE:
if (comp == null) {
return 0;
}
return isHor ? comp.getMaximumWidth(comp.getHeight()) : comp.getMaximumHeight(comp.getWidth());
case UnitValueToolkitCommon.BUTTON:
return MigLayoutToolkitImpl.getMigPlatformDefaults()
.getMinimumButtonWidth()
.getPixels(refValue, parent, comp);
case UnitValueToolkitCommon.LINK_X:
case UnitValueToolkitCommon.LINK_Y:
case UnitValueToolkitCommon.LINK_W:
case UnitValueToolkitCommon.LINK_H:
case UnitValueToolkitCommon.LINK_X2:
case UnitValueToolkitCommon.LINK_Y2:
case UnitValueToolkitCommon.LINK_XPOS:
case UnitValueToolkitCommon.LINK_YPOS:
final Integer v = MigLayoutToolkitImpl.getMigLinkHandler()
.getValue(parent.getLayout(), getLinkTargetId(), unit
- (unit >= UnitValueToolkitCommon.LINK_XPOS
? UnitValueToolkitCommon.LINK_XPOS : UnitValueToolkitCommon.LINK_X));
if (v == null) {
return 0;
}
if (unit == UnitValueToolkitCommon.LINK_XPOS) {
return parent.getScreenLocationX() + v;
}
if (unit == UnitValueToolkitCommon.LINK_YPOS) {
return parent.getScreenLocationY() + v;
}
return v;
case UnitValueToolkitCommon.LOOKUP:
final float res = lookup(refValue, parent, comp);
if (res != UnitConverterCommon.UNABLE) {
return res;
}
//CHECKSTYLE:OFF
//Fall through is not possible because unit may not be LOOKUP and LABEL_ALIGN at the same time
case UnitValueToolkitCommon.LABEL_ALIGN:
return MigLayoutToolkitImpl.getMigPlatformDefaults().getLabelAlignPercentage() * refValue;
//CHECKSTYLE:ON
case UnitValueToolkitCommon.IDENTITY:
default:
break;
}
throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
}
if (subUnits != null && subUnits.length == 2) {
final float r1 = subUnits[0].getPixelsExact(refValue, parent, comp);
final float r2 = subUnits[1].getPixelsExact(refValue, parent, comp);
switch (oper) {
case UnitValueToolkitCommon.ADD:
return r1 + r2;
case UnitValueToolkitCommon.SUB:
return r1 - r2;
case UnitValueToolkitCommon.MUL:
return r1 * r2;
case UnitValueToolkitCommon.DIV:
return r1 / r2;
case UnitValueToolkitCommon.MIN:
return r1 < r2 ? r1 : r2;
case UnitValueToolkitCommon.MAX:
return r1 > r2 ? r1 : r2;
case UnitValueToolkitCommon.MID:
return (r1 + r2) * 0.5f;
default:
break;
}
}
throw new IllegalArgumentException("Internal: Unknown Oper: " + oper);
}
private float lookup(final float refValue, final IContainerWrapperCommon parent, final IComponentWrapperCommon comp) {
float res = UnitConverterCommon.UNABLE;
final ArrayList<UnitConverterCommon> converters = MigLayoutToolkitImpl.getMigUnitValueToolkit().getConverters();
for (int i = converters.size() - 1; i >= 0; i--) {
res = converters.get(i).convertToPixels(value, unitStr, isHor, refValue, parent, comp);
if (res != UnitConverterCommon.UNABLE) {
return res;
}
}
return MigLayoutToolkitImpl.getMigPlatformDefaults().convertToPixels(value, unitStr, isHor, refValue, parent, comp);
}
private int parseUnitString() {
final int len = unitStr.length();
if (len == 0) {
return isHor
? MigLayoutToolkitImpl.getMigPlatformDefaults().getDefaultHorizontalUnit()
: MigLayoutToolkitImpl.getMigPlatformDefaults().getDefaultVerticalUnit();
}
final Integer u = MigLayoutToolkitImpl.getMigUnitValueToolkit().getUnitMap().get(unitStr);
if (u != null) {
return u;
}
if (unitStr.equals("lp")) {
return isHor ? UnitValueToolkitCommon.LPX : UnitValueToolkitCommon.LPY;
}
if (unitStr.equals("sp")) {
return isHor ? UnitValueToolkitCommon.SPX : UnitValueToolkitCommon.SPY;
}
if (lookup(0, null, null) != UnitConverterCommon.UNABLE) {
return UnitValueToolkitCommon.LOOKUP;
}
// Only link left. E.g. "otherID.width"
final int pIx = unitStr.indexOf('.');
if (pIx != -1) {
linkId = unitStr.substring(0, pIx);
final String e = unitStr.substring(pIx + 1);
if (e.equals("x")) {
return UnitValueToolkitCommon.LINK_X;
}
if (e.equals("y")) {
return UnitValueToolkitCommon.LINK_Y;
}
if (e.equals("w") || e.equals("width")) {
return UnitValueToolkitCommon.LINK_W;
}
if (e.equals("h") || e.equals("height")) {
return UnitValueToolkitCommon.LINK_H;
}
if (e.equals("x2")) {
return UnitValueToolkitCommon.LINK_X2;
}
if (e.equals("y2")) {
return UnitValueToolkitCommon.LINK_Y2;
}
if (e.equals("xpos")) {
return UnitValueToolkitCommon.LINK_XPOS;
}
if (e.equals("ypos")) {
return UnitValueToolkitCommon.LINK_YPOS;
}
}
throw new IllegalArgumentException("Unknown keyword: " + unitStr);
}
boolean isLinked() {
return linkId != null;
}
boolean isLinkedDeep() {
if (subUnits == null) {
return linkId != null;
}
for (final UnitValueCommon subUnit : subUnits) {
if (subUnit.isLinkedDeep()) {
return true;
}
}
return false;
}
String getLinkTargetId() {
return linkId;
}
UnitValueCommon getSubUnitValue(final int i) {
return subUnits[i];
}
int getSubUnitCount() {
return subUnits != null ? subUnits.length : 0;
}
UnitValueCommon[] getSubUnits() {
return subUnits != null ? subUnits.clone() : null;
}
int getUnit() {
return unit;
}
String getUnitString() {
return unitStr;
}
int getOperation() {
return oper;
}
float getValue() {
return value;
}
boolean isHorizontal() {
return isHor;
}
@Override
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 LayoutUtilCommon#setDesignTime(IContainerWrapperCommon, 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 String getConstraintString() {
final LayoutUtilCommon layoutUtil = MigLayoutToolkitImpl.getMigLayoutUtil();
return layoutUtil.getCCString(this);
}
@Override
public int hashCode() {
return (int) (value * 12345) + (oper >>> 5) + unit >>> 17;
}
@Override
public boolean equals(final Object obj) {
return super.equals(obj);
}
static {
final LayoutUtilCommon layoutUtil = MigLayoutToolkitImpl.getMigLayoutUtil();
if (layoutUtil.hasBeans()) {
layoutUtil.setDelegate(UnitValueCommon.class, new PersistenceDelegate() {
@Override
protected Expression instantiate(final Object oldInstance, final Encoder out) {
final UnitValueCommon uv = (UnitValueCommon) oldInstance;
final 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,
ConstraintParserCommon.class,
"parseUnitValueOrAlign",
new Object[] {uv.getConstraintString(), (uv.isHorizontal() ? Boolean.TRUE : Boolean.FALSE), null});
}
});
}
}
// ************************************************
// Persistence Delegate and Serializable combined.
// ************************************************
private Object readResolve() throws ObjectStreamException {
return MigLayoutToolkitImpl.getMigLayoutUtil().getSerializedObject(this);
}
private void writeObject(final ObjectOutputStream out) throws IOException {
if (getClass() == UnitValueCommon.class) {
MigLayoutToolkitImpl.getMigLayoutUtil().writeAsXML(out, this);
}
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
final LayoutUtilCommon layoutUtil = MigLayoutToolkitImpl.getMigLayoutUtil();
layoutUtil.setSerializedObject(this, layoutUtil.readAsXML(in));
}
}