blob: ec508580cc02e3ffa7a06aa7d8d1b69fa870bd3b [file] [log] [blame]
/*
* 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.flow.table;
import org.apache.fop.apps.FOPException;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.ValidationPercentBaseContext;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.EnumNumber;
import org.apache.fop.fo.properties.EnumProperty;
import org.apache.fop.fo.properties.NumberProperty;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.PropertyMaker;
import org.apache.fop.layoutmgr.table.CollapsingBorderModel;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
/**
* Common base class for table-related FOs
*/
public abstract class TableFObj extends FObj {
private Numeric borderAfterPrecedence;
private Numeric borderBeforePrecedence;
private Numeric borderEndPrecedence;
private Numeric borderStartPrecedence;
ConditionalBorder borderBefore;
ConditionalBorder borderAfter;
BorderSpecification borderStart;
BorderSpecification borderEnd;
CollapsingBorderModel collapsingBorderModel;
/**
* Create a TableFObj instance that is a child
* of the given {@link FONode}
*
* @param parent the parent {@link FONode}
*/
public TableFObj(FONode parent) {
super(parent);
}
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
borderAfterPrecedence = pList.get(PR_BORDER_AFTER_PRECEDENCE).getNumeric();
borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric();
borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric();
borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric();
if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java
&& getNameId() != FO_TABLE_CELL
&& getCommonBorderPaddingBackground().hasPadding(
ValidationPercentBaseContext.getPseudoContext())) {
TableEventProducer eventProducer = TableEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.paddingNotApplicable(this, getName(), getLocator());
}
}
/**
* Return the value for the "border-precedence" property
* for the given side.
*
* @param side the side for which to return the border precedence
* @return the "border-precedence" value for the given side
*/
public Numeric getBorderPrecedence(int side) {
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
return borderBeforePrecedence;
case CommonBorderPaddingBackground.AFTER:
return borderAfterPrecedence;
case CommonBorderPaddingBackground.START:
return borderStartPrecedence;
case CommonBorderPaddingBackground.END:
return borderEndPrecedence;
default:
return null;
}
}
/**
* Convenience method to returns a reference
* to the base {@link Table} instance.
*
* @return the base table instance
*
*/
public Table getTable() {
// Overridden in Table; for any other Table-node, recursive call to
// parent.getTable()
return ((TableFObj) parent).getTable();
}
/**
* @return the Common Border, Padding, and Background Properties.
*/
public abstract CommonBorderPaddingBackground getCommonBorderPaddingBackground();
/**
* {@link PropertyMaker} subclass for the column-number property
*/
public static class ColumnNumberPropertyMaker extends PropertyMaker {
/**
* Constructor
*
* @param propId the id of the property for which the maker should
* be created
*/
public ColumnNumberPropertyMaker(int propId) {
super(propId);
}
/** {@inheritDoc} */
public Property make(PropertyList propertyList)
throws PropertyException {
FObj fo = propertyList.getFObj();
return NumberProperty.getInstance(((ColumnNumberManagerHolder) fo.getParent())
.getColumnNumberManager().getCurrentColumnNumber());
}
/**
* {@inheritDoc}
* Check the value of the column-number property.
*/
public Property make(PropertyList propertyList, String value, FObj fo)
throws PropertyException {
Property p = super.make(propertyList, value, fo);
int columnIndex = p.getNumeric().getValue();
int colSpan = propertyList.get(Constants.PR_NUMBER_COLUMNS_SPANNED)
.getNumeric().getValue();
// only check whether the column-number is occupied in case it was
// specified on a fo:table-cell or fo:table-column
int foId = propertyList.getFObj().getNameId();
if (foId == FO_TABLE_COLUMN || foId == FO_TABLE_CELL) {
ColumnNumberManagerHolder parent
= (ColumnNumberManagerHolder) propertyList.getParentFObj();
ColumnNumberManager columnIndexManager = parent.getColumnNumberManager();
int lastIndex = columnIndex - 1 + colSpan;
for (int i = columnIndex; i <= lastIndex; ++i) {
if (columnIndexManager.isColumnNumberUsed(i)) {
/* if column-number is already in use by another
* cell/column => error!
*/
TableEventProducer eventProducer
= TableEventProducer.Provider.get(
fo.getUserAgent().getEventBroadcaster());
eventProducer.cellOverlap(
this, propertyList.getFObj().getName(),
i, propertyList.getFObj().getLocator());
}
}
}
return p;
}
/**
* {@inheritDoc}
* If the value is not positive, return a property whose value
* is the next column number.
*/
public Property convertProperty(Property p,
PropertyList propertyList, FObj fo)
throws PropertyException {
if (p instanceof EnumProperty) {
return EnumNumber.getInstance(p);
}
Number val = p.getNumber();
if (val != null) {
int i = Math.round(val.floatValue());
int foId = propertyList.getFObj().getNameId();
if (i <= 0) {
if (foId == FO_TABLE_CELL || foId == FO_TABLE_COLUMN) {
ColumnNumberManagerHolder parent =
(ColumnNumberManagerHolder) propertyList.getParentFObj();
ColumnNumberManager columnIndexManager = parent.getColumnNumberManager();
i = columnIndexManager.getCurrentColumnNumber();
} else {
/* very exceptional case:
* negative column-number specified on
* a FO that is not a fo:table-cell or fo:table-column
*/
i = 1;
}
TableEventProducer eventProducer =
TableEventProducer.Provider.get(fo.getUserAgent().getEventBroadcaster());
eventProducer.forceNextColumnNumber(this, propertyList.getFObj().getName(),
val, i, propertyList.getFObj().getLocator());
}
return NumberProperty.getInstance(i);
}
return convertPropertyDatatype(p, propertyList, fo);
}
}
/** {@inheritDoc} */
public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList pList) throws FOPException {
super.processNode(elementName, locator, attlist, pList);
Table table = getTable();
if (!inMarker() && !table.isSeparateBorderModel()) {
collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table
.getBorderCollapse());
setCollapsedBorders();
}
}
/**
* Prepares the borders of this element if the collapsing-border model is in use.
* Conflict resolution with parent elements is done where applicable.
*/
protected void setCollapsedBorders() {
createBorder(CommonBorderPaddingBackground.START);
createBorder(CommonBorderPaddingBackground.END);
createBorder(CommonBorderPaddingBackground.BEFORE);
createBorder(CommonBorderPaddingBackground.AFTER);
}
/**
* Creates a BorderSpecification from the border set on the given side. If no border
* is set, a BorderSpecification with border-style none is created.
*
* @param side one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END
*/
private void createBorder(int side) {
BorderSpecification borderSpec = new BorderSpecification(
getCommonBorderPaddingBackground().getBorderInfo(side), getNameId());
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore = new ConditionalBorder(borderSpec, collapsingBorderModel);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter = new ConditionalBorder(borderSpec, collapsingBorderModel);
break;
case CommonBorderPaddingBackground.START:
borderStart = borderSpec;
break;
case CommonBorderPaddingBackground.END:
borderEnd = borderSpec;
break;
default: assert false;
}
}
}