Author: msahyoun Date: Mon Dec 23 10:55:46 2019 New Revision: 1871922 URL: http://svn.apache.org/viewvc?rev=1871922&view=rev Log: PDFBOX-4669: throw UnsupportedOperationException for remove operation for a filtered list; adjust unit test
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSArrayList.java pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/COSArrayListTest.java Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSArrayList.java URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSArrayList.java?rev=1871922&r1=1871921&r2=1871922&view=diff ============================================================================== --- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSArrayList.java (original) +++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/COSArrayList.java Mon Dec 23 10:55:46 2019 @@ -43,6 +43,10 @@ public class COSArrayList<E> implements private final COSArray array; private final List<E> actual; + // indicates that the list has been filtered + // i.e. the number of entries in array and actual differ + private boolean isFiltered = false; + private COSDictionary parentDict; private COSName dictKey; @@ -65,6 +69,12 @@ public class COSArrayList<E> implements { actual = actualList; array = cosArray; + + // if the number of entries differs this may come from a filter being + // applied at the PDModel level + if (actual.size() != array.size()) { + isFiltered = true; + } } /** @@ -200,6 +210,11 @@ public class COSArrayList<E> implements @Override public boolean remove(Object o) { + + if (isFiltered) { + throw new UnsupportedOperationException("removing entries from a filtered List is not permitted"); + } + boolean retval = true; int index = actual.indexOf( o ); if( index >= 0 ) @@ -607,8 +622,13 @@ public class COSArrayList<E> implements @Override public E remove(int index) { + if (isFiltered) { + throw new UnsupportedOperationException("removing entries from a filtered List is not permitted"); + } + E toBeRemoved = actual.get(index); - remove(toBeRemoved); + array.remove(index); + actual.remove(index); return toBeRemoved; } Modified: pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/COSArrayListTest.java URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/COSArrayListTest.java?rev=1871922&r1=1871921&r2=1871922&view=diff ============================================================================== --- pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/COSArrayListTest.java (original) +++ pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/common/COSArrayListTest.java Mon Dec 23 10:55:46 2019 @@ -16,14 +16,15 @@ package org.apache.pdfbox.pdmodel.common; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.interactive.annotation.AnnotationFilter; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; @@ -31,9 +32,15 @@ import org.apache.pdfbox.pdmodel.interac import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationSquareCircle; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; public class COSArrayListTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + // next two entries are to be used for comparison with // COSArrayList behaviour in order to ensure that the // intented object is now at the correct position. @@ -64,25 +71,29 @@ public class COSArrayListTest { annotationsList.add(txtMark); annotationsList.add(txtLink); annotationsList.add(aCircle); - assertTrue(annotationsList.size() == 3); + annotationsList.add(txtLink); + assertTrue(annotationsList.size() == 4); tbcAnnotationsList = new ArrayList<PDAnnotation>(); tbcAnnotationsList.add(txtMark); tbcAnnotationsList.add(txtLink); tbcAnnotationsList.add(aCircle); - assertTrue(tbcAnnotationsList.size() == 3); + tbcAnnotationsList.add(txtLink); + assertTrue(tbcAnnotationsList.size() == 4); annotationsArray = new COSArray(); annotationsArray.add(txtMark); annotationsArray.add(txtLink); annotationsArray.add(aCircle); - assertTrue(annotationsArray.size() == 3); + annotationsArray.add(txtLink); + assertTrue(annotationsArray.size() == 4); - tbcAnnotationsArray = new COSBase[3]; + tbcAnnotationsArray = new COSBase[4]; tbcAnnotationsArray[0] = txtMark.getCOSObject(); tbcAnnotationsArray[1] = txtLink.getCOSObject(); tbcAnnotationsArray[2] = aCircle.getCOSObject(); - assertTrue(tbcAnnotationsArray.length == 3); + tbcAnnotationsArray[3] = txtLink.getCOSObject(); + assertTrue(tbcAnnotationsArray.length == 4); // add the annotations to the page pdPage = new PDPage(); @@ -96,17 +107,16 @@ public class COSArrayListTest { public void getFromList() throws Exception { COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < cosArrayList.size(); i++) { PDAnnotation annot = (PDAnnotation) cosArrayList.get(i); - // compare position using COSArrayList - assertTrue("PDAnnotations cosObject at " + i + " shall be at index " + i + " of COSArray", - annotationsArray.indexOf(annot.getCOSObject()) == i); + assertTrue("PDAnnotations cosObject at " + i + " shall be equal to index " + i + " of COSArray", + annotationsArray.get(i).equals(annot.getCOSObject())); // compare with Java List/Array assertTrue("PDAnnotations at " + i + " shall be at index " + i + " of List", - tbcAnnotationsList.indexOf(annot) == i); + tbcAnnotationsList.get(i).equals((annot))); assertEquals("PDAnnotations cosObject at " + i + " shall be at position " + i + " of Array", - tbcAnnotationsArray[i], annot.getCOSObject()); + tbcAnnotationsArray[i], annot.getCOSObject()); } } @@ -121,12 +131,12 @@ public class COSArrayListTest { PDAnnotationSquareCircle aSquare = new PDAnnotationSquareCircle(PDAnnotationSquareCircle.SUB_TYPE_SQUARE); cosArrayList.add(aSquare); - assertTrue("List size shall be 4", annotationsList.size() == 4); - assertTrue("COSArray size shall be 4", annotationsArray.size() == 4); + assertTrue("List size shall be 5", annotationsList.size() == 5); + assertTrue("COSArray size shall be 5", annotationsArray.size() == 5); - PDAnnotation annot = (PDAnnotation) annotationsList.get(3); - assertTrue("Added annotation shall be 4th entry in COSArray", annotationsArray.indexOf(annot.getCOSObject()) == 3); - assertEquals("Provided COSArray and underlying COSArray shall be equal", annotationsArray, cosArrayList.toList()); + PDAnnotation annot = (PDAnnotation) annotationsList.get(4); + assertTrue("Added annotation shall be 4th entry in COSArray", annotationsArray.indexOf(annot.getCOSObject()) == 4); + assertEquals("Provided COSArray and underlying COSArray shall be equal", annotationsArray, cosArrayList.getCOSArray()); } /** @@ -136,66 +146,114 @@ public class COSArrayListTest { public void removeFromListByIndex() throws Exception { COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); - int positionToRemove = 1; + int positionToRemove = 2; cosArrayList.remove(positionToRemove); - assertTrue("List size shall be 2", cosArrayList.size() == 2); - assertTrue("COSArray size shall be 2", annotationsArray.size() == 2); + assertTrue("List size shall be 3", cosArrayList.size() == 3); + assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); - PDAnnotation annot = (PDAnnotation) cosArrayList.get(1); - assertTrue("Object at original position 2 shall now be at position 1 in underlying COSArray", - annotationsArray.indexOf(annot.getCOSObject()) == 1); + assertTrue("PDAnnotation shall no longer exist in List", + cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); + assertTrue("COSObject shall no longer exist in COSArray", + annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); + } + + /** + * Test removing a unique PDModel element by index is in sync with underlying COSArray + */ + @Test + public void removeUniqueFromListByObject() throws Exception { + COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); + + int positionToRemove = 2; + PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); + + assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); + assertTrue("List size shall be 3", cosArrayList.size() == 3); + assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); // compare with Java List/Array to ensure correct object at position - assertTrue("List object at 2 is at position 1 in COSArrayList now", - cosArrayList.indexOf(tbcAnnotationsList.get(2)) == 1); - assertTrue("COSObject of List object at 2 is at position 1 in COSArray now", - annotationsArray.indexOf(tbcAnnotationsList.get(2).getCOSObject()) == 1); - assertTrue("Array object at 2 is at position 1 in underlying COSArray now", - annotationsArray.indexOf(tbcAnnotationsArray[2]) == 1); + assertTrue("List object at 3 is at position 2 in COSArrayList now", + cosArrayList.get(2).equals(tbcAnnotationsList.get(3))); + assertTrue("COSObject of List object at 3 is at position 2 in COSArray now", + annotationsArray.get(2).equals(tbcAnnotationsList.get(3).getCOSObject())); + assertTrue("Array object at 3 is at position 2 in underlying COSArray now", + annotationsArray.get(2).equals(tbcAnnotationsArray[3])); assertTrue("PDAnnotation shall no longer exist in List", - cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); + cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); assertTrue("COSObject shall no longer exist in COSArray", - annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); + annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); + + assertFalse("Remove shall not remove any object",cosArrayList.remove(toBeRemoved)); + } /** - * Test removing a PDModel element by index is in sync with underlying COSArray + * Test removing a unique PDModel element by index is in sync with underlying COSArray + */ + @Test + public void removeAllUniqueFromListByObject() throws Exception { + COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); + + int positionToRemove = 2; + PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); + + List<PDAnnotation> toBeRemovedInstances = Collections.singletonList(toBeRemoved); + + assertTrue("Remove operation shall return true",cosArrayList.removeAll(toBeRemovedInstances)); + assertTrue("List size shall be 3", cosArrayList.size() == 3); + assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); + + assertFalse("Remove shall not remove any object",cosArrayList.removeAll(toBeRemovedInstances)); + } + + + /** + * Test removing a multiple appearing PDModel element by index is in sync with underlying COSArray */ @Test - public void removeFromListByObject() throws Exception { + public void removeMultipleFromListByObject() throws Exception { COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); int positionToRemove = 1; PDAnnotation toBeRemoved = tbcAnnotationsList.get(positionToRemove); - cosArrayList.remove(toBeRemoved); + assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); + assertTrue("List size shall be 3", cosArrayList.size() == 3); + assertTrue("COSArray size shall be 3", annotationsArray.size() == 3); + assertTrue("Remove operation shall return true",cosArrayList.remove(toBeRemoved)); assertTrue("List size shall be 2", cosArrayList.size() == 2); assertTrue("COSArray size shall be 2", annotationsArray.size() == 2); - PDAnnotation annot = (PDAnnotation) cosArrayList.get(1); - assertTrue("Object at original position 2 shall now be at position 1 in underlying COSArray", - annotationsArray.indexOf(annot.getCOSObject()) == 1); + } - // compare with Java List/Array to ensure correct object at position - assertTrue("List object at 2 is at position 1 in COSArrayList now", - cosArrayList.indexOf(tbcAnnotationsList.get(2)) == 1); - assertTrue("COSObject of List object at 2 is at position 1 in COSArray now", - annotationsArray.indexOf(tbcAnnotationsList.get(2).getCOSObject()) == 1); - assertTrue("Array object at 2 is at position 1 in underlying COSArray now", - annotationsArray.indexOf(tbcAnnotationsArray[2]) == 1); + /** + * Test removing a unique PDModel element by index is in sync with underlying COSArray + */ + @Test + public void removeAllMultipleFromListByObject() throws Exception { + COSArrayList<PDAnnotation> cosArrayList = new COSArrayList<PDAnnotation>(annotationsList, annotationsArray); - assertTrue("PDAnnotation shall no longer exist in List", - cosArrayList.indexOf(tbcAnnotationsList.get(positionToRemove)) == -1); - assertTrue("COSObject shall no longer exist in COSArray", - annotationsArray.indexOf(tbcAnnotationsArray[positionToRemove]) == -1); + int positionToRemove = 1; + PDAnnotation toBeRemoved = annotationsList.get(positionToRemove); + + List<PDAnnotation> toBeRemovedInstances = Collections.singletonList(toBeRemoved); + + assertTrue("Remove operation shall return true",cosArrayList.removeAll(toBeRemovedInstances)); + assertTrue("List size shall be 2", cosArrayList.size() == 2); + assertTrue("COSArray size shall be 2", annotationsArray.size() == 2); + + assertFalse("Remove shall not remove any object",cosArrayList.removeAll(toBeRemovedInstances)); } @Test public void removeFromFilteredListByIndex() throws Exception { + // removing from a filtered list is not permitted + thrown.expect(UnsupportedOperationException.class); + // retrieve all annotations from page but the link annotation // which is 2nd in list - see above setup AnnotationFilter annotsFilter = new AnnotationFilter() @@ -208,31 +266,18 @@ public class COSArrayListTest { }; COSArrayList<PDAnnotation> cosArrayList = (COSArrayList<PDAnnotation>) pdPage.getAnnotations(annotsFilter); - COSArray underlyingCOSArray = pdPage.getCOSObject().getCOSArray(COSName.ANNOTS); - - assertTrue("Filtered COSArrayList size shall be 2", cosArrayList.size() == 2); - assertTrue("Underlying COSArray shall have 3 entries", underlyingCOSArray.size() == 3); - assertTrue("Backed COSArray shall have 3 entries", cosArrayList.getCOSArray().size() == 3); - // remove aCircle annotation - int positionToRemove = 1; - PDAnnotation toBeRemoved = cosArrayList.get(positionToRemove); - assertTrue("We should remove the circle annotation", toBeRemoved.getSubtype().equals(PDAnnotationSquareCircle.SUB_TYPE_CIRCLE)); - cosArrayList.remove(positionToRemove); - - assertTrue("List size shall be 2", cosArrayList.size() == 1); - assertTrue("COSArray size shall be 2", underlyingCOSArray.size() == 2); - assertTrue("Backed COSArray size shall be 2", cosArrayList.getCOSArray().size() == 2); - - assertTrue("Removed annotation shall no longer appear in COSArrayList", cosArrayList.indexOf(toBeRemoved) == -1); - assertTrue("Removed annotation shall no longer appear in underlying COSArray", underlyingCOSArray.indexOf(toBeRemoved.getCOSObject()) == -1); - assertTrue("Removed annotation shall no longer appear in backed COSArray", cosArrayList.getCOSArray().indexOf(toBeRemoved.getCOSObject()) == -1); + // this call should fail + cosArrayList.remove(1); } @Test public void removeFromFilteredListByObject() throws Exception { + // removing from a filtered list is not permitted + thrown.expect(UnsupportedOperationException.class); + // retrieve all annotations from page but the link annotation // which is 2nd in list - see above setup AnnotationFilter annotsFilter = new AnnotationFilter() @@ -245,24 +290,13 @@ public class COSArrayListTest { }; COSArrayList<PDAnnotation> cosArrayList = (COSArrayList<PDAnnotation>) pdPage.getAnnotations(annotsFilter); - COSArray underlyingCOSArray = pdPage.getCOSObject().getCOSArray(COSName.ANNOTS); - - assertTrue("Filtered COSArrayList size shall be 2", cosArrayList.size() == 2); - assertTrue("Underlying COSArray shall have 3 entries", underlyingCOSArray.size() == 3); - assertTrue("Backed COSArray shall have 3 entries", cosArrayList.getCOSArray().size() == 3); - // remove aCircle annotation + // remove object int positionToRemove = 1; PDAnnotation toBeRemoved = cosArrayList.get(positionToRemove); - assertTrue("We should remove the circle annotation", toBeRemoved.getSubtype().equals(PDAnnotationSquareCircle.SUB_TYPE_CIRCLE)); + + // this call should fail cosArrayList.remove(toBeRemoved); - assertTrue("List size shall be 2", cosArrayList.size() == 1); - assertTrue("COSArray size shall be 2", underlyingCOSArray.size() == 2); - assertTrue("Backed COSArray size shall be 2", cosArrayList.getCOSArray().size() == 2); - - assertTrue("Removed annotation shall no longer appear in COSArrayList", cosArrayList.indexOf(toBeRemoved) == -1); - assertTrue("Removed annotation shall no longer appear in underlying COSArray", underlyingCOSArray.indexOf(toBeRemoved.getCOSObject()) == -1); - assertTrue("Removed annotation shall no longer appear in backed COSArray", cosArrayList.getCOSArray().indexOf(toBeRemoved.getCOSObject()) == -1); } } \ No newline at end of file