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