123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- /*
- * 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;
-
- // Java
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
-
- import org.xml.sax.Attributes;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.xmlgraphics.util.QName;
-
- import org.apache.fop.apps.FOPException;
- import org.apache.fop.apps.FOUserAgent;
- import org.apache.fop.fo.expr.PropertyException;
- import org.apache.fop.fo.properties.CommonAbsolutePosition;
- import org.apache.fop.fo.properties.CommonAural;
- import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
- import org.apache.fop.fo.properties.CommonFont;
- import org.apache.fop.fo.properties.CommonHyphenation;
- import org.apache.fop.fo.properties.CommonMarginBlock;
- import org.apache.fop.fo.properties.CommonMarginInline;
- 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.PropertyMaker;
-
- /**
- * Class containing the collection of properties for a given FObj.
- */
- public abstract class PropertyList {
-
- private static boolean[] inheritableProperty;
-
- static {
- inheritableProperty = new boolean[Constants.PROPERTY_COUNT + 1];
- PropertyMaker maker = null;
- for (int prop = 1; prop <= Constants.PROPERTY_COUNT; prop++) {
- maker = findMaker(prop);
- inheritableProperty[prop] = (maker != null && maker.isInherited());
- }
- }
-
- /** reference to the parent FO's propertyList **/
- protected PropertyList parentPropertyList;
- private FObj fobj;
-
- private static Log log = LogFactory.getLog(PropertyList.class);
-
- private final UnknownPropertyHandler unknownPropertyHandler = new UnknownPropertyHandler();
-
- /**
- * Basic constructor.
- * @param fObjToAttach the FO this PropertyList should be attached to
- * @param parentPropertyList the PropertyList belonging to the new objects
- * parent
- */
- public PropertyList(FObj fObjToAttach, PropertyList parentPropertyList) {
- this.fobj = fObjToAttach;
- this.parentPropertyList = parentPropertyList;
- }
-
- /**
- * @return the FObj object to which this propertyList is attached
- */
- public FObj getFObj() {
- return this.fobj;
- }
-
- /**
- * @return the FObj object attached to the parentPropertyList
- */
- public FObj getParentFObj() {
- if (parentPropertyList != null) {
- return parentPropertyList.getFObj();
- } else {
- return null;
- }
- }
-
- /**
- * Adds an unknown property value to the property list so that if
- * necessary, a warning can be displayed.
- * @param propertyValue The unknown property value
- * @param output The output of the property to validate
- * @param property The original property containing the full value
- */
- public void validatePropertyValue(String propertyValue, Property output, Property property) {
- unknownPropertyHandler.validatePropertyValue(propertyValue, output, property);
- }
-
- /**
- * Gets the current list of unknown property values
- * @return The set containing the list of unknown property values
- */
- public Map<String, Property> getUnknownPropertyValues() {
- return unknownPropertyHandler.getUnknownPropertyValues();
- }
-
- /**
- * @return the FObj object attached to the parentPropetyList
- */
- public PropertyList getParentPropertyList() {
- return parentPropertyList;
- }
-
- /**
- * Return the value explicitly specified on this FO.
- * @param propId The id of the property whose value is desired.
- * @return The value if the property is explicitly set or set by
- * a shorthand property, otherwise null.
- * @throws PropertyException ...
- */
- public Property getExplicitOrShorthand(int propId) throws PropertyException {
- /* Handle request for one part of a compound property */
- Property p = getExplicit(propId);
- if (p == null) {
- p = getShorthand(propId);
- }
- return p;
- }
-
- /**
- * A class to handle unknown shorthand property values e.g. border="solit 1pt"
- */
- private static class UnknownPropertyHandler {
-
- /**
- * A list of unknown properties identified by the value and property in which its featured
- */
- private Map<String, Property> unknownPropertyValues = new HashMap<String, Property>();
-
- /**
- * A list of known properties which have already been processed
- */
- private Set<Property> knownProperties = new HashSet<Property>();
-
- void validatePropertyValue(String propertyValue, Property output, Property property) {
- if (!knownProperties.contains(property) && output == null) {
- if (propertyValue != null) {
- unknownPropertyValues.put(propertyValue, property);
- }
- } else {
- knownProperties.add(property);
- }
- }
-
- Map<String, Property> getUnknownPropertyValues() {
- return unknownPropertyValues;
- }
- }
-
- /**
- * Return the value explicitly specified on this FO.
- * @param propId The ID of the property whose value is desired.
- * @return The value if the property is explicitly set, otherwise null.
- */
- public abstract Property getExplicit(int propId);
-
- /**
- * Set an value defined explicitly on this FO.
- * @param propId The ID of the property to set.
- * @param value The value of the property.
- */
- public abstract void putExplicit(int propId, Property value);
-
- /**
- * Return the value of this property inherited by this FO.
- * Implements the inherited-property-value function.
- * The property must be inheritable!
- * @param propId The ID of the property whose value is desired.
- * @return The inherited value, otherwise null.
- * @throws PropertyException ...
- */
- public Property getInherited(int propId) throws PropertyException {
-
- if (isInherited(propId)) {
- return getFromParent(propId);
- } else {
- // return the "initial" value
- return makeProperty(propId);
- }
- }
-
- /**
- * Return the property on the current FlowObject. If it isn't set explicitly,
- * 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 propId The Constants ID of the property whose value is desired.
- * @return the Property corresponding to that name
- * @throws PropertyException if there is a problem evaluating the property
- */
- public Property get(int propId) throws PropertyException {
- return get(propId, true, true);
- }
-
- /**
- * 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 propId the property's id
- * @param bTryInherit true for inherited properties, or when the inherited
- * value is needed
- * @param bTryDefault true when the default value may be used as a last resort
- * @return the property
- * @throws PropertyException if there is a problem evaluating the property
- */
- public Property get(int propId, boolean bTryInherit,
- boolean bTryDefault) throws PropertyException {
-
- PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
- if (propertyMaker != null) {
- return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
- bTryInherit, bTryDefault);
- }
- return null;
- }
-
- /**
- * Return the "nearest" specified value for the given property.
- * Implements the from-nearest-specified-value function.
- * @param propId The ID of the property whose value is desired.
- * @return The computed value if the property is explicitly set on some
- * ancestor of the current FO, else the initial value.
- * @throws PropertyException if there an error occurred when getting the property
- */
- public Property getNearestSpecified(int propId) throws PropertyException {
- Property p = null;
- PropertyList pList = parentPropertyList;
-
- while (pList != null) {
- p = pList.getExplicit(propId);
- if (p != null) {
- return p;
- } else {
- pList = pList.parentPropertyList;
- }
- }
-
- // If no explicit value found on any of the ancestor-nodes,
- // return initial (default) value.
- return makeProperty(propId);
- }
-
- /**
- * Return the value of this property on the parent of this FO.
- * Implements the from-parent function.
- * @param propId The Constants ID of the property whose value is desired.
- * @return The computed value on the parent or the initial value if this
- * FO is the root or is in a different namespace from its parent.
- * @throws PropertyException ...
- */
- public Property getFromParent(int propId) throws PropertyException {
- if (parentPropertyList != null) {
- return parentPropertyList.get(propId);
- } else {
- return makeProperty(propId);
- }
- }
-
- /**
- * Select a writing mode dependent property ID based on value of writing mode property.
- * @param lrtb the property ID to return under lrtb writingmode.
- * @param rltb the property ID to return under rltb writingmode.
- * @param tbrl the property ID to return under tbrl writingmode.
- * @param tblr the property ID to return under tblr writingmode.
- * @return one of the property IDs, depending on the writing mode.
- */
- public int selectFromWritingMode(int lrtb, int rltb, int tbrl, int tblr) {
- int propID;
- try {
- switch (get(Constants.PR_WRITING_MODE).getEnum()) {
- case Constants.EN_LR_TB:
- propID = lrtb;
- break;
- case Constants.EN_RL_TB:
- propID = rltb;
- break;
- case Constants.EN_TB_RL:
- propID = tbrl;
- break;
- case Constants.EN_TB_LR:
- propID = tblr;
- break;
- default:
- propID = -1;
- break;
- }
- } catch (PropertyException e) {
- propID = -1;
- }
- return propID;
- }
-
- private String addAttributeToList(Attributes attributes,
- String attributeName) throws ValidationException {
- String attributeValue = attributes.getValue(attributeName);
- if (attributeValue != null) {
- convertAttributeToProperty(attributes, attributeName, attributeValue);
- }
- return attributeValue;
- }
-
- /**
- * <p>Adds the attributes, passed in by the parser to the PropertyList.</p>
- * <p>Note that certain attributes are given priority in terms of order of
- * processing due to conversion dependencies, where the order is as follows:</p>
- * <ol>
- * <li>writing-mode</li>
- * <li>column-number</li>
- * <li>number-columns-spanned</li>
- * <li>font</li>
- * <li>font-size</li>
- * <li>all others in order of appearance</li>
- * </ol>
- *
- * @param attributes Collection of attributes passed to us from the parser.
- * @throws ValidationException if there is an attribute that does not
- * map to a property id (strict validation only)
- */
- public void addAttributesToList(Attributes attributes)
- throws ValidationException {
- /*
- * Give writing-mode highest conversion priority.
- */
- addAttributeToList(attributes, "writing-mode");
-
- /*
- * If column-number/number-columns-spanned are specified, then we
- * need them before all others (possible from-table-column() on any
- * other property further in the list...
- */
- addAttributeToList(attributes, "column-number");
- addAttributeToList(attributes, "number-columns-spanned");
-
- /*
- * If font-size is set on this FO, must set it first, since
- * other attributes specified in terms of "ems" depend on it.
- */
- String checkValue = addAttributeToList(attributes, "font");
- if (checkValue == null || "".equals(checkValue)) {
- /*
- * font shorthand wasn't specified, so still need to process
- * explicit font-size
- */
- addAttributeToList(attributes, "font-size");
- }
-
- String attributeNS;
- String attributeName;
- String attributeValue;
- FOUserAgent userAgent = getFObj().getUserAgent();
- for (int i = 0; i < attributes.getLength(); i++) {
- /* convert all attributes with the same namespace as the fo element
- * the "xml:lang" and "xml:base" properties are special cases */
- attributeNS = attributes.getURI(i);
- attributeName = attributes.getQName(i);
- attributeValue = attributes.getValue(i);
- if (attributeNS == null || attributeNS.length() == 0
- || "xml:lang".equals(attributeName)
- || "xml:base".equals(attributeName)) {
- convertAttributeToProperty(attributes, attributeName, attributeValue);
- } else if (!userAgent.isNamespaceIgnored(attributeNS)) {
- ElementMapping mapping = userAgent.getElementMappingRegistry().getElementMapping(
- attributeNS);
- QName attr = new QName(attributeNS, attributeName);
- if (mapping != null) {
- if (mapping.isAttributeProperty(attr)
- && mapping.getStandardPrefix() != null) {
- convertAttributeToProperty(attributes,
- mapping.getStandardPrefix() + ":" + attr.getLocalName(),
- attributeValue);
- } else {
- getFObj().addForeignAttribute(attr, attributeValue);
- }
- } else {
- handleInvalidProperty(attr);
- }
- }
- }
- }
-
- /**
- * Validates a property name.
- * @param propertyName the property name to check
- * @return true if the base property name and the subproperty name (if any)
- * can be correctly mapped to an id
- */
- protected boolean isValidPropertyName(String propertyName) {
-
- int propId = FOPropertyMapping.getPropertyId(
- findBasePropertyName(propertyName));
- int subpropId = FOPropertyMapping.getSubPropertyId(
- findSubPropertyName(propertyName));
-
- return !(propId == -1
- || (subpropId == -1
- && findSubPropertyName(propertyName) != null));
- }
-
- public Property getPropertyForAttribute(Attributes attributes, String attributeName, String attributeValue)
- throws FOPException {
- if (attributeValue != null) {
- if (attributeName.startsWith("xmlns:") || "xmlns".equals(attributeName)) {
- return null;
- }
- String basePropertyName = findBasePropertyName(attributeName);
- String subPropertyName = findSubPropertyName(attributeName);
-
- int propId = FOPropertyMapping.getPropertyId(basePropertyName);
- int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
-
- if (propId == -1 || (subpropId == -1 && subPropertyName != null)) {
- return null;
- }
-
- return getExplicit(propId);
- }
- return null;
- }
-
- /**
- *
- * @param attributes Collection of attributes
- * @param attributeName Attribute name to convert
- * @param attributeValue Attribute value to assign to property
- * @throws ValidationException in case the property name is invalid
- * for the FO namespace
- */
- private void convertAttributeToProperty(Attributes attributes,
- String attributeName,
- String attributeValue)
- throws ValidationException {
-
- if (attributeName.startsWith("xmlns:")
- || "xmlns".equals(attributeName)) {
- /* Ignore namespace declarations if the XML parser/XSLT processor
- * reports them as 'regular' attributes */
- return;
- }
-
- if (attributeValue != null) {
- /* Handle "compound" properties, ex. space-before.minimum */
- String basePropertyName = findBasePropertyName(attributeName);
- String subPropertyName = findSubPropertyName(attributeName);
-
- int propId = FOPropertyMapping.getPropertyId(basePropertyName);
- int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
-
- if (propId == -1
- || (subpropId == -1 && subPropertyName != null)) {
- handleInvalidProperty(new QName(null, attributeName));
- }
- FObj parentFO = fobj.findNearestAncestorFObj();
-
- PropertyMaker propertyMaker = findMaker(propId);
- if (propertyMaker == null) {
- log.warn("No PropertyMaker registered for " + attributeName
- + ". Ignoring property.");
- return;
- }
-
- try {
- Property prop = null;
- if (subPropertyName == null) { // base attribute only found
- /* Do nothing if the base property has already been created.
- * This is e.g. the case when a compound attribute was
- * specified before the base attribute; in these cases
- * the base attribute was already created in
- * findBaseProperty()
- */
- if (getExplicit(propId) != null) {
- return;
- }
- prop = propertyMaker.make(this, attributeValue, parentFO);
- } else { // e.g. "leader-length.maximum"
- Property baseProperty
- = findBaseProperty(attributes, parentFO, propId,
- basePropertyName, propertyMaker);
- prop = propertyMaker.make(baseProperty, subpropId,
- this, attributeValue, parentFO);
- }
- if (prop != null) {
- putExplicit(propId, prop);
- }
- } catch (PropertyException e) {
- fobj.getFOValidationEventProducer().invalidPropertyValue(this, fobj.getName(),
- attributeName, attributeValue, e, fobj.locator);
- }
- }
- }
-
- private Property findBaseProperty(Attributes attributes,
- FObj parentFO,
- int propId,
- String basePropertyName,
- PropertyMaker propertyMaker)
- throws PropertyException {
-
- /* If the baseProperty has already been created, return it
- * e.g. <fo:leader xxxx="120pt" xxxx.maximum="200pt"... />
- */
-
- Property baseProperty = getExplicit(propId);
-
- if (baseProperty != null) {
- return baseProperty;
- }
-
- /* Otherwise If it is specified later in this list of Attributes, create it now
- * e.g. <fo:leader xxxx.maximum="200pt" xxxx="200pt"... />
- */
- String basePropertyValue = attributes.getValue(basePropertyName);
-
- if (basePropertyValue != null && propertyMaker != null) {
- baseProperty = propertyMaker.make(this, basePropertyValue,
- parentFO);
- return baseProperty;
- }
-
- return null; // could not find base property
- }
-
- /**
- * Handles an invalid property.
- * @param attr the invalid attribute
- * @throws ValidationException if an exception needs to be thrown depending on the
- * validation settings
- */
- protected void handleInvalidProperty(QName attr)
- throws ValidationException {
- if (!attr.getQName().startsWith("xmlns")) {
- fobj.getFOValidationEventProducer().invalidProperty(this, fobj.getName(),
- attr, true, fobj.locator);
- }
- }
-
- /**
- * Finds the first or base part (up to any period) of an attribute name.
- * For example, if input is "space-before.minimum", should return
- * "space-before".
- * @param attributeName String to be atomized
- * @return the base portion of the attribute
- */
- protected static String findBasePropertyName(String attributeName) {
- int separatorCharIndex = attributeName.indexOf('.');
- String basePropertyName = attributeName;
- if (separatorCharIndex > -1) {
- basePropertyName = attributeName.substring(0, separatorCharIndex);
- }
- return basePropertyName;
- }
-
- /**
- * Finds the second or sub part (portion past any period) of an attribute
- * name. For example, if input is "space-before.minimum", should return
- * "minimum".
- * @param attributeName String to be atomized
- * @return the sub portion of the attribute
- */
- protected static String findSubPropertyName(String attributeName) {
- int separatorCharIndex = attributeName.indexOf('.');
- String subpropertyName = null;
- if (separatorCharIndex > -1) {
- subpropertyName = attributeName.substring(separatorCharIndex + 1);
- }
- return subpropertyName;
- }
-
- /**
- * @param propId ID of property
- * @return new Property object
- * @throws PropertyException if there's a problem while processing the property
- */
- private Property getShorthand(int propId) throws PropertyException {
- PropertyMaker propertyMaker = findMaker(propId);
-
- if (propertyMaker != null) {
- return propertyMaker.getShorthand(this);
- } else {
- //log.error("no Maker for " + propertyName);
- return null;
- }
- }
-
- /**
- * @param propId ID of property
- * @return new Property object
- * @throws PropertyException if there's a problem while processing the property
- */
- private Property makeProperty(int propId) throws PropertyException {
- PropertyMaker propertyMaker = findMaker(propId);
- if (propertyMaker != null) {
- return propertyMaker.make(this);
- } else {
- //log.error("property " + propertyName
- // + " ignored");
- }
- return null;
- }
-
- /**
- * @param propId ID of property
- * @return isInherited value from the requested Property.Maker
- */
- private boolean isInherited(int propId) {
- return inheritableProperty[propId];
- }
-
- /**
- * @param propId Id of property
- * @return the Property.Maker for this property
- */
- private static PropertyMaker findMaker(int propId) {
- if (propId < 1 || propId > Constants.PROPERTY_COUNT) {
- return null;
- } else {
- return FObj.getPropertyMakerFor(propId);
- }
- }
-
- /**
- * Constructs a BorderAndPadding object.
- * @return a BorderAndPadding object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonBorderPaddingBackground getBorderPaddingBackgroundProps()
- throws PropertyException {
- return CommonBorderPaddingBackground.getInstance(this);
- }
-
- /**
- * Constructs a CommonHyphenation object.
- * @return the CommonHyphenation object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonHyphenation getHyphenationProps() throws PropertyException {
- return CommonHyphenation.getInstance(this);
- }
-
- /**
- * Constructs a CommonMarginBlock object.
- * @return the CommonMarginBlock object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonMarginBlock getMarginBlockProps() throws PropertyException {
- return new CommonMarginBlock(this);
- }
-
- /**
- * Constructs a CommonMarginInline object.
- * @return the CommonMarginInline object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonMarginInline getMarginInlineProps() throws PropertyException {
- return new CommonMarginInline(this);
- }
-
- /**
- * Constructs a CommonAural object.
- * @return the CommonAural object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonAural getAuralProps() throws PropertyException {
- CommonAural props = new CommonAural(this);
- return props;
- }
-
- /**
- * Constructs a RelativePositionProps objects.
- * @return a RelativePositionProps object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonRelativePosition getRelativePositionProps() throws PropertyException {
- return new CommonRelativePosition(this);
- }
-
- /**
- * Constructs a CommonAbsolutePosition object.
- * @return the CommonAbsolutePosition object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonAbsolutePosition getAbsolutePositionProps() throws PropertyException {
- return new CommonAbsolutePosition(this);
- }
-
- /**
- * Constructs a CommonFont object.
- *
- * @return A CommonFont object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonFont getFontProps() throws PropertyException {
- return CommonFont.getInstance(this);
- }
-
- /**
- * Constructs a CommonTextDecoration object.
- * @return a CommonTextDecoration object
- * @throws PropertyException if there's a problem while processing the properties
- */
- public CommonTextDecoration getTextDecorationProps() throws PropertyException {
- return CommonTextDecoration.createFromPropertyList(this);
- }
- }
|