/**
* base class for nodes in the XML tree
*/
-public abstract class FONode {
+public abstract class FONode implements Cloneable {
protected static String FO_URI = FOElementMapping.URI;
this.parent = parent;
}
+ /**
+ * Perform a shallow cloning operation,
+ * set its parent, and optionally clean the list of child nodes
+ * @param parent the intended parent of the clone
+ * @param removeChildren if true, clean the list of child nodes
+ * @return the cloned FO node
+ */
+ public FONode clone(FONode parent, boolean removeChildren)
+ throws FOPException {
+ FONode foNode = (FONode) clone();
+ foNode.parent = parent;
+ parent.addChildNode(foNode);
+ return foNode;
+ }
+
+ /**
+ * Perform a shallow cloning operation
+ *
+ * @see java.lang.Object#clone()
+ * @return the cloned object
+ */
+ protected Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) { }
+ return null;
+ }
+
/**
* Set the location information for this element
* @param locator the org.xml.sax.Locator object
private String markerClassName;
// End of property values
- private MarkerPropertyList propertyList;
private PropertyListMaker savePropertyListMaker;
- private HashMap children = new HashMap();
+ private HashMap descPLists = new HashMap();
/**
* Create a marker fo.
}
/**
- * Rebind the marker and all the children using the specified
- * parentPropertyList which comes from the fo:retrieve-marker element.
- * @param parentPropertyList The property list from fo:retrieve-marker.
+ * retrieve the property list of foNode
+ * @param foNode the FO node whose property list is requested
+ * @return the MarkerPropertyList of foNode
*/
- public void rebind(PropertyList parentPropertyList) throws FOPException {
- // Set a new parent property list and bind all the children again.
- propertyList.setParentPropertyList(parentPropertyList);
- for (Iterator i = children.keySet().iterator(); i.hasNext(); ) {
- FONode child = (FONode) i.next();
- PropertyList childList = (PropertyList) children.get(child);
- if (child instanceof FObj) {
- ((FObj) child).bind(childList);
- } else if (child instanceof FOText) {
- ((FOText) child).bind(childList);
- }
- }
+ protected MarkerPropertyList getPList(FONode foNode) {
+ return (MarkerPropertyList) descPLists.get(foNode);
}
protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) throws FOPException {
- propertyList = new MarkerPropertyList(this, parent);
- return propertyList;
+ return new MarkerPropertyList(this, parent);
}
protected void startOfNode() {
foEventHandler.setPropertyListMaker(new PropertyListMaker() {
public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
PropertyList pList = new MarkerPropertyList(fobj, parentPropertyList);
- children.put(fobj, pList);
+ descPLists.put(fobj, pList);
return pList;
}
});
}
- protected void addChildNode(FONode child) throws FOPException {
- if (!children.containsKey(child)) {
- children.put(child, propertyList);
- }
- super.addChildNode(child);
- }
-
protected void endOfNode() {
// Pop the MarkerPropertyList maker.
getFOEventHandler().setPropertyListMaker(savePropertyListMaker);
savePropertyListMaker = null;
+ // unparent the child property lists
+ Iterator iter = getChildNodes();
+ if (iter != null) {
+ while (iter.hasNext()) {
+ FONode child = (FONode) iter.next();
+ MarkerPropertyList pList
+ = (MarkerPropertyList) descPLists.get(child);
+ if (pList != null) {
+ pList.setParentPropertyList(null);
+ }
+ }
+ }
}
/**
/**
* @see org.apache.fop.fo.FONode#addLayoutManager(List)
- * @todo remove null check when vCN() & endOfNode() implemented
*/
public void addLayoutManager(List list) {
- ListIterator baseIter = getChildNodes();
- if (baseIter == null) {
- return;
- }
- while (baseIter.hasNext()) {
- FONode child = (FONode) baseIter.next();
- child.addLayoutManager(list);
- }
+ // no layout manager
}
/**
package org.apache.fop.fo.flow;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+
import org.xml.sax.Locator;
import org.apache.commons.logging.Log;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FOEventHandler;
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.StaticPropertyList;
invalidChildError(loc, nsURI, localName);
}
+ /**
+ * @see org.apache.fop.fo.FONode#addLayoutManager(List)
+ * @todo remove null check when vCN() & endOfNode() implemented
+ */
+ public void addLayoutManager(List list) {
+ Iterator baseIter = getChildNodes();
+ if (baseIter == null) {
+ return;
+ }
+ while (baseIter.hasNext()) {
+ FONode child = (FONode) baseIter.next();
+ child.addLayoutManager(list);
+ }
+ }
+
protected PropertyList createPropertyList(PropertyList parent,
FOEventHandler foEventHandler) throws FOPException {
// TODO: A special RetrieveMarkerPropertyList would be more memory
return retrieveBoundary;
}
+ /**
+ * 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 descPLists the map of the new nodes to property lists
+ */
+ private void cloneSubtree(Iterator parentIter, FONode newParent,
+ Marker marker, Map descPLists)
+ throws FOPException {
+ if (parentIter == null) return;
+ while (parentIter.hasNext()) {
+ FONode child = (FONode) parentIter.next();
+ FONode newChild = child.clone(newParent, true);
+ descPLists.put(newChild, marker.getPList(child));
+ cloneSubtree(child.getChildNodes(), newChild, marker, descPLists);
+ }
+ }
+
+ /**
+ * Clone the subtree of marker,
+ * attach the new subtree to this node,
+ * reparent the property lists of the direct children
+ * to the property list of this node.
+ * @param marker the marker that is to be cloned
+ * @param descPLists the map of the new nodes to property lists
+ */
+ private void cloneFromMarker(Marker marker, Map descPLists)
+ throws FOPException {
+ // release child nodes from a possible earlier layout
+ childNodes = new ArrayList();
+ Iterator markerIter = marker.getChildNodes();
+ cloneSubtree(markerIter, this, marker, descPLists);
+ // reparent the property lists of the direct children
+ for (Iterator iter = getChildNodes(); iter.hasNext(); ) {
+ FONode child = (FONode) iter.next();
+ Marker.MarkerPropertyList pList
+ = (Marker.MarkerPropertyList) descPLists.get(child);
+ if (pList != null) {
+ pList.setParentPropertyList(propertyList);
+ }
+ }
+ }
+
+ /**
+ * Bind the new nodes to the property values in this context
+ * @param descPLists the map of the new nodes to property lists
+ */
+ private void bindChildren(Map descPLists) throws FOPException {
+ for (Iterator i = descPLists.keySet().iterator(); i.hasNext(); ) {
+ FONode desc = (FONode) i.next();
+ PropertyList descPList;
+ if (desc instanceof FObj) {
+ descPList = (PropertyList) descPLists.get(desc);
+ ((FObj) desc).bind(descPList);
+ } else if (desc instanceof FOText) {
+ descPList = (PropertyList) descPLists.get(desc.getParent());
+ if (descPList == null) {
+ descPList = propertyList;
+ }
+ ((FOText) desc).bind(descPList);
+ }
+ }
+ }
+
+ /**
+ * Clone the subtree of marker
+ * and bind the nodes to the property values in this context
+ * @param marker the marker that is to be cloned
+ */
public void bindMarker(Marker marker) {
// assert(marker != null);
+ // catch empty marker
+ if (marker.getChildNodes() == null) {
+ return;
+ }
+ HashMap descPLists = new HashMap();
+ try {
+ cloneFromMarker(marker, descPLists);
+ } catch (FOPException exc) {
+ Log log = getLogger();
+ log.error("fo:retrieve-marker unable to clone subtree of fo:marker", exc);
+ return;
+ }
try {
- marker.rebind(getPropertyList());
+ bindChildren(descPLists);
} catch (FOPException exc) {
Log log = getLogger();
log.error("fo:retrieve-marker unable to rebind property values", exc);