diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java index 3bb712735..1d48ff751 100644 --- a/src/java/org/apache/poi/hssf/record/EscherAggregate.java +++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java @@ -43,16 +43,7 @@ import org.apache.poi.hssf.model.CommentShape; import org.apache.poi.hssf.model.ConvertAnchor; import org.apache.poi.hssf.model.DrawingManager2; import org.apache.poi.hssf.model.TextboxShape; -import org.apache.poi.hssf.usermodel.HSSFAnchor; -import org.apache.poi.hssf.usermodel.HSSFChildAnchor; -import org.apache.poi.hssf.usermodel.HSSFClientAnchor; -import org.apache.poi.hssf.usermodel.HSSFPatriarch; -import org.apache.poi.hssf.usermodel.HSSFPicture; -import org.apache.poi.hssf.usermodel.HSSFShape; -import org.apache.poi.hssf.usermodel.HSSFShapeContainer; -import org.apache.poi.hssf.usermodel.HSSFShapeGroup; -import org.apache.poi.hssf.usermodel.HSSFSimpleShape; -import org.apache.poi.hssf.usermodel.HSSFTextbox; +import org.apache.poi.hssf.usermodel.*; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -322,7 +313,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { /** * list of "tail" records that need to be serialized after all drawing group records */ - private List tailRec = new ArrayList(); + private List tailRec = new ArrayList(); public EscherAggregate(DrawingManager2 drawingManager) { this.drawingManager = drawingManager; @@ -413,7 +404,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { } // Decode the shapes - // agg.escherRecords = new ArrayList(); + // agg.escherRecords = new ArrayList(); int pos = 0; while (pos < buffer.size()) { EscherRecord r = recordFactory.createRecord(buffer.toByteArray(), pos); @@ -486,7 +477,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { public void afterRecordSerialize(int offset, short recordId, int size, EscherRecord record) { if (recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID) { - spEndingOffsets.add(Integer.valueOf(offset)); + spEndingOffsets.add(offset); shapes.add(record); } } @@ -501,12 +492,17 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { pos = offset; int writtenEscherBytes = 0; for (int i = 1; i < shapes.size(); i++) { - int endOffset = ((Integer) spEndingOffsets.get(i)).intValue() - 1; + int endOffset; + if (i == shapes.size()-1){ + endOffset = buffer.length - 1; + } else { + endOffset = (Integer) spEndingOffsets.get(i) - 1; + } int startOffset; if (i == 1) startOffset = 0; else - startOffset = ((Integer) spEndingOffsets.get(i - 1)).intValue(); + startOffset = (Integer) spEndingOffsets.get(i - 1); byte[] drawingData = new byte[endOffset - startOffset + 1]; @@ -790,7 +786,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { container.getChildren().add(shape); } - private static HSSFClientAnchor toClientAnchor(EscherClientAnchorRecord anchorRecord) { + public static HSSFClientAnchor toClientAnchor(EscherClientAnchorRecord anchorRecord) { HSSFClientAnchor anchor = new HSSFClientAnchor(); anchor.setAnchorType(anchorRecord.getFlag()); anchor.setCol1(anchorRecord.getCol1()); @@ -804,7 +800,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { return anchor; } - private static HSSFChildAnchor toChildAnchor(EscherChildAnchorRecord anchorRecord) { + public static HSSFChildAnchor toChildAnchor(EscherChildAnchorRecord anchorRecord) { HSSFChildAnchor anchor = new HSSFChildAnchor(); // anchor.setAnchorType(anchorRecord.getFlag()); // anchor.setCol1( anchorRecord.getCol1() ); @@ -1081,4 +1077,24 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { return null; } + /** + * Returns the mapping of {@link EscherClientDataRecord} and {@link EscherTextboxRecord} + * to their {@link TextObjectRecord} or {@link ObjRecord} . + * + * We need to access it outside of EscherAggregate when building shapes + * + * @return + */ + public Map getShapeToObjMapping(){ + return Collections.unmodifiableMap(shapeToObj); + } + + /** + * + * @return tails records. We need to access them when building shapes. + * Every HSSFComment shape has a link to a NoteRecord from the tailRec collection. + */ + public List getTailRecords(){ + return Collections.unmodifiableList(tailRec); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java index 513ac619f..ccd0e620c 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java @@ -18,7 +18,16 @@ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherChildAnchorRecord; + public final class HSSFChildAnchor extends HSSFAnchor { + + private EscherChildAnchorRecord escherChildAnchorRecord; + + public HSSFChildAnchor(EscherChildAnchorRecord escherChildAnchorRecord) { + this.escherChildAnchorRecord = escherChildAnchorRecord; + } + public HSSFChildAnchor() { } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java index cffb00cd5..a040144c2 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherClientAnchorRecord; import org.apache.poi.ss.usermodel.ClientAnchor; @@ -33,6 +34,13 @@ public final class HSSFClientAnchor extends HSSFAnchor implements ClientAnchor { int row2; int anchorType; + private EscherClientAnchorRecord escherClientAnchorRecord; + + public HSSFClientAnchor(EscherClientAnchorRecord escherClientAnchorRecord) { + this.escherClientAnchorRecord = escherClientAnchorRecord; + //TODO set properties or read properties from EscherRecord ? + } + /** * Creates a new client anchor and defaults all the anchor positions to 0. */ diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index 672f82cc7..71549bbf3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -22,9 +22,11 @@ import java.util.Iterator; import java.util.List; import org.apache.poi.ddf.EscherComplexProperty; +import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherOptRecord; import org.apache.poi.ddf.EscherProperty; import org.apache.poi.ddf.EscherBSERecord; +import org.apache.poi.ddf.EscherSpgrRecord; import org.apache.poi.hssf.record.EscherAggregate; import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.util.StringUtil; @@ -314,4 +316,23 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { throw new RuntimeException("NotImplemented"); } + + void buildShapeTree(){ + EscherContainerRecord dgContainer = _boundAggregate.getEscherContainer(); + EscherContainerRecord spgrConrainer = dgContainer.getChildContainers().get(0); + List spgrChildren = spgrConrainer.getChildContainers(); + + for(int i = 0; i < spgrChildren.size(); i++){ + EscherContainerRecord spContainer = spgrChildren.get(i); + if (i == 0){ + EscherSpgrRecord spgr = (EscherSpgrRecord)spContainer.getChildById(EscherSpgrRecord.RECORD_ID); + setCoordinates( + spgr.getRectX1(), spgr.getRectY1(), + spgr.getRectX2(), spgr.getRectY2() + ); + } else { + HSSFShapeFactory.createShapeTree(spContainer, _boundAggregate, this); + } + } + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRectangle.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRectangle.java new file mode 100644 index 000000000..0a0468c14 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRectangle.java @@ -0,0 +1,15 @@ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.hssf.record.ObjRecord; + +/** + * @author Evgeniy Berlog + * @date 08.06.12 + */ +public class HSSFRectangle extends HSSFShape{ + + public HSSFRectangle(EscherContainerRecord spContainer, ObjRecord objRecord) { + super(spContainer, objRecord); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java index 3c586340f..7ffdfdb9b 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java @@ -17,6 +17,9 @@ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.hssf.record.ObjRecord; + /** * An abstract shape. * @@ -40,7 +43,7 @@ public abstract class HSSFShape { public static final int LINESTYLE_NONE = -1; // TODO - make all these fields private - final HSSFShape parent; + HSSFShape parent; HSSFAnchor anchor; HSSFPatriarch _patriarch; private int _lineStyleColor = 0x08000040; @@ -49,15 +52,30 @@ public abstract class HSSFShape { private int _lineStyle = LINESTYLE_SOLID; private boolean _noFill = false; + private EscherContainerRecord spContainer; + private ObjRecord objRecord; + + public HSSFShape(EscherContainerRecord spContainer, ObjRecord objRecord){ + this.spContainer = spContainer; + this.objRecord = objRecord; + } /** * Create a new shape with the specified parent and anchor. */ - HSSFShape( HSSFShape parent, HSSFAnchor anchor ) + public HSSFShape( HSSFShape parent, HSSFAnchor anchor ) { this.parent = parent; this.anchor = anchor; } + public EscherContainerRecord getSpContainer() { + return spContainer; + } + + public ObjRecord getObjRecord() { + return objRecord; + } + /** * Gets the parent shape. */ diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java index 99e6a5de6..181b3d17a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java @@ -31,4 +31,15 @@ public interface HSSFShapeContainer */ List getChildren(); + /** + * add shape to the list of child records + * @param shape + */ + public void addShape(HSSFShape shape); + + /** + * set coordinates of this group relative to the parent + */ + void setCoordinates( int x1, int y1, int x2, int y2 ); + } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java new file mode 100644 index 000000000..fc88a2a16 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java @@ -0,0 +1,157 @@ +/* + * 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.hssf.usermodel; + +import org.apache.poi.ddf.EscherClientDataRecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherSpRecord; +import org.apache.poi.ddf.EscherSpgrRecord; +import org.apache.poi.ddf.EscherTextboxRecord; +import org.apache.poi.hssf.model.TextboxShape; +import org.apache.poi.hssf.record.CommonObjectDataSubRecord; +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.TextObjectRecord; +import org.apache.poi.hssf.usermodel.drawing.HSSFShapeType; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author evgeniy + * date: 05.06.12 + */ +public class HSSFShapeFactory { + + private static final Map shapeTypeToClass = new HashMap(HSSFShapeType.values().length); + private static final ReflectionConstructorShapeCreator shapeCreator = new ReflectionConstructorShapeCreator(shapeTypeToClass); + + static { + for (HSSFShapeType type: HSSFShapeType.values()){ + shapeTypeToClass.put(type.getType(), type.getShape()); + } + } + + private static class ReflectionConstructorShapeCreator { + + private final Map shapeTypeToClass; + + private ReflectionConstructorShapeCreator(Map shapeTypeToClass) { + this.shapeTypeToClass = shapeTypeToClass; + } + + public HSSFShape createNewShape(Short type, EscherContainerRecord spContainer, ObjRecord objRecord){ + if (!shapeTypeToClass.containsKey(type)){ + return new HSSFUnknownShape(spContainer, objRecord); + } + Class clazz = shapeTypeToClass.get(type); + if (null == clazz){ + System.out.println("No class attached to shape type: "+type); + return new HSSFUnknownShape(spContainer, objRecord); + } + try{ + Constructor constructor = clazz.getConstructor(new Class[]{EscherContainerRecord.class, ObjRecord.class}); + return (HSSFShape) constructor.newInstance(spContainer, objRecord); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(clazz.getName() +" doesn't have required for shapes constructor"); + } catch (Exception e) { + throw new IllegalStateException("Couldn't create new instance of " + clazz.getName()); + } + } + } + + public static HSSFShape createShape(EscherRecord container, ObjRecord objRecord){ + if (0 == container.getChildRecords().size()){ + throw new IllegalArgumentException("Couldn't create shape from empty escher container"); + } + if (container.getChild(0) instanceof EscherSpgrRecord){ + return new HSSFShapeGroup((EscherContainerRecord) container, objRecord); + } + + //TODO implement cases for all shapes + return new HSSFUnknownShape(container, objRecord); + } + + public static HSSFShapeGroup createShapeGroup(){ + return null; + } + + public static HSSFShapeGroup createSimpleShape(EscherRecord container, ObjRecord objRecord){ + return null; + } + + public static void createShapeTree(EscherContainerRecord container, EscherAggregate agg, HSSFShapeContainer out){ + if(container.getRecordId() == EscherContainerRecord.SPGR_CONTAINER){ + HSSFShapeGroup group = new HSSFShapeGroup(container, + null /* shape containers don't have a associated Obj record*/); + List children = container.getChildContainers(); + // skip the first child record, it is group descriptor + for(int i = 0; i < children.size(); i++) { + EscherContainerRecord spContainer = children.get(i); + if(i == 0){ + EscherSpgrRecord spgr = (EscherSpgrRecord)spContainer.getChildById(EscherSpgrRecord.RECORD_ID); + group.setCoordinates( + spgr.getRectX1(), spgr.getRectY1(), + spgr.getRectX2(), spgr.getRectY2() + ); + } else { + createShapeTree(spContainer, agg, group); + } + } + out.addShape(group); + } else if (container.getRecordId() == EscherContainerRecord.SP_CONTAINER){ + Map shapeToObj = agg.getShapeToObjMapping(); + EscherSpRecord spRecord = null; + ObjRecord objRecord = null; + TextObjectRecord txtRecord = null; + + for(EscherRecord record : container.getChildRecords()) { + switch(record.getRecordId()) { + case EscherSpRecord.RECORD_ID: + spRecord = (EscherSpRecord)record; + break; + case EscherClientDataRecord.RECORD_ID: + objRecord = (ObjRecord)shapeToObj.get(record); + break; + case EscherTextboxRecord.RECORD_ID: + txtRecord = (TextObjectRecord)shapeToObj.get(record); + break; + } + } + if (null != objRecord){ + HSSFShape shape = shapeCreator.createNewShape(spRecord.getShapeType(), container, objRecord); + out.addShape(shape); + } + if (null != txtRecord){ + //TODO resolve textbox +// TextboxShape shape = new TextboxShape(container, txtRecord); +// out.a + } +// +// //TODO decide what shape to create based on ObjRecord / EscherSpRecord +// HSSFShape shape = new HSSFUnknownShape(container, objRecord); +// out.addShape(shape); + } + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java index 905dad3fc..7cab3e493 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java @@ -17,6 +17,15 @@ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherChildAnchorRecord; +import org.apache.poi.ddf.EscherClientAnchorRecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherSpgrRecord; +import org.apache.poi.hssf.model.TextboxShape; +import org.apache.poi.hssf.record.EscherAggregate; +import org.apache.poi.hssf.record.ObjRecord; + import java.util.ArrayList; import java.util.List; import java.util.Iterator; @@ -37,6 +46,32 @@ public class HSSFShapeGroup int x2 = 1023; int y2 = 255; + public HSSFShapeGroup(EscherContainerRecord spgrContainer, ObjRecord objRecord) { + super(spgrContainer, objRecord); + + // read internal and external coordinates from spgrContainer + EscherContainerRecord spContainer = spgrContainer.getChildContainers().get(0); + for(EscherRecord ch : spContainer.getChildRecords()){ + switch(ch.getRecordId()) { + case EscherSpgrRecord.RECORD_ID: + EscherSpgrRecord spgr = (EscherSpgrRecord)ch; + setCoordinates( + spgr.getRectX1(), spgr.getRectY1(), + spgr.getRectX2(), spgr.getRectY2() + ); + break; + case EscherClientAnchorRecord.RECORD_ID: + this.anchor = EscherAggregate.toClientAnchor((EscherClientAnchorRecord)ch); + // TODO anchor = new HSSFClientAnchor((EscherChildAnchorRecord)ch); + break; + case EscherChildAnchorRecord.RECORD_ID: + this.anchor = EscherAggregate.toChildAnchor((EscherChildAnchorRecord)ch); + // TODO anchor = new HSSFChildAnchor((EscherClientAnchorRecord)ch); + break; + } + } + + } public HSSFShapeGroup( HSSFShape parent, HSSFAnchor anchor ) { @@ -61,6 +96,11 @@ public class HSSFShapeGroup shapes.add(shape); } + public void addTextBox(TextboxShape textboxShape){ +// HSSFTextbox shape = new HSSFTextbox(this, textboxShape.geanchor); +// shapes.add(textboxShape); + } + /** * Create a new simple shape under this group. * @param anchor the position of the shape. diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index fe332eb83..da7a404b2 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -1738,12 +1738,16 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { if(agg == null) return null; _patriarch = new HSSFPatriarch(this, agg); - agg.setPatriarch(_patriarch); + _patriarch.buildShapeTree(); + + //HSSFShapeFactory.createShapeTree(); + //agg.setPatriarch(_patriarch); + //EscherAggregate.createShapeTree(EscherAggregate.getMainSpgrContainer(agg), agg.getPatriarch(), agg); // Have it process the records into high level objects // as best it can do (this step may eat anything // that isn't supported, you were warned...) - agg.convertRecordsToUserModel(); +// agg.convertRecordsToUserModel(); // Return what we could cope with return _patriarch; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java new file mode 100644 index 000000000..d4cac9be9 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java @@ -0,0 +1,33 @@ +/* + * 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.hssf.usermodel; + +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.hssf.record.ObjRecord; + +/** + * @author Evgeniy Berlog + * date: 05.06.12 + */ +public class HSSFUnknownShape extends HSSFShape { + + public HSSFUnknownShape(EscherRecord spContainer, ObjRecord objRecord) { + super((EscherContainerRecord) spContainer, objRecord); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/drawing/HSSFShapeType.java b/src/java/org/apache/poi/hssf/usermodel/drawing/HSSFShapeType.java new file mode 100644 index 000000000..281868792 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/drawing/HSSFShapeType.java @@ -0,0 +1,29 @@ +package org.apache.poi.hssf.usermodel.drawing; + +import org.apache.poi.hssf.usermodel.HSSFRectangle; + +/** + * @author Evgeniy Berlog + * date: 08.06.12 + */ +public enum HSSFShapeType { + NOT_PRIMITIVE(0x0, null), + RECTANGLE(0x1, HSSFRectangle.class), + ROUND_RECTANGLE(0x2, null); + + private Short type; + private Class shape; + + HSSFShapeType(Integer type, Class shape) { + this.type = type.shortValue(); + this.shape = shape; + } + + public Short getType() { + return type; + } + + public Class getShape() { + return shape; + } +} diff --git a/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java b/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java index a22ce4a0c..0608c0eb1 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java +++ b/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java @@ -17,13 +17,13 @@ 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.aggregates.RowRecordsAggregate; -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.hssf.usermodel.*; import org.apache.poi.util.HexRead; import java.io.*; @@ -35,18 +35,84 @@ import java.util.List; * @author Evgeniy Berlog */ public class TestDrawingAggregate extends TestCase { - private static byte[] toByteArray(List records){ - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for(RecordBase rb : records) { - Record r = (Record)rb; - try { - out.write(r.serialize()); - } catch (IOException e){ - throw new RuntimeException(e); - } - } - return out.toByteArray(); + + 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 + */ + 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 void calculateEscherContainersCount(EscherContainerRecord spgr) { + for (EscherRecord record : spgr.getChildRecords()) { + if (EscherContainerRecord.SP_CONTAINER == record.getRecordId()) { + spCount++; + continue; + } + if (EscherContainerRecord.SPGR_CONTAINER == record.getRecordId()) { + spgrCount++; + calculateEscherContainersCount((EscherContainerRecord) record); + } } + } + + private void calculateShapesCount(HSSFShapeContainer group) { + for (HSSFShape shape : (List) group.getChildren()) { + if (shape instanceof HSSFShapeGroup) { + shGroupCount++; + calculateShapesCount((HSSFShapeGroup) shape); + } else { + shapeCount++; + } + } + } + + + private static byte[] toByteArray(List records) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (RecordBase rb : records) { + Record r = (Record) rb; + try { + out.write(r.serialize()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return out.toByteArray(); + } + + public void testSolverContainerMustBeSavedDuringSerialization(){ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("SolverContainerAfterSPGR.xls"); + HSSFSheet sh = wb.getSheetAt(0); + InternalSheet ish = HSSFTestHelper.getSheetForTest(sh); + sh.getDrawingPatriarch(); + EscherAggregate agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid); + assertEquals(agg.getEscherRecords().get(0).getChildRecords().size(), 3); + assertEquals(agg.getEscherRecords().get(0).getChild(2).getRecordId(), EscherContainerRecord.SOLVER_CONTAINER); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + sh = wb.getSheetAt(0); + sh.getDrawingPatriarch(); + ish = HSSFTestHelper.getSheetForTest(sh); + agg = (EscherAggregate) ish.findFirstRecordBySid(EscherAggregate.sid); + assertEquals(agg.getEscherRecords().get(0).getChildRecords().size(), 3); + assertEquals(agg.getEscherRecords().get(0).getChild(2).getRecordId(), EscherContainerRecord.SOLVER_CONTAINER); + + } /** * test reading drawing aggregate from a test file from Bugzilla 45129 @@ -107,6 +173,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); } /** @@ -174,7 +241,7 @@ 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); } @@ -185,21 +252,20 @@ public class TestDrawingAggregate extends TestCase { List records = isheet.getRecords(); HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); - InternalSheet isheet2 = HSSFTestHelper.getSheetForTest( wb2.getSheetAt(0)); + InternalSheet isheet2 = HSSFTestHelper.getSheetForTest(wb2.getSheetAt(0)); List records2 = isheet2.getRecords(); assertEquals(records.size(), records2.size()); - for(int i = 0; i < records.size(); i++) { + for (int i = 0; i < records.size(); i++) { RecordBase r1 = records.get(i); RecordBase r2 = records2.get(i); assertTrue(r1.getClass() == r2.getClass()); assertEquals(r1.getRecordSize(), r2.getRecordSize()); - if(r1 instanceof Record ){ - assertEquals(((Record)r1).getSid(), ((Record)r2).getSid()); + if (r1 instanceof Record) { + assertEquals(((Record) r1).getSid(), ((Record) r2).getSid()); assertTrue(Arrays.equals(((Record) r1).serialize(), ((Record) r2).serialize())); } } - } public void testSerializeDrawingWithComments() throws IOException { @@ -257,6 +323,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 before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); + checkEscherAndShapesCount(agg, sh); } @@ -314,7 +381,8 @@ 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)); + assertTrue("drawing data before and after save is different", Arrays.equals(dgBytes, dgBytesAfterSave)); + checkEscherAndShapesCount(agg, sh); } public void testUnhandledContinue() { diff --git a/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java b/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java index 7c5a88bd4..0b79eae50 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java @@ -18,6 +18,7 @@ package org.apache.poi.hssf.usermodel; import org.apache.poi.hssf.model.InternalSheet; import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.hssf.record.EscherAggregate; /** * Helper class for HSSF tests that aren't within the @@ -34,4 +35,8 @@ public class HSSFTestHelper { public static InternalSheet getSheetForTest(HSSFSheet sheet) { return sheet.getSheet(); } + + public static HSSFPatriarch createTestPatriarch(HSSFSheet sheet, EscherAggregate agg){ + return new HSSFPatriarch(sheet, agg); + } } diff --git a/test-data/spreadsheet/SolverContainerAfterSPGR.xls b/test-data/spreadsheet/SolverContainerAfterSPGR.xls new file mode 100755 index 000000000..57a1b1704 Binary files /dev/null and b/test-data/spreadsheet/SolverContainerAfterSPGR.xls differ