* 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.
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()));
}
}
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()));
}
}
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) {
return prop;
case TOK_LITERAL:
- prop = new StringProperty(currentTokenValue);
+ prop = StringProperty.getInstance(currentTokenValue);
break;
case TOK_NCNAME:
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:
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 {
}
} else {
// WARNING? Interpret as a decimal fraction, eg. 50% = .5
- prop = new NumberProperty(pcval);
+ prop = NumberProperty.getInstance(pcval);
}
break;
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;
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
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());
}
}
if (r == 0.0 && n < 0.0) {
r = -r; // round(-0.2) returns -0 not 0
}
- return new NumberProperty(r);
+ return NumberProperty.getInstance(r);
}
}
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() {
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();
+ }
+
}
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
}
}
- private static final Map propertyCache = new WeakHashMap();
-
private final int value;
private final String text;
}
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));
}
/**
}
}
+ /**
+ * @see java.lang.Object#hashCode()
+ */
public int hashCode() {
return value + text.hashCode();
}
* 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)
*/
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;
+ }
}
+ tmpVal.substring(dblSpaceIndex + 1);
dblSpaceIndex = tmpVal.indexOf(" ");
}
- prop.addProperty(new StringProperty(tmpVal));
+ prop.addProperty(StringProperty.getInstance(tmpVal));
}
}
return prop;
}
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) {
}
- 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;
}
* Constructor for double input
* @param num double numeric value for property
*/
- public NumberProperty(double num) {
+ protected NumberProperty(double num) {
this.number = new Double(num);
}
* 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.
/** @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");
}
/**
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
} // 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
*/
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();
+ }
}
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);
<changes>
<release version="FOP Trunk">
+ <action context="code" dev="AD" type="add" fixes-bug="41044" due-to="Richard Wheeldon">
+ 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
+ </action>
<action context="code" dev="AD" type="update" fixes-bug="41656">
Refactoring in the fo package:
-> removal of the childNodes instance member in fop.fo.FObj