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 2020/08/02 11:51:46 UTC
svn commit: r1880517 - in /pdfbox/trunk/pdfbox/src:
main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java
test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java
Author: lehmi
Date: Sun Aug 2 11:51:46 2020
New Revision: 1880517
URL: http://svn.apache.org/viewvc?rev=1880517&view=rev
Log:
PDFBOX-4836: introduce new class to wrap a sequence of RandomAccessRead into one
Added:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java (with props)
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java (with props)
Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java?rev=1880517&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java Sun Aug 2 11:51:46 2020
@@ -0,0 +1,226 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Wrapper class to combine several RandomAccessRead instances so that they can be accessed as one big RandomAccessRead.
+ */
+public class SequenceRandomAccessRead implements RandomAccessRead
+{
+ private final List<RandomAccessRead> randomAccessReadList;
+ private final long[] startPositions;
+ private final long[] endPositions;
+ private final int numberOfReader;
+ private int currentIndex = 0;
+ private long currentPosition = 0;
+ private long length = 0;
+ private boolean isClosed = false;
+ private RandomAccessRead currentRandomAccessRead = null;
+
+ public SequenceRandomAccessRead(List<RandomAccessRead> randomAccessReadList)
+ {
+ if (randomAccessReadList == null)
+ {
+ throw new IllegalArgumentException("Missing input parameter");
+ }
+ if (randomAccessReadList.isEmpty())
+ {
+ throw new IllegalArgumentException("Empty list");
+ }
+ this.randomAccessReadList = new ArrayList<>(randomAccessReadList);
+ numberOfReader = randomAccessReadList.size();
+ currentRandomAccessRead = randomAccessReadList.get(currentIndex);
+ startPositions = new long[numberOfReader];
+ endPositions = new long[numberOfReader];
+ for(int i=0;i<numberOfReader;i++)
+ {
+ try
+ {
+ startPositions[i] = length;
+ length += randomAccessReadList.get(i).length();
+ endPositions[i] = length - 1;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Problematic list", e);
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ for (RandomAccessRead randomAccessRead : randomAccessReadList)
+ {
+ randomAccessRead.close();
+ }
+ randomAccessReadList.clear();
+ currentRandomAccessRead = null;
+ isClosed = true;
+ }
+
+ private RandomAccessRead getCurrentReader() throws IOException
+ {
+ if (currentRandomAccessRead.isEOF() && currentIndex < numberOfReader - 1)
+ {
+ currentIndex++;
+ currentRandomAccessRead = randomAccessReadList.get(currentIndex);
+ currentRandomAccessRead.seek(0);
+ }
+ return currentRandomAccessRead;
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ checkClosed();
+ RandomAccessRead randomAccessRead = getCurrentReader();
+ int value = randomAccessRead.read();
+ if (value > -1)
+ {
+ currentPosition++;
+ }
+ return value;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public int read(byte[] b, int offset, int length) throws IOException
+ {
+ checkClosed();
+ RandomAccessRead randomAccessRead = getCurrentReader();
+ int bytesRead = randomAccessRead.read(b, offset, length);
+ while (bytesRead < length && available() > 0)
+ {
+ randomAccessRead = getCurrentReader();
+ bytesRead += randomAccessRead.read(b, offset + bytesRead, length - bytesRead);
+ }
+ currentPosition += bytesRead;
+ return bytesRead;
+ }
+
+ @Override
+ public long getPosition() throws IOException
+ {
+ checkClosed();
+ return currentPosition;
+ }
+
+ @Override
+ public void seek(long position) throws IOException
+ {
+ checkClosed();
+ if (position < 0)
+ {
+ throw new IOException("Invalid position " + position);
+ }
+ // it is allowed to jump beyond the end of the file
+ // jump to the end of the reader
+ if (position >= length)
+ {
+ currentIndex = numberOfReader - 1;
+ }
+ else
+ {
+ for (int i = 0; i < numberOfReader; i++)
+ {
+ if (position >= startPositions[i] && position <= endPositions[i])
+ {
+ currentIndex = i;
+ break;
+ }
+ }
+ }
+ currentRandomAccessRead = randomAccessReadList.get(currentIndex);
+ currentRandomAccessRead.seek(position - startPositions[currentIndex]);
+ currentPosition = position;
+ }
+
+ @Override
+ public long length() throws IOException
+ {
+ checkClosed();
+ return length;
+ }
+
+ @Override
+ public void rewind(int bytes) throws IOException
+ {
+ seek(getPosition() - bytes);
+ }
+
+ @Override
+ public boolean isClosed()
+ {
+ return isClosed;
+ }
+
+ /**
+ * Ensure that the SequenceRandomAccessRead is not closed
+ *
+ * @throws IOException
+ */
+ private void checkClosed() throws IOException
+ {
+ if (isClosed)
+ {
+ // consider that the rab is closed if there is no current buffer
+ throw new IOException("RandomAccessBuffer already closed");
+ }
+ }
+
+ @Override
+ public int peek() throws IOException
+ {
+ int result = read();
+ if (result != -1)
+ {
+ rewind(1);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isEOF() throws IOException
+ {
+ checkClosed();
+ return currentPosition >= length;
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ checkClosed();
+ return (int) Math.min(length - currentPosition, Integer.MAX_VALUE);
+ }
+
+ @Override
+ public RandomAccessReadView createView(long startPosition, long streamLength) throws IOException
+ {
+ throw new IOException(getClass().getName() + ".createView isn't supported.");
+ }
+
+}
Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/io/SequenceRandomAccessRead.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java?rev=1880517&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java (added)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java Sun Aug 2 11:51:46 2020
@@ -0,0 +1,216 @@
+/*
+ * 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.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Unittest for org.apache.pdfbox.io.SequenceRandomAccessRead
+ *
+ */
+public class SequenceRandomAccessReadTest
+{
+
+ @Test
+ public void TestCreateAndRead() throws IOException
+ {
+ String input1 = "This is a test string number 1";
+ RandomAccessReadBuffer randomAccessReadBuffer1 = new RandomAccessReadBuffer(
+ input1.getBytes());
+ String input2 = "This is a test string number 2";
+ RandomAccessReadBuffer randomAccessReadBuffer2 = new RandomAccessReadBuffer(
+ input2.getBytes());
+ List<RandomAccessRead> inputList = Arrays.asList(randomAccessReadBuffer1,
+ randomAccessReadBuffer2);
+ SequenceRandomAccessRead sequenceRandomAccessRead = new SequenceRandomAccessRead(inputList);
+
+ int overallLength = input1.length() + input2.length();
+ assertEquals(overallLength, sequenceRandomAccessRead.length());
+
+ byte[] bytesRead = new byte[overallLength];
+
+ assertEquals(overallLength, sequenceRandomAccessRead.read(bytesRead));
+ assertEquals(input1 + input2, new String(bytesRead));
+
+ sequenceRandomAccessRead.close();
+
+ // test missing parameter
+ try (RandomAccessRead read = new SequenceRandomAccessRead(null))
+ {
+ fail("Constructor should have thrown an IllegalArgumentException");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+
+ // test empty list
+ try (RandomAccessRead read = new SequenceRandomAccessRead(Collections.emptyList()))
+ {
+ fail("Constructor should have thrown an IllegalArgumentException");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ // test problematic list
+ try (RandomAccessRead read = new SequenceRandomAccessRead(inputList))
+ {
+ fail("Constructor should have thrown an IllegalArgumentException");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ @Test
+ public void TestSeekPeekAndRewind() throws IOException
+ {
+ String input1 = "01234567890123456789";
+ RandomAccessReadBuffer randomAccessReadBuffer1 = new RandomAccessReadBuffer(
+ input1.getBytes());
+ String input2 = "abcdefghijklmnopqrst";
+ RandomAccessReadBuffer randomAccessReadBuffer2 = new RandomAccessReadBuffer(
+ input2.getBytes());
+ List<RandomAccessRead> inputList = Arrays.asList(randomAccessReadBuffer1,
+ randomAccessReadBuffer2);
+ SequenceRandomAccessRead sequenceRandomAccessRead = new SequenceRandomAccessRead(inputList);
+
+ // test seek, rewind and peek in the first part of the sequence
+ sequenceRandomAccessRead.seek(4);
+ assertEquals(4, sequenceRandomAccessRead.getPosition());
+ assertEquals('4', sequenceRandomAccessRead.read());
+ assertEquals(5, sequenceRandomAccessRead.getPosition());
+ sequenceRandomAccessRead.rewind(1);
+ assertEquals(4, sequenceRandomAccessRead.getPosition());
+ assertEquals('4', sequenceRandomAccessRead.read());
+ assertEquals('5', sequenceRandomAccessRead.peek());
+ assertEquals(5, sequenceRandomAccessRead.getPosition());
+ assertEquals('5', sequenceRandomAccessRead.read());
+ assertEquals(6, sequenceRandomAccessRead.getPosition());
+
+ // test seek, rewind and peek in the second part of the sequence
+ sequenceRandomAccessRead.seek(24);
+ assertEquals(24, sequenceRandomAccessRead.getPosition());
+ assertEquals('e', sequenceRandomAccessRead.read());
+ sequenceRandomAccessRead.rewind(1);
+ assertEquals('e', sequenceRandomAccessRead.read());
+ assertEquals('f', sequenceRandomAccessRead.peek());
+ assertEquals('f', sequenceRandomAccessRead.read());
+
+ sequenceRandomAccessRead.close();
+ }
+
+ @Test
+ public void TestBorderCases() throws IOException
+ {
+ String input1 = "01234567890123456789";
+ RandomAccessReadBuffer randomAccessReadBuffer1 = new RandomAccessReadBuffer(
+ input1.getBytes());
+ String input2 = "abcdefghijklmnopqrst";
+ RandomAccessReadBuffer randomAccessReadBuffer2 = new RandomAccessReadBuffer(
+ input2.getBytes());
+ List<RandomAccessRead> inputList = Arrays.asList(randomAccessReadBuffer1,
+ randomAccessReadBuffer2);
+ SequenceRandomAccessRead sequenceRandomAccessRead = new SequenceRandomAccessRead(inputList);
+
+ // jump to the last byte of the first part of the sequence
+ sequenceRandomAccessRead.seek(19);
+ assertEquals('9', sequenceRandomAccessRead.read());
+ sequenceRandomAccessRead.rewind(1);
+ assertEquals('9', sequenceRandomAccessRead.read());
+ assertEquals('a', sequenceRandomAccessRead.peek());
+ assertEquals('a', sequenceRandomAccessRead.read());
+
+ // jump back to the first sequence
+ sequenceRandomAccessRead.seek(17);
+ byte[] bytesRead = new byte[6];
+ assertEquals(6, sequenceRandomAccessRead.read(bytesRead));
+ assertEquals("789abc", new String(bytesRead));
+ assertEquals(23, sequenceRandomAccessRead.getPosition());
+
+ // rewind back to the first sequence
+ sequenceRandomAccessRead.rewind(6);
+ assertEquals(17, sequenceRandomAccessRead.getPosition());
+ bytesRead = new byte[6];
+ assertEquals(6, sequenceRandomAccessRead.read(bytesRead));
+ assertEquals("789abc", new String(bytesRead));
+
+ // jump to the start of the sequence
+ sequenceRandomAccessRead.seek(0);
+ bytesRead = new byte[6];
+ assertEquals(6, sequenceRandomAccessRead.read(bytesRead));
+ assertEquals("012345", new String(bytesRead));
+
+ sequenceRandomAccessRead.close();
+ }
+
+ @Test
+ public void TestEOF() throws IOException
+ {
+ String input1 = "01234567890123456789";
+ RandomAccessReadBuffer randomAccessReadBuffer1 = new RandomAccessReadBuffer(
+ input1.getBytes());
+ String input2 = "abcdefghijklmnopqrst";
+ RandomAccessReadBuffer randomAccessReadBuffer2 = new RandomAccessReadBuffer(
+ input2.getBytes());
+ List<RandomAccessRead> inputList = Arrays.asList(randomAccessReadBuffer1,
+ randomAccessReadBuffer2);
+ SequenceRandomAccessRead sequenceRandomAccessRead = new SequenceRandomAccessRead(inputList);
+
+ int overallLength = input1.length() + input2.length();
+
+ sequenceRandomAccessRead.seek(overallLength - 1);
+ assertFalse(sequenceRandomAccessRead.isEOF());
+ assertEquals('t', sequenceRandomAccessRead.peek());
+ assertFalse(sequenceRandomAccessRead.isEOF());
+ assertEquals('t', sequenceRandomAccessRead.read());
+ assertTrue(sequenceRandomAccessRead.isEOF());
+ // rewind
+ sequenceRandomAccessRead.rewind(5);
+ assertFalse(sequenceRandomAccessRead.isEOF());
+ byte[] bytesRead = new byte[5];
+ assertEquals(5, sequenceRandomAccessRead.read(bytesRead));
+ assertEquals("pqrst", new String(bytesRead));
+ assertTrue(sequenceRandomAccessRead.isEOF());
+
+ // seek to a position beyond the end of the input
+ sequenceRandomAccessRead.seek(overallLength + 10);
+ assertTrue(sequenceRandomAccessRead.isEOF());
+ assertEquals(overallLength + 10, sequenceRandomAccessRead.getPosition());
+
+ sequenceRandomAccessRead.close();
+
+ try
+ {
+ sequenceRandomAccessRead.read();
+ fail("checkClosed should have thrown an IOException");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+}
Propchange: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/io/SequenceRandomAccessReadTest.java
------------------------------------------------------------------------------
svn:eol-style = native