From 50505cd29cf2acf34df427f9048a36337e8f1304 Mon Sep 17 00:00:00 2001 From: "Andreas L. Delmelle" Date: Sat, 7 Jul 2007 20:13:41 +0000 Subject: [PATCH] Partial application of the patch in Bugzilla 41044: * addition of a generic PropertyCache to be used by all Property types that can be safely canonicalized * modified EnumProperty, StringProperty, NumberProperty, EnumNumber and FixedLength to make use of the cache infrastructure git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@554251 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/StaticPropertyList.java | 4 +- .../apache/fop/fo/expr/CeilingFunction.java | 2 +- .../org/apache/fop/fo/expr/FloorFunction.java | 2 +- .../apache/fop/fo/expr/PropertyParser.java | 22 ++++---- .../org/apache/fop/fo/expr/RoundFunction.java | 2 +- .../apache/fop/fo/properties/EnumNumber.java | 45 +++++++++++---- .../fop/fo/properties/EnumProperty.java | 21 +++---- .../apache/fop/fo/properties/FixedLength.java | 47 ++++++++++++---- .../fop/fo/properties/FontFamilyProperty.java | 2 +- .../fop/fo/properties/LengthProperty.java | 2 +- .../fop/fo/properties/NumberProperty.java | 46 ++++++++++++++-- .../fop/fo/properties/PropertyCache.java | 55 +++++++++++++++++++ .../fop/fo/properties/StringProperty.java | 39 ++++++++++++- .../fo/properties/TableBorderPrecedence.java | 14 ++--- status.xml | 7 +++ 15 files changed, 242 insertions(+), 68 deletions(-) create mode 100644 src/java/org/apache/fop/fo/properties/PropertyCache.java diff --git a/src/java/org/apache/fop/fo/StaticPropertyList.java b/src/java/org/apache/fop/fo/StaticPropertyList.java index 1ef3abd73..de020126f 100755 --- a/src/java/org/apache/fop/fo/StaticPropertyList.java +++ b/src/java/org/apache/fop/fo/StaticPropertyList.java @@ -24,8 +24,8 @@ import org.apache.fop.fo.properties.Property; * 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. diff --git a/src/java/org/apache/fop/fo/expr/CeilingFunction.java b/src/java/org/apache/fop/fo/expr/CeilingFunction.java index 9330fbdb3..29ac1940a 100644 --- a/src/java/org/apache/fop/fo/expr/CeilingFunction.java +++ b/src/java/org/apache/fop/fo/expr/CeilingFunction.java @@ -34,7 +34,7 @@ class CeilingFunction extends FunctionBase { if (dbl == null) { throw new PropertyException("Non number operand to ceiling function"); } - return new NumberProperty(Math.ceil(dbl.doubleValue())); + return NumberProperty.getInstance(Math.ceil(dbl.doubleValue())); } } diff --git a/src/java/org/apache/fop/fo/expr/FloorFunction.java b/src/java/org/apache/fop/fo/expr/FloorFunction.java index e7d253d69..9cdbf1a28 100644 --- a/src/java/org/apache/fop/fo/expr/FloorFunction.java +++ b/src/java/org/apache/fop/fo/expr/FloorFunction.java @@ -35,7 +35,7 @@ class FloorFunction extends FunctionBase { if (dbl == null) { throw new PropertyException("Non number operand to floor function"); } - return new NumberProperty(Math.floor(dbl.doubleValue())); + return NumberProperty.getInstance(Math.floor(dbl.doubleValue())); } } diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java index 5b5956f44..3bb15cc4c 100644 --- a/src/java/org/apache/fop/fo/expr/PropertyParser.java +++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java @@ -117,7 +117,7 @@ public final class PropertyParser extends PropertyTokenizer { next(); if (currentToken == TOK_EOF) { // if prop value is empty string, force to StringProperty - return new StringProperty(""); + return StringProperty.getInstance(""); } ListProperty propList = null; while (true) { @@ -245,7 +245,7 @@ public final class PropertyParser extends PropertyTokenizer { return prop; case TOK_LITERAL: - prop = new StringProperty(currentTokenValue); + prop = StringProperty.getInstance(currentTokenValue); break; case TOK_NCNAME: @@ -254,11 +254,11 @@ public final class PropertyParser extends PropertyTokenizer { break; case TOK_FLOAT: - prop = new NumberProperty(new Double(currentTokenValue)); + prop = NumberProperty.getInstance(new Double(currentTokenValue)); break; case TOK_INTEGER: - prop = new NumberProperty(new Integer(currentTokenValue)); + prop = NumberProperty.getInstance(new Integer(currentTokenValue)); break; case TOK_PERCENT: @@ -271,7 +271,7 @@ public final class PropertyParser extends PropertyTokenizer { PercentBase pcBase = this.propInfo.getPercentBase(); if (pcBase != null) { if (pcBase.getDimension() == 0) { - prop = new NumberProperty(pcval * pcBase.getBaseValue()); + prop = NumberProperty.getInstance(pcval * pcBase.getBaseValue()); } else if (pcBase.getDimension() == 1) { prop = new PercentLength(pcval, pcBase); } else { @@ -279,7 +279,7 @@ public final class PropertyParser extends PropertyTokenizer { } } else { // WARNING? Interpret as a decimal fraction, eg. 50% = .5 - prop = new NumberProperty(pcval); + prop = NumberProperty.getInstance(pcval); } break; @@ -290,10 +290,11 @@ public final class PropertyParser extends PropertyTokenizer { Double numPart = new Double(currentTokenValue.substring(0, numLen)); if (unitPart.equals(RELUNIT)) { - prop = (Property) NumericOp.multiply(new NumberProperty(numPart.doubleValue()), + prop = (Property) NumericOp.multiply( + NumberProperty.getInstance(numPart.doubleValue()), propInfo.currentFontSize()); } else { - prop = new FixedLength(numPart.doubleValue(), unitPart); + prop = FixedLength.getInstance(numPart.doubleValue(), unitPart); } break; @@ -365,7 +366,8 @@ public final class PropertyParser extends PropertyTokenizer { expectRpar(); } if (i == nbArgs - 1 && function.padArgsWithPropertyName()) { - args[i++] = new StringProperty(propInfo.getPropertyMaker().getName()); + args[i++] = StringProperty.getInstance( + propInfo.getPropertyMaker().getName()); } if (nbArgs != i) { throw new PropertyException("Expected " + nbArgs @@ -521,7 +523,7 @@ public final class PropertyParser extends PropertyTokenizer { if (op1 == null || op2 == null) { throw new PropertyException("Non number operand to modulo"); } - return new NumberProperty(op1.doubleValue() % op2.doubleValue()); + return NumberProperty.getInstance(op1.doubleValue() % op2.doubleValue()); } } diff --git a/src/java/org/apache/fop/fo/expr/RoundFunction.java b/src/java/org/apache/fop/fo/expr/RoundFunction.java index f68f15e16..a28a46e28 100644 --- a/src/java/org/apache/fop/fo/expr/RoundFunction.java +++ b/src/java/org/apache/fop/fo/expr/RoundFunction.java @@ -39,7 +39,7 @@ class RoundFunction extends FunctionBase { if (r == 0.0 && n < 0.0) { r = -r; // round(-0.2) returns -0 not 0 } - return new NumberProperty(r); + return NumberProperty.getInstance(r); } } diff --git a/src/java/org/apache/fop/fo/properties/EnumNumber.java b/src/java/org/apache/fop/fo/properties/EnumNumber.java index a5679cbcc..f8e940b01 100755 --- a/src/java/org/apache/fop/fo/properties/EnumNumber.java +++ b/src/java/org/apache/fop/fo/properties/EnumNumber.java @@ -19,30 +19,34 @@ package org.apache.fop.fo.properties; -import java.util.Map; -import java.util.WeakHashMap; - /** * A number quantity in XSL which is specified as an enum, such as "no-limit". */ public class EnumNumber extends NumberProperty { - private static final Map cache = new WeakHashMap(); + /** cache holding all canonical EnumNumber instances */ + private static final PropertyCache cache = new PropertyCache(); private final EnumProperty enumProperty; - private EnumNumber(EnumProperty enumProperty) { + /** + * Constructor + * @param enumProperty the base EnumProperty + */ + private EnumNumber(Property enumProperty) { super(null); - this.enumProperty = enumProperty; + this.enumProperty = (EnumProperty) enumProperty; } + /** + * Returns the canonical EnumNumber instance corresponding + * to the given Property + * @param enumProperty the base EnumProperty + * @return the canonical instance + */ 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; + return (EnumNumber)cache.fetch( + new EnumNumber((EnumProperty) enumProperty)); } public int getEnum() { @@ -81,5 +85,22 @@ public class EnumNumber extends NumberProperty { return enumProperty.getObject(); } + /** + * @see java.lang.Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj instanceof EnumNumber) { + return (((EnumNumber)obj).enumProperty == this.enumProperty); + } else { + return false; + } + } + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return enumProperty.hashCode(); + } + } diff --git a/src/java/org/apache/fop/fo/properties/EnumProperty.java b/src/java/org/apache/fop/fo/properties/EnumProperty.java index 533ebd64f..f9801e222 100644 --- a/src/java/org/apache/fop/fo/properties/EnumProperty.java +++ b/src/java/org/apache/fop/fo/properties/EnumProperty.java @@ -23,13 +23,13 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; -import java.util.Map; -import java.util.WeakHashMap; - /** * Superclass for properties that wrap an enumeration value */ public class EnumProperty extends Property { + + /** cache holding all canonical EnumProperty instances */ + private static final PropertyCache cache = new PropertyCache(); /** * Inner class for creating EnumProperty instances @@ -65,8 +65,6 @@ public class EnumProperty extends Property { } } - private static final Map propertyCache = new WeakHashMap(); - private final int value; private final String text; @@ -80,14 +78,8 @@ public class EnumProperty extends Property { } 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)); } /** @@ -119,6 +111,9 @@ public class EnumProperty extends Property { } } + /** + * @see java.lang.Object#hashCode() + */ public int hashCode() { return value + text.hashCode(); } diff --git a/src/java/org/apache/fop/fo/properties/FixedLength.java b/src/java/org/apache/fop/fo/properties/FixedLength.java index a043d803e..80016ba2e 100644 --- a/src/java/org/apache/fop/fo/properties/FixedLength.java +++ b/src/java/org/apache/fop/fo/properties/FixedLength.java @@ -25,26 +25,34 @@ import org.apache.fop.datatypes.PercentBaseContext; * An absolute length quantity in XSL */ public class FixedLength extends LengthProperty { + + /** cache holding all canonical FixedLength instances */ + private static final PropertyCache cache = new PropertyCache(); + private int millipoints; - /** - * Set the length given - * @param numRelUnits the number of relative units - * @param iCurFontSize the current font size in base units. - */ - public FixedLength(double numRelUnits, int iCurFontSize) { - millipoints = (int) (numRelUnits * (double)iCurFontSize); - } - /** * Set the length given a number of units and a unit name. * @param numUnits quantity of input units * @param units input unit specifier (in, cm, etc.) */ - public FixedLength(double numUnits, String units) { + private FixedLength(double numUnits, String units) { convert(numUnits, units); } - + + /** + * Return the canonical FixedLength instance corresponding + * to the computed value + * @param numUnits input units + * @param units unit specifier + * @return + */ + public static FixedLength getInstance(double numUnits, String units) { + return (FixedLength) cache.fetch( + new FixedLength(numUnits, units)); + + } + /** * @param baseUnits the length as a number of base units (millipoints) */ @@ -139,5 +147,22 @@ public class FixedLength extends LengthProperty { return millipoints + "mpt"; } + /** + * @see java.lang.Object#equals(Object) + */ + public boolean equals(Object obj) { + if (obj instanceof EnumProperty) { + return (((FixedLength)obj).millipoints == this.millipoints); + } else { + return false; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return millipoints; + } } diff --git a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java index 62263a02c..58cbc6402 100644 --- a/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java +++ b/src/java/org/apache/fop/fo/properties/FontFamilyProperty.java @@ -86,7 +86,7 @@ public class FontFamilyProperty extends ListProperty { + tmpVal.substring(dblSpaceIndex + 1); dblSpaceIndex = tmpVal.indexOf(" "); } - prop.addProperty(new StringProperty(tmpVal)); + prop.addProperty(StringProperty.getInstance(tmpVal)); } } return prop; diff --git a/src/java/org/apache/fop/fo/properties/LengthProperty.java b/src/java/org/apache/fop/fo/properties/LengthProperty.java index 79f9cfba7..8b5565e68 100644 --- a/src/java/org/apache/fop/fo/properties/LengthProperty.java +++ b/src/java/org/apache/fop/fo/properties/LengthProperty.java @@ -59,7 +59,7 @@ public abstract class LengthProperty extends Property } 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) { diff --git a/src/java/org/apache/fop/fo/properties/NumberProperty.java b/src/java/org/apache/fop/fo/properties/NumberProperty.java index 21ffd32e7..55206cff7 100644 --- a/src/java/org/apache/fop/fo/properties/NumberProperty.java +++ b/src/java/org/apache/fop/fo/properties/NumberProperty.java @@ -69,13 +69,16 @@ public class NumberProperty extends Property implements Numeric { } - private Number number; + /** cache holding all canonical NumberProperty instances */ + private static final PropertyCache cache = new PropertyCache(); + + private final Number number; /** * Constructor for Number input * @param num Number object value for property */ - public NumberProperty(Number num) { + protected NumberProperty(Number num) { this.number = num; } @@ -83,7 +86,7 @@ public class NumberProperty extends Property implements Numeric { * Constructor for double input * @param num double numeric value for property */ - public NumberProperty(double num) { + protected NumberProperty(double num) { this.number = new Double(num); } @@ -91,10 +94,43 @@ public class NumberProperty extends Property implements Numeric { * Constructor for integer input * @param num integer numeric value for property */ - public NumberProperty(int num) { + protected NumberProperty(int num) { this.number = new Integer(num); } + /** + * Returns the canonical NumberProperty instance + * corresponding to the given Number + * @param num the base Number + * @return the canonical NumberProperty + */ + public static NumberProperty getInstance(Number num) { + return (NumberProperty)cache.fetch( + new NumberProperty(num)); + } + + /** + * Returns the canonical NumberProperty instance + * corresponding to the given double + * @param num the base double value + * @return the canonical NumberProperty + */ + public static NumberProperty getInstance(double num) { + return (NumberProperty)cache.fetch( + new NumberProperty(num)); + } + + /** + * Returns the canonical NumberProperty instance + * corresponding to the given int + * @param num the base int value + * @return the canonical NumberProperty + */ + public static NumberProperty getInstance(int num) { + return (NumberProperty)cache.fetch( + new NumberProperty(num)); + } + /** * Plain number always has a dimension of 0. * @return a dimension of 0. @@ -172,7 +208,7 @@ public class NumberProperty extends Property implements Numeric { /** @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"); } /** diff --git a/src/java/org/apache/fop/fo/properties/PropertyCache.java b/src/java/org/apache/fop/fo/properties/PropertyCache.java new file mode 100644 index 000000000..176cdd4aa --- /dev/null +++ b/src/java/org/apache/fop/fo/properties/PropertyCache.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.properties; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Thin wrapper around a HashMap to implement the property caching idiom + * in which a new Property instance is created then tested against cached + * instances created previously. If an existing property is found, this is + * retained and the newly created one is instantly eligible for garbage + * collection. + */ +public class PropertyCache { + + private Map propCache = Collections.synchronizedMap(new WeakHashMap()); + + + /** + * Checks if the given property is present in the cache - if so, returns + * a reference to the cached value. Otherwise the given object is added + * to the cache and returned. + * @param obj + * @return the cached instance + */ + public Property fetch(Property prop) { + + Property cacheEntry = (Property) propCache.get(prop); + if (cacheEntry != null) { + return cacheEntry; + } else { + propCache.put(prop, prop); + return prop; + } + } +} diff --git a/src/java/org/apache/fop/fo/properties/StringProperty.java b/src/java/org/apache/fop/fo/properties/StringProperty.java index 1b1699c5a..691942d78 100644 --- a/src/java/org/apache/fop/fo/properties/StringProperty.java +++ b/src/java/org/apache/fop/fo/properties/StringProperty.java @@ -77,16 +77,30 @@ public class StringProperty extends Property { } // end String.Maker - private String str; + /** cache containing all canonical StringProperty instances */ + private static final PropertyCache cache = new PropertyCache(); + + private final String str; /** + * Constructor * @param str String value to place in this object */ - public StringProperty(String str) { + private StringProperty(String str) { this.str = str; - // log.debug("Set StringProperty: " + str); } + /** + * Return the canonical StringProperty instance + * corresponding to the given string value + * @param str the base String + * @return the canonical instance + */ + public static StringProperty getInstance(String str) { + return (StringProperty)cache.fetch( + new StringProperty(str)); + } + /** * @return the Object equivalent of this property */ @@ -101,4 +115,23 @@ public class StringProperty extends Property { return this.str; } + /** + * @see java.lang.Object#hashCode() + */ + public boolean equals(Object obj) { + if (obj instanceof StringProperty) { + StringProperty sp = (StringProperty)obj; + return (sp.str == this.str + || sp.str.equals(this.str)); + } else { + return false; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return str.hashCode(); + } } diff --git a/src/java/org/apache/fop/fo/properties/TableBorderPrecedence.java b/src/java/org/apache/fop/fo/properties/TableBorderPrecedence.java index 9c094840e..dff9626a1 100644 --- a/src/java/org/apache/fop/fo/properties/TableBorderPrecedence.java +++ b/src/java/org/apache/fop/fo/properties/TableBorderPrecedence.java @@ -25,13 +25,13 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; public class TableBorderPrecedence extends NumberProperty.Maker{ - private static Property num0 = new NumberProperty(0); - private static Property num1 = new NumberProperty(1); - private static Property num2 = new NumberProperty(2); - private static Property num3 = new NumberProperty(3); - private static Property num4 = new NumberProperty(4); - private static Property num5 = new NumberProperty(5); - private static Property num6 = new NumberProperty(6); + private static Property num0 = NumberProperty.getInstance(0); + private static Property num1 = NumberProperty.getInstance(1); + private static Property num2 = NumberProperty.getInstance(2); + private static Property num3 = NumberProperty.getInstance(3); + private static Property num4 = NumberProperty.getInstance(4); + private static Property num5 = NumberProperty.getInstance(5); + private static Property num6 = NumberProperty.getInstance(6); public TableBorderPrecedence(int propId) { super(propId); diff --git a/status.xml b/status.xml index fdbf53fa1..81d8e6751 100644 --- a/status.xml +++ b/status.xml @@ -28,6 +28,13 @@ + + Partial application of the patch in Bugzilla 41044: + * addition of a generic PropertyCache to be used by all Property + types that can be safely canonicalized + * modified EnumProperty, StringProperty, NumberProperty, EnumNumber + and FixedLength to make use of the cache infrastructure + Refactoring in the fo package: -> removal of the childNodes instance member in fop.fo.FObj -- 2.39.5