Index: examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java
===================================================================
--- examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java	(revisione 1659261)
+++ examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreateBookmarks.java	(copia locale)
@@ -67,7 +67,7 @@
                 document.getDocumentCatalog().setDocumentOutline( outline );
                 PDOutlineItem pagesOutline = new PDOutlineItem();
                 pagesOutline.setTitle( "All Pages" );
-                outline.appendChild( pagesOutline );
+                outline.addLast( pagesOutline );
                 int pageNum = 0;
                 for( PDPage page : document.getPages() )
                 {
@@ -77,7 +77,7 @@
                     PDOutlineItem bookmark = new PDOutlineItem();
                     bookmark.setDestination( dest );
                     bookmark.setTitle( "Page " + pageNum );
-                    pagesOutline.appendChild( bookmark );
+                    pagesOutline.addLast( bookmark );
                 }
                 pagesOutline.openNode();
                 outline.openNode();
Index: pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java	(revisione 1659261)
+++ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutline.java	(copia locale)
@@ -25,25 +25,38 @@
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * @version $Revision: 1.2 $
  */
-public class PDDocumentOutline extends PDOutlineNode
+public final class PDDocumentOutline extends PDOutlineNode
 {
 
-    /**
-     * Default Constructor.
-     */
     public PDDocumentOutline()
     {
-        super();
-        node.setName( COSName.TYPE, "Outlines" );
+    	getCOSDictionary().setName(COSName.TYPE, COSName.OUTLINES.getName());
     }
 
     /**
-     * Constructor for an existing document outline.
-     *
      * @param dic The storage dictionary.
      */
-    public PDDocumentOutline( COSDictionary dic )
+    public PDDocumentOutline(COSDictionary dic)
     {
-        super( dic );
+        super(dic);
+        getCOSDictionary().setName(COSName.TYPE, COSName.OUTLINES.getName());
     }
+
+    @Override
+    public boolean isNodeOpen()
+    {
+        return true;
+    }
+
+    @Override
+    public void openNode()
+    {
+        // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed
+    }
+
+    @Override
+    public void closeNode()
+    {
+        // The root of the outline hierarchy is not an OutlineItem and cannot be opened or closed
+    }
 }
Index: pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java	(revisione 1659261)
+++ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItem.java	(copia locale)
@@ -30,8 +30,8 @@
 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
 import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
 import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory;
-import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
 import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination;
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDNamedDestination;
@@ -39,18 +39,15 @@
 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
 
 /**
- * This represents an outline in a pdf document.
+ * This represents an outline item in a pdf document.
  *
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  */
-public class PDOutlineItem extends PDOutlineNode
+public final class PDOutlineItem extends PDOutlineNode
 {
     private static final int ITALIC_FLAG = 1;
     private static final int BOLD_FLAG = 2;
 
-    /**
-     * Default Constructor.
-     */
     public PDOutlineItem()
     {
         super();
@@ -57,57 +54,73 @@
     }
 
     /**
-     * Constructor for an existing outline item.
-     *
      * @param dic The storage dictionary.
      */
-    public PDOutlineItem( COSDictionary dic )
+    public PDOutlineItem(COSDictionary dic)
     {
-        super( dic );
+        super(dic);
     }
 
     /**
-     * Insert a sibling after this node.
+     * Insert a single sibling after this node.
      *
-     * @param item The item to insert.
+     * @param newSibling The item to insert.
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    public void insertSiblingAfter( PDOutlineItem item )
+    public void insertSiblingAfter(PDOutlineItem newSibling)
     {
-        item.setParent( getParent() );
+        requireSingleNode(newSibling);
+        PDOutlineNode parent = getParent();
+        newSibling.setParent(parent);
         PDOutlineItem next = getNextSibling();
-        setNextSibling( item );
-        item.setPreviousSibling( this );
-        if( next != null )
+        setNextSibling(newSibling);
+        newSibling.setPreviousSibling(this);
+        if (next != null)
         {
-            item.setNextSibling( next );
-            next.setPreviousSibling( item );
+            newSibling.setNextSibling(next);
+            next.setPreviousSibling(newSibling);
         }
-        updateParentOpenCount( 1 );
+        else if (parent != null)
+        {
+            getParent().setLastChild(newSibling);
+        }
+        updateParentOpenCountForAddedChild(newSibling);
     }
 
     /**
-     * {@inheritDoc}
+     * Insert a single sibling before this node.
+     *
+     * @param newSibling The item to insert.
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    @Override
-    public PDOutlineNode getParent()
+    public void insertSiblingBefore(PDOutlineItem newSibling)
     {
-        return super.getParent();
+        requireSingleNode(newSibling);
+        PDOutlineNode parent = getParent();
+        newSibling.setParent(parent);
+        PDOutlineItem previous = getPreviousSibling();
+        setPreviousSibling(newSibling);
+        newSibling.setNextSibling(this);
+        if (previous != null)
+        {
+            previous.setNextSibling(newSibling);
+            newSibling.setPreviousSibling(previous);
+        }
+        else if (parent != null)
+        {
+            getParent().setFirstChild(newSibling);
+        }
+        updateParentOpenCountForAddedChild(newSibling);
     }
 
     /**
-     * Return the previous sibling or null if there is no sibling.
-     *
-     * @return The previous sibling.
+     * @return The previous sibling or null if there is no sibling.
      */
     public PDOutlineItem getPreviousSibling()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( COSName.PREV );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getItem(COSName.PREV);
     }
 
     /**
@@ -115,25 +128,17 @@
      *
      * @param outlineNode The new previous sibling.
      */
-    protected void setPreviousSibling( PDOutlineNode outlineNode )
+    void setPreviousSibling(PDOutlineNode outlineNode)
     {
-        node.setItem( COSName.PREV, outlineNode );
+    	getCOSDictionary().setItem(COSName.PREV, outlineNode);
     }
 
     /**
-     * Return the next sibling or null if there is no next sibling.
-     *
-     * @return The next sibling.
+     * @return The next sibling or null if there is no next sibling.
      */
     public PDOutlineItem getNextSibling()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( COSName.NEXT );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getItem(COSName.NEXT);
     }
 
     /**
@@ -141,19 +146,17 @@
      *
      * @param outlineNode The new next sibling.
      */
-    protected void setNextSibling( PDOutlineNode outlineNode )
+    void setNextSibling(PDOutlineNode outlineNode)
     {
-        node.setItem( COSName.NEXT, outlineNode );
+    	getCOSDictionary().setItem(COSName.NEXT, outlineNode);
     }
 
     /**
-     * Get the title of this node.
-     *
      * @return The title of this node.
      */
     public String getTitle()
     {
-        return node.getString( COSName.TITLE );
+        return getCOSDictionary().getString(COSName.TITLE);
     }
 
     /**
@@ -161,20 +164,18 @@
      *
      * @param title The new title for this node.
      */
-    public void setTitle( String title )
+    public void setTitle(String title)
     {
-        node.setString( COSName.TITLE, title );
+    	getCOSDictionary().setString(COSName.TITLE, title);
     }
 
     /**
-     * Get the page destination of this node.
-     *
      * @return The page destination of this node.
      * @throws IOException If there is an error creating the destination.
      */
     public PDDestination getDestination() throws IOException
     {
-        return PDDestination.create( node.getDictionaryObject( COSName.DEST ) );
+        return PDDestination.create(getCOSDictionary().getDictionaryObject(COSName.DEST));
     }
 
     /**
@@ -182,9 +183,9 @@
      *
      * @param dest The new page destination for this node.
      */
-    public void setDestination( PDDestination dest )
+    public void setDestination(PDDestination dest)
     {
-        node.setItem( COSName.DEST, dest );
+    	getCOSDictionary().setItem(COSName.DEST, dest);
     }
 
     /**
@@ -192,21 +193,21 @@
      *
      * @param page The page to refer to.
      */
-    public void setDestination( PDPage page )
+    public void setDestination(PDPage page)
     {
         PDPageXYZDestination dest = null;
-        if( page != null )
+        if (page != null)
         {
             dest = new PDPageXYZDestination();
-            dest.setPage( page );
+            dest.setPage(page);
         }
-        setDestination( dest );
+        setDestination(dest);
     }
 
     /**
-     * This method will attempt to find the page in this PDF document that this outline points to.
-     * If the outline does not point to anything then this method will return null.  If the outline
-     * is an action that is not a GoTo action then this methods will throw the OutlineNotLocationException
+     * This method will attempt to find the page in this PDF document that this outline points to. If the outline does
+     * not point to anything then this method will return null. If the outline is an action that is not a GoTo action
+     * then this methods will throw the OutlineNotLocationException
      *
      * @param doc The document to get the page from.
      *
@@ -213,15 +214,15 @@
      * @return The page that this outline will go to when activated or null if it does not point to anything.
      * @throws IOException If there is an error when trying to find the page.
      */
-    public PDPage findDestinationPage( PDDocument doc ) throws IOException
+    public PDPage findDestinationPage(PDDocument doc) throws IOException
     {
         PDDestination dest = getDestination();
-        if( dest == null )
+        if (dest == null)
         {
             PDAction outlineAction = getAction();
-            if( outlineAction instanceof PDActionGoTo )
+            if (outlineAction instanceof PDActionGoTo)
             {
-                dest = ((PDActionGoTo)outlineAction).getDestination();
+                dest = ((PDActionGoTo) outlineAction).getDestination();
             }
             else
             {
@@ -230,17 +231,18 @@
         }
 
         PDPageDestination pageDestination;
-        if( dest instanceof PDNamedDestination )
+        if (dest instanceof PDNamedDestination)
         {
-            //if we have a named destination we need to lookup the PDPageDestination
-            PDNamedDestination namedDest = (PDNamedDestination)dest;
+            // if we have a named destination we need to lookup the PDPageDestination
+            PDNamedDestination namedDest = (PDNamedDestination) dest;
             PDDocumentNameDictionary namesDict = doc.getDocumentCatalog().getNames();
-            if( namesDict != null )
+            if (namesDict != null)
             {
                 PDDestinationNameTreeNode destsTree = namesDict.getDests();
-                if( destsTree != null )
+                if (destsTree != null)
                 {
-                    pageDestination = (PDPageDestination)destsTree.getValue( namedDest.getNamedDestination() );
+                    pageDestination = (PDPageDestination) destsTree.getValue(namedDest
+                            .getNamedDestination());
                 }
                 else
                 {
@@ -252,26 +254,26 @@
                 return null;
             }
         }
-        else if( dest instanceof PDPageDestination)
+        else if (dest instanceof PDPageDestination)
         {
             pageDestination = (PDPageDestination) dest;
         }
-        else if( dest == null )
+        else if (dest == null)
         {
             return null;
         }
         else
         {
-            throw new IOException( "Error: Unknown destination type " + dest );
+            throw new IOException("Error: Unknown destination type " + dest);
         }
 
         PDPage page = pageDestination.getPage();
-        if( page == null )
+        if (page == null)
         {
             int pageNumber = pageDestination.getPageNumber();
-            if( pageNumber != -1 )
+            if (pageNumber != -1)
             {
-                page = doc.getPage( pageNumber - 1 );
+                page = doc.getPage(pageNumber - 1);
             }
         }
 
@@ -285,7 +287,8 @@
      */
     public PDAction getAction()
     {
-        return PDActionFactory.createAction( (COSDictionary)node.getDictionaryObject( COSName.A ) );
+        return PDActionFactory.createAction((COSDictionary) getCOSDictionary().getDictionaryObject(
+                COSName.A));
     }
 
     /**
@@ -293,9 +296,9 @@
      *
      * @param action The new action for this node.
      */
-    public void setAction( PDAction action )
+    public void setAction(PDAction action)
     {
-        node.setItem( COSName.A, action );
+    	getCOSDictionary().setItem(COSName.A, action);
     }
 
     /**
@@ -306,10 +309,10 @@
     public PDStructureElement getStructureElement()
     {
         PDStructureElement se = null;
-        COSDictionary dic = (COSDictionary)node.getDictionaryObject( COSName.SE );
-        if( dic != null )
+        COSDictionary dic = (COSDictionary) getCOSDictionary().getDictionaryObject(COSName.SE);
+        if (dic != null)
         {
-            se = new PDStructureElement( dic );
+            se = new PDStructureElement(dic);
         }
         return se;
     }
@@ -319,14 +322,13 @@
      *
      * @param structureElement The new structure element for this node.
      */
-    public void setStructuredElement( PDStructureElement structureElement )
+    public void setStructuredElement(PDStructureElement structureElement)
     {
-        node.setItem( COSName.SE, structureElement );
+    	getCOSDictionary().setItem(COSName.SE, structureElement);
     }
 
     /**
-     * Get the RGB text color of this node.  Default is black and this method
-     * will never return null.
+     * Get the RGB text color of this node. Default is black and this method will never return null.
      *
      * @return The structure element of this node.
      */
@@ -333,12 +335,12 @@
     public PDColor getTextColor()
     {
         PDColor retval = null;
-        COSArray csValues = (COSArray)node.getDictionaryObject( COSName.C );
-        if( csValues == null )
+        COSArray csValues = (COSArray) getCOSDictionary().getDictionaryObject(COSName.C);
+        if (csValues == null)
         {
             csValues = new COSArray();
-            csValues.growToSize( 3, new COSFloat( 0 ) );
-            node.setItem( COSName.C, csValues );
+            csValues.growToSize(3, new COSFloat(0));
+            getCOSDictionary().setItem(COSName.C, csValues);
         }
         retval = new PDColor(csValues.toFloatArray(), PDDeviceRGB.INSTANCE);
         return retval;
@@ -349,9 +351,9 @@
      *
      * @param textColor The text color for this node.
      */
-    public void setTextColor( PDColor textColor )
+    public void setTextColor(PDColor textColor)
     {
-        node.setItem( COSName.C, textColor.toCOSArray() );
+    	getCOSDictionary().setItem(COSName.C, textColor.toCOSArray());
     }
 
     /**
@@ -359,13 +361,13 @@
      *
      * @param textColor The text color for this node.
      */
-    public void setTextColor( Color textColor )
+    public void setTextColor(Color textColor)
     {
         COSArray array = new COSArray();
-        array.add( new COSFloat( textColor.getRed()/255f));
-        array.add( new COSFloat( textColor.getGreen()/255f));
-        array.add( new COSFloat( textColor.getBlue()/255f));
-        node.setItem( COSName.C, array );
+        array.add(new COSFloat(textColor.getRed() / 255f));
+        array.add(new COSFloat(textColor.getGreen() / 255f));
+        array.add(new COSFloat(textColor.getBlue() / 255f));
+        getCOSDictionary().setItem(COSName.C, array);
     }
 
     /**
@@ -375,7 +377,7 @@
      */
     public boolean isItalic()
     {
-        return node.getFlag( COSName.F, ITALIC_FLAG );
+        return getCOSDictionary().getFlag(COSName.F, ITALIC_FLAG);
     }
 
     /**
@@ -383,9 +385,9 @@
      *
      * @param italic The new italic flag.
      */
-    public void setItalic( boolean italic )
+    public void setItalic(boolean italic)
     {
-        node.setFlag( COSName.F, ITALIC_FLAG, italic );
+    	getCOSDictionary().setFlag(COSName.F, ITALIC_FLAG, italic);
     }
 
     /**
@@ -395,7 +397,7 @@
      */
     public boolean isBold()
     {
-        return node.getFlag( COSName.F, BOLD_FLAG );
+        return getCOSDictionary().getFlag(COSName.F, BOLD_FLAG);
     }
 
     /**
@@ -403,9 +405,9 @@
      *
      * @param bold The new bold flag.
      */
-    public void setBold( boolean bold )
+    public void setBold(boolean bold)
     {
-        node.setFlag( COSName.F, BOLD_FLAG, bold );
+    	getCOSDictionary().setFlag(COSName.F, BOLD_FLAG, bold);
     }
 
 }
Index: pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java	(revisione 1659261)
+++ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNode.java	(copia locale)
@@ -16,182 +16,198 @@
  */
 package org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline;
 
-import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.common.PDDictionaryWrapper;
 
-import org.apache.pdfbox.pdmodel.common.COSObjectable;
-
 /**
- * This represents an node in an outline in a pdf document.
+ * Base class for a node in the outline of a PDF document.
  *
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
  * @version $Revision: 1.3 $
  */
-public class PDOutlineNode implements COSObjectable
+public abstract class PDOutlineNode extends PDDictionaryWrapper
 {
-    /**
-     * The dictionary for this node.
-     */
-    protected COSDictionary node;
 
-    /**
-     * Default Constructor.
-     */
     public PDOutlineNode()
     {
-        node = new COSDictionary();
+        super();
     }
 
     /**
-     * Default Constructor.
-     *
      * @param dict The dictionary storage.
      */
-    public PDOutlineNode( COSDictionary dict)
+    public PDOutlineNode(COSDictionary dict)
     {
-        node = dict;
+        super(dict);
     }
 
+    @Override
+    public COSDictionary getCOSDictionary()
+    {
+        return super.getCOSDictionary();
+    }
+    
     /**
-     * Convert this standard java object to a COS object.
-     *
-     * @return The cos object that matches this Java object.
+     * @return The parent of this node or null if there is no parent.
      */
-    @Override
-    public COSBase getCOSObject()
+    PDOutlineNode getParent()
     {
-        return node;
+        COSDictionary item = (COSDictionary) getCOSDictionary().getDictionaryObject(COSName.PARENT);
+        if (item != null)
+        {
+            if (COSName.OUTLINES.equals(item.getCOSName(COSName.TYPE)))
+            {
+                return new PDDocumentOutline(item);
+            }
+            return new PDOutlineItem(item);
+        }
+        return null;
     }
 
+    void setParent(PDOutlineNode parent)
+    {
+    	getCOSDictionary().setItem(COSName.PARENT, parent);
+    }
+
     /**
-     * Convert this standard java object to a COS object.
+     * Adds the given node to the bottom of the children list.
      *
-     * @return The cos object that matches this Java object.
+     * @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 COSDictionary getCOSDictionary()
+    public void addLast(PDOutlineItem newChild)
     {
-        return node;
+        requireSingleNode(newChild);
+        append(newChild);
+        updateParentOpenCountForAddedChild(newChild);
     }
 
     /**
-     * Get the parent of this object.  This will either be a DocumentOutline or an OutlineItem.
-     *
-     * @return The parent of this object, or null if this is the document outline and there
-     * is no parent.
+     * Adds the given node to the top of the children list.
+     * 
+     * @param newChild
+     * @return the newly added child
+     * @throws IllegalArgumentException if the given node is part of a list (i.e. if it has a previous or a next
+     * sibling)
      */
-    protected PDOutlineNode getParent()
+    public void addFirst(PDOutlineItem newChild)
     {
-        PDOutlineNode retval = null;
-        COSDictionary parent = (COSDictionary) node.getDictionaryObject(COSName.PARENT);
-        if (parent != null)
+        requireSingleNode(newChild);
+        prepend(newChild);
+        updateParentOpenCountForAddedChild(newChild);
+    }
+
+    /**
+     * @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)
+    {
+        if (node.getNextSibling() != null || node.getPreviousSibling() != null)
         {
-            if (parent.getDictionaryObject(COSName.PARENT) == null)
-            {
-                retval = new PDDocumentOutline(parent);
-            }
-            else
-            {
-                retval = new PDOutlineItem(parent);
-            }
+            throw new IllegalArgumentException("A single node with no siblings is required");
         }
-
-        return retval;
     }
 
     /**
-     * Set the parent of this object, this is maintained by these objects and should not
-     * be called by any clients of PDFBox code.
-     *
-     * @param parent The parent of this object.
+     * 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
      */
-    protected void setParent( PDOutlineNode parent )
+    private void append(PDOutlineItem newChild)
     {
-        node.setItem(COSName.PARENT, parent );
+        newChild.setParent(this);
+        if (!hasChildren())
+        {
+            setFirstChild(newChild);
+        }
+        else
+        {
+            PDOutlineItem previousLastChild = getLastChild();
+            previousLastChild.setNextSibling(newChild);
+            newChild.setPreviousSibling(previousLastChild);
+        }
+        setLastChild(newChild);
     }
 
     /**
-     * append a child node to this node.
-     *
-     * @param outlineNode The node to add.
+     * 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
      */
-    public void appendChild( PDOutlineItem outlineNode )
+    private void prepend(PDOutlineItem newChild)
     {
-        outlineNode.setParent( this );
-        if( getFirstChild() == null )
+        newChild.setParent(this);
+        if (!hasChildren())
         {
-            int currentOpenCount = getOpenCount();
-            setFirstChild( outlineNode );
-            //1 for the the item we are adding;
-            int numberOfOpenNodesWeAreAdding = 1;
-            if( outlineNode.isNodeOpen() )
-            {
-                numberOfOpenNodesWeAreAdding += outlineNode.getOpenCount();
-            }
-            if( isNodeOpen() )
-            {
-                setOpenCount( currentOpenCount + numberOfOpenNodesWeAreAdding );
-            }
-            else
-            {
-                setOpenCount( currentOpenCount - numberOfOpenNodesWeAreAdding );
-            }
-            updateParentOpenCount( numberOfOpenNodesWeAreAdding );
+            setLastChild(newChild);
         }
         else
         {
-            PDOutlineItem previousLastChild = getLastChild();
-            previousLastChild.insertSiblingAfter( outlineNode );
+            PDOutlineItem previousFirstChild = getFirstChild();
+            newChild.setNextSibling(previousFirstChild);
+            previousFirstChild.setPreviousSibling(newChild);
         }
-        
-        PDOutlineItem lastNode = outlineNode;
-        while(lastNode.getNextSibling() != null)
+        setFirstChild(newChild);
+    }
+
+    void updateParentOpenCountForAddedChild(PDOutlineItem newChild)
+    {
+        int delta = 1;
+        if (newChild.isNodeOpen())
         {
-            lastNode = lastNode.getNextSibling();
+            delta += newChild.getOpenCount();
         }
-        setLastChild( lastNode );
+        newChild.updateParentOpenCount(delta);
     }
 
     /**
-     * Return the first child or null if there is no child.
-     *
-     * @return The first child.
+     * @return true if the node has at least one child
      */
-    public PDOutlineItem getFirstChild()
+    public boolean hasChildren()
     {
-        PDOutlineItem first = null;
-        COSDictionary firstDic = (COSDictionary)node.getDictionaryObject( "First" );
-        if( firstDic != null )
+        return getFirstChild() != null;
+    }
+
+    PDOutlineItem getItem(COSName name)
+    {
+        COSDictionary item = (COSDictionary) getCOSDictionary().getDictionaryObject(name);
+        if (item != null)
         {
-            first = new PDOutlineItem( firstDic );
+            return new PDOutlineItem(item);
         }
-        return first;
+        return null;
     }
 
     /**
+     * @return The first child or null if there is no child.
+     */
+    public PDOutlineItem getFirstChild()
+    {
+        return getItem(COSName.FIRST);
+    }
+
+    /**
      * Set the first child, this will be maintained by this class.
      *
      * @param outlineNode The new first child.
      */
-    protected void setFirstChild( PDOutlineNode outlineNode )
+    void setFirstChild(PDOutlineNode outlineNode)
     {
-        node.setItem(COSName.FIRST, outlineNode);
+    	getCOSDictionary().setItem(COSName.FIRST, outlineNode);
     }
 
     /**
-     * Return the last child or null if there is no child.
-     *
-     * @return The last child.
+     * @return The last child or null if there is no child.
      */
     public PDOutlineItem getLastChild()
     {
-        PDOutlineItem last = null;
-        COSDictionary lastDic = (COSDictionary)node.getDictionaryObject( "Last" );
-        if( lastDic != null )
-        {
-            last = new PDOutlineItem( lastDic );
-        }
-        return last;
+        return getItem(COSName.LAST);
     }
 
     /**
@@ -199,80 +215,64 @@
      *
      * @param outlineNode The new last child.
      */
-    protected void setLastChild( PDOutlineNode outlineNode )
+    void setLastChild(PDOutlineNode outlineNode)
     {
-        node.setItem( "Last", outlineNode );
+    	getCOSDictionary().setItem(COSName.LAST, outlineNode);
     }
 
     /**
-     * Get the number of open nodes.  Or a negative number if this node
-     * is closed.  See PDF Reference for more details.  This value
-     * is updated as you append children and siblings.
+     * 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()
     {
-        return node.getInt( "Count", 0 );
+        return getCOSDictionary().getInt(COSName.COUNT, 0);
     }
 
     /**
-     * Set the open count.  This number is automatically managed for you
-     * when you add items to the outline.
+     * Set the open count. This number is automatically managed for you when you add items to the outline.
      *
-     * @param openCount The new open cound.
+     * @param openCount The new open count.
      */
-    protected void setOpenCount( int openCount )
+    void setOpenCount(int openCount)
     {
-        node.setInt( "Count", openCount );
+    	getCOSDictionary().setInt(COSName.COUNT, openCount);
     }
 
     /**
-     * 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.
+     * 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.
-        if( !isNodeOpen() )
+        if (!isNodeOpen())
         {
-            int openChildrenCount = 0;
-            PDOutlineItem currentChild = getFirstChild();
-            while( currentChild != null )
-            {
-                //first increase by one for the current child
-                openChildrenCount++;
-                //then increase by the number of open nodes the child has
-                if( currentChild.isNodeOpen() )
-                {
-                    openChildrenCount += currentChild.getOpenCount();
-                }
-                currentChild = currentChild.getNextSibling();
-            }
-            setOpenCount( openChildrenCount );
-            updateParentOpenCount( openChildrenCount );
+            switchNodeCount();
         }
     }
 
     /**
      * Close this node.
-     *
      */
     public void closeNode()
     {
-        //if the node is already closed then do nothing.
-        if( isNodeOpen() )
+        if (isNodeOpen())
         {
-            int openCount = getOpenCount();
-            updateParentOpenCount( -openCount );
-            setOpenCount( -openCount );
+            switchNodeCount();
         }
     }
 
+    private void switchNodeCount()
+    {
+        int openCount = getOpenCount();
+        setOpenCount(-openCount);
+        updateParentOpenCount(-openCount);
+    }
+
     /**
-     * Node is open if the open count is greater than zero.
-     * @return true if this node is open.
+     * @return true if this node count is greater than zero, false otherwise.
      */
     public boolean isNodeOpen()
     {
@@ -280,34 +280,23 @@
     }
 
     /**
-     * The count parameter needs to be updated when you add or remove elements to
-     * the outline.  When you add an element at a lower level then you need to
-     * increase all of the parents.
+     * The count parameter needs to be updated when you add, remove, open or close outline items.
      *
-     * @param amount The amount to update by.
+     * @param delta The amount to update by.
      */
-    protected void updateParentOpenCount( int amount )
+    void updateParentOpenCount(int delta)
     {
         PDOutlineNode parent = getParent();
-        if( parent != null )
+        if (parent != null)
         {
-            int currentCount = parent.getOpenCount();
-            //if the currentCount is negative or it is absent then
-            //we will treat it as negative.  The default is to be negative.
-            boolean negative = currentCount < 0 ||
-                parent.getCOSDictionary().getDictionaryObject( "Count" ) == null;
-            currentCount = Math.abs( currentCount );
-            currentCount += amount;
-            if( negative )
+            if (parent.isNodeOpen())
             {
-                currentCount = -currentCount;
+                parent.setOpenCount(parent.getOpenCount() + delta);
+                parent.updateParentOpenCount(delta);
             }
-            parent.setOpenCount( currentCount );
-            //recursively call parent to update count, but the parents count is only
-            //updated if this is an open node
-            if( !negative )
+            else
             {
-                parent.updateParentOpenCount( amount );
+                parent.setOpenCount(parent.getOpenCount() - delta);
             }
         }
     }
Index: pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java	(revisione 1659261)
+++ pdfbox/src/main/java/org/apache/pdfbox/util/PDFMergerUtility.java	(copia locale)
@@ -328,7 +328,7 @@
                 if (first != null)
                 {
                     PDOutlineItem clonedFirst = new PDOutlineItem((COSDictionary) cloner.cloneForNewDocument(first));
-                    destOutline.appendChild(clonedFirst);
+                    destOutline.addLast(clonedFirst);
                 }
             }
         }
Index: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutlineTest.java
===================================================================
--- pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutlineTest.java	(revisione 0)
+++ pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutlineTest.java	(copia locale)
@@ -0,0 +1,64 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+/**
+ * @author Andrea Vacondio
+ *
+ */
+public class PDDocumentOutlineTest
+{
+    /**
+     * see PDF 32000-1:2008 table 152
+     */
+    @Test
+    public void outlinesCountShouldNotBeNegative()
+    {
+        PDDocumentOutline outline = new PDDocumentOutline();
+        PDOutlineItem firstLevelChild = new PDOutlineItem();
+        outline.addLast(firstLevelChild);
+        PDOutlineItem secondLevelChild = new PDOutlineItem();
+        firstLevelChild.addLast(secondLevelChild);
+        assertEquals(0, secondLevelChild.getOpenCount());
+        assertEquals(-1, firstLevelChild.getOpenCount());
+        assertFalse("Outlines count cannot be " + outline.getOpenCount(),
+                outline.getOpenCount() < 0);
+    }
+
+    @Test
+    public void outlinesCount()
+    {
+        PDDocumentOutline outline = new PDDocumentOutline();
+        PDOutlineItem root = new PDOutlineItem();
+        outline.addLast(root);
+        assertEquals(1, outline.getOpenCount());
+        root.addLast(new PDOutlineItem());
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(1, outline.getOpenCount());
+        root.addLast(new PDOutlineItem());
+        assertEquals(-2, root.getOpenCount());
+        assertEquals(1, outline.getOpenCount());
+        root.openNode();
+        assertEquals(2, root.getOpenCount());
+        assertEquals(3, outline.getOpenCount());
+    }
+}

Proprietà modificate su: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDDocumentOutlineTest.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItemTest.java
===================================================================
--- pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItemTest.java	(revisione 0)
+++ pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItemTest.java	(copia locale)
@@ -0,0 +1,191 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Andrea Vacondio
+ *
+ */
+public class PDOutlineItemTest
+{
+    private PDOutlineItem root;
+    private PDOutlineItem first;
+    private PDOutlineItem second;
+    private PDOutlineItem newSibling;
+
+    @Before
+    public void setUp()
+    {
+        root = new PDOutlineItem();
+        first = new PDOutlineItem();
+        second = new PDOutlineItem();
+        root.addLast(first);
+        root.addLast(second);
+        newSibling = new PDOutlineItem();
+        newSibling.addLast(new PDOutlineItem());
+        newSibling.addLast(new PDOutlineItem());
+    }
+
+    @Test
+    public void insertSiblingAfter_OpenChildToOpenParent()
+    {
+        newSibling.openNode();
+        root.openNode();
+        assertEquals(2, root.getOpenCount());
+        first.insertSiblingAfter(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(5, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingBefore_OpenChildToOpenParent()
+    {
+        newSibling.openNode();
+        root.openNode();
+        assertEquals(2, root.getOpenCount());
+        second.insertSiblingBefore(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(5, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingAfter_OpenChildToClosedParent()
+    {
+        newSibling.openNode();
+        assertEquals(-2, root.getOpenCount());
+        first.insertSiblingAfter(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(-5, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingBefore_OpenChildToClosedParent()
+    {
+        newSibling.openNode();
+        assertEquals(-2, root.getOpenCount());
+        second.insertSiblingBefore(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(-5, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingAfter_ClosedChildToOpenParent()
+    {
+        root.openNode();
+        assertEquals(2, root.getOpenCount());
+        first.insertSiblingAfter(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(3, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingBefore_ClosedChildToOpenParent()
+    {
+        root.openNode();
+        assertEquals(2, root.getOpenCount());
+        second.insertSiblingBefore(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(3, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingAfter_ClosedChildToClosedParent()
+    {
+        assertEquals(-2, root.getOpenCount());
+        first.insertSiblingAfter(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(-3, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingBefore_ClosedChildToClosedParent()
+    {
+        assertEquals(-2, root.getOpenCount());
+        second.insertSiblingBefore(newSibling);
+        assertEquals(first.getNextSibling(), newSibling);
+        assertEquals(second.getPreviousSibling(), newSibling);
+        assertEquals(-3, root.getOpenCount());
+    }
+
+    @Test
+    public void insertSiblingTop()
+    {
+        assertEquals(root.getFirstChild(), first);
+        PDOutlineItem newSibling = new PDOutlineItem();
+        first.insertSiblingBefore(newSibling);
+        assertEquals(first.getPreviousSibling(), newSibling);
+        assertEquals(root.getFirstChild(), newSibling);
+    }
+
+    @Test
+    public void insertSiblingTopNoParent()
+    {
+        assertEquals(root.getFirstChild(), first);
+        PDOutlineItem newSibling = new PDOutlineItem();
+        root.insertSiblingBefore(newSibling);
+        assertEquals(root.getPreviousSibling(), newSibling);
+    }
+
+    @Test
+    public void insertSiblingBottom()
+    {
+        assertEquals(root.getLastChild(), second);
+        PDOutlineItem newSibling = new PDOutlineItem();
+        second.insertSiblingAfter(newSibling);
+        assertEquals(second.getNextSibling(), newSibling);
+        assertEquals(root.getLastChild(), newSibling);
+    }
+
+    @Test
+    public void insertSiblingBottomNoParent()
+    {
+        assertEquals(root.getLastChild(), second);
+        PDOutlineItem newSibling = new PDOutlineItem();
+        root.insertSiblingAfter(newSibling);
+        assertEquals(root.getNextSibling(), newSibling);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void cannotInsertSiblingBeforeAList()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.insertSiblingAfter(new PDOutlineItem());
+        child.insertSiblingAfter(new PDOutlineItem());
+        root.insertSiblingBefore(child);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void cannotInsertSiblingAfterAList()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.insertSiblingAfter(new PDOutlineItem());
+        child.insertSiblingAfter(new PDOutlineItem());
+        root.insertSiblingAfter(child);
+    }
+}

Proprietà modificate su: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineItemTest.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNodeTest.java
===================================================================
--- pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNodeTest.java	(revisione 0)
+++ pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNodeTest.java	(copia locale)
@@ -0,0 +1,350 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Andrea Vacondio
+ *
+ */
+public class PDOutlineNodeTest
+{
+    private PDOutlineItem root;
+
+    @Before
+    public void setUp()
+    {
+        root = new PDOutlineItem();
+    }
+
+    @Test
+    public void getParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        root.addLast(child);
+        PDDocumentOutline outline = new PDDocumentOutline();
+        outline.addLast(root);
+        assertNull(outline.getParent());
+        assertEquals(outline, root.getParent());
+        assertEquals(root, child.getParent());
+    }
+
+    @Test
+    public void nullLastChild()
+    {
+        assertNull(root.getLastChild());
+    }
+
+    @Test
+    public void nullFirstChild()
+    {
+        assertNull(root.getFirstChild());
+    }
+
+    @Test
+    public void openAlreadyOpenedRootNode()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        assertEquals(0, root.getOpenCount());
+        root.addLast(child);
+        root.openNode();
+        assertTrue(root.isNodeOpen());
+        assertEquals(1, root.getOpenCount());
+        root.openNode();
+        assertTrue(root.isNodeOpen());
+        assertEquals(1, root.getOpenCount());
+    }
+
+    @Test
+    public void closeAlreadyClosedRootNode()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        assertEquals(0, root.getOpenCount());
+        root.addLast(child);
+        root.openNode();
+        root.closeNode();
+        assertFalse(root.isNodeOpen());
+        assertEquals(-1, root.getOpenCount());
+        root.closeNode();
+        assertFalse(root.isNodeOpen());
+        assertEquals(-1, root.getOpenCount());
+    }
+
+    @Test
+    public void openLeaf()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        root.addLast(child);
+        child.openNode();
+        assertFalse(child.isNodeOpen());
+    }
+
+    @Test
+    public void nodeClosedByDefault()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        root.addLast(child);
+        assertFalse(root.isNodeOpen());
+        assertEquals(-1, root.getOpenCount());
+    }
+
+    @Test
+    public void closeNodeWithOpendParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        child.openNode();
+        root.addLast(child);
+        root.openNode();
+        assertEquals(3, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        child.closeNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+    }
+
+    @Test
+    public void closeNodeWithClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        child.openNode();
+        root.addLast(child);
+        assertEquals(-3, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        child.closeNode();
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+    }
+
+    @Test
+    public void openNodeWithOpendParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        root.addLast(child);
+        root.openNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        child.openNode();
+        assertEquals(3, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+    }
+
+    @Test
+    public void openNodeWithClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        root.addLast(child);
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        child.openNode();
+        assertEquals(-3, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+    }
+
+    @Test
+    public void addLastSingleChild()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        root.addLast(child);
+        assertEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+    }
+
+    @Test
+    public void addFirstSingleChild()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        root.addFirst(child);
+        assertEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+    }
+
+    @Test
+    public void addLastOpenChildToOpenParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        child.openNode();
+        root.addLast(new PDOutlineItem());
+        root.openNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        root.addLast(child);
+        assertNotEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+        assertEquals(4, root.getOpenCount());
+    }
+
+    @Test
+    public void addFirstOpenChildToOpenParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addFirst(new PDOutlineItem());
+        child.addFirst(new PDOutlineItem());
+        child.openNode();
+        root.addFirst(new PDOutlineItem());
+        root.openNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        root.addFirst(child);
+        assertNotEquals(child, root.getLastChild());
+        assertEquals(child, root.getFirstChild());
+        assertEquals(4, root.getOpenCount());
+    }
+
+    @Test
+    public void addLastOpenChildToClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        child.openNode();
+        root.addLast(new PDOutlineItem());
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        root.addLast(child);
+        assertNotEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+        assertEquals(-4, root.getOpenCount());
+    }
+
+    @Test
+    public void addFirstOpenChildToClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addFirst(new PDOutlineItem());
+        child.addFirst(new PDOutlineItem());
+        child.openNode();
+        root.addFirst(new PDOutlineItem());
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(2, child.getOpenCount());
+        root.addFirst(child);
+        assertNotEquals(child, root.getLastChild());
+        assertEquals(child, root.getFirstChild());
+        assertEquals(-4, root.getOpenCount());
+    }
+
+    @Test
+    public void addLastClosedChildToOpenParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        root.addLast(new PDOutlineItem());
+        root.openNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        root.addLast(child);
+        assertNotEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+        assertEquals(2, root.getOpenCount());
+    }
+
+    @Test
+    public void addFirstClosedChildToOpenParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addFirst(new PDOutlineItem());
+        child.addFirst(new PDOutlineItem());
+        root.addFirst(new PDOutlineItem());
+        root.openNode();
+        assertEquals(1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        root.addFirst(child);
+        assertNotEquals(child, root.getLastChild());
+        assertEquals(child, root.getFirstChild());
+        assertEquals(2, root.getOpenCount());
+    }
+
+    @Test
+    public void addLastClosedChildToClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addLast(new PDOutlineItem());
+        child.addLast(new PDOutlineItem());
+        root.addLast(new PDOutlineItem());
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        root.addLast(child);
+        assertNotEquals(child, root.getFirstChild());
+        assertEquals(child, root.getLastChild());
+        assertEquals(-2, root.getOpenCount());
+    }
+
+    @Test
+    public void addFirstClosedChildToClosedParent()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.addFirst(new PDOutlineItem());
+        child.addFirst(new PDOutlineItem());
+        root.addFirst(new PDOutlineItem());
+        assertEquals(-1, root.getOpenCount());
+        assertEquals(-2, child.getOpenCount());
+        root.addFirst(child);
+        assertNotEquals(child, root.getLastChild());
+        assertEquals(child, root.getFirstChild());
+        assertEquals(-2, root.getOpenCount());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void cannotAddLastAList()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.insertSiblingAfter(new PDOutlineItem());
+        child.insertSiblingAfter(new PDOutlineItem());
+        root.addLast(child);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void cannotAddFirstAList()
+    {
+        PDOutlineItem child = new PDOutlineItem();
+        child.insertSiblingAfter(new PDOutlineItem());
+        child.insertSiblingAfter(new PDOutlineItem());
+        root.addFirst(child);
+    }
+
+    @Test
+    public void equalsNode()
+    {
+        root.addFirst(new PDOutlineItem());
+        assertEquals(root.getFirstChild(), root.getLastChild());
+    }
+
+    @Test
+    public void openNodeAndAppend()
+    {
+        // TODO
+    }
+
+}

Proprietà modificate su: pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/documentnavigation/outline/PDOutlineNodeTest.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
