| /* |
| * Copyright 1999-2005 The Apache Software Foundation. |
| * |
| * Licensed 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; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.fop.apps.FOPException; |
| import org.apache.fop.datatypes.PercentBase; |
| import org.apache.fop.fo.flow.Marker; |
| import org.apache.fop.fo.properties.PropertyMaker; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.Locator; |
| |
| /** |
| * Base class for representation of formatting objects and their processing. |
| */ |
| public abstract class FObj extends FONode implements Constants { |
| public static PropertyMaker[] propertyListTable = null; |
| |
| /** The immediate child nodes of this node. */ |
| public List childNodes = null; |
| |
| /** Used to indicate if this FO is either an Out Of Line FO (see rec) |
| or a descendant of one. Used during validateChildNode() FO |
| validation. |
| */ |
| private boolean isOutOfLineFODescendant = false; |
| |
| /** Markers added to this element. */ |
| protected Map markers = null; |
| |
| /** Dynamic layout dimension. Used to resolve relative lengths. */ |
| protected Map layoutDimension = null; |
| |
| /** |
| * Create a new formatting object. |
| * All formatting object classes extend this class. |
| * |
| * @param parent the parent node |
| * @todo move propertyListTable initialization someplace else? |
| */ |
| public FObj(FONode parent) { |
| super(parent); |
| |
| // determine if isOutOfLineFODescendant should be set |
| if (parent != null && parent instanceof FObj) { |
| if (((FObj)parent).getIsOutOfLineFODescendant() == true) { |
| isOutOfLineFODescendant = true; |
| } else { |
| int foID = getNameId(); |
| if (foID == FO_FLOAT || foID == FO_FOOTNOTE |
| || foID == FO_FOOTNOTE_BODY) { |
| isOutOfLineFODescendant = true; |
| } |
| } |
| } |
| |
| if (propertyListTable == null) { |
| propertyListTable = new PropertyMaker[Constants.PROPERTY_COUNT + 1]; |
| PropertyMaker[] list = FOPropertyMapping.getGenericMappings(); |
| for (int i = 1; i < list.length; i++) { |
| if (list[i] != null) { |
| propertyListTable[i] = list[i]; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.fop.fo.FONode#clone(FONode, boolean) |
| */ |
| public FONode clone(FONode parent, boolean removeChildren) |
| throws FOPException { |
| FObj fobj = (FObj) super.clone(parent, removeChildren); |
| if (removeChildren) { |
| fobj.childNodes = null; |
| } |
| return fobj; |
| } |
| |
| /** |
| * @see org.apache.fop.fo.FONode#processNode |
| */ |
| public void processNode(String elementName, Locator locator, |
| Attributes attlist, PropertyList pList) throws FOPException { |
| setLocator(locator); |
| pList.addAttributesToList(attlist); |
| pList.setWritingMode(); |
| bind(pList); |
| } |
| |
| /** |
| * Create a default property list for this element. |
| */ |
| protected PropertyList createPropertyList(PropertyList parent, |
| FOEventHandler foEventHandler) throws FOPException { |
| return foEventHandler.getPropertyListMaker().make(this, parent); |
| } |
| |
| /** |
| * Bind property values from the property list to the FO node. |
| * Must be overridden in all FObj subclasses that have properties |
| * applying to it. |
| * @param pList the PropertyList where the properties can be found. |
| * @throws FOPException |
| */ |
| public void bind(PropertyList pList) throws FOPException { |
| } |
| |
| /** |
| * Setup the id for this formatting object. |
| * Most formatting objects can have an id that can be referenced. |
| * This methods checks that the id isn't already used by another |
| * fo and sets the id attribute of this object. |
| */ |
| protected void checkId(String id) throws ValidationException { |
| if (!id.equals("")) { |
| Set idrefs = getFOEventHandler().getIDReferences(); |
| if (!idrefs.contains(id)) { |
| idrefs.add(id); |
| } else { |
| throw new ValidationException("Property id \"" + id + |
| "\" previously used; id values must be unique" + |
| " in document.", locator); |
| } |
| } |
| } |
| |
| /** |
| * Returns Out Of Line FO Descendant indicator. |
| * @return true if Out of Line FO or Out Of Line descendant, false otherwise |
| */ |
| public boolean getIsOutOfLineFODescendant() { |
| return isOutOfLineFODescendant; |
| } |
| |
| /** |
| * @see org.apache.fop.fo.FONode#addChildNode(FONode) |
| */ |
| protected void addChildNode(FONode child) throws FOPException { |
| if (PropertySets.canHaveMarkers(getNameId()) && |
| child.getNameId() == FO_MARKER) { |
| addMarker((Marker) child); |
| } else { |
| if (childNodes == null) { |
| childNodes = new java.util.ArrayList(); |
| } |
| childNodes.add(child); |
| } |
| } |
| |
| /** @see org.apache.fop.fo.FONode#removeChild(org.apache.fop.fo.FONode) */ |
| public void removeChild(FONode child) { |
| if (childNodes != null) { |
| childNodes.remove(child); |
| } |
| } |
| |
| /** |
| * Find the nearest parent, grandparent, etc. FONode that is also an FObj |
| * @return FObj the nearest ancestor FONode that is an FObj |
| */ |
| public FObj findNearestAncestorFObj() { |
| FONode par = parent; |
| while (par != null && !(par instanceof FObj)) { |
| par = par.parent; |
| } |
| return (FObj) par; |
| } |
| |
| /* This section is the implemenation of the property context. */ |
| |
| /** |
| * Assign the size of a layout dimension to the key. |
| * @param key the Layout dimension, from PercentBase. |
| * @param dimension The layout length. |
| */ |
| public void setLayoutDimension(PercentBase.LayoutDimension key, int dimension) { |
| if (layoutDimension == null) { |
| layoutDimension = new java.util.HashMap(); |
| } |
| layoutDimension.put(key, new Integer(dimension)); |
| } |
| |
| /** |
| * Assign the size of a layout dimension to the key. |
| * @param key the Layout dimension, from PercentBase. |
| * @param dimension The layout length. |
| */ |
| public void setLayoutDimension(PercentBase.LayoutDimension key, float dimension) { |
| if (layoutDimension == null) { |
| layoutDimension = new java.util.HashMap(); |
| } |
| layoutDimension.put(key, new Float(dimension)); |
| } |
| |
| /** |
| * Return the size associated with the key. |
| * @param key The layout dimension key. |
| * @return the length. |
| */ |
| public Number getLayoutDimension(PercentBase.LayoutDimension key) { |
| if (layoutDimension != null) { |
| Number result = (Number) layoutDimension.get(key); |
| if (result != null) { |
| return result; |
| } |
| } |
| if (parent != null) { |
| return ((FObj) parent).getLayoutDimension(key); |
| } |
| return new Integer(0); |
| } |
| |
| /** |
| * Check if this formatting object generates reference areas. |
| * @return true if generates reference areas |
| * @todo see if needed |
| */ |
| public boolean generatesReferenceAreas() { |
| return false; |
| } |
| |
| /** |
| * @see org.apache.fop.fo.FONode#getChildNodes() |
| */ |
| public ListIterator getChildNodes() { |
| if (childNodes != null) { |
| return childNodes.listIterator(); |
| } |
| return null; |
| } |
| |
| /** |
| * Return an iterator over the object's childNodes starting |
| * at the passed-in node. |
| * @param childNode First node in the iterator |
| * @return A ListIterator or null if childNode isn't a child of |
| * this FObj. |
| */ |
| public ListIterator getChildNodes(FONode childNode) { |
| if (childNodes != null) { |
| int i = childNodes.indexOf(childNode); |
| if (i >= 0) { |
| return childNodes.listIterator(i); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Notifies a FObj that one of it's children is removed. |
| * This method is subclassed by Block to clear the firstInlineChild variable. |
| * @param node the node that was removed |
| */ |
| protected void notifyChildRemoval(FONode node) { |
| //nop |
| } |
| |
| /** |
| * Add the marker to this formatting object. |
| * If this object can contain markers it checks that the marker |
| * has a unique class-name for this object and that it is |
| * the first child. |
| * @param marker Marker to add. |
| */ |
| protected void addMarker(Marker marker) { |
| String mcname = marker.getMarkerClassName(); |
| if (childNodes != null) { |
| // check for empty childNodes |
| for (Iterator iter = childNodes.iterator(); iter.hasNext();) { |
| FONode node = (FONode)iter.next(); |
| if (node instanceof FOText) { |
| FOText text = (FOText)node; |
| if (text.willCreateArea()) { |
| getLogger().error("fo:marker must be an initial child: " + mcname); |
| return; |
| } else { |
| iter.remove(); |
| notifyChildRemoval(node); |
| } |
| } else { |
| getLogger().error("fo:marker must be an initial child: " + mcname); |
| return; |
| } |
| } |
| } |
| if (markers == null) { |
| markers = new java.util.HashMap(); |
| } |
| if (!markers.containsKey(mcname)) { |
| markers.put(mcname, marker); |
| } else { |
| getLogger().error("fo:marker 'marker-class-name' " |
| + "must be unique for same parent: " + mcname); |
| } |
| } |
| |
| /** |
| * @return true if there are any Markers attached to this object |
| */ |
| public boolean hasMarkers() { |
| return markers != null && !markers.isEmpty(); |
| } |
| |
| /** |
| * @return th collection of Markers attached to this object |
| */ |
| public Map getMarkers() { |
| return markers; |
| } |
| |
| /* |
| * Return a string representation of the fo element. |
| * Deactivated in order to see precise ID of each fo element created |
| * (helpful for debugging) |
| */ |
| /* public String toString() { |
| return getName() + " at line " + line + ":" + column; |
| } |
| */ |
| |
| /** |
| * Convenience method for validity checking. Checks if the |
| * incoming node is a member of the "%block;" parameter entity |
| * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations |
| * @param nsURI namespace URI of incoming node |
| * @param lName local name (i.e., no prefix) of incoming node |
| * @return true if a member, false if not |
| */ |
| protected boolean isBlockItem(String nsURI, String lName) { |
| return (nsURI == FO_URI && |
| (lName.equals("block") |
| || lName.equals("table") |
| || lName.equals("table-and-caption") |
| || lName.equals("block-container") |
| || lName.equals("list-block") |
| || lName.equals("float") |
| || isNeutralItem(nsURI, lName))); |
| } |
| |
| /** |
| * Convenience method for validity checking. Checks if the |
| * incoming node is a member of the "%inline;" parameter entity |
| * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations |
| * @param nsURI namespace URI of incoming node |
| * @param lName local name (i.e., no prefix) of incoming node |
| * @return true if a member, false if not |
| */ |
| protected boolean isInlineItem(String nsURI, String lName) { |
| return (nsURI == FO_URI && |
| (lName.equals("bidi-override") |
| || lName.equals("character") |
| || lName.equals("external-graphic") |
| || lName.equals("instream-foreign-object") |
| || lName.equals("inline") |
| || lName.equals("inline-container") |
| || lName.equals("leader") |
| || lName.equals("page-number") |
| || lName.equals("page-number-citation") |
| || lName.equals("basic-link") |
| || (lName.equals("multi-toggle") |
| && (getNameId() == FO_MULTI_CASE || findAncestor(FO_MULTI_CASE) > 0)) |
| || (lName.equals("footnote") && !isOutOfLineFODescendant) |
| || isNeutralItem(nsURI, lName))); |
| } |
| |
| /** |
| * Convenience method for validity checking. Checks if the |
| * incoming node is a member of the "%block;" parameter entity |
| * or "%inline;" parameter entity |
| * @param nsURI namespace URI of incoming node |
| * @param lName local name (i.e., no prefix) of incoming node |
| * @return true if a member, false if not |
| */ |
| protected boolean isBlockOrInlineItem(String nsURI, String lName) { |
| return (isBlockItem(nsURI, lName) || isInlineItem(nsURI, lName)); |
| } |
| |
| /** |
| * Convenience method for validity checking. Checks if the |
| * incoming node is a member of the neutral item list |
| * as defined in Sect. 6.2 of the XSL 1.0 & 1.1 Recommendations |
| * @param nsURI namespace URI of incoming node |
| * @param lName local name (i.e., no prefix) of incoming node |
| * @return true if a member, false if not |
| */ |
| protected boolean isNeutralItem(String nsURI, String lName) { |
| return (nsURI == FO_URI && |
| (lName.equals("multi-switch") |
| || lName.equals("multi-properties") |
| || lName.equals("wrapper") |
| || (!isOutOfLineFODescendant && lName.equals("float")) |
| || lName.equals("retrieve-marker"))); |
| } |
| |
| /** |
| * Convenience method for validity checking. Checks if the |
| * current node has an ancestor of a given name. |
| * @param ancestorID -- Constants ID of node name to check for (e.g., FO_ROOT) |
| * @return number of levels above FO where ancestor exists, |
| * -1 if not found |
| */ |
| protected int findAncestor(int ancestorID) { |
| int found = 1; |
| FONode temp = getParent(); |
| while (temp != null) { |
| if (temp.getNameId() == ancestorID) { |
| return found; |
| } |
| found += 1; |
| temp = temp.getParent(); |
| } |
| return -1; |
| } |
| } |
| |