You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2020/01/26 19:50:40 UTC
svn commit: r1873187 - in /poi/trunk/src: java/org/apache/poi/ddf/
java/org/apache/poi/hssf/record/ java/org/apache/poi/hssf/usermodel/
scratchpad/src/org/apache/poi/hslf/record/
testcases/org/apache/poi/hssf/model/
Author: kiwiwings
Date: Sun Jan 26 19:50:40 2020
New Revision: 1873187
URL: http://svn.apache.org/viewvc?rev=1873187&view=rev
Log:
#64036 - Replace reflection calls in factories for Java 9+ - Escher factories
Removed:
poi/trunk/src/java/org/apache/poi/ddf/EscherPictBlip.java
Modified:
poi/trunk/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java
poi/trunk/src/java/org/apache/poi/ddf/EscherClientDataRecord.java
poi/trunk/src/java/org/apache/poi/ddf/EscherRecordTypes.java
poi/trunk/src/java/org/apache/poi/hssf/record/ContinueRecord.java
poi/trunk/src/java/org/apache/poi/hssf/record/DrawingRecord.java
poi/trunk/src/java/org/apache/poi/hssf/record/EscherAggregate.java
poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/HSLFEscherRecordFactory.java
poi/trunk/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java
Modified: poi/trunk/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java (original)
+++ poi/trunk/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java Sun Jan 26 19:50:40 2020
@@ -17,11 +17,12 @@
package org.apache.poi.ddf;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.function.Supplier;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Removal;
/**
* Generates escher records when provided the byte array containing those records.
@@ -29,14 +30,7 @@ import org.apache.poi.util.LittleEndian;
* @see EscherRecordFactory
*/
public class DefaultEscherRecordFactory implements EscherRecordFactory {
- private static Class<?>[] escherRecordClasses = { EscherBSERecord.class,
- EscherOptRecord.class, EscherTertiaryOptRecord.class,
- EscherClientAnchorRecord.class, EscherDgRecord.class,
- EscherSpgrRecord.class, EscherSpRecord.class,
- EscherClientDataRecord.class, EscherDggRecord.class,
- EscherSplitMenuColorsRecord.class, EscherChildAnchorRecord.class,
- EscherTextboxRecord.class };
- private static Map<Short, Constructor<? extends EscherRecord>> recordsMap = recordsToMap( escherRecordClasses );
+ private static final BitField IS_CONTAINER = BitFieldFactory.getInstance(0xF);
/**
* Creates an instance of the escher record factory
@@ -51,86 +45,41 @@ public class DefaultEscherRecordFactory
short recordId = LittleEndian.getShort( data, offset + 2 );
// int remainingBytes = LittleEndian.getInt( data, offset + 4 );
+ final EscherRecord escherRecord = getConstructor(options, recordId).get();
+ escherRecord.setRecordId(recordId);
+ escherRecord.setOptions(options);
+ return escherRecord;
+ }
+
+ protected Supplier<? extends EscherRecord> getConstructor(short options, short recordId) {
+ EscherRecordTypes recordTypes = EscherRecordTypes.forTypeID(recordId);
+
// Options of 0x000F means container record
- // However, EscherTextboxRecord are containers of records for the
- // host application, not of other Escher records, so treat them
- // differently
- if (isContainer(options, recordId)) {
- EscherContainerRecord r = new EscherContainerRecord();
- r.setRecordId( recordId );
- r.setOptions( options );
- return r;
+ // However, EscherTextboxRecord are containers of records for the host application,
+ // not of other Escher records, but those are returned by the above anyway
+ if (recordTypes == EscherRecordTypes.UNKNOWN && IS_CONTAINER.isAllSet(options)) {
+ return EscherContainerRecord::new;
}
- if (recordId >= EscherBlipRecord.RECORD_ID_START
- && recordId <= EscherBlipRecord.RECORD_ID_END) {
- EscherBlipRecord r;
- if (recordId == EscherBitmapBlip.RECORD_ID_DIB ||
- recordId == EscherBitmapBlip.RECORD_ID_JPEG ||
- recordId == EscherBitmapBlip.RECORD_ID_PNG)
- {
- r = new EscherBitmapBlip();
- }
- else if (recordId == EscherMetafileBlip.RECORD_ID_EMF ||
- recordId == EscherMetafileBlip.RECORD_ID_WMF ||
- recordId == EscherMetafileBlip.RECORD_ID_PICT)
- {
- r = new EscherMetafileBlip();
- } else {
- r = new EscherBlipRecord();
- }
- r.setRecordId( recordId );
- r.setOptions( options );
- return r;
+ if (recordTypes.constructor != null) {
+ return recordTypes.constructor;
}
- Constructor<? extends EscherRecord> recordConstructor = recordsMap.get(Short.valueOf(recordId));
- final EscherRecord escherRecord;
- if (recordConstructor == null) {
- return new UnknownEscherRecord();
+ // handle unknown blip records
+ if (EscherBlipRecord.RECORD_ID_START <= recordId && recordId <= EscherBlipRecord.RECORD_ID_END) {
+ return EscherBlipRecord::new;
}
- try {
- escherRecord = recordConstructor.newInstance();
- } catch (Exception e) {
- return new UnknownEscherRecord();
- }
- escherRecord.setRecordId(recordId);
- escherRecord.setOptions(options);
- return escherRecord;
+
+ // catch all
+ return UnknownEscherRecord::new;
}
+
/**
- * Converts from a list of classes into a map that contains the record id as the key and
- * the Constructor in the value part of the map. It does this by using reflection to look up
- * the RECORD_ID field then using reflection again to find a reference to the constructor.
- *
- * @param recClasses The records to convert
- * @return The map containing the id/constructor pairs.
+ * @deprecated this method is not used anymore to identify container records
*/
- protected static Map<Short, Constructor<? extends EscherRecord>> recordsToMap(Class<?>[] recClasses) {
- Map<Short, Constructor<? extends EscherRecord>> result = new HashMap<>();
- final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
-
- for (Class<?> recClass : recClasses) {
- @SuppressWarnings("unchecked")
- Class<? extends EscherRecord> recCls = (Class<? extends EscherRecord>) recClass;
- short sid;
- try {
- sid = recCls.getField("RECORD_ID").getShort(null);
- } catch (IllegalArgumentException | NoSuchFieldException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- Constructor<? extends EscherRecord> constructor;
- try {
- constructor = recCls.getConstructor(EMPTY_CLASS_ARRAY);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- result.put(Short.valueOf(sid), constructor);
- }
- return result;
- }
-
+ @Deprecated
+ @Removal(version = "5.0.0")
public static boolean isContainer(short options, short recordId){
if(recordId >= EscherContainerRecord.DGG_CONTAINER && recordId
<= EscherContainerRecord.SOLVER_CONTAINER){
Modified: poi/trunk/src/java/org/apache/poi/ddf/EscherClientDataRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ddf/EscherClientDataRecord.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ddf/EscherClientDataRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/ddf/EscherClientDataRecord.java Sun Jan 26 19:50:40 2020
@@ -30,11 +30,13 @@ import org.apache.poi.util.LittleEndian;
* shape within a container.
*/
public class EscherClientDataRecord extends EscherRecord {
- //arbitrarily selected; may need to increase
- private static final int MAX_RECORD_LENGTH = 100_000;
public static final short RECORD_ID = EscherRecordTypes.CLIENT_DATA.typeID;
+ //arbitrarily selected; may need to increase
+ private static final int MAX_RECORD_LENGTH = 100_000;
+ private static final byte[] EMPTY = {};
+
private byte[] remainingData;
public EscherClientDataRecord() {}
@@ -48,7 +50,7 @@ public class EscherClientDataRecord exte
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) {
int bytesRemaining = readHeader( data, offset );
int pos = offset + 8;
- remainingData = IOUtils.safelyAllocate(bytesRemaining, MAX_RECORD_LENGTH);
+ remainingData = (bytesRemaining == 0) ? EMPTY : IOUtils.safelyAllocate(bytesRemaining, MAX_RECORD_LENGTH);
System.arraycopy( data, pos, remainingData, 0, bytesRemaining );
return 8 + bytesRemaining;
}
@@ -58,7 +60,7 @@ public class EscherClientDataRecord exte
listener.beforeRecordSerialize( offset, getRecordId(), this );
if (remainingData == null) {
- remainingData = new byte[0];
+ remainingData = EMPTY;
}
LittleEndian.putShort( data, offset, getOptions() );
LittleEndian.putShort( data, offset + 2, getRecordId() );
Modified: poi/trunk/src/java/org/apache/poi/ddf/EscherRecordTypes.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ddf/EscherRecordTypes.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ddf/EscherRecordTypes.java (original)
+++ poi/trunk/src/java/org/apache/poi/ddf/EscherRecordTypes.java Sun Jan 26 19:50:40 2020
@@ -19,62 +19,65 @@ package org.apache.poi.ddf;
import java.util.Map;
import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public enum EscherRecordTypes {
- // records greater then 0xF000 belong to with Microsoft Office Drawing format also known as Escher
- DGG_CONTAINER(0xF000, "DggContainer", null),
- BSTORE_CONTAINER(0xf001, "BStoreContainer", null),
- DG_CONTAINER(0xf002, "DgContainer", null),
- SPGR_CONTAINER(0xf003, "SpgrContainer", null),
- SP_CONTAINER(0xf004, "SpContainer", null),
- SOLVER_CONTAINER(0xf005, "SolverContainer", null),
- DGG(0xf006, "Dgg", "MsofbtDgg"),
- BSE(0xf007, "BSE", "MsofbtBSE"),
- DG(0xf008, "Dg", "MsofbtDg"),
- SPGR(0xf009, "Spgr", "MsofbtSpgr"),
- SP(0xf00a, "Sp", "MsofbtSp"),
- OPT(0xf00b, "Opt", "msofbtOPT"),
- TEXTBOX(0xf00c, null, null),
- CLIENT_TEXTBOX(0xf00d, "ClientTextbox", "msofbtClientTextbox"),
- ANCHOR(0xf00e, null, null),
- CHILD_ANCHOR(0xf00f, "ChildAnchor", "MsofbtChildAnchor"),
- CLIENT_ANCHOR(0xf010, "ClientAnchor", "MsofbtClientAnchor"),
- CLIENT_DATA(0xf011, "ClientData", "MsofbtClientData"),
- CONNECTOR_RULE(0xf012, null, null),
- ALIGN_RULE(0xf013, null, null),
- ARC_RULE(0xf014, null, null),
- CLIENT_RULE(0xf015, null, null),
- CLSID(0xf016, null, null),
- CALLOUT_RULE(0xf017, null, null),
- BLIP_START(0xf018, "Blip", "msofbtBlip"),
- BLIP_EMF(0xf018 + 2, "BlipEmf", null),
- BLIP_WMF(0xf018 + 3, "BlipWmf", null),
- BLIP_PICT(0xf018 + 4, "BlipPict", null),
- BLIP_JPEG(0xf018 + 5, "BlipJpeg", null),
- BLIP_PNG(0xf018 + 6, "BlipPng", null),
- BLIP_DIB(0xf018 + 7, "BlipDib", null),
- BLIP_END(0xf117, "Blip", "msofbtBlip"),
- REGROUP_ITEMS(0xf118, null, null),
- SELECTION(0xf119, null, null),
- COLOR_MRU(0xf11a, null, null),
- DELETED_PSPL(0xf11d, null, null),
- SPLIT_MENU_COLORS(0xf11e, "SplitMenuColors", "MsofbtSplitMenuColors"),
- OLE_OBJECT(0xf11f, null, null),
- COLOR_SCHEME(0xf120, null, null),
+ // records greater then 0xF000 belong to Microsoft Office Drawing format also known as Escher
+ DGG_CONTAINER(0xF000, "DggContainer", null, EscherContainerRecord::new),
+ BSTORE_CONTAINER(0xf001, "BStoreContainer", null, EscherContainerRecord::new),
+ DG_CONTAINER(0xf002, "DgContainer", null, EscherContainerRecord::new),
+ SPGR_CONTAINER(0xf003, "SpgrContainer", null, EscherContainerRecord::new),
+ SP_CONTAINER(0xf004, "SpContainer", null, EscherContainerRecord::new),
+ SOLVER_CONTAINER(0xf005, "SolverContainer", null, EscherContainerRecord::new),
+ DGG(0xf006, "Dgg", "MsofbtDgg", EscherDggRecord::new),
+ BSE(0xf007, "BSE", "MsofbtBSE", EscherBSERecord::new),
+ DG(0xf008, "Dg", "MsofbtDg", EscherDgRecord::new),
+ SPGR(0xf009, "Spgr", "MsofbtSpgr", EscherSpgrRecord::new),
+ SP(0xf00a, "Sp", "MsofbtSp", EscherSpRecord::new),
+ OPT(0xf00b, "Opt", "msofbtOPT", EscherOptRecord::new),
+ TEXTBOX(0xf00c, null, null, EscherTextboxRecord::new),
+ CLIENT_TEXTBOX(0xf00d, "ClientTextbox", "msofbtClientTextbox", EscherTextboxRecord::new),
+ ANCHOR(0xf00e, null, null, null),
+ CHILD_ANCHOR(0xf00f, "ChildAnchor", "MsofbtChildAnchor", EscherChildAnchorRecord::new),
+ CLIENT_ANCHOR(0xf010, "ClientAnchor", "MsofbtClientAnchor", EscherClientAnchorRecord::new),
+ CLIENT_DATA(0xf011, "ClientData", "MsofbtClientData", EscherClientDataRecord::new),
+ CONNECTOR_RULE(0xf012, null, null, null),
+ ALIGN_RULE(0xf013, null, null, null),
+ ARC_RULE(0xf014, null, null, null),
+ CLIENT_RULE(0xf015, null, null, null),
+ CLSID(0xf016, null, null, null),
+ CALLOUT_RULE(0xf017, null, null, null),
+ BLIP_START(0xf018, "Blip", "msofbtBlip", null),
+ BLIP_EMF(0xf018 + 2, "BlipEmf", null, EscherMetafileBlip::new),
+ BLIP_WMF(0xf018 + 3, "BlipWmf", null, EscherMetafileBlip::new),
+ BLIP_PICT(0xf018 + 4, "BlipPict", null, EscherMetafileBlip::new),
+ BLIP_JPEG(0xf018 + 5, "BlipJpeg", null, EscherBitmapBlip::new),
+ BLIP_PNG(0xf018 + 6, "BlipPng", null, EscherBitmapBlip::new),
+ BLIP_DIB(0xf018 + 7, "BlipDib", null, EscherBitmapBlip::new),
+ BLIP_END(0xf117, "Blip", "msofbtBlip", null),
+ REGROUP_ITEMS(0xf118, null, null, null),
+ SELECTION(0xf119, null, null, null),
+ COLOR_MRU(0xf11a, null, null, null),
+ DELETED_PSPL(0xf11d, null, null, null),
+ SPLIT_MENU_COLORS(0xf11e, "SplitMenuColors", "MsofbtSplitMenuColors", EscherSplitMenuColorsRecord::new),
+ OLE_OBJECT(0xf11f, null, null, null),
+ COLOR_SCHEME(0xf120, null, null, null),
// same as EscherTertiaryOptRecord.RECORD_ID
- USER_DEFINED(0xf122, "TertiaryOpt", null),
- UNKNOWN(0xffff, "unknown", "unknown");
+ USER_DEFINED(0xf122, "TertiaryOpt", null, EscherTertiaryOptRecord::new),
+ UNKNOWN(0xffff, "unknown", "unknown", UnknownEscherRecord::new);
public final short typeID;
public final String recordName;
public final String description;
+ public final Supplier<? extends EscherRecord> constructor;
- EscherRecordTypes(int typeID, String recordName, String description) {
+ EscherRecordTypes(int typeID, String recordName, String description, Supplier<? extends EscherRecord> constructor) {
this.typeID = (short) typeID;
this.recordName = recordName;
this.description = description;
+ this.constructor = constructor;
}
private Short getTypeId() {
Modified: poi/trunk/src/java/org/apache/poi/hssf/record/ContinueRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/ContinueRecord.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/ContinueRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/ContinueRecord.java Sun Jan 26 19:50:40 2020
@@ -31,7 +31,7 @@ public final class ContinueRecord extend
private byte[] _data;
public ContinueRecord(byte[] data) {
- _data = data;
+ _data = data.clone();
}
public ContinueRecord(ContinueRecord other) {
Modified: poi/trunk/src/java/org/apache/poi/hssf/record/DrawingRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/DrawingRecord.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/DrawingRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/DrawingRecord.java Sun Jan 26 19:50:40 2020
@@ -43,6 +43,10 @@ public final class DrawingRecord extends
recordData = in.readRemainder();
}
+ public DrawingRecord(byte[] data) {
+ recordData = data.clone();
+ }
+
/**
* @deprecated POI 3.9
*/
Modified: poi/trunk/src/java/org/apache/poi/hssf/record/EscherAggregate.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/EscherAggregate.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/EscherAggregate.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/EscherAggregate.java Sun Jan 26 19:50:40 2020
@@ -17,11 +17,15 @@
package org.apache.poi.hssf.record;
+import static org.apache.poi.hssf.record.RecordInputStream.MAX_RECORD_DATA_SIZE;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -31,14 +35,11 @@ import org.apache.poi.ddf.EscherClientDa
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherDgRecord;
import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherRecordFactory;
import org.apache.poi.ddf.EscherSerializationListener;
import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.ddf.EscherSpgrRecord;
import org.apache.poi.ddf.EscherTextboxRecord;
import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
import org.apache.poi.util.RecordFormatException;
/**
@@ -84,8 +85,8 @@ import org.apache.poi.util.RecordFormatE
*/
public final class EscherAggregate extends AbstractEscherHolderRecord {
- public static final short sid = 9876; // not a real sid - dummy value
- private static final POILogger log = POILogFactory.getLogger(EscherAggregate.class);
+ // not a real sid - dummy value
+ public static final short sid = 9876;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000_000;
@@ -365,17 +366,6 @@ public final class EscherAggregate exten
}
/**
- * @param sid - record sid we want to check if it belongs to drawing layer
- * @return true if record is instance of DrawingRecord or ContinueRecord or ObjRecord or TextObjRecord
- */
- private static boolean isDrawingLayerRecord(final short sid) {
- return sid == DrawingRecord.sid ||
- sid == ContinueRecord.sid ||
- sid == ObjRecord.sid ||
- sid == TextObjectRecord.sid;
- }
-
- /**
* Collapses the drawing records into an aggregate.
* read Drawing, Obj, TxtObj, Note and Continue records into single byte array,
* create Escher tree from byte array, create map <EscherRecord, Record>
@@ -384,82 +374,83 @@ public final class EscherAggregate exten
* @param locFirstDrawingRecord - location of the first DrawingRecord inside sheet
* @return new EscherAggregate create from all aggregated records which belong to drawing layer
*/
- public static EscherAggregate createAggregate(List<RecordBase> records, int locFirstDrawingRecord) {
- // Keep track of any shape records created so we can match them back to the object id's.
- // Textbox objects are also treated as shape objects.
- final List<EscherRecord> shapeRecords = new ArrayList<>();
- EscherRecordFactory recordFactory = new DefaultEscherRecordFactory() {
- public EscherRecord createRecord(byte[] data, int offset) {
- EscherRecord r = super.createRecord(data, offset);
- if (r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID) {
- shapeRecords.add(r);
- }
- return r;
- }
- };
-
- // Create one big buffer
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ public static EscherAggregate createAggregate(final List<RecordBase> records, final int locFirstDrawingRecord) {
EscherAggregate agg = new EscherAggregate(false);
- int loc = locFirstDrawingRecord;
- while (loc + 1 < records.size()
- && (isDrawingLayerRecord(sid(records, loc)))) {
- try {
- if (!(sid(records, loc) == DrawingRecord.sid || sid(records, loc) == ContinueRecord.sid)) {
- loc++;
+
+ ShapeCollector recordFactory = new ShapeCollector();
+ List<Record> objectRecords = new ArrayList<>();
+
+ int nextIdx = locFirstDrawingRecord;
+ for (RecordBase rb : records.subList(locFirstDrawingRecord, records.size())) {
+ nextIdx++;
+ switch (sid(rb)) {
+ case DrawingRecord.sid:
+ recordFactory.addBytes(((DrawingRecord)rb).getRecordData());
continue;
- }
- if (sid(records, loc) == DrawingRecord.sid) {
- buffer.write(((DrawingRecord) records.get(loc)).getRecordData());
- } else {
- buffer.write(((ContinueRecord) records.get(loc)).getData());
- }
- } catch (IOException e) {
- throw new RuntimeException("Couldn't get data from drawing/continue records", e);
+ case ContinueRecord.sid:
+ recordFactory.addBytes(((ContinueRecord)rb).getData());
+ continue;
+ case ObjRecord.sid:
+ case TextObjectRecord.sid:
+ objectRecords.add((org.apache.poi.hssf.record.Record)rb);
+ continue;
+ case NoteRecord.sid:
+ // any NoteRecords that follow the drawing block must be aggregated and saved in the tailRec collection
+ NoteRecord r = (NoteRecord)rb;
+ agg.tailRec.put(r.getShapeId(), r);
+ continue;
+ default:
+ nextIdx--;
+ break;
}
- loc++;
+ break;
}
- // Decode the shapes
- // agg.escherRecords = new ArrayList();
- int pos = 0;
- while (pos < buffer.size()) {
- EscherRecord r = recordFactory.createRecord(buffer.toByteArray(), pos);
- int bytesRead = r.fillFields(buffer.toByteArray(), pos, recordFactory);
- agg.addEscherRecord(r);
- pos += bytesRead;
+ // replace drawing block with the created EscherAggregate
+ records.set(locFirstDrawingRecord, agg);
+ if (locFirstDrawingRecord+1 <= nextIdx) {
+ records.subList(locFirstDrawingRecord + 1, nextIdx).clear();
}
+ // Decode the shapes
+ Iterator<EscherRecord> shapeIter = recordFactory.parse(agg).iterator();
+
// Associate the object records with the shapes
- loc = locFirstDrawingRecord + 1;
- int shapeIndex = 0;
- while (loc < records.size()
- && (isDrawingLayerRecord(sid(records, loc)))) {
- if (!isObjectRecord(records, loc)) {
- loc++;
- continue;
+ objectRecords.forEach(or -> agg.shapeToObj.put(shapeIter.next(), or));
+
+ return agg;
+ }
+
+ private static class ShapeCollector extends DefaultEscherRecordFactory {
+ final List<EscherRecord> objShapes = new ArrayList<>();
+ final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ void addBytes(byte[] data) {
+ try {
+ buffer.write(data);
+ } catch (IOException e) {
+ throw new RuntimeException("Couldn't get data from drawing/continue records", e);
}
- Record objRecord = (org.apache.poi.hssf.record.Record) records.get(loc);
- agg.shapeToObj.put(shapeRecords.get(shapeIndex++), objRecord);
- loc++;
}
- // any NoteRecords that follow the drawing block must be aggregated and and saved in the tailRec collection
- while (loc < records.size()) {
- if (sid(records, loc) == NoteRecord.sid) {
- NoteRecord r = (NoteRecord) records.get(loc);
- agg.tailRec.put(r.getShapeId(), r);
- } else {
- break;
+ public EscherRecord createRecord(byte[] data, int offset) {
+ EscherRecord r = super.createRecord(data, offset);
+ short rid = r.getRecordId();
+ if (rid == EscherClientDataRecord.RECORD_ID || rid == EscherTextboxRecord.RECORD_ID) {
+ objShapes.add(r);
}
- loc++;
+ return r;
}
- int locLastDrawingRecord = loc;
- // replace drawing block with the created EscherAggregate
- records.subList(locFirstDrawingRecord, locLastDrawingRecord).clear();
- records.add(locFirstDrawingRecord, agg);
- return agg;
+ List<EscherRecord> parse(EscherAggregate agg) {
+ byte[] buf = buffer.toByteArray();
+ for (int pos = 0, bytesRead; pos < buf.length; pos += bytesRead) {
+ EscherRecord r = createRecord(buf, pos);
+ bytesRead = r.fillFields(buf, pos, this);
+ agg.addEscherRecord(r);
+ }
+ return objShapes;
+ }
}
/**
@@ -470,7 +461,7 @@ public final class EscherAggregate exten
* @param data The byte array to serialize to.
* @return The number of bytes serialized.
*/
- public int serialize(int offset, byte[] data) {
+ public int serialize(final int offset, final byte[] data) {
// Determine buffer size
List <EscherRecord>records = getEscherRecords();
int size = getEscherRecordSize(records);
@@ -501,18 +492,14 @@ public final class EscherAggregate exten
// the first one because it's the patriach).
pos = offset;
int writtenEscherBytes = 0;
- int i;
- for (i = 1; i < shapes.size(); i++) {
- int endOffset = spEndingOffsets.get(i) - 1;
- int startOffset;
- if (i == 1)
- startOffset = 0;
- else
- startOffset = spEndingOffsets.get(i - 1);
-
- byte[] drawingData = new byte[endOffset - startOffset + 1];
- System.arraycopy(buffer, startOffset, drawingData, 0, drawingData.length);
- pos += writeDataIntoDrawingRecord(drawingData, writtenEscherBytes, pos, data, i);
+ boolean isFirst = true;
+ int endOffset = 0;
+ for (int i = 1; i < shapes.size(); i++) {
+ int startOffset = endOffset;
+ endOffset = spEndingOffsets.get(i);
+
+ byte[] drawingData = Arrays.copyOfRange(buffer, startOffset, endOffset);
+ pos += writeDataIntoDrawingRecord(drawingData, writtenEscherBytes, pos, data, isFirst);
writtenEscherBytes += drawingData.length;
@@ -520,24 +507,22 @@ public final class EscherAggregate exten
Record obj = shapeToObj.get(shapes.get(i));
pos += obj.serialize(pos, data);
- if (i == shapes.size() - 1 && endOffset < buffer.length - 1) {
- drawingData = new byte[buffer.length - endOffset - 1];
- System.arraycopy(buffer, endOffset + 1, drawingData, 0, drawingData.length);
- pos += writeDataIntoDrawingRecord(drawingData, writtenEscherBytes, pos, data, i);
- }
+ isFirst = false;
}
- if ((pos - offset) < buffer.length - 1) {
- byte[] drawingData = new byte[buffer.length - (pos - offset)];
- System.arraycopy(buffer, (pos - offset), drawingData, 0, drawingData.length);
- pos += writeDataIntoDrawingRecord(drawingData, writtenEscherBytes, pos, data, i);
+
+ if (endOffset < buffer.length - 1) {
+ byte[] drawingData = Arrays.copyOfRange(buffer, endOffset, buffer.length);
+ pos += writeDataIntoDrawingRecord(drawingData, writtenEscherBytes, pos, data, isFirst);
}
for (NoteRecord noteRecord : tailRec.values()) {
pos += noteRecord.serialize(pos, data);
}
+
int bytesWritten = pos - offset;
- if (bytesWritten != getRecordSize())
+ if (bytesWritten != getRecordSize()) {
throw new RecordFormatException(bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize());
+ }
return bytesWritten;
}
@@ -547,34 +532,19 @@ public final class EscherAggregate exten
* drawing or continue record)
* @param pos current position of data array
* @param data - array of bytes where drawing records must be serialized
- * @param i - number of shape, saved into data array
+ * @param isFirst - is it the first shape, saved into data array
* @return offset of data array after serialization
*/
- private int writeDataIntoDrawingRecord(byte[] drawingData, int writtenEscherBytes, int pos, byte[] data, int i) {
+ private int writeDataIntoDrawingRecord(final byte[] drawingData, final int writtenEscherBytes, final int pos, final byte[] data, final boolean isFirst) {
int temp = 0;
//First record in drawing layer MUST be DrawingRecord
- if (writtenEscherBytes + drawingData.length > RecordInputStream.MAX_RECORD_DATA_SIZE && i != 1) {
- for (int j = 0; j < drawingData.length; j += RecordInputStream.MAX_RECORD_DATA_SIZE) {
- byte[] buf = new byte[Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j)];
- System.arraycopy(drawingData, j, buf, 0, Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j));
- ContinueRecord drawing = new ContinueRecord(buf);
- temp += drawing.serialize(pos + temp, data);
- }
- } else {
- for (int j = 0; j < drawingData.length; j += RecordInputStream.MAX_RECORD_DATA_SIZE) {
- if (j == 0) {
- DrawingRecord drawing = new DrawingRecord();
- byte[] buf = new byte[Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j)];
- System.arraycopy(drawingData, j, buf, 0, Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j));
- drawing.setData(buf);
- temp += drawing.serialize(pos + temp, data);
- } else {
- byte[] buf = new byte[Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j)];
- System.arraycopy(drawingData, j, buf, 0, Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE, drawingData.length - j));
- ContinueRecord drawing = new ContinueRecord(buf);
- temp += drawing.serialize(pos + temp, data);
- }
- }
+ boolean useDrawingRecord = isFirst || (writtenEscherBytes + drawingData.length) <= MAX_RECORD_DATA_SIZE;
+
+ for (int j = 0; j < drawingData.length; j += MAX_RECORD_DATA_SIZE) {
+ byte[] buf = Arrays.copyOfRange(drawingData, j, Math.min(j+MAX_RECORD_DATA_SIZE, drawingData.length));
+ Record drawing = (useDrawingRecord) ? new DrawingRecord(buf) : new ContinueRecord(buf);
+ temp += drawing.serialize(pos + temp, data);
+ useDrawingRecord = false;
}
return temp;
}
@@ -624,14 +594,15 @@ public final class EscherAggregate exten
if (i == spEndingOffsets.size() - 1 && spEndingOffsets.get(i) < pos) {
continueRecordsHeadersSize += 4;
}
- if (spEndingOffsets.get(i) - spEndingOffsets.get(i - 1) <= RecordInputStream.MAX_RECORD_DATA_SIZE) {
+ if (spEndingOffsets.get(i) - spEndingOffsets.get(i - 1) <= MAX_RECORD_DATA_SIZE) {
continue;
}
- continueRecordsHeadersSize += ((spEndingOffsets.get(i) - spEndingOffsets.get(i - 1)) / RecordInputStream.MAX_RECORD_DATA_SIZE) * 4;
+ continueRecordsHeadersSize += ((spEndingOffsets.get(i) - spEndingOffsets.get(i - 1)) / MAX_RECORD_DATA_SIZE) * 4;
}
int drawingRecordSize = rawEscherSize + (shapeToObj.size()) * 4;
- if (rawEscherSize != 0 && spEndingOffsets.size() == 1/**EMPTY**/) {
+ if (rawEscherSize != 0 && spEndingOffsets.size() == 1) {
+ // EMPTY
continueRecordsHeadersSize += 4;
}
int objRecordSize = 0;
@@ -672,16 +643,6 @@ public final class EscherAggregate exten
// =============== Private methods ========================
/**
- *
- * @param records list of the record to look inside
- * @param loc location of the checked record
- * @return true if record is instance of ObjRecord or TextObjectRecord
- */
- private static boolean isObjectRecord(List <RecordBase>records, int loc) {
- return sid(records, loc) == ObjRecord.sid || sid(records, loc) == TextObjectRecord.sid;
- }
-
- /**
* create base tree with such structure:
* EscherDgContainer
* -EscherSpgrContainer
@@ -741,7 +702,9 @@ public final class EscherAggregate exten
public void setDgId(short dgId) {
EscherContainerRecord dgContainer = getEscherContainer();
EscherDgRecord dg = dgContainer.getChildById(EscherDgRecord.RECORD_ID);
- dg.setOptions((short) (dgId << 4));
+ if (dg != null) {
+ dg.setOptions((short) (dgId << 4));
+ }
}
/**
@@ -757,26 +720,26 @@ public final class EscherAggregate exten
*/
public void setMainSpRecordId(int shapeId) {
EscherContainerRecord dgContainer = getEscherContainer();
- EscherContainerRecord spgrConatiner = dgContainer.getChildById(EscherContainerRecord.SPGR_CONTAINER);
- EscherContainerRecord spContainer = (EscherContainerRecord) spgrConatiner.getChild(0);
- EscherSpRecord sp = spContainer.getChildById(EscherSpRecord.RECORD_ID);
- sp.setShapeId(shapeId);
+ EscherContainerRecord spgrContainer = dgContainer.getChildById(EscherContainerRecord.SPGR_CONTAINER);
+ if (spgrContainer != null) {
+ EscherContainerRecord spContainer = (EscherContainerRecord) spgrContainer.getChild(0);
+ EscherSpRecord sp = spContainer.getChildById(EscherSpRecord.RECORD_ID);
+ if (sp != null) {
+ sp.setShapeId(shapeId);
+ }
+ }
}
/**
- * @param records list of records to look into
- * @param loc - location of the record which sid must be returned
- * @return sid of the record with selected location
- */
- private static short sid(List<RecordBase> records, int loc) {
- RecordBase record = records.get(loc);
- if (record instanceof Record) {
- return ((org.apache.poi.hssf.record.Record)record).getSid();
- } else {
- // Aggregates don't have a sid
- // We could step into them, but for these needs we don't care
- return -1;
- }
+ * @param record the record to look into
+ * @return sid of the record
+ */
+ private static short sid(RecordBase record) {
+ // Aggregates don't have a sid
+ // We could step into them, but for these needs we don't care
+ return (record instanceof org.apache.poi.hssf.record.Record)
+ ? ((org.apache.poi.hssf.record.Record)record).getSid()
+ : -1;
}
/**
Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java Sun Jan 26 19:50:40 2020
@@ -58,11 +58,8 @@ public class HSSFShapeFactory {
HSSFShapeGroup group = new HSSFShapeGroup(container, obj);
List<EscherContainerRecord> 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) {
- createShapeTree(spContainer, agg, group, root);
- }
+ if (children.size() > 1) {
+ children.subList(1, children.size()).forEach(c -> createShapeTree(c, agg, group, root));
}
out.addShape(group);
} else if (container.getRecordId() == EscherContainerRecord.SP_CONTAINER) {
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/HSLFEscherRecordFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/HSLFEscherRecordFactory.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/HSLFEscherRecordFactory.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/HSLFEscherRecordFactory.java Sun Jan 26 19:50:40 2020
@@ -17,11 +17,11 @@
package org.apache.poi.hslf.record;
-import java.lang.reflect.Constructor;
-import java.util.Map;
+import java.util.function.Supplier;
-import org.apache.poi.ddf.*;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordFactory;
/**
* Generates escher records when provided the byte array containing those records.
@@ -29,39 +29,20 @@ import org.apache.poi.util.LittleEndian;
* @see EscherRecordFactory
*/
public class HSLFEscherRecordFactory extends DefaultEscherRecordFactory {
- private static Class<?>[] escherRecordClasses = { EscherPlaceholder.class, HSLFEscherClientDataRecord.class };
- private static Map<Short, Constructor<? extends EscherRecord>> recordsMap = recordsToMap( escherRecordClasses );
-
-
/**
* Creates an instance of the escher record factory
*/
public HSLFEscherRecordFactory() {
// no instance initialisation
}
-
- @Override
- public EscherRecord createRecord(byte[] data, int offset) {
- short options = LittleEndian.getShort( data, offset );
- short recordId = LittleEndian.getShort( data, offset + 2 );
- // int remainingBytes = LittleEndian.getInt( data, offset + 4 );
- Constructor<? extends EscherRecord> recordConstructor = recordsMap.get(Short.valueOf(recordId));
- if (recordConstructor == null) {
- return super.createRecord(data, offset);
- }
- EscherRecord escherRecord = null;
- try {
- escherRecord = recordConstructor.newInstance(new Object[] {});
- } catch (Exception e) {
- return super.createRecord(data, offset);
- }
- escherRecord.setRecordId(recordId);
- escherRecord.setOptions(options);
- if (escherRecord instanceof EscherContainerRecord) {
- escherRecord.fillFields(data, offset, this);
+ @Override
+ protected Supplier<? extends EscherRecord> getConstructor(short options, short recordId) {
+ if (recordId == EscherPlaceholder.RECORD_ID) {
+ return EscherPlaceholder::new;
+ } else if (recordId == HSLFEscherClientDataRecord.RECORD_ID) {
+ return HSLFEscherClientDataRecord::new;
}
-
- return escherRecord;
+ return super.getConstructor(options, recordId);
}
}
Modified: poi/trunk/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java?rev=1873187&r1=1873186&r2=1873187&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/model/TestDrawingAggregate.java Sun Jan 26 19:50:40 2020
@@ -726,31 +726,22 @@ public class TestDrawingAggregate {
List<org.apache.poi.hssf.record.Record> dgRecords = RecordFactory.createRecords(new ByteArrayInputStream(dgBytes));
assertEquals(20, dgRecords.size());
- short[] expectedSids = {
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- TextObjectRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- TextObjectRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- TextObjectRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- TextObjectRecord.sid,
- ContinueRecord.sid,
- ObjRecord.sid,
- ContinueRecord.sid,
- TextObjectRecord.sid
+ int[] expectedSids = {
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, TextObjectRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, TextObjectRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, TextObjectRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, TextObjectRecord.sid,
+ ContinueRecord.sid, ObjRecord.sid,
+ ContinueRecord.sid, TextObjectRecord.sid
};
- for (int i = 0; i < expectedSids.length; i++) {
- assertEquals("unexpected record.sid and index[" + i + "]", expectedSids[i], dgRecords.get(i).getSid());
- }
+
+ int[] actualSids = dgRecords.stream().mapToInt(Record::getSid).toArray();
+ assertArrayEquals("unexpected record.sid", expectedSids, actualSids);
+
DrawingManager2 drawingManager = new DrawingManager2(new EscherDggRecord());
// create a dummy sheet consisting of our test data
@@ -904,26 +895,19 @@ public class TestDrawingAggregate {
List<org.apache.poi.hssf.record.Record> dgRecords = RecordFactory.createRecords(new ByteArrayInputStream(dgBytes));
assertEquals(14, dgRecords.size());
- short[] expectedSids = {
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- DrawingRecord.sid,
- ObjRecord.sid,
- ContinueRecord.sid,
- ObjRecord.sid,
- ContinueRecord.sid,
- ObjRecord.sid,
- ContinueRecord.sid,
- ObjRecord.sid
+ int[] expectedSids = {
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ DrawingRecord.sid, ObjRecord.sid,
+ ContinueRecord.sid, ObjRecord.sid,
+ ContinueRecord.sid, ObjRecord.sid,
+ ContinueRecord.sid, ObjRecord.sid
};
- for (int i = 0; i < expectedSids.length; i++) {
- assertEquals("unexpected record.sid and index[" + i + "]", expectedSids[i], dgRecords.get(i).getSid());
- }
+ int[] actualSids = dgRecords.stream().mapToInt(Record::getSid).toArray();
+ assertArrayEquals("unexpected record.sid", expectedSids, actualSids);
+
DrawingManager2 drawingManager = new DrawingManager2(new EscherDggRecord());
// create a dummy sheet consisting of our test data
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org