Author: msahyoun
Date: Fri Jan  3 10:57:03 2020
New Revision: 1872281

URL: http://svn.apache.org/viewvc?rev=1872281&view=rev
Log:
PDFBOX-4723: port equals and hashCode from trunk to 2.0; add SmallMap for 
COSDictionary; fix PDPageTree

Added:
    
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/util/SmallMap.java
Modified:
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
    
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
    
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
    
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java?rev=1872281&r1=1872280&r2=1872281&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java 
(original)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java 
Fri Jan  3 10:57:03 2020
@@ -18,6 +18,7 @@ package org.apache.pdfbox.cos;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -408,6 +409,49 @@ public class COSArray extends COSBase im
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof COSArray))
+        {
+            return false;
+        }
+
+        COSArray toBeCompared = (COSArray) o;
+
+        if (toBeCompared.size() != size())
+        {
+            return false;
+        }
+
+        for (int i=0; i<size(); i++)
+        {
+            if (!(get(i).equals(toBeCompared.get(i))))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Object[] members = {objects, needToBeUpdated};
+        return Arrays.hashCode(members);
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java?rev=1872281&r1=1872280&r2=1872281&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
 (original)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDictionary.java
 Fri Jan  3 10:57:03 2020
@@ -22,14 +22,16 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
-import java.util.LinkedHashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;
 
 import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.util.DateConverter;
+import org.apache.pdfbox.util.SmallMap;
 
 /**
  * This class represents a dictionary where name/value pairs reside.
@@ -45,7 +47,7 @@ public class COSDictionary extends COSBa
     /**
      * The name-value pairs of this dictionary. The pairs are kept in the 
order they were added to the dictionary.
      */
-    protected Map<COSName, COSBase> items = new LinkedHashMap<COSName, 
COSBase>();
+    protected Map<COSName, COSBase> items = new SmallMap<COSName, COSBase>();
 
     /**
      * Constructor.
@@ -1538,6 +1540,64 @@ public class COSDictionary extends COSBa
 
     /**
      * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o == null || !(o.getClass() == COSDictionary.class))
+        {
+            return false;
+        }
+
+        COSDictionary toBeCompared = (COSDictionary) o;
+
+        if (toBeCompared.size() != size())
+        {
+            return false;
+        }
+
+        Iterator<Entry<COSName, COSBase>> iter = entrySet().iterator();
+        while (iter.hasNext())
+        {
+            Entry<COSName, COSBase> entry = iter.next();
+            COSName key = entry.getKey();
+            COSBase value = entry.getValue();
+
+            if (!toBeCompared.containsKey(key)) 
+            {
+                return false;
+            }
+            else if (value == null)
+            {
+                if (toBeCompared.getItem(key) != null)
+                {
+                    return false;
+                }
+            }
+            else if (!value.equals(toBeCompared.getItem(key)))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Object[] members = {items, needToBeUpdated};
+        return Arrays.hashCode(members);
+    }    
+
+    /**
+     * {@inheritDoc}
      */
     @Override
     public String toString()

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java?rev=1872281&r1=1872280&r2=1872281&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java 
(original)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java 
Fri Jan  3 10:57:03 2020
@@ -23,7 +23,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map.Entry;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.filter.DecodeOptions;
@@ -71,6 +75,73 @@ public class COSStream extends COSDictio
         setInt(COSName.LENGTH, 0);
         this.scratchFile = scratchFile != null ? scratchFile : 
ScratchFile.getMainMemoryOnlyInstance();
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (!(o instanceof COSStream))
+        {
+            return false;
+        }
+
+        COSStream toBeCompared = (COSStream) o;
+
+        if (toBeCompared.size() != size())
+        {
+            return false;
+        }
+
+        // compare dictionary content
+        Iterator<Entry<COSName, COSBase>> iter = entrySet().iterator();
+        while (iter.hasNext())
+        {
+            Entry<COSName, COSBase> entry = iter.next();
+            COSName key = entry.getKey();
+            COSBase value = entry.getValue();
+
+            if (!toBeCompared.containsKey(key)) 
+            {
+                return false;
+            }
+            else if (value == null)
+            {
+                if (toBeCompared.getItem(key) != null)
+                {
+                    return false;
+                }
+            }
+            else if (!value.equals(toBeCompared.getItem(key)))
+            {
+                return false;
+            }
+        }
+
+        // compare stream content
+        if (!toBeCompared.toTextString().equals(toTextString()))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Object[] members = {items, randomAccess, scratchFile, isWriting};
+        return Arrays.hashCode(members);
+    }
+
+
     
     /**
      * Throws if the random access backing store has been closed. Helpful for 
catching cases where

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java?rev=1872281&r1=1872280&r2=1872281&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java
 (original)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDPageTree.java
 Fri Jan  3 10:57:03 2020
@@ -381,7 +381,7 @@ public class PDPageTree implements COSOb
         private void visitPage(COSDictionary current)
         {
             index++;
-            found = searched.equals(current);
+            found = searched == current;
         }
     }
 

Added: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/util/SmallMap.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/util/SmallMap.java?rev=1872281&view=auto
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/util/SmallMap.java 
(added)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/util/SmallMap.java 
Fri Jan  3 10:57:03 2020
@@ -0,0 +1,388 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Map implementation with a smallest possible memory usage.
+ * It should only be used for maps with small number of items
+ * (e.g. &lt;30) since most operations have an O(n) complexity.
+ * Thus it should be used in cases with large number of map
+ * objects, each having only few items.
+ * 
+ * <p><code>null</code> is not supported for keys or values.</p>
+ */
+public class SmallMap<K, V> implements Map<K, V>
+{
+    /**
+     * stores key-value pair as 2 objects; key first; in case of empty map 
this might be <code>null</code>
+     */
+    private Object[] mapArr;
+
+    /** Creates empty map. */
+    public SmallMap()
+    {
+    }
+    
+    /** Creates map filled with entries from provided map. */
+    public SmallMap(Map<? extends K, ? extends V> initMap)
+    {
+        putAll(initMap);
+    }
+    
+    /**
+     * Returns index of key within map-array or <code>-1</code>
+     * if key is not found (or key is <code>null</code>).
+     */
+    private int findKey(Object key)
+    {
+        if (isEmpty() || (key==null))
+        {
+            return -1;
+        }
+        
+        for ( int aIdx = 0; aIdx < mapArr.length; aIdx+=2 )
+        {
+            if (key.equals(mapArr[aIdx]))
+            {
+                return aIdx;
+            }
+        }
+        
+        return -1;
+    }
+    
+    /**
+     * Returns index of value within map-array or <code>-1</code>
+     * if value is not found (or value is <code>null</code>).
+     */
+    private int findValue(Object value)
+    {
+        if (isEmpty() || (value==null))
+        {
+            return -1;
+        }
+        
+        for ( int aIdx = 1; aIdx < mapArr.length; aIdx+=2 )
+        {
+            if (value.equals(mapArr[aIdx]))
+            {
+                return aIdx;
+            }
+        }
+        
+        return -1;
+    }
+    
+    @Override
+    public int size()
+    {
+        return mapArr == null ? 0 : mapArr.length >> 1;
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return (mapArr == null) || (mapArr.length == 0);
+    }
+
+    @Override
+    public boolean containsKey(Object key)
+    {
+        return findKey(key) >= 0;
+    }
+
+    @Override
+    public boolean containsValue(Object value)
+    {
+        return findValue(value) >= 0;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public V get(Object key)
+    {
+        int kIdx = findKey(key);
+        
+        return kIdx < 0 ? null : (V) mapArr[kIdx+1];
+    }
+
+    @Override
+    public V put(K key, V value)
+    {
+        if ((key == null) || (value == null))
+        {
+            throw new NullPointerException( "Key or value must not be null.");
+        }
+        
+        if (mapArr == null)
+        {
+            mapArr = new Object[] { key, value };
+            return null;
+        }
+        else
+        {
+            int kIdx = findKey(key);
+            
+            if (kIdx < 0)
+            {
+                // key unknown
+                int oldLen = mapArr.length;
+                Object[] newMapArr = new Object[oldLen+2];
+                System.arraycopy(mapArr, 0, newMapArr, 0, oldLen);
+                newMapArr[oldLen] = key;
+                newMapArr[oldLen+1] = value;
+                mapArr = newMapArr;
+                return null;
+            }
+            else
+            {
+                // key exists; replace value
+                @SuppressWarnings("unchecked")
+                V oldValue = (V) mapArr[kIdx+1];
+                mapArr[kIdx+1] = value;
+                return oldValue;
+            }
+        }
+    }
+
+    @Override
+    public V remove(Object key)
+    {
+        int kIdx = findKey(key);
+        
+        if (kIdx < 0)
+        {
+            // not found
+            return null;
+        }
+
+        @SuppressWarnings("unchecked")
+        V oldValue = (V) mapArr[kIdx+1];
+        int oldLen = mapArr.length;
+        
+        if (oldLen == 2)
+        {
+            // was last entry
+            mapArr = null;
+        }
+        else
+        {
+            Object[] newMapArr = new Object[oldLen-2];
+            System.arraycopy(mapArr, 0, newMapArr, 0, kIdx);
+            System.arraycopy(mapArr, kIdx+2, newMapArr, kIdx, oldLen - kIdx - 
2);
+            mapArr = newMapArr;
+        }
+        
+        return oldValue;
+    }
+
+    @Override
+    public final void putAll(Map<? extends K, ? extends V> otherMap)
+    {
+        if ((mapArr == null) || (mapArr.length == 0))
+        {
+            // existing map is empty
+            mapArr = new Object[otherMap.size() << 1];
+            int aIdx = 0;
+            for (Entry<? extends K, ? extends V> entry : otherMap.entrySet())
+            {
+                if ((entry.getKey() == null) || (entry.getValue() == null))
+                {
+                    throw new NullPointerException( "Key or value must not be 
null.");
+                }
+                
+                mapArr[aIdx++] = entry.getKey();
+                mapArr[aIdx++] = entry.getValue();
+            }
+        }
+        else
+        {
+            int oldLen = mapArr.length;
+            // first increase array size to hold all to put entries as if they 
have unknown keys
+            // reduce after adding all to the required size
+            Object[] newMapArr = new Object[oldLen+(otherMap.size() << 1)];
+            System.arraycopy(mapArr, 0, newMapArr, 0, oldLen);
+            
+            int newIdx = oldLen;
+            for (Entry<? extends K, ? extends V> entry : otherMap.entrySet())
+            {
+                if ((entry.getKey() == null) || (entry.getValue() == null))
+                {
+                    throw new NullPointerException( "Key or value must not be 
null.");
+                }
+                
+                int existKeyIdx = findKey(entry.getKey());
+                
+                if (existKeyIdx >= 0)
+                {
+                    // existing key
+                    newMapArr[existKeyIdx+1] = entry.getValue();
+                }
+                else
+                {
+                    // new key
+                    newMapArr[newIdx++] = entry.getKey();
+                    newMapArr[newIdx++] = entry.getValue();
+                }
+            }
+
+            if (newIdx < newMapArr.length)
+            {
+                Object[] reducedMapArr = new Object[newIdx];
+                System.arraycopy(newMapArr, 0, reducedMapArr, 0, newIdx);
+                newMapArr = reducedMapArr;
+            }
+            
+            mapArr = newMapArr;
+        }
+    }
+
+    @Override
+    public void clear()
+    {
+        mapArr = null;
+    }
+
+    /**
+     * Returns a set view of the keys contained in this map.
+     * 
+     * <p>The current implementation does not allow changes to the
+     * returned key set (which would have to be reflected in the
+     * underlying map.</p>
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<K> keySet()
+    {
+        if (isEmpty())
+        {
+            return Collections.emptySet();
+        }
+        
+        Set<K> keys = new LinkedHashSet<K>();
+        for (int kIdx = 0; kIdx < mapArr.length; kIdx+=2)
+        {
+            keys.add((K)mapArr[kIdx]);
+        }
+        return Collections.unmodifiableSet( keys );
+    }
+
+    /**
+     * Returns a collection of the values contained in this map.
+     * 
+     * <p>The current implementation does not allow changes to the
+     * returned collection (which would have to be reflected in the
+     * underlying map.</p>
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<V> values()
+    {
+        if (isEmpty())
+        {
+            return Collections.emptySet();
+        }
+        
+        List<V> values = new ArrayList<V>(mapArr.length >> 1);
+        for (int vIdx = 1; vIdx < mapArr.length; vIdx+=2)
+        {
+            values.add((V)mapArr[vIdx]);
+        }
+        return Collections.unmodifiableList( values );
+    }
+
+    private class SmallMapEntry implements Entry<K, V>
+    {
+        private final int keyIdx;
+        
+        SmallMapEntry(int keyInMapIdx)
+        {
+            keyIdx = keyInMapIdx;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public K getKey()
+        {
+            return (K)mapArr[keyIdx];
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public V getValue()
+        {
+            return (V)mapArr[keyIdx+1];
+        }
+
+        @Override
+        public V setValue(V value)
+        {
+            if (value == null)
+            {
+                throw new NullPointerException( "Key or value must not be 
null.");
+            }
+
+            V oldValue = getValue();
+            mapArr[keyIdx+1] = value;
+            return oldValue;
+        }
+        
+        @Override
+        public int hashCode()
+        {
+            return getKey().hashCode();
+        }
+        
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (!(obj instanceof SmallMap.SmallMapEntry))
+            {
+                return false;
+            }
+            @SuppressWarnings("unchecked")
+            SmallMapEntry other = (SmallMapEntry) obj;
+            
+            return getKey().equals(other.getKey()) && 
getValue().equals(other.getValue());
+        }
+    }
+    
+    @Override
+    public Set<java.util.Map.Entry<K, V>> entrySet()
+    {
+        if (isEmpty())
+        {
+            return Collections.emptySet();
+        }
+        
+        Set<java.util.Map.Entry<K, V>> entries = new LinkedHashSet<Entry<K, 
V>>();
+        for (int kIdx = 0; kIdx < mapArr.length; kIdx+=2)
+        {
+            entries.add(new SmallMapEntry(kIdx));
+        }
+        return Collections.unmodifiableSet( entries );
+    }
+
+}


Reply via email to