<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>PDOutlineNode.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Apache PDFBox</a> > <a href="index.source.html" class="el_package">org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline</a> > <span class="el_source">PDOutlineNode.java</span></div><h1>PDOutlineNode.java</h1><pre class="source lang-java linenums">/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.common.PDDictionaryWrapper; /** * Base class for a node in the outline of a PDF document. * * @author Ben Litchfield */ public abstract class PDOutlineNode extends PDDictionaryWrapper { /** * Default Constructor. */ public PDOutlineNode() <span class="fc" id="L36"> {</span> <span class="fc" id="L37"> }</span> /** * @param dict The dictionary storage. */ public PDOutlineNode(COSDictionary dict) { <span class="fc" id="L44"> super(dict);</span> <span class="fc" id="L45"> }</span> /** * @return The parent of this node or null if there is no parent. */ PDOutlineNode getParent() { <span class="fc" id="L52"> COSDictionary parent = getCOSObject().getCOSDictionary(COSName.PARENT);</span> <span class="fc bfc" id="L53" title="All 2 branches covered."> if (parent != null)</span> { <span class="fc bfc" id="L55" title="All 2 branches covered."> if (COSName.OUTLINES.equals(parent.getCOSName(COSName.TYPE)))</span> { <span class="fc" id="L57"> return new PDDocumentOutline(parent);</span> } <span class="fc" id="L59"> return new PDOutlineItem(parent);</span> } <span class="fc" id="L61"> return null;</span> } void setParent(PDOutlineNode parent) { <span class="fc" id="L66"> getCOSObject().setItem(COSName.PARENT, parent);</span> <span class="fc" id="L67"> }</span> /** * Adds the given node to the bottom of the children list. * * @param newChild The node to add. * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ public void addLast(PDOutlineItem newChild) { <span class="fc" id="L78"> requireSingleNode(newChild);</span> <span class="fc" id="L79"> append(newChild);</span> <span class="fc" id="L80"> updateParentOpenCountForAddedChild(newChild);</span> <span class="fc" id="L81"> }</span> /** * Adds the given node to the top of the children list. * * @param newChild The node to add. * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ public void addFirst(PDOutlineItem newChild) { <span class="fc" id="L92"> requireSingleNode(newChild);</span> <span class="fc" id="L93"> prepend(newChild);</span> <span class="fc" id="L94"> updateParentOpenCountForAddedChild(newChild);</span> <span class="fc" id="L95"> }</span> /** * @param node * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next * sibling) */ void requireSingleNode(PDOutlineItem node) { <span class="pc bpc" id="L104" title="1 of 4 branches missed."> if (node.getNextSibling() != null || node.getPreviousSibling() != null)</span> { <span class="fc" id="L106"> throw new IllegalArgumentException("A single node with no siblings is required");</span> } <span class="fc" id="L108"> }</span> /** * Appends the child to the linked list of children. This method only adjust pointers but doesn't take care of the * Count key in the parent hierarchy. * * @param newChild */ private void append(PDOutlineItem newChild) { <span class="fc" id="L118"> newChild.setParent(this);</span> <span class="fc bfc" id="L119" title="All 2 branches covered."> if (!hasChildren())</span> { <span class="fc" id="L121"> setFirstChild(newChild);</span> } else { <span class="fc" id="L125"> PDOutlineItem previousLastChild = getLastChild();</span> <span class="fc" id="L126"> previousLastChild.setNextSibling(newChild);</span> <span class="fc" id="L127"> newChild.setPreviousSibling(previousLastChild);</span> } <span class="fc" id="L129"> setLastChild(newChild);</span> <span class="fc" id="L130"> }</span> /** * Prepends the child to the linked list of children. This method only adjust pointers but doesn't take care of the * Count key in the parent hierarchy. * * @param newChild */ private void prepend(PDOutlineItem newChild) { <span class="fc" id="L140"> newChild.setParent(this);</span> <span class="fc bfc" id="L141" title="All 2 branches covered."> if (!hasChildren())</span> { <span class="fc" id="L143"> setLastChild(newChild);</span> } else { <span class="fc" id="L147"> PDOutlineItem previousFirstChild = getFirstChild();</span> <span class="fc" id="L148"> newChild.setNextSibling(previousFirstChild);</span> <span class="fc" id="L149"> previousFirstChild.setPreviousSibling(newChild);</span> } <span class="fc" id="L151"> setFirstChild(newChild);</span> <span class="fc" id="L152"> }</span> void updateParentOpenCountForAddedChild(PDOutlineItem newChild) { <span class="fc" id="L156"> int delta = 1;</span> <span class="fc bfc" id="L157" title="All 2 branches covered."> if (newChild.isNodeOpen())</span> { <span class="fc" id="L159"> delta += newChild.getOpenCount();</span> } <span class="fc" id="L161"> newChild.updateParentOpenCount(delta);</span> <span class="fc" id="L162"> }</span> /** * @return true if the node has at least one child */ public boolean hasChildren() { <span class="fc bfc" id="L169" title="All 2 branches covered."> return getCOSObject().getCOSDictionary(COSName.FIRST) != null;</span> } PDOutlineItem getOutlineItem(COSName name) { <span class="fc" id="L174"> COSDictionary outline = getCOSObject().getCOSDictionary(name);</span> <span class="fc bfc" id="L175" title="All 2 branches covered."> return outline != null ? new PDOutlineItem(outline) : null;</span> } /** * @return The first child or null if there is no child. */ public PDOutlineItem getFirstChild() { <span class="fc" id="L183"> return getOutlineItem(COSName.FIRST);</span> } /** * Set the first child, this will be maintained by this class. * * @param outlineNode The new first child. */ void setFirstChild(PDOutlineNode outlineNode) { <span class="fc" id="L193"> getCOSObject().setItem(COSName.FIRST, outlineNode);</span> <span class="fc" id="L194"> }</span> /** * @return The last child or null if there is no child. */ public PDOutlineItem getLastChild() { <span class="fc" id="L201"> return getOutlineItem(COSName.LAST);</span> } /** * Set the last child, this will be maintained by this class. * * @param outlineNode The new last child. */ void setLastChild(PDOutlineNode outlineNode) { <span class="fc" id="L211"> getCOSObject().setItem(COSName.LAST, outlineNode);</span> <span class="fc" id="L212"> }</span> /** * Get the number of open nodes or a negative number if this node is closed. * See PDF Reference 32000-1:2008 table 152 and 153 for more details. This * value is updated as you append children and siblings. * * @return The Count attribute of the outline dictionary. */ public int getOpenCount() { <span class="fc" id="L223"> return getCOSObject().getInt(COSName.COUNT, 0);</span> } /** * Set the open count. This number is automatically managed for you when you add items to the outline. * * @param openCount The new open count. */ void setOpenCount(int openCount) { <span class="fc" id="L233"> getCOSObject().setInt(COSName.COUNT, openCount);</span> <span class="fc" id="L234"> }</span> /** * This will set this node to be open when it is shown in the viewer. By default, when a new node is created it will * be closed. This will do nothing if the node is already open. */ public void openNode() { //if the node is already open then do nothing. <span class="fc bfc" id="L243" title="All 2 branches covered."> if( !isNodeOpen() )</span> { <span class="fc" id="L245"> switchNodeCount();</span> } <span class="fc" id="L247"> }</span> /** * Close this node. * */ public void closeNode() { <span class="fc bfc" id="L255" title="All 2 branches covered."> if (isNodeOpen())</span> { <span class="fc" id="L257"> switchNodeCount();</span> } <span class="fc" id="L259"> }</span> private void switchNodeCount() { <span class="fc" id="L263"> int openCount = getOpenCount();</span> <span class="fc" id="L264"> setOpenCount(-openCount);</span> <span class="fc" id="L265"> updateParentOpenCount(-openCount);</span> <span class="fc" id="L266"> }</span> /** * @return true if this node count is greater than zero, false otherwise. */ public boolean isNodeOpen() { <span class="fc bfc" id="L273" title="All 2 branches covered."> return getOpenCount() > 0;</span> } /** * The count parameter needs to be updated when you add, remove, open or close outline items. * * @param delta The amount to update by. */ void updateParentOpenCount(int delta) { <span class="fc" id="L283"> PDOutlineNode parent = getParent();</span> <span class="fc bfc" id="L284" title="All 2 branches covered."> if (parent != null)</span> { <span class="fc bfc" id="L286" title="All 2 branches covered."> if (parent.isNodeOpen())</span> { <span class="fc" id="L288"> parent.setOpenCount(parent.getOpenCount() + delta);</span> <span class="fc" id="L289"> parent.updateParentOpenCount(delta);</span> } else { <span class="fc" id="L293"> parent.setOpenCount(parent.getOpenCount() - delta);</span> } } <span class="fc" id="L296"> }</span> /** * @return An {@link Iterable} view of the items children */ public Iterable<PDOutlineItem> children() { <span class="fc" id="L303"> return () -> new PDOutlineItemIterator(getFirstChild());</span> } } </pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.8.202204050719</span></div></body></html>