aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--poi-integration/src/test/java/org/apache/poi/stress/HSSFFileHandler.java7
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java29
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java162
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/BiffViewer.java713
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/EFBiffViewer.java74
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/FormulaViewer.java200
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/ReSave.java71
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/dev/RecordLister.java193
-rw-r--r--poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java12
-rw-r--r--poi/src/main/java/org/apache/poi/util/CloseIgnoringInputStream.java38
-rw-r--r--poi/src/main/java9/module-info.classbin3421 -> 3385 bytes
-rw-r--r--poi/src/main/java9/module-info.java1
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/BiffDumpingStream.java121
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/BiffViewer.java342
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java77
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java24
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java32
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java132
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestReSave.java94
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java142
-rw-r--r--poi/src/test/java/org/apache/poi/hssf/dev/package-info.java (renamed from poi/src/main/java/org/apache/poi/hssf/dev/package-info.java)0
21 files changed, 799 insertions, 1665 deletions
diff --git a/poi-integration/src/test/java/org/apache/poi/stress/HSSFFileHandler.java b/poi-integration/src/test/java/org/apache/poi/stress/HSSFFileHandler.java
index 62e0d7cbc3..b009d2ea0b 100644
--- a/poi-integration/src/test/java/org/apache/poi/stress/HSSFFileHandler.java
+++ b/poi-integration/src/test/java/org/apache/poi/stress/HSSFFileHandler.java
@@ -33,7 +33,6 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.commons.io.output.NullPrintStream;
import org.junit.jupiter.api.Test;
public class HSSFFileHandler extends SpreadsheetHandler {
@@ -88,10 +87,8 @@ public class HSSFFileHandler extends SpreadsheetHandler {
PrintStream oldOut = System.out;
String fileWithParent = file.getParentFile().getName() + "/" + file.getName();
try {
- System.setOut(new NullPrintStream());
-
- BiffViewer.main(new String[]{file.getAbsolutePath()});
-
+ BiffViewer bv = new BiffViewer();
+ bv.parse(file, null);
assertFalse( EXPECTED_ADDITIONAL_FAILURES.contains(fileWithParent), "Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!" );
} catch (OldExcelFormatException e) {
// old excel formats are not supported here
diff --git a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java
index f66808354a..0e06557812 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java
@@ -21,7 +21,6 @@ package org.apache.poi.poifs.crypt.temp;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
@@ -35,6 +34,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.io.output.CloseShieldOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.openxml4j.util.ZipEntrySource;
@@ -47,7 +47,7 @@ import org.apache.poi.util.RandomSingleton;
import org.apache.poi.util.TempFile;
/**
- * An example <code>ZipEntrySource</code> that has encrypted temp files to ensure that
+ * An example {@code ZipEntrySource} that has encrypted temp files to ensure that
* sensitive data is not stored in raw format on disk.
*/
@Beta
@@ -60,14 +60,14 @@ public final class AesZipFileZipEntrySource implements ZipEntrySource {
private final ZipFile zipFile;
private final Cipher ci;
private boolean closed;
-
+
private AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException {
this.tmpFile = tmpFile;
this.zipFile = new ZipFile(tmpFile);
this.ci = ci;
this.closed = false;
}
-
+
/**
* Note: the file sizes are rounded up to the next cipher block size,
* so don't rely on file sizes of these custom encrypted zip file entries!
@@ -87,7 +87,7 @@ public final class AesZipFileZipEntrySource implements ZipEntrySource {
InputStream is = zipFile.getInputStream(entry);
return new CipherInputStream(is, ci);
}
-
+
@Override
public void close() throws IOException {
if(!closed) {
@@ -118,7 +118,7 @@ public final class AesZipFileZipEntrySource implements ZipEntrySource {
private static void copyToFile(InputStream is, File tmpFile, byte[] keyBytes, byte[] ivBytes) throws IOException {
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, CipherAlgorithm.aes128.jceId);
Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, CipherAlgorithm.aes128, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, PADDING);
-
+
try (ZipArchiveInputStream zis = new ZipArchiveInputStream(is);
FileOutputStream fos = new FileOutputStream(tmpFile);
ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
@@ -133,16 +133,11 @@ public final class AesZipFileZipEntrySource implements ZipEntrySource {
zeNew.setTime(ze.getTime());
// zeNew.setMethod(ze.getMethod());
zos.putArchiveEntry(zeNew);
- FilterOutputStream fos2 = new FilterOutputStream(zos) {
- // don't close underlying ZipOutputStream
- @Override
- public void close() {
- }
- };
- CipherOutputStream cos = new CipherOutputStream(fos2, ciEnc);
- IOUtils.copy(zis, cos);
- cos.close();
- fos2.close();
+
+ // don't close underlying ZipOutputStream
+ try (CipherOutputStream cos = new CipherOutputStream(CloseShieldOutputStream.wrap(zos), ciEnc)) {
+ IOUtils.copy(zis, cos);
+ }
zos.closeArchiveEntry();
}
}
@@ -153,5 +148,5 @@ public final class AesZipFileZipEntrySource implements ZipEntrySource {
Cipher ciDec = CryptoFunctions.getCipher(skeySpec, CipherAlgorithm.aes128, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, PADDING);
return new AesZipFileZipEntrySource(tmpFile, ciDec);
}
-
+
} \ No newline at end of file
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java b/poi/src/main/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java
deleted file mode 100644
index 3fc3f5d3be..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * ====================================================================
- * 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.dev;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.hssf.model.InternalWorkbook;
-import org.apache.poi.hssf.record.DrawingGroupRecord;
-import org.apache.poi.hssf.usermodel.HSSFPatriarch;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.util.StringUtil;
-
-/**
- * Utility for representing drawings contained in a binary Excel file as a XML tree
- */
-public class BiffDrawingToXml {
- private static final String SHEET_NAME_PARAM = "-sheet-name";
- private static final String SHEET_INDEXES_PARAM = "-sheet-indexes";
- private static final String EXCLUDE_WORKBOOK_RECORDS = "-exclude-workbook";
-
- private static int getAttributeIndex(String attribute, String[] params) {
- for (int i = 0; i < params.length; i++) {
- String param = params[i];
- if (attribute.equals(param)) {
- return i;
- }
- }
- return -1;
- }
-
- private static boolean isExcludeWorkbookRecords(String[] params) {
- return -1 != getAttributeIndex(EXCLUDE_WORKBOOK_RECORDS, params);
- }
-
- private static List<Integer> getIndexesByName(String[] params, HSSFWorkbook workbook) {
- List<Integer> list = new ArrayList<>();
- int pos = getAttributeIndex(SHEET_NAME_PARAM, params);
- if (-1 != pos) {
- if (pos >= params.length) {
- throw new IllegalArgumentException("sheet name param value was not specified");
- }
- String sheetName = params[pos + 1];
- int sheetPos = workbook.getSheetIndex(sheetName);
- if (-1 == sheetPos){
- throw new IllegalArgumentException("specified sheet name has not been found in xls file");
- }
- list.add(sheetPos);
- }
- return list;
- }
-
- private static List<Integer> getIndexesByIdArray(String[] params) {
- List<Integer> list = new ArrayList<>();
- int pos = getAttributeIndex(SHEET_INDEXES_PARAM, params);
- if (-1 != pos) {
- if (pos >= params.length) {
- throw new IllegalArgumentException("sheet list value was not specified");
- }
- String sheetParam = params[pos + 1];
- String[] sheets = sheetParam.split(",");
- for (String sheet : sheets) {
- list.add(Integer.parseInt(sheet));
- }
- }
- return list;
- }
-
- private static List<Integer> getSheetsIndexes(String[] params, HSSFWorkbook workbook) {
- List<Integer> list = new ArrayList<>();
- list.addAll(getIndexesByIdArray(params));
- list.addAll(getIndexesByName(params, workbook));
- if (0 == list.size()) {
- int size = workbook.getNumberOfSheets();
- for (int i = 0; i < size; i++) {
- list.add(i);
- }
- }
- return list;
- }
-
- private static String getInputFileName(String[] params) {
- return params[params.length - 1];
- }
-
- private static String getOutputFileName(String input) {
- if (input.contains("xls")) {
- return input.replace(".xls", ".xml");
- }
- return input + ".xml";
- }
-
- public static void main(String[] params) throws IOException {
- if (0 == params.length) {
- System.out.println("Usage: BiffDrawingToXml [options] inputWorkbook");
- System.out.println("Options:");
- System.out.println(" -exclude-workbook exclude workbook-level records");
- System.out.println(" -sheet-indexes <indexes> output sheets with specified indexes");
- System.out.println(" -sheet-namek <names> output sheets with specified name");
- return;
- }
- String input = getInputFileName(params);
- String output = getOutputFileName(input);
- try (FileInputStream inp = new FileInputStream(input);
- FileOutputStream outputStream = new FileOutputStream(output)) {
- writeToFile(outputStream, inp, isExcludeWorkbookRecords(params), params);
- }
- }
-
- public static void writeToFile(OutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException {
- try (HSSFWorkbook workbook = new HSSFWorkbook(xlsWorkbook)) {
- InternalWorkbook internalWorkbook = workbook.getInternalWorkbook();
- DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid);
-
- StringBuilder builder = new StringBuilder();
- builder.append("<workbook>\n");
- String tab = "\t";
- if (!excludeWorkbookRecords && r != null) {
- r.decode();
- List<EscherRecord> escherRecords = r.getEscherRecords();
- for (EscherRecord record : escherRecords) {
- builder.append(record.toXml(tab));
- }
- }
- List<Integer> sheets = getSheetsIndexes(params, workbook);
- for (Integer i : sheets) {
- HSSFPatriarch p = workbook.getSheetAt(i).getDrawingPatriarch();
- if (p != null) {
- builder.append(tab).append("<sheet").append(i).append(">\n");
- builder.append(p.getBoundAggregate().toXml(tab + "\t"));
- builder.append(tab).append("</sheet").append(i).append(">\n");
- }
- }
- builder.append("</workbook>\n");
- fos.write(builder.toString().getBytes(StringUtil.UTF8));
- }
- }
-
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/BiffViewer.java b/poi/src/main/java/org/apache/poi/hssf/dev/BiffViewer.java
deleted file mode 100644
index 006194d0be..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/BiffViewer.java
+++ /dev/null
@@ -1,713 +0,0 @@
-/* ====================================================================
- 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.dev;
-
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.poi.hssf.record.*;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordInputStream.LeftoverDataException;
-import org.apache.poi.hssf.record.chart.*;
-import org.apache.poi.hssf.record.pivottable.DataItemRecord;
-import org.apache.poi.hssf.record.pivottable.ExtendedPivotTableViewFieldsRecord;
-import org.apache.poi.hssf.record.pivottable.PageItemRecord;
-import org.apache.poi.hssf.record.pivottable.StreamIDRecord;
-import org.apache.poi.hssf.record.pivottable.ViewDefinitionRecord;
-import org.apache.poi.hssf.record.pivottable.ViewFieldsRecord;
-import org.apache.poi.hssf.record.pivottable.ViewSourceRecord;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.RecordFormatException;
-import org.apache.poi.util.StringUtil;
-import org.apache.poi.util.SuppressForbidden;
-
-import static org.apache.logging.log4j.util.Unbox.box;
-
-/**
- * Utility for reading in BIFF8 records and displaying data from them.
- * @see #main
- */
-public final class BiffViewer {
- private static final char[] NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray();
- private static final Logger LOG = LogManager.getLogger(BiffViewer.class);
-
- private BiffViewer() {
- // no instances of this class
- }
-
- /**
- * Create an array of records from an input stream
- *
- * @param is the InputStream from which the records will be obtained
- * @param ps the PrintWriter to output the record data
- * @param recListener the record listener to notify about read records
- * @param dumpInterpretedRecords if {@code true}, the read records will be written to the PrintWriter
- *
- * @exception RecordFormatException on error processing the InputStream
- */
- private static void createRecords(InputStream is, PrintWriter ps, BiffRecordListener recListener, boolean dumpInterpretedRecords)
- throws RecordFormatException {
- RecordInputStream recStream = new RecordInputStream(is);
- while (true) {
- boolean hasNext;
- try {
- hasNext = recStream.hasNextRecord();
- } catch (LeftoverDataException e) {
- LOG.atError().withThrowable(e).log("Discarding {} bytes and continuing", box(recStream.remaining()));
- recStream.readRemainder();
- hasNext = recStream.hasNextRecord();
- }
- if (!hasNext) {
- break;
- }
- recStream.nextRecord();
- if (recStream.getSid() == 0) {
- continue;
- }
- Record record;
- if (dumpInterpretedRecords) {
- record = createRecord (recStream);
- if (record.getSid() == ContinueRecord.sid) {
- continue;
- }
-
- for (String header : recListener.getRecentHeaders()) {
- ps.println(header);
- }
- ps.print(record);
- } else {
- recStream.readRemainder();
- }
- ps.println();
- }
- }
-
-
- /**
- * Essentially a duplicate of RecordFactory. Kept separate as not to screw
- * up non-debug operations.
- *
- */
- private static Record createRecord(RecordInputStream in) {
- switch (in.getSid()) {
- case AreaFormatRecord.sid: return new AreaFormatRecord(in);
- case AreaRecord.sid: return new AreaRecord(in);
- case ArrayRecord.sid: return new ArrayRecord(in);
- case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
- case AxisOptionsRecord.sid: return new AxisOptionsRecord(in);
- case AxisParentRecord.sid: return new AxisParentRecord(in);
- case AxisRecord.sid: return new AxisRecord(in);
- case AxisUsedRecord.sid: return new AxisUsedRecord(in);
- case AutoFilterInfoRecord.sid: return new AutoFilterInfoRecord(in);
- case BOFRecord.sid: return new BOFRecord(in);
- case BackupRecord.sid: return new BackupRecord(in);
- case BarRecord.sid: return new BarRecord(in);
- case BeginRecord.sid: return new BeginRecord(in);
- case BlankRecord.sid: return new BlankRecord(in);
- case BookBoolRecord.sid: return new BookBoolRecord(in);
- case BoolErrRecord.sid: return new BoolErrRecord(in);
- case BottomMarginRecord.sid: return new BottomMarginRecord(in);
- case BoundSheetRecord.sid: return new BoundSheetRecord(in);
- case CFHeaderRecord.sid: return new CFHeaderRecord(in);
- case CFHeader12Record.sid: return new CFHeader12Record(in);
- case CFRuleRecord.sid: return new CFRuleRecord(in);
- case CFRule12Record.sid: return new CFRule12Record(in);
- // TODO Add CF Ex, and remove from UnknownRecord
- case CalcCountRecord.sid: return new CalcCountRecord(in);
- case CalcModeRecord.sid: return new CalcModeRecord(in);
- case CategorySeriesAxisRecord.sid:return new CategorySeriesAxisRecord(in);
- case ChartFormatRecord.sid: return new ChartFormatRecord(in);
- case ChartRecord.sid: return new ChartRecord(in);
- case CodepageRecord.sid: return new CodepageRecord(in);
- case ColumnInfoRecord.sid: return new ColumnInfoRecord(in);
- case ContinueRecord.sid: return new ContinueRecord(in);
- case CountryRecord.sid: return new CountryRecord(in);
- case DBCellRecord.sid: return new DBCellRecord(in);
- case DSFRecord.sid: return new DSFRecord(in);
- case DatRecord.sid: return new DatRecord(in);
- case DataFormatRecord.sid: return new DataFormatRecord(in);
- case DateWindow1904Record.sid: return new DateWindow1904Record(in);
- case DConRefRecord.sid: return new DConRefRecord(in);
- case DefaultColWidthRecord.sid: return new DefaultColWidthRecord(in);
- case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
- case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
- case DeltaRecord.sid: return new DeltaRecord(in);
- case DimensionsRecord.sid: return new DimensionsRecord(in);
- case DrawingGroupRecord.sid: return new DrawingGroupRecord(in);
- case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
- case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
- case DVRecord.sid: return new DVRecord(in);
- case DVALRecord.sid: return new DVALRecord(in);
- case EOFRecord.sid: return new EOFRecord(in);
- case EndRecord.sid: return new EndRecord(in);
- case ExtSSTRecord.sid: return new ExtSSTRecord(in);
- case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
- case ExternSheetRecord.sid: return new ExternSheetRecord(in);
- case ExternalNameRecord.sid: return new ExternalNameRecord(in);
- case FeatRecord.sid: return new FeatRecord(in);
- case FeatHdrRecord.sid: return new FeatHdrRecord(in);
- case FilePassRecord.sid: return new FilePassRecord(in);
- case FileSharingRecord.sid: return new FileSharingRecord(in);
- case FnGroupCountRecord.sid: return new FnGroupCountRecord(in);
- case FontBasisRecord.sid: return new FontBasisRecord(in);
- case FontIndexRecord.sid: return new FontIndexRecord(in);
- case FontRecord.sid: return new FontRecord(in);
- case FooterRecord.sid: return new FooterRecord(in);
- case FormatRecord.sid: return new FormatRecord(in);
- case FormulaRecord.sid: return new FormulaRecord(in);
- case FrameRecord.sid: return new FrameRecord(in);
- case GridsetRecord.sid: return new GridsetRecord(in);
- case GutsRecord.sid: return new GutsRecord(in);
- case HCenterRecord.sid: return new HCenterRecord(in);
- case HeaderRecord.sid: return new HeaderRecord(in);
- case HideObjRecord.sid: return new HideObjRecord(in);
- case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
- case HyperlinkRecord.sid: return new HyperlinkRecord(in);
- case IndexRecord.sid: return new IndexRecord(in);
- case InterfaceEndRecord.sid: return InterfaceEndRecord.create(in);
- case InterfaceHdrRecord.sid: return new InterfaceHdrRecord(in);
- case IterationRecord.sid: return new IterationRecord(in);
- case LabelRecord.sid: return new LabelRecord(in);
- case LabelSSTRecord.sid: return new LabelSSTRecord(in);
- case LeftMarginRecord.sid: return new LeftMarginRecord(in);
- case LegendRecord.sid: return new LegendRecord(in);
- case LineFormatRecord.sid: return new LineFormatRecord(in);
- case LinkedDataRecord.sid: return new LinkedDataRecord(in);
- case MMSRecord.sid: return new MMSRecord(in);
- case MergeCellsRecord.sid: return new MergeCellsRecord(in);
- case MulBlankRecord.sid: return new MulBlankRecord(in);
- case MulRKRecord.sid: return new MulRKRecord(in);
- case NameRecord.sid: return new NameRecord(in);
- case NameCommentRecord.sid: return new NameCommentRecord(in);
- case NoteRecord.sid: return new NoteRecord(in);
- case NumberRecord.sid: return new NumberRecord(in);
- case ObjRecord.sid: return new ObjRecord(in);
- case ObjectLinkRecord.sid: return new ObjectLinkRecord(in);
- case PaletteRecord.sid: return new PaletteRecord(in);
- case PaneRecord.sid: return new PaneRecord(in);
- case PasswordRecord.sid: return new PasswordRecord(in);
- case PasswordRev4Record.sid: return new PasswordRev4Record(in);
- case PlotAreaRecord.sid: return new PlotAreaRecord(in);
- case PlotGrowthRecord.sid: return new PlotGrowthRecord(in);
- case PrecisionRecord.sid: return new PrecisionRecord(in);
- case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
- case PrintHeadersRecord.sid: return new PrintHeadersRecord(in);
- case PrintSetupRecord.sid: return new PrintSetupRecord(in);
- case ProtectRecord.sid: return new ProtectRecord(in);
- case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
- case RKRecord.sid: return new RKRecord(in);
- case RecalcIdRecord.sid: return new RecalcIdRecord(in);
- case RefModeRecord.sid: return new RefModeRecord(in);
- case RefreshAllRecord.sid: return new RefreshAllRecord(in);
- case RightMarginRecord.sid: return new RightMarginRecord(in);
- case RowRecord.sid: return new RowRecord(in);
- case SCLRecord.sid: return new SCLRecord(in);
- case SSTRecord.sid: return new SSTRecord(in);
- case SaveRecalcRecord.sid: return new SaveRecalcRecord(in);
- case SelectionRecord.sid: return new SelectionRecord(in);
- case SeriesIndexRecord.sid: return new SeriesIndexRecord(in);
- case SeriesListRecord.sid: return new SeriesListRecord(in);
- case SeriesRecord.sid: return new SeriesRecord(in);
- case SeriesTextRecord.sid: return new SeriesTextRecord(in);
- case SeriesChartGroupIndexRecord.sid:return new SeriesChartGroupIndexRecord(in);
- case SharedFormulaRecord.sid: return new SharedFormulaRecord(in);
- case SheetPropertiesRecord.sid: return new SheetPropertiesRecord(in);
- case StringRecord.sid: return new StringRecord(in);
- case StyleRecord.sid: return new StyleRecord(in);
- case SupBookRecord.sid: return new SupBookRecord(in);
- case TabIdRecord.sid: return new TabIdRecord(in);
- case TableStylesRecord.sid: return new TableStylesRecord(in);
- case TableRecord.sid: return new TableRecord(in);
- case TextObjectRecord.sid: return new TextObjectRecord(in);
- case TextRecord.sid: return new TextRecord(in);
- case TickRecord.sid: return new TickRecord(in);
- case TopMarginRecord.sid: return new TopMarginRecord(in);
- case UncalcedRecord.sid: return new UncalcedRecord(in);
- case UnitsRecord.sid: return new UnitsRecord(in);
- case UseSelFSRecord.sid: return new UseSelFSRecord(in);
- case VCenterRecord.sid: return new VCenterRecord(in);
- case ValueRangeRecord.sid: return new ValueRangeRecord(in);
- case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
- case WSBoolRecord.sid: return new WSBoolRecord(in);
- case WindowOneRecord.sid: return new WindowOneRecord(in);
- case WindowProtectRecord.sid: return new WindowProtectRecord(in);
- case WindowTwoRecord.sid: return new WindowTwoRecord(in);
- case WriteAccessRecord.sid: return new WriteAccessRecord(in);
- case WriteProtectRecord.sid: return new WriteProtectRecord(in);
-
- // chart
- case CatLabRecord.sid: return new CatLabRecord(in);
- case ChartEndBlockRecord.sid: return new ChartEndBlockRecord(in);
- case ChartEndObjectRecord.sid: return new ChartEndObjectRecord(in);
- case ChartFRTInfoRecord.sid: return new ChartFRTInfoRecord(in);
- case ChartStartBlockRecord.sid: return new ChartStartBlockRecord(in);
- case ChartStartObjectRecord.sid: return new ChartStartObjectRecord(in);
-
- // pivot table
- case StreamIDRecord.sid: return new StreamIDRecord(in);
- case ViewSourceRecord.sid: return new ViewSourceRecord(in);
- case PageItemRecord.sid: return new PageItemRecord(in);
- case ViewDefinitionRecord.sid: return new ViewDefinitionRecord(in);
- case ViewFieldsRecord.sid: return new ViewFieldsRecord(in);
- case DataItemRecord.sid: return new DataItemRecord(in);
- case ExtendedPivotTableViewFieldsRecord.sid: return new ExtendedPivotTableViewFieldsRecord(in);
- }
- return new UnknownRecord(in);
- }
-
- private static final class CommandArgs {
-
- private final boolean _biffhex;
- private final boolean _noint;
- private final boolean _out;
- private final boolean _rawhex;
- private final boolean _noHeader;
- private final File _file;
-
- private CommandArgs(boolean biffhex, boolean noint, boolean out, boolean rawhex, boolean noHeader, File file) {
- _biffhex = biffhex;
- _noint = noint;
- _out = out;
- _rawhex = rawhex;
- _file = file;
- _noHeader = noHeader;
- }
-
- public static CommandArgs parse(String[] args) throws CommandParseException {
- int nArgs = args.length;
- boolean biffhex = false;
- boolean noint = false;
- boolean out = false;
- boolean rawhex = false;
- boolean noheader = false;
- File file = null;
- for (int i=0; i<nArgs; i++) {
- String arg = args[i];
- if (arg.startsWith("--")) {
- if ("--biffhex".equals(arg)) {
- biffhex = true;
- } else if ("--noint".equals(arg)) {
- noint = true;
- } else if ("--out".equals(arg)) {
- out = true;
- } else if ("--escher".equals(arg)) {
- System.setProperty("poi.deserialize.escher", "true");
- } else if ("--rawhex".equals(arg)) {
- rawhex = true;
- } else if ("--noheader".equals(arg)) {
- noheader = true;
- } else {
- throw new CommandParseException("Unexpected option '" + arg + "'");
- }
- continue;
- }
- file = new File(arg);
- if (!file.exists()) {
- throw new CommandParseException("Specified file '" + arg + "' does not exist");
- }
- if (i+1<nArgs) {
- throw new CommandParseException("File name must be the last arg");
- }
- }
- if (file == null) {
- throw new CommandParseException("Biff viewer needs a filename");
- }
- return new CommandArgs(biffhex, noint, out, rawhex, noheader, file);
- }
- boolean shouldDumpBiffHex() {
- return _biffhex;
- }
- boolean shouldDumpRecordInterpretations() {
- return !_noint;
- }
- boolean shouldOutputToFile() {
- return _out;
- }
- boolean shouldOutputRawHexOnly() {
- return _rawhex;
- }
- boolean suppressHeader() {
- return _noHeader;
- }
- public File getFile() {
- return _file;
- }
- }
- private static final class CommandParseException extends Exception {
- CommandParseException(String msg) {
- super(msg);
- }
- }
-
- /**
- * Method main with 1 argument just run straight biffview against given
- * file<p>
- *
- * <b>Usage</b>:<p>
- *
- * BiffViewer [--biffhex] [--noint] [--noescher] [--out] &lt;fileName&gt;<p>
- * BiffViewer --rawhex [--out] &lt;fileName&gt;
- *
- * <table>
- * <caption>BiffViewer options</caption>
- * <tr><td>--biffhex</td><td>show hex dump of each BIFF record</td></tr>
- * <tr><td>--noint</td><td>do not output interpretation of BIFF records</td></tr>
- * <tr><td>--out</td><td>send output to &lt;fileName&gt;.out</td></tr>
- * <tr><td>--rawhex</td><td>output raw hex dump of whole workbook stream</td></tr>
- * <tr><td>--escher</td><td>turn on deserialization of escher records (default is off)</td></tr>
- * <tr><td>--noheader</td><td>do not print record header (default is on)</td></tr>
- * </table>
- *
- * @param args the command line arguments
- *
- * @throws IOException if the file doesn't exist or contained errors
- * @throws CommandParseException if the command line contained errors
- */
- public static void main(String[] args) throws IOException, CommandParseException {
- // args = new String[] { "--out", "", };
- CommandArgs cmdArgs = CommandArgs.parse(args);
-
- try (POIFSFileSystem fs = new POIFSFileSystem(cmdArgs.getFile(), true);
- InputStream is = getPOIFSInputStream(fs);
- PrintWriter pw = getOutputStream(cmdArgs.shouldOutputToFile() ? cmdArgs.getFile().getAbsolutePath() : null)
- ) {
- if (cmdArgs.shouldOutputRawHexOnly()) {
- byte[] data = IOUtils.toByteArray(is);
- HexDump.dump(data, 0, System.out, 0);
- } else {
- boolean dumpInterpretedRecords = cmdArgs.shouldDumpRecordInterpretations();
- boolean dumpHex = cmdArgs.shouldDumpBiffHex();
- runBiffViewer(pw, is, dumpInterpretedRecords, dumpHex, dumpInterpretedRecords,
- cmdArgs.suppressHeader());
- }
- }
- }
-
- static PrintWriter getOutputStream(String outputPath) throws FileNotFoundException {
- // Use the system default encoding when sending to System Out
- OutputStream os = System.out;
- Charset cs = Charset.defaultCharset();
- if (outputPath != null) {
- cs = StringUtil.UTF8;
- os = new FileOutputStream(outputPath + ".out");
- }
- return new PrintWriter(new OutputStreamWriter(os, cs));
- }
-
-
- static InputStream getPOIFSInputStream(POIFSFileSystem fs) throws IOException {
- String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot());
- return fs.createDocumentInputStream(workbookName);
- }
-
- static void runBiffViewer(PrintWriter pw, InputStream is,
- boolean dumpInterpretedRecords, boolean dumpHex, boolean zeroAlignHexDump,
- boolean suppressHeader) {
- BiffRecordListener recListener = new BiffRecordListener(dumpHex ? pw : null, zeroAlignHexDump, suppressHeader);
- is = new BiffDumpingStream(is, recListener);
- createRecords(is, pw, recListener, dumpInterpretedRecords);
- }
-
- private static final class BiffRecordListener implements IBiffRecordListener {
- private final Writer _hexDumpWriter;
- private List<String> _headers;
- private final boolean _zeroAlignEachRecord;
- private final boolean _noHeader;
- private BiffRecordListener(Writer hexDumpWriter, boolean zeroAlignEachRecord, boolean noHeader) {
- _hexDumpWriter = hexDumpWriter;
- _zeroAlignEachRecord = zeroAlignEachRecord;
- _noHeader = noHeader;
- _headers = new ArrayList<>();
- }
-
- @Override
- public void processRecord(int globalOffset, int recordCounter, int sid, int dataSize,
- byte[] data) {
- String header = formatRecordDetails(globalOffset, sid, dataSize, recordCounter);
- if(!_noHeader) {
- _headers.add(header);
- }
- Writer w = _hexDumpWriter;
- if (w != null) {
- try {
- w.write(header);
- w.write(NEW_LINE_CHARS);
- hexDumpAligned(w, data, dataSize+4, globalOffset, _zeroAlignEachRecord);
- w.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- private List<String> getRecentHeaders() {
- List<String> result = _headers;
- _headers = new ArrayList<>();
- return result;
- }
- private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
- return "Offset=" + HexDump.intToHex(globalOffset) + "(" + globalOffset + ")" +
- " recno=" + recordCounter +
- " sid=" + HexDump.shortToHex(sid) +
- " size=" + HexDump.shortToHex(size) + "(" + size + ")";
- }
- }
-
- private interface IBiffRecordListener {
-
- void processRecord(int globalOffset, int recordCounter, int sid, int dataSize, byte[] data);
-
- }
-
- /**
- * Wraps a plain {@link InputStream} and allows BIFF record information to be tapped off
- *
- */
- private static final class BiffDumpingStream extends InputStream {
- private final DataInputStream _is;
- private final IBiffRecordListener _listener;
- private final byte[] _data;
- private int _recordCounter;
- private int _overallStreamPos;
- private int _currentPos;
- private int _currentSize;
- private boolean _innerHasReachedEOF;
-
- private BiffDumpingStream(InputStream is, IBiffRecordListener listener) {
- _is = new DataInputStream(is);
- _listener = listener;
- _data = new byte[RecordInputStream.MAX_RECORD_DATA_SIZE + 4];
- _recordCounter = 0;
- _overallStreamPos = 0;
- _currentSize = 0;
- _currentPos = 0;
- }
-
- @Override
- public int read() throws IOException {
- if (_currentPos >= _currentSize) {
- fillNextBuffer();
- }
- if (_currentPos >= _currentSize) {
- return -1;
- }
- int result = _data[_currentPos] & 0x00FF;
- _currentPos ++;
- _overallStreamPos ++;
- formatBufferIfAtEndOfRec();
- return result;
- }
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (b == null || off < 0 || len < 0 || b.length < off+len) {
- throw new IllegalArgumentException();
- }
- if (_currentPos >= _currentSize) {
- fillNextBuffer();
- }
- if (_currentPos >= _currentSize) {
- return -1;
- }
- final int result = Math.min(len, _currentSize - _currentPos);
- System.arraycopy(_data, _currentPos, b, off, result);
- _currentPos += result;
- _overallStreamPos += result;
- formatBufferIfAtEndOfRec();
- return result;
- }
-
- @Override
- @SuppressForbidden("just delegating the call")
- public int available() throws IOException {
- return _currentSize - _currentPos + _is.available();
- }
- private void fillNextBuffer() throws IOException {
- if (_innerHasReachedEOF) {
- return;
- }
- int b0 = _is.read();
- if (b0 == -1) {
- _innerHasReachedEOF = true;
- return;
- }
- _data[0] = (byte) b0;
- _is.readFully(_data, 1, 3);
- int len = LittleEndian.getShort(_data, 2);
- _is.readFully(_data, 4, len);
- _currentPos = 0;
- _currentSize = len + 4;
- _recordCounter++;
- }
- private void formatBufferIfAtEndOfRec() {
- if (_currentPos != _currentSize) {
- return;
- }
- int dataSize = _currentSize-4;
- int sid = LittleEndian.getShort(_data, 0);
- int globalOffset = _overallStreamPos-_currentSize;
- _listener.processRecord(globalOffset, _recordCounter, sid, dataSize, _data);
- }
- @Override
- public void close() throws IOException {
- _is.close();
- }
- }
-
- private static final int DUMP_LINE_LEN = 16;
- private static final char[] COLUMN_SEPARATOR = " | ".toCharArray();
- /**
- * Hex-dumps a portion of a byte array in typical format, also preserving dump-line alignment
- * @param globalOffset (somewhat arbitrary) used to calculate the addresses printed at the
- * start of each line
- */
- private static void hexDumpAligned(Writer w, byte[] data, int dumpLen, int globalOffset,
- boolean zeroAlignEachRecord) {
- int baseDataOffset = 0;
-
- // perhaps this code should be moved to HexDump
- int globalStart = globalOffset + baseDataOffset;
- int globalEnd = globalOffset + baseDataOffset + dumpLen;
- int startDelta = globalStart % DUMP_LINE_LEN;
- int endDelta = globalEnd % DUMP_LINE_LEN;
- if (zeroAlignEachRecord) {
- endDelta -= startDelta;
- if (endDelta < 0) {
- endDelta += DUMP_LINE_LEN;
- }
- startDelta = 0;
- }
- int startLineAddr;
- int endLineAddr;
- if (zeroAlignEachRecord) {
- endLineAddr = globalEnd - endDelta - (globalStart - startDelta);
- startLineAddr = 0;
- } else {
- startLineAddr = globalStart - startDelta;
- endLineAddr = globalEnd - endDelta;
- }
-
- int lineDataOffset = baseDataOffset - startDelta;
- int lineAddr = startLineAddr;
-
- // output (possibly incomplete) first line
- if (startLineAddr == endLineAddr) {
- hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, endDelta);
- return;
- }
- hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, DUMP_LINE_LEN);
-
- // output all full lines in the middle
- while (true) {
- lineAddr += DUMP_LINE_LEN;
- lineDataOffset += DUMP_LINE_LEN;
- if (lineAddr >= endLineAddr) {
- break;
- }
- hexDumpLine(w, data, lineAddr, lineDataOffset, 0, DUMP_LINE_LEN);
- }
-
-
- // output (possibly incomplete) last line
- if (endDelta != 0) {
- hexDumpLine(w, data, lineAddr, lineDataOffset, 0, endDelta);
- }
- }
-
- private static void hexDumpLine(Writer w, byte[] data, int lineStartAddress, int lineDataOffset, int startDelta, int endDelta) {
- final char[] buf = new char[8+2*COLUMN_SEPARATOR.length+DUMP_LINE_LEN*3-1+DUMP_LINE_LEN+NEW_LINE_CHARS.length];
-
- if (startDelta >= endDelta) {
- throw new IllegalArgumentException("Bad start/end delta");
- }
- int idx=0;
- try {
- writeHex(buf, idx, lineStartAddress, 8);
- idx = arraycopy(COLUMN_SEPARATOR, buf, idx+8);
- // raw hex data
- for (int i=0; i< DUMP_LINE_LEN; i++) {
- if (i>0) {
- buf[idx++] = ' ';
- }
- if (i >= startDelta && i < endDelta) {
- writeHex(buf, idx, data[lineDataOffset+i], 2);
- } else {
- buf[idx] = ' ';
- buf[idx+1] = ' ';
- }
- idx += 2;
- }
- idx = arraycopy(COLUMN_SEPARATOR, buf, idx);
-
- // interpreted ascii
- for (int i=0; i< DUMP_LINE_LEN; i++) {
- char ch = ' ';
- if (i >= startDelta && i < endDelta) {
- ch = getPrintableChar(data[lineDataOffset+i]);
- }
- buf[idx++] = ch;
- }
-
- idx = arraycopy(NEW_LINE_CHARS, buf, idx);
-
- w.write(buf, 0, idx);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static int arraycopy(char[] in, char[] out, int pos) {
- int idx = pos;
- for (char c : in) {
- out[idx++] = c;
- }
- return idx;
- }
-
- private static char getPrintableChar(byte b) {
- char ib = (char) (b & 0x00FF);
- if (ib < 32 || ib > 126) {
- return '.';
- }
- return ib;
- }
-
- private static void writeHex(char[] buf, int startInBuf, int value, int nDigits) {
- int acc = value;
- for(int i=nDigits-1; i>=0; i--) {
- int digit = acc & 0x0F;
- buf[startInBuf+i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10));
- acc >>>= 4;
- }
- }
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/EFBiffViewer.java b/poi/src/main/java/org/apache/poi/hssf/dev/EFBiffViewer.java
deleted file mode 100644
index 8fe58a11c4..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/EFBiffViewer.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/* ====================================================================
- 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.dev;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
-import org.apache.poi.hssf.eventusermodel.HSSFRequest;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-public class EFBiffViewer
-{
- String file;
-
- /** Creates a new instance of EFBiffViewer */
-
- @SuppressWarnings("WeakerAccess")
- public EFBiffViewer() {
- }
-
- public void run() throws IOException {
- try (POIFSFileSystem fs = new POIFSFileSystem(new File(file), true);
- InputStream din = BiffViewer.getPOIFSInputStream(fs)) {
- HSSFRequest req = new HSSFRequest();
-
- req.addListenerForAllRecords(System.out::println);
- HSSFEventFactory factory = new HSSFEventFactory();
-
- factory.processEvents(req, din);
- }
- }
-
- public void setFile(String file)
- {
- this.file = file;
- }
-
- public static void main(String [] args) throws IOException
- {
- if ((args.length == 1) && !args[ 0 ].equals("--help"))
- {
- EFBiffViewer viewer = new EFBiffViewer();
-
- viewer.setFile(args[ 0 ]);
- viewer.run();
- }
- else
- {
- System.out.println("EFBiffViewer");
- System.out.println(
- "Outputs biffview of records based on HSSFEventFactory");
- System.out
- .println("usage: java org.apache.poi.hssf.dev.EBBiffViewer "
- + "filename");
- }
- }
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/FormulaViewer.java b/poi/src/main/java/org/apache/poi/hssf/dev/FormulaViewer.java
deleted file mode 100644
index 20aa00f63b..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/FormulaViewer.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/* ====================================================================
- 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.dev;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
-import org.apache.poi.hssf.model.HSSFFormulaParser;
-import org.apache.poi.hssf.record.FormulaRecord;
-import org.apache.poi.hssf.record.RecordFactory;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.ss.formula.ptg.ExpPtg;
-import org.apache.poi.ss.formula.ptg.FuncPtg;
-import org.apache.poi.ss.formula.ptg.Ptg;
-
-/**
- * FormulaViewer - finds formulas in a BIFF8 file and attempts to read them/display
- * data from them. Only works if Formulas are enabled in "RecordFactory"
- */
-public class FormulaViewer
-{
- private String file;
- private boolean list;
-
- /** Creates new FormulaViewer */
-
- public FormulaViewer()
- {
- }
-
- /**
- * Method run
- *
- * @throws IOException if the file contained errors
- */
- public void run() throws IOException {
- try (POIFSFileSystem fs = new POIFSFileSystem(new File(file), true)) {
- try (InputStream is = BiffViewer.getPOIFSInputStream(fs)) {
- List<org.apache.poi.hssf.record.Record> records = RecordFactory.createRecords(is);
-
- for (org.apache.poi.hssf.record.Record record : records) {
- if (record.getSid() == FormulaRecord.sid) {
- if (list) {
- listFormula((FormulaRecord) record);
- } else {
- parseFormulaRecord((FormulaRecord) record);
- }
- }
- }
- }
- }
- }
-
- private void listFormula(FormulaRecord record) {
- Ptg[] tokens= record.getParsedExpression();
- int numptgs = tokens.length;
- final Ptg lastToken = tokens[numptgs-1];
-
- if (lastToken instanceof ExpPtg) return;
-
- String buf = String.join("~",
- lastToken.toFormulaString(),
- mapToken(lastToken),
- (numptgs > 1 ? mapToken(tokens[numptgs - 2]) : "VALUE"),
- String.valueOf(lastToken instanceof FuncPtg ? numptgs-1 : -1)
- );
-
- System.out.println(buf);
- }
-
- private static String mapToken(Ptg token) {
- switch (token.getPtgClass()) {
- case Ptg.CLASS_REF :
- return "REF";
- case Ptg.CLASS_VALUE :
- return "VALUE";
- case Ptg.CLASS_ARRAY :
- return "ARRAY";
- default:
- throwInvalidRVAToken(token);
- return "";
- }
- }
-
- /**
- * Method parseFormulaRecord
- *
- * @param record the record to be parsed
- */
- public void parseFormulaRecord(FormulaRecord record)
- {
- System.out.println("==============================");
- System.out.print("row = " + record.getRow());
- System.out.println(", col = " + record.getColumn());
- System.out.println("value = " + record.getValue());
- System.out.print("xf = " + record.getXFIndex());
- System.out.print(", number of ptgs = "
- + record.getParsedExpression().length);
- System.out.println(", options = " + record.getOptions());
- System.out.println("RPN List = "+formulaString(record));
- System.out.println("Formula text = "+ composeFormula(record));
- }
-
- private String formulaString(FormulaRecord record) {
-
- StringBuilder buf = new StringBuilder();
- Ptg[] tokens = record.getParsedExpression();
- for (Ptg token : tokens) {
- buf.append( token.toFormulaString());
- switch (token.getPtgClass()) {
- case Ptg.CLASS_REF :
- buf.append("(R)");
- break;
- case Ptg.CLASS_VALUE :
- buf.append("(V)");
- break;
- case Ptg.CLASS_ARRAY :
- buf.append("(A)");
- break;
- default:
- throwInvalidRVAToken(token);
- }
- buf.append(' ');
- }
- return buf.toString();
- }
-
- private static void throwInvalidRVAToken(Ptg token) {
- throw new IllegalStateException("Invalid RVA type (" + token.getPtgClass() + "). This should never happen.");
- }
-
-
- private static String composeFormula(FormulaRecord record)
- {
- return HSSFFormulaParser.toFormulaString(null, record.getParsedExpression());
- }
-
- /**
- * Method setFile
- *
- * @param file the file to process
- */
-
- public void setFile(String file)
- {
- this.file = file;
- }
-
- public void setList(boolean list) {
- this.list=list;
- }
-
- /**
- * Method main
- *
- * pass me a filename and I'll try and parse the formulas from it
- *
- * @param args pass one argument with the filename or --help
- * @throws IOException if the file can't be read or contained errors
- */
- public static void main(String[] args) throws IOException
- {
- if ((args == null) || (args.length >2 )
- || args[ 0 ].equals("--help"))
- {
- System.out.println(
- "FormulaViewer .8 proof that the devil lies in the details (or just in BIFF8 files in general)");
- System.out.println("usage: Give me a big fat file name");
- } else if (args[0].equals("--listFunctions")) { // undocumented attribute to research functions!~
- FormulaViewer viewer = new FormulaViewer();
- viewer.setFile(args[1]);
- viewer.setList(true);
- viewer.run();
- }
- else
- {
- FormulaViewer viewer = new FormulaViewer();
-
- viewer.setFile(args[ 0 ]);
- viewer.run();
- }
- }
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/ReSave.java b/poi/src/main/java/org/apache/poi/hssf/dev/ReSave.java
deleted file mode 100644
index 2254ed58fe..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/ReSave.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.dev;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-
-/**
- * Utility to test that POI produces readable output
- * after re-saving xls files.
- *
- * Usage: ReSave [-dg] input.xls
- * -dg initialize drawings, causes to re-build escher aggregates in all sheets
- * -bos only write to memory instead of a file
- */
-public class ReSave {
- public static void main(String[] args) throws Exception {
- boolean initDrawing = false;
- boolean saveToMemory = false;
- for(String filename : args) {
- if(filename.equals("-dg")) {
- initDrawing = true;
- } else if(filename.equals("-bos")) {
- saveToMemory = true;
- } else {
- System.out.print("reading " + filename + "...");
- try (FileInputStream is = new FileInputStream(filename);
- HSSFWorkbook wb = new HSSFWorkbook(is)) {
- System.out.println("done");
-
- for(int i = 0; i < wb.getNumberOfSheets(); i++){
- HSSFSheet sheet = wb.getSheetAt(i);
- if(initDrawing) {
- /*HSSFPatriarch dg =*/ sheet.getDrawingPatriarch();
- }
- }
-
- String outputFile = filename.replace(".xls", "-saved.xls");
- if (!saveToMemory) {
- System.out.print("saving to " + outputFile + "...");
- }
-
- try (OutputStream os = saveToMemory ? new UnsynchronizedByteArrayOutputStream() : new FileOutputStream(outputFile)) {
- wb.write(os);
- }
- System.out.println("done");
- }
- }
- }
- }
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/RecordLister.java b/poi/src/main/java/org/apache/poi/hssf/dev/RecordLister.java
deleted file mode 100644
index fd6c8ef111..0000000000
--- a/poi/src/main/java/org/apache/poi/hssf/dev/RecordLister.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/* ====================================================================
- 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.dev;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.poi.hssf.record.ContinueRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordFactory;
-import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-/**
- * This is a low-level debugging class, which simply prints
- * out what records come in what order.
- * Most people will want to use {@link BiffViewer} or
- * {@link EFBiffViewer}, but this can be handy when
- * trying to make sense of {@link ContinueRecord}
- * special cases.
- *
- * Output is of the form:
- * SID - Length - Type (if known)
- * byte0 byte1 byte2 byte3 .... byte(n-4) byte(n-3) byte(n-2) byte(n-1)
- */
-public class RecordLister
-{
- String file;
-
- public RecordLister()
- {
- }
-
- public void run() throws IOException {
- try (POIFSFileSystem fs = new POIFSFileSystem(new File(file), true);
- InputStream din = BiffViewer.getPOIFSInputStream(fs)) {
- RecordInputStream rinp = new RecordInputStream(din);
-
- while (rinp.hasNextRecord()) {
- int sid = rinp.getNextSid();
- rinp.nextRecord();
-
- int size = rinp.available();
- Class<? extends Record> clz = RecordFactory.getRecordClass(sid);
-
- System.out.print(
- formatSID(sid) +
- " - " +
- formatSize(size) +
- " bytes"
- );
- if (clz != null) {
- System.out.print(" \t");
- System.out.print(clz.getName().replace("org.apache.poi.hssf.record.", ""));
- }
- System.out.println();
-
- byte[] data = rinp.readRemainder();
- if (data.length > 0) {
- System.out.print(" ");
- System.out.println(formatData(data));
- }
- }
- }
- }
-
- private static String formatSID(int sid) {
- String hex = Integer.toHexString(sid);
- String dec = Integer.toString(sid);
-
- StringBuilder s = new StringBuilder();
- s.append("0x");
- for(int i=hex.length(); i<4; i++) {
- s.append('0');
- }
- s.append(hex);
-
- s.append(" (");
- for(int i=dec.length(); i<4; i++) {
- s.append('0');
- }
- s.append(dec);
- s.append(")");
-
- return s.toString();
- }
- private static String formatSize(int size) {
- String hex = Integer.toHexString(size);
- String dec = Integer.toString(size);
-
- StringBuilder s = new StringBuilder();
- for(int i=hex.length(); i<3; i++) {
- s.append('0');
- }
- s.append(hex);
-
- s.append(" (");
- for(int i=dec.length(); i<3; i++) {
- s.append('0');
- }
- s.append(dec);
- s.append(")");
-
- return s.toString();
- }
- private static String formatData(byte[] data) {
- if(data == null || data.length == 0)
- return "";
-
- // If possible, do first 4 and last 4 bytes
- StringBuilder s = new StringBuilder();
- if(data.length > 9) {
- s.append(byteToHex(data[0]));
- s.append(' ');
- s.append(byteToHex(data[1]));
- s.append(' ');
- s.append(byteToHex(data[2]));
- s.append(' ');
- s.append(byteToHex(data[3]));
- s.append(' ');
-
- s.append(" .... ");
-
- s.append(' ');
- s.append(byteToHex(data[data.length-4]));
- s.append(' ');
- s.append(byteToHex(data[data.length-3]));
- s.append(' ');
- s.append(byteToHex(data[data.length-2]));
- s.append(' ');
- s.append(byteToHex(data[data.length-1]));
- } else {
- for (byte aData : data) {
- s.append(byteToHex(aData));
- s.append(' ');
- }
- }
-
- return s.toString();
- }
- private static String byteToHex(byte b) {
- int i = b;
- if(i<0) {
- i += 256;
- }
- String s = Integer.toHexString(i);
- if(i < 16) {
- return "0" + s;
- }
- return s;
- }
-
- public void setFile(String file)
- {
- this.file = file;
- }
-
- public static void main(String [] args) throws IOException
- {
- if ((args.length == 1) && !args[ 0 ].equals("--help"))
- {
- RecordLister viewer = new RecordLister();
-
- viewer.setFile(args[ 0 ]);
- viewer.run();
- }
- else
- {
- System.out.println("RecordLister");
- System.out.println(
- "Outputs the summary of the records in file order");
- System.out
- .println("usage: java org.apache.poi.hssf.dev.RecordLister "
- + "filename");
- }
- }
-}
diff --git a/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java b/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
index 6cb3aacb5e..62535e50b8 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java
@@ -22,7 +22,6 @@ import java.io.InputStream;
import java.util.Locale;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
-import org.apache.poi.hssf.dev.BiffViewer;
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.crypt.EncryptionInfo;
@@ -52,7 +51,7 @@ public final class RecordInputStream implements LittleEndianInput {
private static final byte[] EMPTY_BYTE_ARRAY = { };
/**
- * For use in {@link BiffViewer} which may construct {@link Record}s that don't completely
+ * For use in BiffViewer which may construct {@link Record}s that don't completely
* read all available data. This exception should never be thrown otherwise.
*/
public static final class LeftoverDataException extends RuntimeException {
@@ -141,15 +140,6 @@ public final class RecordInputStream implements LittleEndianInput {
_nextSid = readNextSid();
}
- static LittleEndianInput getLEI(InputStream is) {
- if (is instanceof LittleEndianInput) {
- // accessing directly is an optimisation
- return (LittleEndianInput) is;
- }
- // less optimal, but should work OK just the same. Often occurs in junit tests.
- return new LittleEndianInputStream(is);
- }
-
/**
* @return the number of bytes available in the current BIFF record
* @see #remaining()
diff --git a/poi/src/main/java/org/apache/poi/util/CloseIgnoringInputStream.java b/poi/src/main/java/org/apache/poi/util/CloseIgnoringInputStream.java
deleted file mode 100644
index 82247b13af..0000000000
--- a/poi/src/main/java/org/apache/poi/util/CloseIgnoringInputStream.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/* ====================================================================
- 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.util;
-
-import java.io.FilterInputStream;
-import java.io.InputStream;
-
-/**
- * A wrapper around an {@link InputStream}, which
- * ignores close requests made to it.
- *
- * Useful with {@link org.apache.poi.poifs.filesystem.POIFSFileSystem}, where you want
- * to control the close yourself.
- */
-public class CloseIgnoringInputStream extends FilterInputStream {
- public CloseIgnoringInputStream(InputStream in) {
- super(in);
- }
-
- public void close() {
- // Does nothing and ignores closing the wrapped stream
- }
-}
diff --git a/poi/src/main/java9/module-info.class b/poi/src/main/java9/module-info.class
index 7ca4ecd882..a27868cdf4 100644
--- a/poi/src/main/java9/module-info.class
+++ b/poi/src/main/java9/module-info.class
Binary files differ
diff --git a/poi/src/main/java9/module-info.java b/poi/src/main/java9/module-info.java
index 6e7fe11df8..389b4b5a85 100644
--- a/poi/src/main/java9/module-info.java
+++ b/poi/src/main/java9/module-info.java
@@ -48,7 +48,6 @@ module org.apache.poi.poi {
exports org.apache.poi.hpsf.extractor;
exports org.apache.poi.hpsf.wellknown;
exports org.apache.poi.hssf;
- exports org.apache.poi.hssf.dev;
exports org.apache.poi.hssf.eventmodel;
exports org.apache.poi.hssf.eventusermodel;
exports org.apache.poi.hssf.eventusermodel.dummyrecord;
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/BiffDumpingStream.java b/poi/src/test/java/org/apache/poi/hssf/dev/BiffDumpingStream.java
new file mode 100644
index 0000000000..0591b359cb
--- /dev/null
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/BiffDumpingStream.java
@@ -0,0 +1,121 @@
+/* ====================================================================
+ 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.dev;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.SuppressForbidden;
+
+/**
+ * Wraps a plain {@link InputStream} and allows BIFF record information to be tapped off
+ */
+final class BiffDumpingStream extends InputStream {
+
+ interface IBiffRecordListener {
+ void processRecord(int globalOffset, int recordCounter, int sid, int dataSize, byte[] data) throws IOException;
+ }
+
+
+ private final DataInputStream _is;
+ private final IBiffRecordListener _listener;
+ private final byte[] _data;
+ private int _recordCounter;
+ private int _overallStreamPos;
+ private int _currentPos;
+ private int _currentSize;
+ private boolean _innerHasReachedEOF;
+ private final byte[] oneByte = new byte[1];
+
+ BiffDumpingStream(InputStream is, IBiffRecordListener listener) {
+ _is = new DataInputStream(is);
+ _listener = listener;
+ _data = new byte[RecordInputStream.MAX_RECORD_DATA_SIZE + 4];
+ _recordCounter = 0;
+ _overallStreamPos = 0;
+ _currentSize = 0;
+ _currentPos = 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int ret = read(oneByte, 0, 1);
+ return (ret == -1) ? -1 : oneByte[0] & 0x00FF;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null || off < 0 || len < 0 || b.length < off + len) {
+ throw new IllegalArgumentException();
+ }
+ if (_currentPos >= _currentSize) {
+ fillNextBuffer();
+ }
+ if (_currentPos >= _currentSize) {
+ return -1;
+ }
+ final int result = Math.min(len, _currentSize - _currentPos);
+ System.arraycopy(_data, _currentPos, b, off, result);
+ _currentPos += result;
+ _overallStreamPos += result;
+ formatBufferIfAtEndOfRec();
+ return result;
+ }
+
+ @Override
+ @SuppressForbidden("just delegating the call")
+ public int available() throws IOException {
+ return _currentSize - _currentPos + _is.available();
+ }
+
+ private void fillNextBuffer() throws IOException {
+ if (_innerHasReachedEOF) {
+ return;
+ }
+ int b0 = _is.read();
+ if (b0 == -1) {
+ _innerHasReachedEOF = true;
+ return;
+ }
+ _data[0] = (byte) b0;
+ _is.readFully(_data, 1, 3);
+ int len = LittleEndian.getShort(_data, 2);
+ _is.readFully(_data, 4, len);
+ _currentPos = 0;
+ _currentSize = len + 4;
+ _recordCounter++;
+ }
+
+ private void formatBufferIfAtEndOfRec() throws IOException {
+ if (_currentPos != _currentSize) {
+ return;
+ }
+ int dataSize = _currentSize - 4;
+ int sid = LittleEndian.getShort(_data, 0);
+ int globalOffset = _overallStreamPos - _currentSize;
+ _listener.processRecord(globalOffset, _recordCounter, sid, dataSize, _data);
+ }
+
+ @Override
+ public void close() throws IOException {
+ _is.close();
+ }
+}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/BiffViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/BiffViewer.java
new file mode 100644
index 0000000000..274c140f17
--- /dev/null
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/BiffViewer.java
@@ -0,0 +1,342 @@
+/* ====================================================================
+ 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.dev;
+
+import static org.apache.logging.log4j.util.Unbox.box;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.output.CloseShieldOutputStream;
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.hssf.dev.BiffDumpingStream.IBiffRecordListener;
+import org.apache.poi.hssf.record.ContinueRecord;
+import org.apache.poi.hssf.record.HSSFRecordTypes;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.RecordInputStream.LeftoverDataException;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.RecordFormatException;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * Utility for reading in BIFF8 records and displaying data from them.
+ */
+public final class BiffViewer {
+ private static final char[] NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray();
+ private static final Logger LOG = LogManager.getLogger(BiffViewer.class);
+ private static final String ESCHER_SERIALIZE = "poi.deserialize.escher";
+ private static final int DUMP_LINE_LEN = 16;
+ private static final char[] COLUMN_SEPARATOR = " | ".toCharArray();
+
+ private boolean biffHex;
+ private boolean interpretRecords = true;
+ private boolean rawHexOnly;
+ private boolean noHeader = true;
+ private boolean zeroAlignRecord = true;
+ private final List<String> _headers = new ArrayList<>();
+
+
+ /**
+ * show hex dump of each BIFF record
+ */
+ public void setDumpBiffHex(boolean biffhex) {
+ this.biffHex = biffhex;
+ }
+
+ /**
+ * output interpretation of BIFF records
+ */
+ public void setInterpretRecords(boolean interpretRecords) {
+ this.interpretRecords = interpretRecords;
+ }
+
+ /**
+ * output raw hex dump of whole workbook stream
+ */
+ public void setOutputRawHexOnly(boolean rawhex) {
+ this.rawHexOnly = rawhex;
+ }
+
+ /**
+ * do not print record header - default is on
+ */
+ public void setSuppressHeader(boolean noHeader) {
+ this.noHeader = noHeader;
+ }
+
+ /**
+ * turn on deserialization of escher records (default is off)
+ */
+ public void setSerializeEscher(boolean serialize) {
+ if (serialize) {
+ System.setProperty(ESCHER_SERIALIZE, "true");
+ } else {
+ System.clearProperty(ESCHER_SERIALIZE);
+ }
+ }
+
+ public void setZeroAlignRecord(boolean zeroAlignRecord) {
+ this.zeroAlignRecord = zeroAlignRecord;
+ }
+
+ public void parse(File file) throws IOException {
+ parse(file, System.out);
+ }
+
+ public void parse(File file, OutputStream os) throws IOException {
+ try (POIFSFileSystem fs = new POIFSFileSystem(file, true);
+ InputStream is = getPOIFSInputStream(fs);
+ PrintWriter pw = wrap(os)
+ ) {
+ if (rawHexOnly) {
+ byte[] data = IOUtils.toByteArray(is);
+ HexDump.dump(data, 0, System.out, 0);
+ } else {
+ IBiffRecordListener recListener = (globalOffset, recordCounter, sid, dataSize, data) -> {
+ String header = formatRecordDetails(globalOffset, sid, dataSize, recordCounter);
+ if (!noHeader) {
+ _headers.add(header);
+ }
+ if (biffHex) {
+ pw.write(header);
+ pw.write(NEW_LINE_CHARS);
+ hexDumpAligned(pw, data, dataSize+4, globalOffset);
+ pw.flush();
+ }
+ };
+
+ try (InputStream is2 = new BiffDumpingStream(is, recListener)) {
+ createRecords(is2, pw);
+ }
+ }
+ }
+ }
+
+ private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
+ return "Offset=" + HexDump.intToHex(globalOffset) + "(" + globalOffset + ")" +
+ " recno=" + recordCounter +
+ " sid=" + HexDump.shortToHex(sid) +
+ " size=" + HexDump.shortToHex(size) + "(" + size + ")";
+ }
+
+ /**
+ * Create an array of records from an input stream
+ *
+ * @param is the InputStream from which the records will be obtained
+ * @param ps the PrintWriter to output the record data
+ *
+ * @exception RecordFormatException on error processing the InputStream
+ */
+ private void createRecords(InputStream is, PrintWriter ps) throws RecordFormatException {
+ RecordInputStream recStream = new RecordInputStream(is);
+ while (true) {
+ _headers.clear();
+ boolean hasNext;
+ try {
+ hasNext = recStream.hasNextRecord();
+ } catch (LeftoverDataException e) {
+ LOG.atError().withThrowable(e).log("Discarding {} bytes and continuing", box(recStream.remaining()));
+ recStream.readRemainder();
+ hasNext = recStream.hasNextRecord();
+ }
+ if (!hasNext) {
+ break;
+ }
+ recStream.nextRecord();
+ if (recStream.getSid() == 0) {
+ continue;
+ }
+ Record record;
+ if (interpretRecords) {
+ record = HSSFRecordTypes.forSID(recStream.getSid()).getRecordConstructor().apply(recStream);
+ if (record.getSid() == ContinueRecord.sid) {
+ continue;
+ }
+
+ _headers.forEach(ps::println);
+ ps.print(record);
+ } else {
+ recStream.readRemainder();
+ }
+ ps.println();
+ }
+ }
+
+ private static PrintWriter wrap(OutputStream os) {
+ final OutputStream osOut;
+ final Charset cs;
+
+ if (os == null) {
+ cs = Charset.defaultCharset();
+ osOut = NullOutputStream.NULL_OUTPUT_STREAM;
+ } else if (os == System.out) {
+ // Use the system default encoding when sending to System Out
+ cs = Charset.defaultCharset();
+ osOut = CloseShieldOutputStream.wrap(System.out);
+ } else {
+ cs = StringUtil.UTF8;
+ osOut = os;
+ }
+ return new PrintWriter(new OutputStreamWriter(osOut, cs));
+ }
+
+
+ static InputStream getPOIFSInputStream(POIFSFileSystem fs) throws IOException {
+ String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot());
+ return fs.createDocumentInputStream(workbookName);
+ }
+
+
+ /**
+ * Hex-dumps a portion of a byte array in typical format, also preserving dump-line alignment
+ * @param globalOffset (somewhat arbitrary) used to calculate the addresses printed at the
+ * start of each line
+ */
+ private void hexDumpAligned(Writer w, byte[] data, int dumpLen, int globalOffset) {
+ int baseDataOffset = 0;
+
+ // perhaps this code should be moved to HexDump
+ int globalStart = globalOffset + baseDataOffset;
+ int globalEnd = globalOffset + baseDataOffset + dumpLen;
+ int startDelta = globalStart % DUMP_LINE_LEN;
+ int endDelta = globalEnd % DUMP_LINE_LEN;
+ if (zeroAlignRecord) {
+ endDelta -= startDelta;
+ if (endDelta < 0) {
+ endDelta += DUMP_LINE_LEN;
+ }
+ startDelta = 0;
+ }
+ int startLineAddr;
+ int endLineAddr;
+ if (zeroAlignRecord) {
+ endLineAddr = globalEnd - endDelta - (globalStart - startDelta);
+ startLineAddr = 0;
+ } else {
+ startLineAddr = globalStart - startDelta;
+ endLineAddr = globalEnd - endDelta;
+ }
+
+ int lineDataOffset = baseDataOffset - startDelta;
+ int lineAddr = startLineAddr;
+
+ // output (possibly incomplete) first line
+ if (startLineAddr == endLineAddr) {
+ hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, endDelta);
+ return;
+ }
+ hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, DUMP_LINE_LEN);
+
+ // output all full lines in the middle
+ while (true) {
+ lineAddr += DUMP_LINE_LEN;
+ lineDataOffset += DUMP_LINE_LEN;
+ if (lineAddr >= endLineAddr) {
+ break;
+ }
+ hexDumpLine(w, data, lineAddr, lineDataOffset, 0, DUMP_LINE_LEN);
+ }
+
+
+ // output (possibly incomplete) last line
+ if (endDelta != 0) {
+ hexDumpLine(w, data, lineAddr, lineDataOffset, 0, endDelta);
+ }
+ }
+
+ private static void hexDumpLine(Writer w, byte[] data, int lineStartAddress, int lineDataOffset, int startDelta, int endDelta) {
+ final char[] buf = new char[8+2*COLUMN_SEPARATOR.length+DUMP_LINE_LEN*3-1+DUMP_LINE_LEN+NEW_LINE_CHARS.length];
+
+ if (startDelta >= endDelta) {
+ throw new IllegalArgumentException("Bad start/end delta");
+ }
+ int idx=0;
+ try {
+ writeHex(buf, idx, lineStartAddress, 8);
+ idx = arraycopy(COLUMN_SEPARATOR, buf, idx+8);
+ // raw hex data
+ for (int i=0; i< DUMP_LINE_LEN; i++) {
+ if (i>0) {
+ buf[idx++] = ' ';
+ }
+ if (i >= startDelta && i < endDelta) {
+ writeHex(buf, idx, data[lineDataOffset+i], 2);
+ } else {
+ buf[idx] = ' ';
+ buf[idx+1] = ' ';
+ }
+ idx += 2;
+ }
+ idx = arraycopy(COLUMN_SEPARATOR, buf, idx);
+
+ // interpreted ascii
+ for (int i=0; i< DUMP_LINE_LEN; i++) {
+ char ch = ' ';
+ if (i >= startDelta && i < endDelta) {
+ ch = getPrintableChar(data[lineDataOffset+i]);
+ }
+ buf[idx++] = ch;
+ }
+
+ idx = arraycopy(NEW_LINE_CHARS, buf, idx);
+
+ w.write(buf, 0, idx);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static int arraycopy(char[] in, char[] out, int pos) {
+ int idx = pos;
+ for (char c : in) {
+ out[idx++] = c;
+ }
+ return idx;
+ }
+
+ private static char getPrintableChar(byte b) {
+ char ib = (char) (b & 0x00FF);
+ if (ib < 32 || ib > 126) {
+ return '.';
+ }
+ return ib;
+ }
+
+ private static void writeHex(char[] buf, int startInBuf, int value, int nDigits) {
+ int acc = value;
+ for(int i=nDigits-1; i>=0; i--) {
+ int digit = acc & 0x0F;
+ buf[startInBuf+i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10));
+ acc >>>= 4;
+ }
+ }
+}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java
index fae6c5f766..3613d1b68c 100644
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java
@@ -20,11 +20,25 @@ import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM;
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.hssf.model.InternalWorkbook;
+import org.apache.poi.hssf.record.DrawingGroupRecord;
import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.usermodel.HSSFPatriarch;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.util.StringUtil;
class TestBiffDrawingToXml extends BaseTestIteratingXLS {
@@ -42,10 +56,69 @@ class TestBiffDrawingToXml extends BaseTestIteratingXLS {
return excludes;
}
+ // output sheets with specified name
+ private static final String[] SHEET_NAMES = {};
+
+ // output sheets with specified indexes
+ private static final int[] SHEET_IDX = {};
+
+ // exclude workbook-level records
+ private static final boolean EXCLUDE_WORKBOOK = false;
+
+
@Override
void runOneFile(File pFile) throws Exception {
- try (InputStream wb = new FileInputStream(pFile)) {
- BiffDrawingToXml.writeToFile(NULL_OUTPUT_STREAM, wb, false, new String[0]);
+ try (InputStream inp = new FileInputStream(pFile);
+ OutputStream outputStream = NULL_OUTPUT_STREAM) {
+ writeToFile(outputStream, inp);
+ }
+ }
+
+ public static void writeToFile(OutputStream fos, InputStream xlsWorkbook) throws IOException {
+ try (HSSFWorkbook workbook = new HSSFWorkbook(xlsWorkbook)) {
+ InternalWorkbook internalWorkbook = workbook.getInternalWorkbook();
+ DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid);
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("<workbook>\n");
+ String tab = "\t";
+ if (!EXCLUDE_WORKBOOK && r != null) {
+ r.decode();
+ List<EscherRecord> escherRecords = r.getEscherRecords();
+ for (EscherRecord record : escherRecords) {
+ builder.append(record.toXml(tab));
+ }
+ }
+ int i = 0;
+ for (HSSFSheet sheet : getSheets(workbook)) {
+ HSSFPatriarch p = sheet.getDrawingPatriarch();
+ if (p != null) {
+ builder.append(tab).append("<sheet").append(i).append(">\n");
+ builder.append(p.getBoundAggregate().toXml(tab + "\t"));
+ builder.append(tab).append("</sheet").append(i).append(">\n");
+ i++;
+ }
+ }
+ builder.append("</workbook>\n");
+ fos.write(builder.toString().getBytes(StringUtil.UTF8));
}
}
+
+ private static List<HSSFSheet> getSheets(HSSFWorkbook workbook) {
+ List<Integer> sheetIdx = Arrays.stream(SHEET_IDX).boxed().collect(Collectors.toList());
+ List<String> sheetNms = Arrays.stream(SHEET_NAMES).collect(Collectors.toList());
+
+ List<HSSFSheet> list = new ArrayList<>();
+
+ for (Sheet sheet : workbook) {
+ if ((sheetIdx.isEmpty() && sheetNms.isEmpty()) ||
+ sheetIdx.contains(workbook.getSheetIndex(sheet)) ||
+ sheetNms.contains(sheet.getSheetName())
+ ) {
+ list.add((HSSFSheet)sheet);
+ }
+ }
+
+ return list;
+ }
}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java
index 790ca14b58..1059213502 100644
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java
@@ -16,17 +16,10 @@
==================================================================== */
package org.apache.poi.hssf.dev;
-import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM;
-
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.util.Map;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.RecordFormatException;
class TestBiffViewer extends BaseTestIteratingXLS {
@@ -52,19 +45,10 @@ class TestBiffViewer extends BaseTestIteratingXLS {
@Override
void runOneFile(File fileIn) throws IOException {
- try (POIFSFileSystem fs = new POIFSFileSystem(fileIn, true);
- InputStream is = BiffViewer.getPOIFSInputStream(fs)) {
- // use a NullOutputStream to not write the bytes anywhere for best runtime
- PrintWriter dummy = new PrintWriter(new OutputStreamWriter(NULL_OUTPUT_STREAM, LocaleUtil.CHARSET_1252));
- BiffViewer.runBiffViewer(dummy, is, true, true, true, false);
- }
+ BiffViewer bv = new BiffViewer();
+ bv.setInterpretRecords(true);
+ bv.setDumpBiffHex(true);
+ bv.parse(fileIn, null);
}
-// @Test
-// @Disabled("only used for manual tests")
-// @SuppressWarnings("java:S2699")
-// void testOneFile() throws Exception {
-// POIDataSamples samples = POIDataSamples.getSpreadSheetInstance();
-// runOneFile(samples.getFile("43493.xls"));
-// }
}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java
index 4924511648..bcd91e74d7 100644
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java
@@ -18,16 +18,16 @@ package org.apache.poi.hssf.dev;
import java.io.File;
import java.io.IOException;
-import java.io.PrintStream;
+import java.io.InputStream;
import java.util.Map;
import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.commons.io.output.NullPrintStream;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.junit.jupiter.api.Assertions;
-@ResourceLock(Resources.SYSTEM_OUT)
class TestEFBiffViewer extends BaseTestIteratingXLS {
@Override
protected Map<String, Class<? extends Throwable>> getExcludes() {
@@ -41,27 +41,19 @@ class TestEFBiffViewer extends BaseTestIteratingXLS {
excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);
// "Buffer overrun"
- excludes.put("XRefCalc.xls", RuntimeException.class);
+ // excludes.put("XRefCalc.xls", RuntimeException.class);
return excludes;
}
@Override
void runOneFile(File fileIn) throws IOException {
- PrintStream save = System.out;
- try {
- // redirect standard out during the test to avoid spamming the console with output
- System.setOut(new NullPrintStream());
+ HSSFRequest req = new HSSFRequest();
+ req.addListenerForAllRecords(Assertions::assertNotNull);
+ HSSFEventFactory factory = new HSSFEventFactory();
- EFBiffViewer.main(new String[] { fileIn.getAbsolutePath() });
- } finally {
- System.setOut(save);
+ try (POIFSFileSystem fs = new POIFSFileSystem(fileIn, true);
+ InputStream din = BiffViewer.getPOIFSInputStream(fs)) {
+ factory.processEvents(req, din);
}
}
-
- //@Test
- void testFile() throws IOException {
- EFBiffViewer viewer = new EFBiffViewer();
- viewer.setFile(new File("test-data/spreadsheet/59074.xls").getAbsolutePath());
- viewer.run();
- }
}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java
index d8e50f134b..177871e25b 100644
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java
@@ -16,19 +16,24 @@
==================================================================== */
package org.apache.poi.hssf.dev;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
-
import java.io.File;
-import java.io.PrintStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import org.apache.commons.io.output.NullWriter;
import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.RecordFactory;
import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.commons.io.output.NullPrintStream;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.formula.ptg.FuncPtg;
+import org.apache.poi.ss.formula.ptg.Ptg;
-@ResourceLock(Resources.SYSTEM_OUT)
class TestFormulaViewer extends BaseTestIteratingXLS {
@Override
protected Map<String, Class<? extends Throwable>> getExcludes() {
@@ -42,27 +47,106 @@ class TestFormulaViewer extends BaseTestIteratingXLS {
return excludes;
}
+ private final boolean doListFormula = true;
+
@Override
void runOneFile(File fileIn) throws Exception {
- PrintStream save = System.out;
+ // replace with System.out for manual tests
+ PrintWriter out = new PrintWriter(new NullWriter());
+
+ final Function<FormulaRecord, String> lister = (doListFormula) ? this::listFormula : this::parseFormulaRecord;
+
+ try (POIFSFileSystem fs = new POIFSFileSystem(fileIn, true);
+ InputStream is = BiffViewer.getPOIFSInputStream(fs)) {
+ RecordFactory.createRecords(is).stream()
+ .filter(r -> r.getSid() == FormulaRecord.sid)
+ .map(FormulaRecord.class::cast)
+ .map(lister)
+ .map(Objects::nonNull)
+ .forEach(out::println);
+ }
+ }
+
+ private String listFormula(FormulaRecord record) {
+ Ptg[] tokens = record.getParsedExpression();
+ int numptgs = tokens.length;
+ final Ptg lastToken = tokens[numptgs - 1];
+
+ String fmlStr;
try {
- // redirect standard out during the test to avoid spamming the console with output
- System.setOut(new NullPrintStream());
-
- FormulaViewer viewer = new FormulaViewer();
- viewer.setFile(fileIn.getAbsolutePath());
- viewer.setList(true);
- viewer.run();
- } catch (RuntimeException re) {
- String m = re.getMessage();
- if (m.startsWith("toFormulaString") || m.startsWith("3D references")) {
- // TODO: fix those cases, but ignore them for now ...
- assumeTrue(true);
- } else {
- throw re;
+ fmlStr = lastToken.toFormulaString();
+ } catch (Exception ignored) {
+ return null;
+ }
+
+ return String.join("~",
+ fmlStr,
+ mapToken(lastToken),
+ (numptgs > 1 ? mapToken(tokens[numptgs - 2]) : "VALUE"),
+ String.valueOf(lastToken instanceof FuncPtg ? numptgs - 1 : -1)
+ );
+ }
+
+ private static String mapToken(Ptg token) {
+ switch (token.getPtgClass()) {
+ case Ptg.CLASS_REF:
+ return "REF";
+ case Ptg.CLASS_VALUE:
+ return "VALUE";
+ case Ptg.CLASS_ARRAY:
+ return "ARRAY";
+ default:
+ throwInvalidRVAToken(token);
+ return "";
+ }
+ }
+
+ /**
+ * Method parseFormulaRecord
+ *
+ * @param record the record to be parsed
+ */
+ public String parseFormulaRecord(FormulaRecord record) {
+ return String.format(Locale.ROOT,
+ "==============================\n" +
+ "row = %d, col = %d\n" +
+ "value = %f\n" +
+ "xf = %d, number of ptgs = %d, options = %d\n" +
+ "RPN List = %s\n" +
+ "Formula text = %s",
+ record.getRow(), record.getColumn(), record.getValue(), record.getXFIndex(),
+ record.getParsedExpression().length, record.getOptions(),
+ formulaString(record), composeFormula(record));
+ }
+
+ private String formulaString(FormulaRecord record) {
+ StringBuilder buf = new StringBuilder();
+ Ptg[] tokens = record.getParsedExpression();
+ for (Ptg token : tokens) {
+ buf.append(token.toFormulaString());
+ switch (token.getPtgClass()) {
+ case Ptg.CLASS_REF:
+ buf.append("(R)");
+ break;
+ case Ptg.CLASS_VALUE:
+ buf.append("(V)");
+ break;
+ case Ptg.CLASS_ARRAY:
+ buf.append("(A)");
+ break;
+ default:
+ throwInvalidRVAToken(token);
}
- } finally {
- System.setOut(save);
+ buf.append(' ');
}
+ return buf.toString();
+ }
+
+ private static void throwInvalidRVAToken(Ptg token) {
+ throw new IllegalStateException("Invalid RVA type (" + token.getPtgClass() + "). This should never happen.");
+ }
+
+ private static String composeFormula(FormulaRecord record) {
+ return HSSFFormulaParser.toFormulaString(null, record.getParsedExpression());
}
}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestReSave.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestReSave.java
deleted file mode 100644
index 475ff2a3d7..0000000000
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestReSave.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ====================================================================
- 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.dev;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.File;
-import java.io.PrintStream;
-import java.util.Map;
-
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.POIDataSamples;
-import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.commons.io.output.NullPrintStream;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.parallel.Isolated;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
-
-@Isolated("Modifies the test data directory")
-@ResourceLock(Resources.SYSTEM_OUT)
-class TestReSave extends BaseTestIteratingXLS {
- @Override
- protected Map<String, Class<? extends Throwable>> getExcludes() {
- Map<String, Class<? extends Throwable>> excludes = super.getExcludes();
- // unsupported crypto api header
- excludes.put("35897-type4.xls", EncryptedDocumentException.class);
- excludes.put("51832.xls", EncryptedDocumentException.class);
- excludes.put("xor-encryption-abc.xls", EncryptedDocumentException.class);
- excludes.put("password.xls", EncryptedDocumentException.class);
- // HSSFWorkbook cannot open it as well
- excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class);
- excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);
- // "Buffer overrun"
- excludes.put("XRefCalc.xls", RuntimeException.class);
- return excludes;
- }
-
- @Override
- void runOneFile(File fileIn) throws Exception {
- // avoid running on files leftover from previous failed runs
- if(fileIn.getName().endsWith("-saved.xls")) {
- return;
- }
-
- PrintStream save = System.out;
- try {
- // redirect standard out during the test to avoid spamming the console with output
- System.setOut(new NullPrintStream());
-
- File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xls", "-saved.xls"));
- try {
- ReSave.main(new String[] { fileIn.getAbsolutePath() });
-
- // also try BiffViewer on the saved file
- new TestBiffViewer().runOneFile(reSavedFile);
-
- // had one case where the re-saved could not be re-saved!
- ReSave.main(new String[] { "-bos", reSavedFile.getAbsolutePath() });
- } finally {
- // clean up the re-saved file
- assertTrue(!reSavedFile.exists() || reSavedFile.delete());
- }
- } finally {
- System.setOut(save);
- }
- }
-
- @Disabled("Only used for local testing")
- @Test
- void testOneFile() throws Exception {
- String dataDirName = System.getProperty(POIDataSamples.TEST_PROPERTY);
- if(dataDirName == null) {
- dataDirName = "test-data";
- }
-
- runOneFile(new File(dataDirName + "/spreadsheet", "49931.xls"));
- }
-}
diff --git a/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java b/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java
index a0462b30c5..dbcb3c0618 100644
--- a/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/TestRecordLister.java
@@ -18,33 +18,135 @@ package org.apache.poi.hssf.dev;
import java.io.File;
import java.io.IOException;
-import java.io.PrintStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.Locale;
-import org.apache.commons.io.output.NullPrintStream;
-import org.junit.jupiter.api.parallel.ResourceLock;
-import org.junit.jupiter.api.parallel.Resources;
+import org.apache.commons.io.output.NullWriter;
+import org.apache.poi.hssf.record.ContinueRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordFactory;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * This is a low-level debugging class, which simply prints out what records come in what order.
+ * Most people will want to use {@link BiffViewer} or {@link TestEFBiffViewer}, but this can be handy when
+ * trying to make sense of {@link ContinueRecord} special cases.
+ * <p>
+ * Output is of the form:
+ * SID - Length - Type (if known)
+ * byte0 byte1 byte2 byte3 .... byte(n-4) byte(n-3) byte(n-2) byte(n-1)
+ */
-@ResourceLock(Resources.SYSTEM_OUT)
class TestRecordLister extends BaseTestIteratingXLS {
@Override
void runOneFile(File fileIn) throws IOException {
- PrintStream save = System.out;
- try {
- // redirect standard out during the test to avoid spamming the console with output
- System.setOut(new NullPrintStream());
-
- RecordLister viewer = new RecordLister();
- viewer.setFile(fileIn.getAbsolutePath());
- viewer.run();
- } finally {
- System.setOut(save);
+ // replace it with System.out if you like it more verbatim
+ PrintWriter out = new PrintWriter(new NullWriter());
+
+ try (POIFSFileSystem fs = new POIFSFileSystem(fileIn, true);
+ InputStream din = BiffViewer.getPOIFSInputStream(fs)) {
+ RecordInputStream rinp = new RecordInputStream(din);
+
+ while (rinp.hasNextRecord()) {
+ int sid = rinp.getNextSid();
+ rinp.nextRecord();
+
+ int size = rinp.available();
+ Class<? extends Record> clz = RecordFactory.getRecordClass(sid);
+
+ out.printf(Locale.ROOT, "%1$#06x (%1$04d) - %2$#05x (%2$03d) bytes", sid, size);
+
+ if (clz != null) {
+ out.print(" \t");
+ out.print(clz.getSimpleName());
+ }
+ out.println();
+
+ byte[] data = rinp.readRemainder();
+ if (data.length > 0) {
+ out.print(" ");
+ out.println(formatData(data));
+ }
+ }
}
}
- //@Test
- void testFile() throws IOException {
- RecordLister viewer = new RecordLister();
- viewer.setFile(new File("test-data/spreadsheet/testEXCEL_95.xls").getAbsolutePath());
- viewer.run();
+ /*
+ private static String formatSID(int sid) {
+ String hex = Integer.toHexString(sid);
+ String dec = Integer.toString(sid);
+
+ StringBuilder s = new StringBuilder();
+ s.append("0x");
+ for (int i = hex.length(); i < 4; i++) {
+ s.append('0');
+ }
+ s.append(hex);
+
+ s.append(" (");
+ for (int i = dec.length(); i < 4; i++) {
+ s.append('0');
+ }
+ s.append(dec);
+ s.append(")");
+
+ return s.toString();
+ }
+
+ private static String formatSize(int size) {
+ String hex = Integer.toHexString(size);
+ String dec = Integer.toString(size);
+
+ final String MAX_DIGITS = "000";
+
+ StringBuilder s = new StringBuilder();
+ s.append(MAX_DIGITS, 0, Math.max(MAX_DIGITS.length()-hex.length(),0));
+ s.append(hex);
+
+ s.append(" (");
+ s.append(MAX_DIGITS, 0, Math.max(MAX_DIGITS.length()-dec.length(),0));
+ s.append(dec);
+ s.append(')');
+
+ return s.toString();
+ }*/
+
+ private static String formatData(byte[] data) {
+ if (data == null || data.length == 0) {
+ return "";
+ }
+
+ StringBuilder s = new StringBuilder();
+
+ // If possible, do first 4 and last 4 bytes
+ final int MAX_BYTES = 9;
+ int bLen = Math.min(data.length, MAX_BYTES);
+ for (int i=0; i<bLen; i++) {
+ if (i>0) {
+ s.append(' ');
+ }
+ int b;
+ if (i<MAX_BYTES/2) {
+ b = data[i];
+ } else if (i == MAX_BYTES/2 && data.length > MAX_BYTES) {
+ s.append("...");
+ continue;
+ } else {
+ b = data[data.length-(bLen-i)];
+ }
+
+ // byte to hex
+ if (b < 0) {
+ b += 256;
+ }
+ if (b < 16) {
+ s.append('0');
+ }
+ s.append(Integer.toHexString(b));
+ }
+
+ return s.toString();
}
}
diff --git a/poi/src/main/java/org/apache/poi/hssf/dev/package-info.java b/poi/src/test/java/org/apache/poi/hssf/dev/package-info.java
index 74d4c09c9b..74d4c09c9b 100644
--- a/poi/src/main/java/org/apache/poi/hssf/dev/package-info.java
+++ b/poi/src/test/java/org/apache/poi/hssf/dev/package-info.java