Remove any reading/writing to files code from HSSFWorkbook
This commit is contained in:
parent
640c88d640
commit
463e00558d
@ -1,164 +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<Integer>();
|
|
||||||
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<Integer>();
|
|
||||||
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<Integer>();
|
|
||||||
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);
|
|
||||||
FileInputStream inp = new FileInputStream(input);
|
|
||||||
String output = getOutputFileName(input);
|
|
||||||
FileOutputStream outputStream = new FileOutputStream(output);
|
|
||||||
writeToFile(outputStream, inp, isExcludeWorkbookRecords(params), params);
|
|
||||||
inp.close();
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeToFile(OutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException {
|
|
||||||
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));
|
|
||||||
fos.close();
|
|
||||||
workbook.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,876 +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.poi.hssf.record.ArrayRecord;
|
|
||||||
import org.apache.poi.hssf.record.AutoFilterInfoRecord;
|
|
||||||
import org.apache.poi.hssf.record.BOFRecord;
|
|
||||||
import org.apache.poi.hssf.record.BackupRecord;
|
|
||||||
import org.apache.poi.hssf.record.BlankRecord;
|
|
||||||
import org.apache.poi.hssf.record.BookBoolRecord;
|
|
||||||
import org.apache.poi.hssf.record.BoolErrRecord;
|
|
||||||
import org.apache.poi.hssf.record.BottomMarginRecord;
|
|
||||||
import org.apache.poi.hssf.record.BoundSheetRecord;
|
|
||||||
import org.apache.poi.hssf.record.CFHeader12Record;
|
|
||||||
import org.apache.poi.hssf.record.CFHeaderRecord;
|
|
||||||
import org.apache.poi.hssf.record.CFRule12Record;
|
|
||||||
import org.apache.poi.hssf.record.CFRuleRecord;
|
|
||||||
import org.apache.poi.hssf.record.CalcCountRecord;
|
|
||||||
import org.apache.poi.hssf.record.CalcModeRecord;
|
|
||||||
import org.apache.poi.hssf.record.CodepageRecord;
|
|
||||||
import org.apache.poi.hssf.record.ColumnInfoRecord;
|
|
||||||
import org.apache.poi.hssf.record.ContinueRecord;
|
|
||||||
import org.apache.poi.hssf.record.CountryRecord;
|
|
||||||
import org.apache.poi.hssf.record.DBCellRecord;
|
|
||||||
import org.apache.poi.hssf.record.DConRefRecord;
|
|
||||||
import org.apache.poi.hssf.record.DSFRecord;
|
|
||||||
import org.apache.poi.hssf.record.DVALRecord;
|
|
||||||
import org.apache.poi.hssf.record.DVRecord;
|
|
||||||
import org.apache.poi.hssf.record.DateWindow1904Record;
|
|
||||||
import org.apache.poi.hssf.record.DefaultColWidthRecord;
|
|
||||||
import org.apache.poi.hssf.record.DefaultRowHeightRecord;
|
|
||||||
import org.apache.poi.hssf.record.DeltaRecord;
|
|
||||||
import org.apache.poi.hssf.record.DimensionsRecord;
|
|
||||||
import org.apache.poi.hssf.record.DrawingGroupRecord;
|
|
||||||
import org.apache.poi.hssf.record.DrawingRecordForBiffViewer;
|
|
||||||
import org.apache.poi.hssf.record.DrawingSelectionRecord;
|
|
||||||
import org.apache.poi.hssf.record.EOFRecord;
|
|
||||||
import org.apache.poi.hssf.record.ExtSSTRecord;
|
|
||||||
import org.apache.poi.hssf.record.ExtendedFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.ExternSheetRecord;
|
|
||||||
import org.apache.poi.hssf.record.ExternalNameRecord;
|
|
||||||
import org.apache.poi.hssf.record.FeatHdrRecord;
|
|
||||||
import org.apache.poi.hssf.record.FeatRecord;
|
|
||||||
import org.apache.poi.hssf.record.FilePassRecord;
|
|
||||||
import org.apache.poi.hssf.record.FileSharingRecord;
|
|
||||||
import org.apache.poi.hssf.record.FnGroupCountRecord;
|
|
||||||
import org.apache.poi.hssf.record.FontRecord;
|
|
||||||
import org.apache.poi.hssf.record.FooterRecord;
|
|
||||||
import org.apache.poi.hssf.record.FormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.FormulaRecord;
|
|
||||||
import org.apache.poi.hssf.record.GridsetRecord;
|
|
||||||
import org.apache.poi.hssf.record.GutsRecord;
|
|
||||||
import org.apache.poi.hssf.record.HCenterRecord;
|
|
||||||
import org.apache.poi.hssf.record.HeaderRecord;
|
|
||||||
import org.apache.poi.hssf.record.HideObjRecord;
|
|
||||||
import org.apache.poi.hssf.record.HorizontalPageBreakRecord;
|
|
||||||
import org.apache.poi.hssf.record.HyperlinkRecord;
|
|
||||||
import org.apache.poi.hssf.record.IndexRecord;
|
|
||||||
import org.apache.poi.hssf.record.InterfaceEndRecord;
|
|
||||||
import org.apache.poi.hssf.record.InterfaceHdrRecord;
|
|
||||||
import org.apache.poi.hssf.record.IterationRecord;
|
|
||||||
import org.apache.poi.hssf.record.LabelRecord;
|
|
||||||
import org.apache.poi.hssf.record.LabelSSTRecord;
|
|
||||||
import org.apache.poi.hssf.record.LeftMarginRecord;
|
|
||||||
import org.apache.poi.hssf.record.MMSRecord;
|
|
||||||
import org.apache.poi.hssf.record.MergeCellsRecord;
|
|
||||||
import org.apache.poi.hssf.record.MulBlankRecord;
|
|
||||||
import org.apache.poi.hssf.record.MulRKRecord;
|
|
||||||
import org.apache.poi.hssf.record.NameCommentRecord;
|
|
||||||
import org.apache.poi.hssf.record.NameRecord;
|
|
||||||
import org.apache.poi.hssf.record.NoteRecord;
|
|
||||||
import org.apache.poi.hssf.record.NumberRecord;
|
|
||||||
import org.apache.poi.hssf.record.ObjRecord;
|
|
||||||
import org.apache.poi.hssf.record.PaletteRecord;
|
|
||||||
import org.apache.poi.hssf.record.PaneRecord;
|
|
||||||
import org.apache.poi.hssf.record.PasswordRecord;
|
|
||||||
import org.apache.poi.hssf.record.PasswordRev4Record;
|
|
||||||
import org.apache.poi.hssf.record.PrecisionRecord;
|
|
||||||
import org.apache.poi.hssf.record.PrintGridlinesRecord;
|
|
||||||
import org.apache.poi.hssf.record.PrintHeadersRecord;
|
|
||||||
import org.apache.poi.hssf.record.PrintSetupRecord;
|
|
||||||
import org.apache.poi.hssf.record.ProtectRecord;
|
|
||||||
import org.apache.poi.hssf.record.ProtectionRev4Record;
|
|
||||||
import org.apache.poi.hssf.record.RKRecord;
|
|
||||||
import org.apache.poi.hssf.record.RecalcIdRecord;
|
|
||||||
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.record.RefModeRecord;
|
|
||||||
import org.apache.poi.hssf.record.RefreshAllRecord;
|
|
||||||
import org.apache.poi.hssf.record.RightMarginRecord;
|
|
||||||
import org.apache.poi.hssf.record.RowRecord;
|
|
||||||
import org.apache.poi.hssf.record.SCLRecord;
|
|
||||||
import org.apache.poi.hssf.record.SSTRecord;
|
|
||||||
import org.apache.poi.hssf.record.SaveRecalcRecord;
|
|
||||||
import org.apache.poi.hssf.record.SelectionRecord;
|
|
||||||
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
|
||||||
import org.apache.poi.hssf.record.StringRecord;
|
|
||||||
import org.apache.poi.hssf.record.StyleRecord;
|
|
||||||
import org.apache.poi.hssf.record.SupBookRecord;
|
|
||||||
import org.apache.poi.hssf.record.TabIdRecord;
|
|
||||||
import org.apache.poi.hssf.record.TableRecord;
|
|
||||||
import org.apache.poi.hssf.record.TableStylesRecord;
|
|
||||||
import org.apache.poi.hssf.record.TextObjectRecord;
|
|
||||||
import org.apache.poi.hssf.record.TopMarginRecord;
|
|
||||||
import org.apache.poi.hssf.record.UncalcedRecord;
|
|
||||||
import org.apache.poi.hssf.record.UnknownRecord;
|
|
||||||
import org.apache.poi.hssf.record.UseSelFSRecord;
|
|
||||||
import org.apache.poi.hssf.record.VCenterRecord;
|
|
||||||
import org.apache.poi.hssf.record.VerticalPageBreakRecord;
|
|
||||||
import org.apache.poi.hssf.record.WSBoolRecord;
|
|
||||||
import org.apache.poi.hssf.record.WindowOneRecord;
|
|
||||||
import org.apache.poi.hssf.record.WindowProtectRecord;
|
|
||||||
import org.apache.poi.hssf.record.WindowTwoRecord;
|
|
||||||
import org.apache.poi.hssf.record.WriteAccessRecord;
|
|
||||||
import org.apache.poi.hssf.record.WriteProtectRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AreaFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AreaRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AxisLineFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AxisOptionsRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AxisParentRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AxisRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.AxisUsedRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.BarRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.BeginRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.CatLabRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.CategorySeriesAxisRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartEndBlockRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartEndObjectRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartFRTInfoRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartStartBlockRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ChartStartObjectRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.DatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.DataFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.DefaultDataLabelTextPropertiesRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.EndRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.FontBasisRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.FontIndexRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.FrameRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.LegendRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.LineFormatRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.LinkedDataRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ObjectLinkRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.PlotAreaRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.PlotGrowthRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SeriesIndexRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SeriesListRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SeriesRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SeriesTextRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SeriesToChartGroupRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.SheetPropertiesRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.TextRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.TickRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.UnitsRecord;
|
|
||||||
import org.apache.poi.hssf.record.chart.ValueRangeRecord;
|
|
||||||
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.NPOIFSFileSystem;
|
|
||||||
import org.apache.poi.util.HexDump;
|
|
||||||
import org.apache.poi.util.IOUtils;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.POILogFactory;
|
|
||||||
import org.apache.poi.util.POILogger;
|
|
||||||
import org.apache.poi.util.StringUtil;
|
|
||||||
import org.apache.poi.util.SuppressForbidden;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 POILogger logger = POILogFactory.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
|
|
||||||
*
|
|
||||||
* @return an array of Records created from the InputStream
|
|
||||||
* @exception org.apache.poi.util.RecordFormatException on error processing the InputStream
|
|
||||||
*/
|
|
||||||
public static Record[] createRecords(InputStream is, PrintWriter ps, BiffRecordListener recListener, boolean dumpInterpretedRecords)
|
|
||||||
throws org.apache.poi.util.RecordFormatException {
|
|
||||||
List<Record> temp = new ArrayList<Record>();
|
|
||||||
|
|
||||||
RecordInputStream recStream = new RecordInputStream(is);
|
|
||||||
while (true) {
|
|
||||||
boolean hasNext;
|
|
||||||
try {
|
|
||||||
hasNext = recStream.hasNextRecord();
|
|
||||||
} catch (LeftoverDataException e) {
|
|
||||||
logger.log(POILogger.ERROR, "Discarding " + recStream.remaining() + " bytes and continuing", e);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
temp.add(record);
|
|
||||||
|
|
||||||
for (String header : recListener.getRecentHeaders()) {
|
|
||||||
ps.println(header);
|
|
||||||
}
|
|
||||||
ps.print(record.toString());
|
|
||||||
} else {
|
|
||||||
recStream.readRemainder();
|
|
||||||
}
|
|
||||||
ps.println();
|
|
||||||
}
|
|
||||||
Record[] result = new Record[temp.size()];
|
|
||||||
temp.toArray(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 SeriesToChartGroupRecord.sid:return new SeriesToChartGroupRecord(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);
|
|
||||||
}
|
|
||||||
public boolean shouldDumpBiffHex() {
|
|
||||||
return _biffhex;
|
|
||||||
}
|
|
||||||
public boolean shouldDumpRecordInterpretations() {
|
|
||||||
return !_noint;
|
|
||||||
}
|
|
||||||
public boolean shouldOutputToFile() {
|
|
||||||
return _out;
|
|
||||||
}
|
|
||||||
public boolean shouldOutputRawHexOnly() {
|
|
||||||
return _rawhex;
|
|
||||||
}
|
|
||||||
public boolean suppressHeader() {
|
|
||||||
return _noHeader;
|
|
||||||
}
|
|
||||||
public File getFile() {
|
|
||||||
return _file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static final class CommandParseException extends Exception {
|
|
||||||
public 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] <fileName><p>
|
|
||||||
* BiffViewer --rawhex [--out] <fileName>
|
|
||||||
*
|
|
||||||
* <table summary="BiffViewer options">
|
|
||||||
* <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 <fileName>.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);
|
|
||||||
|
|
||||||
PrintWriter pw;
|
|
||||||
if (cmdArgs.shouldOutputToFile()) {
|
|
||||||
OutputStream os = new FileOutputStream(cmdArgs.getFile().getAbsolutePath() + ".out");
|
|
||||||
pw = new PrintWriter(new OutputStreamWriter(os, StringUtil.UTF8));
|
|
||||||
} else {
|
|
||||||
// Use the system default encoding when sending to System Out
|
|
||||||
pw = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset()));
|
|
||||||
}
|
|
||||||
|
|
||||||
NPOIFSFileSystem fs = null;
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
fs = new NPOIFSFileSystem(cmdArgs.getFile(), true);
|
|
||||||
is = getPOIFSInputStream(fs);
|
|
||||||
|
|
||||||
if (cmdArgs.shouldOutputRawHexOnly()) {
|
|
||||||
byte[] data = IOUtils.toByteArray(is);
|
|
||||||
HexDump.dump(data, 0, System.out, 0);
|
|
||||||
} else {
|
|
||||||
boolean dumpInterpretedRecords = cmdArgs.shouldDumpRecordInterpretations();
|
|
||||||
boolean dumpHex = cmdArgs.shouldDumpBiffHex();
|
|
||||||
boolean zeroAlignHexDump = dumpInterpretedRecords; // TODO - fix non-zeroAlign
|
|
||||||
runBiffViewer(pw, is, dumpInterpretedRecords, dumpHex, zeroAlignHexDump,
|
|
||||||
cmdArgs.suppressHeader());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(is);
|
|
||||||
IOUtils.closeQuietly(fs);
|
|
||||||
IOUtils.closeQuietly(pw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static InputStream getPOIFSInputStream(NPOIFSFileSystem fs)
|
|
||||||
throws IOException, FileNotFoundException {
|
|
||||||
String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot());
|
|
||||||
return fs.createDocumentInputStream(workbookName);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected 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;
|
|
||||||
public BiffRecordListener(Writer hexDumpWriter, boolean zeroAlignEachRecord, boolean noHeader) {
|
|
||||||
_hexDumpWriter = hexDumpWriter;
|
|
||||||
_zeroAlignEachRecord = zeroAlignEachRecord;
|
|
||||||
_noHeader = noHeader;
|
|
||||||
_headers = new ArrayList<String>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public List<String> getRecentHeaders() {
|
|
||||||
List<String> result = _headers;
|
|
||||||
_headers = new ArrayList<String>();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
|
|
||||||
StringBuilder sb = new StringBuilder(64);
|
|
||||||
sb.append("Offset=").append(HexDump.intToHex(globalOffset)).append("(").append(globalOffset).append(")");
|
|
||||||
sb.append(" recno=").append(recordCounter);
|
|
||||||
sb.append( " sid=").append(HexDump.shortToHex(sid));
|
|
||||||
sb.append( " size=").append(HexDump.shortToHex(size)).append("(").append(size).append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public 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 (_currentPos >= _currentSize) {
|
|
||||||
fillNextBuffer();
|
|
||||||
}
|
|
||||||
if (_currentPos >= _currentSize) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int availSize = _currentSize - _currentPos;
|
|
||||||
int result;
|
|
||||||
if (len > availSize) {
|
|
||||||
System.err.println("Unexpected request to read past end of current biff record");
|
|
||||||
result = availSize;
|
|
||||||
} else {
|
|
||||||
result = len;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
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) throws IOException {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
|
|
||||||
import org.apache.poi.hssf.record.Record;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author andy
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class EFBiffViewer
|
|
||||||
{
|
|
||||||
String file;
|
|
||||||
|
|
||||||
/** Creates a new instance of EFBiffViewer */
|
|
||||||
|
|
||||||
public EFBiffViewer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() throws IOException {
|
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File(file), true);
|
|
||||||
try {
|
|
||||||
InputStream din = BiffViewer.getPOIFSInputStream(fs);
|
|
||||||
try {
|
|
||||||
HSSFRequest req = new HSSFRequest();
|
|
||||||
|
|
||||||
req.addListenerForAllRecords(new HSSFListener()
|
|
||||||
{
|
|
||||||
public void processRecord(Record rec)
|
|
||||||
{
|
|
||||||
System.out.println(rec.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
HSSFEventFactory factory = new HSSFEventFactory();
|
|
||||||
|
|
||||||
factory.processEvents(req, din);
|
|
||||||
} finally {
|
|
||||||
din.close();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,236 +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.Record;
|
|
||||||
import org.apache.poi.hssf.record.RecordFactory;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
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"
|
|
||||||
* @author andy
|
|
||||||
* @author Avik
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class FormulaViewer
|
|
||||||
{
|
|
||||||
private String file;
|
|
||||||
private boolean list=false;
|
|
||||||
|
|
||||||
/** Creates new FormulaViewer */
|
|
||||||
|
|
||||||
public FormulaViewer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method run
|
|
||||||
*
|
|
||||||
* @throws IOException if the file contained errors
|
|
||||||
*/
|
|
||||||
public void run() throws IOException {
|
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File(file), true);
|
|
||||||
try {
|
|
||||||
InputStream is = BiffViewer.getPOIFSInputStream(fs);
|
|
||||||
try {
|
|
||||||
List<Record> records = RecordFactory.createRecords(is);
|
|
||||||
|
|
||||||
for (Record record : records) {
|
|
||||||
if (record.getSid() == FormulaRecord.sid) {
|
|
||||||
if (list) {
|
|
||||||
listFormula((FormulaRecord) record);
|
|
||||||
} else {
|
|
||||||
parseFormulaRecord((FormulaRecord) record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void listFormula(FormulaRecord record) {
|
|
||||||
String sep="~";
|
|
||||||
Ptg[] tokens= record.getParsedExpression();
|
|
||||||
Ptg token;
|
|
||||||
int numptgs = tokens.length;
|
|
||||||
String numArg;
|
|
||||||
token = tokens[numptgs-1];
|
|
||||||
if (token instanceof FuncPtg) {
|
|
||||||
numArg = String.valueOf(numptgs-1);
|
|
||||||
} else {
|
|
||||||
numArg = String.valueOf(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
|
|
||||||
if (token instanceof ExpPtg) return;
|
|
||||||
buf.append(token.toFormulaString());
|
|
||||||
buf.append(sep);
|
|
||||||
switch (token.getPtgClass()) {
|
|
||||||
case Ptg.CLASS_REF :
|
|
||||||
buf.append("REF");
|
|
||||||
break;
|
|
||||||
case Ptg.CLASS_VALUE :
|
|
||||||
buf.append("VALUE");
|
|
||||||
break;
|
|
||||||
case Ptg.CLASS_ARRAY :
|
|
||||||
buf.append("ARRAY");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throwInvalidRVAToken(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.append(sep);
|
|
||||||
if (numptgs>1) {
|
|
||||||
token = tokens[numptgs-2];
|
|
||||||
switch (token.getPtgClass()) {
|
|
||||||
case Ptg.CLASS_REF :
|
|
||||||
buf.append("REF");
|
|
||||||
break;
|
|
||||||
case Ptg.CLASS_VALUE :
|
|
||||||
buf.append("VALUE");
|
|
||||||
break;
|
|
||||||
case Ptg.CLASS_ARRAY :
|
|
||||||
buf.append("ARRAY");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throwInvalidRVAToken(token);
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
buf.append("VALUE");
|
|
||||||
}
|
|
||||||
buf.append(sep);
|
|
||||||
buf.append(numArg);
|
|
||||||
System.out.println(buf.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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((HSSFWorkbook)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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +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.ByteArrayOutputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
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;
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
for(String filename : args) {
|
|
||||||
if(filename.equals("-dg")) {
|
|
||||||
initDrawing = true;
|
|
||||||
} else if(filename.equals("-bos")) {
|
|
||||||
saveToMemory = true;
|
|
||||||
} else {
|
|
||||||
System.out.print("reading " + filename + "...");
|
|
||||||
FileInputStream is = new FileInputStream(filename);
|
|
||||||
HSSFWorkbook wb = new HSSFWorkbook(is);
|
|
||||||
try {
|
|
||||||
System.out.println("done");
|
|
||||||
|
|
||||||
for(int i = 0; i < wb.getNumberOfSheets(); i++){
|
|
||||||
HSSFSheet sheet = wb.getSheetAt(i);
|
|
||||||
if(initDrawing) {
|
|
||||||
/*HSSFPatriarch dg =*/ sheet.getDrawingPatriarch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputStream os;
|
|
||||||
if (saveToMemory) {
|
|
||||||
bos.reset();
|
|
||||||
os = bos;
|
|
||||||
} else {
|
|
||||||
String outputFile = filename.replace(".xls", "-saved.xls");
|
|
||||||
System.out.print("saving to " + outputFile + "...");
|
|
||||||
os = new FileOutputStream(outputFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
wb.write(os);
|
|
||||||
} finally {
|
|
||||||
os.close();
|
|
||||||
}
|
|
||||||
System.out.println("done");
|
|
||||||
} finally {
|
|
||||||
wb.close();
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,202 +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.NPOIFSFileSystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
{
|
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File(file), true);
|
|
||||||
try {
|
|
||||||
InputStream din = BiffViewer.getPOIFSInputStream(fs);
|
|
||||||
try {
|
|
||||||
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) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
din.close();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatSID(int sid) {
|
|
||||||
String hex = Integer.toHexString(sid);
|
|
||||||
String dec = Integer.toString(sid);
|
|
||||||
|
|
||||||
StringBuffer s = new StringBuffer();
|
|
||||||
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);
|
|
||||||
|
|
||||||
StringBuffer s = new StringBuffer();
|
|
||||||
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
|
|
||||||
StringBuffer s = new StringBuffer();
|
|
||||||
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(int i=0; i<data.length; i++) {
|
|
||||||
s.append(byteToHex(data[i]));
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
||||||
<!--
|
|
||||||
====================================================================
|
|
||||||
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.
|
|
||||||
====================================================================
|
|
||||||
-->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
</head>
|
|
||||||
<body bgcolor="white">
|
|
||||||
|
|
||||||
DEV package serves two purposes. 1. Examples for how to use HSSF and 2. tools for developing
|
|
||||||
and validating HSSF.
|
|
||||||
|
|
||||||
<h2>Related Documentation</h2>
|
|
||||||
|
|
||||||
For overviews, tutorials, examples, guides, and tool documentation, please see:
|
|
||||||
<ul>
|
|
||||||
<li><a href="http://poi.apache.org">Apache POI Project</a>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,266 +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.extractor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.poi.POIDocument;
|
|
||||||
import org.apache.poi.POIOLE2TextExtractor;
|
|
||||||
import org.apache.poi.hpsf.DocumentSummaryInformation;
|
|
||||||
import org.apache.poi.hpsf.SummaryInformation;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
|
||||||
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
|
|
||||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
|
||||||
import org.apache.poi.hssf.record.BOFRecord;
|
|
||||||
import org.apache.poi.hssf.record.BoundSheetRecord;
|
|
||||||
import org.apache.poi.hssf.record.FormulaRecord;
|
|
||||||
import org.apache.poi.hssf.record.LabelRecord;
|
|
||||||
import org.apache.poi.hssf.record.LabelSSTRecord;
|
|
||||||
import org.apache.poi.hssf.record.NoteRecord;
|
|
||||||
import org.apache.poi.hssf.record.NumberRecord;
|
|
||||||
import org.apache.poi.hssf.record.Record;
|
|
||||||
import org.apache.poi.hssf.record.SSTRecord;
|
|
||||||
import org.apache.poi.hssf.record.StringRecord;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A text extractor for Excel files, that is based
|
|
||||||
* on the HSSF EventUserModel API.
|
|
||||||
* It will typically use less memory than
|
|
||||||
* {@link ExcelExtractor}, but may not provide
|
|
||||||
* the same richness of formatting.
|
|
||||||
* Returns the textual content of the file, suitable for
|
|
||||||
* indexing by something like Lucene, but not really
|
|
||||||
* intended for display to the user.
|
|
||||||
* <p>
|
|
||||||
* To turn an excel file into a CSV or similar, then see
|
|
||||||
* the XLS2CSVmra example
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see <a href="http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java">XLS2CSVmra</a>
|
|
||||||
*/
|
|
||||||
public class EventBasedExcelExtractor extends POIOLE2TextExtractor implements org.apache.poi.ss.extractor.ExcelExtractor {
|
|
||||||
private DirectoryNode _dir;
|
|
||||||
boolean _includeSheetNames = true;
|
|
||||||
boolean _formulasNotResults = false;
|
|
||||||
|
|
||||||
public EventBasedExcelExtractor( DirectoryNode dir )
|
|
||||||
{
|
|
||||||
super( (POIDocument)null );
|
|
||||||
_dir = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EventBasedExcelExtractor(POIFSFileSystem fs) {
|
|
||||||
this(fs.getRoot());
|
|
||||||
super.setFilesystem(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Would return the document information metadata for the document,
|
|
||||||
* if we supported it
|
|
||||||
*/
|
|
||||||
public DocumentSummaryInformation getDocSummaryInformation() {
|
|
||||||
throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor");
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Would return the summary information metadata for the document,
|
|
||||||
* if we supported it
|
|
||||||
*/
|
|
||||||
public SummaryInformation getSummaryInformation() {
|
|
||||||
throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Would control the inclusion of cell comments from the document,
|
|
||||||
* if we supported it
|
|
||||||
*/
|
|
||||||
public void setIncludeCellComments(boolean includeComments) {
|
|
||||||
throw new IllegalStateException("Comment extraction not supported in streaming mode, please use ExcelExtractor");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Would control the inclusion of headers and footers from the document,
|
|
||||||
* if we supported it
|
|
||||||
*/
|
|
||||||
public void setIncludeHeadersFooters(boolean includeHeadersFooters) {
|
|
||||||
throw new IllegalStateException("Header/Footer extraction not supported in streaming mode, please use ExcelExtractor");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should sheet names be included? Default is true
|
|
||||||
*/
|
|
||||||
public void setIncludeSheetNames(boolean includeSheetNames) {
|
|
||||||
_includeSheetNames = includeSheetNames;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Should we return the formula itself, and not
|
|
||||||
* the result it produces? Default is false
|
|
||||||
*/
|
|
||||||
public void setFormulasNotResults(boolean formulasNotResults) {
|
|
||||||
_formulasNotResults = formulasNotResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retreives the text contents of the file
|
|
||||||
*/
|
|
||||||
public String getText() {
|
|
||||||
String text = null;
|
|
||||||
try {
|
|
||||||
TextListener tl = triggerExtraction();
|
|
||||||
|
|
||||||
text = tl._text.toString();
|
|
||||||
if(! text.endsWith("\n")) {
|
|
||||||
text = text + "\n";
|
|
||||||
}
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextListener triggerExtraction() throws IOException {
|
|
||||||
TextListener tl = new TextListener();
|
|
||||||
FormatTrackingHSSFListener ft = new FormatTrackingHSSFListener(tl);
|
|
||||||
tl._ft = ft;
|
|
||||||
|
|
||||||
// Register and process
|
|
||||||
HSSFEventFactory factory = new HSSFEventFactory();
|
|
||||||
HSSFRequest request = new HSSFRequest();
|
|
||||||
request.addListenerForAllRecords(ft);
|
|
||||||
|
|
||||||
factory.processWorkbookEvents(request, _dir);
|
|
||||||
|
|
||||||
return tl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TextListener implements HSSFListener {
|
|
||||||
FormatTrackingHSSFListener _ft;
|
|
||||||
private SSTRecord sstRecord;
|
|
||||||
|
|
||||||
private final List<String> sheetNames;
|
|
||||||
final StringBuffer _text = new StringBuffer();
|
|
||||||
private int sheetNum = -1;
|
|
||||||
private int rowNum;
|
|
||||||
|
|
||||||
private boolean outputNextStringValue = false;
|
|
||||||
private int nextRow = -1;
|
|
||||||
|
|
||||||
public TextListener() {
|
|
||||||
sheetNames = new ArrayList<String>();
|
|
||||||
}
|
|
||||||
public void processRecord(Record record) {
|
|
||||||
String thisText = null;
|
|
||||||
int thisRow = -1;
|
|
||||||
|
|
||||||
switch(record.getSid()) {
|
|
||||||
case BoundSheetRecord.sid:
|
|
||||||
BoundSheetRecord sr = (BoundSheetRecord)record;
|
|
||||||
sheetNames.add(sr.getSheetname());
|
|
||||||
break;
|
|
||||||
case BOFRecord.sid:
|
|
||||||
BOFRecord bof = (BOFRecord)record;
|
|
||||||
if(bof.getType() == BOFRecord.TYPE_WORKSHEET) {
|
|
||||||
sheetNum++;
|
|
||||||
rowNum = -1;
|
|
||||||
|
|
||||||
if(_includeSheetNames) {
|
|
||||||
if(_text.length() > 0) _text.append("\n");
|
|
||||||
_text.append(sheetNames.get(sheetNum));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SSTRecord.sid:
|
|
||||||
sstRecord = (SSTRecord)record;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FormulaRecord.sid:
|
|
||||||
FormulaRecord frec = (FormulaRecord) record;
|
|
||||||
thisRow = frec.getRow();
|
|
||||||
|
|
||||||
if(_formulasNotResults) {
|
|
||||||
thisText = HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, frec.getParsedExpression());
|
|
||||||
} else {
|
|
||||||
if(frec.hasCachedResultString()) {
|
|
||||||
// Formula result is a string
|
|
||||||
// This is stored in the next record
|
|
||||||
outputNextStringValue = true;
|
|
||||||
nextRow = frec.getRow();
|
|
||||||
} else {
|
|
||||||
thisText = _ft.formatNumberDateCell(frec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case StringRecord.sid:
|
|
||||||
if(outputNextStringValue) {
|
|
||||||
// String for formula
|
|
||||||
StringRecord srec = (StringRecord)record;
|
|
||||||
thisText = srec.getString();
|
|
||||||
thisRow = nextRow;
|
|
||||||
outputNextStringValue = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LabelRecord.sid:
|
|
||||||
LabelRecord lrec = (LabelRecord) record;
|
|
||||||
thisRow = lrec.getRow();
|
|
||||||
thisText = lrec.getValue();
|
|
||||||
break;
|
|
||||||
case LabelSSTRecord.sid:
|
|
||||||
LabelSSTRecord lsrec = (LabelSSTRecord) record;
|
|
||||||
thisRow = lsrec.getRow();
|
|
||||||
if(sstRecord == null) {
|
|
||||||
throw new IllegalStateException("No SST record found");
|
|
||||||
}
|
|
||||||
thisText = sstRecord.getString(lsrec.getSSTIndex()).toString();
|
|
||||||
break;
|
|
||||||
case NoteRecord.sid:
|
|
||||||
NoteRecord nrec = (NoteRecord) record;
|
|
||||||
thisRow = nrec.getRow();
|
|
||||||
// TODO: Find object to match nrec.getShapeId()
|
|
||||||
break;
|
|
||||||
case NumberRecord.sid:
|
|
||||||
NumberRecord numrec = (NumberRecord) record;
|
|
||||||
thisRow = numrec.getRow();
|
|
||||||
thisText = _ft.formatNumberDateCell(numrec);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(thisText != null) {
|
|
||||||
if(thisRow != rowNum) {
|
|
||||||
rowNum = thisRow;
|
|
||||||
if(_text.length() > 0)
|
|
||||||
_text.append("\n");
|
|
||||||
} else {
|
|
||||||
_text.append("\t");
|
|
||||||
}
|
|
||||||
_text.append(thisText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,421 +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.extractor;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.apache.poi.POIOLE2TextExtractor;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFComment;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFDataFormatter;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
|
||||||
import org.apache.poi.ss.usermodel.HeaderFooter;
|
|
||||||
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A text extractor for Excel files.
|
|
||||||
* <p>
|
|
||||||
* Returns the textual content of the file, suitable for
|
|
||||||
* indexing by something like Lucene, but not really
|
|
||||||
* intended for display to the user.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* To turn an excel file into a CSV or similar, then see
|
|
||||||
* the XLS2CSVmra example
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @see <a href="http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java">XLS2CSVmra</a>
|
|
||||||
*/
|
|
||||||
public class ExcelExtractor extends POIOLE2TextExtractor implements org.apache.poi.ss.extractor.ExcelExtractor {
|
|
||||||
private final HSSFWorkbook _wb;
|
|
||||||
private final HSSFDataFormatter _formatter;
|
|
||||||
private boolean _includeSheetNames = true;
|
|
||||||
private boolean _shouldEvaluateFormulas = true;
|
|
||||||
private boolean _includeCellComments = false;
|
|
||||||
private boolean _includeBlankCells = false;
|
|
||||||
private boolean _includeHeadersFooters = true;
|
|
||||||
|
|
||||||
public ExcelExtractor(HSSFWorkbook wb) {
|
|
||||||
super(wb);
|
|
||||||
_wb = wb;
|
|
||||||
_formatter = new HSSFDataFormatter();
|
|
||||||
}
|
|
||||||
public ExcelExtractor(POIFSFileSystem fs) throws IOException {
|
|
||||||
this(fs.getRoot());
|
|
||||||
}
|
|
||||||
public ExcelExtractor(DirectoryNode dir) throws IOException {
|
|
||||||
this(new HSSFWorkbook(dir, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class CommandParseException extends Exception {
|
|
||||||
public CommandParseException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static final class CommandArgs {
|
|
||||||
private final boolean _requestHelp;
|
|
||||||
private final File _inputFile;
|
|
||||||
private final boolean _showSheetNames;
|
|
||||||
private final boolean _evaluateFormulas;
|
|
||||||
private final boolean _showCellComments;
|
|
||||||
private final boolean _showBlankCells;
|
|
||||||
private final boolean _headersFooters;
|
|
||||||
public CommandArgs(String[] args) throws CommandParseException {
|
|
||||||
int nArgs = args.length;
|
|
||||||
File inputFile = null;
|
|
||||||
boolean requestHelp = false;
|
|
||||||
boolean showSheetNames = true;
|
|
||||||
boolean evaluateFormulas = true;
|
|
||||||
boolean showCellComments = false;
|
|
||||||
boolean showBlankCells = false;
|
|
||||||
boolean headersFooters = true;
|
|
||||||
for (int i=0; i<nArgs; i++) {
|
|
||||||
String arg = args[i];
|
|
||||||
if ("-help".equalsIgnoreCase(arg)) {
|
|
||||||
requestHelp = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ("-i".equals(arg)) {
|
|
||||||
// step to next arg
|
|
||||||
if (++i >= nArgs) {
|
|
||||||
throw new CommandParseException("Expected filename after '-i'");
|
|
||||||
}
|
|
||||||
arg = args[i];
|
|
||||||
if (inputFile != null) {
|
|
||||||
throw new CommandParseException("Only one input file can be supplied");
|
|
||||||
}
|
|
||||||
inputFile = new File(arg);
|
|
||||||
if (!inputFile.exists()) {
|
|
||||||
throw new CommandParseException("Specified input file '" + arg + "' does not exist");
|
|
||||||
}
|
|
||||||
if (inputFile.isDirectory()) {
|
|
||||||
throw new CommandParseException("Specified input file '" + arg + "' is a directory");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ("--show-sheet-names".equals(arg)) {
|
|
||||||
showSheetNames = parseBoolArg(args, ++i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ("--evaluate-formulas".equals(arg)) {
|
|
||||||
evaluateFormulas = parseBoolArg(args, ++i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ("--show-comments".equals(arg)) {
|
|
||||||
showCellComments = parseBoolArg(args, ++i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ("--show-blanks".equals(arg)) {
|
|
||||||
showBlankCells = parseBoolArg(args, ++i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ("--headers-footers".equals(arg)) {
|
|
||||||
headersFooters = parseBoolArg(args, ++i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new CommandParseException("Invalid argument '" + arg + "'");
|
|
||||||
}
|
|
||||||
_requestHelp = requestHelp;
|
|
||||||
_inputFile = inputFile;
|
|
||||||
_showSheetNames = showSheetNames;
|
|
||||||
_evaluateFormulas = evaluateFormulas;
|
|
||||||
_showCellComments = showCellComments;
|
|
||||||
_showBlankCells = showBlankCells;
|
|
||||||
_headersFooters = headersFooters;
|
|
||||||
}
|
|
||||||
private static boolean parseBoolArg(String[] args, int i) throws CommandParseException {
|
|
||||||
if (i >= args.length) {
|
|
||||||
throw new CommandParseException("Expected value after '" + args[i-1] + "'");
|
|
||||||
}
|
|
||||||
String value = args[i].toUpperCase(Locale.ROOT);
|
|
||||||
if ("Y".equals(value) || "YES".equals(value) || "ON".equals(value) || "TRUE".equals(value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ("N".equals(value) || "NO".equals(value) || "OFF".equals(value) || "FALSE".equals(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw new CommandParseException("Invalid value '" + args[i] + "' for '" + args[i-1] + "'. Expected 'Y' or 'N'");
|
|
||||||
}
|
|
||||||
public boolean isRequestHelp() {
|
|
||||||
return _requestHelp;
|
|
||||||
}
|
|
||||||
public File getInputFile() {
|
|
||||||
return _inputFile;
|
|
||||||
}
|
|
||||||
public boolean shouldShowSheetNames() {
|
|
||||||
return _showSheetNames;
|
|
||||||
}
|
|
||||||
public boolean shouldEvaluateFormulas() {
|
|
||||||
return _evaluateFormulas;
|
|
||||||
}
|
|
||||||
public boolean shouldShowCellComments() {
|
|
||||||
return _showCellComments;
|
|
||||||
}
|
|
||||||
public boolean shouldShowBlankCells() {
|
|
||||||
return _showBlankCells;
|
|
||||||
}
|
|
||||||
public boolean shouldIncludeHeadersFooters() {
|
|
||||||
return _headersFooters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void printUsageMessage(PrintStream ps) {
|
|
||||||
ps.println("Use:");
|
|
||||||
ps.println(" " + ExcelExtractor.class.getName() + " [<flag> <value> [<flag> <value> [...]]] [-i <filename.xls>]");
|
|
||||||
ps.println(" -i <filename.xls> specifies input file (default is to use stdin)");
|
|
||||||
ps.println(" Flags can be set on or off by using the values 'Y' or 'N'.");
|
|
||||||
ps.println(" Following are available flags and their default values:");
|
|
||||||
ps.println(" --show-sheet-names Y");
|
|
||||||
ps.println(" --evaluate-formulas Y");
|
|
||||||
ps.println(" --show-comments N");
|
|
||||||
ps.println(" --show-blanks Y");
|
|
||||||
ps.println(" --headers-footers Y");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command line extractor.
|
|
||||||
*
|
|
||||||
* @param args the command line parameters
|
|
||||||
*
|
|
||||||
* @throws IOException if the file can't be read or contains errors
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
|
|
||||||
CommandArgs cmdArgs;
|
|
||||||
try {
|
|
||||||
cmdArgs = new CommandArgs(args);
|
|
||||||
} catch (CommandParseException e) {
|
|
||||||
System.err.println(e.getMessage());
|
|
||||||
printUsageMessage(System.err);
|
|
||||||
System.exit(1);
|
|
||||||
return; // suppress compiler error
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdArgs.isRequestHelp()) {
|
|
||||||
printUsageMessage(System.out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream is;
|
|
||||||
if(cmdArgs.getInputFile() == null) {
|
|
||||||
is = System.in;
|
|
||||||
} else {
|
|
||||||
is = new FileInputStream(cmdArgs.getInputFile());
|
|
||||||
}
|
|
||||||
HSSFWorkbook wb = new HSSFWorkbook(is);
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
ExcelExtractor extractor = new ExcelExtractor(wb);
|
|
||||||
extractor.setIncludeSheetNames(cmdArgs.shouldShowSheetNames());
|
|
||||||
extractor.setFormulasNotResults(!cmdArgs.shouldEvaluateFormulas());
|
|
||||||
extractor.setIncludeCellComments(cmdArgs.shouldShowCellComments());
|
|
||||||
extractor.setIncludeBlankCells(cmdArgs.shouldShowBlankCells());
|
|
||||||
extractor.setIncludeHeadersFooters(cmdArgs.shouldIncludeHeadersFooters());
|
|
||||||
System.out.println(extractor.getText());
|
|
||||||
extractor.close();
|
|
||||||
wb.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIncludeSheetNames(boolean includeSheetNames) {
|
|
||||||
_includeSheetNames = includeSheetNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFormulasNotResults(boolean formulasNotResults) {
|
|
||||||
_shouldEvaluateFormulas = !formulasNotResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIncludeCellComments(boolean includeCellComments) {
|
|
||||||
_includeCellComments = includeCellComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should blank cells be output? Default is to only
|
|
||||||
* output cells that are present in the file and are
|
|
||||||
* non-blank.
|
|
||||||
*
|
|
||||||
* @param includeBlankCells {@code true} if blank cells should be included
|
|
||||||
*/
|
|
||||||
public void setIncludeBlankCells(boolean includeBlankCells) {
|
|
||||||
_includeBlankCells = includeBlankCells;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIncludeHeadersFooters(boolean includeHeadersFooters) {
|
|
||||||
_includeHeadersFooters = includeHeadersFooters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getText() {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
|
|
||||||
// We don't care about the difference between
|
|
||||||
// null (missing) and blank cells
|
|
||||||
_wb.setMissingCellPolicy(MissingCellPolicy.RETURN_BLANK_AS_NULL);
|
|
||||||
|
|
||||||
// Process each sheet in turn
|
|
||||||
for(int i=0;i<_wb.getNumberOfSheets();i++) {
|
|
||||||
HSSFSheet sheet = _wb.getSheetAt(i);
|
|
||||||
if(sheet == null) { continue; }
|
|
||||||
|
|
||||||
if(_includeSheetNames) {
|
|
||||||
String name = _wb.getSheetName(i);
|
|
||||||
if(name != null) {
|
|
||||||
text.append(name);
|
|
||||||
text.append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header text, if there is any
|
|
||||||
if(_includeHeadersFooters) {
|
|
||||||
text.append(_extractHeaderFooter(sheet.getHeader()));
|
|
||||||
}
|
|
||||||
|
|
||||||
int firstRow = sheet.getFirstRowNum();
|
|
||||||
int lastRow = sheet.getLastRowNum();
|
|
||||||
for(int j=firstRow;j<=lastRow;j++) {
|
|
||||||
HSSFRow row = sheet.getRow(j);
|
|
||||||
if(row == null) { continue; }
|
|
||||||
|
|
||||||
// Check each cell in turn
|
|
||||||
int firstCell = row.getFirstCellNum();
|
|
||||||
int lastCell = row.getLastCellNum();
|
|
||||||
if(_includeBlankCells) {
|
|
||||||
firstCell = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int k=firstCell;k<lastCell;k++) {
|
|
||||||
HSSFCell cell = row.getCell(k);
|
|
||||||
boolean outputContents = true;
|
|
||||||
|
|
||||||
if(cell == null) {
|
|
||||||
// Only output if requested
|
|
||||||
outputContents = _includeBlankCells;
|
|
||||||
} else {
|
|
||||||
switch(cell.getCellTypeEnum()) {
|
|
||||||
case STRING:
|
|
||||||
text.append(cell.getRichStringCellValue().getString());
|
|
||||||
break;
|
|
||||||
case NUMERIC:
|
|
||||||
text.append(_formatter.formatCellValue(cell));
|
|
||||||
break;
|
|
||||||
case BOOLEAN:
|
|
||||||
text.append(cell.getBooleanCellValue());
|
|
||||||
break;
|
|
||||||
case ERROR:
|
|
||||||
text.append(ErrorEval.getText(cell.getErrorCellValue()));
|
|
||||||
break;
|
|
||||||
case FORMULA:
|
|
||||||
if(!_shouldEvaluateFormulas) {
|
|
||||||
text.append(cell.getCellFormula());
|
|
||||||
} else {
|
|
||||||
switch(cell.getCachedFormulaResultTypeEnum()) {
|
|
||||||
case STRING:
|
|
||||||
HSSFRichTextString str = cell.getRichStringCellValue();
|
|
||||||
if(str != null && str.length() > 0) {
|
|
||||||
text.append(str.toString());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NUMERIC:
|
|
||||||
HSSFCellStyle style = cell.getCellStyle();
|
|
||||||
double nVal = cell.getNumericCellValue();
|
|
||||||
short df = style.getDataFormat();
|
|
||||||
String dfs = style.getDataFormatString();
|
|
||||||
text.append(_formatter.formatRawCellContents(nVal, df, dfs));
|
|
||||||
break;
|
|
||||||
case BOOLEAN:
|
|
||||||
text.append(cell.getBooleanCellValue());
|
|
||||||
break;
|
|
||||||
case ERROR:
|
|
||||||
text.append(ErrorEval.getText(cell.getErrorCellValue()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unexpected cell cached formula result type: " + cell.getCachedFormulaResultTypeEnum());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellTypeEnum() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output the comment, if requested and exists
|
|
||||||
HSSFComment comment = cell.getCellComment();
|
|
||||||
if(_includeCellComments && comment != null) {
|
|
||||||
// Replace any newlines with spaces, otherwise it
|
|
||||||
// breaks the output
|
|
||||||
String commentText = comment.getString().getString().replace('\n', ' ');
|
|
||||||
text.append(" Comment by "+comment.getAuthor()+": "+commentText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output a tab if we're not on the last cell
|
|
||||||
if(outputContents && k < (lastCell-1)) {
|
|
||||||
text.append("\t");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish off the row
|
|
||||||
text.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally Footer text, if there is any
|
|
||||||
if(_includeHeadersFooters) {
|
|
||||||
text.append(_extractHeaderFooter(sheet.getFooter()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return text.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String _extractHeaderFooter(HeaderFooter hf) {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
|
|
||||||
if(hf.getLeft() != null) {
|
|
||||||
text.append(hf.getLeft());
|
|
||||||
}
|
|
||||||
if(hf.getCenter() != null) {
|
|
||||||
if(text.length() > 0)
|
|
||||||
text.append("\t");
|
|
||||||
text.append(hf.getCenter());
|
|
||||||
}
|
|
||||||
if(hf.getRight() != null) {
|
|
||||||
if(text.length() > 0)
|
|
||||||
text.append("\t");
|
|
||||||
text.append(hf.getRight());
|
|
||||||
}
|
|
||||||
if(text.length() > 0)
|
|
||||||
text.append("\n");
|
|
||||||
|
|
||||||
return text.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,324 +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.extractor;
|
|
||||||
|
|
||||||
import static org.apache.poi.hssf.model.InternalWorkbook.OLD_WORKBOOK_DIR_ENTRY_NAME;
|
|
||||||
import static org.apache.poi.hssf.model.InternalWorkbook.WORKBOOK_DIR_ENTRY_NAMES;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.apache.poi.EncryptedDocumentException;
|
|
||||||
import org.apache.poi.hssf.OldExcelFormatException;
|
|
||||||
import org.apache.poi.hssf.record.BOFRecord;
|
|
||||||
import org.apache.poi.hssf.record.CodepageRecord;
|
|
||||||
import org.apache.poi.hssf.record.FormulaRecord;
|
|
||||||
import org.apache.poi.hssf.record.NumberRecord;
|
|
||||||
import org.apache.poi.hssf.record.OldFormulaRecord;
|
|
||||||
import org.apache.poi.hssf.record.OldLabelRecord;
|
|
||||||
import org.apache.poi.hssf.record.OldSheetRecord;
|
|
||||||
import org.apache.poi.hssf.record.OldStringRecord;
|
|
||||||
import org.apache.poi.hssf.record.RKRecord;
|
|
||||||
import org.apache.poi.hssf.record.RecordInputStream;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.DocumentNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
import org.apache.poi.poifs.filesystem.NotOLE2FileException;
|
|
||||||
import org.apache.poi.ss.usermodel.CellType;
|
|
||||||
import org.apache.poi.util.IOUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A text extractor for old Excel files, which are too old for
|
|
||||||
* HSSFWorkbook to handle. This includes Excel 95, and very old
|
|
||||||
* (pre-OLE2) Excel files, such as Excel 4 files.
|
|
||||||
* <p>
|
|
||||||
* Returns much (but not all) of the textual content of the file,
|
|
||||||
* suitable for indexing by something like Apache Lucene, or used
|
|
||||||
* by Apache Tika, but not really intended for display to the user.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class OldExcelExtractor implements Closeable {
|
|
||||||
|
|
||||||
private final static int FILE_PASS_RECORD_SID = 0x2f;
|
|
||||||
|
|
||||||
private RecordInputStream ris;
|
|
||||||
|
|
||||||
// sometimes we hold the stream here and thus need to ensure it is closed at some point
|
|
||||||
private Closeable toClose;
|
|
||||||
|
|
||||||
private int biffVersion;
|
|
||||||
private int fileType;
|
|
||||||
|
|
||||||
public OldExcelExtractor(InputStream input) throws IOException {
|
|
||||||
open(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OldExcelExtractor(File f) throws IOException {
|
|
||||||
NPOIFSFileSystem poifs = null;
|
|
||||||
try {
|
|
||||||
poifs = new NPOIFSFileSystem(f);
|
|
||||||
open(poifs);
|
|
||||||
toClose = poifs;
|
|
||||||
return;
|
|
||||||
} catch (OldExcelFormatException e) {
|
|
||||||
// will be handled by workaround below
|
|
||||||
} catch (NotOLE2FileException e) {
|
|
||||||
// will be handled by workaround below
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ensure streams are closed correctly
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// ensure streams are closed correctly
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
if (toClose == null) {
|
|
||||||
IOUtils.closeQuietly(poifs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
|
||||||
FileInputStream biffStream = new FileInputStream(f); // NOSONAR
|
|
||||||
try {
|
|
||||||
open(biffStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ensure that the stream is properly closed here if an Exception
|
|
||||||
// is thrown while opening
|
|
||||||
biffStream.close();
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// ensure that the stream is properly closed here if an Exception
|
|
||||||
// is thrown while opening
|
|
||||||
biffStream.close();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OldExcelExtractor(NPOIFSFileSystem fs) throws IOException {
|
|
||||||
open(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OldExcelExtractor(DirectoryNode directory) throws IOException {
|
|
||||||
open(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void open(InputStream biffStream) throws IOException {
|
|
||||||
BufferedInputStream bis = (biffStream instanceof BufferedInputStream)
|
|
||||||
? (BufferedInputStream)biffStream
|
|
||||||
: new BufferedInputStream(biffStream, 8);
|
|
||||||
|
|
||||||
if (NPOIFSFileSystem.hasPOIFSHeader(bis)) {
|
|
||||||
NPOIFSFileSystem poifs = new NPOIFSFileSystem(bis);
|
|
||||||
try {
|
|
||||||
open(poifs);
|
|
||||||
} finally {
|
|
||||||
poifs.close();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ris = new RecordInputStream(bis);
|
|
||||||
toClose = bis;
|
|
||||||
prepare();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void open(NPOIFSFileSystem fs) throws IOException {
|
|
||||||
open(fs.getRoot());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void open(DirectoryNode directory) throws IOException {
|
|
||||||
DocumentNode book;
|
|
||||||
try {
|
|
||||||
book = (DocumentNode)directory.getEntry(OLD_WORKBOOK_DIR_ENTRY_NAME);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
// some files have "Workbook" instead
|
|
||||||
book = (DocumentNode)directory.getEntry(WORKBOOK_DIR_ENTRY_NAMES[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (book == null) {
|
|
||||||
throw new IOException("No Excel 5/95 Book stream found");
|
|
||||||
}
|
|
||||||
|
|
||||||
ris = new RecordInputStream(directory.createDocumentInputStream(book));
|
|
||||||
prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
if (args.length < 1) {
|
|
||||||
System.err.println("Use:");
|
|
||||||
System.err.println(" OldExcelExtractor <filename>");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
OldExcelExtractor extractor = new OldExcelExtractor(new File(args[0]));
|
|
||||||
System.out.println(extractor.getText());
|
|
||||||
extractor.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepare() {
|
|
||||||
if (! ris.hasNextRecord()) {
|
|
||||||
throw new IllegalArgumentException("File contains no records!");
|
|
||||||
}
|
|
||||||
ris.nextRecord();
|
|
||||||
|
|
||||||
// Work out what version we're dealing with
|
|
||||||
int bofSid = ris.getSid();
|
|
||||||
switch (bofSid) {
|
|
||||||
case BOFRecord.biff2_sid:
|
|
||||||
biffVersion = 2;
|
|
||||||
break;
|
|
||||||
case BOFRecord.biff3_sid:
|
|
||||||
biffVersion = 3;
|
|
||||||
break;
|
|
||||||
case BOFRecord.biff4_sid:
|
|
||||||
biffVersion = 4;
|
|
||||||
break;
|
|
||||||
case BOFRecord.biff5_sid:
|
|
||||||
biffVersion = 5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("File does not begin with a BOF, found sid of " + bofSid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the type
|
|
||||||
BOFRecord bof = new BOFRecord(ris);
|
|
||||||
fileType = bof.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Biff version, largely corresponding to the Excel version
|
|
||||||
*
|
|
||||||
* @return the Biff version
|
|
||||||
*/
|
|
||||||
public int getBiffVersion() {
|
|
||||||
return biffVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The kind of the file, one of {@link BOFRecord#TYPE_WORKSHEET},
|
|
||||||
* {@link BOFRecord#TYPE_CHART}, {@link BOFRecord#TYPE_EXCEL_4_MACRO}
|
|
||||||
* or {@link BOFRecord#TYPE_WORKSPACE_FILE}
|
|
||||||
*
|
|
||||||
* @return the file type
|
|
||||||
*/
|
|
||||||
public int getFileType() {
|
|
||||||
return fileType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the text contents of the file, as best we can
|
|
||||||
* for these old file formats
|
|
||||||
*
|
|
||||||
* @return the text contents of the file
|
|
||||||
*/
|
|
||||||
public String getText() {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
|
|
||||||
// To track formats and encodings
|
|
||||||
CodepageRecord codepage = null;
|
|
||||||
// TODO track the XFs and Format Strings
|
|
||||||
|
|
||||||
// Process each record in turn, looking for interesting ones
|
|
||||||
while (ris.hasNextRecord()) {
|
|
||||||
int sid = ris.getNextSid();
|
|
||||||
ris.nextRecord();
|
|
||||||
|
|
||||||
switch (sid) {
|
|
||||||
case FILE_PASS_RECORD_SID:
|
|
||||||
throw new EncryptedDocumentException("Encryption not supported for Old Excel files");
|
|
||||||
|
|
||||||
case OldSheetRecord.sid:
|
|
||||||
OldSheetRecord shr = new OldSheetRecord(ris);
|
|
||||||
shr.setCodePage(codepage);
|
|
||||||
text.append("Sheet: ");
|
|
||||||
text.append(shr.getSheetname());
|
|
||||||
text.append('\n');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OldLabelRecord.biff2_sid:
|
|
||||||
case OldLabelRecord.biff345_sid:
|
|
||||||
OldLabelRecord lr = new OldLabelRecord(ris);
|
|
||||||
lr.setCodePage(codepage);
|
|
||||||
text.append(lr.getValue());
|
|
||||||
text.append('\n');
|
|
||||||
break;
|
|
||||||
case OldStringRecord.biff2_sid:
|
|
||||||
case OldStringRecord.biff345_sid:
|
|
||||||
OldStringRecord sr = new OldStringRecord(ris);
|
|
||||||
sr.setCodePage(codepage);
|
|
||||||
text.append(sr.getString());
|
|
||||||
text.append('\n');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NumberRecord.sid:
|
|
||||||
NumberRecord nr = new NumberRecord(ris);
|
|
||||||
handleNumericCell(text, nr.getValue());
|
|
||||||
break;
|
|
||||||
case OldFormulaRecord.biff2_sid:
|
|
||||||
case OldFormulaRecord.biff3_sid:
|
|
||||||
case OldFormulaRecord.biff4_sid:
|
|
||||||
// Biff 2 and 5+ share the same SID, due to a bug...
|
|
||||||
if (biffVersion == 5) {
|
|
||||||
FormulaRecord fr = new FormulaRecord(ris);
|
|
||||||
if (fr.getCachedResultType() == CellType.NUMERIC.getCode()) {
|
|
||||||
handleNumericCell(text, fr.getValue());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
OldFormulaRecord fr = new OldFormulaRecord(ris);
|
|
||||||
if (fr.getCachedResultType() == CellType.NUMERIC.getCode()) {
|
|
||||||
handleNumericCell(text, fr.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RKRecord.sid:
|
|
||||||
RKRecord rr = new RKRecord(ris);
|
|
||||||
handleNumericCell(text, rr.getRKNumber());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CodepageRecord.sid:
|
|
||||||
codepage = new CodepageRecord(ris);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ris.readFully(new byte[ris.remaining()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
ris = null;
|
|
||||||
|
|
||||||
return text.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// some cases require this close here
|
|
||||||
if(toClose != null) {
|
|
||||||
IOUtils.closeQuietly(toClose);
|
|
||||||
toClose = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleNumericCell(StringBuffer text, double value) {
|
|
||||||
// TODO Need to fetch / use format strings
|
|
||||||
text.append(value);
|
|
||||||
text.append('\n');
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,7 +42,6 @@ import org.apache.poi.ddf.EscherRecord;
|
|||||||
import org.apache.poi.ddf.EscherSimpleProperty;
|
import org.apache.poi.ddf.EscherSimpleProperty;
|
||||||
import org.apache.poi.ddf.EscherSpRecord;
|
import org.apache.poi.ddf.EscherSpRecord;
|
||||||
import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
|
import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
|
||||||
import org.apache.poi.hssf.extractor.OldExcelExtractor;
|
|
||||||
import org.apache.poi.hssf.record.BOFRecord;
|
import org.apache.poi.hssf.record.BOFRecord;
|
||||||
import org.apache.poi.hssf.record.BackupRecord;
|
import org.apache.poi.hssf.record.BackupRecord;
|
||||||
import org.apache.poi.hssf.record.BookBoolRecord;
|
import org.apache.poi.hssf.record.BookBoolRecord;
|
||||||
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.apache.poi.hssf.dev.BiffViewer;
|
|
||||||
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
|
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
|
||||||
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
||||||
import org.apache.poi.util.Internal;
|
import org.apache.poi.util.Internal;
|
||||||
|
@ -80,13 +80,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
|
|||||||
* @param boundAggregate -low level representation of all binary data inside sheet
|
* @param boundAggregate -low level representation of all binary data inside sheet
|
||||||
*/
|
*/
|
||||||
HSSFPatriarch(HSSFSheet sheet, EscherAggregate boundAggregate) {
|
HSSFPatriarch(HSSFSheet sheet, EscherAggregate boundAggregate) {
|
||||||
_sheet = sheet;
|
throw new IllegalStateException("support for this has been removed in poi-fast-calc");
|
||||||
_boundAggregate = boundAggregate;
|
|
||||||
_mainSpgrContainer = _boundAggregate.getEscherContainer().getChildContainers().get(0);
|
|
||||||
EscherContainerRecord spContainer = (EscherContainerRecord) _boundAggregate.getEscherContainer()
|
|
||||||
.getChildContainers().get(0).getChild(0);
|
|
||||||
_spgrRecord = spContainer.getChildById(EscherSpgrRecord.RECORD_ID);
|
|
||||||
buildShapeTree();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,77 +217,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
|
public HSSFObjectData createObjectData(ClientAnchor anchor, int storageId, int pictureIndex) {
|
||||||
ObjRecord obj = new ObjRecord();
|
throw new IllegalStateException("support for this has been removed in poi-fast-calc");
|
||||||
|
|
||||||
CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord();
|
|
||||||
ftCmo.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE);
|
|
||||||
// ftCmo.setObjectId(oleShape.getShapeId()); ... will be set by onCreate(...)
|
|
||||||
ftCmo.setLocked(true);
|
|
||||||
ftCmo.setPrintable(true);
|
|
||||||
ftCmo.setAutofill(true);
|
|
||||||
ftCmo.setAutoline(true);
|
|
||||||
ftCmo.setReserved1(0);
|
|
||||||
ftCmo.setReserved2(0);
|
|
||||||
ftCmo.setReserved3(0);
|
|
||||||
obj.addSubRecord(ftCmo);
|
|
||||||
|
|
||||||
// FtCf (pictFormat)
|
|
||||||
FtCfSubRecord ftCf = new FtCfSubRecord();
|
|
||||||
HSSFPictureData pictData = getSheet().getWorkbook().getAllPictures().get(pictureIndex-1);
|
|
||||||
switch (pictData.getFormat()) {
|
|
||||||
case Workbook.PICTURE_TYPE_WMF:
|
|
||||||
case Workbook.PICTURE_TYPE_EMF:
|
|
||||||
// this needs patch #49658 to be applied to actually work
|
|
||||||
ftCf.setFlags(FtCfSubRecord.METAFILE_BIT);
|
|
||||||
break;
|
|
||||||
case Workbook.PICTURE_TYPE_DIB:
|
|
||||||
case Workbook.PICTURE_TYPE_PNG:
|
|
||||||
case Workbook.PICTURE_TYPE_JPEG:
|
|
||||||
case Workbook.PICTURE_TYPE_PICT:
|
|
||||||
ftCf.setFlags(FtCfSubRecord.BITMAP_BIT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Invalid picture type: " + pictData.getFormat());
|
|
||||||
}
|
|
||||||
obj.addSubRecord(ftCf);
|
|
||||||
// FtPioGrbit (pictFlags)
|
|
||||||
FtPioGrbitSubRecord ftPioGrbit = new FtPioGrbitSubRecord();
|
|
||||||
ftPioGrbit.setFlagByBit(FtPioGrbitSubRecord.AUTO_PICT_BIT, true);
|
|
||||||
obj.addSubRecord(ftPioGrbit);
|
|
||||||
|
|
||||||
EmbeddedObjectRefSubRecord ftPictFmla = new EmbeddedObjectRefSubRecord();
|
|
||||||
ftPictFmla.setUnknownFormulaData(new byte[]{2, 0, 0, 0, 0});
|
|
||||||
ftPictFmla.setOleClassname("Paket");
|
|
||||||
ftPictFmla.setStorageId(storageId);
|
|
||||||
|
|
||||||
obj.addSubRecord(ftPictFmla);
|
|
||||||
obj.addSubRecord(new EndSubRecord());
|
|
||||||
|
|
||||||
String entryName = "MBD"+HexDump.toHex(storageId);
|
|
||||||
DirectoryEntry oleRoot;
|
|
||||||
try {
|
|
||||||
DirectoryNode dn = _sheet.getWorkbook().getDirectory();
|
|
||||||
if (dn == null) {
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
}
|
|
||||||
oleRoot = (DirectoryEntry)dn.getEntry(entryName);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new IllegalStateException("trying to add ole shape without actually adding data first - use HSSFWorkbook.addOlePackage first", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create picture shape, which need to be minimal modified for oleshapes
|
|
||||||
HSSFPicture shape = new HSSFPicture(null, (HSSFClientAnchor)anchor);
|
|
||||||
shape.setPictureIndex(pictureIndex);
|
|
||||||
EscherContainerRecord spContainer = shape.getEscherContainer();
|
|
||||||
EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
|
|
||||||
spRecord.setFlags(spRecord.getFlags() | EscherSpRecord.FLAG_OLESHAPE);
|
|
||||||
|
|
||||||
HSSFObjectData oleShape = new HSSFObjectData(spContainer, obj, oleRoot);
|
|
||||||
addShape(oleShape);
|
|
||||||
onCreate(oleShape);
|
|
||||||
|
|
||||||
|
|
||||||
return oleShape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -532,26 +456,6 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing<HSSFShap
|
|||||||
throw new RuntimeException("NotImplemented");
|
throw new RuntimeException("NotImplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create shape tree from existing escher records tree
|
|
||||||
*/
|
|
||||||
void buildShapeTree() {
|
|
||||||
EscherContainerRecord dgContainer = _boundAggregate.getEscherContainer();
|
|
||||||
if (dgContainer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
EscherContainerRecord spgrConrainer = dgContainer.getChildContainers().get(0);
|
|
||||||
List<EscherContainerRecord> spgrChildren = spgrConrainer.getChildContainers();
|
|
||||||
|
|
||||||
for (int i = 0; i < spgrChildren.size(); i++) {
|
|
||||||
EscherContainerRecord spContainer = spgrChildren.get(i);
|
|
||||||
if (i != 0) {
|
|
||||||
HSSFShapeFactory.createShapeTree(spContainer, _boundAggregate, this, _sheet.getWorkbook().getDirectory());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFlipFlags(HSSFShape shape){
|
private void setFlipFlags(HSSFShape shape){
|
||||||
EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID);
|
||||||
if (shape.getAnchor().isHorizontallyFlipped()) {
|
if (shape.getAnchor().isHorizontallyFlipped()) {
|
||||||
|
@ -17,16 +17,8 @@
|
|||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.hssf.usermodel;
|
||||||
|
|
||||||
import static org.apache.poi.hssf.model.InternalWorkbook.OLD_WORKBOOK_DIR_ENTRY_NAME;
|
|
||||||
import static org.apache.poi.hssf.model.InternalWorkbook.WORKBOOK_DIR_ENTRY_NAMES;
|
import static org.apache.poi.hssf.model.InternalWorkbook.WORKBOOK_DIR_ENTRY_NAMES;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@ -75,20 +67,7 @@ import org.apache.poi.hssf.record.SSTRecord;
|
|||||||
import org.apache.poi.hssf.record.UnknownRecord;
|
import org.apache.poi.hssf.record.UnknownRecord;
|
||||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
||||||
import org.apache.poi.hssf.record.common.UnicodeString;
|
import org.apache.poi.hssf.record.common.UnicodeString;
|
||||||
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
|
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
import org.apache.poi.hssf.util.CellReference;
|
||||||
import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
|
|
||||||
import org.apache.poi.poifs.crypt.Decryptor;
|
|
||||||
import org.apache.poi.poifs.crypt.Encryptor;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.DocumentNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.EntryUtils;
|
|
||||||
import org.apache.poi.poifs.filesystem.FilteringDirectoryNode;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSDocument;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
import org.apache.poi.poifs.filesystem.Ole10Native;
|
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.ss.SpreadsheetVersion;
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
import org.apache.poi.ss.formula.FormulaShifter;
|
import org.apache.poi.ss.formula.FormulaShifter;
|
||||||
import org.apache.poi.ss.formula.FormulaType;
|
import org.apache.poi.ss.formula.FormulaType;
|
||||||
@ -104,9 +83,6 @@ import org.apache.poi.ss.usermodel.Workbook;
|
|||||||
import org.apache.poi.util.Configurator;
|
import org.apache.poi.util.Configurator;
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
import org.apache.poi.util.Internal;
|
import org.apache.poi.util.Internal;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianByteArrayInputStream;
|
|
||||||
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
|
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.Removal;
|
import org.apache.poi.util.Removal;
|
||||||
@ -119,7 +95,7 @@ import org.apache.poi.util.Removal;
|
|||||||
* @see org.apache.poi.hssf.model.InternalWorkbook
|
* @see org.apache.poi.hssf.model.InternalWorkbook
|
||||||
* @see org.apache.poi.hssf.usermodel.HSSFSheet
|
* @see org.apache.poi.hssf.usermodel.HSSFSheet
|
||||||
*/
|
*/
|
||||||
public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
|
public final class HSSFWorkbook implements org.apache.poi.ss.usermodel.Workbook {
|
||||||
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
|
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,14 +177,14 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
private UDFFinder _udfFinder = new IndexedUDFFinder(AggregatingUDFFinder.DEFAULT);
|
private UDFFinder _udfFinder = new IndexedUDFFinder(AggregatingUDFFinder.DEFAULT);
|
||||||
|
|
||||||
public static HSSFWorkbook create(InternalWorkbook book) {
|
public static HSSFWorkbook create(InternalWorkbook book) {
|
||||||
return new HSSFWorkbook(book);
|
return new HSSFWorkbook(book, SpreadsheetVersion.EXCEL2007);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Creates new HSSFWorkbook from scratch (start here!)
|
* Creates new HSSFWorkbook from scratch (start here!)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public HSSFWorkbook() {
|
public HSSFWorkbook() {
|
||||||
this(InternalWorkbook.createWorkbook());
|
this(InternalWorkbook.createWorkbook(), SpreadsheetVersion.EXCEL2007);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HSSFWorkbook(final SpreadsheetVersion spreadsheetVersion) {
|
public HSSFWorkbook(final SpreadsheetVersion spreadsheetVersion) {
|
||||||
@ -217,210 +193,13 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
throw new IllegalArgumentException("SpreadsheetVersion must be non-null");
|
throw new IllegalArgumentException("SpreadsheetVersion must be non-null");
|
||||||
}
|
}
|
||||||
|
|
||||||
private HSSFWorkbook(InternalWorkbook book) {
|
|
||||||
this(book, SpreadsheetVersion.EXCEL97);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HSSFWorkbook(InternalWorkbook book, final SpreadsheetVersion spreadsheetVersion) {
|
private HSSFWorkbook(InternalWorkbook book, final SpreadsheetVersion spreadsheetVersion) {
|
||||||
super((DirectoryNode)null);
|
|
||||||
this.spreadsheetVersion = spreadsheetVersion;
|
this.spreadsheetVersion = spreadsheetVersion;
|
||||||
workbook = book;
|
workbook = book;
|
||||||
_sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
|
_sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
|
||||||
names = new ArrayList<HSSFName>(INITIAL_CAPACITY);
|
names = new ArrayList<HSSFName>(INITIAL_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a POI POIFSFileSystem object, read in its Workbook along
|
|
||||||
* with all related nodes, and populate the high and low level models.
|
|
||||||
* <p>This calls {@link #HSSFWorkbook(POIFSFileSystem, boolean)} with
|
|
||||||
* preserve nodes set to true.
|
|
||||||
*
|
|
||||||
* @see #HSSFWorkbook(POIFSFileSystem, boolean)
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(POIFSFileSystem fs) throws IOException {
|
|
||||||
this(fs,true);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Given a POI POIFSFileSystem object, read in its Workbook along
|
|
||||||
* with all related nodes, and populate the high and low level models.
|
|
||||||
* <p>This calls {@link #HSSFWorkbook(POIFSFileSystem, boolean)} with
|
|
||||||
* preserve nodes set to true.
|
|
||||||
*
|
|
||||||
* @see #HSSFWorkbook(POIFSFileSystem, boolean)
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(NPOIFSFileSystem fs) throws IOException {
|
|
||||||
this(fs.getRoot(),true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a POI POIFSFileSystem object, read in its Workbook and populate
|
|
||||||
* the high and low level models. If you're reading in a workbook... start here!
|
|
||||||
*
|
|
||||||
* @param fs the POI filesystem that contains the Workbook stream.
|
|
||||||
* @param preserveNodes whether to preserve other nodes, such as
|
|
||||||
* macros. This takes more memory, so only say yes if you
|
|
||||||
* need to. If set, will store all of the POIFSFileSystem
|
|
||||||
* in memory
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes)
|
|
||||||
throws IOException {
|
|
||||||
this(fs.getRoot(), fs, preserveNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getWorkbookDirEntryName(DirectoryNode directory) {
|
|
||||||
|
|
||||||
for (String wbName : WORKBOOK_DIR_ENTRY_NAMES) {
|
|
||||||
try {
|
|
||||||
directory.getEntry(wbName);
|
|
||||||
return wbName;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
// continue - to try other options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for an encrypted .xlsx file - they get OLE2 wrapped
|
|
||||||
try {
|
|
||||||
directory.getEntry(Decryptor.DEFAULT_POIFS_ENTRY);
|
|
||||||
throw new EncryptedDocumentException("The supplied spreadsheet seems to be an Encrypted .xlsx file. " +
|
|
||||||
"It must be decrypted before use by XSSF, it cannot be used by HSSF");
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for previous version of file format
|
|
||||||
try {
|
|
||||||
directory.getEntry(OLD_WORKBOOK_DIR_ENTRY_NAME);
|
|
||||||
throw new OldExcelFormatException("The supplied spreadsheet seems to be Excel 5.0/7.0 (BIFF5) format. "
|
|
||||||
+ "POI only supports BIFF8 format (from Excel versions 97/2000/XP/2003)");
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("The supplied POIFSFileSystem does not contain a BIFF8 'Workbook' entry. "
|
|
||||||
+ "Is it really an excel file? Had: " + directory.getEntryNames());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a POI POIFSFileSystem object, and a specific directory
|
|
||||||
* within it, read in its Workbook and populate the high and
|
|
||||||
* low level models. If you're reading in a workbook...start here.
|
|
||||||
*
|
|
||||||
* @param directory the POI filesystem directory to process from
|
|
||||||
* @param fs the POI filesystem that contains the Workbook stream.
|
|
||||||
* @param preserveNodes whether to preserve other nodes, such as
|
|
||||||
* macros. This takes more memory, so only say yes if you
|
|
||||||
* need to. If set, will store all of the POIFSFileSystem
|
|
||||||
* in memory
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this(directory, preserveNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* given a POI POIFSFileSystem object, and a specific directory
|
|
||||||
* within it, read in its Workbook and populate the high and
|
|
||||||
* low level models. If you're reading in a workbook...start here.
|
|
||||||
*
|
|
||||||
* @param directory the POI filesystem directory to process from
|
|
||||||
* @param preserveNodes whether to preserve other nodes, such as
|
|
||||||
* macros. This takes more memory, so only say yes if you
|
|
||||||
* need to. If set, will store all of the POIFSFileSystem
|
|
||||||
* in memory
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
super(directory);
|
|
||||||
spreadsheetVersion = SpreadsheetVersion.EXCEL97;
|
|
||||||
String workbookName = getWorkbookDirEntryName(directory);
|
|
||||||
|
|
||||||
this.preserveNodes = preserveNodes;
|
|
||||||
|
|
||||||
// If we're not preserving nodes, don't track the
|
|
||||||
// POIFS any more
|
|
||||||
if(! preserveNodes) {
|
|
||||||
clearDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
_sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
|
|
||||||
names = new ArrayList<HSSFName>(INITIAL_CAPACITY);
|
|
||||||
|
|
||||||
// Grab the data from the workbook stream, however
|
|
||||||
// it happens to be spelled.
|
|
||||||
InputStream stream = directory.createDocumentInputStream(workbookName);
|
|
||||||
|
|
||||||
List<Record> records = RecordFactory.createRecords(stream);
|
|
||||||
|
|
||||||
workbook = InternalWorkbook.createWorkbook(records);
|
|
||||||
setPropertiesFromWorkbook(workbook);
|
|
||||||
int recOffset = workbook.getNumRecords();
|
|
||||||
|
|
||||||
// convert all LabelRecord records to LabelSSTRecord
|
|
||||||
convertLabelRecords(records, recOffset);
|
|
||||||
RecordStream rs = new RecordStream(records, recOffset);
|
|
||||||
while (rs.hasNext()) {
|
|
||||||
try {
|
|
||||||
InternalSheet sheet = InternalSheet.createSheet(rs);
|
|
||||||
_sheets.add(new HSSFSheet(this, sheet));
|
|
||||||
} catch (UnsupportedBOFType eb) {
|
|
||||||
// Hopefully there's a supported one after this!
|
|
||||||
log.log(POILogger.WARN, "Unsupported BOF found of type " + eb.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0 ; i < workbook.getNumNames() ; ++i){
|
|
||||||
NameRecord nameRecord = workbook.getNameRecord(i);
|
|
||||||
HSSFName name = new HSSFName(this, nameRecord, workbook.getNameCommentRecord(nameRecord));
|
|
||||||
names.add(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Companion to HSSFWorkbook(POIFSFileSystem), this constructs the
|
|
||||||
* POI filesystem around your {@link InputStream}, including all nodes.
|
|
||||||
* <p>This calls {@link #HSSFWorkbook(InputStream, boolean)} with
|
|
||||||
* preserve nodes set to true.
|
|
||||||
*
|
|
||||||
* @see #HSSFWorkbook(InputStream, boolean)
|
|
||||||
* @see #HSSFWorkbook(POIFSFileSystem)
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
public HSSFWorkbook(InputStream s) throws IOException {
|
|
||||||
this(s,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Companion to HSSFWorkbook(POIFSFileSystem), this constructs the
|
|
||||||
* POI filesystem around your {@link InputStream}.
|
|
||||||
*
|
|
||||||
* @param s the POI filesystem that contains the Workbook stream.
|
|
||||||
* @param preserveNodes whether to preserve other nodes, such as
|
|
||||||
* macros. This takes more memory, so only say yes if you
|
|
||||||
* need to.
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
* @see #HSSFWorkbook(POIFSFileSystem)
|
|
||||||
* @exception IOException if the stream cannot be read
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("resource") // NPOIFSFileSystem always closes the stream
|
|
||||||
public HSSFWorkbook(InputStream s, boolean preserveNodes)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this(new NPOIFSFileSystem(s).getRoot(), preserveNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used internally to set the workbook properties.
|
* used internally to set the workbook properties.
|
||||||
*/
|
*/
|
||||||
@ -1366,139 +1145,12 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
return new HSSFCellStyle((short)idx, xfr, this);
|
return new HSSFCellStyle((short)idx, xfr, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the underlying {@link NPOIFSFileSystem} from which
|
|
||||||
* the Workbook was read, if any.
|
|
||||||
*
|
|
||||||
* <p>Once this has been called, no further
|
|
||||||
* operations, updates or reads should be performed on the
|
|
||||||
* Workbook.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void validateSpreadsheetVersionWritePossible() throws IllegalStateException {
|
protected void validateSpreadsheetVersionWritePossible() throws IllegalStateException {
|
||||||
if (this.spreadsheetVersion != SpreadsheetVersion.EXCEL97) {
|
if (this.spreadsheetVersion != SpreadsheetVersion.EXCEL97) {
|
||||||
throw new IllegalStateException("SpreadsheetVersion not EXCEL97, cannot write file meant only for in-memory calculations");
|
throw new IllegalStateException("SpreadsheetVersion not EXCEL97, cannot write file meant only for in-memory calculations");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Write out this workbook to the currently open {@link File} via the
|
|
||||||
* writeable {@link POIFSFileSystem} it was opened as.
|
|
||||||
*
|
|
||||||
* <p>This will fail (with an {@link IllegalStateException} if the
|
|
||||||
* Workbook was opened read-only, opened from an {@link InputStream}
|
|
||||||
* instead of a File, or if this is not the root document. For those cases,
|
|
||||||
* you must use {@link #write(OutputStream)} or {@link #write(File)} to
|
|
||||||
* write to a brand new document.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write() throws IOException {
|
|
||||||
validateSpreadsheetVersionWritePossible();
|
|
||||||
validateInPlaceWritePossible();
|
|
||||||
final DirectoryNode dir = getDirectory();
|
|
||||||
|
|
||||||
// Update the Workbook stream in the file
|
|
||||||
DocumentNode workbookNode = (DocumentNode)dir.getEntry(
|
|
||||||
getWorkbookDirEntryName(dir));
|
|
||||||
NPOIFSDocument workbookDoc = new NPOIFSDocument(workbookNode);
|
|
||||||
workbookDoc.replaceContents(new ByteArrayInputStream(getBytes()));
|
|
||||||
|
|
||||||
// Update the properties streams in the file
|
|
||||||
writeProperties();
|
|
||||||
|
|
||||||
// Sync with the File on disk
|
|
||||||
dir.getFileSystem().writeFilesystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method write - write out this workbook to a new {@link File}. Constructs
|
|
||||||
* a new POI POIFSFileSystem, passes in the workbook binary representation and
|
|
||||||
* writes it out. If the file exists, it will be replaced, otherwise a new one
|
|
||||||
* will be created.
|
|
||||||
*
|
|
||||||
* Note that you cannot write to the currently open File using this method.
|
|
||||||
* If you opened your Workbook from a File, you <i>must</i> use the {@link #write()}
|
|
||||||
* method instead!
|
|
||||||
*
|
|
||||||
* @param newFile The new File you wish to write the XLS to
|
|
||||||
*
|
|
||||||
* @exception IOException if anything can't be written.
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(File newFile) throws IOException {
|
|
||||||
validateSpreadsheetVersionWritePossible();
|
|
||||||
POIFSFileSystem fs = POIFSFileSystem.create(newFile);
|
|
||||||
try {
|
|
||||||
write(fs);
|
|
||||||
fs.writeFilesystem();
|
|
||||||
} finally {
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method write - write out this workbook to an {@link OutputStream}. Constructs
|
|
||||||
* a new POI POIFSFileSystem, passes in the workbook binary representation and
|
|
||||||
* writes it out.
|
|
||||||
*
|
|
||||||
* If {@code stream} is a {@link java.io.FileOutputStream} on a networked drive
|
|
||||||
* or has a high cost/latency associated with each written byte,
|
|
||||||
* consider wrapping the OutputStream in a {@link java.io.BufferedOutputStream}
|
|
||||||
* to improve write performance.
|
|
||||||
*
|
|
||||||
* @param stream - the java OutputStream you wish to write the XLS to
|
|
||||||
*
|
|
||||||
* @exception IOException if anything can't be written.
|
|
||||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(OutputStream stream) throws IOException {
|
|
||||||
validateSpreadsheetVersionWritePossible();
|
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem();
|
|
||||||
try {
|
|
||||||
write(fs);
|
|
||||||
fs.writeFilesystem(stream);
|
|
||||||
} finally {
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Writes the workbook out to a brand new, empty POIFS */
|
|
||||||
private void write(NPOIFSFileSystem fs) throws IOException {
|
|
||||||
validateSpreadsheetVersionWritePossible();
|
|
||||||
// For tracking what we've written out, used if we're
|
|
||||||
// going to be preserving nodes
|
|
||||||
List<String> excepts = new ArrayList<String>(1);
|
|
||||||
|
|
||||||
// Write out the Workbook stream
|
|
||||||
fs.createDocument(new ByteArrayInputStream(getBytes()), "Workbook");
|
|
||||||
|
|
||||||
// Write out our HPFS properties, if we have them
|
|
||||||
writeProperties(fs, excepts);
|
|
||||||
|
|
||||||
if (preserveNodes) {
|
|
||||||
// Don't write out the old Workbook, we'll be doing our new one
|
|
||||||
// If the file had an "incorrect" name for the workbook stream,
|
|
||||||
// don't write the old one as we'll use the correct name shortly
|
|
||||||
excepts.addAll(Arrays.asList(WORKBOOK_DIR_ENTRY_NAMES));
|
|
||||||
|
|
||||||
// Copy over all the other nodes to our new poifs
|
|
||||||
EntryUtils.copyNodes(
|
|
||||||
new FilteringDirectoryNode(getDirectory(), excepts)
|
|
||||||
, new FilteringDirectoryNode(fs.getRoot(), excepts)
|
|
||||||
);
|
|
||||||
|
|
||||||
// YK: preserve StorageClsid, it is important for embedded workbooks,
|
|
||||||
// see Bugzilla 47920
|
|
||||||
fs.getRoot().setStorageClsid(getDirectory().getStorageClsid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Totals the sizes of all sheet records and eventually serializes them
|
* Totals the sizes of all sheet records and eventually serializes them
|
||||||
*/
|
*/
|
||||||
@ -1529,125 +1181,6 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method getBytes - get the bytes of just the HSSF portions of the XLS file.
|
|
||||||
* Use this to construct a POI POIFSFileSystem yourself.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return byte[] array containing the binary representation of this workbook and all contained
|
|
||||||
* sheets, rows, cells, etc.
|
|
||||||
*/
|
|
||||||
public byte[] getBytes() {
|
|
||||||
if (log.check( POILogger.DEBUG )) {
|
|
||||||
log.log(DEBUG, "HSSFWorkbook.getBytes()");
|
|
||||||
}
|
|
||||||
|
|
||||||
HSSFSheet[] sheets = getSheets();
|
|
||||||
int nSheets = sheets.length;
|
|
||||||
|
|
||||||
|
|
||||||
// before getting the workbook size we must tell the sheets that
|
|
||||||
// serialization is about to occur.
|
|
||||||
workbook.preSerialize();
|
|
||||||
for (HSSFSheet sheet : sheets) {
|
|
||||||
sheet.getSheet().preSerialize();
|
|
||||||
sheet.preSerialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalsize = workbook.getSize();
|
|
||||||
|
|
||||||
// pre-calculate all the sheet sizes and set BOF indexes
|
|
||||||
SheetRecordCollector[] srCollectors = new SheetRecordCollector[nSheets];
|
|
||||||
for (int k = 0; k < nSheets; k++) {
|
|
||||||
workbook.setSheetBof(k, totalsize);
|
|
||||||
SheetRecordCollector src = new SheetRecordCollector();
|
|
||||||
sheets[k].getSheet().visitContainedRecords(src, totalsize);
|
|
||||||
totalsize += src.getTotalSize();
|
|
||||||
srCollectors[k] = src;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] retval = new byte[totalsize];
|
|
||||||
int pos = workbook.serialize(0, retval);
|
|
||||||
|
|
||||||
for (int k = 0; k < nSheets; k++) {
|
|
||||||
SheetRecordCollector src = srCollectors[k];
|
|
||||||
int serializedSize = src.serialize(pos, retval);
|
|
||||||
if (serializedSize != src.getTotalSize()) {
|
|
||||||
// Wrong offset values have been passed in the call to setSheetBof() above.
|
|
||||||
// For books with more than one sheet, this discrepancy would cause excel
|
|
||||||
// to report errors and loose data while reading the workbook
|
|
||||||
throw new IllegalStateException("Actual serialized sheet size (" + serializedSize
|
|
||||||
+ ") differs from pre-calculated size (" + src.getTotalSize()
|
|
||||||
+ ") for sheet (" + k + ")");
|
|
||||||
// TODO - add similar sanity check to ensure that Sheet.serializeIndexRecord() does not write mis-aligned offsets either
|
|
||||||
}
|
|
||||||
pos += serializedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptBytes(retval);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
|
||||||
protected void encryptBytes(byte buf[]) {
|
|
||||||
int initialOffset = 0;
|
|
||||||
FilePassRecord fpr = null;
|
|
||||||
for (Record r : workbook.getRecords()) {
|
|
||||||
initialOffset += r.getRecordSize();
|
|
||||||
if (r instanceof FilePassRecord) {
|
|
||||||
fpr = (FilePassRecord)r;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fpr == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndianByteArrayInputStream plain = new LittleEndianByteArrayInputStream(buf, 0); // NOSONAR
|
|
||||||
LittleEndianByteArrayOutputStream leos = new LittleEndianByteArrayOutputStream(buf, 0); // NOSONAR
|
|
||||||
Encryptor enc = fpr.getEncryptionInfo().getEncryptor();
|
|
||||||
enc.setChunkSize(Biff8DecryptingStream.RC4_REKEYING_INTERVAL);
|
|
||||||
byte tmp[] = new byte[1024];
|
|
||||||
try {
|
|
||||||
ChunkedCipherOutputStream os = enc.getDataStream(leos, initialOffset);
|
|
||||||
int totalBytes = 0;
|
|
||||||
while (totalBytes < buf.length) {
|
|
||||||
plain.read(tmp, 0, 4);
|
|
||||||
final int sid = LittleEndian.getUShort(tmp, 0);
|
|
||||||
final int len = LittleEndian.getUShort(tmp, 2);
|
|
||||||
boolean isPlain = Biff8DecryptingStream.isNeverEncryptedRecord(sid);
|
|
||||||
os.setNextRecordSize(len, isPlain);
|
|
||||||
os.writePlain(tmp, 0, 4);
|
|
||||||
if (sid == BoundSheetRecord.sid) {
|
|
||||||
// special case for the field_1_position_of_BOF (=lbPlyPos) field of
|
|
||||||
// the BoundSheet8 record which must be unencrypted
|
|
||||||
byte bsrBuf[] = new byte[len];
|
|
||||||
plain.readFully(bsrBuf);
|
|
||||||
os.writePlain(bsrBuf, 0, 4);
|
|
||||||
os.write(bsrBuf, 4, len-4);
|
|
||||||
} else {
|
|
||||||
int todo = len;
|
|
||||||
while (todo > 0) {
|
|
||||||
int nextLen = Math.min(todo, tmp.length);
|
|
||||||
plain.readFully(tmp, 0, nextLen);
|
|
||||||
if (isPlain) {
|
|
||||||
os.writePlain(tmp, 0, nextLen);
|
|
||||||
} else {
|
|
||||||
os.write(tmp, 0, nextLen);
|
|
||||||
}
|
|
||||||
todo -= nextLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
totalBytes += 4 + len;
|
|
||||||
}
|
|
||||||
os.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new EncryptedDocumentException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*package*/ InternalWorkbook getWorkbook() {
|
/*package*/ InternalWorkbook getWorkbook() {
|
||||||
return workbook;
|
return workbook;
|
||||||
}
|
}
|
||||||
@ -1996,65 +1529,6 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
return olemap;
|
return olemap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an OLE package manager object with the given POIFS to the sheet
|
|
||||||
*
|
|
||||||
* @param poiData an POIFS containing the embedded document, to be added
|
|
||||||
* @param label the label of the payload
|
|
||||||
* @param fileName the original filename
|
|
||||||
* @param command the command to open the payload
|
|
||||||
* @return the index of the added ole object
|
|
||||||
* @throws IOException if the object can't be embedded
|
|
||||||
*/
|
|
||||||
public int addOlePackage(POIFSFileSystem poiData, String label, String fileName, String command)
|
|
||||||
throws IOException {
|
|
||||||
DirectoryNode root = poiData.getRoot();
|
|
||||||
Map<String,ClassID> olemap = getOleMap();
|
|
||||||
for (Map.Entry<String,ClassID> entry : olemap.entrySet()) {
|
|
||||||
if (root.hasEntry(entry.getKey())) {
|
|
||||||
root.setStorageClsid(entry.getValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
poiData.writeFilesystem(bos);
|
|
||||||
return addOlePackage(bos.toByteArray(), label, fileName, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int addOlePackage(byte[] oleData, String label, String fileName, String command)
|
|
||||||
throws IOException {
|
|
||||||
// check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data
|
|
||||||
if (initDirectory()) {
|
|
||||||
preserveNodes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get free MBD-Node
|
|
||||||
int storageId = 0;
|
|
||||||
DirectoryEntry oleDir = null;
|
|
||||||
do {
|
|
||||||
String storageStr = "MBD"+ HexDump.toHex(++storageId);
|
|
||||||
if (!getDirectory().hasEntry(storageStr)) {
|
|
||||||
oleDir = getDirectory().createDirectory(storageStr);
|
|
||||||
oleDir.setStorageClsid(ClassID.OLE10_PACKAGE);
|
|
||||||
}
|
|
||||||
} while (oleDir == null);
|
|
||||||
|
|
||||||
// the following data was taken from an example libre office document
|
|
||||||
// beside this "\u0001Ole" record there were several other records, e.g. CompObj,
|
|
||||||
// OlePresXXX, but it seems, that they aren't neccessary
|
|
||||||
byte oleBytes[] = { 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
oleDir.createDocument("\u0001Ole", new ByteArrayInputStream(oleBytes));
|
|
||||||
|
|
||||||
Ole10Native oleNative = new Ole10Native(label, fileName, command, oleData);
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
oleNative.writeOut(bos);
|
|
||||||
oleDir.createDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.toByteArray()));
|
|
||||||
|
|
||||||
return storageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the LinkTable records required to allow formulas referencing
|
* Adds the LinkTable records required to allow formulas referencing
|
||||||
* the specified external workbook to be added to this one. Allows
|
* the specified external workbook to be added to this one. Allows
|
||||||
@ -2234,15 +1708,6 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
return workbook.changeExternalReference(oldUrl, newUrl);
|
return workbook.changeExternalReference(oldUrl, newUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated POI 3.16 beta 1. use {@link POIDocument#getDirectory()} instead
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
@Removal(version="3.18")
|
|
||||||
public DirectoryNode getRootDirectory(){
|
|
||||||
return getDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public InternalWorkbook getInternalWorkbook() {
|
public InternalWorkbook getInternalWorkbook() {
|
||||||
return workbook;
|
return workbook;
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package org.apache.poi.ss.usermodel;
|
package org.apache.poi.ss.usermodel;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -33,7 +32,7 @@ import org.apache.poi.util.Removal;
|
|||||||
* will construct whether they are reading or writing a workbook. It is also the
|
* will construct whether they are reading or writing a workbook. It is also the
|
||||||
* top level object for creating new sheets/etc.
|
* top level object for creating new sheets/etc.
|
||||||
*/
|
*/
|
||||||
public interface Workbook extends Closeable, Iterable<Sheet> {
|
public interface Workbook extends Iterable<Sheet> {
|
||||||
|
|
||||||
/** Extended windows meta file */
|
/** Extended windows meta file */
|
||||||
int PICTURE_TYPE_EMF = 2;
|
int PICTURE_TYPE_EMF = 2;
|
||||||
@ -344,25 +343,6 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
|
|||||||
*/
|
*/
|
||||||
CellStyle getCellStyleAt(int idx);
|
CellStyle getCellStyleAt(int idx);
|
||||||
|
|
||||||
/**
|
|
||||||
* Write out this workbook to an Outputstream.
|
|
||||||
*
|
|
||||||
* @param stream - the java OutputStream you wish to write to
|
|
||||||
* @exception IOException if anything can't be written.
|
|
||||||
*/
|
|
||||||
void write(OutputStream stream) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the underlying input resource (File or Stream),
|
|
||||||
* from which the Workbook was read.
|
|
||||||
*
|
|
||||||
* <p>Once this has been called, no further
|
|
||||||
* operations, updates or reads should be performed on the
|
|
||||||
* Workbook.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void close() throws IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the total number of defined names in this workbook
|
* @return the total number of defined names in this workbook
|
||||||
*/
|
*/
|
||||||
@ -657,20 +637,6 @@ public interface Workbook extends Closeable, Iterable<Sheet> {
|
|||||||
*/
|
*/
|
||||||
SpreadsheetVersion getSpreadsheetVersion();
|
SpreadsheetVersion getSpreadsheetVersion();
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an OLE package manager object with the given content to the sheet
|
|
||||||
*
|
|
||||||
* @param oleData the payload
|
|
||||||
* @param label the label of the payload
|
|
||||||
* @param fileName the original filename
|
|
||||||
* @param command the command to open the payload
|
|
||||||
*
|
|
||||||
* @return the index of the added ole object, i.e. the storage id
|
|
||||||
*
|
|
||||||
* @throws IOException if the object can't be embedded
|
|
||||||
*/
|
|
||||||
int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether a call to {@link Cell#setCellFormula(String)} will validate the formula or not.
|
* Whether a call to {@link Cell#setCellFormula(String)} will validate the formula or not.
|
||||||
*
|
*
|
||||||
|
@ -1,57 +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.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
import org.apache.poi.ss.usermodel.Sheet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dump out the aggregated escher records
|
|
||||||
*/
|
|
||||||
public class DrawingDump
|
|
||||||
{
|
|
||||||
public static void main( String[] args ) throws IOException {
|
|
||||||
OutputStreamWriter osw = new OutputStreamWriter(System.out, Charset.defaultCharset());
|
|
||||||
PrintWriter pw = new PrintWriter(osw);
|
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File(args[0]));
|
|
||||||
HSSFWorkbook wb = new HSSFWorkbook(fs);
|
|
||||||
try {
|
|
||||||
pw.println( "Drawing group:" );
|
|
||||||
wb.dumpDrawingGroupRecords(true);
|
|
||||||
|
|
||||||
int i = 1;
|
|
||||||
for (Sheet sheet : wb)
|
|
||||||
{
|
|
||||||
pw.println( "Sheet " + i + "(" + sheet.getSheetName() + "):" );
|
|
||||||
((HSSFSheet) sheet).dumpDrawingRecords(true, pw);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
wb.close();
|
|
||||||
fs.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
//package org.apache.poi.hssf.usermodel.examples;
|
//package org.apache.poi.hssf.usermodel.examples;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.apache.poi.ss.SpreadsheetVersion;
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.usermodel.*;
|
||||||
@ -39,9 +40,9 @@ public class CalculationTest {
|
|||||||
|
|
||||||
public static void runAssert(final int maxRow, final double expected) {
|
public static void runAssert(final int maxRow, final double expected) {
|
||||||
//runAssert(new org.apache.poi.hssf.usermodel.GenericSSEvaluationWorkbook(), maxRow, expected);
|
//runAssert(new org.apache.poi.hssf.usermodel.GenericSSEvaluationWorkbook(), maxRow, expected);
|
||||||
runAssert(new org.apache.poi.hssf.usermodel.HSSFWorkbook(SpreadsheetVersion.EXCEL2007), maxRow, expected);
|
runAssert(new org.apache.poi.hssf.usermodel.HSSFWorkbook(), maxRow, expected);
|
||||||
if(maxRow <= SpreadsheetVersion.EXCEL97.getMaxRows())
|
if(maxRow <= SpreadsheetVersion.EXCEL97.getMaxRows())
|
||||||
runAssert(new org.apache.poi.hssf.usermodel.HSSFWorkbook(), maxRow, expected);
|
runAssert(new org.apache.poi.hssf.usermodel.HSSFWorkbook(SpreadsheetVersion.EXCEL97), maxRow, expected);
|
||||||
//runAssert(newXSSFWorkbookNoFormulaValidation(), maxRow, expected);
|
//runAssert(newXSSFWorkbookNoFormulaValidation(), maxRow, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user