You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2013/03/10 14:00:46 UTC

svn commit: r1454845 - in /pdfbox/trunk/pdfbox/src: main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java

Author: lehmi
Date: Sun Mar 10 13:00:46 2013
New Revision: 1454845

URL: http://svn.apache.org/r1454845
Log:
PDFBOX-1381: update limits in setKids and avoid NPEs as proposed by Dominic Tubach

Added:
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java   (with props)
Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java?rev=1454845&r1=1454844&r2=1454845&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDNumberTreeNode.java Sun Mar 10 13:00:46 2013
@@ -24,6 +24,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
@@ -33,22 +35,24 @@ import org.apache.pdfbox.cos.COSName;
 /**
  * This class represents a PDF Number tree. See the PDF Reference 1.7 section
  * 7.9.7 for more details.
- * 
+ *
  * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>,
  *         <a href="igor.podolskiy@ievvwi.uni-stuttgart.de">Igor Podolskiy</a>
  * @version $Revision: 1.4 $
  */
 public class PDNumberTreeNode implements COSObjectable
 {
+    private static final Log LOG = LogFactory.getLog( PDNumberTreeNode.class );
+
     private COSDictionary node;
-    private Class<?> valueType = null;
+    private Class<? extends COSObjectable> valueType = null;
 
     /**
      * Constructor.
      *
      * @param valueClass The PD Model type of object that is the value.
      */
-    public PDNumberTreeNode( Class<?> valueClass )
+    public PDNumberTreeNode( Class<? extends COSObjectable> valueClass )
     {
         node = new COSDictionary();
         valueType = valueClass;
@@ -60,7 +64,7 @@ public class PDNumberTreeNode implements
      * @param dict The dictionary that holds the name information.
      * @param valueClass The PD Model type of object that is the value.
      */
-    public PDNumberTreeNode( COSDictionary dict, Class<?> valueClass )
+    public PDNumberTreeNode( COSDictionary dict, Class<? extends COSObjectable> valueClass )
     {
         node = dict;
         valueType = valueClass;
@@ -91,19 +95,18 @@ public class PDNumberTreeNode implements
      *
      * @return The list of children or null if there are no children.
      */
-    public List getKids()
+    public List<PDNumberTreeNode> getKids()
     {
-
-        List retval = null;
+        List<PDNumberTreeNode> retval = null;
         COSArray kids = (COSArray)node.getDictionaryObject( COSName.KIDS );
         if( kids != null )
         {
-            List pdObjects = new ArrayList();
+            List<PDNumberTreeNode> pdObjects = new ArrayList<PDNumberTreeNode>();
             for( int i=0; i<kids.size(); i++ )
             {
                 pdObjects.add( createChildNode( (COSDictionary)kids.getObject(i) ) );
             }
-            retval = new COSArrayList(pdObjects,kids);
+            retval = new COSArrayList<PDNumberTreeNode>(pdObjects,kids);
         }
 
         return retval;
@@ -114,8 +117,22 @@ public class PDNumberTreeNode implements
      *
      * @param kids The children of this number tree.
      */
-    public void setKids( List kids )
+    public void setKids( List<? extends PDNumberTreeNode> kids )
     {
+        if (kids != null && kids.size() > 0)
+        {
+            PDNumberTreeNode firstKid = kids.get(0);
+            PDNumberTreeNode lastKid = kids.get(kids.size() - 1);
+            Integer lowerLimit = firstKid.getLowerLimit();
+            this.setLowerLimit(lowerLimit);
+            Integer upperLimit = lastKid.getUpperLimit();
+            this.setUpperLimit(upperLimit);
+        }
+        else if ( node.getDictionaryObject( COSName.NUMS ) == null )
+        {
+            // Remove limits if there are no kids and no numbers set.
+            node.setItem( COSName.LIMITS, null);
+        }
         node.setItem( COSName.KIDS, COSArrayList.converterToCOSArray( kids ) );
     }
 
@@ -125,29 +142,36 @@ public class PDNumberTreeNode implements
      * @param index The index in the number tree.
      *
      * @return The value corresponding to the index.
-     * 
+     *
      * @throws IOException If there is a problem creating the values.
      */
     public Object getValue( Integer index ) throws IOException
     {
         Object retval = null;
-        Map<Integer,Object> names = getNumbers();
+        Map<Integer,COSObjectable> names = getNumbers();
         if( names != null )
         {
             retval = names.get( index );
         }
         else
         {
-            List kids = getKids();
-            for( int i=0; i<kids.size() && retval == null; i++ )
+            List<PDNumberTreeNode> kids = getKids();
+            if ( kids != null )
             {
-                PDNumberTreeNode childNode = (PDNumberTreeNode)kids.get( i );
-                if( childNode.getLowerLimit().compareTo( index ) <= 0 &&
-                        childNode.getUpperLimit().compareTo( index ) >= 0 )
+                for( int i=0; i<kids.size() && retval == null; i++ )
                 {
-                    retval = childNode.getValue( index );
+                    PDNumberTreeNode childNode = kids.get( i );
+                    if( childNode.getLowerLimit().compareTo( index ) <= 0 &&
+                        childNode.getUpperLimit().compareTo( index ) >= 0 )
+                    {
+                        retval = childNode.getValue( index );
+                    }
                 }
             }
+            else
+            {
+                LOG.warn("NumberTreeNode does not have \"nums\" nor \"kids\" objects.");
+            }
         }
         return retval;
     }
@@ -158,27 +182,25 @@ public class PDNumberTreeNode implements
      * depend on where this class is being used.
      *
      * @return A map of COS objects.
-     * 
+     *
      * @throws IOException If there is a problem creating the values.
      */
-    public Map getNumbers()  throws IOException
+    public Map<Integer,COSObjectable> getNumbers()  throws IOException
     {
-        Map<Integer,Object> indices = null;
+        Map<Integer, COSObjectable> indices = null;
         COSArray namesArray = (COSArray)node.getDictionaryObject( COSName.NUMS );
         if( namesArray != null )
         {
-            indices = new HashMap<Integer,Object>();
+            indices = new HashMap<Integer,COSObjectable>();
             for( int i=0; i<namesArray.size(); i+=2 )
             {
                 COSInteger key = (COSInteger)namesArray.getObject(i);
                 COSBase cosValue = namesArray.getObject( i+1 );
-                Object pdValue = convertCOSToPD( cosValue );
-
+                COSObjectable pdValue = convertCOSToPD( cosValue );
                 indices.put( Integer.valueOf(key.intValue()), pdValue );
             }
             indices = Collections.unmodifiableMap(indices);
         }
-
         return indices;
     }
 
@@ -191,12 +213,12 @@ public class PDNumberTreeNode implements
      * @return The converted PD Model object.
      * @throws IOException If there is an error during creation.
      */
-    protected Object convertCOSToPD( COSBase base ) throws IOException
+    protected COSObjectable convertCOSToPD( COSBase base ) throws IOException
     {
-        Object retval = null;
+        COSObjectable retval = null;
         try
         {
-            Constructor<?> ctor = valueType.getConstructor( new Class[] { base.getClass() } );
+            Constructor<? extends COSObjectable> ctor = valueType.getConstructor( new Class[] { base.getClass() } );
             retval = ctor.newInstance( new Object[] { base } );
         }
         catch( Throwable t )
@@ -225,7 +247,7 @@ public class PDNumberTreeNode implements
      *
      * @param numbers The map of names to objects.
      */
-    public void setNumbers( Map<Integer,Object> numbers )
+    public void setNumbers( Map<Integer, ? extends COSObjectable> numbers )
     {
         if( numbers == null )
         {
@@ -234,7 +256,7 @@ public class PDNumberTreeNode implements
         }
         else
         {
-            List<Integer> keys = new ArrayList( numbers.keySet() );
+            List<Integer> keys = new ArrayList<Integer>( numbers.keySet() );
             Collections.sort( keys );
             COSArray array = new COSArray();
             for( int i=0; i<keys.size(); i++ )
@@ -266,7 +288,7 @@ public class PDNumberTreeNode implements
     {
         Integer retval = null;
         COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS );
-        if( arr != null )
+        if( arr != null && arr.get(0) != null )
         {
             retval = Integer.valueOf(arr.getInt( 1 ));
         }
@@ -286,8 +308,16 @@ public class PDNumberTreeNode implements
             arr = new COSArray();
             arr.add( null );
             arr.add( null );
+            node.setItem( COSName.LIMITS, arr );
+        }
+        if ( upper != null)
+        {
+            arr.setInt( 1, upper.intValue() );
+        }
+        else
+        {
+            arr.set( 1, null );
         }
-        arr.setInt( 1, upper.intValue() );
     }
 
     /**
@@ -299,7 +329,7 @@ public class PDNumberTreeNode implements
     {
         Integer retval = null;
         COSArray arr = (COSArray)node.getDictionaryObject( COSName.LIMITS );
-        if( arr != null )
+        if( arr != null && arr.get(0) != null )
         {
             retval = Integer.valueOf(arr.getInt( 0 ));
         }
@@ -319,7 +349,15 @@ public class PDNumberTreeNode implements
             arr = new COSArray();
             arr.add( null );
             arr.add( null );
+            node.setItem( COSName.LIMITS, arr );
+        }
+        if ( lower != null )
+        {
+            arr.setInt( 0, lower.intValue() );
+        }
+        else
+        {
+            arr.set( 0, null );
         }
-        arr.setInt( 0, lower.intValue() );
     }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java?rev=1454845&r1=1454844&r2=1454845&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDPageLabels.java Sun Mar 10 13:00:46 2013
@@ -98,19 +98,25 @@ public class PDPageLabels implements COS
         findLabels(root);
     }
     
-    private void findLabels(PDNumberTreeNode node) throws IOException {
-        if (node.getKids() != null) {
+    private void findLabels(PDNumberTreeNode node) throws IOException 
+    {
+        if (node.getKids() != null) 
+        {
             List<PDNumberTreeNode> kids = node.getKids();
-            for (PDNumberTreeNode kid : kids) {
+            for (PDNumberTreeNode kid : kids) 
+            {
                 findLabels(kid);
             }
         }
-        else if (node.getNumbers() != null) {
-            Map<Integer, COSDictionary> numbers = node.getNumbers();
-            for (Entry<Integer, COSDictionary> i : numbers.entrySet())
+        else if (node.getNumbers() != null) 
+        {
+            Map<Integer, COSObjectable> numbers = node.getNumbers();
+            for (Entry<Integer, COSObjectable> i : numbers.entrySet())
             {
                 if(i.getKey() >= 0)
-                    labels.put(i.getKey(), new PDPageLabelRange(i.getValue()));
+                {
+                    labels.put(i.getKey(), new PDPageLabelRange((COSDictionary)i.getValue()));
+                }
             }
         }
     }
@@ -160,7 +166,10 @@ public class PDPageLabels implements COS
     {
         labels.put(startPage, item);
     }
-
+    
+    /**
+     * {@inheritDoc} 
+     */
     public COSBase getCOSObject()
     {
         COSDictionary dict = new COSDictionary();

Added: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java?rev=1454845&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java (added)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java Sun Mar 10 13:00:46 2013
@@ -0,0 +1,188 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSInteger;
+
+/**
+ * A test case for PDNumberTreeNode.
+ * Based on TestPDNameTreeNode.
+ * 
+ * @author Dominic Tubach
+ */
+public class TestPDNumberTreeNode extends TestCase
+{
+
+    private PDNumberTreeNode node1;
+    private PDNumberTreeNode node2;
+    private PDNumberTreeNode node4;
+    private PDNumberTreeNode node5;
+    private PDNumberTreeNode node24;
+    
+    public static class PDTest implements COSObjectable {
+        private int value;
+
+        public PDTest(int value) {
+            this.value = value;
+        }
+        
+        public PDTest(COSInteger cosInt) {
+            this.value = cosInt.intValue();
+        }
+
+        public COSBase getCOSObject()
+        {
+            return COSInteger.get( value );
+        }
+
+        @Override
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + value;
+            return result;
+        }
+
+        @Override
+        public boolean equals( Object obj )
+        {
+            if ( this == obj)
+                return true;
+            if ( obj == null)
+                return false;
+            if ( getClass() != obj.getClass())
+                return false;
+            PDTest other = (PDTest) obj;
+            if ( value != other.value)
+                return false;
+            return true;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception
+    {
+        this.node5 = new PDNumberTreeNode(PDTest.class);
+        Map<Integer,PDTest> Numbers = new TreeMap<Integer, PDTest>();
+        Numbers.put( 1, new PDTest( 89 ) );
+        Numbers.put( 2, new PDTest( 13 ) );
+        Numbers.put( 3, new PDTest( 95 ) );
+        Numbers.put( 4, new PDTest( 51 ) );
+        Numbers.put( 5, new PDTest( 18 ) );
+        Numbers.put( 6, new PDTest( 33 ) );
+        Numbers.put( 7, new PDTest( 85 ) );
+        this.node5.setNumbers( Numbers );
+
+        this.node24 = new PDNumberTreeNode( PDTest.class );
+        Numbers = new TreeMap<Integer, PDTest>();
+        Numbers.put( 8, new PDTest( 54 ) );
+        Numbers.put( 9, new PDTest( 70 ) );
+        Numbers.put( 10, new PDTest( 39 ) );
+        Numbers.put( 11, new PDTest( 30 ) );
+        Numbers.put( 12, new PDTest( 40 ) );
+        this.node24.setNumbers( Numbers );
+
+        this.node2 = new PDNumberTreeNode( PDTest.class );
+        List<PDNumberTreeNode> kids = this.node2.getKids();
+        if ( kids == null)
+        {
+            kids = new COSArrayList<PDNumberTreeNode>();
+        }
+        kids.add( this.node5 );
+        this.node2.setKids( kids );
+
+        this.node4 = new PDNumberTreeNode( PDTest.class );
+        kids = this.node4.getKids();
+        if ( kids == null)
+        {
+            kids = new COSArrayList<PDNumberTreeNode>();
+        }
+        kids.add( this.node24 );
+        this.node4.setKids( kids );
+
+        this.node1 = new PDNumberTreeNode( PDTest.class );
+        kids = this.node1.getKids();
+        if ( kids == null)
+        {
+            kids = new COSArrayList<PDNumberTreeNode>();
+        }
+        kids.add( this.node2 );
+        kids.add( this.node4 );
+        this.node1.setKids( kids );
+    }
+    
+    public void testGetValue() throws IOException {
+        Assert.assertEquals(new PDTest( 51 ), this.node5.getValue( 4 ));
+        Assert.assertEquals(new PDTest(70), this.node1.getValue( 9 ));
+        
+        this.node1.setKids( null );
+        this.node1.setNumbers( null );
+        Assert.assertNull( this.node1.getValue( 0 ) );
+    }
+
+    public void testUpperLimit() throws IOException
+    {
+        Assert.assertEquals(Integer.valueOf( 7 ), this.node5.getUpperLimit());
+        Assert.assertEquals(Integer.valueOf( 7 ), this.node2.getUpperLimit());
+
+        Assert.assertEquals(Integer.valueOf( 12 ), this.node24.getUpperLimit());
+        Assert.assertEquals(Integer.valueOf( 12 ), this.node4.getUpperLimit());
+
+        Assert.assertEquals(Integer.valueOf( 12 ), this.node1.getUpperLimit());
+
+        this.node24.setNumbers( new HashMap<Integer, COSObjectable>() );
+        Assert.assertNull( this.node24.getUpperLimit() );
+        
+        this.node5.setNumbers( null );
+        Assert.assertNull( this.node5.getUpperLimit() );
+        
+        this.node1.setKids( null );
+        Assert.assertNull( this.node1.getUpperLimit() );
+    }
+
+    public void testLowerLimit() throws IOException
+    {
+        Assert.assertEquals(Integer.valueOf( 1 ), this.node5.getLowerLimit());
+        Assert.assertEquals(Integer.valueOf( 1 ), this.node2.getLowerLimit());
+
+        Assert.assertEquals(Integer.valueOf( 8 ), this.node24.getLowerLimit());
+        Assert.assertEquals(Integer.valueOf( 8 ), this.node4.getLowerLimit());
+
+        Assert.assertEquals(Integer.valueOf( 1 ), this.node1.getLowerLimit());
+        
+        this.node24.setNumbers( new HashMap<Integer, COSObjectable>() );
+        Assert.assertNull( this.node24.getLowerLimit() );
+        
+        this.node5.setNumbers( null );
+        Assert.assertNull( this.node5.getLowerLimit() );
+        
+        this.node1.setKids( null );
+        Assert.assertNull( this.node1.getLowerLimit() );
+    }
+
+}

Propchange: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/TestPDNumberTreeNode.java
------------------------------------------------------------------------------
    svn:eol-style = native