1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /* $Id: AbstractRetrieveMarker.java 1466146 2013-04-09 17:31:41Z vhennebert $ */
19 
20 package org.apache.fop.fo.flow;
21 
22 import java.util.Iterator;
23 
24 import org.xml.sax.Locator;
25 
26 import org.apache.fop.accessibility.StructureTreeElement;
27 import org.apache.fop.apps.FOPException;
28 import org.apache.fop.fo.FONode;
29 import org.apache.fop.fo.FOText;
30 import org.apache.fop.fo.FObj;
31 import org.apache.fop.fo.FObjMixed;
32 import org.apache.fop.fo.PropertyList;
33 import org.apache.fop.fo.ValidationException;
34 import org.apache.fop.fo.XMLObj;
35 import org.apache.fop.fo.flow.table.Table;
36 
37 /**
38  * Abstract base class for the <a href="http://www.w3.org/TR/xsl/#fo_retrieve-marker">
39  * <code>fo:retrieve-marker</code></a> and
40  * <a href="http://www.w3.org/TR/xsl/#fo_retrieve-table-marker">
41  * <code>fo:retrieve-table-marker</code></a> formatting objects.
42 
43  */
44 public abstract class AbstractRetrieveMarker extends FObjMixed {
45 
46     private PropertyList propertyList;
47 
48     private String retrieveClassName;
49 
50     private int position;
51     private String positionLabel;
52     private int boundary;
53     private String boundaryLabel;
54 
55     private StructureTreeElement structureTreeElement;
56 
57     /**
58      * Create a new AbstractRetrieveMarker instance that
59      * is a child of the given {@link FONode}
60      *
61      * @param parent    the parent {@link FONode}
62      */
AbstractRetrieveMarker(FONode parent)63     public AbstractRetrieveMarker(FONode parent) {
64         super(parent);
65     }
66 
67     /**
68      * {@inheritDoc}
69      * <p>XSL Content Model: empty
70      */
validateChildNode(Locator loc, String nsURI, String localName)71     protected void validateChildNode(Locator loc, String nsURI, String localName)
72                 throws ValidationException {
73         if (FO_URI.equals(nsURI)) {
74             invalidChildError(loc, nsURI, localName);
75         }
76     }
77 
78     /**
79      * {@inheritDoc}
80      * Store a reference to the parent {@link PropertyList}
81      * to be used when the retrieve-marker is resolved.
82      */
bind(PropertyList pList)83     public void bind(PropertyList pList) throws FOPException {
84         super.bind(pList);
85         this.retrieveClassName = pList.get(PR_RETRIEVE_CLASS_NAME).getString();
86         if (retrieveClassName == null || retrieveClassName.equals("")) {
87             missingPropertyError("retrieve-class-name");
88         }
89         this.propertyList = pList.getParentPropertyList();
90     }
91 
92     @Override
setStructureTreeElement(StructureTreeElement structureTreeElement)93     public void setStructureTreeElement(StructureTreeElement structureTreeElement) {
94         this.structureTreeElement = structureTreeElement;
95     }
96 
97     @Override
getStructureTreeElement()98     public StructureTreeElement getStructureTreeElement() {
99         return structureTreeElement;
100     }
101 
createPropertyListFor(FObj fo, PropertyList parent)102     private PropertyList createPropertyListFor(FObj fo, PropertyList parent) {
103         return getBuilderContext().getPropertyListMaker().make(fo, parent);
104     }
105 
cloneSingleNode(FONode child, FONode newParent, Marker marker, PropertyList parentPropertyList)106     private void cloneSingleNode(FONode child, FONode newParent,
107                             Marker marker, PropertyList parentPropertyList)
108         throws FOPException {
109 
110         if (child != null) {
111             FONode newChild = child.clone(newParent, true);
112             if (child instanceof FObj) {
113                 Marker.MarkerPropertyList pList;
114                 PropertyList newPropertyList = createPropertyListFor(
115                             (FObj) newChild, parentPropertyList);
116 
117                 pList = marker.getPropertyListFor(child);
118                 newChild.processNode(
119                         child.getLocalName(),
120                         getLocator(),
121                         pList,
122                         newPropertyList);
123                 addChildTo(newChild, newParent);
124                 newChild.startOfNode();
125                 switch (newChild.getNameId()) {
126                 case FO_TABLE:
127                     Table t = (Table) child;
128                     cloneSubtree(t.getColumns().iterator(),
129                                  newChild, marker, newPropertyList);
130                     cloneSingleNode(t.getTableHeader(),
131                                     newChild, marker, newPropertyList);
132                     cloneSingleNode(t.getTableFooter(),
133                                     newChild, marker, newPropertyList);
134                     cloneSubtree(child.getChildNodes(),
135                                     newChild, marker, newPropertyList);
136                     break;
137                 case FO_LIST_ITEM:
138                     ListItem li = (ListItem) child;
139                     cloneSingleNode(li.getLabel(),
140                                     newChild, marker, newPropertyList);
141                     cloneSingleNode(li.getBody(),
142                                     newChild, marker, newPropertyList);
143                     break;
144                 default:
145                     cloneSubtree(child.getChildNodes(),
146                                     newChild, marker, newPropertyList);
147                     break;
148                 }
149                 newChild.endOfNode();
150             } else if (child instanceof FOText) {
151                 FOText ft = (FOText) newChild;
152                 ft.bind(parentPropertyList);
153                 addChildTo(newChild, newParent);
154                 if (newParent instanceof AbstractRetrieveMarker) {
155                     /*
156                      * Otherwise the parent of newChild is a cloned FObjMixed that will
157                      * call this FOText's endOfNode when its own endOfNode method is
158                      * called.
159                      */
160                     newChild.endOfNode();
161                 }
162             } else if (child instanceof XMLObj) {
163                 addChildTo(newChild, newParent);
164             }
165         }
166     }
167 
168     /**
169      * Clone the FO nodes in the parent iterator,
170      * attach the new nodes to the new parent,
171      * and map the new nodes to the existing property lists.
172      * FOText nodes are also in the new map, with a null value.
173      * Clone the subtree by a recursive call to this method.
174      * @param parentIter the iterator over the children of the old parent
175      * @param newParent the new parent for the cloned nodes
176      * @param marker the marker that contains the old property list mapping
177      * @param parentPropertyList the parent PropertyList
178      * @throws FOPException in case there was an error
179      */
cloneSubtree(Iterator parentIter, FONode newParent, Marker marker, PropertyList parentPropertyList)180     private void cloneSubtree(Iterator parentIter, FONode newParent,
181                               Marker marker, PropertyList parentPropertyList)
182         throws FOPException {
183         if (parentIter != null) {
184             FONode child;
185             while (parentIter.hasNext()) {
186                 child = (FONode) parentIter.next();
187                 cloneSingleNode(child, newParent,
188                         marker, parentPropertyList);
189             }
190         }
191     }
192 
cloneFromMarker(Marker marker)193     private void cloneFromMarker(Marker marker)
194         throws FOPException {
195         cloneSubtree(marker.getChildNodes(), this,
196                         marker, propertyList);
197         handleWhiteSpaceFor(this, null);
198     }
199 
200     /**
201      * Clone the subtree of the given marker
202      *
203      * @param marker the marker that is to be cloned
204      */
bindMarker(Marker marker)205     public void bindMarker(Marker marker) {
206         // clean up remnants from a possible earlier layout
207         if (firstChild != null) {
208             currentTextNode = null;
209             firstChild = null;
210         }
211         if (marker.getChildNodes() != null) {
212             try {
213                 restoreFOEventHandlerState();
214                 cloneFromMarker(marker);
215             } catch (FOPException exc) {
216                 getFOValidationEventProducer().markerCloningFailed(this,
217                         marker.getMarkerClassName(), exc, getLocator());
218             }
219         } else if (log.isDebugEnabled()) {
220             log.debug("Empty marker retrieved...");
221         }
222     }
223 
restoreFOEventHandlerState()224     protected abstract void restoreFOEventHandlerState();
225 
226     /**
227      * Return the value for the <code>retrieve-class-name</code>
228      * property
229      *
230      * @return the value for retrieve-class-name
231      */
getRetrieveClassName()232     public String getRetrieveClassName() {
233         return this.retrieveClassName;
234     }
235 
setBoundaryLabel(String label)236     protected void setBoundaryLabel(String label) {
237         this.boundaryLabel = label;
238     }
239 
setPositionLabel(String label)240     protected void setPositionLabel(String label) {
241         this.positionLabel = label;
242     }
243 
getBoundaryLabel()244     public String getBoundaryLabel() {
245         return this.boundaryLabel;
246     }
247 
getPositionLabel()248     public String getPositionLabel() {
249         return this.positionLabel;
250     }
251 
setPosition(int position)252     protected void setPosition(int position) {
253         this.position = position;
254     }
255 
setBoundary(int boundary)256     protected void setBoundary(int boundary) {
257         this.boundary = boundary;
258     }
259 
getPosition()260     public int getPosition() {
261         return this.position;
262     }
263 
getBoundary()264     public int getBoundary() {
265         return this.boundary;
266     }
267 
getLocalName()268     public abstract String getLocalName();
269 
getNameId()270     public abstract int getNameId();
271 
changePositionTo(int position)272     public void changePositionTo(int position) {
273         this.position = position;
274     }
275 }
276