/* * 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; import java.util.Iterator; import org.xml.sax.Locator; import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObj; import org.apache.fop.fo.FObjMixed; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.XMLObj; import org.apache.fop.fo.flow.table.Table; /** * Abstract base class for the <a href="http://www.w3.org/TR/xsl/#fo_retrieve-marker"> * <code>fo:retrieve-marker</code></a> and * <a href="http://www.w3.org/TR/xsl/#fo_retrieve-table-marker"> * <code>fo:retrieve-table-marker</code></a> formatting objects. */ public abstract class AbstractRetrieveMarker extends FObjMixed { private PropertyList propertyList; private String retrieveClassName; private int position; private String positionLabel; private int boundary; private String boundaryLabel; private StructureTreeElement structureTreeElement; /** * Create a new AbstractRetrieveMarker instance that * is a child of the given {@link FONode} * * @param parent the parent {@link FONode} */ public AbstractRetrieveMarker(FONode parent) { super(parent); } /** * {@inheritDoc} * <p>XSL Content Model: empty */ protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { invalidChildError(loc, nsURI, localName); } } /** * {@inheritDoc} * Store a reference to the parent {@link PropertyList} * to be used when the retrieve-marker is resolved. */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); this.retrieveClassName = pList.get(PR_RETRIEVE_CLASS_NAME).getString(); if (retrieveClassName == null || retrieveClassName.equals("")) { missingPropertyError("retrieve-class-name"); } this.propertyList = pList.getParentPropertyList(); } @Override public void setStructureTreeElement(StructureTreeElement structureTreeElement) { this.structureTreeElement = structureTreeElement; } @Override public StructureTreeElement getStructureTreeElement() { return structureTreeElement; } private PropertyList createPropertyListFor(FObj fo, PropertyList parent) { return getBuilderContext().getPropertyListMaker().make(fo, parent); } private void cloneSingleNode(FONode child, FONode newParent, Marker marker, PropertyList parentPropertyList) throws FOPException { if (child != null) { FONode newChild = child.clone(newParent, true); if (child instanceof FObj) { Marker.MarkerPropertyList pList; PropertyList newPropertyList = createPropertyListFor( (FObj) newChild, parentPropertyList); pList = marker.getPropertyListFor(child); newChild.processNode( child.getLocalName(), getLocator(), pList, newPropertyList); addChildTo(newChild, newParent); newChild.startOfNode(); switch (newChild.getNameId()) { case FO_TABLE: Table t = (Table) child; cloneSubtree(t.getColumns().iterator(), newChild, marker, newPropertyList); cloneSingleNode(t.getTableHeader(), newChild, marker, newPropertyList); cloneSingleNode(t.getTableFooter(), newChild, marker, newPropertyList); cloneSubtree(child.getChildNodes(), newChild, marker, newPropertyList); break; case FO_LIST_ITEM: ListItem li = (ListItem) child; cloneSingleNode(li.getLabel(), newChild, marker, newPropertyList); cloneSingleNode(li.getBody(), newChild, marker, newPropertyList); break; default: cloneSubtree(child.getChildNodes(), newChild, marker, newPropertyList); break; } newChild.endOfNode(); } else if (child instanceof FOText) { FOText ft = (FOText) newChild; ft.bind(parentPropertyList); addChildTo(newChild, newParent); if (newParent instanceof AbstractRetrieveMarker) { /* * Otherwise the parent of newChild is a cloned FObjMixed that will * call this FOText's endOfNode when its own endOfNode method is * called. */ newChild.endOfNode(); } } else if (child instanceof XMLObj) { addChildTo(newChild, newParent); } } } /** * Clone the FO nodes in the parent iterator, * attach the new nodes to the new parent, * and map the new nodes to the existing property lists. * FOText nodes are also in the new map, with a null value. * Clone the subtree by a recursive call to this method. * @param parentIter the iterator over the children of the old parent * @param newParent the new parent for the cloned nodes * @param marker the marker that contains the old property list mapping * @param parentPropertyList the parent PropertyList * @throws FOPException in case there was an error */ private void cloneSubtree(Iterator parentIter, FONode newParent, Marker marker, PropertyList parentPropertyList) throws FOPException { if (parentIter != null) { FONode child; while (parentIter.hasNext()) { child = (FONode) parentIter.next(); cloneSingleNode(child, newParent, marker, parentPropertyList); } } } private void cloneFromMarker(Marker marker) throws FOPException { cloneSubtree(marker.getChildNodes(), this, marker, propertyList); handleWhiteSpaceFor(this, null); } /** * Clone the subtree of the given marker * * @param marker the marker that is to be cloned */ public void bindMarker(Marker marker) { // clean up remnants from a possible earlier layout if (firstChild != null) { currentTextNode = null; firstChild = null; } if (marker.getChildNodes() != null) { try { restoreFOEventHandlerState(); cloneFromMarker(marker); } catch (FOPException exc) { getFOValidationEventProducer().markerCloningFailed(this, marker.getMarkerClassName(), exc, getLocator()); } } else if (log.isDebugEnabled()) { log.debug("Empty marker retrieved..."); } } protected abstract void restoreFOEventHandlerState(); /** * Return the value for the <code>retrieve-class-name</code> * property * * @return the value for retrieve-class-name */ public String getRetrieveClassName() { return this.retrieveClassName; } protected void setBoundaryLabel(String label) { this.boundaryLabel = label; } protected void setPositionLabel(String label) { this.positionLabel = label; } public String getBoundaryLabel() { return this.boundaryLabel; } public String getPositionLabel() { return this.positionLabel; } protected void setPosition(int position) { this.position = position; } protected void setBoundary(int boundary) { this.boundary = boundary; } public int getPosition() { return this.position; } public int getBoundary() { return this.boundary; } public abstract String getLocalName(); public abstract int getNameId(); public void changePositionTo(int position) { this.position = position; } }