Index: fop
===================================================================
--- fop	(revision 496642)
+++ fop	(working copy)
@@ -243,6 +243,11 @@
 
 # Execute FOP using eval/exec to preserve spaces in paths,
 # java options, and FOP args
+
+# Add the following options for extra debug:
+# JMP: -Xrunjmp
+# Hat: -Xrunhprof:file=dump.hprof,format=b
+
 fop_exec_command="exec \"$JAVACMD\" $LOGCHOICE $LOGLEVEL -classpath \"$LOCALCLASSPATH\" $FOP_OPTS org.apache.fop.cli.Main $fop_exec_args"
 if $fop_exec_debug ; then
     echo $fop_exec_command
Index: src/java/org/apache/fop/render/txt/TXTHandler.java
===================================================================
--- src/java/org/apache/fop/render/txt/TXTHandler.java	(revision 496642)
+++ src/java/org/apache/fop/render/txt/TXTHandler.java	(working copy)
@@ -107,7 +107,7 @@
      * @param value  new integer value
      */
     private static void setLength(CompoundDatatype cd, int value) {
-        cd.setComponent(Constants.CP_LENGTH, new FixedLength(value), true);
+        cd.setComponent(Constants.CP_LENGTH, FixedLength.getInstance(value), true);
     }
 
     /**
@@ -193,7 +193,7 @@
      */
     private void modifySpace(SpaceProperty space, int q) {
         int value = space.getOptimum(null).getLength().getValue();
-        setMinOptMax(space, new FixedLength(Helper.round(value, q)));
+        setMinOptMax(space, FixedLength.getInstance(Helper.round(value, q)));
     }
 
     /**
@@ -204,7 +204,7 @@
      */
     private Length roundLength(Length length, int q) {
         int x = Helper.round(length.getValue(), q);
-        return new FixedLength(x);
+        return FixedLength.getInstance(x);
     }
 
     /**
@@ -215,7 +215,7 @@
      */
     private Length ceilLength(Length length, int q) {
         int x = Helper.ceil(length.getValue(), q);
-        return new FixedLength(x);
+        return FixedLength.getInstance(x);
     }
 
     /**
@@ -233,7 +233,7 @@
         }
         int newValue = indent.getValue() + overPatching[side];
         newValue = Helper.round(newValue, quantum[side]);
-        return new FixedLength(newValue);
+        return FixedLength.getInstance(newValue);
     }
 
     /**
@@ -247,23 +247,25 @@
      * @param cmb instance of CommonMarginBlock to modify.
      */
     private void modifyCommonMarginBlock(CommonMarginBlock cmb) {
-        cmb.marginTop = roundLength(cmb.marginTop, TXTRenderer.CHAR_HEIGHT);
-        cmb.marginBottom = roundLength(cmb.marginBottom,
-                TXTRenderer.CHAR_HEIGHT);
-        cmb.marginLeft = roundLength(cmb.marginLeft, TXTRenderer.CHAR_WIDTH);
-        cmb.marginRight = roundLength(cmb.marginRight, TXTRenderer.CHAR_WIDTH);
+      // TODO: Fix this, broken when CommonMarginBlock made immutable
 
-        modifySpace(cmb.spaceBefore, TXTRenderer.CHAR_HEIGHT);
-        modifySpace(cmb.spaceAfter, TXTRenderer.CHAR_HEIGHT);
+//         cmb.marginTop = roundLength(cmb.marginTop, TXTRenderer.CHAR_HEIGHT);
+//         cmb.marginBottom = roundLength(cmb.marginBottom,
+//                 TXTRenderer.CHAR_HEIGHT);
+//         cmb.marginLeft = roundLength(cmb.marginLeft, TXTRenderer.CHAR_WIDTH);
+//         cmb.marginRight = roundLength(cmb.marginRight, TXTRenderer.CHAR_WIDTH);
 
-        if (!(cmb.startIndent instanceof RelativeNumericProperty)) {
-            cmb.startIndent = modifyIndent(cmb.startIndent,
-                    CommonBorderPaddingBackground.START);
-        }
-        if (!(cmb.endIndent instanceof RelativeNumericProperty)) {
-            cmb.endIndent = modifyIndent(cmb.endIndent,
-                    CommonBorderPaddingBackground.END);
-        }
+//         modifySpace(cmb.spaceBefore, TXTRenderer.CHAR_HEIGHT);
+//         modifySpace(cmb.spaceAfter, TXTRenderer.CHAR_HEIGHT);
+
+//         if (!(cmb.startIndent instanceof RelativeNumericProperty)) {
+//             cmb.startIndent = modifyIndent(cmb.startIndent,
+//                     CommonBorderPaddingBackground.START);
+//         }
+//         if (!(cmb.endIndent instanceof RelativeNumericProperty)) {
+//             cmb.endIndent = modifyIndent(cmb.endIndent,
+//                     CommonBorderPaddingBackground.END);
+//         }
     }
 
     /**
@@ -283,7 +285,7 @@
             // impove interaction with other FO.
             int value = cmb.spaceAfter.getOptimum(null).getLength().getValue();
             value += TXTRenderer.CHAR_HEIGHT;
-            setMinOptMax(cmb.spaceAfter, new FixedLength(value));
+            setMinOptMax(cmb.spaceAfter, FixedLength.getInstance(value));
         }
         modifyCommonMarginBlock(cmb);
 
@@ -352,10 +354,11 @@
      * @param cap CommonAbsolutePosition to modify.
      */
     private void modifyCommonAbsolutePosition(CommonAbsolutePosition cap) {
-        if (cap.absolutePosition == Constants.EN_ABSOLUTE) {
-            cap.left = roundLength(cap.left, TXTRenderer.CHAR_WIDTH);
-            cap.top = roundLength(cap.top, TXTRenderer.CHAR_HEIGHT);
-        }
+      // TODO: Broken since CommonAbsolutePosition made immutable
+//         if (cap.absolutePosition == Constants.EN_ABSOLUTE) {
+//             cap.left = roundLength(cap.left, TXTRenderer.CHAR_WIDTH);
+//             cap.top = roundLength(cap.top, TXTRenderer.CHAR_HEIGHT);
+//         }
     }
 
     /**
@@ -370,7 +373,7 @@
 
         int height = TXTRenderer.CHAR_HEIGHT;
         int newValue = Math.max(Helper.floor(value, height), height);
-        setMinOptMax(lineHeight, new FixedLength(newValue));
+        setMinOptMax(lineHeight, FixedLength.getInstance(newValue));
     }
 
     /**
@@ -385,12 +388,13 @@
      * @param cf the font to modify.
      */
     private void modifyCommonFont(CommonFont cf) {
-        if (cf != null) {
-            cf.overrideFontFamily("Courier");
-            cf.fontSize = new FixedLength(MODIFIED_FONT_SIZE);
-            cf.fontStretch = Constants.EN_NORMAL;
-            cf.fontWeight = Constants.EN_NORMAL;
-        }
+      // TODO: Broken since CommonFont made immutable
+//         if (cf != null) {
+//             cf.overrideFontFamily("Courier");
+//             cf.fontSize = new FixedLength(MODIFIED_FONT_SIZE);
+//             cf.fontStretch = Constants.EN_NORMAL;
+//             cf.fontWeight = Constants.EN_NORMAL;
+//         }
     }
 
     /**
Index: src/java/org/apache/fop/layoutmgr/table/GridUnit.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/table/GridUnit.java	(revision 496642)
+++ src/java/org/apache/fop/layoutmgr/table/GridUnit.java	(working copy)
@@ -265,14 +265,8 @@
     public void resolveBorder(GridUnit other, int side, int resFlags) {
         CollapsingBorderModel borderModel = CollapsingBorderModel.getBorderModelFor(
                 getTable().getBorderCollapse());
-        if (effectiveBorders == null) {
-            effectiveBorders = new CommonBorderPaddingBackground();
-        }
-        effectiveBorders.setBorderInfo(borderModel.determineWinner(this, other,
-                        side, resFlags), side);
-        if (cell != null) {
-            effectiveBorders.setPadding(cell.getCommonBorderPaddingBackground());
-        }
+        BorderInfo bi = borderModel.determineWinner(this, other, side, resFlags);
+        effectiveBorders = CommonBorderPaddingBackground.getInstance(effectiveBorders, cell, bi, side);
     }
 
     /**
Index: src/java/org/apache/fop/datatypes/CompoundDatatype.java
===================================================================
--- src/java/org/apache/fop/datatypes/CompoundDatatype.java	(revision 496642)
+++ src/java/org/apache/fop/datatypes/CompoundDatatype.java	(working copy)
@@ -39,6 +39,7 @@
      * Returns a component of the compound datatype.
      * @param cmpId ID of the component
      * @return the value of the component
+     * @deprecated Fop is moving towards a system of immutable properties.
      */
     Property getComponent(int cmpId);
 }
Index: src/java/org/apache/fop/datatypes/LengthBase.java
===================================================================
--- src/java/org/apache/fop/datatypes/LengthBase.java	(revision 496642)
+++ src/java/org/apache/fop/datatypes/LengthBase.java	(working copy)
@@ -28,7 +28,7 @@
 import org.apache.fop.fo.flow.Character;
 import org.apache.fop.fo.flow.ExternalGraphic;
 import org.apache.fop.fo.flow.InstreamForeignObject;
-import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager;
+import org.apache.fop.fo.properties.Property;
 
 /**
  * Models a length which can be used as a factor in a percentage length
@@ -71,15 +71,15 @@
     /**
      * The FO for which this property is to be calculated.
      */
-    protected /* final */ FObj fobj;
+    private final FObj fobj;
 
     /**
      * One of the defined types of LengthBase
      */
-    private /* final */ int baseType;
+    private final int baseType;
 
     /** For percentages based on other length properties */
-    private Length baseLength;
+    private final Length baseLength;
     
     /**
      * Constructor
@@ -101,6 +101,13 @@
         default:
             // TODO: pacify CheckStyle
             // throw new RuntimeException();
+            /**
+               This is the behaviour of the old code - probably not the desired
+               behaviour. Throwing IllegalArgumentException causes all sorts of
+               problems, including failure to process simple fos such as readme.fo
+               Not initializing anything gives null - RSW
+            */
+            baseLength = null;
             break;
         }
     }
@@ -134,6 +141,19 @@
         }
         return baseLen;
     }
+    
+    public int hashCode() {
+        return baseType + (baseLength==null ? 29 : baseLength.hashCode()) << 4;
+    }
+    
+    public boolean equals(Object obj) {
+        if (! (obj instanceof LengthBase))
+            return false;
+        LengthBase lb = (LengthBase)obj;
+        return lb.baseType == this.baseType &&
+            Property.eq(lb.baseLength, this.baseLength);
+    }
 
+
 }
 
Index: src/java/org/apache/fop/apps/FOUserAgent.java
===================================================================
--- src/java/org/apache/fop/apps/FOUserAgent.java	(revision 496642)
+++ src/java/org/apache/fop/apps/FOUserAgent.java	(working copy)
@@ -84,6 +84,7 @@
     private File outputFile = null;
     private Renderer rendererOverride = null;
     private FOEventHandler foEventHandlerOverride = null;
+    private boolean locatorEnabled = false; // TODO: Should be true by default (for error messages).
     
     /** Producer:  Metadata element for the system/software that produces
      * the document. (Some renderers can store this in the document.)
@@ -542,5 +543,24 @@
         return getFactory().getXMLHandlerRegistry();
     }
 
+    /**
+     * Enables use of SAXLocators to provide location information in error
+     * messages. Since this a somewhat expensive feature in terms of memory
+     * usage, many end user may which to disable it.
+     * @return true if context information should be stored on each node in the FO tree.
+     */
+    public void setLocatorEnabled(boolean en) {
+        locatorEnabled = en;
+    }
+
+    /**
+     * Are SAXLocators enabled?
+     * @see #setLocatorEnabled(boolean)
+     * @return true if context information should be stored on each node in the FO tree.
+     */
+    public boolean isLocatorEnabled() {
+        return locatorEnabled;
+    }
+
 }
 
Index: src/java/org/apache/fop/fo/properties/CommonHyphenation.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonHyphenation.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonHyphenation.java	(working copy)
@@ -29,46 +29,49 @@
  * Public "structure" allows direct member access.
  */
 public class CommonHyphenation {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "language" property.
      */
-    public String language;
+    public final String language;
 
     /**
      * The "country" property.
      */
-    public String country;
+    public final String country;
 
     /**
      * The "script" property.
      */
-    public String script;
+    public final String script;
 
     /**
      * The "hyphenate" property.
      */
-    public int hyphenate;
+    public final int hyphenate;
 
     /**
      * The "hyphenation-character" property.
      */
-    public char hyphenationCharacter;
+    public final char hyphenationCharacter;
 
     /**
      * The "hyphenation-push-character" property.
      */
-    public int hyphenationPushCharacterCount;
+    public final int hyphenationPushCharacterCount;
 
     /**
      * The "hyphenation-remain-character-count" property.
      */
-    public int hyphenationRemainCharacterCount;
+    public final int hyphenationRemainCharacterCount;
 
     /**
      * Create a CommonHyphenation object.
-     * @param pList The PropertyList with propery values.
+     * @param pList The PropertyList with property values.
      */
-    public CommonHyphenation(PropertyList pList) throws PropertyException {
+    private CommonHyphenation(PropertyList pList) throws PropertyException {
         language = pList.get(Constants.PR_LANGUAGE).getString();
         country = pList.get(Constants.PR_COUNTRY).getString();
         script = pList.get(Constants.PR_SCRIPT).getString();
@@ -81,4 +84,26 @@
 
     }
 
+    public int hashCode() {
+      return Property.hc(language) << 1 +
+        Property.hc(country) << 4 +
+        Property.hc(script) << 9 +
+        hyphenate;
+    }
+
+    public boolean equals(Object obj) {
+      CommonHyphenation c = (CommonHyphenation)obj;
+      return Property.eq(language, language) &&
+        Property.eq(country, country) &&
+        Property.eq(script, script) &&
+        hyphenate == c.hyphenate &&
+        hyphenationCharacter == c.hyphenationCharacter &&
+        hyphenationPushCharacterCount == c.hyphenationPushCharacterCount &&
+        hyphenationRemainCharacterCount == c.hyphenationRemainCharacterCount;
+    }
+
+    public static CommonHyphenation getInstance(PropertyList pList) throws PropertyException {
+        CommonHyphenation c = new CommonHyphenation(pList);
+        return (CommonHyphenation)cache.fetch(c);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/CommonAccessibility.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonAccessibility.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonAccessibility.java	(working copy)
@@ -28,32 +28,44 @@
  * See Sec 7.4 of the XSL-FO Standard.
  * Public "structure" allows direct member access.
  */
-public class CommonAccessibility {
+public final class CommonAccessibility {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "source-doc" property.
      */
-    public String sourceDoc = null;
+    public final String sourceDoc;
 
     /**
      * The "role" property.
      */
-    public String role = null;
+    public final String role;
 
     /**
      * Create a CommonAbsolutePosition object.
      * @param pList The PropertyList with propery values.
      */
-    public CommonAccessibility(PropertyList pList) throws PropertyException {
-        sourceDoc = pList.get(Constants.PR_SOURCE_DOCUMENT).getString();
-        if ("none".equals(sourceDoc)) {
-            sourceDoc = null;
-        }
-        role = pList.get(Constants.PR_ROLE).getString();
-        if ("none".equals(role)) {
-            role = null;
-        }
-        
+    private CommonAccessibility(PropertyList pList) throws PropertyException {
+        String s = pList.get(Constants.PR_SOURCE_DOCUMENT).getString();
+        sourceDoc = ("none".equals(s)) ? null : s;
+        String r = pList.get(Constants.PR_ROLE).getString();
+        role = ("none".equals(r)) ? null : r;
     }
 
+    public int hashCode() {
+        return sourceDoc == null ? 0 : sourceDoc.hashCode() +
+            role == null ? 0 : role.hashCode() << 1;
+    }
 
+    public boolean equals(Object obj) {
+        CommonAccessibility ca = (CommonAccessibility)obj;        
+        return Property.eq(ca.sourceDoc, this.sourceDoc) &&
+            Property.eq(ca.role, this.role);
+    }
+
+    public static CommonAccessibility getInstance(PropertyList pList) throws PropertyException {
+        CommonAccessibility ca = new CommonAccessibility(pList);
+        return (CommonAccessibility)cache.fetch(ca);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java
===================================================================
--- src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/BorderWidthPropertyMaker.java	(working copy)
@@ -63,7 +63,7 @@
         // Calculate the values as described in 7.7.20.
         Property style = propertyList.get(borderStyleId);
         if (style.getEnum() == Constants.EN_NONE) {
-            return new FixedLength(0);
+            return FixedLength.getInstance(0);
         }
         return p;
     }
Index: src/java/org/apache/fop/fo/properties/LengthRangeProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/LengthRangeProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/LengthRangeProperty.java	(working copy)
@@ -84,7 +84,7 @@
                         log.warn(FObj.decorateWithContextInfo(
                                 "Replaced negative value (" + len + ") for " + getName()
                                 + " with 0mpt", fo));
-                        p = new FixedLength(0);
+                        p = FixedLength.getInstance(0);
                     }
                 }
             }
@@ -106,8 +106,7 @@
                     if (isNegativeLength(len)) {
                         log.warn("Replaced negative value (" + len + ") for " + getName()
                                 + " with 0mpt");
-                        val.setComponent(subpropertyId,
-                                new FixedLength(0), false);
+                        val.setComponent(subpropertyId, FixedLength.getInstance(0), false);
                         return baseProperty;
                     }
                 }
@@ -136,6 +135,7 @@
 
     /**
      * @see org.apache.fop.datatypes.CompoundDatatype#getComponent(int)
+     * @deprecated LengthRangeProperty should be immutable
      */
     public Property getComponent(int cmpId) {
         if (cmpId == CP_MINIMUM) {
@@ -155,6 +155,7 @@
      * LengthRange.
      * @param bIsDefault If true, this is set as a "default" value
      * and not a user-specified explicit value.
+     * @deprecated LengthRangeProperty should be immutable
      */
     protected void setMinimum(Property minimum, boolean bIsDefault) {
         this.minimum = minimum;
@@ -170,6 +171,7 @@
      * @param max A Length value specifying the maximum value for this
      * @param bIsDefault If true, this is set as a "default" value
      * and not a user-specified explicit value.
+     * @deprecated LengthRangeProperty should be immutable
      */
     protected void setMaximum(Property max, boolean bIsDefault) {
         maximum = max;
@@ -185,6 +187,7 @@
      * @param opt A Length value specifying the optimum value for this
      * @param bIsDefault If true, this is set as a "default" value
      * and not a user-specified explicit value.
+     * @deprecated LengthRangeProperty should be immutable
      */
     protected void setOptimum(Property opt, boolean bIsDefault) {
         optimum = opt;
@@ -298,5 +301,21 @@
     public Object getObject() {
         return this;
     }
-
+    
+    public int hashCode() {
+        return bfSet + minimum.hashCode() << 4 +
+            optimum.hashCode() << 8 + maximum.hashCode() << 12;
+    }
+    
+    public boolean equals(Object obj) {
+        if (! (obj instanceof LengthRangeProperty))
+            return false;
+        LengthRangeProperty lrp = (LengthRangeProperty)obj;
+        return this.bfSet == lrp.bfSet &&
+            this.consistent == lrp.consistent &&
+            Property.eq(this.minimum, lrp.minimum) &&
+            Property.eq(this.optimum, lrp.optimum) &&
+            Property.eq(this.maximum, lrp.maximum);
+    }
+    
 }
Index: src/java/org/apache/fop/fo/properties/EnumProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/EnumProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/EnumProperty.java	(working copy)
@@ -65,7 +65,7 @@
         }
     }
 
-    private static final Map propertyCache = new WeakHashMap();
+    private static final PropertyCache cache = new PropertyCache();
 
     private final int value;
     private final String text;
@@ -80,14 +80,7 @@
     }
 
     public static EnumProperty getInstance(int explicitValue, String text) {
-        EnumProperty ep = new EnumProperty(explicitValue, text);
-        EnumProperty cacheEntry = (EnumProperty)propertyCache.get(ep);
-        if (cacheEntry == null) {
-            propertyCache.put(ep, ep);
-            return ep;
-        } else {
-            return cacheEntry;
-        }
+        return (EnumProperty)cache.fetch(new EnumProperty(explicitValue, text));
     }
 
     /**
@@ -110,10 +103,8 @@
     public boolean equals(Object obj) {
         if (obj instanceof EnumProperty) {
             EnumProperty ep = (EnumProperty)obj;
-            return (ep.value == this.value) 
-                && ((ep.text == this.text)
-                    || (ep.text != null
-                        && ep.text.equals(this.text)));
+            return this.value == ep.value &&
+              Property.eq(this.text, ep.text);
         } else {
             return false;
         }
Index: src/java/org/apache/fop/fo/properties/CommonAbsolutePosition.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonAbsolutePosition.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonAbsolutePosition.java	(working copy)
@@ -30,30 +30,33 @@
  * Public "structure" allows direct member access.
  */
 public class CommonAbsolutePosition {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "absolute-position" property.
      */
-    public int absolutePosition;
+    public final int absolutePosition;
 
     /**
      * The "top" property.
      */
-    public Length top;
+    public final Length top;
 
     /**
      * The "right" property.
      */
-    public Length right;
+    public final Length right;
     
     /**
      * The "bottom" property.
      */
-    public Length bottom;
+    public final Length bottom;
     
     /**
      * The "left" property.
      */
-    public Length left;
+    public final Length left;
 
     /**
      * Create a CommonAbsolutePosition object.
@@ -82,4 +85,27 @@
         sb.append("}");
         return sb.toString();
     }
+
+    public int hashCode() {
+      return absolutePosition + 
+        Property.hc(top) << 2 +
+        Property.hc(right) << 4 +
+        Property.hc(bottom) << 6 +
+        Property.hc(left) << 8;
+    }
+
+    public boolean equals(Object obj) {
+        CommonAbsolutePosition cap = (CommonAbsolutePosition)obj;        
+        return cap.absolutePosition == absolutePosition &&
+          Property.eq(cap.top, top) &&
+          Property.eq(cap.right, right) &&
+          Property.eq(cap.bottom, bottom) &&
+          Property.eq(cap.left, left);
+    }
+
+    public static CommonAbsolutePosition getInstance(PropertyList pList) throws PropertyException {
+        CommonAbsolutePosition cap = new CommonAbsolutePosition(pList);
+        return (CommonAbsolutePosition)cache.fetch(cap);
+    }
+
 }
Index: src/java/org/apache/fop/fo/properties/EnumNumber.java
===================================================================
--- src/java/org/apache/fop/fo/properties/EnumNumber.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/EnumNumber.java	(working copy)
@@ -27,7 +27,7 @@
  */
 public class EnumNumber extends NumberProperty {
 
-    private static final Map cache = new WeakHashMap();
+    private static final PropertyCache cache = new PropertyCache();
 
     private final EnumProperty enumProperty;
     
@@ -37,12 +37,8 @@
     }
 
     public static EnumNumber getInstance(Property enumProperty) {
-        EnumNumber en = (EnumNumber)cache.get(enumProperty);
-        if (en == null) {
-            en = new EnumNumber((EnumProperty)enumProperty);
-            cache.put(enumProperty, en);
-        }
-        return en;
+        EnumNumber en = new EnumNumber((EnumProperty)enumProperty);
+        return (EnumNumber)cache.fetch(en);
     }
 
     public int getEnum() {
@@ -81,5 +77,19 @@
         return enumProperty.getObject();
     }
 
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof EnumNumber) {
+            EnumNumber ep = (EnumNumber)obj;
+            return Property.eq(this.enumProperty, ep.enumProperty);
+        } else {
+            return false;
+        }
+    }
 
+    public int hashCode() {
+        return enumProperty.hashCode();
+    }
 }
Index: src/java/org/apache/fop/fo/properties/Property.java
===================================================================
--- src/java/org/apache/fop/fo/properties/Property.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/Property.java	(working copy)
@@ -199,4 +199,31 @@
         }
         return null;
     }
+
+    /**
+       Utility method which returns true iff obj1.equals(obj2)
+       or obj1 and obj2 are both null. Performs a deep equality
+       comparison if obj1 and obj2 are both arrays.
+    */
+    public static final boolean eq(Object obj1, Object obj2) {
+        if (obj1 instanceof Object[]) {
+            if (obj1==null && obj2==null) return true;
+            if (obj1==null || obj2==null) return false;
+            if (! (obj1 instanceof Object[])) return false;
+            Object[] arr1 = (Object[])obj1;
+            Object[] arr2 = (Object[])obj2;
+            if (arr1.length != arr2.length) return false;
+            for (int i = 0; i < arr1.length; i++)
+                if (!eq(arr1[i], arr2[i]))
+                    return false;
+            return true;
+        } else {
+            return (obj1 == obj2) ||
+                (obj1 != null && obj2 != null && obj1.equals(obj2));
+        }
+    }
+
+    static final int hc(Object obj) {
+        return obj == null ? 0 : obj.hashCode();
+    }
 }
Index: src/java/org/apache/fop/fo/properties/CondLengthProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CondLengthProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CondLengthProperty.java	(working copy)
@@ -162,4 +162,14 @@
         return this;
     }
 
+    public int hashCode() {
+        return length.hashCode() + conditionality.hashCode();
+    }
+
+    public boolean equals(Object obj) {
+        if (! (obj instanceof CondLengthProperty)) return false;
+        CondLengthProperty c = (CondLengthProperty)obj;
+        return Property.eq(length, c.length) &&
+            Property.eq(conditionality, c.conditionality);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/NumberProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/NumberProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/NumberProperty.java	(working copy)
@@ -69,7 +69,7 @@
 
     }
 
-    private Number number;
+    private final Number number;
 
     /**
      * Constructor for Number input
@@ -172,7 +172,7 @@
     /** @see org.apache.fop.fo.properties.Property#getLength() */
     public Length getLength() {
         //Assume pixels (like in HTML) when there's no unit
-        return new FixedLength(getNumericValue(), "px");
+        return FixedLength.getInstance(getNumericValue(), "px");
     }
 
     /**
@@ -187,4 +187,14 @@
         return Color.black;
     }
 
+    public int hashCode() {
+        return number.hashCode();
+    }
+
+    public boolean equals(Object obj) {
+        if (! (obj instanceof NumberProperty))
+            return false;
+        NumberProperty n = (NumberProperty)obj;
+        return Property.eq(number, n.number);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/SpaceProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/SpaceProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/SpaceProperty.java	(working copy)
@@ -167,4 +167,13 @@
         return this;
     }
 
+    public boolean equals(Object obj) {
+        if (! (obj instanceof SpaceProperty))
+          return false;
+        SpaceProperty c = (SpaceProperty)obj;
+        return super.equals(obj) &&
+          Property.eq(precedence, c.precedence) &&
+          Property.eq(conditionality, c.conditionality);
+    }
+
 }
Index: src/java/org/apache/fop/fo/properties/FixedLength.java
===================================================================
--- src/java/org/apache/fop/fo/properties/FixedLength.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/FixedLength.java	(working copy)
@@ -25,31 +25,45 @@
  * An absolute length quantity in XSL
  */
 public class FixedLength extends LengthProperty {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     private int millipoints;
 
+    private FixedLength(int millis) {
+        millipoints = millis;
+    }
+
     /**
-     * Set the length given
+     * Obtain an instance given a number of units and a font size.
      * @param numRelUnits the number of relative units
      * @param iCurFontSize the current font size in base units.
+     * @return A new or previously cached instance of FixedLength
      */
-    public FixedLength(double numRelUnits, int iCurFontSize) {
-        millipoints = (int) (numRelUnits * (double)iCurFontSize);
+    public static FixedLength getInstance(double numRelUnits, int iCurFontSize) {
+        FixedLength fl = new FixedLength((int) (numRelUnits * (double)iCurFontSize));
+        return (FixedLength)cache.fetch(fl);
     }
 
     /**
-     * Set the length given a number of units and a unit name.
+     * Obtain an instance given a number of units and a unit name.
      * @param numUnits quantity of input units
      * @param units input unit specifier (in, cm, etc.)
+     * @return A new or previously cached instance of FixedLength
      */
-    public FixedLength(double numUnits, String units) {
-        convert(numUnits, units);
+    public static FixedLength getInstance(double numUnits, String units) {
+        FixedLength fl = new FixedLength(convert(numUnits, units));
+        return (FixedLength)cache.fetch(fl);
     }
 
     /**
+     * Obtain an instance given a number of units.
      * @param baseUnits the length as a number of base units (millipoints)
+     * @return A new or previously cached instance of FixedLength
      */
-    public FixedLength(int baseUnits) {
-        millipoints = baseUnits;
+    public static FixedLength getInstance(int baseUnits) {
+        FixedLength fl = new FixedLength(baseUnits);
+        return (FixedLength)cache.fetch(fl);
     }
 
     /**
@@ -58,7 +72,7 @@
      * @param dvalue quantity of input units
      * @param unit input unit specifier (in, cm, etc.)
      */
-    protected void convert(double dvalue, String unit) {
+    private static int convert(double dvalue, String unit) {
         // TODO: the whole routine smells fishy.
 
         int assumedResolution = 1;    // points/pixel = 72dpi
@@ -90,9 +104,9 @@
             log.error("Unknown length unit '" + unit + "'");
         }
         if (unit.equals("mpt")) {
-            millipoints = (int)dvalue;
+            return (int)dvalue;
         } else {
-            millipoints = (int)(dvalue * 1000);
+            return (int)(dvalue * 1000);
         }
     }
 
@@ -139,5 +153,15 @@
         return millipoints + "mpt";
     }
 
+    public boolean equals(Object obj) {
+        if (!(obj instanceof FixedLength))
+            return false;
+        FixedLength fl = (FixedLength)obj;
+        return millipoints == fl.millipoints;
+    }
+
+    public int hashCode() {
+        return millipoints;
+    }
 }
 
Index: src/java/org/apache/fop/fo/properties/CommonMarginBlock.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonMarginBlock.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonMarginBlock.java	(working copy)
@@ -30,51 +30,59 @@
  * Public "structure" allows direct member access.
  */
 public class CommonMarginBlock {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "margin-top" property.
      */
-    public Length marginTop;
+    public final Length marginTop;
 
     /**
      * The "margin-bottom" property.
      */
-    public Length marginBottom;
+    public final Length marginBottom;
 
     /**
      * The "margin-left" property.
      */
-    public Length marginLeft;
+    public final Length marginLeft;
 
     /**
      * The "margin-right" property.
      */
-    public Length marginRight;
+    public final Length marginRight;
 
     /**
      * The "space-before" property.
      */
-    public SpaceProperty spaceBefore;
+    public final SpaceProperty spaceBefore;
 
     /**
      * The "space-after" property.
      */
-    public SpaceProperty spaceAfter;
+    public final SpaceProperty spaceAfter;
 
     /**
      * The "start-indent" property.
      */
-    public Length startIndent;
+    public final Length startIndent;
 
     /**
      * The "end-indent" property.
      */
-    public Length endIndent;
+    public final Length endIndent;
 
+    public static final CommonMarginBlock getInstance(PropertyList pList) throws PropertyException {
+        CommonMarginBlock c = new CommonMarginBlock(pList);
+        return (CommonMarginBlock)cache.fetch(c);
+    }
+
     /**
      * Create a CommonMarginBlock object.
      * @param pList The PropertyList with propery values.
      */
-    public CommonMarginBlock(PropertyList pList) throws PropertyException {
+    private CommonMarginBlock(PropertyList pList) throws PropertyException {
         marginTop = pList.get(Constants.PR_MARGIN_TOP).getLength();
         marginBottom = pList.get(Constants.PR_MARGIN_BOTTOM).getLength();
         marginLeft = pList.get(Constants.PR_MARGIN_LEFT).getLength();
@@ -98,5 +106,30 @@
             + "Indents (start, end): ("
             + startIndent + ", " + endIndent + ")\n";
     }
+
+    public int hashCode() {
+        return marginTop.hashCode() << 1 +
+            marginBottom.hashCode() << 2 +
+            marginLeft.hashCode() << 4 +
+            marginRight.hashCode() << 6 +
+            spaceBefore.hashCode() << 8 +
+            spaceAfter.hashCode() << 10 +
+            startIndent.hashCode() << 12 +
+            endIndent.hashCode() << 14;
+    }
+
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CommonMarginBlock))
+            return false;
+        CommonMarginBlock cmb = (CommonMarginBlock)obj;
+        return Property.eq(this.marginTop, cmb.marginTop) &&
+            Property.eq(this.marginBottom, cmb.marginBottom) &&
+            Property.eq(this.marginLeft, cmb.marginLeft) &&
+            Property.eq(this.marginRight, cmb.marginRight) &&
+            Property.eq(this.spaceBefore, cmb.spaceBefore) &&
+            Property.eq(this.spaceAfter, cmb.spaceAfter) &&
+            Property.eq(this.startIndent, cmb.startIndent) &&
+            Property.eq(this.endIndent, cmb.endIndent);
+    }
     
 }
Index: src/java/org/apache/fop/fo/properties/PercentLength.java
===================================================================
--- src/java/org/apache/fop/fo/properties/PercentLength.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/PercentLength.java	(working copy)
@@ -32,15 +32,13 @@
      * The percentage itself, expressed as a decimal value, e.g. for 95%, set
      * the value to .95
      */
-    private double factor;
+    private final double factor;
 
     /**
      * A PercentBase implementation that contains the base length to which the
      * {@link #factor} should be applied to compute the actual length
      */
-    private PercentBase lbase = null;
-    
-    private double resolvedValue;
+    private final PercentBase lbase;
 
     /**
      * Main constructor. Construct an object based on a factor (the percent,
@@ -92,8 +90,7 @@
      */
     public double getNumericValue(PercentBaseContext context) {
         try {
-            resolvedValue = factor * lbase.getBaseLength(context);
-            return resolvedValue;
+            return factor * lbase.getBaseLength(context);
         } catch (PropertyException exc) {
             log.error(exc);
             return 0;
@@ -123,4 +120,17 @@
         return (new Double(factor * 100.0).toString()) + "%";
     }
 
+    public int hashCode() {
+      return lbase.hashCode();
+    }
+    
+    public boolean equals(Object obj) {
+        if (! (obj instanceof PercentLength))
+            return false;
+        PercentLength pl = (PercentLength)obj;
+        return pl.factor == this.factor &&
+          Property.eq(pl.lbase, this.lbase);
+    }
+
+
 }
Index: src/java/org/apache/fop/fo/properties/PropertyMaker.java
===================================================================
--- src/java/org/apache/fop/fo/properties/PropertyMaker.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/PropertyMaker.java	(working copy)
@@ -68,7 +68,7 @@
     /**
      * @return the name of the property for this Maker
      */
-    public int getPropId() {
+    public final int getPropId() {
         return propId;
     }
 
Index: src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java
===================================================================
--- src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java	(working copy)
@@ -108,7 +108,7 @@
             //Margin is used
             Numeric margin = propertyList.get(marginProp).getNumeric();
             
-            Numeric v = new FixedLength(0);
+            Numeric v = FixedLength.getInstance(0);
             if (!propertyList.getFObj().generatesReferenceAreas()) {
                 // The inherited_value_of([start|end]-indent)
                 v = NumericOp.addition(v, propertyList.getInherited(baseMaker.propId).getNumeric());
@@ -173,7 +173,7 @@
                 if (isInherited(propertyList) || !marginNearest) {
                     return null;
                 } else {
-                    return new FixedLength(0);
+                    return FixedLength.getInstance(0);
                 }
             } else {
                 return indent;
@@ -182,7 +182,7 @@
             //Margin is used
             Numeric margin = propertyList.get(marginProp).getNumeric();
             
-            Numeric v = new FixedLength(0);
+            Numeric v = FixedLength.getInstance(0);
             if (isInherited(propertyList)) {
                 // The inherited_value_of([start|end]-indent)
                 v = NumericOp.addition(v, propertyList.getInherited(baseMaker.propId).getNumeric());
Index: src/java/org/apache/fop/fo/properties/CommonRelativePosition.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonRelativePosition.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonRelativePosition.java	(working copy)
@@ -30,30 +30,33 @@
  * Public "structure" allows direct member access.
  */
 public class CommonRelativePosition {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "relative-position" property.
      */
-    public int relativePosition;
+    public final int relativePosition;
     
     /**
      * The "top" property.
      */
-    public Length top;
+    public final Length top;
 
     /**
      * The "right" property.
      */
-    public Length right;
+    public final Length right;
     
     /**
      * The "bottom" property.
      */
-    public Length bottom;
+    public final Length bottom;
     
     /**
      * The "left" property.
      */
-    public Length left;
+    public final Length left;
 
     /**
      * Create a CommonRelativePosition object.
@@ -67,4 +70,25 @@
         right = pList.get(Constants.PR_RIGHT).getLength();      
     }
 
+    public int hashCode() {
+      return relativePosition + 
+        Property.hc(top) << 2 +
+        Property.hc(right) << 4 +
+        Property.hc(bottom) << 6 +
+        Property.hc(left) << 8;
+    }
+
+    public boolean equals(Object obj) {
+        CommonRelativePosition c = (CommonRelativePosition)obj;        
+        return c.relativePosition == relativePosition &&
+          Property.eq(c.top, top) &&
+          Property.eq(c.right, right) &&
+          Property.eq(c.bottom, bottom) &&
+          Property.eq(c.left, left);
+    }
+
+    public static CommonRelativePosition getInstance(PropertyList pList) throws PropertyException {
+        CommonRelativePosition c = new CommonRelativePosition(pList);
+        return (CommonRelativePosition)cache.fetch(c);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java	(working copy)
@@ -28,6 +28,7 @@
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.expr.PropertyException;
+import org.apache.fop.fo.flow.TableCell;
 import org.apache.fop.image.FopImage;
 import org.apache.fop.image.ImageFactory;
 
@@ -36,40 +37,39 @@
  * See Sec. 7.7 of the XSL-FO Standard.
  */
 public class CommonBorderPaddingBackground {
+
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "background-attachment" property.
      */
-    public int backgroundAttachment;
+    public final int backgroundAttachment;
 
     /**
      * The "background-color" property.
      */
-    public Color backgroundColor;
+    public final Color backgroundColor;
 
     /**
      * The "background-image" property.
      */
-    public String backgroundImage;
+    public final String backgroundImage;
 
     /**
      * The "background-repeat" property.
      */
-    public int backgroundRepeat;
+    public final int backgroundRepeat;
 
     /**
      * The "background-position-horizontal" property.
      */
-    public Length backgroundPositionHorizontal;
+    public final Length backgroundPositionHorizontal;
 
     /**
      * The "background-position-vertical" property.
      */
-    public Length backgroundPositionVertical;
+    public final Length backgroundPositionVertical;
     
-    
-    private FopImage fopimage;
-    
-    
     /** the "before" edge */ 
     public static final int BEFORE = 0;
     /** the "after" edge */ 
@@ -123,16 +123,71 @@
             sb.append("}");
             return sb.toString();
         }
+
+        public int hashCode() {
+            return mStyle + mColor.hashCode() << 4 + mWidth.hashCode() << 7;
+        }
+
+        public boolean equals(Object obj) {
+            if (!( obj instanceof BorderInfo))
+                return false;
+            BorderInfo b = (BorderInfo)obj;
+            return this.mStyle == b.mStyle &&
+                Property.eq(this.mColor, b.mColor) &&
+                Property.eq(this.mWidth, b.mWidth);
+        }
     }
+    
+    private final FopImage fopimage;
 
-    private BorderInfo[] borderInfo = new BorderInfo[4];
-    private CondLengthProperty[] padding = new CondLengthProperty[4];
+    private final BorderInfo[] borderInfo = new BorderInfo[4];
 
+    /** 
+        Padding lengths, indexed by BEFORE, AFTER, START, END
+    */
+    private CondLengthProperty[] padding = new CondLengthProperty[4]; // TODO: Should be final
+
     /**
-     * Construct a CommonBorderPaddingBackground object.
+       Returns a new  CommonBorderPaddingBackground instance with appropriate changes
+       made to border and padding.
+       @param src The original CBPB whose properties to take.
+       @param cell 
+    */
+    public static final CommonBorderPaddingBackground getInstance(CommonBorderPaddingBackground src,
+                                                                  TableCell cell,
+                                                                  BorderInfo bi, int side) {
+        if (bi==null && cell==null) {
+            return src;
+        }
+        CommonBorderPaddingBackground nc = new CommonBorderPaddingBackground(src, cell, bi, side);
+        return (CommonBorderPaddingBackground)cache.fetch(nc);
+    }
+
+    public static final CommonBorderPaddingBackground getInstance(PropertyList pList) throws PropertyException {
+        CommonBorderPaddingBackground c = new CommonBorderPaddingBackground(pList, pList.getFObj());
+        return (CommonBorderPaddingBackground)cache.fetch(c);
+    }
+
+    /**
+     * See the above get instance() method.
      */
-    public CommonBorderPaddingBackground() {
-        
+    private CommonBorderPaddingBackground(CommonBorderPaddingBackground src, TableCell cell,
+                                          BorderInfo bi, int side) {
+        this.backgroundAttachment = src.backgroundAttachment;
+        this.backgroundColor = src.backgroundColor;
+        this.backgroundImage = src.backgroundImage;
+        this.backgroundRepeat = src.backgroundRepeat;
+        this.backgroundPositionHorizontal = src.backgroundPositionHorizontal;
+        this.backgroundPositionVertical = src.backgroundPositionVertical;
+        this.fopimage = src.fopimage;
+        if (cell!=null) {
+            CondLengthProperty[] srcPadding = cell.getCommonBorderPaddingBackground().padding;
+            for (int i = 0; i < padding.length ; i++)
+                this.padding[i] = srcPadding[i];
+        }
+        if (bi!=null) {
+            this.borderInfo[side] = bi;
+        }
     }
     
     /**
@@ -141,19 +196,22 @@
      * @param fobj The FO to create this instance for.
      * @throws PropertyException if there's an error while binding the properties
      */
-    public CommonBorderPaddingBackground(PropertyList pList, FObj fobj) throws PropertyException {
+    private CommonBorderPaddingBackground(PropertyList pList, FObj fobj) throws PropertyException {
         
         backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();
-        backgroundColor = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
+        Color bc = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
                 fobj == null ? null : fobj.getUserAgent());
-        if (backgroundColor.getAlpha() == 0) {
-            backgroundColor = null;
-        }
+        backgroundColor = bc.getAlpha() == 0 ? null : bc;
 
-        backgroundImage = pList.get(Constants.PR_BACKGROUND_IMAGE).getString();
-        if (backgroundImage == null || "none".equals(backgroundImage)) {
+        String bi = pList.get(Constants.PR_BACKGROUND_IMAGE).getString();
+        if (bi == null || "none".equals(bi)) {
             backgroundImage = null;
+            backgroundRepeat = 0;
+            backgroundPositionHorizontal = null;
+            backgroundPositionVertical = null;
+            fopimage = null;
         } else {
+            backgroundImage = bi;
             backgroundRepeat = pList.get(Constants.PR_BACKGROUND_REPEAT).getEnum();
             backgroundPositionHorizontal = pList.get(
                     Constants.PR_BACKGROUND_POSITION_HORIZONTAL).getLength();
@@ -220,6 +278,7 @@
      * Sets a border.
      * @param info the border information
      * @param side the side to apply the info to
+     * @deprecated CommonBorderPaddingBackground should be immutable.
      */
     public void setBorderInfo(BorderInfo info, int side) {
         this.borderInfo[side] = info;
@@ -232,10 +291,11 @@
     public BorderInfo getBorderInfo(int side) {
         return this.borderInfo[side];
     }
-    
+
     /**
      * Set padding.
      * @param source the padding info to copy from
+     * @deprecated CommonBorderPaddingBackground should be immutable.
      */
     public void setPadding(CommonBorderPaddingBackground source) {
         this.padding = source.padding;
@@ -405,4 +465,32 @@
         return (borderInfo[BEFORE] != null || borderInfo[AFTER] != null
                 || borderInfo[START] != null || borderInfo[END] != null);
     }
+
+    public int hashCode() {
+        return Property.hc(backgroundColor) << 1 +
+            Property.hc(backgroundImage) << 3 +
+            backgroundRepeat << 5 +
+            Property.hc(backgroundPositionHorizontal) << 7 +
+            Property.hc(backgroundPositionVertical);
+    }
+
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CommonBorderPaddingBackground))
+            return false;
+        CommonBorderPaddingBackground c = (CommonBorderPaddingBackground)obj;
+        return backgroundAttachment == c.backgroundAttachment &&
+            backgroundRepeat == c.backgroundRepeat &&
+            Property.eq(backgroundColor, c.backgroundColor) &&
+            Property.eq(backgroundImage, c.backgroundImage) &&
+            Property.eq(backgroundPositionHorizontal, c.backgroundPositionHorizontal) &&
+            Property.eq(backgroundPositionVertical, c.backgroundPositionVertical) &&
+            Property.eq(padding[BEFORE], c.padding[BEFORE]) &&
+            Property.eq(padding[AFTER], c.padding[AFTER]) &&
+            Property.eq(padding[START], c.padding[START]) &&
+            Property.eq(padding[END], c.padding[END]) &&
+            Property.eq(borderInfo, c.borderInfo) &&
+            (fopimage == c.fopimage ||
+             (fopimage!=null &&
+              fopimage.getOriginalURI().equals(c.fopimage.getOriginalURI())));
+    }
 }
Index: src/java/org/apache/fop/fo/properties/CommonAural.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonAural.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonAural.java	(working copy)
@@ -121,6 +121,13 @@
      * Create a CommonAbsolutePosition object.
      * @param pList The PropertyList with propery values.
      */
-    public CommonAural(PropertyList pList) {
+    private CommonAural(PropertyList pList) {
     }
+
+    /**
+       Placeholder. See CommonAccessibility for the correct instantiation idiom.
+    */
+    public static CommonAural getInstance(PropertyList pList) {
+        return null;
+    }
 }
Index: src/java/org/apache/fop/fo/properties/CommonFont.java
===================================================================
--- src/java/org/apache/fop/fo/properties/CommonFont.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/CommonFont.java	(working copy)
@@ -38,45 +38,47 @@
  */
 public class CommonFont {
 
+    private static final PropertyCache cache = new PropertyCache();
+
     /**
      * The "font-family" property.
      */
-    private String[] fontFamily;
+    private final String[] fontFamily;
 
     /**
      * The "font-selection-strategy" property.
      */
-    public int fontSelectionStrategy;
+    public final int fontSelectionStrategy;
 
     /**
      * The "font-size" property.
      */
-    public Length fontSize;
+    public final Length fontSize;
 
     /**
      * The "font-stretch" property.
      */
-    public int fontStretch;
+    public final int fontStretch;
 
     /**
      * The "font-size-adjust" property.
      */
-    public Numeric fontSizeAdjust;
+    public final Numeric fontSizeAdjust;
 
     /**
      * The "font-style" property.
      */
-    public int fontStyle;
+    public final int fontStyle;
 
     /**
      * The "font-variant" property.
      */
-    public int fontVariant;
+    public final int fontVariant;
 
     /**
      * The "font-weight" property.
      */
-    public int fontWeight;
+    public final int fontWeight;
 
     private Font fontState;
 
@@ -84,15 +86,17 @@
      * Create a CommonFont object.
      * @param pList The PropertyList to get properties from.
      */
-    public CommonFont(PropertyList pList) throws PropertyException {
+    private CommonFont(PropertyList pList) throws PropertyException {
         List lst = pList.get(Constants.PR_FONT_FAMILY).getList();
-        fontFamily = new String[lst.size()];
-        for (int i = 0, c = lst.size(); i < c; i++) {
-            fontFamily[i] = ((Property)lst.get(i)).getString();
-        }
-        if (fontFamily.length == 0) {
+        int lstSize = lst.size();
+        if (lstSize == 0) {
             //Shouldn't happen, but we never know.
             fontFamily = new String[] {"any"};
+        } else {
+          fontFamily = new String[lstSize];
+          for (int i = 0, c = lstSize; i < c; i++) {
+            fontFamily[i] = ((Property)lst.get(i)).getString();
+          }
         }
         fontSelectionStrategy = pList.get(Constants.PR_FONT_SELECTION_STRATEGY).getEnum();
         fontSize = pList.get(Constants.PR_FONT_SIZE).getLength();
@@ -103,6 +107,11 @@
         fontWeight = pList.get(Constants.PR_FONT_WEIGHT).getEnum();
     }
 
+    public static CommonFont getInstance(PropertyList pList) throws PropertyException {
+        CommonFont cf = new CommonFont(pList);
+        return (CommonFont)cache.fetch(cf);
+    }
+
     /** @return the first font-family name in the list */
     public String getFirstFontFamily() {
         return this.fontFamily[0];
@@ -114,15 +123,6 @@
     }
     
     /**
-     * Overrides the font-family.
-     * @param value the new font-family
-     */
-    public void overrideFontFamily(String value) {
-        this.fontFamily = new String[] {value};
-        
-    }
-    
-    /**
      * Create and return a Font object based on the properties. 
      * 
      * @param fontInfo
@@ -175,4 +175,22 @@
         return fontState;
     }
 
+    public int hashCode() {
+        return fontStretch + fontStyle + fontVariant + fontWeight;
+    }
+
+    public boolean equals(Object obj) {
+        if (! (obj instanceof CommonFont))
+            return false;
+        CommonFont cf = (CommonFont)obj;
+        return fontSelectionStrategy == cf.fontSelectionStrategy &&
+            fontStretch == cf.fontStretch &&
+            fontStyle == cf.fontStyle &&
+            fontVariant == cf.fontVariant &&
+            fontWeight == cf.fontWeight &&
+            fontFamily.length == cf.fontFamily.length &&
+            Property.eq(fontSize,  cf.fontSize) &&
+            Property.eq(fontSizeAdjust, cf.fontSizeAdjust) &&
+            Property.eq(fontFamily, cf.fontFamily);
+    }
 }
Index: src/java/org/apache/fop/fo/properties/ColorProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/ColorProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/ColorProperty.java	(working copy)
@@ -138,5 +138,22 @@
     public Object getObject() {
         return this;
     }
+
+    /**
+     * @see java.lang.Object#equals(Object)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof ColorProperty) {
+            ColorProperty cp = (ColorProperty)obj;
+            return Property.eq(cp.color, this.color);
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        return color.hashCode();
+    }
+
 }
 
Index: src/java/org/apache/fop/fo/properties/LengthProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/LengthProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/LengthProperty.java	(working copy)
@@ -59,7 +59,7 @@
             }
             if (p instanceof NumberProperty) {
                 //Assume pixels (like in HTML) when there's no unit
-                return new FixedLength(p.getNumeric().getNumericValue(), "px");
+                return FixedLength.getInstance(p.getNumeric().getNumericValue(), "px");
             }
             Length val = p.getLength();
             if (val != null) {
Index: src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java
===================================================================
--- src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java	(working copy)
@@ -55,9 +55,9 @@
             Property pp = propertyList.getFromParent(this.getPropId());
             int baseFontSize = computeClosestAbsoluteFontSize(pp.getLength().getValue());
             if (p.getEnum() == EN_LARGER) {
-                return new FixedLength((int)Math.round((baseFontSize * FONT_SIZE_GROWTH_FACTOR)));
+                return FixedLength.getInstance((int)Math.round((baseFontSize * FONT_SIZE_GROWTH_FACTOR)));
             } else {
-                return new FixedLength((int)Math.round((baseFontSize / FONT_SIZE_GROWTH_FACTOR)));
+                return FixedLength.getInstance((int)Math.round((baseFontSize / FONT_SIZE_GROWTH_FACTOR)));
             }
         }
         return super.convertProperty(p, propertyList, fo);
Index: src/java/org/apache/fop/fo/properties/KeepProperty.java
===================================================================
--- src/java/org/apache/fop/fo/properties/KeepProperty.java	(revision 496642)
+++ src/java/org/apache/fop/fo/properties/KeepProperty.java	(working copy)
@@ -165,4 +165,19 @@
         return this;
     }
 
+    public int hashCode() {
+        return withinLine.hashCode() << 3 +
+            withinColumn.hashCode() << 7 +
+            withinPage.hashCode() << 9;
+    }
+
+    public boolean equals(Object obj) {
+        if (! (obj instanceof KeepProperty))
+            return false;
+        KeepProperty c = (KeepProperty)obj;
+        return Property.eq(this.withinLine, c.withinLine) &&
+          Property.eq(this.withinColumn, c.withinColumn) &&
+          Property.eq(this.withinPage, c.withinPage);
+    }
+
 }
Index: src/java/org/apache/fop/fo/PropertyList.java
===================================================================
--- src/java/org/apache/fop/fo/PropertyList.java	(revision 496642)
+++ src/java/org/apache/fop/fo/PropertyList.java	(working copy)
@@ -38,6 +38,7 @@
 import org.apache.fop.fo.properties.CommonRelativePosition;
 import org.apache.fop.fo.properties.CommonTextDecoration;
 import org.apache.fop.fo.properties.Property;
+import org.apache.fop.fo.properties.PropertyCache;
 import org.apache.fop.fo.properties.PropertyMaker;
 import org.apache.fop.util.QName;
 
@@ -46,6 +47,8 @@
  */
 public abstract class PropertyList {
 
+    private static final PropertyCache cache = new PropertyCache();
+
     // writing-mode index
     private int writingMode;
 
@@ -53,7 +56,7 @@
 
     /** reference to the parent FO's propertyList **/
     protected PropertyList parentPropertyList = null;
-    private FObj fobj = null;
+    private final FObj fobj;
 
     private static Log log = LogFactory.getLog(PropertyList.class);
 
@@ -171,8 +174,9 @@
 
         PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
         if (propertyMaker != null) {
-            return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
-                                         bTryInherit, bTryDefault);
+            Property p = propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
+                                           bTryInherit, bTryDefault);
+            return (Property)cache.fetch(p);
         }
         return null;
     }
@@ -520,7 +524,7 @@
             return null;
         }
     }
-
+    
     /**
      * @param propID ID of property
      * @return new Property object
@@ -529,7 +533,7 @@
     private Property makeProperty(int propId) throws PropertyException {
         PropertyMaker propertyMaker = findMaker(propId);
         if (propertyMaker != null) {
-            return propertyMaker.make(this);
+            return (Property)cache.fetch(propertyMaker.make(this));
         } else {
             //log.error("property " + propertyName
             //                       + " ignored");
@@ -574,7 +578,7 @@
      */
     public CommonBorderPaddingBackground getBorderPaddingBackgroundProps() 
                 throws PropertyException {
-        return new CommonBorderPaddingBackground(this, getFObj());
+        return CommonBorderPaddingBackground.getInstance(this);
     }
     
     /**
@@ -583,7 +587,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonHyphenation getHyphenationProps() throws PropertyException {
-        return new CommonHyphenation(this);
+        return CommonHyphenation.getInstance(this);
     }
     
     /**
@@ -592,7 +596,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonMarginBlock getMarginBlockProps() throws PropertyException {
-        return new CommonMarginBlock(this);
+        return CommonMarginBlock.getInstance(this);
     }
     
     /**
@@ -601,7 +605,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonMarginInline getMarginInlineProps() throws PropertyException {
-        return new CommonMarginInline(this);
+      return new CommonMarginInline(this); // TODO: Replace with getInstance()
     }
     
     /**
@@ -610,7 +614,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonAccessibility getAccessibilityProps() throws PropertyException {
-        return new CommonAccessibility(this);
+        return CommonAccessibility.getInstance(this);
     }
 
     /**
@@ -619,8 +623,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonAural getAuralProps() throws PropertyException {
-        CommonAural props = new CommonAural(this);
-        return props;
+        return CommonAural.getInstance(this);
     }
 
     /**
@@ -629,7 +632,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonRelativePosition getRelativePositionProps() throws PropertyException {
-        return new CommonRelativePosition(this);
+        return CommonRelativePosition.getInstance(this);
     }
     
     /**
@@ -638,7 +641,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonAbsolutePosition getAbsolutePositionProps() throws PropertyException {
-        return new CommonAbsolutePosition(this);
+        return CommonAbsolutePosition.getInstance(this);
     }    
     
 
@@ -648,7 +651,7 @@
      * @throws PropertyException if there's a problem while processing the properties
      */
     public CommonFont getFontProps() throws PropertyException {
-        return new CommonFont(this);
+        return CommonFont.getInstance(this);
     }
     
     /**
Index: src/java/org/apache/fop/fo/flow/Block.java
===================================================================
--- src/java/org/apache/fop/fo/flow/Block.java	(revision 496642)
+++ src/java/org/apache/fop/fo/flow/Block.java	(working copy)
@@ -52,8 +52,6 @@
     private boolean initialPropertySetFound = false;
 
     // The value of properties relevant for fo:block.
-    private CommonAccessibility commonAccessibility;
-    private CommonAural commonAural;
     private CommonBorderPaddingBackground commonBorderPaddingBackground;
     private CommonFont commonFont;
     private CommonHyphenation commonHyphenation;
@@ -83,7 +81,10 @@
     private int whiteSpaceCollapse;
     private Numeric widows;
     private int wrapOption;
+
     // Unused but valid items, commented out for performance:
+    //     private CommonAccessibility commonAccessibility;
+    //     private CommonAural commonAural;
     //     private Length textDepth;
     //     private Length textAltitude;
     //     private int visibility;
@@ -101,8 +102,6 @@
      * @see org.apache.fop.fo.FObj#bind(PropertyList)
      */
     public void bind(PropertyList pList) throws FOPException {
-        commonAccessibility = pList.getAccessibilityProps();
-        commonAural = pList.getAuralProps();
         commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
         commonFont = pList.getFontProps();
         commonHyphenation = pList.getHyphenationProps();
@@ -346,20 +345,6 @@
     }
     
     /**
-     * @return Returns the commonAccessibility.
-     */
-    public CommonAccessibility getCommonAccessibility() {
-        return this.commonAccessibility;
-    }
-
-    /**
-     * @return Returns the commonAural.
-     */
-    public CommonAural getCommonAural() {
-        return this.commonAural;
-    }
-
-    /**
      * @return Returns the commonRelativePosition.
      */
     public CommonRelativePosition getCommonRelativePosition() {
Index: src/java/org/apache/fop/fo/StaticPropertyList.java
===================================================================
--- src/java/org/apache/fop/fo/StaticPropertyList.java	(revision 496642)
+++ src/java/org/apache/fop/fo/StaticPropertyList.java	(working copy)
@@ -24,8 +24,8 @@
  * the explicit set properties and another array to store cached values.
  */
 public class StaticPropertyList extends PropertyList {
-    private Property[] explicit;
-    private Property[] values;
+    private final Property[] explicit;
+    private final Property[] values;
     
     /**
      * Construct a StaticPropertyList. 
Index: src/java/org/apache/fop/fo/FOTreeBuilder.java
===================================================================
--- src/java/org/apache/fop/fo/FOTreeBuilder.java	(revision 496642)
+++ src/java/org/apache/fop/fo/FOTreeBuilder.java	(working copy)
@@ -109,11 +109,10 @@
      * true no Locator is passed to the FO tree nodes which would copy the information into
      * a SAX LocatorImpl instance.
      * @return true if no context information should be stored on each node in the FO tree.
+     * @deprecated Use FOUserAgent.isLocatorEnabled() instead.
      */
     protected boolean isLocatorDisabled() {
-        //TODO make this configurable through the FOUserAgent so people can optimize memory
-        //consumption.
-        return false;
+        return !userAgent.isLocatorEnabled();
     }
     
     /**
@@ -126,7 +125,7 @@
     
     /** @return a Locator instance if it is available and not disabled */
     protected Locator getEffectiveLocator() {
-        return (isLocatorDisabled() ? null : this.locator);
+        return (userAgent.isLocatorEnabled() ? this.locator : null);
     }
     
     /**
Index: src/java/org/apache/fop/fo/expr/PropertyParser.java
===================================================================
--- src/java/org/apache/fop/fo/expr/PropertyParser.java	(revision 496642)
+++ src/java/org/apache/fop/fo/expr/PropertyParser.java	(working copy)
@@ -293,7 +293,7 @@
                 prop = (Property) NumericOp.multiply(new NumberProperty(numPart.doubleValue()),
                                     propInfo.currentFontSize());
             } else {
-                prop = new FixedLength(numPart.doubleValue(), unitPart);
+                prop = FixedLength.getInstance(numPart.doubleValue(), unitPart);
             }
             break;
 
Index: build.xml
===================================================================
--- build.xml	(revision 496642)
+++ build.xml	(working copy)
@@ -152,7 +152,7 @@
 
   <property name="javac.debug" value="on"/>
   <property name="javac.optimize" value="off"/>
-  <property name="javac.deprecation" value="on"/>
+  <property name="javac.deprecation" value="off"/>
   <property name="javac.source" value="1.3"/>
   <property name="javac.target" value="1.3"/>
   <property name="javac.fork" value="no"/>
