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
This commit is contained in:
Dominik Stadler 2014-10-30 05:53:06 +00:00
parent 3b341743de
commit c3753981dd
3 changed files with 192 additions and 60 deletions

View File

@ -17,9 +17,9 @@
package org.apache.poi.ss.util; 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.RecordInputStream;
import org.apache.poi.hssf.record.SelectionRecord; 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.LittleEndianByteArrayOutputStream;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
@ -38,13 +38,25 @@ public class CellRangeAddress extends CellRangeAddressBase {
*/ */
public static final int ENCODED_SIZE = 8; 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) { public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {
super(firstRow, lastRow, firstCol, lastCol); super(firstRow, lastRow, firstCol, lastCol);
if (lastRow < firstRow || lastCol < firstCol)
throw new IllegalArgumentException("lastRow < firstRow || lastCol < firstCol");
} }
/** /**
* @deprecated use {@link #serialize(LittleEndianOutput)} * @deprecated use {@link #serialize(LittleEndianOutput)}
*/ */
@Deprecated
public int serialize(int offset, byte[] data) { public int serialize(int offset, byte[] data) {
serialize(new LittleEndianByteArrayOutputStream(data, offset, ENCODED_SIZE)); serialize(new LittleEndianByteArrayOutputStream(data, offset, ENCODED_SIZE));
return ENCODED_SIZE; return ENCODED_SIZE;

View File

@ -30,6 +30,7 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement
super(out); super(out);
} }
@Override
public void writeByte(int v) { public void writeByte(int v) {
try { try {
out.write(v); out.write(v);
@ -38,10 +39,12 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement
} }
} }
@Override
public void writeDouble(double v) { public void writeDouble(double v) {
writeLong(Double.doubleToLongBits(v)); writeLong(Double.doubleToLongBits(v));
} }
@Override
public void writeInt(int v) { public void writeInt(int v) {
int b3 = (v >>> 24) & 0xFF; int b3 = (v >>> 24) & 0xFF;
int b2 = (v >>> 16) & 0xFF; int b2 = (v >>> 16) & 0xFF;
@ -57,11 +60,13 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement
} }
} }
@Override
public void writeLong(long v) { public void writeLong(long v) {
writeInt((int)(v >> 0)); writeInt((int)(v >> 0));
writeInt((int)(v >> 32)); writeInt((int)(v >> 32));
} }
@Override
public void writeShort(int v) { public void writeShort(int v) {
int b1 = (v >>> 8) & 0xFF; int b1 = (v >>> 8) & 0xFF;
int b0 = (v >>> 0) & 0xFF; int b0 = (v >>> 0) & 0xFF;
@ -72,6 +77,7 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override
public void write(byte[] b) { public void write(byte[] b) {
// suppress IOException for interface method // suppress IOException for interface method
try { try {
@ -80,6 +86,7 @@ public final class LittleEndianOutputStream extends FilterOutputStream implement
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override
public void write(byte[] b, int off, int len) { public void write(byte[] b, int off, int len) {
// suppress IOException for interface method // suppress IOException for interface method
try { try {

View File

@ -18,41 +18,44 @@ limitations under the License.
package org.apache.poi.ss.util; package org.apache.poi.ss.util;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.LittleEndianOutputStream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.util.LittleEndianOutputStream;
public final class TestCellRangeAddress extends TestCase { public final class TestCellRangeAddress extends TestCase {
byte[] data = new byte[] { byte[] data = new byte[] { (byte) 0x02, (byte) 0x00, (byte) 0x04,
(byte)0x02,(byte)0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, };
(byte)0x04,(byte)0x00,
(byte)0x00,(byte)0x00,
(byte)0x03,(byte)0x00,
};
public void testLoad() { public void testLoad() {
CellRangeAddress ref = new CellRangeAddress( CellRangeAddress ref = new CellRangeAddress(
TestcaseRecordInputStream.create(0x000, data) TestcaseRecordInputStream.create(0x000, data));
);
assertEquals(2, ref.getFirstRow()); assertEquals(2, ref.getFirstRow());
assertEquals(4, ref.getLastRow()); assertEquals(4, ref.getLastRow());
assertEquals(0, ref.getFirstColumn()); assertEquals(0, ref.getFirstColumn());
assertEquals(3, ref.getLastColumn()); assertEquals(3, ref.getLastColumn());
assertEquals( 8, CellRangeAddress.ENCODED_SIZE ); assertEquals(8, CellRangeAddress.ENCODED_SIZE);
} }
public void testStore() public void testLoadInvalid() {
{ try {
CellRangeAddress ref = new CellRangeAddress(0,0,0,0); 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; byte[] recordBytes;
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
LittleEndianOutputStream out = new LittleEndianOutputStream(baos); LittleEndianOutputStream out = new LittleEndianOutputStream(baos);
try {
// With nothing set // With nothing set
ref.serialize(out); ref.serialize(out);
recordBytes = baos.toByteArray(); recordBytes = baos.toByteArray();
@ -62,10 +65,10 @@ public final class TestCellRangeAddress extends TestCase {
} }
// Now set the flags // Now set the flags
ref.setFirstRow((short)2); ref.setFirstRow((short) 2);
ref.setLastRow((short)4); ref.setLastRow((short) 4);
ref.setFirstColumn((short)0); ref.setFirstColumn((short) 0);
ref.setLastColumn((short)3); ref.setLastColumn((short) 3);
// Re-test // Re-test
baos.reset(); baos.reset();
@ -76,5 +79,115 @@ public final class TestCellRangeAddress extends TestCase {
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
assertEquals("At offset " + i, data[i], recordBytes[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));
}
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());
} }
} }