JAL-3032 adds Java 8 functionality (2/2)
[jalview.git] / src2 / net / miginfocom / layout / UnitValue.java
diff --git a/src2/net/miginfocom/layout/UnitValue.java b/src2/net/miginfocom/layout/UnitValue.java
new file mode 100644 (file)
index 0000000..70d8f72
--- /dev/null
@@ -0,0 +1,691 @@
+package 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));
+       }
+}