]> _ Git - cubeextranet.git/blob
eed8df1af50a29fe02a2005ef5ad49d83d1c0cba
[cubeextranet.git] /
1 /*\r
2  * Licensed to the Apache Software Foundation (ASF) under one or more\r
3  * contributor license agreements.  See the NOTICE file distributed with\r
4  * this work for additional information regarding copyright ownership.\r
5  * The ASF licenses this file to You under the Apache License, Version 2.0\r
6  * (the "License"); you may not use this file except in compliance with\r
7  * the License.  You may obtain a copy of the License at\r
8  *\r
9  *      http://www.apache.org/licenses/LICENSE-2.0\r
10  *\r
11  * Unless required by applicable law or agreed to in writing, software\r
12  * distributed under the License is distributed on an "AS IS" BASIS,\r
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
14  * See the License for the specific language governing permissions and\r
15  * limitations under the License.\r
16  */\r
17 package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline;\r
18 \r
19 import org.apache.pdfbox.cos.COSBase;\r
20 import org.apache.pdfbox.cos.COSDictionary;\r
21 \r
22 import org.apache.pdfbox.pdmodel.common.COSObjectable;\r
23 \r
24 /**\r
25  * This represents an node in an outline in a pdf document.\r
26  *\r
27  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>\r
28  * @version $Revision: 1.3 $\r
29  */\r
30 public class PDOutlineNode implements COSObjectable\r
31 {\r
32     /**\r
33      * The dictionary for this node.\r
34      */\r
35     protected COSDictionary node;\r
36 \r
37     /**\r
38      * Default Constructor.\r
39      */\r
40     public PDOutlineNode()\r
41     {\r
42         node = new COSDictionary();\r
43     }\r
44 \r
45     /**\r
46      * Default Constructor.\r
47      *\r
48      * @param dict The dictionary storage.\r
49      */\r
50     public PDOutlineNode( COSDictionary dict)\r
51     {\r
52         node = dict;\r
53     }\r
54 \r
55     /**\r
56      * Convert this standard java object to a COS object.\r
57      *\r
58      * @return The cos object that matches this Java object.\r
59      */\r
60     public COSBase getCOSObject()\r
61     {\r
62         return node;\r
63     }\r
64 \r
65     /**\r
66      * Convert this standard java object to a COS object.\r
67      *\r
68      * @return The cos object that matches this Java object.\r
69      */\r
70     public COSDictionary getCOSDictionary()\r
71     {\r
72         return node;\r
73     }\r
74 \r
75     /**\r
76      * Get the parent of this object.  This will either be a DocumentOutline or an OutlineItem.\r
77      *\r
78      * @return The parent of this object, or null if this is the document outline and there\r
79      * is no parent.\r
80      */\r
81     protected PDOutlineNode getParent()\r
82     {\r
83         PDOutlineNode retval = null;\r
84         COSDictionary parent = (COSDictionary)node.getDictionaryObject( "Parent", "P" );\r
85         if( parent != null )\r
86         {\r
87             if( parent.getDictionaryObject( "Parent", "P" ) == null )\r
88             {\r
89                 retval = new PDDocumentOutline( parent );\r
90             }\r
91             else\r
92             {\r
93                 retval = new PDOutlineItem( parent );\r
94             }\r
95         }\r
96 \r
97         return retval;\r
98     }\r
99 \r
100     /**\r
101      * Set the parent of this object, this is maintained by these objects and should not\r
102      * be called by any clients of PDFBox code.\r
103      *\r
104      * @param parent The parent of this object.\r
105      */\r
106     protected void setParent( PDOutlineNode parent )\r
107     {\r
108         node.setItem( "Parent", parent );\r
109     }\r
110 \r
111     /**\r
112      * append a child node to this node.\r
113      *\r
114      * @param outlineNode The node to add.\r
115      */\r
116     public void appendChild( PDOutlineItem outlineNode )\r
117     {\r
118         outlineNode.setParent( this );\r
119         if( getFirstChild() == null )\r
120         {\r
121             int currentOpenCount = getOpenCount();\r
122             setFirstChild( outlineNode );\r
123             //1 for the the item we are adding;\r
124             int numberOfOpenNodesWeAreAdding = 1;\r
125             if( outlineNode.isNodeOpen() )\r
126             {\r
127                 numberOfOpenNodesWeAreAdding += outlineNode.getOpenCount();\r
128             }\r
129             if( isNodeOpen() )\r
130             {\r
131                 setOpenCount( currentOpenCount + numberOfOpenNodesWeAreAdding );\r
132             }\r
133             else\r
134             {\r
135                 setOpenCount( currentOpenCount - numberOfOpenNodesWeAreAdding );\r
136             }\r
137             updateParentOpenCount( numberOfOpenNodesWeAreAdding );\r
138         }\r
139         else\r
140         {\r
141             PDOutlineItem previousLastChild = getLastChild();\r
142             previousLastChild.insertSiblingAfter( outlineNode );\r
143         }\r
144         \r
145         PDOutlineItem lastNode = outlineNode;\r
146         while(lastNode.getNextSibling() != null)\r
147         {\r
148             lastNode = lastNode.getNextSibling();\r
149         }\r
150         setLastChild( lastNode );\r
151     }\r
152 \r
153     /**\r
154      * Return the first child or null if there is no child.\r
155      *\r
156      * @return The first child.\r
157      */\r
158     public PDOutlineItem getFirstChild()\r
159     {\r
160         PDOutlineItem last = null;\r
161         COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "First" );\r
162         if( lastDic != null )\r
163         {\r
164             last = new PDOutlineItem( lastDic );\r
165         }\r
166         return last;\r
167     }\r
168 \r
169     /**\r
170      * Set the first child, this will be maintained by this class.\r
171      *\r
172      * @param outlineNode The new first child.\r
173      */\r
174     protected void setFirstChild( PDOutlineNode outlineNode )\r
175     {\r
176         node.setItem( "First", outlineNode );\r
177     }\r
178 \r
179     /**\r
180      * Return the last child or null if there is no child.\r
181      *\r
182      * @return The last child.\r
183      */\r
184     public PDOutlineItem getLastChild()\r
185     {\r
186         PDOutlineItem last = null;\r
187         COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Last" );\r
188         if( lastDic != null )\r
189         {\r
190             last = new PDOutlineItem( lastDic );\r
191         }\r
192         return last;\r
193     }\r
194 \r
195     /**\r
196      * Set the last child, this will be maintained by this class.\r
197      *\r
198      * @param outlineNode The new last child.\r
199      */\r
200     protected void setLastChild( PDOutlineNode outlineNode )\r
201     {\r
202         node.setItem( "Last", outlineNode );\r
203     }\r
204 \r
205     /**\r
206      * Get the number of open nodes.  Or a negative number if this node\r
207      * is closed.  See PDF Reference for more details.  This value\r
208      * is updated as you append children and siblings.\r
209      *\r
210      * @return The Count attribute of the outline dictionary.\r
211      */\r
212     public int getOpenCount()\r
213     {\r
214         return node.getInt( "Count", 0 );\r
215     }\r
216 \r
217     /**\r
218      * Set the open count.  This number is automatically managed for you\r
219      * when you add items to the outline.\r
220      *\r
221      * @param openCount The new open cound.\r
222      */\r
223     protected void setOpenCount( int openCount )\r
224     {\r
225         node.setInt( "Count", openCount );\r
226     }\r
227 \r
228     /**\r
229      * This will set this node to be open when it is shown in the viewer.  By default, when\r
230      * a new node is created it will be closed.\r
231      * This will do nothing if the node is already open.\r
232      */\r
233     public void openNode()\r
234     {\r
235         //if the node is already open then do nothing.\r
236         if( !isNodeOpen() )\r
237         {\r
238             int openChildrenCount = 0;\r
239             PDOutlineItem currentChild = getFirstChild();\r
240             while( currentChild != null )\r
241             {\r
242                 //first increase by one for the current child\r
243                 openChildrenCount++;\r
244                 //then increase by the number of open nodes the child has\r
245                 if( currentChild.isNodeOpen() )\r
246                 {\r
247                     openChildrenCount += currentChild.getOpenCount();\r
248                 }\r
249                 currentChild = currentChild.getNextSibling();\r
250             }\r
251             setOpenCount( openChildrenCount );\r
252             updateParentOpenCount( openChildrenCount );\r
253         }\r
254     }\r
255 \r
256     /**\r
257      * Close this node.\r
258      *\r
259      */\r
260     public void closeNode()\r
261     {\r
262         //if the node is already closed then do nothing.\r
263         if( isNodeOpen() )\r
264         {\r
265             int openCount = getOpenCount();\r
266             updateParentOpenCount( -openCount );\r
267             setOpenCount( -openCount );\r
268         }\r
269     }\r
270 \r
271     /**\r
272      * Node is open if the open count is greater than zero.\r
273      * @return true if this node is open.\r
274      */\r
275     public boolean isNodeOpen()\r
276     {\r
277         return getOpenCount() > 0;\r
278     }\r
279 \r
280     /**\r
281      * The count parameter needs to be updated when you add or remove elements to\r
282      * the outline.  When you add an element at a lower level then you need to\r
283      * increase all of the parents.\r
284      *\r
285      * @param amount The amount to update by.\r
286      */\r
287     protected void updateParentOpenCount( int amount )\r
288     {\r
289         PDOutlineNode parent = getParent();\r
290         if( parent != null )\r
291         {\r
292             int currentCount = parent.getOpenCount();\r
293             //if the currentCount is negative or it is absent then\r
294             //we will treat it as negative.  The default is to be negative.\r
295             boolean negative = currentCount < 0 ||\r
296                 parent.getCOSDictionary().getDictionaryObject( "Count" ) == null;\r
297             currentCount = Math.abs( currentCount );\r
298             currentCount += amount;\r
299             if( negative )\r
300             {\r
301                 currentCount = -currentCount;\r
302             }\r
303             parent.setOpenCount( currentCount );\r
304             //recursively call parent to update count, but the parents count is only\r
305             //updated if this is an open node\r
306             if( !negative )\r
307             {\r
308                 parent.updateParentOpenCount( amount );\r
309             }\r
310         }\r
311     }\r
312 }\r