From 39c271cde0d37d7805fbbbe8489cf8e690b1e0ac Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Thu, 14 Jun 2012 15:36:34 +0000 Subject: fixed EscherAggregate to correctly write records with trailing solver container git-svn-id: https://svn.apache.org/repos/asf/poi/branches/gsoc2012@1350298 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/model/TestDrawingAggregate.java | 235 +++++++++++++++++---- 1 file changed, 192 insertions(+), 43 deletions(-) (limited to 'src/testcases/org/apache/poi/hssf/model') diff --git a/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java b/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java index 0608c0eb18..baa5f66c08 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java +++ b/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java @@ -19,16 +19,35 @@ package org.apache.poi.hssf.model; import junit.framework.TestCase; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherDggRecord; -import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.record.ContinueRecord; +import org.apache.poi.hssf.record.DrawingRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.EscherAggregate; +import org.apache.poi.hssf.record.NoteRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; +import org.apache.poi.hssf.record.RecordFactory; +import org.apache.poi.hssf.record.TextObjectRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; -import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.hssf.usermodel.HSSFPatriarch; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFTestHelper; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.util.HexRead; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author Yegor Kozlov @@ -36,52 +55,158 @@ import java.util.List; */ public class TestDrawingAggregate extends TestCase { - private int spgrCount = 0; - private int spCount = 0; - private int shapeCount = 0; - private int shGroupCount = 0; - /* - * EscherAggregate must have for each SpgrContainer HSSFShapeGroup and for each SpContainer HSSFShape + /** + * information about drawing aggregate in a worksheet */ - private void checkEscherAndShapesCount(EscherAggregate agg, HSSFSheet sheet) { - /* - HSSFPatriarch patriarch = HSSFTestHelper.createTestPatriarch(sheet, agg); - agg.setPatriarch(patriarch); - EscherAggregate.createShapeTree(EscherAggregate.getMainSpgrContainer(agg), agg.getPatriarch(), agg); - EscherContainerRecord mainContainer = EscherAggregate.getMainSpgrContainer(agg); - calculateShapesCount(agg.getPatriarch()); - calculateEscherContainersCount(mainContainer); - - assertEquals(spgrCount, shGroupCount); - assertEquals(spCount - spgrCount - 1, shapeCount); - */ + private static class DrawingAggregateInfo { + /** + * start and end indices of the aggregate in the worksheet stream + */ + private int startRecordIndex, endRecordIndex; + /** + * the records being aggregated + */ + private List aggRecords; + + /** + * @return aggregate info or null if the sheet does not contain drawing objects + */ + static DrawingAggregateInfo get(HSSFSheet sheet){ + DrawingAggregateInfo info = null; + InternalSheet isheet = HSSFTestHelper.getSheetForTest(sheet); + List records = isheet.getRecords(); + for(int i = 0; i < records.size(); i++){ + RecordBase rb = records.get(i); + if((rb instanceof DrawingRecord) && info == null) { + info = new DrawingAggregateInfo(); + info.startRecordIndex = i; + info.endRecordIndex = i; + } else if (info != null && ( + rb instanceof DrawingRecord + || rb instanceof ObjRecord + || rb instanceof TextObjectRecord + || rb instanceof ContinueRecord + || rb instanceof NoteRecord + )){ + info.endRecordIndex = i; + } else { + if(rb instanceof EscherAggregate) + throw new IllegalStateException("Drawing data already aggregated. " + + "You should cal this method before the first invocation of HSSFSheet#getDrawingPatriarch()"); + if (info != null) break; + } + } + if(info != null){ + info.aggRecords = new ArrayList( + records.subList(info.startRecordIndex, info.endRecordIndex + 1)); + } + return info; + } + + /** + * @return the raw data being aggregated + */ + byte[] getRawBytes(){ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (RecordBase rb : aggRecords) { + Record r = (Record) rb; + try { + out.write(r.serialize()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return out.toByteArray(); + } } - private void calculateEscherContainersCount(EscherContainerRecord spgr) { - for (EscherRecord record : spgr.getChildRecords()) { - if (EscherContainerRecord.SP_CONTAINER == record.getRecordId()) { - spCount++; - continue; + /** + * iterate over all sheets, aggregate drawing records (if there are any) + * and remember information about the aggregated data. + * Then serialize the workbook, read back and assert that the aggregated data is preserved. + * + * The assertion is strict meaning that the drawing data before and after save must be equal. + */ + private static void assertWriteAndReadBack(HSSFWorkbook wb){ + // map aggregate info by sheet index + Map aggs = new HashMap(); + for(int i = 0; i < wb.getNumberOfSheets(); i++){ + HSSFSheet sheet = wb.getSheetAt(i); + DrawingAggregateInfo info = DrawingAggregateInfo.get(sheet); + if(info != null) { + aggs.put(i, info); + HSSFPatriarch p = sheet.getDrawingPatriarch(); + + // compare aggregate.serialize() with raw bytes from the record stream + EscherAggregate agg = HSSFTestHelper.getEscherAggregate(p); + + byte[] dgBytes1 = info.getRawBytes(); + byte[] dgBytes2 = agg.serialize(); + + assertEquals("different size of raw data ande aggregate.serialize()", dgBytes1.length, dgBytes2.length); + assertTrue("raw drawing data ("+dgBytes1.length+" bytes) and aggregate.serialize() are different.", + Arrays.equals(dgBytes1, dgBytes2)); } - if (EscherContainerRecord.SPGR_CONTAINER == record.getRecordId()) { - spgrCount++; - calculateEscherContainersCount((EscherContainerRecord) record); + } + + if(aggs.size() != 0){ + HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); + for(int i = 0; i < wb2.getNumberOfSheets(); i++){ + DrawingAggregateInfo info1 = aggs.get(i); + if(info1 != null) { + HSSFSheet sheet2 = wb2.getSheetAt(i); + DrawingAggregateInfo info2 = DrawingAggregateInfo.get(sheet2); + byte[] dgBytes1 = info1.getRawBytes(); + byte[] dgBytes2 = info2.getRawBytes(); + assertEquals("different size of drawing data before and after save", dgBytes1.length, dgBytes2.length); + assertTrue("drawing data ("+dgBytes1.length+" bytes) before and after save is different.", + Arrays.equals(dgBytes1, dgBytes2)); + } } } } - private void calculateShapesCount(HSSFShapeContainer group) { - for (HSSFShape shape : (List) group.getChildren()) { - if (shape instanceof HSSFShapeGroup) { - shGroupCount++; - calculateShapesCount((HSSFShapeGroup) shape); - } else { - shapeCount++; + /** + * test that we correctly read and write drawing aggregates + * in all .xls files in POI test samples + */ + public void testAllTestSamples(){ + File[] xls = new File(System.getProperty("POI.testdata.path"), "spreadsheet").listFiles( + new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".xls"); + } + } + ); + for(File file : xls) { + HSSFWorkbook wb; + try { + wb = HSSFTestDataSamples.openSampleWorkbook(file.getName()); + } catch (Throwable e){ + // don't bother about files we cannot read - they are different bugs + // System.out.println("[WARN] Cannot read " + file.getName()); + continue; + } + try { + assertWriteAndReadBack(wb); + } catch (Throwable e){ + //e.printStackTrace(); + System.err.println("[ERROR] assertion failed for " + file.getName() + ": " + e.getMessage()); } } } + /** + * TODO: figure out why it fails with "RecordFormatException: 0 bytes written but getRecordSize() reports 80" + */ + public void testFailing(){ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("15573.xls"); + HSSFSheet sh = wb.getSheetAt(0); + sh.getDrawingPatriarch(); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + } private static byte[] toByteArray(List records) { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -96,10 +221,14 @@ public class TestDrawingAggregate extends TestCase { return out.toByteArray(); } - public void testSolverContainerMustBeSavedDuringSerialization(){ + public void testSolverContainerMustBeSavedDuringSerialization() throws IOException{ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SolverContainerAfterSPGR.xls"); HSSFSheet sh = wb.getSheetAt(0); InternalSheet ish = HSSFTestHelper.getSheetForTest(sh); + List records = ish.getRecords(); + // records to be aggregated + List dgRecords = records.subList(19, 22); + byte[] dgBytes = toByteArray(dgRecords); sh.getDrawingPatriarch(); EscherAggregate agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid); assertEquals(agg.getEscherRecords().get(0).getChildRecords().size(), 3); @@ -112,6 +241,29 @@ public class TestDrawingAggregate extends TestCase { assertEquals(agg.getEscherRecords().get(0).getChildRecords().size(), 3); assertEquals(agg.getEscherRecords().get(0).getChild(2).getRecordId(), EscherContainerRecord.SOLVER_CONTAINER); + + // collect drawing records into a byte buffer. + agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid); + byte[] dgBytesAfterSave = agg.serialize(); + assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); + assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); + } + + public void testFileWithTextbox() throws IOException{ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("text.xls"); + HSSFSheet sh = wb.getSheetAt(0); + InternalSheet ish = HSSFTestHelper.getSheetForTest(sh); + List records = ish.getRecords(); + // records to be aggregated + List dgRecords = records.subList(19, 23); + byte[] dgBytes = toByteArray(dgRecords); + sh.getDrawingPatriarch(); + + // collect drawing records into a byte buffer. + EscherAggregate agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid); + byte[] dgBytesAfterSave = agg.serialize(); + assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); + assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); } /** @@ -172,8 +324,7 @@ public class TestDrawingAggregate extends TestCase { byte[] dgBytesAfterSave = agg.serialize(); assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); - assertTrue("drawing data brefpore and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); - checkEscherAndShapesCount(agg, sh); + assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); } /** @@ -241,7 +392,6 @@ public class TestDrawingAggregate extends TestCase { assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); - checkEscherAndShapesCount(agg, sh); } @@ -323,7 +473,6 @@ public class TestDrawingAggregate extends TestCase { byte[] dgBytesAfterSave = agg.serialize(); assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); - checkEscherAndShapesCount(agg, sh); } @@ -382,7 +531,6 @@ public class TestDrawingAggregate extends TestCase { byte[] dgBytesAfterSave = agg.serialize(); assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); - checkEscherAndShapesCount(agg, sh); } public void testUnhandledContinue() { @@ -2051,4 +2199,5 @@ public class TestDrawingAggregate extends TestCase { assertEquals("different size of drawing data before and after save", dgBytes.length, dgBytesAfterSave.length); assertTrue("drawing data brefpore and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); } + } -- cgit v1.2.3