/* * 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.NumberProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * 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; /** * Main constructor * * @param parent the parent node */ 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())) { attributeWarning( "padding-* properties are not applicable to " + getName() + ", but a non-zero value for padding was found."); } } /** * * @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 Table instance * * @return the base table instance * */ public Table getTable() { // Will be 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(); /** * PropertyMaker subclass for the column-number property * */ public static class ColumnNumberPropertyMaker extends NumberProperty.Maker { /** * 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()); } /** * Check the value of the column-number property. * Return the parent's column index (initial value) in case * of a negative or zero value * * @see org.apache.fop.fo.properties.PropertyMaker#make(PropertyList, String, FObj) */ public Property make(PropertyList propertyList, String value, FObj fo) throws PropertyException { Property p = super.make(propertyList, value, fo); ColumnNumberManagerHolder parent = (ColumnNumberManagerHolder) propertyList.getParentFObj(); ColumnNumberManager columnIndexManager = parent.getColumnNumberManager(); int columnIndex = p.getNumeric().getValue(); if (columnIndex <= 0) { log.warn("Specified negative or zero value for " + "column-number on " + fo.getName() + ": " + columnIndex + " forced to " + columnIndexManager.getCurrentColumnNumber()); return NumberProperty.getInstance(columnIndexManager.getCurrentColumnNumber()); } else { double tmpIndex = p.getNumeric().getNumericValue(); if (tmpIndex - columnIndex > 0.0) { columnIndex = (int) Math.round(tmpIndex); log.warn("Rounding specified column-number of " + tmpIndex + " to " + columnIndex); p = NumberProperty.getInstance(columnIndex); } } int colSpan = propertyList.get(Constants.PR_NUMBER_COLUMNS_SPANNED) .getNumeric().getValue(); int i = -1; while (++i < colSpan) { if (columnIndexManager.isColumnNumberUsed(columnIndex + i)) { /* if column-number is already in use by another * cell/column => error! */ StringBuffer errorMessage = new StringBuffer(); errorMessage.append(fo.getName() + " overlaps in column ") .append(columnIndex + i); org.xml.sax.Locator loc = fo.getLocator(); if (loc != null && loc.getLineNumber() != -1) { errorMessage.append(" (line #") .append(loc.getLineNumber()).append(", column #") .append(loc.getColumnNumber()).append(")"); } throw new PropertyException(errorMessage.toString()); } } return p; } } /** {@inheritDoc} */ public void startOfNode() throws FOPException { super.startOfNode(); Table table = getTable(); if (!inMarker() && !table.isSeparateBorderModel()) { collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table .getBorderCollapse()); setCollapsedBorders(); } } /* * TODO made public so that RetrieveMarker can access it. */ /** {@inheritDoc} */ public void endOfNode() throws FOPException { super.endOfNode(); } /** * Prepares the borders of this element if the collapsing-border model is in use. * Conflict resolution with parent elements is done where applicable. */ protected abstract void setCollapsedBorders(); /** * 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 */ protected 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; } } /** * Creates a BorderSpecification from the border set on the given side, performing * conflict resolution with the same border on the given object. * * @param side one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END * @param competitor a parent table element whose side coincides with the given side * on this element */ protected void createBorder(int side, TableFObj competitor) { createBorder(side); switch (side) { case CommonBorderPaddingBackground.BEFORE: borderBefore.integrateSegment(competitor.borderBefore, true, true, true); break; case CommonBorderPaddingBackground.AFTER: borderAfter.integrateSegment(competitor.borderAfter, true, true, true); break; case CommonBorderPaddingBackground.START: borderStart = collapsingBorderModel.determineWinner(borderStart, competitor.borderStart); break; case CommonBorderPaddingBackground.END: borderEnd = collapsingBorderModel.determineWinner(borderEnd, competitor.borderEnd); break; default: assert false; } } }