org.apache.fop.fo.FONode Maven / Gradle / Ivy
/*
* 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: FONode.java 1835810 2018-07-13 10:29:57Z ssteiner $ */
package org.apache.fop.fo;
// Java
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.helpers.LocatorImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.util.QName;
import org.apache.fop.accessibility.StructureTreeElement;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.complexscripts.bidi.DelimitedTextRange;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.fo.extensions.svg.SVGElementMapping;
import org.apache.fop.fo.flow.ChangeBar;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.util.CharUtilities;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.text.AdvancedMessageFormat.Function;
/**
* Base class for nodes in the XML tree
*/
public abstract class FONode implements Cloneable {
/** the XSL-FO namespace URI */
protected static final String FO_URI = FOElementMapping.URI;
/** FOP's proprietary extension namespace URI */
protected static final String FOX_URI = ExtensionElementMapping.URI;
/** Parent FO node */
protected FONode parent;
/** pointer to the sibling nodes */
protected FONode[] siblings;
/** The list of active change bars for the given node */
protected List nodeChangeBarList;
/** The list of active change bars for the start of the given node */
protected List startOfNodeChangeBarList;
/**
* Marks the location of this object from the input FO
*
Call locator.getSystemId()
,
* getLineNumber()
,
* getColumnNumber()
for file, line, column
* information
*/
protected Locator locator;
/** Logger for fo-tree related messages **/
protected static final Log log = LogFactory.getLog(FONode.class);
/**
* Base constructor
*
* @param parent parent of this node
*/
protected FONode(FONode parent) {
this.parent = parent;
}
/**
* Performs a shallow cloning operation, sets the clone's parent,
* and optionally cleans the list of child nodes
*
* @param cloneparent the intended parent of the clone
* @param removeChildren if true, clean the list of child nodes
* @return the cloned FO node
* @throws FOPException if there's a problem while cloning the node
*/
public FONode clone(FONode cloneparent, boolean removeChildren)
throws FOPException {
FONode foNode = (FONode) clone();
foNode.parent = cloneparent;
foNode.siblings = null;
return foNode;
}
/** {@inheritDoc} */
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen
}
}
/**
* Bind the given PropertyList
to this node
* Does nothing by default. Subclasses should override this method
* in case they want to use the properties available on the
* PropertyList
.
*
* @param propertyList the PropertyList
* @throws FOPException if there was an error when
* processing the PropertyList
*/
public void bind(PropertyList propertyList) throws FOPException {
//nop
}
/**
* Set the location information for this element
* @param locator the org.xml.sax.Locator object
*/
public void setLocator(Locator locator) {
if (locator != null) {
//Create a copy of the locator so the info is preserved when we need to
//give pointers during layout.
this.locator = new LocatorImpl(locator);
}
}
/**
* Returns the Locator
containing the location information for this
* element, or null
if not available
*
* @return the location information for this element or null
, if not available
*/
public Locator getLocator() {
return this.locator;
}
/**
* Recursively goes up the FOTree hierarchy until the fo:root
* is found, which returns the parent FOEventHandler
.
*
(see also: {@link org.apache.fop.fo.pagination.Root#getFOEventHandler()})
*
* @return the FOEventHandler object that is the parent of the FO Tree
*/
public FOEventHandler getFOEventHandler() {
return parent.getFOEventHandler();
}
/**
* Returns the context class providing information used during FO tree building.
* @return the builder context
*/
public FOTreeBuilderContext getBuilderContext() {
return parent.getBuilderContext();
}
/**
* Indicates whether this node is a child of an fo:marker.
* @return true if this node is a child of an fo:marker
*/
protected boolean inMarker() {
return getBuilderContext().inMarker();
}
/**
* Tests if the given element is a change bar element.
*
* @param namespaceURI The name space of the element
* @param localName The local name of the element
* @return A boolean value true if the given element is a change bar element
*/
public boolean isChangeBarElement(String namespaceURI, String localName) {
return FO_URI.equals(namespaceURI)
&& (localName.equals("change-bar-begin") || localName.equals("change-bar-end"));
}
/**
* Returns the user agent that is associated with the
* tree's FOEventHandler
.
*
* @return the user agent
*/
public FOUserAgent getUserAgent() {
return getFOEventHandler().getUserAgent();
}
/**
* Returns the logger for the node.
*
* @return the logger
*/
public Log getLogger() {
return log;
}
/**
* Initialize the node with its name, location information, and attributes
* The attributes must be used immediately as the sax attributes
* will be altered for the next element.
*
* @param elementName element name (e.g., "fo:block")
* @param locator Locator object (ignored by default)
* @param attlist Collection of attributes passed to us from the parser.
* @param pList the property list of the parent node
* @throws FOPException for errors or inconsistencies in the attributes
*/
public void processNode(String elementName, Locator locator,
Attributes attlist, PropertyList pList) throws FOPException {
if (log.isDebugEnabled()) {
log.debug("Unhandled element: " + elementName
+ (locator != null ? " at " + getLocatorString(locator) : ""));
}
}
/**
* Create a property list for this node. Return null if the node does not
* need a property list.
*
* @param pList the closest parent propertylist.
* @param foEventHandler The FOEventHandler where the PropertyListMaker
* instance can be found.
* @return A new property list.
* @throws FOPException if there's a problem during processing
*/
protected PropertyList createPropertyList(
PropertyList pList,
FOEventHandler foEventHandler)
throws FOPException {
return null;
}
/**
* Checks to make sure, during SAX processing of input document, that the
* incoming node is valid for this (parent) node (e.g., checking to
* see that fo:table
is not an immediate child of fo:root
)
* called from {@link FOTreeBuilder#startElement(String, String, String, Attributes)}
* before constructing the child {@link FObj}.
*
* @param loc location in the FO source file
* @param namespaceURI namespace of incoming node
* @param localName name of the incoming node (without namespace prefix)
* @throws ValidationException if incoming node not valid for parent
*/
protected void validateChildNode(
Locator loc,
String namespaceURI,
String localName)
throws ValidationException {
//nop
}
/**
* Static version of {@link FONode#validateChildNode(Locator, String, String)} that
* can be used by subclasses that need to validate children against a different node
* (for example: fo:wrapper
needs to check if the incoming node is a
* valid child to its parent)
*
* @param fo the {@link FONode} to validate against
* @param loc location in the source file
* @param namespaceURI namespace of the incoming node
* @param localName name of the incoming node (without namespace prefix)
* @throws ValidationException if the incoming node is not a valid child for the given FO
*/
protected static void validateChildNode(
FONode fo,
Locator loc,
String namespaceURI,
String localName)
throws ValidationException {
fo.validateChildNode(loc, namespaceURI, localName);
}
/**
* Adds characters. Does nothing by default. To be overridden in subclasses
* that allow #PCDATA
content.
*
* @param data array of characters containing text to be added
* @param start starting array element to add
* @param end ending array element to add
* @param pList currently applicable PropertyList
* @param locator location in the XSL-FO source file.
* @throws FOPException if there's a problem during processing
* @deprecated Please override {@link #characters(char[], int, int, PropertyList, Locator)}
* instead!
*/
protected void addCharacters(char[] data, int start, int end,
PropertyList pList,
Locator locator) throws FOPException {
// ignore
}
/**
* Adds characters. Does nothing by default. To be overridden in subclasses
* that allow #PCDATA
content.
*
* @param data array of characters containing text to be added
* @param start starting array element to add
* @param length number of elements to add
* @param pList currently applicable PropertyList
* @param locator location in the XSL-FO source file.
* @throws FOPException if there's a problem during processing
*/
protected void characters(char[] data, int start, int length,
PropertyList pList,
Locator locator) throws FOPException {
addCharacters(data, start, start + length, pList, locator);
}
/**
* Called after processNode() is called. Subclasses can do additional processing.
*
* @throws FOPException if there's a problem during processing
*/
public void startOfNode() throws FOPException {
// do nothing by default
}
/**
* Primarily used for making final content model validation checks
* and/or informing the {@link FOEventHandler} that the end of this FO
* has been reached.
* The default implementation simply calls {@link #finalizeNode()}, without
* sending any event to the {@link FOEventHandler}.
* Note: the recommended way to override this method in subclasses is
* super.endOfNode(); // invoke finalizeNode()
* getFOEventHandler().endXXX(); // send endOfNode() notification
*
* @throws FOPException if there's a problem during processing
*/
public void endOfNode() throws FOPException {
this.finalizeNode();
}
/**
* Adds a node as a child of this node. The default implementation of this method
* just ignores any child node being added.
*
* @param child child node to be added to the childNodes of this node
* @throws FOPException if there's a problem during processing
*/
protected void addChildNode(FONode child) throws FOPException {
// do nothing by default
}
/**
* Removes a child node. Used by the child nodes to remove themselves, for
* example table-body if it has no children.
*
* @param child child node to be removed
*/
public void removeChild(FONode child) {
//nop
}
/**
* Finalize this node.
* This method can be overridden by subclasses to perform finishing
* tasks (cleanup, validation checks, ...) without triggering
* endXXX() events in the {@link FOEventHandler}.
* The method is called by the default {@link #endOfNode()}
* implementation.
*
* @throws FOPException in case there was an error
*/
public void finalizeNode() throws FOPException {
// do nothing by default
}
/**
* Return the parent node of this node
*
* @return the parent node of this node
*/
public FONode getParent() {
return this.parent;
}
/**
* Return an iterator over all the child nodes of this node.
*
* @return the iterator over the FO's childnodes
*/
public FONodeIterator getChildNodes() {
return null;
}
/**
* Return an iterator over the object's child nodes starting
* at the passed node.
*
* @param childNode First node in the iterator
* @return the iterator, or null
if
* the given node is not a child of this node.
*/
public FONodeIterator getChildNodes(FONode childNode) {
return null;
}
/**
* Return a {@link CharIterator} over all characters in this node
*
* @return an iterator for the characters in this node
*/
public CharIterator charIterator() {
return new OneCharIterator(CharUtilities.CODE_EOT);
}
/**
* Helper function to obtain standard usage prefix for FOP related
* namespace URIs.
* @param namespaceURI URI of node found
* (e.g., "http://www.w3.org/1999/XSL/Format")
* @return the prefix or null if none
*/
public static String getNodePrefix(String namespaceURI) {
if (namespaceURI.equals(FOElementMapping.URI)) {
return "fo";
} else if (namespaceURI.equals(ExtensionElementMapping.URI)) {
return "fox";
} else if (namespaceURI.equals(InternalElementMapping.URI)) {
return "foi";
} else if (namespaceURI.equals(SVGElementMapping.URI)) {
return "svg";
} else {
return null;
}
}
/**
* Helper function to standardize the names of all namespace URI - local
* name pairs in text messages.
* For readability, using fo:, fox:, svg:, for those namespaces even
* though that prefix may not have been chosen in the document.
* @param namespaceURI URI of node found
* (e.g., "http://www.w3.org/1999/XSL/Format")
* @param localName local name of node, (e.g., "root" for "fo:root")
* @return the prefix:localname, if fo/fox/svg, or a longer representation
* with the unabbreviated URI otherwise.
*/
public static String getNodeString(String namespaceURI, String localName) {
String prefix = getNodePrefix(namespaceURI);
if (prefix != null) {
return prefix + ":" + localName;
} else {
return "(Namespace URI: \"" + namespaceURI + "\", "
+ "Local Name: \"" + localName + "\")";
}
}
/**
* Returns an instance of the FOValidationEventProducer.
* @return an event producer for FO validation
*/
protected FOValidationEventProducer getFOValidationEventProducer() {
return FOValidationEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
}
/**
* Helper function to standardize "too many" error exceptions
* (e.g., two fo:declarations within fo:root)
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
* @throws ValidationException the validation error provoked by the method call
*/
protected void tooManyNodesError(Locator loc, String nsURI, String lName)
throws ValidationException {
tooManyNodesError(loc, new QName(nsURI, lName));
}
/**
* Helper function to standardize "too many" error exceptions
* (e.g., two fo:declarations
within fo:root
)
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param offendingNode the qualified name of the offending node
* @throws ValidationException the validation error provoked by the method call
*/
protected void tooManyNodesError(Locator loc, QName offendingNode)
throws ValidationException {
getFOValidationEventProducer().tooManyNodes(this, getName(), offendingNode, loc);
}
/**
* Helper function to standardize "too many" error exceptions
* (e.g., two fo:declarations within fo:root)
* This overloaded method helps make the caller code better self-documenting
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param offendingNode incoming node that would cause a duplication.
* @throws ValidationException the validation error provoked by the method call
*/
protected void tooManyNodesError(Locator loc, String offendingNode)
throws ValidationException {
tooManyNodesError(loc, new QName(FO_URI, offendingNode));
}
/**
* Helper function to standardize "out of order" exceptions
* (e.g., fo:layout-master-set
appearing after fo:page-sequence
)
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param tooLateNode string name of node that should be earlier in document
* @param tooEarlyNode string name of node that should be later in document
* @throws ValidationException the validation error provoked by the method call
*/
protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
String tooEarlyNode) throws ValidationException {
nodesOutOfOrderError(loc, tooLateNode, tooEarlyNode, false);
}
/**
* Helper function to standardize "out of order" exceptions
* (e.g., fo:layout-master-set appearing after fo:page-sequence)
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param tooLateNode string name of node that should be earlier in document
* @param tooEarlyNode string name of node that should be later in document
* @param canRecover indicates whether FOP can recover from this problem and continue working
* @throws ValidationException the validation error provoked by the method call
*/
protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
String tooEarlyNode, boolean canRecover) throws ValidationException {
getFOValidationEventProducer().nodeOutOfOrder(this, getName(),
tooLateNode, tooEarlyNode, canRecover, loc);
}
/**
* Helper function to return "invalid child" exceptions
* (e.g., fo:block
appearing immediately under fo:root
)
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
* @throws ValidationException the validation error provoked by the method call
*/
protected void invalidChildError(Locator loc, String nsURI, String lName)
throws ValidationException {
invalidChildError(loc, getName(), nsURI, lName, null);
}
/**
* Helper function to return "invalid child" exceptions with more
* complex validation rules (i.e., needing more explanation of the problem)
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param parentName the name of the parent element
* @param nsURI namespace URI of incoming offending node
* @param lName local name (i.e., no prefix) of incoming offending node
* @param ruleViolated name of the rule violated (used to lookup a resource in a bundle)
* @throws ValidationException the validation error provoked by the method call
*/
protected void invalidChildError(Locator loc, String parentName, String nsURI, String lName,
String ruleViolated)
throws ValidationException {
String prefix = getNodePrefix(nsURI);
QName qn; // qualified name of offending node
if (prefix != null) {
qn = new QName(nsURI, prefix, lName);
} else {
qn = new QName(nsURI, lName);
}
getFOValidationEventProducer().invalidChild(this, parentName, qn, ruleViolated, loc);
}
/**
* Helper function to return "not supported child" exceptions. Note that the child is valid, just not
* supported yet by FOP.
*
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
* @throws ValidationException the validation error provoked by the method call
*/
protected void notSupportedChildError(Locator loc, String nsURI, String lName) throws ValidationException {
getFOValidationEventProducer().notSupportedChild(this, getName(), new QName(nsURI, lName), loc);
}
/**
* Helper function to throw an error caused by missing mandatory child elements.
* (e.g., fo:layout-master-set
not having any fo:page-master
* child element.
*
* @param contentModel The XSL Content Model for the fo: object or a similar description
* indicating the necessary child elements.
* @throws ValidationException the validation error provoked by the method call
*/
protected void missingChildElementError(String contentModel)
throws ValidationException {
getFOValidationEventProducer().missingChildElement(this, getName(),
contentModel, false, locator);
}
/**
* Helper function to throw an error caused by missing mandatory child elements.
* E.g., fo:layout-master-set not having any page-master child element.
* @param contentModel The XSL Content Model for the fo: object or a similar description
* indicating the necessary child elements.
* @param canRecover indicates whether FOP can recover from this problem and continue working
* @throws ValidationException the validation error provoked by the method call
*/
protected void missingChildElementError(String contentModel, boolean canRecover)
throws ValidationException {
getFOValidationEventProducer().missingChildElement(this, getName(),
contentModel, canRecover, locator);
}
/**
* Helper function to throw an error caused by missing mandatory properties
*
* @param propertyName the name of the missing property.
* @throws ValidationException the validation error provoked by the method call
*/
protected void missingPropertyError(String propertyName)
throws ValidationException {
getFOValidationEventProducer().missingProperty(this, getName(), propertyName, locator);
}
/**
* Helper function to throw an error caused by an invalid property
*
* @param propertyName the name of the property.
* @param propertyValue the value of the property.
* @param e optional property parsing exception.
* @throws ValidationException the validation error provoked by the method call
*/
protected void invalidPropertyValueError(String propertyName, String propertyValue, Exception e)
throws ValidationException {
getFOValidationEventProducer().invalidPropertyValue(this, getName(), propertyName,
propertyValue, new PropertyException(e), locator);
}
/**
* Helper function to return "Error(line#/column#)" string for
* above exception messages
*
* @param loc org.xml.sax.Locator object
* @return String opening error text
*/
protected static String errorText(Locator loc) {
return "Error(" + getLocatorString(loc) + "): ";
}
/**
* Helper function to return "Warning(line#/column#)" string for
* warning messages
*
* @param loc org.xml.sax.Locator object
* @return String opening warning text
*/
protected static String warningText(Locator loc) {
return "Warning(" + getLocatorString(loc) + "): ";
}
/**
* Helper function to format a Locator instance.
*
* @param loc org.xml.sax.Locator object
* @return String the formatted text
*/
public static String getLocatorString(Locator loc) {
if (loc == null) {
return "Unknown location";
} else {
return loc.getLineNumber() + ":" + loc.getColumnNumber();
}
}
/**
* Decorates a log or warning message with context information on the given node.
*
* @param text the original message
* @param node the context node
* @return the decorated text
*/
public static String decorateWithContextInfo(String text, FONode node) {
if (node != null) {
StringBuffer sb = new StringBuffer(text);
sb.append(" (").append(node.getContextInfo()).append(")");
return sb.toString();
} else {
return text;
}
}
/**
* Returns a String containing as much context information as possible about a node. Call
* this method only in exceptional conditions because this method may perform quite extensive
* information gathering inside the FO tree.
* @return a String containing context information
*/
// [GA] remove deprecation - no alternative specified
// @deprecated Not localized! Should rename getContextInfoAlt() to getContextInfo() when done!
public String getContextInfo() {
StringBuffer sb = new StringBuffer();
if (getLocalName() != null) {
sb.append(getName());
sb.append(", ");
}
if (this.locator != null) {
sb.append("location: ");
sb.append(getLocatorString(this.locator));
} else {
String s = gatherContextInfo();
if (s != null) {
sb.append("\"");
sb.append(s);
sb.append("\"");
} else {
sb.append("no context info available");
}
}
if (sb.length() > 80) {
sb.setLength(80);
}
return sb.toString();
}
/**
* Returns a String containing as some context information about a node. It does not take the
* locator into consideration and returns null if no useful context information can be found.
* Call this method only in exceptional conditions because this method may perform quite
* extensive information gathering inside the FO tree. All text returned by this method that
* is not extracted from document content needs to be locale-independent.
* @return a String containing context information
*/
protected String getContextInfoAlt() {
String s = gatherContextInfo();
if (s != null) {
StringBuffer sb = new StringBuffer();
if (getLocalName() != null) {
sb.append(getName());
sb.append(", ");
}
sb.append("\"");
sb.append(s);
sb.append("\"");
return sb.toString();
} else {
return null;
}
}
/** Function for AdvancedMessageFormat to retrieve context info from an FONode. */
public static class GatherContextInfoFunction implements Function {
/** {@inheritDoc} */
public Object evaluate(Map params) {
Object obj = params.get("source");
if (obj instanceof PropertyList) {
PropertyList propList = (PropertyList)obj;
obj = propList.getFObj();
}
if (obj instanceof FONode) {
FONode node = (FONode)obj;
return node.getContextInfoAlt();
}
return null;
}
/** {@inheritDoc} */
public Object getName() {
return "gatherContextInfo";
}
}
/**
* Gathers context information for the getContextInfo() method.
* @return the collected context information or null, if none is available
*/
protected String gatherContextInfo() {
return null;
}
/**
* Returns the root node of this tree
*
* @return the root node
*/
public Root getRoot() {
return parent.getRoot();
}
/**
* Returns the fully qualified name of the node
*
* @return the fully qualified name of this node
*/
public String getName() {
return getName(getNormalNamespacePrefix());
}
/**
* Returns the fully qualified name of the node
*
* @param prefix the namespace prefix to build the name with (may be null)
* @return the fully qualified name of this node
*/
public String getName(String prefix) {
if (prefix != null) {
StringBuffer sb = new StringBuffer();
sb.append(prefix).append(':').append(getLocalName());
return sb.toString();
} else {
return getLocalName();
}
}
/**
* Returns the local name (i.e. without namespace prefix) of the node
*
* @return the local name of this node
*/
public abstract String getLocalName();
/**
* Returns the normally used namespace prefix for this node
*
* @return the normally used namespace prefix for this kind of node (ex. "fo" for XSL-FO)
*/
public abstract String getNormalNamespacePrefix();
/**
* Returns the namespace URI for this node
*
* @return the namespace URI for this node
*/
public String getNamespaceURI() {
return null;
}
/**
* Returns the {@link Constants} class integer value of this node
*
* @return the integer enumeration of this FO (e.g. {@link Constants#FO_ROOT})
* if a formatting object, {@link Constants#FO_UNKNOWN_NODE} otherwise
*/
public int getNameId() {
return Constants.FO_UNKNOWN_NODE;
}
/**
* This method is overridden by extension elements and allows the extension element
* to return a pass-through attachment which the parent formatting objects should simply
* carry with them but otherwise ignore. This mechanism is used to pass non-standard
* information from the FO tree through to the layout engine and the renderers.
*
* @return the extension attachment if one is created by the extension element, null otherwise.
*/
public ExtensionAttachment getExtensionAttachment() {
return null;
}
/**
* This method is overridden by extension elements and allows the extension element to return
* a {@link ContentHandlerFactory}. This factory can create ContentHandler implementations that
* handle foreign XML content by either building up a specific DOM, a Java object or something
* else.
*
* @return the ContentHandlerFactory
or null
if not applicable
*/
public ContentHandlerFactory getContentHandlerFactory() {
return null;
}
/**
* Returns true
if fo:marker
is allowed as
* a child node.
*
To be overridden only in extension nodes that need it.
*
* @return true if markers are valid children
*/
protected boolean canHaveMarkers() {
int foId = getNameId();
switch (foId) {
case Constants.FO_BASIC_LINK:
case Constants.FO_BIDI_OVERRIDE:
case Constants.FO_BLOCK:
case Constants.FO_BLOCK_CONTAINER:
case Constants.FO_FLOW:
case Constants.FO_INLINE:
case Constants.FO_INLINE_CONTAINER:
case Constants.FO_LIST_BLOCK:
case Constants.FO_LIST_ITEM:
case Constants.FO_LIST_ITEM_BODY:
case Constants.FO_LIST_ITEM_LABEL:
case Constants.FO_TABLE:
case Constants.FO_TABLE_BODY:
case Constants.FO_TABLE_HEADER:
case Constants.FO_TABLE_FOOTER:
case Constants.FO_TABLE_CELL:
case Constants.FO_TABLE_AND_CAPTION:
case Constants.FO_TABLE_CAPTION:
case Constants.FO_WRAPPER:
return true;
default:
return false;
}
}
/**
* This method is used when adding child nodes to a FO that already
* contains at least one child. In this case, the new child becomes a
* sibling to the previous one
*
* @param precedingSibling the previous child
* @param followingSibling the new child
*/
protected static void attachSiblings(FONode precedingSibling,
FONode followingSibling) {
if (precedingSibling.siblings == null) {
precedingSibling.siblings = new FONode[2];
}
if (followingSibling.siblings == null) {
followingSibling.siblings = new FONode[2];
}
precedingSibling.siblings[1] = followingSibling;
followingSibling.siblings[0] = precedingSibling;
}
/**
* Determine if node has a delimited text range boundary. N.B. that we report
* this to be true by default, while specific subclasses override this method to report false.
* @param boundary one of {EN_BEFORE, EN_AFTER, or EN_BOTH} enumeration constants
* @return true if indicated boundary (or boundaries) constitute a delimited text range
* boundary.
*/
public boolean isDelimitedTextRangeBoundary(int boundary) {
return true;
}
/**
* Collect the sequence of delimited text ranges, where each new
* range is pushed onto RANGES.
* @param ranges a stack of delimited text ranges
* @return the (possibly) updated stack of delimited text ranges
*/
public Stack collectDelimitedTextRanges(Stack ranges) {
// if boundary before, then push new range
if (isRangeBoundaryBefore()) {
maybeNewRange(ranges);
}
// get current range, if one exists
DelimitedTextRange currentRange;
if (ranges.size() > 0) {
currentRange = ranges.peek();
} else {
currentRange = null;
}
// proceses this node
ranges = collectDelimitedTextRanges(ranges, currentRange);
// if boundary after, then push new range
if (isRangeBoundaryAfter()) {
maybeNewRange(ranges);
}
return ranges;
}
/**
* Collect the sequence of delimited text ranges, where each new
* range is pushed onto RANGES, where default implementation collects ranges
* of child nodes.
* @param ranges a stack of delimited text ranges
* @param currentRange the current range or null (if none)
* @return the (possibly) updated stack of delimited text ranges
*/
protected Stack collectDelimitedTextRanges(
Stack ranges, DelimitedTextRange currentRange) {
for (FONodeIterator it = getChildNodes(); (it != null) && it.hasNext();) {
ranges = it.next().collectDelimitedTextRanges(ranges);
}
return ranges;
}
/**
* Determine if this node is a new bidi RANGE block item.
* @return true if this node is a new bidi RANGE block item
*/
public boolean isBidiRangeBlockItem() {
return false;
}
/**
* Conditionally add a new delimited text range to RANGES. Always add new
* range unless there are no ranges on the stack yet and this node is not a block item.
* @param ranges stack of delimited text ranges
* @return new range (if constructed and pushed onto stack) or current range (if any) or null
*/
private DelimitedTextRange maybeNewRange(Stack ranges) {
DelimitedTextRange rCur = !ranges.empty() ? ranges.peek() : null;
DelimitedTextRange rNew;
if ((rCur != null) || isBidiRangeBlockItem()) {
rNew = new DelimitedTextRange(this);
} else {
rNew = null;
}
if (rNew != null) {
ranges.push(rNew);
} else {
rNew = rCur;
}
return rNew;
}
private boolean isRangeBoundaryBefore() {
return isDelimitedTextRangeBoundary(Constants.EN_BEFORE);
}
private boolean isRangeBoundaryAfter() {
return isDelimitedTextRangeBoundary(Constants.EN_AFTER);
}
/**
* Base iterator interface over a FO's children, offering three methods on top of the base interface
* methods.
*/
public interface FONodeIterator extends ListIterator {
/** @return the next node */
FONode next();
/** @return the previous node */
FONode previous();
/**
* Replace the node at the current index with the given newNode
.
*
* @param newNode the new node
*/
void set(FONode newNode);
/**
* Add the given newNode
at the current position.
*
* @param newNode the new node
*/
void add(FONode newNode);
/** @return true
if there is a next node, false
otherwise */
boolean hasNext();
/** @return true
if there is a previous node, false
otherwise */
boolean hasPrevious();
/** @return the current index */
int nextIndex();
/** @return the previous index */
int previousIndex();
/**
* Removes the node at the current position.
*/
void remove();
/**
* Returns the parent node for this iterator's list
* of child nodes
*
* @return the parent node
*/
FObj parent();
/**
* Returns the first node in the list, and decreases the index,
* so that a subsequent call to hasPrevious()
will
* return false
*
* @return the first node in the list
*/
FONode first();
/**
* Returns the last node in the list, and advances the
* current position, so that a subsequent call to hasNext()
* will return false
*
* @return the last node in the list
*/
FONode last();
}
/**
* Returns the list of active change bars.
*
* @return The list of active change bars
*/
public List getChangeBarList() {
return nodeChangeBarList;
}
/**
* Sets the structure tree element.
*
* @param structureTreeElement set.
*/
public void setStructureTreeElement(StructureTreeElement structureTreeElement) {
throw new UnsupportedOperationException();
}
/**
* Returns the structure tree element associated to this object.
*
* @return the structure tree element
*/
public StructureTreeElement getStructureTreeElement() {
return null;
}
}