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: Root.java 1835810 2018-07-13 10:29:57Z ssteiner $ */
19 
20 package org.apache.fop.fo.pagination;
21 
22 // java
23 import java.util.List;
24 import java.util.Locale;
25 
26 import org.xml.sax.Locator;
27 
28 import org.apache.fop.apps.FOPException;
29 import org.apache.fop.fo.FOEventHandler;
30 import org.apache.fop.fo.FONode;
31 import org.apache.fop.fo.FOTreeBuilderContext;
32 import org.apache.fop.fo.FObj;
33 import org.apache.fop.fo.PropertyList;
34 import org.apache.fop.fo.ValidationException;
35 import org.apache.fop.fo.extensions.destination.Destination;
36 import org.apache.fop.fo.pagination.bookmarks.BookmarkTree;
37 import org.apache.fop.fo.properties.CommonAccessibility;
38 import org.apache.fop.fo.properties.CommonAccessibilityHolder;
39 import org.apache.fop.fo.properties.CommonHyphenation;
40 
41 /**
42  * Class modeling the <a href="http://www.w3.org/TR/xsl/#fo_root">
43  * <code>fo:root</code></a> formatting object.
44  * Contains page masters, page-sequences.
45  */
46 public class Root extends FObj implements CommonAccessibilityHolder {
47 
48     private CommonAccessibility commonAccessibility;
49 
50     private int mediaUsage;
51 
52     private LayoutMasterSet layoutMasterSet;
53     private Declarations declarations;
54     private BookmarkTree bookmarkTree;
55     private List<Destination> destinationList;
56     private List<PageSequence> pageSequences;
57     private Locale locale;
58 
59     // temporary until above list populated
60     private boolean pageSequenceFound;
61 
62     /**
63      * Keeps count of page number from over PageSequence instances
64      */
65     private int endingPageNumberOfPreviousSequence;
66     private int totalPagesGenerated;
67 
68     /**
69      * Context class used while building the FO tree.
70      */
71     private FOTreeBuilderContext builderContext;
72 
73     /**
74      * FOEventHandler object for this FO Tree
75      */
76     private FOEventHandler foEventHandler;
77 
78     private PageSequence lastSeq;
79 
setLastSeq(PageSequence seq)80     public void setLastSeq(PageSequence seq) {
81         lastSeq = seq;
82     }
83 
getLastSeq()84     public PageSequence getLastSeq() {
85           return lastSeq;
86     }
87 
88     /**
89      * Base constructor
90      *
91      * @param parent {@link FONode} that is the parent of this object
92      * Note: parent should be null for the fo:root.
93      */
Root(FONode parent)94     public Root(FONode parent) {
95         super(parent);
96         pageSequences = new java.util.ArrayList<PageSequence>();
97     }
98 
99     /** {@inheritDoc} */
bind(PropertyList pList)100     public void bind(PropertyList pList) throws FOPException {
101         super.bind(pList);
102         commonAccessibility = CommonAccessibility.getInstance(pList);
103         mediaUsage = pList.get(PR_MEDIA_USAGE).getEnum();
104         String language = pList.get(PR_LANGUAGE).getString();
105         String country = pList.get(PR_COUNTRY).getString();
106         locale = CommonHyphenation.toLocale(language, country);
107     }
108 
109      /** {@inheritDoc} */
startOfNode()110     public void startOfNode() throws FOPException {
111         foEventHandler.startRoot(this);
112     }
113 
114     /** {@inheritDoc} */
endOfNode()115     public void endOfNode() throws FOPException {
116         if (!pageSequenceFound || layoutMasterSet == null) {
117             missingChildElementError("(layout-master-set, declarations?, "
118                 + "bookmark-tree?, (page-sequence|fox:external-document)+)");
119         }
120         foEventHandler.endRoot(this);
121     }
122 
123     /**
124      * {@inheritDoc}
125      * <br>XSL 1.0 Spec: (layout-master-set,declarations?,page-sequence+)
126      * <br>FOP: (layout-master-set, declarations?, fox:bookmarks?, page-sequence+)
127      */
validateChildNode(Locator loc, String nsURI, String localName)128     protected void validateChildNode(Locator loc, String nsURI, String localName)
129         throws ValidationException {
130         if (FO_URI.equals(nsURI)) {
131             if (localName.equals("layout-master-set")) {
132                 if (layoutMasterSet != null) {
133                     tooManyNodesError(loc, "fo:layout-master-set");
134                 }
135             } else if (localName.equals("declarations")) {
136                 if (layoutMasterSet == null) {
137                     nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:declarations");
138                 } else if (declarations != null) {
139                     tooManyNodesError(loc, "fo:declarations");
140                 } else if (bookmarkTree != null) {
141                     nodesOutOfOrderError(loc, "fo:declarations", "fo:bookmark-tree");
142                 } else if (pageSequenceFound) {
143                     nodesOutOfOrderError(loc, "fo:declarations", "fo:page-sequence");
144                 }
145             } else if (localName.equals("bookmark-tree")) {
146                 if (layoutMasterSet == null) {
147                     nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:bookmark-tree");
148                 } else if (bookmarkTree != null) {
149                     tooManyNodesError(loc, "fo:bookmark-tree");
150                 } else if (pageSequenceFound) {
151                     nodesOutOfOrderError(loc, "fo:bookmark-tree", "fo:page-sequence");
152                 }
153             } else if (localName.equals("page-sequence")) {
154                 if (layoutMasterSet == null) {
155                     nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:page-sequence");
156                 } else {
157                     pageSequenceFound = true;
158                 }
159             } else {
160                 invalidChildError(loc, nsURI, localName);
161             }
162         } else {
163             if (FOX_URI.equals(nsURI)) {
164                 if ("external-document".equals(localName)) {
165                     pageSequenceFound = true;
166                 }
167             }
168             //invalidChildError(loc, nsURI, localName);
169             //Ignore non-FO elements under root
170         }
171     }
172 
173 
174     /**
175      * @param loc location in the source file
176      * @param child the {@link FONode} to validate against
177      * @throws ValidationException if the incoming node is not a valid child for the given FO
178      */
validateChildNode(Locator loc, FONode child)179     protected void validateChildNode(Locator loc, FONode child) throws ValidationException {
180         if (child instanceof AbstractPageSequence) {
181             pageSequenceFound = true;
182         }
183     }
184 
185     /** {@inheritDoc} */
getCommonAccessibility()186     public CommonAccessibility getCommonAccessibility() {
187         return commonAccessibility;
188     }
189 
190     /**
191      * Sets the FOEventHandler object that this Root is attached to
192      * @param foEventHandler the FOEventHandler object
193      */
setFOEventHandler(FOEventHandler foEventHandler)194     public void setFOEventHandler(FOEventHandler foEventHandler) {
195         this.foEventHandler = foEventHandler;
196     }
197 
198     /**
199      * This method overrides the FONode version. The FONode version calls the
200      * method by the same name for the parent object. Since Root is at the top
201      * of the tree, it returns the actual FOEventHandler object. Thus, any FONode
202      * can use this chain to find which FOEventHandler it is being built for.
203      * @return the FOEventHandler implementation that this Root is attached to
204      */
getFOEventHandler()205     public FOEventHandler getFOEventHandler() {
206         return foEventHandler;
207     }
208 
209     /**
210      * Sets the builder context for this FO tree.
211      * @param context the builder context to be used
212      */
setBuilderContext(FOTreeBuilderContext context)213     public void setBuilderContext(FOTreeBuilderContext context) {
214         this.builderContext = context;
215     }
216 
217     /** {@inheritDoc} */
getBuilderContext()218     public FOTreeBuilderContext getBuilderContext() {
219         return this.builderContext;
220     }
221 
222     /**
223     * Gets the last page number generated by the previous page-sequence
224     * @return the last page number, 0 if no page sequences yet generated
225     */
getEndingPageNumberOfPreviousSequence()226     public int getEndingPageNumberOfPreviousSequence() {
227         return endingPageNumberOfPreviousSequence;
228     }
229 
230     /**
231      * Returns the total number of pages generated by FOP
232      * (May not equal endingPageNumberOfPreviousSequence due to
233      * initial-page-number property on fo:page-sequences.)
234      * @return the last page number, 0 if no page sequences yet generated
235      */
getTotalPagesGenerated()236     public int getTotalPagesGenerated() {
237         return totalPagesGenerated;
238     }
239 
240     /**
241      * Notify additional pages generated to increase the totalPagesGenerated counter
242      * @param lastPageNumber the last page number generated by the sequence
243      * @param additionalPages the total pages generated by the sequence (for statistics)
244      * @throws IllegalArgumentException for negative additional page counts
245      */
notifyPageSequenceFinished(int lastPageNumber, int additionalPages)246     public void notifyPageSequenceFinished(int lastPageNumber, int additionalPages)
247             throws IllegalArgumentException {
248 
249         if (additionalPages >= 0) {
250             totalPagesGenerated += additionalPages;
251             endingPageNumberOfPreviousSequence = lastPageNumber;
252         } else {
253             throw new IllegalArgumentException(
254                 "Number of additional pages must be zero or greater.");
255         }
256     }
257 
258     /**
259      * Returns the number of PageSequence instances.
260      * @return the number of PageSequence instances
261      */
getPageSequenceCount()262     public int getPageSequenceCount() {
263         return pageSequences.size();
264     }
265 
266     /**
267      * Some properties, such as 'force-page-count', require a
268      * page-sequence to know about some properties of the next.
269      * @param current the current PageSequence
270      * @return succeeding PageSequence; null if none
271      */
getSucceedingPageSequence(PageSequence current)272     public PageSequence getSucceedingPageSequence(PageSequence current) {
273         int currentIndex = pageSequences.indexOf(current);
274         if (currentIndex == -1) {
275             return null;
276         }
277         if (currentIndex < (pageSequences.size() - 1)) {
278             return pageSequences.get(currentIndex + 1);
279         } else {
280             return null;
281         }
282     }
283 
284     /**
285      * Adds the specified page sequence.
286      *
287      * @param pageSequence The page sequence to add
288      */
addPageSequence(PageSequence pageSequence)289     public void addPageSequence(PageSequence pageSequence) {
290         pageSequences.add(pageSequence);
291     }
292 
293     /**
294      * Returns the last page sequence (current while parsing).
295      *
296      * @return The last page sequence or null
297      */
getLastPageSequence()298     public PageSequence getLastPageSequence() {
299         if (getPageSequenceCount() > 0) {
300             return pageSequences.get(getPageSequenceCount() - 1);
301         } else {
302             return null;
303         }
304     }
305 
306     /**
307      * Returns the associated LayoutMasterSet.
308      * @return the LayoutMasterSet instance
309      */
getLayoutMasterSet()310     public LayoutMasterSet getLayoutMasterSet() {
311         return this.layoutMasterSet;
312     }
313 
314     /**
315      * Sets the associated LayoutMasterSet.
316      * @param layoutMasterSet the LayoutMasterSet to use
317      */
setLayoutMasterSet(LayoutMasterSet layoutMasterSet)318     public void setLayoutMasterSet(LayoutMasterSet layoutMasterSet) {
319         this.layoutMasterSet = layoutMasterSet;
320     }
321 
322     /**
323      * Returns the associated Declarations.
324      * @return the Declarations instance
325      */
getDeclarations()326     public Declarations getDeclarations() {
327         return this.declarations;
328     }
329 
330     /**
331      * Sets the associated Declarations.
332      * @param declarations the Declarations to use
333      */
setDeclarations(Declarations declarations)334     public void setDeclarations(Declarations declarations) {
335         this.declarations = declarations;
336     }
337 
338     /**
339      * Set the BookmarkTree object for this FO
340      * @param bookmarkTree the BookmarkTree object
341      */
setBookmarkTree(BookmarkTree bookmarkTree)342     public void setBookmarkTree(BookmarkTree bookmarkTree) {
343         this.bookmarkTree = bookmarkTree;
344     }
345 
346     /**
347      * Add a Destination object to this FO
348      * @param destination the Destination object to add
349      */
addDestination(Destination destination)350     public void addDestination(Destination destination) {
351         if (destinationList == null) {
352           destinationList = new java.util.ArrayList<Destination>();
353         }
354         destinationList.add(destination);
355     }
356 
357     /**
358      * Public accessor for the list of Destination objects for this FO
359      * @return the Destination object
360      */
getDestinationList()361     public List getDestinationList() {
362         return destinationList;
363     }
364 
365     /**
366      * Public accessor for the BookmarkTree object for this FO
367      * @return the BookmarkTree object
368      */
getBookmarkTree()369     public BookmarkTree getBookmarkTree() {
370         return bookmarkTree;
371     }
372 
373     /** {@inheritDoc} */
getRoot()374     public Root getRoot() {
375         return this;
376     }
377 
378     /** {@inheritDoc} */
getLocalName()379     public String getLocalName() {
380         return "root";
381     }
382 
383     /**
384      * {@inheritDoc}
385      * @return {@link org.apache.fop.fo.Constants#FO_ROOT}
386      */
getNameId()387     public int getNameId() {
388         return FO_ROOT;
389     }
390 
391 
392     /** @return locale proprty. */
getLocale()393     public Locale getLocale() {
394         return locale;
395     }
396 
397 }
398