From: Javen O'Neal Date: Sun, 10 Apr 2016 14:09:02 +0000 (+0000) Subject: bug 52949: add junit test for RLEDecompressingInputStream X-Git-Tag: REL_3_15_BETA2~336 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=17a339549da4392415cb11016a906889c7400589;p=poi.git bug 52949: add junit test for RLEDecompressingInputStream git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1738436 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java index 439f47e761..80151146ad 100644 --- a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java +++ b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java @@ -48,8 +48,8 @@ import org.apache.poi.poifs.storage.SmallBlockTableReader; public class POIFSReader { - private POIFSReaderRegistry registry; - private boolean registryClosed; + private final POIFSReaderRegistry registry; + private boolean registryClosed; /** * Create a POIFSReader diff --git a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReaderEvent.java b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReaderEvent.java index 7a64adef94..4d9d93fa24 100644 --- a/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReaderEvent.java +++ b/src/java/org/apache/poi/poifs/eventfilesystem/POIFSReaderEvent.java @@ -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 diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java b/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java index dc1539a939..5b6441cdcc 100644 --- a/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java +++ b/src/java/org/apache/poi/poifs/filesystem/POIFSDocumentPath.java @@ -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,13 +199,19 @@ 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); - - parent.components = new String[ length ]; - System.arraycopy(components, 0, parent.components, 0, length); + String[] parentComponents = new String[ length ]; + System.arraycopy(components, 0, parentComponents, 0, length); + POIFSDocumentPath parent = new POIFSDocumentPath(parentComponents); + return parent; } + + /** + *

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.

+ * + * @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]; + } /** *

Returns a string representation of the path. Components are - * separated by the platform-specific file separator.

+ * separated by the platform-specific file separator {@link File#separatorChar}

* * @return string representation * diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java index befebf444c..2f61fd1d73 100644 --- a/src/java/org/apache/poi/util/IOUtils.java +++ b/src/java/org/apache/poi/util/IOUtils.java @@ -120,13 +120,18 @@ public final class IOUtils { } /** - * Same as the normal in.read(b, off, len), but tries to ensure - * that the entire len number of bytes is read. - *

- * If the end of file is reached before any bytes are read, returns -1. If + *

Same as the normal {@link InputStream#read(byte[], int, int)}, but tries to ensure + * that the entire len number of bytes is read.

+ * + *

If the end of file is reached before any bytes are read, returns -1. 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 len + * bytes have been read, will return len bytes.

+ * + * @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 b 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; diff --git a/src/java/org/apache/poi/util/RLEDecompressingInputStream.java b/src/java/org/apache/poi/util/RLEDecompressingInputStream.java index 0090ebb3f8..b8a8587021 100644 --- a/src/java/org/apache/poi/util/RLEDecompressingInputStream.java +++ b/src/java/org/apache/poi/util/RLEDecompressingInputStream.java @@ -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(); + } } diff --git a/src/testcases/org/apache/poi/util/TestRLEDecompressingInputStream.java b/src/testcases/org/apache/poi/util/TestRLEDecompressingInputStream.java new file mode 100644 index 0000000000..6693ab2d47 --- /dev/null +++ b/src/testcases/org/apache/poi/util/TestRLEDecompressingInputStream.java @@ -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()); + } +}