From: Dominik Stadler Date: Thu, 30 Oct 2014 05:53:06 +0000 (+0000) Subject: Apply patch for bug 57151: Document CellRangeAddress and add some validation to preve... X-Git-Tag: REL_3_11_BETA3~29 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c3753981dd89fe172cd4daee76f431500902d69f;p=poi.git Apply patch for bug 57151: Document CellRangeAddress and add some validation to prevent invalid row/column combinations. Cover class CellRangeAddress fully in unit tests. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1635389 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddress.java b/src/java/org/apache/poi/ss/util/CellRangeAddress.java index b69476ea24..1f40cf693d 100644 --- a/src/java/org/apache/poi/ss/util/CellRangeAddress.java +++ b/src/java/org/apache/poi/ss/util/CellRangeAddress.java @@ -17,9 +17,9 @@ package org.apache.poi.ss.util; -import org.apache.poi.ss.formula.SheetNameFormatter; import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.SelectionRecord; +import org.apache.poi.ss.formula.SheetNameFormatter; import org.apache.poi.util.LittleEndianByteArrayOutputStream; import org.apache.poi.util.LittleEndianOutput; @@ -38,14 +38,26 @@ public class CellRangeAddress extends CellRangeAddressBase { */ public static final int ENCODED_SIZE = 8; + /** + * Creates new cell range. Indexes are zero-based. + * + * @param firstRow Index of first row + * @param lastRow Index of last row (inclusive), must be equal to or larger than {@code firstRow} + * @param firstCol Index of first column + * @param lastCol Index of last column (inclusive), must be equal to or larger than {@code firstCol} + */ public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) { super(firstRow, lastRow, firstCol, lastCol); + + if (lastRow < firstRow || lastCol < firstCol) + throw new IllegalArgumentException("lastRow < firstRow || lastCol < firstCol"); } /** * @deprecated use {@link #serialize(LittleEndianOutput)} */ - public int serialize(int offset, byte[] data) { + @Deprecated + public int serialize(int offset, byte[] data) { serialize(new LittleEndianByteArrayOutputStream(data, offset, ENCODED_SIZE)); return ENCODED_SIZE; } diff --git a/src/java/org/apache/poi/util/LittleEndianOutputStream.java b/src/java/org/apache/poi/util/LittleEndianOutputStream.java index c8bb9d4b78..3e0daa310d 100644 --- a/src/java/org/apache/poi/util/LittleEndianOutputStream.java +++ b/src/java/org/apache/poi/util/LittleEndianOutputStream.java @@ -30,7 +30,8 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement super(out); } - public void writeByte(int v) { + @Override + public void writeByte(int v) { try { out.write(v); } catch (IOException e) { @@ -38,11 +39,13 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement } } - public void writeDouble(double v) { + @Override + public void writeDouble(double v) { writeLong(Double.doubleToLongBits(v)); } - public void writeInt(int v) { + @Override + public void writeInt(int v) { int b3 = (v >>> 24) & 0xFF; int b2 = (v >>> 16) & 0xFF; int b1 = (v >>> 8) & 0xFF; @@ -57,12 +60,14 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement } } - public void writeLong(long v) { + @Override + public void writeLong(long v) { writeInt((int)(v >> 0)); writeInt((int)(v >> 32)); } - public void writeShort(int v) { + @Override + public void writeShort(int v) { int b1 = (v >>> 8) & 0xFF; int b0 = (v >>> 0) & 0xFF; try { @@ -72,7 +77,8 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement throw new RuntimeException(e); } } - public void write(byte[] b) { + @Override + public void write(byte[] b) { // suppress IOException for interface method try { super.write(b); @@ -80,7 +86,8 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement throw new RuntimeException(e); } } - public void write(byte[] b, int off, int len) { + @Override + public void write(byte[] b, int off, int len) { // suppress IOException for interface method try { super.write(b, off, len); diff --git a/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java b/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java index a2dff595c5..c5a163394c 100644 --- a/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java +++ b/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java @@ -18,63 +18,176 @@ limitations under the License. package org.apache.poi.ss.util; import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import junit.framework.TestCase; import org.apache.poi.hssf.record.TestcaseRecordInputStream; -import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.util.LittleEndianOutputStream; -import junit.framework.TestCase; - public final class TestCellRangeAddress extends TestCase { - byte[] data = new byte[] { - (byte)0x02,(byte)0x00, - (byte)0x04,(byte)0x00, - (byte)0x00,(byte)0x00, - (byte)0x03,(byte)0x00, - }; - - public void testLoad() { - CellRangeAddress ref = new CellRangeAddress( - TestcaseRecordInputStream.create(0x000, data) - ); - assertEquals(2, ref.getFirstRow()); - assertEquals(4, ref.getLastRow()); - assertEquals(0, ref.getFirstColumn()); - assertEquals(3, ref.getLastColumn()); - - assertEquals( 8, CellRangeAddress.ENCODED_SIZE ); - } - - public void testStore() - { - CellRangeAddress ref = new CellRangeAddress(0,0,0,0); - - byte[] recordBytes; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - LittleEndianOutputStream out = new LittleEndianOutputStream(baos); - - // With nothing set - ref.serialize(out); - recordBytes = baos.toByteArray(); - assertEquals(recordBytes.length, data.length); - for (int i = 0; i < data.length; i++) { - assertEquals("At offset " + i, 0, recordBytes[i]); + byte[] data = new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x04, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, }; + + public void testLoad() { + CellRangeAddress ref = new CellRangeAddress( + TestcaseRecordInputStream.create(0x000, data)); + assertEquals(2, ref.getFirstRow()); + assertEquals(4, ref.getLastRow()); + assertEquals(0, ref.getFirstColumn()); + assertEquals(3, ref.getLastColumn()); + + assertEquals(8, CellRangeAddress.ENCODED_SIZE); + } + + public void testLoadInvalid() { + try { + assertNotNull(new CellRangeAddress( + TestcaseRecordInputStream.create(0x000, new byte[] { (byte)0x02 }))); + } catch (RuntimeException e) { + assertTrue("Had: " + e, e.getMessage().contains("Ran out of data")); + } + } + + public void testStore() throws IOException { + CellRangeAddress ref = new CellRangeAddress(0, 0, 0, 0); + + byte[] recordBytes; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + LittleEndianOutputStream out = new LittleEndianOutputStream(baos); + try { + // With nothing set + ref.serialize(out); + recordBytes = baos.toByteArray(); + assertEquals(recordBytes.length, data.length); + for (int i = 0; i < data.length; i++) { + assertEquals("At offset " + i, 0, recordBytes[i]); + } + + // Now set the flags + ref.setFirstRow((short) 2); + ref.setLastRow((short) 4); + ref.setFirstColumn((short) 0); + ref.setLastColumn((short) 3); + + // Re-test + baos.reset(); + ref.serialize(out); + recordBytes = baos.toByteArray(); + + assertEquals(recordBytes.length, data.length); + for (int i = 0; i < data.length; i++) { + assertEquals("At offset " + i, data[i], recordBytes[i]); + } + } finally { + out.close(); + } + } + + @SuppressWarnings("deprecation") + public void testStoreDeprecated() throws IOException { + CellRangeAddress ref = new CellRangeAddress(0, 0, 0, 0); + + byte[] recordBytes = new byte[CellRangeAddress.ENCODED_SIZE]; + // With nothing set + ref.serialize(0, recordBytes); + assertEquals(recordBytes.length, data.length); + for (int i = 0; i < data.length; i++) { + assertEquals("At offset " + i, 0, recordBytes[i]); + } + + // Now set the flags + ref.setFirstRow((short) 2); + ref.setLastRow((short) 4); + ref.setFirstColumn((short) 0); + ref.setLastColumn((short) 3); + + // Re-test + ref.serialize(0, recordBytes); + + assertEquals(recordBytes.length, data.length); + for (int i = 0; i < data.length; i++) { + assertEquals("At offset " + i, data[i], recordBytes[i]); + } + } + + public void testCreateIllegal() throws IOException { + // for some combinations we expected exceptions + try { + assertNotNull(new CellRangeAddress(1, 0, 0, 0)); + fail("Expect to catch an exception"); + } catch (IllegalArgumentException e) { + // expected here + } + try { + assertNotNull(new CellRangeAddress(0, 0, 1, 0)); + fail("Expect to catch an exception"); + } catch (IllegalArgumentException e) { + // expected here + } + } + + public void testCopy() throws IOException { + CellRangeAddress ref = new CellRangeAddress(1, 2, 3, 4); + CellRangeAddress copy = ref.copy(); + assertEquals(ref.toString(), copy.toString()); + } + + public void testGetEncodedSize() throws IOException { + assertEquals(2*CellRangeAddress.ENCODED_SIZE, CellRangeAddress.getEncodedSize(2)); } - - // Now set the flags - ref.setFirstRow((short)2); - ref.setLastRow((short)4); - ref.setFirstColumn((short)0); - ref.setLastColumn((short)3); - - // Re-test - baos.reset(); - ref.serialize(out); - recordBytes = baos.toByteArray(); - - assertEquals(recordBytes.length, data.length); - for (int i = 0; i < data.length; i++) { - assertEquals("At offset " + i, data[i], recordBytes[i]); + + public void testFormatAsString() throws IOException { + CellRangeAddress ref = new CellRangeAddress(1, 2, 3, 4); + + assertEquals("D2:E3", ref.formatAsString()); + assertEquals("D2:E3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString()); + + assertEquals("sheet1!$D$2:$E$3", ref.formatAsString("sheet1", true)); + assertEquals("sheet1!$D$2:$E$3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", true)); + assertEquals("sheet1!$D$2:$E$3", CellRangeAddress.valueOf(ref.formatAsString("sheet1", true)).formatAsString("sheet1", true)); + + assertEquals("sheet1!D2:E3", ref.formatAsString("sheet1", false)); + assertEquals("sheet1!D2:E3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", false)); + assertEquals("sheet1!D2:E3", CellRangeAddress.valueOf(ref.formatAsString("sheet1", false)).formatAsString("sheet1", false)); + + assertEquals("D2:E3", ref.formatAsString(null, false)); + assertEquals("D2:E3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString(null, false)); + assertEquals("D2:E3", CellRangeAddress.valueOf(ref.formatAsString(null, false)).formatAsString(null, false)); + + assertEquals("$D$2:$E$3", ref.formatAsString(null, true)); + assertEquals("$D$2:$E$3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString(null, true)); + assertEquals("$D$2:$E$3", CellRangeAddress.valueOf(ref.formatAsString(null, true)).formatAsString(null, true)); + + ref = new CellRangeAddress(-1, -1, 3, 4); + assertEquals("D:E", ref.formatAsString()); + assertEquals("sheet1!$D:$E", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", true)); + assertEquals("sheet1!$D:$E", CellRangeAddress.valueOf(ref.formatAsString("sheet1", true)).formatAsString("sheet1", true)); + assertEquals("$D:$E", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString(null, true)); + assertEquals("$D:$E", CellRangeAddress.valueOf(ref.formatAsString(null, true)).formatAsString(null, true)); + assertEquals("sheet1!D:E", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", false)); + assertEquals("sheet1!D:E", CellRangeAddress.valueOf(ref.formatAsString("sheet1", false)).formatAsString("sheet1", false)); + + ref = new CellRangeAddress(1, 2, -1, -1); + assertEquals("2:3", ref.formatAsString()); + assertEquals("sheet1!$2:$3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", true)); + assertEquals("sheet1!$2:$3", CellRangeAddress.valueOf(ref.formatAsString("sheet1", true)).formatAsString("sheet1", true)); + assertEquals("$2:$3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString(null, true)); + assertEquals("$2:$3", CellRangeAddress.valueOf(ref.formatAsString(null, true)).formatAsString(null, true)); + assertEquals("sheet1!2:3", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", false)); + assertEquals("sheet1!2:3", CellRangeAddress.valueOf(ref.formatAsString("sheet1", false)).formatAsString("sheet1", false)); + + ref = new CellRangeAddress(1, 1, 2, 2); + assertEquals("C2", ref.formatAsString()); + assertEquals("sheet1!$C$2", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", true)); + assertEquals("sheet1!$C$2", CellRangeAddress.valueOf(ref.formatAsString("sheet1", true)).formatAsString("sheet1", true)); + assertEquals("$C$2", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString(null, true)); + assertEquals("$C$2", CellRangeAddress.valueOf(ref.formatAsString(null, true)).formatAsString(null, true)); + assertEquals("sheet1!C2", CellRangeAddress.valueOf(ref.formatAsString()).formatAsString("sheet1", false)); + assertEquals("sheet1!C2", CellRangeAddress.valueOf(ref.formatAsString("sheet1", false)).formatAsString("sheet1", false)); + + // is this a valid address? + ref = new CellRangeAddress(-1, -1, -1, -1); + assertEquals(":", ref.formatAsString()); } - } }