Package org.apache.fop.fo

Source Code of org.apache.fop.fo.FObj

/*
* 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: FObj.java 442282 2006-09-11 18:24:35Z jeremias $ */

package org.apache.fop.fo;

import java.util.Collections;
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.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.properties.PropertyMaker;
import org.apache.fop.util.QName;
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 {
   
    /** the list of property makers */
    private static PropertyMaker[] propertyListTable
                            = FOPropertyMapping.getGenericMappings();
   
    /** The immediate child nodes of this node. */
    protected List childNodes = null;

    /** The list of extension attachments, null if none */
    private List extensionAttachments = null;
   
    /** The map of foreign attributes, null if none */
    private Map foreignAttributes = null;
   
    /** Used to indicate if this FO is either an Out Of Line FO (see rec)
     *  or a descendant of one. Used during FO validation.
     */
    private boolean isOutOfLineFODescendant = false;

    /** Markers added to this element. */
    private Map markers = null;
   
    /**
     * Create a new formatting object.
     * All formatting object classes extend this class.
     *
     * @param parent the parent node
     */
    public FObj(FONode parent) {
        super(parent);
       
        // determine if isOutOfLineFODescendant should be set
        if (parent != null && parent instanceof FObj) {
            if (((FObj) parent).getIsOutOfLineFODescendant()) {
                isOutOfLineFODescendant = true;
            } else {
                int foID = getNameId();
                if (foID == FO_FLOAT || foID == FO_FOOTNOTE
                    || foID == FO_FOOTNOTE_BODY) {
                        isOutOfLineFODescendant = true;
                }
            }
        }
    }

    /**
     * @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;
    }
   
    /**
     * Returns the PropertyMaker for a given property ID.
     * @param propId the property ID
     * @return the requested Property Maker
     */
    public static PropertyMaker getPropertyMakerFor(int propId) {
        return propertyListTable[propId];
    }

    /**
     * @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);
        if (!inMarker()
                || "marker".equals(elementName)) {
            pList.setWritingMode();
            bind(pList);
        }
    }

    /**
     * Create a default property list for this element.
     * @see org.apache.fop.fo.FONode
     */
    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 if there is a problem binding the values
     */
    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
     *
     * @param id    the id to check
     * @throws ValidationException if the ID is already defined elsewhere
     *                              (strict validation only)
     */
    protected void checkId(String id) throws ValidationException {
        if (!inMarker() && !id.equals("")) {
            Set idrefs = getFOEventHandler().getIDReferences();
            if (!idrefs.contains(id)) {
                idrefs.add(id);
            } else {
                if (getUserAgent().validateStrictly()) {
                    throw new ValidationException("Property id \"" + id
                            + "\" previously used; id values must be unique"
                            + " in document.", locator);
                } else {
                    if (log.isWarnEnabled()) {
                        StringBuffer msg = new StringBuffer();
                        msg.append("Found non-unique id on ").append(getName());
                        if (locator.getLineNumber() != -1) {
                            msg.append(" (at ").append(locator.getLineNumber())
                                .append("/").append(locator.getColumnNumber())
                                .append(")");
                        }
                        msg.append("\nAny reference to it will be considered "
                                + "a reference to the first occurrence "
                                + "in the document.");
                        log.warn(msg);
                    }
                }
            }
        }
    }

    /**
     * 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 (canHaveMarkers() && child.getNameId() == FO_MARKER) {
            addMarker((Marker) child);
        } else {
            ExtensionAttachment attachment = child.getExtensionAttachment();
            if (attachment != null) {
                /* This removes the element from the normal children,
                 * so no layout manager is being created for them
                 * as they are only additional information.
                 */
                addExtensionAttachment(attachment);
            } else {
                if (childNodes == null) {
                    childNodes = new java.util.ArrayList();
                }
                childNodes.add(child);
            }
        }
    }

    /**
     * Used by RetrieveMarker during Marker-subtree cloning
     * @param child     the (cloned) child node
     * @param parent    the (cloned) parent node
     * @throws FOPException when the child could not be added to the parent
     */
    protected static void addChildTo(FONode child, FObj parent)
                            throws FOPException {
        parent.addChildNode(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;
    }

    /**
     * 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;
    }

    /**
     * Return a FONode based on the index in the list of childNodes.
     * @param nodeIndex index of the node to return
     * @return the node or null if the index is invalid
     */
    public FONode getChildNodeAt(int nodeIndex) {
        if (childNodes != null) {
            if (nodeIndex >= 0 && nodeIndex < childNodes.size()) {
                return (FONode) childNodes.get(nodeIndex);
            }
        }
        return null;
    }

    /**
     * Notifies a FObj that one of it's children is removed.
     * This method is subclassed by Block to clear the
     * firstInlineChild variable in case it doesn't generate
     * any areas (see addMarker()).
     * @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 FObj
                        || (node instanceof FOText
                                && ((FOText) node).willCreateArea())) {
                    getLogger().error(
                            "fo:marker must be an initial child: " + mcname);
                    return;
                } else if (node instanceof FOText) {
                    iter.remove();
                    notifyChildRemoval(node);
                }
            }
        }
        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 the collection of Markers attached to this object
     */
    public Map getMarkers() {
        return markers;
    }

    /** @see org.apache.fop.fo.FONode#gatherContextInfo() */
    protected String gatherContextInfo() {
        if (getLocator() != null) {
            return super.gatherContextInfo();
        } else {
            ListIterator iter = getChildNodes();
            if (iter == null) {
                return null;
            }
            StringBuffer sb = new StringBuffer();
            while (iter.hasNext()) {
                FONode node = (FONode) iter.next();
                String s = node.gatherContextInfo();
                if (s != null) {
                    if (sb.length() > 0) {
                        sb.append(", ");
                    }
                    sb.append(s);
                }
            }
            return (sb.length() > 0 ? sb.toString() : null);
        }
    }

    /**
     * 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 (FO_URI.equals(nsURI)
                && (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 (FO_URI.equals(nsURI)
                && (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("page-number-citation-last")
                        || 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 (FO_URI.equals(nsURI)
                && (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    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;
    }
   
   
    /** @see org.apache.fop.fo.FONode#getNamespaceURI() */
    public String getNamespaceURI() {
        return FOElementMapping.URI;
    }

    /** @see org.apache.fop.fo.FONode#getNormalNamespacePrefix() */
    public String getNormalNamespacePrefix() {
        return "fo";
    }

    /**
     * Add a new extension attachment to this FObj.
     * (see org.apache.fop.fo.FONode for details)
     *
     * @param attachment the attachment to add.
     */
    public void addExtensionAttachment(ExtensionAttachment attachment) {
        if (attachment == null) {
            throw new NullPointerException(
                    "Parameter attachment must not be null");
        }
        if (extensionAttachments == null) {
            extensionAttachments = new java.util.ArrayList();
        }
        if (log.isDebugEnabled()) {
            getLogger().debug("ExtensionAttachment of category "
                    + attachment.getCategory() + " added to "
                    + getName() + ": " + attachment);
        }
        extensionAttachments.add(attachment);
    }
   
    /** @return the extension attachments of this FObj. */
    public List getExtensionAttachments() {
        if (extensionAttachments == null) {
            return Collections.EMPTY_LIST;
        } else {
            return extensionAttachments;
        }
    }

    /**
     * Adds a foreign attribute to this FObj.
     * @param attributeName the attribute name as a QName instance
     * @param value the attribute value
     */
    public void addForeignAttribute(QName attributeName, String value) {
        /* TODO: Handle this over FOP's property mechanism so we can use
         *       inheritance.
         */
        if (attributeName == null) {
            throw new NullPointerException("Parameter attributeName must not be null");
        }
        if (foreignAttributes == null) {
            foreignAttributes = new java.util.HashMap();
        }
        foreignAttributes.put(attributeName, value);
    }
   
    /** @return the map of foreign attributes */
    public Map getForeignAttributes() {
        if (foreignAttributes == null) {
            return Collections.EMPTY_MAP;
        } else {
            return foreignAttributes;
        }
    }
}
TOP

Related Classes of org.apache.fop.fo.FObj

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.