bug 52949: add junit test for RLEDecompressingInputStream

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1738436 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2016-04-10 14:09:02 +00:00
parent 46a29e4a24
commit 17a339549d
6 changed files with 231 additions and 22 deletions

View File

@ -48,7 +48,7 @@ import org.apache.poi.poifs.storage.SmallBlockTableReader;
public class POIFSReader
{
private POIFSReaderRegistry registry;
private final POIFSReaderRegistry registry;
private boolean registryClosed;
/**

View File

@ -31,9 +31,9 @@ import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
public class POIFSReaderEvent
{
private DocumentInputStream stream;
private POIFSDocumentPath path;
private String documentName;
private final DocumentInputStream stream;
private final POIFSDocumentPath path;
private final String documentName;
/**
* package scoped constructor

View File

@ -35,8 +35,8 @@ public class POIFSDocumentPath
{
private static final POILogger log = POILogFactory.getLogger(POIFSDocumentPath.class);
private String[] components;
private int hashcode = 0;
private final String[] components;
private int hashcode = 0; //lazy-compute hashCode
/**
* constructor for the path of a document that is not in the root
@ -199,14 +199,20 @@ public class POIFSDocumentPath
{
if (hashcode == 0)
{
for (int j = 0; j < components.length; j++)
{
hashcode += components[ j ].hashCode();
}
hashcode = computeHashCode();
}
return hashcode;
}
private int computeHashCode() {
int code = 0;
for (int j = 0; j < components.length; j++)
{
code += components[ j ].hashCode();
}
return code;
}
/**
* @return the number of components
*/
@ -249,16 +255,32 @@ public class POIFSDocumentPath
{
return null;
}
POIFSDocumentPath parent = new POIFSDocumentPath(null);
String[] parentComponents = new String[ length ];
System.arraycopy(components, 0, parentComponents, 0, length);
POIFSDocumentPath parent = new POIFSDocumentPath(parentComponents);
parent.components = new String[ length ];
System.arraycopy(components, 0, parent.components, 0, length);
return parent;
}
/**
* <p>Returns the last name in the document path's name sequence.
* If the document path's name sequence is empty, then the empty string is returned.</p>
*
* @since 2016-04-09
* @return The last name in the document path's name sequence, or empty string if this is the root path
*/
public String getName()
{
if (components.length == 0) {
return "";
}
return components[components.length - 1];
}
/**
* <p>Returns a string representation of the path. Components are
* separated by the platform-specific file separator.</p>
* separated by the platform-specific file separator {@link File#separatorChar}</p>
*
* @return string representation
*

View File

@ -120,13 +120,18 @@ public final class IOUtils {
}
/**
* Same as the normal <tt>in.read(b, off, len)</tt>, but tries to ensure
* that the entire len number of bytes is read.
* <p>
* If the end of file is reached before any bytes are read, returns -1. If
* <p>Same as the normal {@link InputStream#read(byte[], int, int)}, but tries to ensure
* that the entire len number of bytes is read.</p>
*
* <p>If the end of file is reached before any bytes are read, returns <tt>-1</tt>. If
* the end of the file is reached after some bytes are read, returns the
* number of bytes read. If the end of the file isn't reached before len
* bytes have been read, will return len bytes.
* number of bytes read. If the end of the file isn't reached before <tt>len</tt>
* bytes have been read, will return <tt>len</tt> bytes.</p>
*
* @param in the stream from which the data is read.
* @param b the buffer into which the data is read.
* @param off the start offset in array <tt>b</tt> at which the data is written.
* @param len the maximum number of bytes to read.
*/
public static int readFully(InputStream in, byte[] b, int off, int len) throws IOException {
int total = 0;

View File

@ -19,6 +19,8 @@ package org.apache.poi.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/**
* Wrapper of InputStream which provides Run Length Encoding (RLE)
@ -270,4 +272,18 @@ public class RLEDecompressingInputStream extends InputStream {
}
return (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);
}
public static final byte[] decompress(byte[] compressed) throws IOException {
return decompress(compressed, 0, compressed.length);
}
public static final byte[] decompress(byte[] compressed, int offset, int length) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream instream = new ByteArrayInputStream(compressed, offset, length);
InputStream stream = new RLEDecompressingInputStream(instream);
IOUtils.copy(stream, out);
stream.close();
out.close();
return out.toByteArray();
}
}

View File

@ -0,0 +1,166 @@
/* ====================================================================
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.poi.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
public class TestRLEDecompressingInputStream {
/**
* Section 3.2.1 No Compression Example
*
* The following string illustrates an ASCII text string with a set of characters that cannot be compressed
* by the compression algorithm specified in section 2.4.1.
*
* abcdefghijklmnopqrstuv.
*
* This example is provided to demonstrate the results of compressing and decompressing the string
* using an interoperable implementation of the algorithm specified in section 2.4.1.
*
* The following hex array represents the compressed byte array of the example string as compressed by
* the compression algorithm.
*
* 01 19 B0 00 61 62 63 64 65 66 67 68 00 69 6A 6B 6C
* 6D 6E 6F 70 00 71 72 73 74 75 76 2E
*
* The following hex array represents the decompressed byte array of the example string as
* decompressed by the decompression algorithm.
*
* 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71
* 72 73 74 75 76 2E
*
*/
@Test
public void noCompressionExample() {
final byte[] compressed = {
0x01, 0x19, (byte)0xB0, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x00, 0x69, 0x6A, 0x6B, 0x6C,
0x6D, 0x6E, 0x6F, 0x70, 0x00, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x2E
};
final String expected = "abcdefghijklmnopqrstuv.";
checkRLEDecompression(expected, compressed);
}
/**
* Section 3.2.2 Normal Compression Example
*
* The following string illustrates an ASCII text string with a typical set of characters that can be
* compressed by the compression algorithm.
*
* #aaabcdefaaaaghijaaaaaklaaamnopqaaaaaaaaaaaarstuvwxyzaaa
*
* This example is provided to demonstrate the results of compressing and decompressing the example
* string using an interoperable implementation of the algorithm specified in section 2.4.1.
*
* The following hex array represents the compressed byte array of the example string as compressed by
* the compression algorithm:
*
* 01 2F B0 00 23 61 61 61 62 63 64 65 82 66 00 70
* 61 67 68 69 6A 01 38 08 61 6B 6C 00 30 6D 6E 6F
* 70 06 71 02 70 04 10 72 73 74 75 76 10 77 78 79
* 7A 00 3C
*
* The following hex array represents the decompressed byte array of the example string as
* decompressed by the decompression algorithm:
*
* 23 61 61 61 62 63 64 65 66 61 61 61 61 67 68 69
* 6a 61 61 61 61 61 6B 6C 61 61 61 6D 6E 6F 70 71
* 61 61 61 61 61 61 61 61 61 61 61 61 72 73 74 75
* 76 77 78 79 7A 61 61 61
*/
@Test
public void normalCompressionExample() {
final byte[] compressed = {
0x01, 0x2F, (byte)0xB0, 0x00, 0x23, 0x61, 0x61, 0x61, 0x62, 0x63, 0x64, 0x65, (byte)0x82, 0x66, 0x00, 0x70,
0x61, 0x67, 0x68, 0x69, 0x6A, 0x01, 0x38, 0x08, 0x61, 0x6B, 0x6C, 0x00, 0x30, 0x6D, 0x6E, 0x6F,
0x70, 0x06, 0x71, 0x02, 0x70, 0x04, 0x10, 0x72, 0x73, 0x74, 0x75, 0x76, 0x10, 0x77, 0x78, 0x79,
0x7A, 0x00, 0x3C
};
final String expected = "#aaabcdefaaaaghijaaaaaklaaamnopqaaaaaaaaaaaarstuvwxyzaaa";
checkRLEDecompression(expected, compressed);
}
/**
* Section 3.2.3 Maximum Compression Example
*
* The following string illustrates an ASCII text string with a typical set of characters that can be
* compressed by the compression algorithm.
*
* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
*
* This example is provided to demonstrate the results of compressing and decompressing the example
* string using an interoperable implementation of the algorithm specified in section 2.4.1.
*
* The following hex array represents the compressed byte array of the example string as compressed by
* the compression algorithm:
*
* 01 03 B0 02 61 45 00
*
* The following hex array represents the decompressed byte array of the example string as
* decompressed by the decompression algorithm:
*
* 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
* 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
* 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
* 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
* 61 61 61 61 61 61 61 61 61
*/
@Test
public void maximumCompressionExample() {
final byte[] compressed = {
0x01, 0x03, (byte)0xB0, 0x02, 0x61, 0x45, 0x00
};
final String expected = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
checkRLEDecompression(expected, compressed);
}
@Test
public void decompress() throws IOException {
final byte[] compressed = {
0x01, 0x03, (byte)0xB0, 0x02, 0x61, 0x45, 0x00
};
final byte[] expanded = RLEDecompressingInputStream.decompress(compressed);
final byte[] expected = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".getBytes();
assertArrayEquals(expected, expanded);
}
private static void checkRLEDecompression(String expected, byte[] runLengthEncodedData) {
InputStream compressedStream = new ByteArrayInputStream(runLengthEncodedData);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
InputStream stream = new RLEDecompressingInputStream(compressedStream);
try {
IOUtils.copy(stream, out);
} finally {
out.close();
stream.close();
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
assertEquals(expected, out.toString());
}
}