org.apache.fop.fo.properties.PropertyMaker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.fop Show documentation
Show all versions of org.apache.fop Show documentation
The core maven build properties
The newest version!
/*
* 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: PropertyMaker.java 1891499 2021-07-13 06:47:09Z ssteiner $ */
package org.apache.fop.fo.properties;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.datatypes.CompoundDatatype;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FOPropertyMapping;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.expr.PropertyInfo;
import org.apache.fop.fo.expr.PropertyParser;
/**
* Base class for all property makers
*/
public class PropertyMaker implements Cloneable {
/** Logger instance */
private static final Log LOG = LogFactory.getLog(PropertyMaker.class);
private static final boolean IS_LOG_TRACE_ENABLED = LOG.isTraceEnabled();
/** the property ID */
protected int propId;
private boolean inherited = true;
private Map enums;
private Map keywords;
/** the default value for the maker */
protected String defaultValue;
/** Indicates whether the property is context-dependant and therefore can't be cached. */
protected boolean contextDep;
/** Indicates whether the property is set through a shorthand. */
protected boolean setByShorthand;
private int percentBase = -1;
private PropertyMaker[] shorthands;
private ShorthandParser datatypeParser;
/** default property **/
protected Property defaultProperty;
/** Maker for 'corresponding' properties **/
protected CorrespondingPropertyMaker corresponding;
/**
* @return the name of the property for this Maker
*/
public int getPropId() {
return propId;
}
/**
* Construct an instance of a Property.Maker for the given property.
* @param propId The Constant ID of the property to be made.
*/
public PropertyMaker(int propId) {
this.propId = propId;
}
/**
* Copy all the values from the generic maker to this maker.
* @param generic a generic property maker.
*/
public void useGeneric(PropertyMaker generic) {
contextDep = generic.contextDep;
inherited = generic.inherited;
defaultValue = generic.defaultValue;
percentBase = generic.percentBase;
if (generic.shorthands != null) {
shorthands = new PropertyMaker[generic.shorthands.length];
System.arraycopy(generic.shorthands, 0, shorthands, 0, shorthands.length);
}
if (generic.enums != null) {
enums = new HashMap(generic.enums);
}
if (generic.keywords != null) {
keywords = new HashMap(generic.keywords);
}
}
/**
* Set the inherited flag.
* @param inherited true if this is an inherited property
*/
public void setInherited(boolean inherited) {
this.inherited = inherited;
}
/**
* Add a keyword-equiv to the maker.
* @param keyword the keyword
* @param value the value to be used when the keyword is specified
*/
public void addKeyword(String keyword, String value) {
if (keywords == null) {
keywords = new HashMap();
}
keywords.put(keyword, value);
}
/**
* Add a enum constant.
* @param constant the enum constant
* @param value the Property value to use when the constant is specified
*/
public void addEnum(String constant, Property value) {
if (enums == null) {
enums = new HashMap();
}
enums.put(constant, value);
}
/**
* Add a subproperty to this maker.
* @param subproperty the PropertyMaker for the subproperty
*/
public void addSubpropMaker(PropertyMaker subproperty) {
throw new RuntimeException("Unable to add subproperties " + getClass());
}
/**
* Return a subproperty maker for the subpropertyId.
* @param subpropertyId The subpropertyId of the maker.
* @return The subproperty maker.
*/
public PropertyMaker getSubpropMaker(int subpropertyId) {
throw new RuntimeException("Unable to add subproperties");
}
/**
* Add a shorthand to this maker. Only an Integer is added to the
* shorthands list. Later the Integers are replaced with references
* to the actual shorthand property makers.
* @param shorthand a property maker thar is that is checked for
* shorthand values.
*/
public void addShorthand(PropertyMaker shorthand) {
if (shorthands == null) {
shorthands = new PropertyMaker[3];
}
for (int i = 0; i < shorthands.length; i++) {
if (shorthands[i] == null) {
shorthands[i] = shorthand;
break;
}
}
}
/**
* Set the shorthand datatype parser.
* @param parser the shorthand parser
*/
public void setDatatypeParser(ShorthandParser parser) {
datatypeParser = parser;
}
/**
* Set the default value for this maker.
* @param defaultValue the default value.
*/
public void setDefault(String defaultValue) {
this.defaultValue = defaultValue;
}
/**
* Set the default value for this maker.
* @param defaultValue the default value
* @param contextDep true when the value context dependent and
* must not be cached.
*/
public void setDefault(String defaultValue, boolean contextDep) {
this.defaultValue = defaultValue;
this.contextDep = contextDep;
}
/**
* Set the percent base identifier for this maker.
* @param percentBase the percent base (ex. LengthBase.FONTSIZE)
*/
public void setPercentBase(int percentBase) {
this.percentBase = percentBase;
}
/**
* Set the setByShorthand flag which only is applicable for subproperty
* makers. It should be true for the subproperties which must be
* assigned a value when the base property is assigned a attribute
* value directly.
* @param setByShorthand true if this subproperty must be set when the base property is set
*/
public void setByShorthand(boolean setByShorthand) {
this.setByShorthand = setByShorthand;
}
/**
* Set the correspoding property information.
* @param corresponding a corresponding maker where the
* isForcedCorresponding and compute methods are delegated to.
*/
public void setCorresponding(CorrespondingPropertyMaker corresponding) {
this.corresponding = corresponding;
}
/**
* Create a new empty property. Must be overriden in compound
* subclasses.
* @return a new instance of the Property for which this is a maker.
*/
public Property makeNewProperty() {
return null;
}
/**
* If the property is a relative property with a corresponding absolute
* value specified, the absolute value is used. This is also true of
* the inheritance priority (I think...)
* If the property is an "absolute" property and it isn't specified, then
* we try to compute it from the corresponding relative property: this
* happens in computeProperty.
* @param propertyList the applicable property list
* @param tryInherit true if inherited properties should be examined.
* @return the property value
* @throws PropertyException if there is a problem evaluating the property
*/
public Property findProperty(PropertyList propertyList,
boolean tryInherit)
throws PropertyException {
Property p = null;
if (IS_LOG_TRACE_ENABLED) {
LOG.trace("PropertyMaker.findProperty: "
+ FOPropertyMapping.getPropertyName(propId)
+ ", " + propertyList.getFObj().getName());
}
if (corresponding != null && corresponding.isCorrespondingForced(propertyList)) {
p = corresponding.compute(propertyList);
} else {
p = propertyList.getExplicit(propId);
if (p == null) { // check for shorthand specification
p = getShorthand(propertyList);
}
if (p == null) {
p = this.compute(propertyList);
}
}
if (p == null && tryInherit) {
// else inherit (if has parent and is inheritable)
PropertyList parentPropertyList = propertyList.getParentPropertyList();
if (parentPropertyList != null && isInherited()) {
p = parentPropertyList.get(propId, true, false);
}
}
return p;
}
/**
* Return the property on the current FlowObject. Depending on the passed flags,
* this will try to compute it based on other properties, or if it is
* inheritable, to return the inherited value. If all else fails, it returns
* the default value.
* @param subpropertyId The subproperty id of the property being retrieved.
* Is 0 when retrieving a base property.
* @param propertyList The PropertyList object being built for this FO.
* @param tryInherit true if inherited properties should be examined.
* @param tryDefault true if the default value should be returned.
* @return the property value
* @throws PropertyException if there is a problem evaluating the property
*/
public Property get(int subpropertyId, PropertyList propertyList,
boolean tryInherit, boolean tryDefault)
throws PropertyException {
Property p = findProperty(propertyList, tryInherit);
if (p == null && tryDefault) { // default value for this FO!
p = make(propertyList);
}
return p;
}
/**
* Default implementation of isInherited.
* @return A boolean indicating whether this property is inherited.
*/
public boolean isInherited() {
return inherited;
}
/**
* This is used to handle properties specified as a percentage of
* some "base length", such as the content width of their containing
* box.
* Overridden by subclasses which allow percent specifications. See
* the documentation on properties.xsl for details.
* @param pl the PropertyList containing the property. (TODO: explain
* what this is used for, or remove it from the signature.)
* @return an object implementing the PercentBase interface.
* @throws PropertyException if there is a problem while evaluating the base property
*/
public PercentBase getPercentBase(PropertyList pl) throws PropertyException {
if (percentBase == -1) {
return null;
} else {
return new LengthBase(pl, percentBase);
}
}
/**
* Return a property value for the given component of a compound
* property.
* @param p A property value for a compound property type such as
* SpaceProperty.
* @param subpropertyId the id of the component whose value is to be
* returned.
* NOTE: this is only to ease porting when calls are made to
* PropertyList.get() using a component name of a compound property,
* such as get("space.optimum"). The recommended technique is:
* get("space").getOptimum().
* Overridden by property maker subclasses which handle
* compound properties.
* @return the Property containing the subproperty
*/
public Property getSubprop(Property p, int subpropertyId) {
CompoundDatatype val = (CompoundDatatype) p.getObject();
return val.getComponent(subpropertyId);
}
/**
* Set a component in a compound property and return the modified
* compound property object.
* This default implementation returns the original base property
* without modifying it.
* It is overridden by property maker subclasses which handle
* compound properties.
* @param baseProperty The Property object representing the compound property,
* such as SpaceProperty.
* @param subpropertyId The ID of the component whose value is specified.
* @param subproperty A Property object holding the specified value of the
* component to be set.
* @return The modified compound property object.
*/
protected Property setSubprop(Property baseProperty, int subpropertyId,
Property subproperty) {
CompoundDatatype val = (CompoundDatatype) baseProperty.getObject();
val.setComponent(subpropertyId, subproperty, false);
return baseProperty;
}
/**
* Return the default value.
* @param propertyList The PropertyList object being built for this FO.
* @return the Property object corresponding to the parameters
* @throws PropertyException for invalid or inconsisten FO input
*/
public Property make(PropertyList propertyList) throws PropertyException {
if (defaultProperty != null) {
if (IS_LOG_TRACE_ENABLED) {
LOG.trace("PropertyMaker.make: reusing defaultProperty, "
+ FOPropertyMapping.getPropertyName(propId));
}
return defaultProperty;
}
if (IS_LOG_TRACE_ENABLED) {
LOG.trace("PropertyMaker.make: making default property value, "
+ FOPropertyMapping.getPropertyName(propId)
+ ", " + propertyList.getFObj().getName());
}
Property p = make(propertyList, defaultValue, propertyList.getParentFObj());
if (!contextDep) {
defaultProperty = p;
}
return p;
}
/**
* Create a Property object from an attribute specification.
* @param propertyList The PropertyList object being built for this FO.
* @param value The attribute value.
* @param fo The parent FO for the FO whose property is being made.
* @return The initialized Property object.
* @throws PropertyException for invalid or inconsistent FO input
*/
public Property make(PropertyList propertyList, String value,
FObj fo) throws PropertyException {
try {
Property newProp = null;
String pvalue = value;
if ("inherit".equals(value)) {
newProp = propertyList.getFromParent(this.propId & Constants.PROPERTY_MASK);
if ((propId & Constants.COMPOUND_MASK) != 0) {
newProp = getSubprop(newProp, propId & Constants.COMPOUND_MASK);
}
if (!isInherited() && LOG.isWarnEnabled()) {
/* check whether explicit value is available on the parent
* (for inherited properties, an inherited value will always
* be available)
*/
Property parentExplicit = propertyList.getParentPropertyList()
.getExplicit(getPropId());
if (parentExplicit == null) {
LOG.warn(FOPropertyMapping.getPropertyName(getPropId())
+ "=\"inherit\" on " + propertyList.getFObj().getName()
+ ", but no explicit value found on the parent FO.");
}
}
} else {
// Check for keyword shorthand values to be substituted.
pvalue = checkValueKeywords(pvalue.trim());
newProp = checkEnumValues(pvalue);
}
if (newProp == null) {
// Override parsePropertyValue in each subclass of Property.Maker
newProp = PropertyParser.parse(pvalue,
new PropertyInfo(this,
propertyList));
}
if (newProp != null) {
newProp = convertProperty(newProp, propertyList, fo);
}
if (newProp == null) {
throw new PropertyException("No conversion defined " + pvalue);
}
return newProp;
} catch (PropertyException propEx) {
if (fo != null) {
propEx.setLocator(fo.getLocator());
}
propEx.setPropertyName(getName());
throw propEx;
}
}
/**
* Make a property value for a compound property. If the property
* value is already partially initialized, this method will modify it.
* @param baseProperty The Property object representing the compound property,
* for example: SpaceProperty.
* @param subpropertyId The Constants ID of the subproperty (component)
* whose value is specified.
* @param propertyList The propertyList being built.
* @param fo The parent FO for the FO whose property is being made.
* @param value the value of the
* @return baseProperty (or if null, a new compound property object) with
* the new subproperty added
* @throws PropertyException for invalid or inconsistent FO input
*/
public Property make(Property baseProperty, int subpropertyId,
PropertyList propertyList, String value,
FObj fo) throws PropertyException {
//getLogger().error("compound property component "
// + partName + " unknown.");
return baseProperty;
}
/**
* Converts a shorthand property
*
* @param propertyList the propertyList for which to convert
* @param prop the shorthand property
* @param fo ...
* @return the converted property
* @throws PropertyException ...
*/
public Property convertShorthandProperty(PropertyList propertyList,
Property prop, FObj fo)
throws PropertyException {
Property pret = convertProperty(prop, propertyList, fo);
if (pret == null) {
// If value is a name token, may be keyword or Enum
String sval = prop.getNCname();
if (sval != null) {
//log.debug("Convert shorthand ncname " + sval);
pret = checkEnumValues(sval);
if (pret == null) {
/* Check for keyword shorthand values to be substituted. */
String pvalue = checkValueKeywords(sval);
if (!pvalue.equals(sval)) {
//log.debug("Convert shorthand keyword" + pvalue);
// Substituted a value: must parse it
Property p = PropertyParser.parse(pvalue,
new PropertyInfo(this,
propertyList));
pret = convertProperty(p, propertyList, fo);
}
}
}
}
return pret;
}
/**
* For properties that contain enumerated values.
* This method should be overridden by subclasses.
* @param value the string containing the property value
* @return the Property encapsulating the enumerated equivalent of the
* input value
*/
protected Property checkEnumValues(String value) {
if (enums != null) {
Property p = (Property) enums.get(value);
return p;
}
return null;
}
/**
* Return a String to be parsed if the passed value corresponds to
* a keyword which can be parsed and used to initialize the property.
* For example, the border-width family of properties can have the
* initializers "thin", "medium", or "thick". The FOPropertyMapping
* file specifies a length value equivalent for these keywords,
* such as "0.5pt" for "thin".
* @param keyword the string value of property attribute.
* @return a String containing a parseable equivalent or null if
* the passed value isn't a keyword initializer for this Property
*/
public String checkValueKeywords(String keyword) {
if (keywords != null) {
String value = (String)keywords.get(keyword);
if (value != null) {
return value;
}
}
// TODO: should return null here?
return keyword;
}
/**
* Return a Property object based on the passed Property object.
* This method is called if the Property object built by the parser
* isn't the right type for this property.
* It is overridden by subclasses.
* @param p The Property object return by the expression parser
* @param propertyList The PropertyList object being built for this FO.
* @param fo The parent FO for the FO whose property is being made.
* @return A Property of the correct type or null if the parsed value
* can't be converted to the correct type.
* @throws PropertyException for invalid or inconsistent FO input
*/
protected Property convertProperty(Property p,
PropertyList propertyList,
FObj fo) throws PropertyException {
return null;
}
/**
* For properties that have more than one legal way to be specified,
* this routine should be overridden to attempt to set them based upon
* the other methods. For example, colors may be specified using an RGB
* model, or they may be specified using an NCname.
* @param p property whose datatype should be converted
* @param propertyList collection of properties. (TODO: explain why
* this is needed, or remove it from the signature.)
* @param fo The parent FO for the FO whose property is being made.
* why this is needed, or remove it from the signature).
* @return an Property with the appropriate datatype used
* @throws PropertyException for invalid or inconsistent input
*/
protected Property convertPropertyDatatype(Property p,
PropertyList propertyList,
FObj fo) throws PropertyException {
return null;
}
/**
* Return a Property object representing the value of this property,
* based on other property values for this FO.
* A special case is properties which inherit the specified value,
* rather than the computed value.
* @param propertyList The PropertyList for the FO.
* @return Property A computed Property value or null if no rules
* are specified to compute the value.
* @throws PropertyException for invalid or inconsistent FO input
*/
protected Property compute(PropertyList propertyList)
throws PropertyException {
if (corresponding != null) {
return corresponding.compute(propertyList);
}
return null; // standard
}
/**
* For properties that can be set by shorthand properties, this method
* should return the Property, if any, that is parsed from any
* shorthand properties that affect this property.
* This method expects to be overridden by subclasses.
* For example, the border-right-width property could be set implicitly
* from the border shorthand property, the border-width shorthand
* property, or the border-right shorthand property. This method should
* be overridden in the appropriate subclass to check each of these, and
* return an appropriate border-right-width Property object.
* @param propertyList the collection of properties to be considered
* @return the Property, if found, the correspons, otherwise, null
* @throws PropertyException if there is a problem while evaluating the shorthand
*/
public Property getShorthand(PropertyList propertyList)
throws PropertyException {
if (shorthands == null) {
return null;
}
Property prop;
int n = shorthands.length;
for (int i = 0; i < n && shorthands[i] != null; i++) {
PropertyMaker shorthand = shorthands[i];
prop = propertyList.getExplicit(shorthand.propId);
if (prop != null) {
ShorthandParser parser = shorthand.datatypeParser;
Property p = parser.getValueForProperty(getPropId(),
prop, this, propertyList);
if (p != null) {
return p;
}
}
}
return null;
}
/** @return the name of the property this maker is used for. */
public String getName() {
return FOPropertyMapping.getPropertyName(propId);
}
/**
* Return a clone of the makers. Used by useGeneric() to clone the
* subproperty makers of the generic compound makers.
* {@inheritDoc}
*/
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException exc) {
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy