mirror of
https://github.com/apache/poi.git
synced 2026-02-27 20:40:08 +08:00
convert tabs to spaces
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1890120 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d3899d4744
commit
3890b7013b
@ -25,12 +25,12 @@ public class EmptyFileException extends IllegalArgumentException {
|
||||
private static final long serialVersionUID = 1536449292174360166L;
|
||||
|
||||
public EmptyFileException() {
|
||||
super("The supplied file was empty (zero bytes long)");
|
||||
}
|
||||
super("The supplied file was empty (zero bytes long)");
|
||||
}
|
||||
|
||||
public EmptyFileException(File file) {
|
||||
super(file.exists() ?
|
||||
"The supplied file '" + file.getAbsolutePath() + "' was empty (zero bytes long)" :
|
||||
"The file '" + file.getAbsolutePath() + "' does not exist");
|
||||
}
|
||||
super(file.exists() ?
|
||||
"The supplied file '" + file.getAbsolutePath() + "' was empty (zero bytes long)" :
|
||||
"The file '" + file.getAbsolutePath() + "' does not exist");
|
||||
}
|
||||
}
|
||||
@ -20,9 +20,9 @@ public class EncryptedDocumentException extends IllegalStateException
|
||||
{
|
||||
private static final long serialVersionUID = 7276950444540469193L;
|
||||
|
||||
public EncryptedDocumentException(String s) {
|
||||
super(s);
|
||||
}
|
||||
public EncryptedDocumentException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public EncryptedDocumentException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
||||
@ -24,6 +24,6 @@ public abstract class OldFileFormatException extends UnsupportedFileFormatExcept
|
||||
private static final long serialVersionUID = 7849681804154571175L;
|
||||
|
||||
public OldFileFormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ public abstract class POIDocument implements Closeable {
|
||||
private SummaryInformation sInf;
|
||||
/** Holds further metadata on our document */
|
||||
private DocumentSummaryInformation dsInf;
|
||||
/** The directory that our document lives in */
|
||||
/** The directory that our document lives in */
|
||||
private DirectoryNode directory;
|
||||
|
||||
/** For our own logging use */
|
||||
@ -73,7 +73,7 @@ public abstract class POIDocument implements Closeable {
|
||||
* @param dir The {@link DirectoryNode} where information is read from.
|
||||
*/
|
||||
protected POIDocument(DirectoryNode dir) {
|
||||
this.directory = dir;
|
||||
this.directory = dir;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -24,8 +24,8 @@ public abstract class UnsupportedFileFormatException extends IllegalArgumentExce
|
||||
private static final long serialVersionUID = -8281969197282030046L;
|
||||
|
||||
protected UnsupportedFileFormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
super(s);
|
||||
}
|
||||
|
||||
protected UnsupportedFileFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
||||
@ -388,7 +388,7 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
|
||||
|
||||
@Override
|
||||
public void setPictureData(byte[] pictureData) {
|
||||
super.setPictureData(pictureData);
|
||||
super.setPictureData(pictureData);
|
||||
setUncompressedSize(pictureData.length);
|
||||
|
||||
// info of chicago project:
|
||||
@ -396,12 +396,12 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
|
||||
// not sure what to do, when lookup tables exceed 32k ...
|
||||
|
||||
try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()) {
|
||||
try (DeflaterOutputStream dos = new DeflaterOutputStream(bos)) {
|
||||
try (DeflaterOutputStream dos = new DeflaterOutputStream(bos)) {
|
||||
dos.write(pictureData);
|
||||
}
|
||||
raw_pictureData = bos.toByteArray();
|
||||
raw_pictureData = bos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Can't compress metafile picture data", e);
|
||||
throw new RuntimeException("Can't compress metafile picture data", e);
|
||||
}
|
||||
|
||||
setCompressedSize(raw_pictureData.length);
|
||||
|
||||
@ -52,14 +52,14 @@ public final class UnknownEscherRecord extends EscherRecord {
|
||||
@Override
|
||||
public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) {
|
||||
int bytesRemaining = readHeader( data, offset );
|
||||
/*
|
||||
* Have a check between available bytes and bytesRemaining,
|
||||
* take the available length if the bytesRemaining out of range.
|
||||
*/
|
||||
int available = data.length - (offset + 8);
|
||||
if (bytesRemaining > available) {
|
||||
bytesRemaining = available;
|
||||
}
|
||||
/*
|
||||
* Have a check between available bytes and bytesRemaining,
|
||||
* take the available length if the bytesRemaining out of range.
|
||||
*/
|
||||
int available = data.length - (offset + 8);
|
||||
if (bytesRemaining > available) {
|
||||
bytesRemaining = available;
|
||||
}
|
||||
|
||||
if (isContainerRecord()) {
|
||||
int bytesWritten = 0;
|
||||
|
||||
@ -34,42 +34,42 @@ import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
||||
* @see org.apache.poi.hwpf.extractor.WordExtractor
|
||||
*/
|
||||
public interface POIOLE2TextExtractor extends POITextExtractor {
|
||||
/**
|
||||
* Returns the document information metadata for the document
|
||||
*
|
||||
/**
|
||||
* Returns the document information metadata for the document
|
||||
*
|
||||
* @return The Document Summary Information or null
|
||||
* if it could not be read for this document.
|
||||
*/
|
||||
default DocumentSummaryInformation getDocSummaryInformation() {
|
||||
return getDocument().getDocumentSummaryInformation();
|
||||
}
|
||||
*/
|
||||
default DocumentSummaryInformation getDocSummaryInformation() {
|
||||
return getDocument().getDocumentSummaryInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary information metadata for the document.
|
||||
*
|
||||
/**
|
||||
* Returns the summary information metadata for the document.
|
||||
*
|
||||
* @return The Summary information for the document or null
|
||||
* if it could not be read for this document.
|
||||
*/
|
||||
default SummaryInformation getSummaryInformation() {
|
||||
return getDocument().getSummaryInformation();
|
||||
}
|
||||
*/
|
||||
default SummaryInformation getSummaryInformation() {
|
||||
return getDocument().getSummaryInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HPSF powered text extractor for the
|
||||
* document properties metadata, such as title and author.
|
||||
*
|
||||
* @return an instance of POIExtractor that can extract meta-data.
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Returns an HPSF powered text extractor for the
|
||||
* document properties metadata, such as title and author.
|
||||
*
|
||||
* @return an instance of POIExtractor that can extract meta-data.
|
||||
*/
|
||||
@Override
|
||||
default POITextExtractor getMetadataTextExtractor() {
|
||||
return new HPSFPropertiesExtractor(this);
|
||||
}
|
||||
return new HPSFPropertiesExtractor(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying DirectoryEntry of this document.
|
||||
*
|
||||
* @return the DirectoryEntry that is associated with the POIDocument of this extractor.
|
||||
*/
|
||||
/**
|
||||
* Return the underlying DirectoryEntry of this document.
|
||||
*
|
||||
* @return the DirectoryEntry that is associated with the POIDocument of this extractor.
|
||||
*/
|
||||
default DirectoryEntry getRoot() {
|
||||
return getDocument().getDirectory();
|
||||
}
|
||||
|
||||
@ -31,57 +31,57 @@ import java.io.IOException;
|
||||
* @see org.apache.poi.hwpf.extractor.WordExtractor
|
||||
*/
|
||||
public interface POITextExtractor extends Closeable {
|
||||
/**
|
||||
* Retrieves all the text from the document.
|
||||
* How cells, paragraphs etc are separated in the text
|
||||
* is implementation specific - see the javadocs for
|
||||
* a specific project for details.
|
||||
* @return All the text from the document
|
||||
*/
|
||||
String getText();
|
||||
/**
|
||||
* Retrieves all the text from the document.
|
||||
* How cells, paragraphs etc are separated in the text
|
||||
* is implementation specific - see the javadocs for
|
||||
* a specific project for details.
|
||||
* @return All the text from the document
|
||||
*/
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* Returns another text extractor, which is able to
|
||||
* output the textual content of the document
|
||||
* metadata / properties, such as author and title.
|
||||
*
|
||||
* @return the metadata and text extractor
|
||||
*/
|
||||
POITextExtractor getMetadataTextExtractor();
|
||||
/**
|
||||
* Returns another text extractor, which is able to
|
||||
* output the textual content of the document
|
||||
* metadata / properties, such as author and title.
|
||||
*
|
||||
* @return the metadata and text extractor
|
||||
*/
|
||||
POITextExtractor getMetadataTextExtractor();
|
||||
|
||||
/**
|
||||
* @param doCloseFilesystem {@code true} (default), if underlying resources/filesystem should be
|
||||
* closed on {@link #close()}
|
||||
*/
|
||||
void setCloseFilesystem(boolean doCloseFilesystem);
|
||||
/**
|
||||
* @param doCloseFilesystem {@code true} (default), if underlying resources/filesystem should be
|
||||
* closed on {@link #close()}
|
||||
*/
|
||||
void setCloseFilesystem(boolean doCloseFilesystem);
|
||||
|
||||
/**
|
||||
* @return {@code true}, if resources/filesystem should be closed on {@link #close()}
|
||||
*/
|
||||
boolean isCloseFilesystem();
|
||||
/**
|
||||
* @return {@code true}, if resources/filesystem should be closed on {@link #close()}
|
||||
*/
|
||||
boolean isCloseFilesystem();
|
||||
|
||||
/**
|
||||
* @return The underlying resources/filesystem
|
||||
*/
|
||||
Closeable getFilesystem();
|
||||
/**
|
||||
* @return The underlying resources/filesystem
|
||||
*/
|
||||
Closeable getFilesystem();
|
||||
|
||||
/**
|
||||
* Allows to free resources of the Extractor as soon as
|
||||
* it is not needed any more. This may include closing
|
||||
* open file handles and freeing memory.
|
||||
*
|
||||
* The Extractor cannot be used after close has been called.
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Allows to free resources of the Extractor as soon as
|
||||
* it is not needed any more. This may include closing
|
||||
* open file handles and freeing memory.
|
||||
*
|
||||
* The Extractor cannot be used after close has been called.
|
||||
*/
|
||||
@Override
|
||||
default void close() throws IOException {
|
||||
Closeable fs = getFilesystem();
|
||||
if (isCloseFilesystem() && fs != null) {
|
||||
fs.close();
|
||||
}
|
||||
}
|
||||
Closeable fs = getFilesystem();
|
||||
if (isCloseFilesystem() && fs != null) {
|
||||
fs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the processed document
|
||||
*/
|
||||
Object getDocument();
|
||||
/**
|
||||
* @return the processed document
|
||||
*/
|
||||
Object getDocument();
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ public class ClassID implements Duplicatable, GenericRecord {
|
||||
public ClassID(String externalForm) {
|
||||
String clsStr = externalForm.replaceAll("[{}-]", "");
|
||||
for (int i=0; i<clsStr.length(); i+=2) {
|
||||
bytes[i/2] = (byte)Integer.parseInt(clsStr.substring(i, i+2), 16);
|
||||
bytes[i/2] = (byte)Integer.parseInt(clsStr.substring(i, i+2), 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ public class DocumentSummaryInformation extends PropertySet {
|
||||
|
||||
@Override
|
||||
public PropertyIDMap getPropertySetIDMap() {
|
||||
return PropertyIDMap.getDocumentSummaryInformationProperties();
|
||||
return PropertyIDMap.getDocumentSummaryInformationProperties();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -25,8 +25,8 @@ package org.apache.poi.hpsf;
|
||||
*/
|
||||
public class HPSFRuntimeException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = -7804271670232727159L;
|
||||
/** <p>The underlying reason for this exception - may be
|
||||
private static final long serialVersionUID = -7804271670232727159L;
|
||||
/** <p>The underlying reason for this exception - may be
|
||||
* <code>null</code>.</p> */
|
||||
private Throwable reason;
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ public final class SummaryInformation extends PropertySet {
|
||||
|
||||
@Override
|
||||
public PropertyIDMap getPropertySetIDMap() {
|
||||
return PropertyIDMap.getSummaryInformationProperties();
|
||||
return PropertyIDMap.getSummaryInformationProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -114,7 +114,7 @@ public class VariantSupport extends Variant {
|
||||
Long vt = Long.valueOf(ex.getVariantType());
|
||||
if (!unsupportedMessage.contains(vt))
|
||||
{
|
||||
LOG.atError().withThrowable(ex).log("Unsupported type");
|
||||
LOG.atError().withThrowable(ex).log("Unsupported type");
|
||||
unsupportedMessage.add(vt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ package org.apache.poi.hssf;
|
||||
import org.apache.poi.OldFileFormatException;
|
||||
|
||||
public class OldExcelFormatException extends OldFileFormatException {
|
||||
public OldExcelFormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
public OldExcelFormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@ -371,33 +371,33 @@ public final class BiffViewer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
/**
|
||||
* 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>
|
||||
* <caption>BiffViewer options</caption>
|
||||
* <tr><td>--biffhex</td><td>show hex dump of each BIFF record</td></tr>
|
||||
* <tr><td>--noint</td><td>do not output interpretation of BIFF records</td></tr>
|
||||
* <tr><td>--out</td><td>send output to <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);
|
||||
* <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);
|
||||
|
||||
try (POIFSFileSystem fs = new POIFSFileSystem(cmdArgs.getFile(), true);
|
||||
InputStream is = getPOIFSInputStream(fs);
|
||||
@ -413,9 +413,9 @@ public final class BiffViewer {
|
||||
cmdArgs.suppressHeader());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PrintWriter getOutputStream(String outputPath) throws FileNotFoundException {
|
||||
static PrintWriter getOutputStream(String outputPath) throws FileNotFoundException {
|
||||
// Use the system default encoding when sending to System Out
|
||||
OutputStream os = System.out;
|
||||
Charset cs = Charset.defaultCharset();
|
||||
@ -427,287 +427,287 @@ public final class BiffViewer {
|
||||
}
|
||||
|
||||
|
||||
static InputStream getPOIFSInputStream(POIFSFileSystem fs) throws IOException {
|
||||
String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot());
|
||||
return fs.createDocumentInputStream(workbookName);
|
||||
}
|
||||
static InputStream getPOIFSInputStream(POIFSFileSystem fs) throws IOException {
|
||||
String workbookName = HSSFWorkbook.getWorkbookDirEntryName(fs.getRoot());
|
||||
return fs.createDocumentInputStream(workbookName);
|
||||
}
|
||||
|
||||
static void runBiffViewer(PrintWriter pw, InputStream is,
|
||||
boolean dumpInterpretedRecords, boolean dumpHex, boolean zeroAlignHexDump,
|
||||
boolean suppressHeader) {
|
||||
BiffRecordListener recListener = new BiffRecordListener(dumpHex ? pw : null, zeroAlignHexDump, suppressHeader);
|
||||
is = new BiffDumpingStream(is, recListener);
|
||||
createRecords(is, pw, recListener, dumpInterpretedRecords);
|
||||
}
|
||||
static void runBiffViewer(PrintWriter pw, InputStream is,
|
||||
boolean dumpInterpretedRecords, boolean dumpHex, boolean zeroAlignHexDump,
|
||||
boolean suppressHeader) {
|
||||
BiffRecordListener recListener = new BiffRecordListener(dumpHex ? pw : null, zeroAlignHexDump, suppressHeader);
|
||||
is = new BiffDumpingStream(is, recListener);
|
||||
createRecords(is, pw, recListener, dumpInterpretedRecords);
|
||||
}
|
||||
|
||||
private static final class BiffRecordListener implements IBiffRecordListener {
|
||||
private final Writer _hexDumpWriter;
|
||||
private List<String> _headers;
|
||||
private final boolean _zeroAlignEachRecord;
|
||||
private final boolean _noHeader;
|
||||
private BiffRecordListener(Writer hexDumpWriter, boolean zeroAlignEachRecord, boolean noHeader) {
|
||||
_hexDumpWriter = hexDumpWriter;
|
||||
_zeroAlignEachRecord = zeroAlignEachRecord;
|
||||
_noHeader = noHeader;
|
||||
_headers = new ArrayList<>();
|
||||
}
|
||||
private static final class BiffRecordListener implements IBiffRecordListener {
|
||||
private final Writer _hexDumpWriter;
|
||||
private List<String> _headers;
|
||||
private final boolean _zeroAlignEachRecord;
|
||||
private final boolean _noHeader;
|
||||
private BiffRecordListener(Writer hexDumpWriter, boolean zeroAlignEachRecord, boolean noHeader) {
|
||||
_hexDumpWriter = hexDumpWriter;
|
||||
_zeroAlignEachRecord = zeroAlignEachRecord;
|
||||
_noHeader = noHeader;
|
||||
_headers = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void processRecord(int globalOffset, int recordCounter, int sid, int dataSize,
|
||||
byte[] data) {
|
||||
String header = formatRecordDetails(globalOffset, sid, dataSize, recordCounter);
|
||||
if(!_noHeader) {
|
||||
_headers.add(header);
|
||||
}
|
||||
Writer w = _hexDumpWriter;
|
||||
if (w != null) {
|
||||
try {
|
||||
w.write(header);
|
||||
w.write(NEW_LINE_CHARS);
|
||||
hexDumpAligned(w, data, dataSize+4, globalOffset, _zeroAlignEachRecord);
|
||||
w.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> getRecentHeaders() {
|
||||
List<String> result = _headers;
|
||||
_headers = new ArrayList<>();
|
||||
return result;
|
||||
}
|
||||
private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
|
||||
byte[] data) {
|
||||
String header = formatRecordDetails(globalOffset, sid, dataSize, recordCounter);
|
||||
if(!_noHeader) {
|
||||
_headers.add(header);
|
||||
}
|
||||
Writer w = _hexDumpWriter;
|
||||
if (w != null) {
|
||||
try {
|
||||
w.write(header);
|
||||
w.write(NEW_LINE_CHARS);
|
||||
hexDumpAligned(w, data, dataSize+4, globalOffset, _zeroAlignEachRecord);
|
||||
w.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> getRecentHeaders() {
|
||||
List<String> result = _headers;
|
||||
_headers = new ArrayList<>();
|
||||
return result;
|
||||
}
|
||||
private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
|
||||
return "Offset=" + HexDump.intToHex(globalOffset) + "(" + globalOffset + ")" +
|
||||
" recno=" + recordCounter +
|
||||
" sid=" + HexDump.shortToHex(sid) +
|
||||
" size=" + HexDump.shortToHex(size) + "(" + size + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface IBiffRecordListener {
|
||||
private interface IBiffRecordListener {
|
||||
|
||||
void processRecord(int globalOffset, int recordCounter, int sid, int dataSize, byte[] data);
|
||||
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;
|
||||
/**
|
||||
* Wraps a plain {@link InputStream} and allows BIFF record information to be tapped off
|
||||
*
|
||||
*/
|
||||
private static final class BiffDumpingStream extends InputStream {
|
||||
private final DataInputStream _is;
|
||||
private final IBiffRecordListener _listener;
|
||||
private final byte[] _data;
|
||||
private int _recordCounter;
|
||||
private int _overallStreamPos;
|
||||
private int _currentPos;
|
||||
private int _currentSize;
|
||||
private boolean _innerHasReachedEOF;
|
||||
|
||||
private BiffDumpingStream(InputStream is, IBiffRecordListener listener) {
|
||||
_is = new DataInputStream(is);
|
||||
_listener = listener;
|
||||
_data = new byte[RecordInputStream.MAX_RECORD_DATA_SIZE + 4];
|
||||
_recordCounter = 0;
|
||||
_overallStreamPos = 0;
|
||||
_currentSize = 0;
|
||||
_currentPos = 0;
|
||||
}
|
||||
private BiffDumpingStream(InputStream is, IBiffRecordListener listener) {
|
||||
_is = new DataInputStream(is);
|
||||
_listener = listener;
|
||||
_data = new byte[RecordInputStream.MAX_RECORD_DATA_SIZE + 4];
|
||||
_recordCounter = 0;
|
||||
_overallStreamPos = 0;
|
||||
_currentSize = 0;
|
||||
_currentPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (_currentPos >= _currentSize) {
|
||||
fillNextBuffer();
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
return -1;
|
||||
}
|
||||
int result = _data[_currentPos] & 0x00FF;
|
||||
_currentPos ++;
|
||||
_overallStreamPos ++;
|
||||
formatBufferIfAtEndOfRec();
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (_currentPos >= _currentSize) {
|
||||
fillNextBuffer();
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
return -1;
|
||||
}
|
||||
int result = _data[_currentPos] & 0x00FF;
|
||||
_currentPos ++;
|
||||
_overallStreamPos ++;
|
||||
formatBufferIfAtEndOfRec();
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (b == null || off < 0 || len < 0 || b.length < off+len) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
fillNextBuffer();
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
return -1;
|
||||
}
|
||||
final int result = Math.min(len, _currentSize - _currentPos);
|
||||
System.arraycopy(_data, _currentPos, b, off, result);
|
||||
_currentPos += result;
|
||||
_overallStreamPos += result;
|
||||
formatBufferIfAtEndOfRec();
|
||||
return result;
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
fillNextBuffer();
|
||||
}
|
||||
if (_currentPos >= _currentSize) {
|
||||
return -1;
|
||||
}
|
||||
final int result = Math.min(len, _currentSize - _currentPos);
|
||||
System.arraycopy(_data, _currentPos, b, off, result);
|
||||
_currentPos += result;
|
||||
_overallStreamPos += result;
|
||||
formatBufferIfAtEndOfRec();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressForbidden("just delegating the call")
|
||||
public int available() throws IOException {
|
||||
return _currentSize - _currentPos + _is.available();
|
||||
}
|
||||
private void fillNextBuffer() throws IOException {
|
||||
if (_innerHasReachedEOF) {
|
||||
return;
|
||||
}
|
||||
int b0 = _is.read();
|
||||
if (b0 == -1) {
|
||||
_innerHasReachedEOF = true;
|
||||
return;
|
||||
}
|
||||
_data[0] = (byte) b0;
|
||||
_is.readFully(_data, 1, 3);
|
||||
int len = LittleEndian.getShort(_data, 2);
|
||||
_is.readFully(_data, 4, len);
|
||||
_currentPos = 0;
|
||||
_currentSize = len + 4;
|
||||
_recordCounter++;
|
||||
}
|
||||
private void formatBufferIfAtEndOfRec() {
|
||||
if (_currentPos != _currentSize) {
|
||||
return;
|
||||
}
|
||||
int dataSize = _currentSize-4;
|
||||
int sid = LittleEndian.getShort(_data, 0);
|
||||
int globalOffset = _overallStreamPos-_currentSize;
|
||||
_listener.processRecord(globalOffset, _recordCounter, sid, dataSize, _data);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
_is.close();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@SuppressForbidden("just delegating the call")
|
||||
public int available() throws IOException {
|
||||
return _currentSize - _currentPos + _is.available();
|
||||
}
|
||||
private void fillNextBuffer() throws IOException {
|
||||
if (_innerHasReachedEOF) {
|
||||
return;
|
||||
}
|
||||
int b0 = _is.read();
|
||||
if (b0 == -1) {
|
||||
_innerHasReachedEOF = true;
|
||||
return;
|
||||
}
|
||||
_data[0] = (byte) b0;
|
||||
_is.readFully(_data, 1, 3);
|
||||
int len = LittleEndian.getShort(_data, 2);
|
||||
_is.readFully(_data, 4, len);
|
||||
_currentPos = 0;
|
||||
_currentSize = len + 4;
|
||||
_recordCounter++;
|
||||
}
|
||||
private void formatBufferIfAtEndOfRec() {
|
||||
if (_currentPos != _currentSize) {
|
||||
return;
|
||||
}
|
||||
int dataSize = _currentSize-4;
|
||||
int sid = LittleEndian.getShort(_data, 0);
|
||||
int globalOffset = _overallStreamPos-_currentSize;
|
||||
_listener.processRecord(globalOffset, _recordCounter, sid, dataSize, _data);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
_is.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DUMP_LINE_LEN = 16;
|
||||
private static final char[] COLUMN_SEPARATOR = " | ".toCharArray();
|
||||
/**
|
||||
* Hex-dumps a portion of a byte array in typical format, also preserving dump-line alignment
|
||||
* @param globalOffset (somewhat arbitrary) used to calculate the addresses printed at the
|
||||
* start of each line
|
||||
*/
|
||||
private static void hexDumpAligned(Writer w, byte[] data, int dumpLen, int globalOffset,
|
||||
boolean zeroAlignEachRecord) {
|
||||
int baseDataOffset = 0;
|
||||
private static final int DUMP_LINE_LEN = 16;
|
||||
private static final char[] COLUMN_SEPARATOR = " | ".toCharArray();
|
||||
/**
|
||||
* Hex-dumps a portion of a byte array in typical format, also preserving dump-line alignment
|
||||
* @param globalOffset (somewhat arbitrary) used to calculate the addresses printed at the
|
||||
* start of each line
|
||||
*/
|
||||
private static void hexDumpAligned(Writer w, byte[] data, int dumpLen, int globalOffset,
|
||||
boolean zeroAlignEachRecord) {
|
||||
int baseDataOffset = 0;
|
||||
|
||||
// perhaps this code should be moved to HexDump
|
||||
int globalStart = globalOffset + baseDataOffset;
|
||||
int globalEnd = globalOffset + baseDataOffset + dumpLen;
|
||||
int startDelta = globalStart % DUMP_LINE_LEN;
|
||||
int endDelta = globalEnd % DUMP_LINE_LEN;
|
||||
if (zeroAlignEachRecord) {
|
||||
endDelta -= startDelta;
|
||||
if (endDelta < 0) {
|
||||
endDelta += DUMP_LINE_LEN;
|
||||
}
|
||||
startDelta = 0;
|
||||
}
|
||||
int startLineAddr;
|
||||
int endLineAddr;
|
||||
if (zeroAlignEachRecord) {
|
||||
endLineAddr = globalEnd - endDelta - (globalStart - startDelta);
|
||||
startLineAddr = 0;
|
||||
} else {
|
||||
startLineAddr = globalStart - startDelta;
|
||||
endLineAddr = globalEnd - endDelta;
|
||||
}
|
||||
// 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;
|
||||
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 (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 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);
|
||||
}
|
||||
}
|
||||
// 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];
|
||||
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);
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
idx = arraycopy(NEW_LINE_CHARS, buf, idx);
|
||||
|
||||
w.write(buf, 0, idx);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
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 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 char getPrintableChar(byte b) {
|
||||
char ib = (char) (b & 0x00FF);
|
||||
if (ib < 32 || ib > 126) {
|
||||
return '.';
|
||||
}
|
||||
return ib;
|
||||
}
|
||||
|
||||
private static void writeHex(char[] buf, int startInBuf, int value, int nDigits) {
|
||||
int acc = value;
|
||||
for(int i=nDigits-1; i>=0; i--) {
|
||||
int digit = acc & 0x0F;
|
||||
buf[startInBuf+i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10));
|
||||
acc >>>= 4;
|
||||
}
|
||||
}
|
||||
private static void writeHex(char[] buf, int startInBuf, int value, int nDigits) {
|
||||
int acc = value;
|
||||
for(int i=nDigits-1; i>=0; i--) {
|
||||
int digit = acc & 0x0F;
|
||||
buf[startInBuf+i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10));
|
||||
acc >>>= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,9 +121,9 @@ public class FormulaViewer
|
||||
private String formulaString(FormulaRecord record) {
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Ptg[] tokens = record.getParsedExpression();
|
||||
for (Ptg token : tokens) {
|
||||
buf.append( token.toFormulaString());
|
||||
Ptg[] tokens = record.getParsedExpression();
|
||||
for (Ptg token : tokens) {
|
||||
buf.append( token.toFormulaString());
|
||||
switch (token.getPtgClass()) {
|
||||
case Ptg.CLASS_REF :
|
||||
buf.append("(R)");
|
||||
|
||||
@ -34,89 +34,89 @@ import org.apache.poi.util.RecordFormatException;
|
||||
*/
|
||||
public final class EventRecordFactory {
|
||||
|
||||
private final ERFListener _listener;
|
||||
private final short[] _sids;
|
||||
private final ERFListener _listener;
|
||||
private final short[] _sids;
|
||||
|
||||
/**
|
||||
* Create an EventRecordFactory
|
||||
*
|
||||
* @param listener the listener to be informed about events
|
||||
* @param sids an array of Record.sid values identifying the records
|
||||
* the listener will work with. Alternatively if this is "null" then
|
||||
* all records are passed. For all 'known' record types use {@link RecordFactory#getAllKnownRecordSIDs()}
|
||||
*/
|
||||
public EventRecordFactory(ERFListener listener, short[] sids) {
|
||||
_listener = listener;
|
||||
if (sids == null) {
|
||||
_sids = null;
|
||||
} else {
|
||||
_sids = sids.clone();
|
||||
Arrays.sort(_sids); // for faster binary search
|
||||
}
|
||||
}
|
||||
private boolean isSidIncluded(short sid) {
|
||||
if (_sids == null) {
|
||||
return true;
|
||||
}
|
||||
return Arrays.binarySearch(_sids, sid) >= 0;
|
||||
}
|
||||
/**
|
||||
* Create an EventRecordFactory
|
||||
*
|
||||
* @param listener the listener to be informed about events
|
||||
* @param sids an array of Record.sid values identifying the records
|
||||
* the listener will work with. Alternatively if this is "null" then
|
||||
* all records are passed. For all 'known' record types use {@link RecordFactory#getAllKnownRecordSIDs()}
|
||||
*/
|
||||
public EventRecordFactory(ERFListener listener, short[] sids) {
|
||||
_listener = listener;
|
||||
if (sids == null) {
|
||||
_sids = null;
|
||||
} else {
|
||||
_sids = sids.clone();
|
||||
Arrays.sort(_sids); // for faster binary search
|
||||
}
|
||||
}
|
||||
private boolean isSidIncluded(short sid) {
|
||||
if (_sids == null) {
|
||||
return true;
|
||||
}
|
||||
return Arrays.binarySearch(_sids, sid) >= 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sends the record event to all registered listeners.
|
||||
* @param record the record to be thrown.
|
||||
* @return <code>false</code> to abort. This aborts
|
||||
* out of the event loop should the listener return false
|
||||
*/
|
||||
private boolean processRecord(org.apache.poi.hssf.record.Record record) {
|
||||
if (!isSidIncluded(record.getSid())) {
|
||||
return true;
|
||||
}
|
||||
return _listener.processRecord(record);
|
||||
}
|
||||
/**
|
||||
* sends the record event to all registered listeners.
|
||||
* @param record the record to be thrown.
|
||||
* @return <code>false</code> to abort. This aborts
|
||||
* out of the event loop should the listener return false
|
||||
*/
|
||||
private boolean processRecord(org.apache.poi.hssf.record.Record record) {
|
||||
if (!isSidIncluded(record.getSid())) {
|
||||
return true;
|
||||
}
|
||||
return _listener.processRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of records from an input stream
|
||||
*
|
||||
* @param in the InputStream from which the records will be
|
||||
* obtained
|
||||
*
|
||||
* @exception RecordFormatException on error processing the
|
||||
* InputStream
|
||||
*/
|
||||
public void processRecords(InputStream in) throws RecordFormatException {
|
||||
Record last_record = null;
|
||||
/**
|
||||
* Create an array of records from an input stream
|
||||
*
|
||||
* @param in the InputStream from which the records will be
|
||||
* obtained
|
||||
*
|
||||
* @exception RecordFormatException on error processing the
|
||||
* InputStream
|
||||
*/
|
||||
public void processRecords(InputStream in) throws RecordFormatException {
|
||||
Record last_record = null;
|
||||
|
||||
RecordInputStream recStream = new RecordInputStream(in);
|
||||
RecordInputStream recStream = new RecordInputStream(in);
|
||||
|
||||
while (recStream.hasNextRecord()) {
|
||||
recStream.nextRecord();
|
||||
Record[] recs = RecordFactory.createRecord(recStream); // handle MulRK records
|
||||
if (recs.length > 1) {
|
||||
for (org.apache.poi.hssf.record.Record rec : recs) {
|
||||
if ( last_record != null ) {
|
||||
if (!processRecord(last_record)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_record = rec; // do to keep the algorithm homogeneous...you can't
|
||||
} // actually continue a number record anyhow.
|
||||
} else {
|
||||
Record record = recs[ 0 ];
|
||||
while (recStream.hasNextRecord()) {
|
||||
recStream.nextRecord();
|
||||
Record[] recs = RecordFactory.createRecord(recStream); // handle MulRK records
|
||||
if (recs.length > 1) {
|
||||
for (org.apache.poi.hssf.record.Record rec : recs) {
|
||||
if ( last_record != null ) {
|
||||
if (!processRecord(last_record)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_record = rec; // do to keep the algorithm homogeneous...you can't
|
||||
} // actually continue a number record anyhow.
|
||||
} else {
|
||||
Record record = recs[ 0 ];
|
||||
|
||||
if (record != null) {
|
||||
if (last_record != null) {
|
||||
if (!processRecord(last_record)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_record = record;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (record != null) {
|
||||
if (last_record != null) {
|
||||
if (!processRecord(last_record)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_record = record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (last_record != null) {
|
||||
processRecord(last_record);
|
||||
}
|
||||
}
|
||||
if (last_record != null) {
|
||||
processRecord(last_record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,31 +41,31 @@ public abstract class AbortableHSSFListener implements HSSFListener
|
||||
* It is never called by HSSFEventFactory or HSSFRequest.
|
||||
* You should implement #abortableProcessRecord instead
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void processRecord(org.apache.poi.hssf.record.Record record)
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an HSSF Record. Called when a record occurs in an HSSF file.
|
||||
* Provides two options for halting the processing of the HSSF file.
|
||||
*
|
||||
* The return value provides a means of non-error termination with a
|
||||
* user-defined result code. A value of zero must be returned to
|
||||
* continue processing, any other value will halt processing by
|
||||
* <code>HSSFEventFactory</code> with the code being passed back by
|
||||
* its abortable process events methods.
|
||||
*
|
||||
* Error termination can be done by throwing the HSSFUserException.
|
||||
*
|
||||
* Note that HSSFEventFactory will not call the inherited process
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*
|
||||
* Process an HSSF Record. Called when a record occurs in an HSSF file.
|
||||
* Provides two options for halting the processing of the HSSF file.
|
||||
*
|
||||
* The return value provides a means of non-error termination with a
|
||||
* user-defined result code. A value of zero must be returned to
|
||||
* continue processing, any other value will halt processing by
|
||||
* <code>HSSFEventFactory</code> with the code being passed back by
|
||||
* its abortable process events methods.
|
||||
*
|
||||
* Error termination can be done by throwing the HSSFUserException.
|
||||
*
|
||||
* Note that HSSFEventFactory will not call the inherited process
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*
|
||||
* @return result code of zero for continued processing.
|
||||
*
|
||||
* @throws HSSFUserException User code can throw this to abort
|
||||
* file processing by HSSFEventFactory and return diagnostic information.
|
||||
* @throws HSSFUserException User code can throw this to abort
|
||||
* file processing by HSSFEventFactory and return diagnostic information.
|
||||
*/
|
||||
public abstract short abortableProcessRecord(org.apache.poi.hssf.record.Record record) throws HSSFUserException;
|
||||
}
|
||||
|
||||
@ -56,131 +56,131 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
public class EventWorkbookBuilder {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a stub Workbook from the supplied records,
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @param sst The SSTRecord in your file.
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static InternalWorkbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds, SSTRecord sst) {
|
||||
List<org.apache.poi.hssf.record.Record> wbRecords = new ArrayList<>();
|
||||
/**
|
||||
* Creates a stub Workbook from the supplied records,
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @param sst The SSTRecord in your file.
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static InternalWorkbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds, SSTRecord sst) {
|
||||
List<org.apache.poi.hssf.record.Record> wbRecords = new ArrayList<>();
|
||||
|
||||
// Core Workbook records go first
|
||||
if(bounds != null) {
|
||||
Collections.addAll(wbRecords, bounds);
|
||||
}
|
||||
if(sst != null) {
|
||||
wbRecords.add(sst);
|
||||
}
|
||||
// Core Workbook records go first
|
||||
if(bounds != null) {
|
||||
Collections.addAll(wbRecords, bounds);
|
||||
}
|
||||
if(sst != null) {
|
||||
wbRecords.add(sst);
|
||||
}
|
||||
|
||||
// Now we can have the ExternSheetRecords,
|
||||
// preceded by a SupBookRecord
|
||||
if(externs != null) {
|
||||
wbRecords.add(SupBookRecord.createInternalReferences(
|
||||
(short)externs.length));
|
||||
Collections.addAll(wbRecords, externs);
|
||||
}
|
||||
// Now we can have the ExternSheetRecords,
|
||||
// preceded by a SupBookRecord
|
||||
if(externs != null) {
|
||||
wbRecords.add(SupBookRecord.createInternalReferences(
|
||||
(short)externs.length));
|
||||
Collections.addAll(wbRecords, externs);
|
||||
}
|
||||
|
||||
// Finally we need an EoF record
|
||||
wbRecords.add(EOFRecord.instance);
|
||||
// Finally we need an EoF record
|
||||
wbRecords.add(EOFRecord.instance);
|
||||
|
||||
return InternalWorkbook.createWorkbook(wbRecords);
|
||||
}
|
||||
return InternalWorkbook.createWorkbook(wbRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a stub workbook from the supplied records,
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static InternalWorkbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds) {
|
||||
return createStubWorkbook(externs, bounds, null);
|
||||
}
|
||||
/**
|
||||
* Creates a stub workbook from the supplied records,
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static InternalWorkbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds) {
|
||||
return createStubWorkbook(externs, bounds, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A wrapping HSSFListener which will collect
|
||||
* {@link BoundSheetRecord}s and {@link ExternSheetRecord}s as
|
||||
* they go past, so you can create a Stub {@link InternalWorkbook} from
|
||||
* them once required.
|
||||
*/
|
||||
public static class SheetRecordCollectingListener implements HSSFListener {
|
||||
private final HSSFListener childListener;
|
||||
private final List<BoundSheetRecord> boundSheetRecords = new ArrayList<>();
|
||||
private final List<ExternSheetRecord> externSheetRecords = new ArrayList<>();
|
||||
private SSTRecord sstRecord;
|
||||
/**
|
||||
* A wrapping HSSFListener which will collect
|
||||
* {@link BoundSheetRecord}s and {@link ExternSheetRecord}s as
|
||||
* they go past, so you can create a Stub {@link InternalWorkbook} from
|
||||
* them once required.
|
||||
*/
|
||||
public static class SheetRecordCollectingListener implements HSSFListener {
|
||||
private final HSSFListener childListener;
|
||||
private final List<BoundSheetRecord> boundSheetRecords = new ArrayList<>();
|
||||
private final List<ExternSheetRecord> externSheetRecords = new ArrayList<>();
|
||||
private SSTRecord sstRecord;
|
||||
|
||||
public SheetRecordCollectingListener(HSSFListener childListener) {
|
||||
this.childListener = childListener;
|
||||
}
|
||||
public SheetRecordCollectingListener(HSSFListener childListener) {
|
||||
this.childListener = childListener;
|
||||
}
|
||||
|
||||
|
||||
public BoundSheetRecord[] getBoundSheetRecords() {
|
||||
return boundSheetRecords.toArray(
|
||||
new BoundSheetRecord[0]
|
||||
);
|
||||
}
|
||||
public ExternSheetRecord[] getExternSheetRecords() {
|
||||
return externSheetRecords.toArray(
|
||||
new ExternSheetRecord[0]
|
||||
);
|
||||
}
|
||||
public SSTRecord getSSTRecord() {
|
||||
return sstRecord;
|
||||
}
|
||||
public BoundSheetRecord[] getBoundSheetRecords() {
|
||||
return boundSheetRecords.toArray(
|
||||
new BoundSheetRecord[0]
|
||||
);
|
||||
}
|
||||
public ExternSheetRecord[] getExternSheetRecords() {
|
||||
return externSheetRecords.toArray(
|
||||
new ExternSheetRecord[0]
|
||||
);
|
||||
}
|
||||
public SSTRecord getSSTRecord() {
|
||||
return sstRecord;
|
||||
}
|
||||
|
||||
public HSSFWorkbook getStubHSSFWorkbook() {
|
||||
// Create a base workbook
|
||||
HSSFWorkbook wb = HSSFWorkbook.create(getStubWorkbook());
|
||||
// Stub the sheets, so sheet name lookups work
|
||||
for (BoundSheetRecord bsr : boundSheetRecords) {
|
||||
wb.createSheet(bsr.getSheetname());
|
||||
}
|
||||
// Ready for Formula use!
|
||||
return wb;
|
||||
}
|
||||
public InternalWorkbook getStubWorkbook() {
|
||||
return createStubWorkbook(
|
||||
getExternSheetRecords(), getBoundSheetRecords(),
|
||||
getSSTRecord()
|
||||
);
|
||||
}
|
||||
public HSSFWorkbook getStubHSSFWorkbook() {
|
||||
// Create a base workbook
|
||||
HSSFWorkbook wb = HSSFWorkbook.create(getStubWorkbook());
|
||||
// Stub the sheets, so sheet name lookups work
|
||||
for (BoundSheetRecord bsr : boundSheetRecords) {
|
||||
wb.createSheet(bsr.getSheetname());
|
||||
}
|
||||
// Ready for Formula use!
|
||||
return wb;
|
||||
}
|
||||
public InternalWorkbook getStubWorkbook() {
|
||||
return createStubWorkbook(
|
||||
getExternSheetRecords(), getBoundSheetRecords(),
|
||||
getSSTRecord()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process this record ourselves, and then
|
||||
* pass it on to our child listener
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Process this record ourselves, and then
|
||||
* pass it on to our child listener
|
||||
*/
|
||||
@Override
|
||||
public void processRecord(org.apache.poi.hssf.record.Record record) {
|
||||
// Handle it ourselves
|
||||
processRecordInternally(record);
|
||||
// Handle it ourselves
|
||||
processRecordInternally(record);
|
||||
|
||||
// Now pass on to our child
|
||||
childListener.processRecord(record);
|
||||
}
|
||||
// Now pass on to our child
|
||||
childListener.processRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the record ourselves, but do not
|
||||
* pass it on to the child Listener.
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*/
|
||||
public void processRecordInternally(org.apache.poi.hssf.record.Record record) {
|
||||
if(record instanceof BoundSheetRecord) {
|
||||
boundSheetRecords.add((BoundSheetRecord)record);
|
||||
}
|
||||
else if(record instanceof ExternSheetRecord) {
|
||||
externSheetRecords.add((ExternSheetRecord)record);
|
||||
}
|
||||
else if(record instanceof SSTRecord) {
|
||||
sstRecord = (SSTRecord)record;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Process the record ourselves, but do not
|
||||
* pass it on to the child Listener.
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*/
|
||||
public void processRecordInternally(org.apache.poi.hssf.record.Record record) {
|
||||
if(record instanceof BoundSheetRecord) {
|
||||
boundSheetRecords.add((BoundSheetRecord)record);
|
||||
}
|
||||
else if(record instanceof ExternSheetRecord) {
|
||||
externSheetRecords.add((ExternSheetRecord)record);
|
||||
}
|
||||
else if(record instanceof SSTRecord) {
|
||||
sstRecord = (SSTRecord)record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,159 +43,159 @@ import static org.apache.logging.log4j.util.Unbox.box;
|
||||
* ids.
|
||||
*/
|
||||
public class FormatTrackingHSSFListener implements HSSFListener {
|
||||
private static final Logger LOG = LogManager.getLogger(FormatTrackingHSSFListener.class);
|
||||
private final HSSFListener _childListener;
|
||||
private final HSSFDataFormatter _formatter;
|
||||
private final NumberFormat _defaultFormat;
|
||||
private final Map<Integer, FormatRecord> _customFormatRecords = new HashMap<>();
|
||||
private final List<ExtendedFormatRecord> _xfRecords = new ArrayList<>();
|
||||
private static final Logger LOG = LogManager.getLogger(FormatTrackingHSSFListener.class);
|
||||
private final HSSFListener _childListener;
|
||||
private final HSSFDataFormatter _formatter;
|
||||
private final NumberFormat _defaultFormat;
|
||||
private final Map<Integer, FormatRecord> _customFormatRecords = new HashMap<>();
|
||||
private final List<ExtendedFormatRecord> _xfRecords = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a format tracking wrapper around the given listener, using
|
||||
* the {@link Locale#getDefault() default locale} for the formats.
|
||||
*
|
||||
* @param childListener the listener to be wrapped
|
||||
*/
|
||||
public FormatTrackingHSSFListener(HSSFListener childListener) {
|
||||
this(childListener, LocaleUtil.getUserLocale());
|
||||
}
|
||||
/**
|
||||
* Creates a format tracking wrapper around the given listener, using
|
||||
* the {@link Locale#getDefault() default locale} for the formats.
|
||||
*
|
||||
* @param childListener the listener to be wrapped
|
||||
*/
|
||||
public FormatTrackingHSSFListener(HSSFListener childListener) {
|
||||
this(childListener, LocaleUtil.getUserLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a format tracking wrapper around the given listener, using
|
||||
* the given locale for the formats.
|
||||
/**
|
||||
* Creates a format tracking wrapper around the given listener, using
|
||||
* the given locale for the formats.
|
||||
*
|
||||
* @param childListener the listener to be wrapped
|
||||
* @param locale the locale for the formats
|
||||
*/
|
||||
public FormatTrackingHSSFListener(
|
||||
HSSFListener childListener, Locale locale) {
|
||||
_childListener = childListener;
|
||||
_formatter = new HSSFDataFormatter(locale);
|
||||
_defaultFormat = NumberFormat.getInstance(locale);
|
||||
}
|
||||
*/
|
||||
public FormatTrackingHSSFListener(
|
||||
HSSFListener childListener, Locale locale) {
|
||||
_childListener = childListener;
|
||||
_formatter = new HSSFDataFormatter(locale);
|
||||
_defaultFormat = NumberFormat.getInstance(locale);
|
||||
}
|
||||
|
||||
protected int getNumberOfCustomFormats() {
|
||||
return _customFormatRecords.size();
|
||||
}
|
||||
protected int getNumberOfCustomFormats() {
|
||||
return _customFormatRecords.size();
|
||||
}
|
||||
|
||||
protected int getNumberOfExtendedFormats() {
|
||||
return _xfRecords.size();
|
||||
}
|
||||
protected int getNumberOfExtendedFormats() {
|
||||
return _xfRecords.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process this record ourselves, and then pass it on to our child listener
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Process this record ourselves, and then pass it on to our child listener
|
||||
*/
|
||||
@Override
|
||||
public void processRecord(Record record) {
|
||||
// Handle it ourselves
|
||||
processRecordInternally(record);
|
||||
// Handle it ourselves
|
||||
processRecordInternally(record);
|
||||
|
||||
// Now pass on to our child
|
||||
_childListener.processRecord(record);
|
||||
}
|
||||
// Now pass on to our child
|
||||
_childListener.processRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the record ourselves, but do not pass it on to the child
|
||||
* Listener.
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*/
|
||||
public void processRecordInternally(Record record) {
|
||||
if (record instanceof FormatRecord) {
|
||||
FormatRecord fr = (FormatRecord) record;
|
||||
_customFormatRecords.put(Integer.valueOf(fr.getIndexCode()), fr);
|
||||
}
|
||||
if (record instanceof ExtendedFormatRecord) {
|
||||
ExtendedFormatRecord xr = (ExtendedFormatRecord) record;
|
||||
_xfRecords.add(xr);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Process the record ourselves, but do not pass it on to the child
|
||||
* Listener.
|
||||
*
|
||||
* @param record the record to be processed
|
||||
*/
|
||||
public void processRecordInternally(Record record) {
|
||||
if (record instanceof FormatRecord) {
|
||||
FormatRecord fr = (FormatRecord) record;
|
||||
_customFormatRecords.put(Integer.valueOf(fr.getIndexCode()), fr);
|
||||
}
|
||||
if (record instanceof ExtendedFormatRecord) {
|
||||
ExtendedFormatRecord xr = (ExtendedFormatRecord) record;
|
||||
_xfRecords.add(xr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given numeric of date cells contents as a String, in as
|
||||
* close as we can to the way that Excel would do so. Uses the various
|
||||
* format records to manage this.
|
||||
*
|
||||
* TODO - move this to a central class in such a way that hssf.usermodel can
|
||||
* make use of it too
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the given numeric of date cells contents as a String
|
||||
*/
|
||||
public String formatNumberDateCell(CellValueRecordInterface cell) {
|
||||
double value;
|
||||
if (cell instanceof NumberRecord) {
|
||||
value = ((NumberRecord) cell).getValue();
|
||||
} else if (cell instanceof FormulaRecord) {
|
||||
value = ((FormulaRecord) cell).getValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported CellValue Record passed in " + cell);
|
||||
}
|
||||
/**
|
||||
* Formats the given numeric of date cells contents as a String, in as
|
||||
* close as we can to the way that Excel would do so. Uses the various
|
||||
* format records to manage this.
|
||||
*
|
||||
* TODO - move this to a central class in such a way that hssf.usermodel can
|
||||
* make use of it too
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the given numeric of date cells contents as a String
|
||||
*/
|
||||
public String formatNumberDateCell(CellValueRecordInterface cell) {
|
||||
double value;
|
||||
if (cell instanceof NumberRecord) {
|
||||
value = ((NumberRecord) cell).getValue();
|
||||
} else if (cell instanceof FormulaRecord) {
|
||||
value = ((FormulaRecord) cell).getValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported CellValue Record passed in " + cell);
|
||||
}
|
||||
|
||||
// Get the built in format, if there is one
|
||||
int formatIndex = getFormatIndex(cell);
|
||||
String formatString = getFormatString(cell);
|
||||
// Get the built in format, if there is one
|
||||
int formatIndex = getFormatIndex(cell);
|
||||
String formatString = getFormatString(cell);
|
||||
|
||||
if (formatString == null) {
|
||||
return _defaultFormat.format(value);
|
||||
}
|
||||
// Format, using the nice new
|
||||
// HSSFDataFormatter to do the work for us
|
||||
return _formatter.formatRawCellContents(value, formatIndex, formatString);
|
||||
}
|
||||
if (formatString == null) {
|
||||
return _defaultFormat.format(value);
|
||||
}
|
||||
// Format, using the nice new
|
||||
// HSSFDataFormatter to do the work for us
|
||||
return _formatter.formatRawCellContents(value, formatIndex, formatString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string, eg $##.##, for the given number format index.
|
||||
*
|
||||
* @param formatIndex the format index
|
||||
*
|
||||
* @return the format string
|
||||
*/
|
||||
public String getFormatString(int formatIndex) {
|
||||
String format = null;
|
||||
if (formatIndex >= HSSFDataFormat.getNumberOfBuiltinBuiltinFormats()) {
|
||||
FormatRecord tfr = _customFormatRecords.get(Integer.valueOf(formatIndex));
|
||||
if (tfr == null) {
|
||||
LOG.atError().log("Requested format at index {}, but it wasn't found", box(formatIndex));
|
||||
} else {
|
||||
format = tfr.getFormatString();
|
||||
}
|
||||
} else {
|
||||
format = HSSFDataFormat.getBuiltinFormat((short) formatIndex);
|
||||
}
|
||||
return format;
|
||||
}
|
||||
/**
|
||||
* Returns the format string, eg $##.##, for the given number format index.
|
||||
*
|
||||
* @param formatIndex the format index
|
||||
*
|
||||
* @return the format string
|
||||
*/
|
||||
public String getFormatString(int formatIndex) {
|
||||
String format = null;
|
||||
if (formatIndex >= HSSFDataFormat.getNumberOfBuiltinBuiltinFormats()) {
|
||||
FormatRecord tfr = _customFormatRecords.get(Integer.valueOf(formatIndex));
|
||||
if (tfr == null) {
|
||||
LOG.atError().log("Requested format at index {}, but it wasn't found", box(formatIndex));
|
||||
} else {
|
||||
format = tfr.getFormatString();
|
||||
}
|
||||
} else {
|
||||
format = HSSFDataFormat.getBuiltinFormat((short) formatIndex);
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string, eg $##.##, used by your cell
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the format string
|
||||
*/
|
||||
public String getFormatString(CellValueRecordInterface cell) {
|
||||
int formatIndex = getFormatIndex(cell);
|
||||
if (formatIndex == -1) {
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
return getFormatString(formatIndex);
|
||||
}
|
||||
/**
|
||||
* Returns the format string, eg $##.##, used by your cell
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the format string
|
||||
*/
|
||||
public String getFormatString(CellValueRecordInterface cell) {
|
||||
int formatIndex = getFormatIndex(cell);
|
||||
if (formatIndex == -1) {
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
return getFormatString(formatIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the format string, used by your cell, or -1 if none found
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the index of the format string
|
||||
*/
|
||||
public int getFormatIndex(CellValueRecordInterface cell) {
|
||||
ExtendedFormatRecord xfr = _xfRecords.get(cell.getXFIndex());
|
||||
if (xfr == null) {
|
||||
LOG.atError().log("Cell {},{} uses XF with index {}, but we don't have that", box(cell.getRow()),box(cell.getColumn()),box(cell.getXFIndex()));
|
||||
return -1;
|
||||
}
|
||||
return xfr.getFormatIndex();
|
||||
}
|
||||
/**
|
||||
* Returns the index of the format string, used by your cell, or -1 if none found
|
||||
*
|
||||
* @param cell the cell
|
||||
*
|
||||
* @return the index of the format string
|
||||
*/
|
||||
public int getFormatIndex(CellValueRecordInterface cell) {
|
||||
ExtendedFormatRecord xfr = _xfRecords.get(cell.getXFIndex());
|
||||
if (xfr == null) {
|
||||
LOG.atError().log("Cell {},{} uses XF with index {}, but we don't have that", box(cell.getRow()),box(cell.getColumn()),box(cell.getXFIndex()));
|
||||
return -1;
|
||||
}
|
||||
return xfr.getFormatIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,22 +36,22 @@ import static org.apache.poi.hssf.model.InternalWorkbook.WORKBOOK_DIR_ENTRY_NAME
|
||||
* to your associated HSSFListener.
|
||||
*/
|
||||
public class HSSFEventFactory {
|
||||
/** Creates a new instance of HSSFEventFactory */
|
||||
public HSSFEventFactory() {
|
||||
// no instance fields
|
||||
}
|
||||
/** Creates a new instance of HSSFEventFactory */
|
||||
public HSSFEventFactory() {
|
||||
// no instance fields
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a file into essentially record events.
|
||||
*
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param fs a POIFS filesystem containing your workbook
|
||||
/**
|
||||
* Processes a file into essentially record events.
|
||||
*
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param fs a POIFS filesystem containing your workbook
|
||||
*
|
||||
* @throws IOException if the workbook contained errors
|
||||
*/
|
||||
public void processWorkbookEvents(HSSFRequest req, POIFSFileSystem fs) throws IOException {
|
||||
processWorkbookEvents(req, fs.getRoot());
|
||||
}
|
||||
*/
|
||||
public void processWorkbookEvents(HSSFRequest req, POIFSFileSystem fs) throws IOException {
|
||||
processWorkbookEvents(req, fs.getRoot());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a file into essentially record events.
|
||||
@ -96,86 +96,86 @@ public class HSSFEventFactory {
|
||||
return abortableProcessWorkbookEvents(req, fs.getRoot());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a file into essentially record events.
|
||||
*
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param dir a DirectoryNode containing your workbook
|
||||
* @return numeric user-specified result code.
|
||||
*
|
||||
* @throws HSSFUserException if the processing should be aborted
|
||||
* @throws IOException if the workbook contained errors
|
||||
*/
|
||||
public short abortableProcessWorkbookEvents(HSSFRequest req, DirectoryNode dir)
|
||||
throws IOException, HSSFUserException {
|
||||
/**
|
||||
* Processes a file into essentially record events.
|
||||
*
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param dir a DirectoryNode containing your workbook
|
||||
* @return numeric user-specified result code.
|
||||
*
|
||||
* @throws HSSFUserException if the processing should be aborted
|
||||
* @throws IOException if the workbook contained errors
|
||||
*/
|
||||
public short abortableProcessWorkbookEvents(HSSFRequest req, DirectoryNode dir)
|
||||
throws IOException, HSSFUserException {
|
||||
try (InputStream in = dir.createDocumentInputStream("Workbook")) {
|
||||
return abortableProcessEvents(req, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* If an <code>AbortableHSSFListener</code> causes a halt to processing during this call
|
||||
* the method will return just as with <code>abortableProcessEvents</code>, but no
|
||||
* user code or <code>HSSFUserException</code> will be passed back.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
*/
|
||||
public void processEvents(HSSFRequest req, InputStream in) {
|
||||
try {
|
||||
genericProcessEvents(req, in);
|
||||
} catch (HSSFUserException hue) {
|
||||
/*If an HSSFUserException user exception is thrown, ignore it.*/
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* If an <code>AbortableHSSFListener</code> causes a halt to processing during this call
|
||||
* the method will return just as with <code>abortableProcessEvents</code>, but no
|
||||
* user code or <code>HSSFUserException</code> will be passed back.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
*/
|
||||
public void processEvents(HSSFRequest req, InputStream in) {
|
||||
try {
|
||||
genericProcessEvents(req, in);
|
||||
} catch (HSSFUserException hue) {
|
||||
/*If an HSSFUserException user exception is thrown, ignore it.*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
* @return numeric user-specified result code.
|
||||
*
|
||||
* @throws HSSFUserException if the processing should be aborted
|
||||
*/
|
||||
public short abortableProcessEvents(HSSFRequest req, InputStream in)
|
||||
throws HSSFUserException {
|
||||
return genericProcessEvents(req, in);
|
||||
}
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
* @return numeric user-specified result code.
|
||||
*
|
||||
* @throws HSSFUserException if the processing should be aborted
|
||||
*/
|
||||
public short abortableProcessEvents(HSSFRequest req, InputStream in)
|
||||
throws HSSFUserException {
|
||||
return genericProcessEvents(req, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
* @return numeric user-specified result code.
|
||||
*/
|
||||
private short genericProcessEvents(HSSFRequest req, InputStream in)
|
||||
throws HSSFUserException {
|
||||
short userCode = 0;
|
||||
/**
|
||||
* Processes a DocumentInputStream into essentially Record events.
|
||||
*
|
||||
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
|
||||
* @param req an Instance of HSSFRequest which has your registered listeners
|
||||
* @param in a DocumentInputStream obtained from POIFS's POIFSFileSystem object
|
||||
* @return numeric user-specified result code.
|
||||
*/
|
||||
private short genericProcessEvents(HSSFRequest req, InputStream in)
|
||||
throws HSSFUserException {
|
||||
short userCode = 0;
|
||||
|
||||
// Create a new RecordStream and use that
|
||||
RecordFactoryInputStream recordStream = new RecordFactoryInputStream(in, false);
|
||||
// Create a new RecordStream and use that
|
||||
RecordFactoryInputStream recordStream = new RecordFactoryInputStream(in, false);
|
||||
|
||||
// Process each record as they come in
|
||||
while(true) {
|
||||
org.apache.poi.hssf.record.Record r = recordStream.nextRecord();
|
||||
if(r == null) {
|
||||
break;
|
||||
}
|
||||
userCode = req.processRecord(r);
|
||||
if (userCode != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Process each record as they come in
|
||||
while(true) {
|
||||
org.apache.poi.hssf.record.Record r = recordStream.nextRecord();
|
||||
if(r == null) {
|
||||
break;
|
||||
}
|
||||
userCode = req.processRecord(r);
|
||||
if (userCode != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All done, return our last code
|
||||
return userCode;
|
||||
}
|
||||
// All done, return our last code
|
||||
return userCode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,79 +34,79 @@ import org.apache.poi.hssf.record.RecordFactory;
|
||||
* @see org.apache.poi.hssf.eventusermodel.HSSFUserException
|
||||
*/
|
||||
public class HSSFRequest {
|
||||
private final Map<Short, List<HSSFListener>> _records;
|
||||
private final Map<Short, List<HSSFListener>> _records;
|
||||
|
||||
/** Creates a new instance of HSSFRequest */
|
||||
public HSSFRequest() {
|
||||
_records = new HashMap<>(50); // most folks won't listen for too many of these
|
||||
}
|
||||
/** Creates a new instance of HSSFRequest */
|
||||
public HSSFRequest() {
|
||||
_records = new HashMap<>(50); // most folks won't listen for too many of these
|
||||
}
|
||||
|
||||
/**
|
||||
* add an event listener for a particular record type. The trick is you have to know
|
||||
* what the records are for or just start with our examples and build on them. Alternatively,
|
||||
* you CAN call addListenerForAllRecords and you'll receive ALL record events in one listener,
|
||||
* but if you like to squeeze every last byte of efficiency out of life you my not like this.
|
||||
* (its sure as heck what I plan to do)
|
||||
*
|
||||
* @see #addListenerForAllRecords(HSSFListener)
|
||||
*
|
||||
* @param lsnr for the event
|
||||
* @param sid identifier for the record type this is the .sid static member on the individual records
|
||||
* for example req.addListener(myListener, BOFRecord.sid)
|
||||
*/
|
||||
public void addListener(HSSFListener lsnr, short sid) {
|
||||
List<HSSFListener> list = _records.computeIfAbsent(Short.valueOf(sid), k -> new ArrayList<>(1));
|
||||
/**
|
||||
* add an event listener for a particular record type. The trick is you have to know
|
||||
* what the records are for or just start with our examples and build on them. Alternatively,
|
||||
* you CAN call addListenerForAllRecords and you'll receive ALL record events in one listener,
|
||||
* but if you like to squeeze every last byte of efficiency out of life you my not like this.
|
||||
* (its sure as heck what I plan to do)
|
||||
*
|
||||
* @see #addListenerForAllRecords(HSSFListener)
|
||||
*
|
||||
* @param lsnr for the event
|
||||
* @param sid identifier for the record type this is the .sid static member on the individual records
|
||||
* for example req.addListener(myListener, BOFRecord.sid)
|
||||
*/
|
||||
public void addListener(HSSFListener lsnr, short sid) {
|
||||
List<HSSFListener> list = _records.computeIfAbsent(Short.valueOf(sid), k -> new ArrayList<>(1));
|
||||
|
||||
// probably most people will use one listener
|
||||
list.add(lsnr);
|
||||
}
|
||||
// probably most people will use one listener
|
||||
list.add(lsnr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the equivalent of calling addListener(myListener, sid) for EVERY
|
||||
* record in the org.apache.poi.hssf.record package. This is for lazy
|
||||
* people like me. You can call this more than once with more than one listener, but
|
||||
* that seems like a bad thing to do from a practice-perspective unless you have a
|
||||
* compelling reason to do so (like maybe you send the event two places or log it or
|
||||
* something?).
|
||||
*
|
||||
* @param lsnr a single listener to associate with ALL records
|
||||
*/
|
||||
public void addListenerForAllRecords(HSSFListener lsnr) {
|
||||
short[] rectypes = RecordFactory.getAllKnownRecordSIDs();
|
||||
/**
|
||||
* This is the equivalent of calling addListener(myListener, sid) for EVERY
|
||||
* record in the org.apache.poi.hssf.record package. This is for lazy
|
||||
* people like me. You can call this more than once with more than one listener, but
|
||||
* that seems like a bad thing to do from a practice-perspective unless you have a
|
||||
* compelling reason to do so (like maybe you send the event two places or log it or
|
||||
* something?).
|
||||
*
|
||||
* @param lsnr a single listener to associate with ALL records
|
||||
*/
|
||||
public void addListenerForAllRecords(HSSFListener lsnr) {
|
||||
short[] rectypes = RecordFactory.getAllKnownRecordSIDs();
|
||||
|
||||
for (short rectype : rectypes) {
|
||||
addListener(lsnr, rectype);
|
||||
}
|
||||
}
|
||||
for (short rectype : rectypes) {
|
||||
addListener(lsnr, rectype);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by HSSFEventFactory, passes the Record to each listener associated with
|
||||
* a record.sid.
|
||||
*
|
||||
* @param rec the record to be processed
|
||||
*
|
||||
* @return numeric user-specified result code. If zero continue processing.
|
||||
* @throws HSSFUserException User exception condition
|
||||
*/
|
||||
protected short processRecord(org.apache.poi.hssf.record.Record rec) throws HSSFUserException {
|
||||
List<HSSFListener> listeners = _records.get(Short.valueOf(rec.getSid()));
|
||||
short userCode = 0;
|
||||
/**
|
||||
* Called by HSSFEventFactory, passes the Record to each listener associated with
|
||||
* a record.sid.
|
||||
*
|
||||
* @param rec the record to be processed
|
||||
*
|
||||
* @return numeric user-specified result code. If zero continue processing.
|
||||
* @throws HSSFUserException User exception condition
|
||||
*/
|
||||
protected short processRecord(org.apache.poi.hssf.record.Record rec) throws HSSFUserException {
|
||||
List<HSSFListener> listeners = _records.get(Short.valueOf(rec.getSid()));
|
||||
short userCode = 0;
|
||||
|
||||
if (listeners != null) {
|
||||
if (listeners != null) {
|
||||
|
||||
for (int k = 0; k < listeners.size(); k++) {
|
||||
Object listenObj = listeners.get(k);
|
||||
if (listenObj instanceof AbortableHSSFListener) {
|
||||
AbortableHSSFListener listener = (AbortableHSSFListener) listenObj;
|
||||
userCode = listener.abortableProcessRecord(rec);
|
||||
if (userCode != 0)
|
||||
break;
|
||||
} else {
|
||||
HSSFListener listener = (HSSFListener) listenObj;
|
||||
listener.processRecord(rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
return userCode;
|
||||
}
|
||||
for (int k = 0; k < listeners.size(); k++) {
|
||||
Object listenObj = listeners.get(k);
|
||||
if (listenObj instanceof AbortableHSSFListener) {
|
||||
AbortableHSSFListener listener = (AbortableHSSFListener) listenObj;
|
||||
userCode = listener.abortableProcessRecord(rec);
|
||||
if (userCode != 0)
|
||||
break;
|
||||
} else {
|
||||
HSSFListener listener = (HSSFListener) listenObj;
|
||||
listener.processRecord(rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
return userCode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,169 +42,169 @@ import org.apache.poi.hssf.record.StringRecord;
|
||||
* blank.
|
||||
*/
|
||||
public final class MissingRecordAwareHSSFListener implements HSSFListener {
|
||||
private HSSFListener childListener;
|
||||
private HSSFListener childListener;
|
||||
|
||||
// Need to have different counters for cell rows and
|
||||
// row rows, as you sometimes get a RowRecord in the
|
||||
// middle of some cells, and that'd break everything
|
||||
private int lastRowRow;
|
||||
// Need to have different counters for cell rows and
|
||||
// row rows, as you sometimes get a RowRecord in the
|
||||
// middle of some cells, and that'd break everything
|
||||
private int lastRowRow;
|
||||
|
||||
private int lastCellRow;
|
||||
private int lastCellColumn;
|
||||
private int lastCellRow;
|
||||
private int lastCellColumn;
|
||||
|
||||
/**
|
||||
* Constructs a new MissingRecordAwareHSSFListener, which
|
||||
* will fire processRecord on the supplied child
|
||||
* HSSFListener for all Records, and missing records.
|
||||
* @param listener The HSSFListener to pass records on to
|
||||
*/
|
||||
public MissingRecordAwareHSSFListener(HSSFListener listener) {
|
||||
resetCounts();
|
||||
childListener = listener;
|
||||
}
|
||||
/**
|
||||
* Constructs a new MissingRecordAwareHSSFListener, which
|
||||
* will fire processRecord on the supplied child
|
||||
* HSSFListener for all Records, and missing records.
|
||||
* @param listener The HSSFListener to pass records on to
|
||||
*/
|
||||
public MissingRecordAwareHSSFListener(HSSFListener listener) {
|
||||
resetCounts();
|
||||
childListener = listener;
|
||||
}
|
||||
|
||||
public void processRecord(org.apache.poi.hssf.record.Record record) {
|
||||
int thisRow;
|
||||
int thisColumn;
|
||||
CellValueRecordInterface[] expandedRecords = null;
|
||||
public void processRecord(org.apache.poi.hssf.record.Record record) {
|
||||
int thisRow;
|
||||
int thisColumn;
|
||||
CellValueRecordInterface[] expandedRecords = null;
|
||||
|
||||
if (record instanceof CellValueRecordInterface) {
|
||||
CellValueRecordInterface valueRec = (CellValueRecordInterface) record;
|
||||
thisRow = valueRec.getRow();
|
||||
thisColumn = valueRec.getColumn();
|
||||
} else {
|
||||
if (record instanceof CellValueRecordInterface) {
|
||||
CellValueRecordInterface valueRec = (CellValueRecordInterface) record;
|
||||
thisRow = valueRec.getRow();
|
||||
thisColumn = valueRec.getColumn();
|
||||
} else {
|
||||
if (record instanceof StringRecord){
|
||||
//it contains only cashed result of the previous FormulaRecord evaluation
|
||||
childListener.processRecord(record);
|
||||
return;
|
||||
}
|
||||
thisRow = -1;
|
||||
thisColumn = -1;
|
||||
thisRow = -1;
|
||||
thisColumn = -1;
|
||||
|
||||
switch (record.getSid()) {
|
||||
// the BOFRecord can represent either the beginning of a sheet or
|
||||
// the workbook
|
||||
case BOFRecord.sid:
|
||||
BOFRecord bof = (BOFRecord) record;
|
||||
if (bof.getType() == BOFRecord.TYPE_WORKBOOK ||
|
||||
bof.getType() == BOFRecord.TYPE_WORKSHEET) {
|
||||
// Reset the row and column counts - new workbook / worksheet
|
||||
resetCounts();
|
||||
}
|
||||
break;
|
||||
case RowRecord.sid:
|
||||
RowRecord rowrec = (RowRecord) record;
|
||||
switch (record.getSid()) {
|
||||
// the BOFRecord can represent either the beginning of a sheet or
|
||||
// the workbook
|
||||
case BOFRecord.sid:
|
||||
BOFRecord bof = (BOFRecord) record;
|
||||
if (bof.getType() == BOFRecord.TYPE_WORKBOOK ||
|
||||
bof.getType() == BOFRecord.TYPE_WORKSHEET) {
|
||||
// Reset the row and column counts - new workbook / worksheet
|
||||
resetCounts();
|
||||
}
|
||||
break;
|
||||
case RowRecord.sid:
|
||||
RowRecord rowrec = (RowRecord) record;
|
||||
|
||||
// If there's a jump in rows, fire off missing row records
|
||||
if (lastRowRow + 1 < rowrec.getRowNumber()) {
|
||||
for (int i = (lastRowRow + 1); i < rowrec.getRowNumber(); i++) {
|
||||
MissingRowDummyRecord dr = new MissingRowDummyRecord(i);
|
||||
childListener.processRecord(dr);
|
||||
}
|
||||
}
|
||||
// If there's a jump in rows, fire off missing row records
|
||||
if (lastRowRow + 1 < rowrec.getRowNumber()) {
|
||||
for (int i = (lastRowRow + 1); i < rowrec.getRowNumber(); i++) {
|
||||
MissingRowDummyRecord dr = new MissingRowDummyRecord(i);
|
||||
childListener.processRecord(dr);
|
||||
}
|
||||
}
|
||||
|
||||
// Record this as the last row we saw
|
||||
lastRowRow = rowrec.getRowNumber();
|
||||
lastCellColumn = -1;
|
||||
break;
|
||||
// Record this as the last row we saw
|
||||
lastRowRow = rowrec.getRowNumber();
|
||||
lastCellColumn = -1;
|
||||
break;
|
||||
|
||||
case SharedFormulaRecord.sid:
|
||||
// SharedFormulaRecord occurs after the first FormulaRecord of the cell range.
|
||||
// There are probably (but not always) more cell records after this
|
||||
// - so don't fire off the LastCellOfRowDummyRecord yet
|
||||
childListener.processRecord(record);
|
||||
return;
|
||||
case MulBlankRecord.sid:
|
||||
// These appear in the middle of the cell records, to
|
||||
// specify that the next bunch are empty but styled
|
||||
// Expand this out into multiple blank cells
|
||||
MulBlankRecord mbr = (MulBlankRecord)record;
|
||||
expandedRecords = RecordFactory.convertBlankRecords(mbr);
|
||||
break;
|
||||
case MulRKRecord.sid:
|
||||
// This is multiple consecutive number cells in one record
|
||||
// Exand this out into multiple regular number cells
|
||||
MulRKRecord mrk = (MulRKRecord)record;
|
||||
expandedRecords = RecordFactory.convertRKRecords(mrk);
|
||||
break;
|
||||
case NoteRecord.sid:
|
||||
NoteRecord nrec = (NoteRecord) record;
|
||||
thisRow = nrec.getRow();
|
||||
thisColumn = nrec.getColumn();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SharedFormulaRecord.sid:
|
||||
// SharedFormulaRecord occurs after the first FormulaRecord of the cell range.
|
||||
// There are probably (but not always) more cell records after this
|
||||
// - so don't fire off the LastCellOfRowDummyRecord yet
|
||||
childListener.processRecord(record);
|
||||
return;
|
||||
case MulBlankRecord.sid:
|
||||
// These appear in the middle of the cell records, to
|
||||
// specify that the next bunch are empty but styled
|
||||
// Expand this out into multiple blank cells
|
||||
MulBlankRecord mbr = (MulBlankRecord)record;
|
||||
expandedRecords = RecordFactory.convertBlankRecords(mbr);
|
||||
break;
|
||||
case MulRKRecord.sid:
|
||||
// This is multiple consecutive number cells in one record
|
||||
// Exand this out into multiple regular number cells
|
||||
MulRKRecord mrk = (MulRKRecord)record;
|
||||
expandedRecords = RecordFactory.convertRKRecords(mrk);
|
||||
break;
|
||||
case NoteRecord.sid:
|
||||
NoteRecord nrec = (NoteRecord) record;
|
||||
thisRow = nrec.getRow();
|
||||
thisColumn = nrec.getColumn();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// First part of expanded record handling
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
thisRow = expandedRecords[0].getRow();
|
||||
thisColumn = expandedRecords[0].getColumn();
|
||||
}
|
||||
// First part of expanded record handling
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
thisRow = expandedRecords[0].getRow();
|
||||
thisColumn = expandedRecords[0].getColumn();
|
||||
}
|
||||
|
||||
// If we're on cells, and this cell isn't in the same
|
||||
// row as the last one, then fire the
|
||||
// dummy end-of-row records
|
||||
if(thisRow != lastCellRow && thisRow > 0) {
|
||||
if (lastCellRow == -1) lastCellRow = 0;
|
||||
for(int i=lastCellRow; i<thisRow; i++) {
|
||||
int cols = -1;
|
||||
if(i == lastCellRow) {
|
||||
cols = lastCellColumn;
|
||||
}
|
||||
childListener.processRecord(new LastCellOfRowDummyRecord(i, cols));
|
||||
}
|
||||
}
|
||||
// If we're on cells, and this cell isn't in the same
|
||||
// row as the last one, then fire the
|
||||
// dummy end-of-row records
|
||||
if(thisRow != lastCellRow && thisRow > 0) {
|
||||
if (lastCellRow == -1) lastCellRow = 0;
|
||||
for(int i=lastCellRow; i<thisRow; i++) {
|
||||
int cols = -1;
|
||||
if(i == lastCellRow) {
|
||||
cols = lastCellColumn;
|
||||
}
|
||||
childListener.processRecord(new LastCellOfRowDummyRecord(i, cols));
|
||||
}
|
||||
}
|
||||
|
||||
// If we've just finished with the cells, then fire the
|
||||
// final dummy end-of-row record
|
||||
if(lastCellRow != -1 && lastCellColumn != -1 && thisRow == -1) {
|
||||
childListener.processRecord(new LastCellOfRowDummyRecord(lastCellRow, lastCellColumn));
|
||||
// If we've just finished with the cells, then fire the
|
||||
// final dummy end-of-row record
|
||||
if(lastCellRow != -1 && lastCellColumn != -1 && thisRow == -1) {
|
||||
childListener.processRecord(new LastCellOfRowDummyRecord(lastCellRow, lastCellColumn));
|
||||
|
||||
lastCellRow = -1;
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
lastCellRow = -1;
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
|
||||
// If we've moved onto a new row, the ensure we re-set
|
||||
// the column counter
|
||||
if(thisRow != lastCellRow) {
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
// If we've moved onto a new row, the ensure we re-set
|
||||
// the column counter
|
||||
if(thisRow != lastCellRow) {
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
|
||||
// If there's a gap in the cells, then fire
|
||||
// the dummy cell records
|
||||
if(lastCellColumn != thisColumn-1) {
|
||||
for(int i=lastCellColumn+1; i<thisColumn; i++) {
|
||||
childListener.processRecord(new MissingCellDummyRecord(thisRow, i));
|
||||
}
|
||||
}
|
||||
// If there's a gap in the cells, then fire
|
||||
// the dummy cell records
|
||||
if(lastCellColumn != thisColumn-1) {
|
||||
for(int i=lastCellColumn+1; i<thisColumn; i++) {
|
||||
childListener.processRecord(new MissingCellDummyRecord(thisRow, i));
|
||||
}
|
||||
}
|
||||
|
||||
// Next part of expanded record handling
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
thisColumn = expandedRecords[expandedRecords.length-1].getColumn();
|
||||
}
|
||||
// Next part of expanded record handling
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
thisColumn = expandedRecords[expandedRecords.length-1].getColumn();
|
||||
}
|
||||
|
||||
|
||||
// Update cell and row counts as needed
|
||||
if(thisColumn != -1) {
|
||||
lastCellColumn = thisColumn;
|
||||
lastCellRow = thisRow;
|
||||
}
|
||||
// Update cell and row counts as needed
|
||||
if(thisColumn != -1) {
|
||||
lastCellColumn = thisColumn;
|
||||
lastCellRow = thisRow;
|
||||
}
|
||||
|
||||
// Pass along the record(s)
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
for(CellValueRecordInterface r : expandedRecords) {
|
||||
childListener.processRecord((org.apache.poi.hssf.record.Record)r);
|
||||
}
|
||||
} else {
|
||||
childListener.processRecord(record);
|
||||
}
|
||||
}
|
||||
// Pass along the record(s)
|
||||
if(expandedRecords != null && expandedRecords.length > 0) {
|
||||
for(CellValueRecordInterface r : expandedRecords) {
|
||||
childListener.processRecord((org.apache.poi.hssf.record.Record)r);
|
||||
}
|
||||
} else {
|
||||
childListener.processRecord(record);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetCounts() {
|
||||
lastRowRow = -1;
|
||||
lastCellRow = -1;
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
private void resetCounts() {
|
||||
lastRowRow = -1;
|
||||
lastCellRow = -1;
|
||||
lastCellColumn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,17 +24,17 @@ import org.apache.poi.util.RecordFormatException;
|
||||
*/
|
||||
abstract class DummyRecordBase extends Record {
|
||||
|
||||
protected DummyRecordBase() {}
|
||||
protected DummyRecordBase() {}
|
||||
|
||||
public final short getSid() {
|
||||
return -1;
|
||||
}
|
||||
public int serialize(int offset, byte[] data) {
|
||||
throw new RecordFormatException("Cannot serialize a dummy record");
|
||||
}
|
||||
public final int getRecordSize() {
|
||||
throw new RecordFormatException("Cannot serialize a dummy record");
|
||||
}
|
||||
public final short getSid() {
|
||||
return -1;
|
||||
}
|
||||
public int serialize(int offset, byte[] data) {
|
||||
throw new RecordFormatException("Cannot serialize a dummy record");
|
||||
}
|
||||
public final int getRecordSize() {
|
||||
throw new RecordFormatException("Cannot serialize a dummy record");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -29,52 +29,52 @@ import org.apache.poi.util.GenericRecordUtil;
|
||||
* cell record for this row.
|
||||
*/
|
||||
public final class LastCellOfRowDummyRecord extends DummyRecordBase {
|
||||
private final int row;
|
||||
private final int lastColumnNumber;
|
||||
private final int row;
|
||||
private final int lastColumnNumber;
|
||||
|
||||
public LastCellOfRowDummyRecord(int row, int lastColumnNumber) {
|
||||
this.row = row;
|
||||
this.lastColumnNumber = lastColumnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (0 based) number of the row we are
|
||||
* currently working on.
|
||||
*
|
||||
* @return the (0 based) number of the row
|
||||
*/
|
||||
public int getRow() {
|
||||
return row;
|
||||
public LastCellOfRowDummyRecord(int row, int lastColumnNumber) {
|
||||
this.row = row;
|
||||
this.lastColumnNumber = lastColumnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (0 based) number of the last column
|
||||
* seen for this row. You should have already been
|
||||
* called with that record.
|
||||
* This is -1 in the case of there being no columns
|
||||
* for the row.
|
||||
*
|
||||
* @return the (0 based) number of the last column
|
||||
*/
|
||||
public int getLastColumnNumber() {
|
||||
return lastColumnNumber;
|
||||
/**
|
||||
* Returns the (0 based) number of the row we are
|
||||
* currently working on.
|
||||
*
|
||||
* @return the (0 based) number of the row
|
||||
*/
|
||||
public int getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LastCellOfRowDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Returns the (0 based) number of the last column
|
||||
* seen for this row. You should have already been
|
||||
* called with that record.
|
||||
* This is -1 in the case of there being no columns
|
||||
* for the row.
|
||||
*
|
||||
* @return the (0 based) number of the last column
|
||||
*/
|
||||
public int getLastColumnNumber() {
|
||||
return lastColumnNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public LastCellOfRowDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"lastColumnNumber", this::getLastColumnNumber
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"lastColumnNumber", this::getLastColumnNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,31 +29,31 @@ import org.apache.poi.util.GenericRecordUtil;
|
||||
* but still want to trigger something
|
||||
*/
|
||||
public final class MissingCellDummyRecord extends DummyRecordBase {
|
||||
private final int row;
|
||||
private final int column;
|
||||
private final int row;
|
||||
private final int column;
|
||||
|
||||
public MissingCellDummyRecord(int row, int column) {
|
||||
this.row = row;
|
||||
this.column = column;
|
||||
}
|
||||
public int getRow() { return row; }
|
||||
public int getColumn() { return column; }
|
||||
public MissingCellDummyRecord(int row, int column) {
|
||||
this.row = row;
|
||||
this.column = column;
|
||||
}
|
||||
public int getRow() { return row; }
|
||||
public int getColumn() { return column; }
|
||||
|
||||
@Override
|
||||
public MissingCellDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public MissingCellDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"column", this::getColumn
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"column", this::getColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,29 +29,29 @@ import org.apache.poi.util.GenericRecordUtil;
|
||||
* want to trigger something
|
||||
*/
|
||||
public final class MissingRowDummyRecord extends DummyRecordBase {
|
||||
private final int rowNumber;
|
||||
private final int rowNumber;
|
||||
|
||||
public MissingRowDummyRecord(int rowNumber) {
|
||||
this.rowNumber = rowNumber;
|
||||
}
|
||||
public int getRowNumber() {
|
||||
return rowNumber;
|
||||
}
|
||||
public MissingRowDummyRecord(int rowNumber) {
|
||||
this.rowNumber = rowNumber;
|
||||
}
|
||||
public int getRowNumber() {
|
||||
return rowNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MissingRowDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public MissingRowDummyRecord copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rowNumber", this::getRowNumber
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rowNumber", this::getRowNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,383 +54,383 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
|
||||
* @see <a href="http://svn.apache.org/repos/asf/poi/trunk/poi-examples/src/main/java/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java">XLS2CSVmra</a>
|
||||
*/
|
||||
public class ExcelExtractor implements POIOLE2TextExtractor, org.apache.poi.ss.extractor.ExcelExtractor {
|
||||
private final HSSFWorkbook _wb;
|
||||
private final HSSFDataFormatter _formatter;
|
||||
private boolean doCloseFilesystem = true;
|
||||
private boolean _includeSheetNames = true;
|
||||
private boolean _shouldEvaluateFormulas = true;
|
||||
private boolean _includeCellComments;
|
||||
private boolean _includeBlankCells;
|
||||
private boolean _includeHeadersFooters = true;
|
||||
private final HSSFWorkbook _wb;
|
||||
private final HSSFDataFormatter _formatter;
|
||||
private boolean doCloseFilesystem = true;
|
||||
private boolean _includeSheetNames = true;
|
||||
private boolean _shouldEvaluateFormulas = true;
|
||||
private boolean _includeCellComments;
|
||||
private boolean _includeBlankCells;
|
||||
private boolean _includeHeadersFooters = true;
|
||||
|
||||
public ExcelExtractor(HSSFWorkbook wb) {
|
||||
_wb = wb;
|
||||
_formatter = new HSSFDataFormatter();
|
||||
}
|
||||
public ExcelExtractor(HSSFWorkbook wb) {
|
||||
_wb = wb;
|
||||
_formatter = new HSSFDataFormatter();
|
||||
}
|
||||
|
||||
public ExcelExtractor(POIFSFileSystem fs) throws IOException {
|
||||
this(fs.getRoot());
|
||||
}
|
||||
public ExcelExtractor(POIFSFileSystem fs) throws IOException {
|
||||
this(fs.getRoot());
|
||||
}
|
||||
|
||||
public ExcelExtractor(DirectoryNode dir) throws IOException {
|
||||
this(new HSSFWorkbook(dir, true));
|
||||
}
|
||||
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 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");
|
||||
}
|
||||
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 {
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (cmdArgs.isRequestHelp()) {
|
||||
printUsageMessage(System.out);
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream is = cmdArgs.getInputFile() == null ? System.in : new FileInputStream(cmdArgs.getInputFile());
|
||||
HSSFWorkbook wb = new HSSFWorkbook(is);
|
||||
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());
|
||||
}
|
||||
}
|
||||
try (InputStream is = cmdArgs.getInputFile() == null ? System.in : new FileInputStream(cmdArgs.getInputFile());
|
||||
HSSFWorkbook wb = new HSSFWorkbook(is);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void setIncludeSheetNames(boolean includeSheetNames) {
|
||||
_includeSheetNames = includeSheetNames;
|
||||
}
|
||||
_includeSheetNames = includeSheetNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void setFormulasNotResults(boolean formulasNotResults) {
|
||||
_shouldEvaluateFormulas = !formulasNotResults;
|
||||
}
|
||||
_shouldEvaluateFormulas = !formulasNotResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void setIncludeCellComments(boolean includeCellComments) {
|
||||
_includeCellComments = 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;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
@Override
|
||||
public void setIncludeHeadersFooters(boolean includeHeadersFooters) {
|
||||
_includeHeadersFooters = includeHeadersFooters;
|
||||
}
|
||||
_includeHeadersFooters = includeHeadersFooters;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public String getText() {
|
||||
StringBuilder text = new StringBuilder();
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
// We don't care about the difference between
|
||||
// null (missing) and blank cells
|
||||
_wb.setMissingCellPolicy(MissingCellPolicy.RETURN_BLANK_AS_NULL);
|
||||
// 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; }
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
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()));
|
||||
}
|
||||
// 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; }
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
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.getCellType()) {
|
||||
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.getCachedFormulaResultType()) {
|
||||
case STRING:
|
||||
HSSFRichTextString str = cell.getRichStringCellValue();
|
||||
if(str != null && str.length() > 0) {
|
||||
text.append(str);
|
||||
}
|
||||
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.getCachedFormulaResultType());
|
||||
if(cell == null) {
|
||||
// Only output if requested
|
||||
outputContents = _includeBlankCells;
|
||||
} else {
|
||||
switch(cell.getCellType()) {
|
||||
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.getCachedFormulaResultType()) {
|
||||
case STRING:
|
||||
HSSFRichTextString str = cell.getRichStringCellValue();
|
||||
if(str != null && str.length() > 0) {
|
||||
text.append(str);
|
||||
}
|
||||
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.getCachedFormulaResultType());
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
||||
// 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 ").append(comment.getAuthor()).append(": ").append(commentText);
|
||||
}
|
||||
}
|
||||
// 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 ").append(comment.getAuthor()).append(": ").append(commentText);
|
||||
}
|
||||
}
|
||||
|
||||
// Output a tab if we're not on the last cell
|
||||
if(outputContents && k < (lastCell-1)) {
|
||||
text.append("\t");
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
// Finish off the row
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
// Finally Footer text, if there is any
|
||||
if(_includeHeadersFooters) {
|
||||
text.append(_extractHeaderFooter(sheet.getFooter()));
|
||||
}
|
||||
}
|
||||
// Finally Footer text, if there is any
|
||||
if(_includeHeadersFooters) {
|
||||
text.append(_extractHeaderFooter(sheet.getFooter()));
|
||||
}
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public static String _extractHeaderFooter(HeaderFooter hf) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
public static String _extractHeaderFooter(HeaderFooter hf) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
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");
|
||||
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();
|
||||
}
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFWorkbook getDocument() {
|
||||
return _wb;
|
||||
}
|
||||
@Override
|
||||
public HSSFWorkbook getDocument() {
|
||||
return _wb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCloseFilesystem(boolean doCloseFilesystem) {
|
||||
this.doCloseFilesystem = doCloseFilesystem;
|
||||
}
|
||||
@Override
|
||||
public void setCloseFilesystem(boolean doCloseFilesystem) {
|
||||
this.doCloseFilesystem = doCloseFilesystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloseFilesystem() {
|
||||
return doCloseFilesystem;
|
||||
}
|
||||
@Override
|
||||
public boolean isCloseFilesystem() {
|
||||
return doCloseFilesystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFWorkbook getFilesystem() {
|
||||
return _wb;
|
||||
}
|
||||
@Override
|
||||
public HSSFWorkbook getFilesystem() {
|
||||
return _wb;
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,38 +316,38 @@ public class OldExcelExtractor implements POITextExtractor {
|
||||
|
||||
@Override
|
||||
public POITextExtractor getMetadataTextExtractor() {
|
||||
return new POITextExtractor() {
|
||||
return new POITextExtractor() {
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String getText() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public POITextExtractor getMetadataTextExtractor() {
|
||||
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
|
||||
}
|
||||
@Override
|
||||
public POITextExtractor getMetadataTextExtractor() {
|
||||
throw new IllegalStateException("You already have the Metadata Text Extractor, not recursing!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCloseFilesystem(boolean doCloseFilesystem) {
|
||||
@Override
|
||||
public void setCloseFilesystem(boolean doCloseFilesystem) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloseFilesystem() {
|
||||
return toClose != null;
|
||||
}
|
||||
@Override
|
||||
public boolean isCloseFilesystem() {
|
||||
return toClose != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Closeable getFilesystem() {
|
||||
return toClose;
|
||||
}
|
||||
@Override
|
||||
public Closeable getFilesystem() {
|
||||
return toClose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDocument() {
|
||||
return ris;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public Object getDocument() {
|
||||
return ris;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -40,7 +40,7 @@ public class DrawingManager2 {
|
||||
* Clears the cached list of drawing groups
|
||||
*/
|
||||
public void clearDrawingGroups() {
|
||||
drawingGroups.clear();
|
||||
drawingGroups.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -249,7 +249,7 @@ public final class InternalSheet {
|
||||
}
|
||||
|
||||
if (recSid == FeatRecord.sid ||
|
||||
recSid == FeatHdrRecord.sid) {
|
||||
recSid == FeatHdrRecord.sid) {
|
||||
records.add(rec);
|
||||
continue;
|
||||
}
|
||||
@ -737,7 +737,7 @@ public final class InternalSheet {
|
||||
* @return Iterator of CellValueRecordInterface representing the value records
|
||||
*/
|
||||
public Iterator<CellValueRecordInterface> getCellValueIterator(){
|
||||
return _rowsAggregate.getCellValueIterator();
|
||||
return _rowsAggregate.getCellValueIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -136,11 +136,11 @@ public final class InternalWorkbook {
|
||||
private final List<HyperlinkRecord> hyperlinks;
|
||||
|
||||
/** the number of extended format records */
|
||||
private int numxfs;
|
||||
private int numxfs;
|
||||
/** the number of font records */
|
||||
private int numfonts;
|
||||
private int numfonts;
|
||||
/** holds the max format id */
|
||||
private int maxformatid;
|
||||
private int maxformatid;
|
||||
/** whether 1904 date windowing is being used */
|
||||
private boolean uses1904datewindowing;
|
||||
private DrawingManager2 drawingManager;
|
||||
@ -156,17 +156,17 @@ public final class InternalWorkbook {
|
||||
private final Map<String, NameCommentRecord> commentRecords;
|
||||
|
||||
private InternalWorkbook() {
|
||||
records = new WorkbookRecordList();
|
||||
records = new WorkbookRecordList();
|
||||
|
||||
boundsheets = new ArrayList<>();
|
||||
formats = new ArrayList<>();
|
||||
hyperlinks = new ArrayList<>();
|
||||
numxfs = 0;
|
||||
numfonts = 0;
|
||||
maxformatid = -1;
|
||||
uses1904datewindowing = false;
|
||||
escherBSERecords = new ArrayList<>();
|
||||
commentRecords = new LinkedHashMap<>();
|
||||
boundsheets = new ArrayList<>();
|
||||
formats = new ArrayList<>();
|
||||
hyperlinks = new ArrayList<>();
|
||||
numxfs = 0;
|
||||
numfonts = 0;
|
||||
maxformatid = -1;
|
||||
uses1904datewindowing = false;
|
||||
escherBSERecords = new ArrayList<>();
|
||||
commentRecords = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -609,7 +609,7 @@ public final class InternalWorkbook {
|
||||
int pos0 = initialBspos - (boundsheets.size() - 1);
|
||||
Record removed = records.get(pos0 + sheetNumber);
|
||||
records.remove(pos0 + sheetNumber);
|
||||
records.add(pos0 + pos, removed);
|
||||
records.add(pos0 + pos, removed);
|
||||
records.setBspos(initialBspos);
|
||||
}
|
||||
|
||||
@ -2224,17 +2224,17 @@ public final class InternalWorkbook {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes an external referenced file to another file.
|
||||
* A formular in Excel which refers a cell in another file is saved in two parts:
|
||||
* The referenced file is stored in an reference table. the row/cell information is saved separate.
|
||||
* This method invokation will only change the reference in the lookup-table itself.
|
||||
* @param oldUrl The old URL to search for and which is to be replaced
|
||||
* @param newUrl The URL replacement
|
||||
* @return true if the oldUrl was found and replaced with newUrl. Otherwise false
|
||||
*/
|
||||
/**
|
||||
* Changes an external referenced file to another file.
|
||||
* A formular in Excel which refers a cell in another file is saved in two parts:
|
||||
* The referenced file is stored in an reference table. the row/cell information is saved separate.
|
||||
* This method invokation will only change the reference in the lookup-table itself.
|
||||
* @param oldUrl The old URL to search for and which is to be replaced
|
||||
* @param newUrl The URL replacement
|
||||
* @return true if the oldUrl was found and replaced with newUrl. Otherwise false
|
||||
*/
|
||||
public boolean changeExternalReference(String oldUrl, String newUrl) {
|
||||
return linkTable.changeExternalReference(oldUrl, newUrl);
|
||||
return linkTable.changeExternalReference(oldUrl, newUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -80,379 +80,379 @@ import org.apache.poi.hssf.record.pivottable.ViewDefinitionRecord;
|
||||
*/
|
||||
final class RecordOrderer {
|
||||
|
||||
// TODO - simplify logic using a generalised record ordering
|
||||
// TODO - simplify logic using a generalised record ordering
|
||||
|
||||
private RecordOrderer() {
|
||||
// no instances of this class
|
||||
}
|
||||
/**
|
||||
* Adds the specified new record in the correct place in sheet records list
|
||||
*/
|
||||
public static void addNewSheetRecord(List<RecordBase> sheetRecords, RecordBase newRecord) {
|
||||
int index = findSheetInsertPos(sheetRecords, newRecord.getClass());
|
||||
sheetRecords.add(index, newRecord);
|
||||
}
|
||||
private RecordOrderer() {
|
||||
// no instances of this class
|
||||
}
|
||||
/**
|
||||
* Adds the specified new record in the correct place in sheet records list
|
||||
*/
|
||||
public static void addNewSheetRecord(List<RecordBase> sheetRecords, RecordBase newRecord) {
|
||||
int index = findSheetInsertPos(sheetRecords, newRecord.getClass());
|
||||
sheetRecords.add(index, newRecord);
|
||||
}
|
||||
|
||||
private static int findSheetInsertPos(List<RecordBase> records, Class<? extends RecordBase> recClass) {
|
||||
if (recClass == DataValidityTable.class) {
|
||||
return findDataValidationTableInsertPos(records);
|
||||
}
|
||||
if (recClass == MergedCellsTable.class) {
|
||||
return findInsertPosForNewMergedRecordTable(records);
|
||||
}
|
||||
if (recClass == ConditionalFormattingTable.class) {
|
||||
return findInsertPosForNewCondFormatTable(records);
|
||||
}
|
||||
if (recClass == GutsRecord.class) {
|
||||
return getGutsRecordInsertPos(records);
|
||||
}
|
||||
if (recClass == PageSettingsBlock.class) {
|
||||
return getPageBreakRecordInsertPos(records);
|
||||
}
|
||||
if (recClass == WorksheetProtectionBlock.class) {
|
||||
return getWorksheetProtectionBlockInsertPos(records);
|
||||
}
|
||||
throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")");
|
||||
}
|
||||
private static int findSheetInsertPos(List<RecordBase> records, Class<? extends RecordBase> recClass) {
|
||||
if (recClass == DataValidityTable.class) {
|
||||
return findDataValidationTableInsertPos(records);
|
||||
}
|
||||
if (recClass == MergedCellsTable.class) {
|
||||
return findInsertPosForNewMergedRecordTable(records);
|
||||
}
|
||||
if (recClass == ConditionalFormattingTable.class) {
|
||||
return findInsertPosForNewCondFormatTable(records);
|
||||
}
|
||||
if (recClass == GutsRecord.class) {
|
||||
return getGutsRecordInsertPos(records);
|
||||
}
|
||||
if (recClass == PageSettingsBlock.class) {
|
||||
return getPageBreakRecordInsertPos(records);
|
||||
}
|
||||
if (recClass == WorksheetProtectionBlock.class) {
|
||||
return getWorksheetProtectionBlockInsertPos(records);
|
||||
}
|
||||
throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index where the protection block should be inserted
|
||||
* @param records the records for this sheet
|
||||
* <pre>
|
||||
* + BOF
|
||||
* o INDEX
|
||||
* o Calculation Settings Block
|
||||
* o PRINTHEADERS
|
||||
* o PRINTGRIDLINES
|
||||
* o GRIDSET
|
||||
* o GUTS
|
||||
* o DEFAULTROWHEIGHT
|
||||
* o SHEETPR
|
||||
* o Page Settings Block
|
||||
* o Worksheet Protection Block
|
||||
* o DEFCOLWIDTH
|
||||
* oo COLINFO
|
||||
* o SORT
|
||||
* + DIMENSION
|
||||
* </pre>
|
||||
*/
|
||||
private static int getWorksheetProtectionBlockInsertPos(List<RecordBase> records) {
|
||||
int i = getDimensionsIndex(records);
|
||||
while (i > 0) {
|
||||
i--;
|
||||
Object rb = records.get(i);
|
||||
if (!isProtectionSubsequentRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("did not find insert pos for protection block");
|
||||
}
|
||||
/**
|
||||
* Finds the index where the protection block should be inserted
|
||||
* @param records the records for this sheet
|
||||
* <pre>
|
||||
* + BOF
|
||||
* o INDEX
|
||||
* o Calculation Settings Block
|
||||
* o PRINTHEADERS
|
||||
* o PRINTGRIDLINES
|
||||
* o GRIDSET
|
||||
* o GUTS
|
||||
* o DEFAULTROWHEIGHT
|
||||
* o SHEETPR
|
||||
* o Page Settings Block
|
||||
* o Worksheet Protection Block
|
||||
* o DEFCOLWIDTH
|
||||
* oo COLINFO
|
||||
* o SORT
|
||||
* + DIMENSION
|
||||
* </pre>
|
||||
*/
|
||||
private static int getWorksheetProtectionBlockInsertPos(List<RecordBase> records) {
|
||||
int i = getDimensionsIndex(records);
|
||||
while (i > 0) {
|
||||
i--;
|
||||
Object rb = records.get(i);
|
||||
if (!isProtectionSubsequentRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("did not find insert pos for protection block");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* These records may occur between the 'Worksheet Protection Block' and DIMENSION:
|
||||
* <pre>
|
||||
* o DEFCOLWIDTH
|
||||
* oo COLINFO
|
||||
* o SORT
|
||||
* </pre>
|
||||
*/
|
||||
private static boolean isProtectionSubsequentRecord(Object rb) {
|
||||
if (rb instanceof ColumnInfoRecordsAggregate) {
|
||||
return true; // oo COLINFO
|
||||
}
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case DefaultColWidthRecord.sid:
|
||||
case UnknownRecord.SORT_0090:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* These records may occur between the 'Worksheet Protection Block' and DIMENSION:
|
||||
* <pre>
|
||||
* o DEFCOLWIDTH
|
||||
* oo COLINFO
|
||||
* o SORT
|
||||
* </pre>
|
||||
*/
|
||||
private static boolean isProtectionSubsequentRecord(Object rb) {
|
||||
if (rb instanceof ColumnInfoRecordsAggregate) {
|
||||
return true; // oo COLINFO
|
||||
}
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case DefaultColWidthRecord.sid:
|
||||
case UnknownRecord.SORT_0090:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getPageBreakRecordInsertPos(List<RecordBase> records) {
|
||||
int dimensionsIndex = getDimensionsIndex(records);
|
||||
int i = dimensionsIndex-1;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
Object rb = records.get(i);
|
||||
if (isPageBreakPriorRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find insert point for GUTS");
|
||||
}
|
||||
private static boolean isPageBreakPriorRecord(Object rb) {
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case BOFRecord.sid:
|
||||
case IndexRecord.sid:
|
||||
// calc settings block
|
||||
case UncalcedRecord.sid:
|
||||
case CalcCountRecord.sid:
|
||||
case CalcModeRecord.sid:
|
||||
case PrecisionRecord.sid:
|
||||
case RefModeRecord.sid:
|
||||
case DeltaRecord.sid:
|
||||
case IterationRecord.sid:
|
||||
case DateWindow1904Record.sid:
|
||||
case SaveRecalcRecord.sid:
|
||||
// end calc settings
|
||||
case PrintHeadersRecord.sid:
|
||||
case PrintGridlinesRecord.sid:
|
||||
case GridsetRecord.sid:
|
||||
case DefaultRowHeightRecord.sid:
|
||||
case UnknownRecord.SHEETPR_0081:
|
||||
return true;
|
||||
// next is the 'Worksheet Protection Block'
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Find correct position to add new CFHeader record
|
||||
*/
|
||||
private static int findInsertPosForNewCondFormatTable(List<RecordBase> records) {
|
||||
private static int getPageBreakRecordInsertPos(List<RecordBase> records) {
|
||||
int dimensionsIndex = getDimensionsIndex(records);
|
||||
int i = dimensionsIndex-1;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
Object rb = records.get(i);
|
||||
if (isPageBreakPriorRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find insert point for GUTS");
|
||||
}
|
||||
private static boolean isPageBreakPriorRecord(Object rb) {
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case BOFRecord.sid:
|
||||
case IndexRecord.sid:
|
||||
// calc settings block
|
||||
case UncalcedRecord.sid:
|
||||
case CalcCountRecord.sid:
|
||||
case CalcModeRecord.sid:
|
||||
case PrecisionRecord.sid:
|
||||
case RefModeRecord.sid:
|
||||
case DeltaRecord.sid:
|
||||
case IterationRecord.sid:
|
||||
case DateWindow1904Record.sid:
|
||||
case SaveRecalcRecord.sid:
|
||||
// end calc settings
|
||||
case PrintHeadersRecord.sid:
|
||||
case PrintGridlinesRecord.sid:
|
||||
case GridsetRecord.sid:
|
||||
case DefaultRowHeightRecord.sid:
|
||||
case UnknownRecord.SHEETPR_0081:
|
||||
return true;
|
||||
// next is the 'Worksheet Protection Block'
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Find correct position to add new CFHeader record
|
||||
*/
|
||||
private static int findInsertPosForNewCondFormatTable(List<RecordBase> records) {
|
||||
|
||||
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
|
||||
Object rb = records.get(i);
|
||||
if (rb instanceof MergedCellsTable) {
|
||||
return i + 1;
|
||||
}
|
||||
if (rb instanceof DataValidityTable) {
|
||||
continue;
|
||||
}
|
||||
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
|
||||
Object rb = records.get(i);
|
||||
if (rb instanceof MergedCellsTable) {
|
||||
return i + 1;
|
||||
}
|
||||
if (rb instanceof DataValidityTable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (rec.getSid()) {
|
||||
case WindowTwoRecord.sid:
|
||||
case SCLRecord.sid:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
// MergedCellsTable usually here
|
||||
case UnknownRecord.LABELRANGES_015F:
|
||||
case UnknownRecord.PHONETICPR_00EF:
|
||||
// ConditionalFormattingTable goes here
|
||||
return i + 1;
|
||||
// HyperlinkTable (not aggregated by POI yet)
|
||||
// DataValidityTable
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find Window2 record");
|
||||
}
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (rec.getSid()) {
|
||||
case WindowTwoRecord.sid:
|
||||
case SCLRecord.sid:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
// MergedCellsTable usually here
|
||||
case UnknownRecord.LABELRANGES_015F:
|
||||
case UnknownRecord.PHONETICPR_00EF:
|
||||
// ConditionalFormattingTable goes here
|
||||
return i + 1;
|
||||
// HyperlinkTable (not aggregated by POI yet)
|
||||
// DataValidityTable
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find Window2 record");
|
||||
}
|
||||
|
||||
private static int findInsertPosForNewMergedRecordTable(List<RecordBase> records) {
|
||||
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
|
||||
Object rb = records.get(i);
|
||||
if (!(rb instanceof Record)) {
|
||||
// DataValidityTable, ConditionalFormattingTable,
|
||||
// even PageSettingsBlock (which doesn't normally appear after 'View Settings')
|
||||
continue;
|
||||
}
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (rec.getSid()) {
|
||||
// 'View Settings' (4 records)
|
||||
case WindowTwoRecord.sid:
|
||||
case SCLRecord.sid:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
private static int findInsertPosForNewMergedRecordTable(List<RecordBase> records) {
|
||||
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
|
||||
Object rb = records.get(i);
|
||||
if (!(rb instanceof Record)) {
|
||||
// DataValidityTable, ConditionalFormattingTable,
|
||||
// even PageSettingsBlock (which doesn't normally appear after 'View Settings')
|
||||
continue;
|
||||
}
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (rec.getSid()) {
|
||||
// 'View Settings' (4 records)
|
||||
case WindowTwoRecord.sid:
|
||||
case SCLRecord.sid:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find Window2 record");
|
||||
}
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find Window2 record");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the index where the sheet validations header record should be inserted
|
||||
* @param records the records for this sheet
|
||||
*
|
||||
* + WINDOW2
|
||||
* o SCL
|
||||
* o PANE
|
||||
* oo SELECTION
|
||||
* o STANDARDWIDTH
|
||||
* oo MERGEDCELLS
|
||||
* o LABELRANGES
|
||||
* o PHONETICPR
|
||||
* o Conditional Formatting Table
|
||||
* o Hyperlink Table
|
||||
* o Data Validity Table
|
||||
* o SHEETLAYOUT
|
||||
* o SHEETPROTECTION
|
||||
* o RANGEPROTECTION
|
||||
* + EOF
|
||||
*/
|
||||
private static int findDataValidationTableInsertPos(List<RecordBase> records) {
|
||||
int i = records.size() - 1;
|
||||
if (!(records.get(i) instanceof EOFRecord)) {
|
||||
throw new IllegalStateException("Last sheet record should be EOFRecord");
|
||||
}
|
||||
while (i > 0) {
|
||||
i--;
|
||||
RecordBase rb = records.get(i);
|
||||
if (isDVTPriorRecord(rb)) {
|
||||
Record nextRec = (org.apache.poi.hssf.record.Record) records.get(i + 1);
|
||||
if (!isDVTSubsequentRecord(nextRec.getSid())) {
|
||||
throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName()
|
||||
+ ") found after (" + rb.getClass().getName() + ")");
|
||||
}
|
||||
return i+1;
|
||||
}
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
if (!isDVTSubsequentRecord(rec.getSid())) {
|
||||
throw new IllegalStateException("Unexpected (" + rec.getClass().getName()
|
||||
+ ") while looking for DV Table insert pos");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Finds the index where the sheet validations header record should be inserted
|
||||
* @param records the records for this sheet
|
||||
*
|
||||
* + WINDOW2
|
||||
* o SCL
|
||||
* o PANE
|
||||
* oo SELECTION
|
||||
* o STANDARDWIDTH
|
||||
* oo MERGEDCELLS
|
||||
* o LABELRANGES
|
||||
* o PHONETICPR
|
||||
* o Conditional Formatting Table
|
||||
* o Hyperlink Table
|
||||
* o Data Validity Table
|
||||
* o SHEETLAYOUT
|
||||
* o SHEETPROTECTION
|
||||
* o RANGEPROTECTION
|
||||
* + EOF
|
||||
*/
|
||||
private static int findDataValidationTableInsertPos(List<RecordBase> records) {
|
||||
int i = records.size() - 1;
|
||||
if (!(records.get(i) instanceof EOFRecord)) {
|
||||
throw new IllegalStateException("Last sheet record should be EOFRecord");
|
||||
}
|
||||
while (i > 0) {
|
||||
i--;
|
||||
RecordBase rb = records.get(i);
|
||||
if (isDVTPriorRecord(rb)) {
|
||||
Record nextRec = (org.apache.poi.hssf.record.Record) records.get(i + 1);
|
||||
if (!isDVTSubsequentRecord(nextRec.getSid())) {
|
||||
throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName()
|
||||
+ ") found after (" + rb.getClass().getName() + ")");
|
||||
}
|
||||
return i+1;
|
||||
}
|
||||
Record rec = (org.apache.poi.hssf.record.Record) rb;
|
||||
if (!isDVTSubsequentRecord(rec.getSid())) {
|
||||
throw new IllegalStateException("Unexpected (" + rec.getClass().getName()
|
||||
+ ") while looking for DV Table insert pos");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isDVTPriorRecord(RecordBase rb) {
|
||||
if (rb instanceof MergedCellsTable || rb instanceof ConditionalFormattingTable) {
|
||||
return true;
|
||||
}
|
||||
short sid = ((org.apache.poi.hssf.record.Record)rb).getSid();
|
||||
switch(sid) {
|
||||
case WindowTwoRecord.sid:
|
||||
case UnknownRecord.SCL_00A0:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
// MergedCellsTable
|
||||
case UnknownRecord.LABELRANGES_015F:
|
||||
case UnknownRecord.PHONETICPR_00EF:
|
||||
// ConditionalFormattingTable
|
||||
case HyperlinkRecord.sid:
|
||||
case UnknownRecord.QUICKTIP_0800:
|
||||
private static boolean isDVTPriorRecord(RecordBase rb) {
|
||||
if (rb instanceof MergedCellsTable || rb instanceof ConditionalFormattingTable) {
|
||||
return true;
|
||||
}
|
||||
short sid = ((org.apache.poi.hssf.record.Record)rb).getSid();
|
||||
switch(sid) {
|
||||
case WindowTwoRecord.sid:
|
||||
case UnknownRecord.SCL_00A0:
|
||||
case PaneRecord.sid:
|
||||
case SelectionRecord.sid:
|
||||
case UnknownRecord.STANDARDWIDTH_0099:
|
||||
// MergedCellsTable
|
||||
case UnknownRecord.LABELRANGES_015F:
|
||||
case UnknownRecord.PHONETICPR_00EF:
|
||||
// ConditionalFormattingTable
|
||||
case HyperlinkRecord.sid:
|
||||
case UnknownRecord.QUICKTIP_0800:
|
||||
// name of a VBA module
|
||||
case UnknownRecord.CODENAME_1BA:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isDVTSubsequentRecord(short sid) {
|
||||
switch(sid) {
|
||||
case UnknownRecord.SHEETEXT_0862:
|
||||
case UnknownRecord.SHEETPROTECTION_0867:
|
||||
case UnknownRecord.PLV_MAC:
|
||||
case FeatRecord.sid:
|
||||
case EOFRecord.sid:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* DIMENSIONS record is always present
|
||||
*/
|
||||
private static int getDimensionsIndex(List<RecordBase> records) {
|
||||
int nRecs = records.size();
|
||||
for(int i=0; i<nRecs; i++) {
|
||||
if(records.get(i) instanceof DimensionsRecord) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// worksheet stream is seriously broken
|
||||
throw new RuntimeException("DimensionsRecord not found");
|
||||
}
|
||||
private static boolean isDVTSubsequentRecord(short sid) {
|
||||
switch(sid) {
|
||||
case UnknownRecord.SHEETEXT_0862:
|
||||
case UnknownRecord.SHEETPROTECTION_0867:
|
||||
case UnknownRecord.PLV_MAC:
|
||||
case FeatRecord.sid:
|
||||
case EOFRecord.sid:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* DIMENSIONS record is always present
|
||||
*/
|
||||
private static int getDimensionsIndex(List<RecordBase> records) {
|
||||
int nRecs = records.size();
|
||||
for(int i=0; i<nRecs; i++) {
|
||||
if(records.get(i) instanceof DimensionsRecord) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// worksheet stream is seriously broken
|
||||
throw new RuntimeException("DimensionsRecord not found");
|
||||
}
|
||||
|
||||
private static int getGutsRecordInsertPos(List<RecordBase> records) {
|
||||
int dimensionsIndex = getDimensionsIndex(records);
|
||||
int i = dimensionsIndex-1;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
RecordBase rb = records.get(i);
|
||||
if (isGutsPriorRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find insert point for GUTS");
|
||||
}
|
||||
private static int getGutsRecordInsertPos(List<RecordBase> records) {
|
||||
int dimensionsIndex = getDimensionsIndex(records);
|
||||
int i = dimensionsIndex-1;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
RecordBase rb = records.get(i);
|
||||
if (isGutsPriorRecord(rb)) {
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Did not find insert point for GUTS");
|
||||
}
|
||||
|
||||
private static boolean isGutsPriorRecord(RecordBase rb) {
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case BOFRecord.sid:
|
||||
case IndexRecord.sid:
|
||||
// calc settings block
|
||||
case UncalcedRecord.sid:
|
||||
case CalcCountRecord.sid:
|
||||
case CalcModeRecord.sid:
|
||||
case PrecisionRecord.sid:
|
||||
case RefModeRecord.sid:
|
||||
case DeltaRecord.sid:
|
||||
case IterationRecord.sid:
|
||||
case DateWindow1904Record.sid:
|
||||
case SaveRecalcRecord.sid:
|
||||
// end calc settings
|
||||
case PrintHeadersRecord.sid:
|
||||
case PrintGridlinesRecord.sid:
|
||||
case GridsetRecord.sid:
|
||||
return true;
|
||||
// DefaultRowHeightRecord.sid is next
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if the specified record ID terminates a sequence of Row block records
|
||||
* It is assumed that at least one row or cell value record has been found prior to the current
|
||||
* record
|
||||
*/
|
||||
public static boolean isEndOfRowBlock(int sid) {
|
||||
switch(sid) {
|
||||
case ViewDefinitionRecord.sid:
|
||||
// should have been prefixed with DrawingRecord (0x00EC), but bug 46280 seems to allow this
|
||||
case DrawingRecord.sid:
|
||||
case DrawingSelectionRecord.sid:
|
||||
case ObjRecord.sid:
|
||||
case TextObjectRecord.sid:
|
||||
private static boolean isGutsPriorRecord(RecordBase rb) {
|
||||
if (rb instanceof Record) {
|
||||
Record record = (org.apache.poi.hssf.record.Record) rb;
|
||||
switch (record.getSid()) {
|
||||
case BOFRecord.sid:
|
||||
case IndexRecord.sid:
|
||||
// calc settings block
|
||||
case UncalcedRecord.sid:
|
||||
case CalcCountRecord.sid:
|
||||
case CalcModeRecord.sid:
|
||||
case PrecisionRecord.sid:
|
||||
case RefModeRecord.sid:
|
||||
case DeltaRecord.sid:
|
||||
case IterationRecord.sid:
|
||||
case DateWindow1904Record.sid:
|
||||
case SaveRecalcRecord.sid:
|
||||
// end calc settings
|
||||
case PrintHeadersRecord.sid:
|
||||
case PrintGridlinesRecord.sid:
|
||||
case GridsetRecord.sid:
|
||||
return true;
|
||||
// DefaultRowHeightRecord.sid is next
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if the specified record ID terminates a sequence of Row block records
|
||||
* It is assumed that at least one row or cell value record has been found prior to the current
|
||||
* record
|
||||
*/
|
||||
public static boolean isEndOfRowBlock(int sid) {
|
||||
switch(sid) {
|
||||
case ViewDefinitionRecord.sid:
|
||||
// should have been prefixed with DrawingRecord (0x00EC), but bug 46280 seems to allow this
|
||||
case DrawingRecord.sid:
|
||||
case DrawingSelectionRecord.sid:
|
||||
case ObjRecord.sid:
|
||||
case TextObjectRecord.sid:
|
||||
case ColumnInfoRecord.sid: // See Bugzilla 53984
|
||||
case GutsRecord.sid: // see Bugzilla 50426
|
||||
case WindowOneRecord.sid:
|
||||
// should really be part of workbook stream, but some apps seem to put this before WINDOW2
|
||||
case WindowTwoRecord.sid:
|
||||
return true;
|
||||
case WindowOneRecord.sid:
|
||||
// should really be part of workbook stream, but some apps seem to put this before WINDOW2
|
||||
case WindowTwoRecord.sid:
|
||||
return true;
|
||||
|
||||
case DVALRecord.sid:
|
||||
return true;
|
||||
case EOFRecord.sid:
|
||||
// WINDOW2 should always be present, so shouldn't have got this far
|
||||
throw new RuntimeException("Found EOFRecord before WindowTwoRecord was encountered");
|
||||
}
|
||||
return PageSettingsBlock.isComponentRecord(sid);
|
||||
}
|
||||
case DVALRecord.sid:
|
||||
return true;
|
||||
case EOFRecord.sid:
|
||||
// WINDOW2 should always be present, so shouldn't have got this far
|
||||
throw new RuntimeException("Found EOFRecord before WindowTwoRecord was encountered");
|
||||
}
|
||||
return PageSettingsBlock.isComponentRecord(sid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the specified record id normally appears in the row blocks section
|
||||
* of the sheet records
|
||||
*/
|
||||
public static boolean isRowBlockRecord(int sid) {
|
||||
switch (sid) {
|
||||
case RowRecord.sid:
|
||||
/**
|
||||
* @return <code>true</code> if the specified record id normally appears in the row blocks section
|
||||
* of the sheet records
|
||||
*/
|
||||
public static boolean isRowBlockRecord(int sid) {
|
||||
switch (sid) {
|
||||
case RowRecord.sid:
|
||||
|
||||
case BlankRecord.sid:
|
||||
case BoolErrRecord.sid:
|
||||
case FormulaRecord.sid:
|
||||
case LabelRecord.sid:
|
||||
case LabelSSTRecord.sid:
|
||||
case NumberRecord.sid:
|
||||
case RKRecord.sid:
|
||||
case BlankRecord.sid:
|
||||
case BoolErrRecord.sid:
|
||||
case FormulaRecord.sid:
|
||||
case LabelRecord.sid:
|
||||
case LabelSSTRecord.sid:
|
||||
case NumberRecord.sid:
|
||||
case RKRecord.sid:
|
||||
|
||||
case ArrayRecord.sid:
|
||||
case SharedFormulaRecord.sid:
|
||||
case TableRecord.sid:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case ArrayRecord.sid:
|
||||
case SharedFormulaRecord.sid:
|
||||
case TableRecord.sid:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,69 +25,69 @@ import org.apache.poi.hssf.record.Record;
|
||||
*/
|
||||
public final class RecordStream {
|
||||
|
||||
private final List<org.apache.poi.hssf.record.Record> _list;
|
||||
private int _nextIndex;
|
||||
private int _countRead;
|
||||
private final int _endIx;
|
||||
private final List<org.apache.poi.hssf.record.Record> _list;
|
||||
private int _nextIndex;
|
||||
private int _countRead;
|
||||
private final int _endIx;
|
||||
|
||||
/**
|
||||
* Creates a RecordStream bounded by startIndex and endIndex
|
||||
*
|
||||
* @param inputList the list to iterate over
|
||||
* @param startIndex the start index within the list
|
||||
* @param endIx the end index within the list, which is the index of the end element + 1
|
||||
*/
|
||||
public RecordStream(List<org.apache.poi.hssf.record.Record> inputList, int startIndex, int endIx) {
|
||||
_list = inputList;
|
||||
_nextIndex = startIndex;
|
||||
_endIx = endIx;
|
||||
_countRead = 0;
|
||||
}
|
||||
/**
|
||||
* Creates a RecordStream bounded by startIndex and endIndex
|
||||
*
|
||||
* @param inputList the list to iterate over
|
||||
* @param startIndex the start index within the list
|
||||
* @param endIx the end index within the list, which is the index of the end element + 1
|
||||
*/
|
||||
public RecordStream(List<org.apache.poi.hssf.record.Record> inputList, int startIndex, int endIx) {
|
||||
_list = inputList;
|
||||
_nextIndex = startIndex;
|
||||
_endIx = endIx;
|
||||
_countRead = 0;
|
||||
}
|
||||
|
||||
public RecordStream(List<org.apache.poi.hssf.record.Record> records, int startIx) {
|
||||
this(records, startIx, records.size());
|
||||
}
|
||||
public RecordStream(List<org.apache.poi.hssf.record.Record> records, int startIx) {
|
||||
this(records, startIx, records.size());
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return _nextIndex < _endIx;
|
||||
}
|
||||
public boolean hasNext() {
|
||||
return _nextIndex < _endIx;
|
||||
}
|
||||
|
||||
public Record getNext() {
|
||||
if(!hasNext()) {
|
||||
throw new RuntimeException("Attempt to read past end of record stream");
|
||||
}
|
||||
_countRead ++;
|
||||
return _list.get(_nextIndex++);
|
||||
}
|
||||
public Record getNext() {
|
||||
if(!hasNext()) {
|
||||
throw new RuntimeException("Attempt to read past end of record stream");
|
||||
}
|
||||
_countRead ++;
|
||||
return _list.get(_nextIndex++);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Class} of the next Record. {@code null} if this stream is exhausted.
|
||||
*/
|
||||
public Class<? extends Record> peekNextClass() {
|
||||
if(!hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return _list.get(_nextIndex).getClass();
|
||||
}
|
||||
/**
|
||||
* @return the {@link Class} of the next Record. {@code null} if this stream is exhausted.
|
||||
*/
|
||||
public Class<? extends Record> peekNextClass() {
|
||||
if(!hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return _list.get(_nextIndex).getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next Record. {@code null} if this stream is exhausted.
|
||||
*/
|
||||
public Record peekNextRecord() {
|
||||
return (hasNext()) ? _list.get(_nextIndex) : null;
|
||||
}
|
||||
/**
|
||||
* @return the next Record. {@code null} if this stream is exhausted.
|
||||
*/
|
||||
public Record peekNextRecord() {
|
||||
return (hasNext()) ? _list.get(_nextIndex) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return -1 if at end of records
|
||||
*/
|
||||
public int peekNextSid() {
|
||||
if(!hasNext()) {
|
||||
return -1;
|
||||
}
|
||||
return _list.get(_nextIndex).getSid();
|
||||
}
|
||||
/**
|
||||
* @return -1 if at end of records
|
||||
*/
|
||||
public int peekNextSid() {
|
||||
if(!hasNext()) {
|
||||
return -1;
|
||||
}
|
||||
return _list.get(_nextIndex).getSid();
|
||||
}
|
||||
|
||||
public int getCountRead() {
|
||||
return _countRead;
|
||||
}
|
||||
public int getCountRead() {
|
||||
return _countRead;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,84 +36,84 @@ import org.apache.poi.ss.util.CellReference;
|
||||
*/
|
||||
public final class RowBlocksReader {
|
||||
|
||||
private final List<org.apache.poi.hssf.record.Record> _plainRecords;
|
||||
private final SharedValueManager _sfm;
|
||||
private final MergeCellsRecord[] _mergedCellsRecords;
|
||||
private final List<org.apache.poi.hssf.record.Record> _plainRecords;
|
||||
private final SharedValueManager _sfm;
|
||||
private final MergeCellsRecord[] _mergedCellsRecords;
|
||||
|
||||
/**
|
||||
* Also collects any loose MergeCellRecords and puts them in the supplied
|
||||
* mergedCellsTable
|
||||
*
|
||||
* @param rs the record stream
|
||||
*/
|
||||
public RowBlocksReader(RecordStream rs) {
|
||||
List<org.apache.poi.hssf.record.Record> plainRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> shFrmRecords = new ArrayList<>();
|
||||
List<CellReference> firstCellRefs = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> arrayRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> tableRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> mergeCellRecords = new ArrayList<>();
|
||||
/**
|
||||
* Also collects any loose MergeCellRecords and puts them in the supplied
|
||||
* mergedCellsTable
|
||||
*
|
||||
* @param rs the record stream
|
||||
*/
|
||||
public RowBlocksReader(RecordStream rs) {
|
||||
List<org.apache.poi.hssf.record.Record> plainRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> shFrmRecords = new ArrayList<>();
|
||||
List<CellReference> firstCellRefs = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> arrayRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> tableRecords = new ArrayList<>();
|
||||
List<org.apache.poi.hssf.record.Record> mergeCellRecords = new ArrayList<>();
|
||||
|
||||
Record prevRec = null;
|
||||
while(!RecordOrderer.isEndOfRowBlock(rs.peekNextSid())) {
|
||||
// End of row/cell records for the current sheet
|
||||
// Note - It is important that this code does not inadvertently add any sheet
|
||||
// records from a subsequent sheet. For example, if SharedFormulaRecords
|
||||
// are taken from the wrong sheet, this could cause bug 44449.
|
||||
if (!rs.hasNext()) {
|
||||
throw new RuntimeException("Failed to find end of row/cell records");
|
||||
Record prevRec = null;
|
||||
while(!RecordOrderer.isEndOfRowBlock(rs.peekNextSid())) {
|
||||
// End of row/cell records for the current sheet
|
||||
// Note - It is important that this code does not inadvertently add any sheet
|
||||
// records from a subsequent sheet. For example, if SharedFormulaRecords
|
||||
// are taken from the wrong sheet, this could cause bug 44449.
|
||||
if (!rs.hasNext()) {
|
||||
throw new RuntimeException("Failed to find end of row/cell records");
|
||||
|
||||
}
|
||||
Record rec = rs.getNext();
|
||||
List<org.apache.poi.hssf.record.Record> dest;
|
||||
switch (rec.getSid()) {
|
||||
case MergeCellsRecord.sid: dest = mergeCellRecords; break;
|
||||
case SharedFormulaRecord.sid: dest = shFrmRecords;
|
||||
if (!(prevRec instanceof FormulaRecord)) {
|
||||
throw new RuntimeException("Shared formula record should follow a FormulaRecord");
|
||||
}
|
||||
FormulaRecord fr = (FormulaRecord)prevRec;
|
||||
firstCellRefs.add(new CellReference(fr.getRow(), fr.getColumn()));
|
||||
break;
|
||||
case ArrayRecord.sid: dest = arrayRecords; break;
|
||||
case TableRecord.sid: dest = tableRecords; break;
|
||||
default: dest = plainRecords;
|
||||
}
|
||||
dest.add(rec);
|
||||
prevRec = rec;
|
||||
}
|
||||
SharedFormulaRecord[] sharedFormulaRecs = new SharedFormulaRecord[shFrmRecords.size()];
|
||||
CellReference[] firstCells = new CellReference[firstCellRefs.size()];
|
||||
ArrayRecord[] arrayRecs = new ArrayRecord[arrayRecords.size()];
|
||||
TableRecord[] tableRecs = new TableRecord[tableRecords.size()];
|
||||
shFrmRecords.toArray(sharedFormulaRecs);
|
||||
firstCellRefs.toArray(firstCells);
|
||||
arrayRecords.toArray(arrayRecs);
|
||||
tableRecords.toArray(tableRecs);
|
||||
}
|
||||
Record rec = rs.getNext();
|
||||
List<org.apache.poi.hssf.record.Record> dest;
|
||||
switch (rec.getSid()) {
|
||||
case MergeCellsRecord.sid: dest = mergeCellRecords; break;
|
||||
case SharedFormulaRecord.sid: dest = shFrmRecords;
|
||||
if (!(prevRec instanceof FormulaRecord)) {
|
||||
throw new RuntimeException("Shared formula record should follow a FormulaRecord");
|
||||
}
|
||||
FormulaRecord fr = (FormulaRecord)prevRec;
|
||||
firstCellRefs.add(new CellReference(fr.getRow(), fr.getColumn()));
|
||||
break;
|
||||
case ArrayRecord.sid: dest = arrayRecords; break;
|
||||
case TableRecord.sid: dest = tableRecords; break;
|
||||
default: dest = plainRecords;
|
||||
}
|
||||
dest.add(rec);
|
||||
prevRec = rec;
|
||||
}
|
||||
SharedFormulaRecord[] sharedFormulaRecs = new SharedFormulaRecord[shFrmRecords.size()];
|
||||
CellReference[] firstCells = new CellReference[firstCellRefs.size()];
|
||||
ArrayRecord[] arrayRecs = new ArrayRecord[arrayRecords.size()];
|
||||
TableRecord[] tableRecs = new TableRecord[tableRecords.size()];
|
||||
shFrmRecords.toArray(sharedFormulaRecs);
|
||||
firstCellRefs.toArray(firstCells);
|
||||
arrayRecords.toArray(arrayRecs);
|
||||
tableRecords.toArray(tableRecs);
|
||||
|
||||
_plainRecords = plainRecords;
|
||||
_sfm = SharedValueManager.create(sharedFormulaRecs, firstCells, arrayRecs, tableRecs);
|
||||
_mergedCellsRecords = new MergeCellsRecord[mergeCellRecords.size()];
|
||||
mergeCellRecords.toArray(_mergedCellsRecords);
|
||||
}
|
||||
_plainRecords = plainRecords;
|
||||
_sfm = SharedValueManager.create(sharedFormulaRecs, firstCells, arrayRecs, tableRecs);
|
||||
_mergedCellsRecords = new MergeCellsRecord[mergeCellRecords.size()];
|
||||
mergeCellRecords.toArray(_mergedCellsRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some unconventional apps place {@link MergeCellsRecord}s within the row block. They
|
||||
* actually should be in the {@link MergedCellsTable} which is much later (see bug 45699).
|
||||
* @return any loose {@code MergeCellsRecord}s found
|
||||
*/
|
||||
public MergeCellsRecord[] getLooseMergedCells() {
|
||||
return _mergedCellsRecords;
|
||||
}
|
||||
/**
|
||||
* Some unconventional apps place {@link MergeCellsRecord}s within the row block. They
|
||||
* actually should be in the {@link MergedCellsTable} which is much later (see bug 45699).
|
||||
* @return any loose {@code MergeCellsRecord}s found
|
||||
*/
|
||||
public MergeCellsRecord[] getLooseMergedCells() {
|
||||
return _mergedCellsRecords;
|
||||
}
|
||||
|
||||
public SharedValueManager getSharedFormulaManager() {
|
||||
return _sfm;
|
||||
}
|
||||
/**
|
||||
* @return a {@link RecordStream} containing all the non-{@link SharedFormulaRecord}
|
||||
* non-{@link ArrayRecord} and non-{@link TableRecord} Records.
|
||||
*/
|
||||
public RecordStream getPlainRecordStream() {
|
||||
return new RecordStream(_plainRecords, 0);
|
||||
}
|
||||
public SharedValueManager getSharedFormulaManager() {
|
||||
return _sfm;
|
||||
}
|
||||
/**
|
||||
* @return a {@link RecordStream} containing all the non-{@link SharedFormulaRecord}
|
||||
* non-{@link ArrayRecord} and non-{@link TableRecord} Records.
|
||||
*/
|
||||
public RecordStream getPlainRecordStream() {
|
||||
return new RecordStream(_plainRecords, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,174 +26,174 @@ public final class WorkbookRecordList {
|
||||
private List<org.apache.poi.hssf.record.Record> records = new ArrayList<>();
|
||||
|
||||
/** holds the position of the protect record */
|
||||
private int protpos;
|
||||
private int protpos;
|
||||
/** holds the position of the last bound sheet */
|
||||
private int bspos;
|
||||
private int bspos;
|
||||
/** holds the position of the tabid record */
|
||||
private int tabpos = -1;
|
||||
private int tabpos = -1;
|
||||
/** hold the position of the last font record */
|
||||
private int fontpos;
|
||||
private int fontpos;
|
||||
/** hold the position of the last extended font record */
|
||||
private int xfpos;
|
||||
private int xfpos;
|
||||
/** holds the position of the backup record */
|
||||
private int backuppos;
|
||||
private int backuppos;
|
||||
/** holds the position of last name record */
|
||||
private int namepos;
|
||||
/** holds the position of sup book */
|
||||
private int supbookpos;
|
||||
/** holds the position of the extern sheet */
|
||||
private int externsheetPos;
|
||||
/** hold the position of the palette, if applicable */
|
||||
private int palettepos = -1;
|
||||
private int namepos;
|
||||
/** holds the position of sup book */
|
||||
private int supbookpos;
|
||||
/** holds the position of the extern sheet */
|
||||
private int externsheetPos;
|
||||
/** hold the position of the palette, if applicable */
|
||||
private int palettepos = -1;
|
||||
|
||||
|
||||
public void setRecords(List<org.apache.poi.hssf.record.Record> records) {
|
||||
this.records = records;
|
||||
}
|
||||
public void setRecords(List<org.apache.poi.hssf.record.Record> records) {
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return records.size();
|
||||
}
|
||||
public int size() {
|
||||
return records.size();
|
||||
}
|
||||
|
||||
public Record get(int i) {
|
||||
return records.get(i);
|
||||
}
|
||||
public Record get(int i) {
|
||||
return records.get(i);
|
||||
}
|
||||
|
||||
public void add(int pos, Record r) {
|
||||
records.add(pos, r);
|
||||
updateRecordPos(pos, true);
|
||||
}
|
||||
public void add(int pos, Record r) {
|
||||
records.add(pos, r);
|
||||
updateRecordPos(pos, true);
|
||||
}
|
||||
|
||||
public List<org.apache.poi.hssf.record.Record> getRecords() {
|
||||
return records;
|
||||
}
|
||||
public List<org.apache.poi.hssf.record.Record> getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the given record in the record list by identity and removes it
|
||||
*
|
||||
* @param record the identical record to be searched for
|
||||
*/
|
||||
public void remove( Object record ) {
|
||||
// can't use List.indexOf here because it checks the records for equality and not identity
|
||||
int i = 0;
|
||||
for (org.apache.poi.hssf.record.Record r : records) {
|
||||
if (r == record) {
|
||||
remove(i);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Find the given record in the record list by identity and removes it
|
||||
*
|
||||
* @param record the identical record to be searched for
|
||||
*/
|
||||
public void remove( Object record ) {
|
||||
// can't use List.indexOf here because it checks the records for equality and not identity
|
||||
int i = 0;
|
||||
for (org.apache.poi.hssf.record.Record r : records) {
|
||||
if (r == record) {
|
||||
remove(i);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove( int pos ) {
|
||||
records.remove(pos);
|
||||
updateRecordPos(pos, false);
|
||||
}
|
||||
public void remove( int pos ) {
|
||||
records.remove(pos);
|
||||
updateRecordPos(pos, false);
|
||||
}
|
||||
|
||||
public int getProtpos() {
|
||||
return protpos;
|
||||
}
|
||||
public int getProtpos() {
|
||||
return protpos;
|
||||
}
|
||||
|
||||
public void setProtpos(int protpos) {
|
||||
this.protpos = protpos;
|
||||
}
|
||||
public void setProtpos(int protpos) {
|
||||
this.protpos = protpos;
|
||||
}
|
||||
|
||||
public int getBspos() {
|
||||
return bspos;
|
||||
}
|
||||
public int getBspos() {
|
||||
return bspos;
|
||||
}
|
||||
|
||||
public void setBspos(int bspos) {
|
||||
this.bspos = bspos;
|
||||
}
|
||||
public void setBspos(int bspos) {
|
||||
this.bspos = bspos;
|
||||
}
|
||||
|
||||
public int getTabpos() {
|
||||
return tabpos;
|
||||
}
|
||||
public int getTabpos() {
|
||||
return tabpos;
|
||||
}
|
||||
|
||||
public void setTabpos(int tabpos) {
|
||||
this.tabpos = tabpos;
|
||||
}
|
||||
public void setTabpos(int tabpos) {
|
||||
this.tabpos = tabpos;
|
||||
}
|
||||
|
||||
public int getFontpos() {
|
||||
return fontpos;
|
||||
}
|
||||
public int getFontpos() {
|
||||
return fontpos;
|
||||
}
|
||||
|
||||
public void setFontpos(int fontpos) {
|
||||
this.fontpos = fontpos;
|
||||
}
|
||||
public void setFontpos(int fontpos) {
|
||||
this.fontpos = fontpos;
|
||||
}
|
||||
|
||||
public int getXfpos() {
|
||||
return xfpos;
|
||||
}
|
||||
public int getXfpos() {
|
||||
return xfpos;
|
||||
}
|
||||
|
||||
public void setXfpos(int xfpos) {
|
||||
this.xfpos = xfpos;
|
||||
}
|
||||
public void setXfpos(int xfpos) {
|
||||
this.xfpos = xfpos;
|
||||
}
|
||||
|
||||
public int getBackuppos() {
|
||||
return backuppos;
|
||||
}
|
||||
public int getBackuppos() {
|
||||
return backuppos;
|
||||
}
|
||||
|
||||
public void setBackuppos(int backuppos) {
|
||||
this.backuppos = backuppos;
|
||||
}
|
||||
public void setBackuppos(int backuppos) {
|
||||
this.backuppos = backuppos;
|
||||
}
|
||||
|
||||
public int getPalettepos() {
|
||||
return palettepos;
|
||||
}
|
||||
public int getPalettepos() {
|
||||
return palettepos;
|
||||
}
|
||||
|
||||
public void setPalettepos(int palettepos) {
|
||||
this.palettepos = palettepos;
|
||||
}
|
||||
public void setPalettepos(int palettepos) {
|
||||
this.palettepos = palettepos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the namepos.
|
||||
* @return int
|
||||
*/
|
||||
public int getNamepos() {
|
||||
return namepos;
|
||||
}
|
||||
/**
|
||||
* Returns the namepos.
|
||||
* @return int
|
||||
*/
|
||||
public int getNamepos() {
|
||||
return namepos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supbookpos.
|
||||
* @return int
|
||||
*/
|
||||
public int getSupbookpos() {
|
||||
return supbookpos;
|
||||
}
|
||||
/**
|
||||
* Returns the supbookpos.
|
||||
* @return int
|
||||
*/
|
||||
public int getSupbookpos() {
|
||||
return supbookpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namepos.
|
||||
* @param namepos The namepos to set
|
||||
*/
|
||||
public void setNamepos(int namepos) {
|
||||
this.namepos = namepos;
|
||||
}
|
||||
/**
|
||||
* Sets the namepos.
|
||||
* @param namepos The namepos to set
|
||||
*/
|
||||
public void setNamepos(int namepos) {
|
||||
this.namepos = namepos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the supbookpos.
|
||||
* @param supbookpos The supbookpos to set
|
||||
*/
|
||||
public void setSupbookpos(int supbookpos) {
|
||||
this.supbookpos = supbookpos;
|
||||
}
|
||||
/**
|
||||
* Sets the supbookpos.
|
||||
* @param supbookpos The supbookpos to set
|
||||
*/
|
||||
public void setSupbookpos(int supbookpos) {
|
||||
this.supbookpos = supbookpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the externsheetPos.
|
||||
* @return int
|
||||
*/
|
||||
public int getExternsheetPos() {
|
||||
return externsheetPos;
|
||||
}
|
||||
/**
|
||||
* Returns the externsheetPos.
|
||||
* @return int
|
||||
*/
|
||||
public int getExternsheetPos() {
|
||||
return externsheetPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the externsheetPos.
|
||||
* @param externsheetPos The externsheetPos to set
|
||||
*/
|
||||
public void setExternsheetPos(int externsheetPos) {
|
||||
this.externsheetPos = externsheetPos;
|
||||
}
|
||||
/**
|
||||
* Sets the externsheetPos.
|
||||
* @param externsheetPos The externsheetPos to set
|
||||
*/
|
||||
public void setExternsheetPos(int externsheetPos) {
|
||||
this.externsheetPos = externsheetPos;
|
||||
}
|
||||
|
||||
private void updateRecordPos(int pos, boolean add) {
|
||||
int delta = (add) ? 1 : -1;
|
||||
|
||||
@ -164,12 +164,12 @@ public abstract class AbstractEscherHolderRecord extends Record {
|
||||
* @return the EscherContainerRecord or {@code null} if no child is a container record
|
||||
*/
|
||||
public EscherContainerRecord getEscherContainer() {
|
||||
for (EscherRecord er : escherRecords) {
|
||||
if(er instanceof EscherContainerRecord) {
|
||||
return (EscherContainerRecord)er;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
for (EscherRecord er : escherRecords) {
|
||||
if(er instanceof EscherContainerRecord) {
|
||||
return (EscherContainerRecord)er;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,29 +182,29 @@ public abstract class AbstractEscherHolderRecord extends Record {
|
||||
* @return the record or {@code null} if it can't be found
|
||||
*/
|
||||
public EscherRecord findFirstWithId(short id) {
|
||||
return findFirstWithId(id, getEscherRecords());
|
||||
return findFirstWithId(id, getEscherRecords());
|
||||
}
|
||||
|
||||
private EscherRecord findFirstWithId(short id, List<EscherRecord> records) {
|
||||
// Check at our level
|
||||
for (EscherRecord r : records) {
|
||||
if(r.getRecordId() == id) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
// Check at our level
|
||||
for (EscherRecord r : records) {
|
||||
if(r.getRecordId() == id) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check our children in turn
|
||||
for (EscherRecord r : records) {
|
||||
if(r.isContainerRecord()) {
|
||||
EscherRecord found = findFirstWithId(id, r.getChildRecords());
|
||||
if(found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then check our children in turn
|
||||
for (EscherRecord r : records) {
|
||||
if(r.isContainerRecord()) {
|
||||
EscherRecord found = findFirstWithId(id, r.getChildRecords());
|
||||
if(found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found in this lot
|
||||
return null;
|
||||
// Not found in this lot
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -33,78 +33,78 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
*/
|
||||
public final class ArrayRecord extends SharedValueRecordBase {
|
||||
|
||||
public static final short sid = 0x0221;
|
||||
private static final int OPT_ALWAYS_RECALCULATE = 0x0001;
|
||||
private static final int OPT_CALCULATE_ON_OPEN = 0x0002;
|
||||
public static final short sid = 0x0221;
|
||||
private static final int OPT_ALWAYS_RECALCULATE = 0x0001;
|
||||
private static final int OPT_CALCULATE_ON_OPEN = 0x0002;
|
||||
|
||||
private int _options;
|
||||
private int _field3notUsed;
|
||||
private Formula _formula;
|
||||
private int _options;
|
||||
private int _field3notUsed;
|
||||
private Formula _formula;
|
||||
|
||||
public ArrayRecord(ArrayRecord other) {
|
||||
super(other);
|
||||
_options = other._options;
|
||||
_field3notUsed = other._field3notUsed;
|
||||
_formula = (other._formula == null) ? null : other._formula.copy();
|
||||
}
|
||||
public ArrayRecord(ArrayRecord other) {
|
||||
super(other);
|
||||
_options = other._options;
|
||||
_field3notUsed = other._field3notUsed;
|
||||
_formula = (other._formula == null) ? null : other._formula.copy();
|
||||
}
|
||||
|
||||
public ArrayRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
_options = in.readUShort();
|
||||
_field3notUsed = in.readInt();
|
||||
int formulaTokenLen = in.readUShort();
|
||||
int totalFormulaLen = in.available();
|
||||
_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
|
||||
}
|
||||
public ArrayRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
_options = in.readUShort();
|
||||
_field3notUsed = in.readInt();
|
||||
int formulaTokenLen = in.readUShort();
|
||||
int totalFormulaLen = in.available();
|
||||
_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
|
||||
}
|
||||
|
||||
public ArrayRecord(Formula formula, CellRangeAddress8Bit range) {
|
||||
super(range);
|
||||
_options = 0; //YK: Excel 2007 leaves this field unset
|
||||
_field3notUsed = 0;
|
||||
_formula = formula;
|
||||
}
|
||||
public ArrayRecord(Formula formula, CellRangeAddress8Bit range) {
|
||||
super(range);
|
||||
_options = 0; //YK: Excel 2007 leaves this field unset
|
||||
_field3notUsed = 0;
|
||||
_formula = formula;
|
||||
}
|
||||
|
||||
public boolean isAlwaysRecalculate() {
|
||||
return (_options & OPT_ALWAYS_RECALCULATE) != 0;
|
||||
}
|
||||
public boolean isCalculateOnOpen() {
|
||||
return (_options & OPT_CALCULATE_ON_OPEN) != 0;
|
||||
}
|
||||
public boolean isAlwaysRecalculate() {
|
||||
return (_options & OPT_ALWAYS_RECALCULATE) != 0;
|
||||
}
|
||||
public boolean isCalculateOnOpen() {
|
||||
return (_options & OPT_CALCULATE_ON_OPEN) != 0;
|
||||
}
|
||||
|
||||
public Ptg[] getFormulaTokens() {
|
||||
return _formula.getTokens();
|
||||
}
|
||||
public Ptg[] getFormulaTokens() {
|
||||
return _formula.getTokens();
|
||||
}
|
||||
|
||||
protected int getExtraDataSize() {
|
||||
return 2 + 4 + _formula.getEncodedSize();
|
||||
}
|
||||
protected void serializeExtraData(LittleEndianOutput out) {
|
||||
out.writeShort(_options);
|
||||
out.writeInt(_field3notUsed);
|
||||
_formula.serialize(out);
|
||||
}
|
||||
protected int getExtraDataSize() {
|
||||
return 2 + 4 + _formula.getEncodedSize();
|
||||
}
|
||||
protected void serializeExtraData(LittleEndianOutput out) {
|
||||
out.writeShort(_options);
|
||||
out.writeInt(_field3notUsed);
|
||||
_formula.serialize(out);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public ArrayRecord copy() {
|
||||
return new ArrayRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.ARRAY;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"range", this::getRange,
|
||||
"options", () -> _options,
|
||||
"notUsed", () -> _field3notUsed,
|
||||
"formula", () -> _formula
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"range", this::getRange,
|
||||
"options", () -> _options,
|
||||
"notUsed", () -> _field3notUsed,
|
||||
"formula", () -> _formula
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,23 +18,23 @@ package org.apache.poi.hssf.record;
|
||||
|
||||
public interface BiffHeaderInput {
|
||||
|
||||
/**
|
||||
* Read an unsigned short from the stream without decrypting
|
||||
*
|
||||
* @return the record sid
|
||||
*/
|
||||
int readRecordSID();
|
||||
|
||||
/**
|
||||
* Read an unsigned short from the stream without decrypting
|
||||
*
|
||||
* @return the data size
|
||||
*/
|
||||
int readDataSize();
|
||||
/**
|
||||
* Read an unsigned short from the stream without decrypting
|
||||
*
|
||||
* @return the record sid
|
||||
*/
|
||||
int readRecordSID();
|
||||
|
||||
/**
|
||||
* Read an unsigned short from the stream without decrypting
|
||||
*
|
||||
* @return the data size
|
||||
*/
|
||||
int readDataSize();
|
||||
|
||||
/**
|
||||
* @return the available bytes
|
||||
*/
|
||||
int available();
|
||||
/**
|
||||
* @return the available bytes
|
||||
*/
|
||||
int available();
|
||||
|
||||
}
|
||||
|
||||
@ -29,173 +29,173 @@ import org.apache.poi.util.RecordFormatException;
|
||||
* Creates new BoolErrRecord. (0x0205)
|
||||
*/
|
||||
public final class BoolErrRecord extends CellRecord {
|
||||
public static final short sid = 0x0205;
|
||||
private int _value;
|
||||
/**
|
||||
* If <code>true</code>, this record represents an error cell value,
|
||||
* otherwise this record represents a boolean cell value
|
||||
*/
|
||||
private boolean _isError;
|
||||
public static final short sid = 0x0205;
|
||||
private int _value;
|
||||
/**
|
||||
* If <code>true</code>, this record represents an error cell value,
|
||||
* otherwise this record represents a boolean cell value
|
||||
*/
|
||||
private boolean _isError;
|
||||
|
||||
/** Creates new BoolErrRecord */
|
||||
public BoolErrRecord() {}
|
||||
/** Creates new BoolErrRecord */
|
||||
public BoolErrRecord() {}
|
||||
|
||||
public BoolErrRecord(BoolErrRecord other) {
|
||||
super(other);
|
||||
_value = other._value;
|
||||
_isError = other._isError;
|
||||
}
|
||||
public BoolErrRecord(BoolErrRecord other) {
|
||||
super(other);
|
||||
_value = other._value;
|
||||
_isError = other._isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public BoolErrRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
switch (in.remaining()) {
|
||||
case 2:
|
||||
_value = in.readByte();
|
||||
break;
|
||||
case 3:
|
||||
_value = in.readUShort();
|
||||
break;
|
||||
default:
|
||||
throw new RecordFormatException("Unexpected size ("
|
||||
+ in.remaining() + ") for BOOLERR record.");
|
||||
}
|
||||
int flag = in.readUByte();
|
||||
switch (flag) {
|
||||
case 0:
|
||||
_isError = false;
|
||||
break;
|
||||
case 1:
|
||||
_isError = true;
|
||||
break;
|
||||
default:
|
||||
throw new RecordFormatException("Unexpected isError flag ("
|
||||
+ flag + ") for BOOLERR record.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public BoolErrRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
switch (in.remaining()) {
|
||||
case 2:
|
||||
_value = in.readByte();
|
||||
break;
|
||||
case 3:
|
||||
_value = in.readUShort();
|
||||
break;
|
||||
default:
|
||||
throw new RecordFormatException("Unexpected size ("
|
||||
+ in.remaining() + ") for BOOLERR record.");
|
||||
}
|
||||
int flag = in.readUByte();
|
||||
switch (flag) {
|
||||
case 0:
|
||||
_isError = false;
|
||||
break;
|
||||
case 1:
|
||||
_isError = true;
|
||||
break;
|
||||
default:
|
||||
throw new RecordFormatException("Unexpected isError flag ("
|
||||
+ flag + ") for BOOLERR record.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the boolean value for the cell
|
||||
*
|
||||
* @param value representing the boolean value
|
||||
*/
|
||||
public void setValue(boolean value) {
|
||||
_value = value ? 1 : 0;
|
||||
_isError = false;
|
||||
}
|
||||
/**
|
||||
* set the boolean value for the cell
|
||||
*
|
||||
* @param value representing the boolean value
|
||||
*/
|
||||
public void setValue(boolean value) {
|
||||
_value = value ? 1 : 0;
|
||||
_isError = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the error value for the cell. See {@link FormulaError} for valid codes.
|
||||
*
|
||||
* @param value error representing the error value
|
||||
* this value can only be 0,7,15,23,29,36 or 42
|
||||
* see bugzilla bug 16560 for an explanation
|
||||
*/
|
||||
public void setValue(byte value) {
|
||||
setValue(FormulaError.forInt(value));
|
||||
}
|
||||
/**
|
||||
* set the error value for the cell. See {@link FormulaError} for valid codes.
|
||||
*
|
||||
* @param value error representing the error value
|
||||
* this value can only be 0,7,15,23,29,36 or 42
|
||||
* see bugzilla bug 16560 for an explanation
|
||||
*/
|
||||
public void setValue(byte value) {
|
||||
setValue(FormulaError.forInt(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* set the error value for the cell
|
||||
*
|
||||
* @param value error representing the error value
|
||||
* this value can only be 0,7,15,23,29,36 or 42
|
||||
* see bugzilla bug 16560 for an explanation
|
||||
*/
|
||||
public void setValue(FormulaError value) {
|
||||
switch(value) {
|
||||
case NULL:
|
||||
case DIV0:
|
||||
case VALUE:
|
||||
case REF:
|
||||
case NAME:
|
||||
case NUM:
|
||||
case NA:
|
||||
_value = value.getCode();
|
||||
_isError = true;
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("Error Value can only be 0,7,15,23,29,36 or 42. It cannot be "+value.getCode()+" ("+value+")");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* set the error value for the cell
|
||||
*
|
||||
* @param value error representing the error value
|
||||
* this value can only be 0,7,15,23,29,36 or 42
|
||||
* see bugzilla bug 16560 for an explanation
|
||||
*/
|
||||
public void setValue(FormulaError value) {
|
||||
switch(value) {
|
||||
case NULL:
|
||||
case DIV0:
|
||||
case VALUE:
|
||||
case REF:
|
||||
case NAME:
|
||||
case NUM:
|
||||
case NA:
|
||||
_value = value.getCode();
|
||||
_isError = true;
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("Error Value can only be 0,7,15,23,29,36 or 42. It cannot be "+value.getCode()+" ("+value+")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the value for the cell
|
||||
*
|
||||
* @return boolean representing the boolean value
|
||||
*/
|
||||
public boolean getBooleanValue() {
|
||||
return _value != 0;
|
||||
}
|
||||
/**
|
||||
* get the value for the cell
|
||||
*
|
||||
* @return boolean representing the boolean value
|
||||
*/
|
||||
public boolean getBooleanValue() {
|
||||
return _value != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the error value for the cell
|
||||
*
|
||||
* @return byte representing the error value
|
||||
*/
|
||||
public byte getErrorValue() {
|
||||
return (byte)_value;
|
||||
}
|
||||
/**
|
||||
* get the error value for the cell
|
||||
*
|
||||
* @return byte representing the error value
|
||||
*/
|
||||
public byte getErrorValue() {
|
||||
return (byte)_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the call holds a boolean value
|
||||
*
|
||||
* @return boolean true if the cell holds a boolean value
|
||||
*/
|
||||
public boolean isBoolean() {
|
||||
return !_isError;
|
||||
}
|
||||
/**
|
||||
* Indicates whether the call holds a boolean value
|
||||
*
|
||||
* @return boolean true if the cell holds a boolean value
|
||||
*/
|
||||
public boolean isBoolean() {
|
||||
return !_isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the call holds an error value
|
||||
*
|
||||
* @return boolean true if the cell holds an error value
|
||||
*/
|
||||
public boolean isError() {
|
||||
return _isError;
|
||||
}
|
||||
/**
|
||||
* Indicates whether the call holds an error value
|
||||
*
|
||||
* @return boolean true if the cell holds an error value
|
||||
*/
|
||||
public boolean isError() {
|
||||
return _isError;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "BOOLERR";
|
||||
}
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "BOOLERR";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void serializeValue(LittleEndianOutput out) {
|
||||
out.writeByte(_value);
|
||||
out.writeByte(_isError ? 1 : 0);
|
||||
}
|
||||
@Override
|
||||
protected void serializeValue(LittleEndianOutput out) {
|
||||
out.writeByte(_value);
|
||||
out.writeByte(_isError ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getValueDataSize() {
|
||||
return 2;
|
||||
}
|
||||
@Override
|
||||
protected int getValueDataSize() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolErrRecord copy() {
|
||||
return new BoolErrRecord(this);
|
||||
}
|
||||
@Override
|
||||
public BoolErrRecord copy() {
|
||||
return new BoolErrRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.BOOL_ERR;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.BOOL_ERR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"base", super::getGenericProperties,
|
||||
"isBoolean", this::isBoolean,
|
||||
"booleanVal", this::getBooleanValue,
|
||||
"isError", this::isError,
|
||||
"errorVal", this::getErrorValue,
|
||||
"errorTxt", () -> isError() ? FormulaError.forInt(getErrorValue()).getString() : null
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"base", super::getGenericProperties,
|
||||
"isBoolean", this::isBoolean,
|
||||
"booleanVal", this::getBooleanValue,
|
||||
"isError", this::isError,
|
||||
"errorVal", this::getErrorValue,
|
||||
"errorTxt", () -> isError() ? FormulaError.forInt(getErrorValue()).getString() : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,194 +36,194 @@ import org.apache.poi.util.StringUtil;
|
||||
* tells where the Beginning of file record is within the HSSF file.
|
||||
*/
|
||||
public final class BoundSheetRecord extends StandardRecord {
|
||||
public static final short sid = 0x0085;
|
||||
private static final BitField hiddenFlag = BitFieldFactory.getInstance(0x01);
|
||||
private static final BitField veryHiddenFlag = BitFieldFactory.getInstance(0x02);
|
||||
public static final short sid = 0x0085;
|
||||
private static final BitField hiddenFlag = BitFieldFactory.getInstance(0x01);
|
||||
private static final BitField veryHiddenFlag = BitFieldFactory.getInstance(0x02);
|
||||
|
||||
private int field_1_position_of_BOF;
|
||||
private int field_2_option_flags;
|
||||
private int field_4_isMultibyteUnicode;
|
||||
private String field_5_sheetname;
|
||||
private int field_1_position_of_BOF;
|
||||
private int field_2_option_flags;
|
||||
private int field_4_isMultibyteUnicode;
|
||||
private String field_5_sheetname;
|
||||
|
||||
public BoundSheetRecord(String sheetname) {
|
||||
field_2_option_flags = 0;
|
||||
setSheetname(sheetname);
|
||||
}
|
||||
public BoundSheetRecord(String sheetname) {
|
||||
field_2_option_flags = 0;
|
||||
setSheetname(sheetname);
|
||||
}
|
||||
|
||||
public BoundSheetRecord(BoundSheetRecord other) {
|
||||
super(other);
|
||||
field_1_position_of_BOF = other.field_1_position_of_BOF;
|
||||
field_2_option_flags = other.field_2_option_flags;
|
||||
field_4_isMultibyteUnicode = other.field_4_isMultibyteUnicode;
|
||||
field_5_sheetname = other.field_5_sheetname;
|
||||
}
|
||||
public BoundSheetRecord(BoundSheetRecord other) {
|
||||
super(other);
|
||||
field_1_position_of_BOF = other.field_1_position_of_BOF;
|
||||
field_2_option_flags = other.field_2_option_flags;
|
||||
field_4_isMultibyteUnicode = other.field_4_isMultibyteUnicode;
|
||||
field_5_sheetname = other.field_5_sheetname;
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF8: sid + len + bof + flags + len(str) + unicode + str 2 + 2 + 4 + 2 +
|
||||
* 1 + 1 + len(str)
|
||||
*
|
||||
* UNICODE: sid + len + bof + flags + len(str) + unicode + str 2 + 2 + 4 + 2 +
|
||||
* 1 + 1 + 2 * len(str)
|
||||
*
|
||||
* @param in the record stream to read from
|
||||
*/
|
||||
public BoundSheetRecord(RecordInputStream in) {
|
||||
/**
|
||||
* UTF8: sid + len + bof + flags + len(str) + unicode + str 2 + 2 + 4 + 2 +
|
||||
* 1 + 1 + len(str)
|
||||
*
|
||||
* UNICODE: sid + len + bof + flags + len(str) + unicode + str 2 + 2 + 4 + 2 +
|
||||
* 1 + 1 + 2 * len(str)
|
||||
*
|
||||
* @param in the record stream to read from
|
||||
*/
|
||||
public BoundSheetRecord(RecordInputStream in) {
|
||||
byte[] buf = new byte[LittleEndianConsts.INT_SIZE];
|
||||
in.readPlain(buf, 0, buf.length);
|
||||
field_1_position_of_BOF = LittleEndian.getInt(buf);
|
||||
field_2_option_flags = in.readUShort();
|
||||
int field_3_sheetname_length = in.readUByte();
|
||||
field_4_isMultibyteUnicode = in.readByte();
|
||||
in.readPlain(buf, 0, buf.length);
|
||||
field_1_position_of_BOF = LittleEndian.getInt(buf);
|
||||
field_2_option_flags = in.readUShort();
|
||||
int field_3_sheetname_length = in.readUByte();
|
||||
field_4_isMultibyteUnicode = in.readByte();
|
||||
|
||||
if (isMultibyte()) {
|
||||
field_5_sheetname = in.readUnicodeLEString(field_3_sheetname_length);
|
||||
} else {
|
||||
field_5_sheetname = in.readCompressedUnicode(field_3_sheetname_length);
|
||||
}
|
||||
}
|
||||
if (isMultibyte()) {
|
||||
field_5_sheetname = in.readUnicodeLEString(field_3_sheetname_length);
|
||||
} else {
|
||||
field_5_sheetname = in.readCompressedUnicode(field_3_sheetname_length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the offset in bytes of the Beginning of File Marker within the HSSF
|
||||
* Stream part of the POIFS file
|
||||
*
|
||||
* @param pos offset in bytes
|
||||
*/
|
||||
public void setPositionOfBof(int pos) {
|
||||
field_1_position_of_BOF = pos;
|
||||
}
|
||||
/**
|
||||
* set the offset in bytes of the Beginning of File Marker within the HSSF
|
||||
* Stream part of the POIFS file
|
||||
*
|
||||
* @param pos offset in bytes
|
||||
*/
|
||||
public void setPositionOfBof(int pos) {
|
||||
field_1_position_of_BOF = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sheetname for this sheet. (this appears in the tabs at the bottom)
|
||||
* @param sheetName the name of the sheet
|
||||
* @see org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)
|
||||
* for a safe way to create valid names
|
||||
* @throws IllegalArgumentException if sheet name will cause excel to crash.
|
||||
*/
|
||||
public void setSheetname(String sheetName) {
|
||||
/**
|
||||
* Set the sheetname for this sheet. (this appears in the tabs at the bottom)
|
||||
* @param sheetName the name of the sheet
|
||||
* @see org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)
|
||||
* for a safe way to create valid names
|
||||
* @throws IllegalArgumentException if sheet name will cause excel to crash.
|
||||
*/
|
||||
public void setSheetname(String sheetName) {
|
||||
|
||||
WorkbookUtil.validateSheetName(sheetName);
|
||||
field_5_sheetname = sheetName;
|
||||
field_4_isMultibyteUnicode = StringUtil.hasMultibyte(sheetName) ? 1 : 0;
|
||||
}
|
||||
WorkbookUtil.validateSheetName(sheetName);
|
||||
field_5_sheetname = sheetName;
|
||||
field_4_isMultibyteUnicode = StringUtil.hasMultibyte(sheetName) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
|
||||
*
|
||||
* @return offset in bytes
|
||||
*/
|
||||
public int getPositionOfBof() {
|
||||
return field_1_position_of_BOF;
|
||||
}
|
||||
/**
|
||||
* get the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
|
||||
*
|
||||
* @return offset in bytes
|
||||
*/
|
||||
public int getPositionOfBof() {
|
||||
return field_1_position_of_BOF;
|
||||
}
|
||||
|
||||
private boolean isMultibyte() {
|
||||
return (field_4_isMultibyteUnicode & 0x01) != 0;
|
||||
}
|
||||
private boolean isMultibyte() {
|
||||
return (field_4_isMultibyteUnicode & 0x01) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the sheetname for this sheet. (this appears in the tabs at the bottom)
|
||||
* @return sheetname the name of the sheet
|
||||
*/
|
||||
public String getSheetname() {
|
||||
return field_5_sheetname;
|
||||
}
|
||||
/**
|
||||
* get the sheetname for this sheet. (this appears in the tabs at the bottom)
|
||||
* @return sheetname the name of the sheet
|
||||
*/
|
||||
public String getSheetname() {
|
||||
return field_5_sheetname;
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 8 + field_5_sheetname.length() * (isMultibyte() ? 2 : 1);
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 8 + field_5_sheetname.length() * (isMultibyte() ? 2 : 1);
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeInt(getPositionOfBof());
|
||||
out.writeShort(field_2_option_flags);
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeInt(getPositionOfBof());
|
||||
out.writeShort(field_2_option_flags);
|
||||
|
||||
String name = field_5_sheetname;
|
||||
out.writeByte(name.length());
|
||||
out.writeByte(field_4_isMultibyteUnicode);
|
||||
String name = field_5_sheetname;
|
||||
out.writeByte(name.length());
|
||||
out.writeByte(field_4_isMultibyteUnicode);
|
||||
|
||||
if (isMultibyte()) {
|
||||
StringUtil.putUnicodeLE(name, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(name, out);
|
||||
}
|
||||
}
|
||||
if (isMultibyte()) {
|
||||
StringUtil.putUnicodeLE(name, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(name, out);
|
||||
}
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet hidden? Different from very hidden
|
||||
*
|
||||
* @return {@code true} if hidden
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return hiddenFlag.isSet(field_2_option_flags);
|
||||
}
|
||||
/**
|
||||
* Is the sheet hidden? Different from very hidden
|
||||
*
|
||||
* @return {@code true} if hidden
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return hiddenFlag.isSet(field_2_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet hidden? Different from very hidden
|
||||
*
|
||||
* @param hidden {@code true} if hidden
|
||||
*/
|
||||
public void setHidden(boolean hidden) {
|
||||
field_2_option_flags = hiddenFlag.setBoolean(field_2_option_flags, hidden);
|
||||
}
|
||||
/**
|
||||
* Is the sheet hidden? Different from very hidden
|
||||
*
|
||||
* @param hidden {@code true} if hidden
|
||||
*/
|
||||
public void setHidden(boolean hidden) {
|
||||
field_2_option_flags = hiddenFlag.setBoolean(field_2_option_flags, hidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet very hidden? Different from (normal) hidden
|
||||
*
|
||||
* @return {@code true} if very hidden
|
||||
*/
|
||||
public boolean isVeryHidden() {
|
||||
return veryHiddenFlag.isSet(field_2_option_flags);
|
||||
}
|
||||
/**
|
||||
* Is the sheet very hidden? Different from (normal) hidden
|
||||
*
|
||||
* @return {@code true} if very hidden
|
||||
*/
|
||||
public boolean isVeryHidden() {
|
||||
return veryHiddenFlag.isSet(field_2_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet very hidden? Different from (normal) hidden
|
||||
*
|
||||
* @param veryHidden {@code true} if very hidden
|
||||
*/
|
||||
public void setVeryHidden(boolean veryHidden) {
|
||||
field_2_option_flags = veryHiddenFlag.setBoolean(field_2_option_flags, veryHidden);
|
||||
}
|
||||
/**
|
||||
* Is the sheet very hidden? Different from (normal) hidden
|
||||
*
|
||||
* @param veryHidden {@code true} if very hidden
|
||||
*/
|
||||
public void setVeryHidden(boolean veryHidden) {
|
||||
field_2_option_flags = veryHiddenFlag.setBoolean(field_2_option_flags, veryHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a List of {@link BoundSheetRecord}s to an array and sorts by the position of their
|
||||
* BOFs.
|
||||
*
|
||||
* @param boundSheetRecords the boundSheetRecord list to arrayify
|
||||
*
|
||||
* @return the sorted boundSheetRecords
|
||||
*/
|
||||
public static BoundSheetRecord[] orderByBofPosition(List<BoundSheetRecord> boundSheetRecords) {
|
||||
BoundSheetRecord[] bsrs = new BoundSheetRecord[boundSheetRecords.size()];
|
||||
boundSheetRecords.toArray(bsrs);
|
||||
Arrays.sort(bsrs, BoundSheetRecord::compareRecords);
|
||||
return bsrs;
|
||||
}
|
||||
/**
|
||||
* Converts a List of {@link BoundSheetRecord}s to an array and sorts by the position of their
|
||||
* BOFs.
|
||||
*
|
||||
* @param boundSheetRecords the boundSheetRecord list to arrayify
|
||||
*
|
||||
* @return the sorted boundSheetRecords
|
||||
*/
|
||||
public static BoundSheetRecord[] orderByBofPosition(List<BoundSheetRecord> boundSheetRecords) {
|
||||
BoundSheetRecord[] bsrs = new BoundSheetRecord[boundSheetRecords.size()];
|
||||
boundSheetRecords.toArray(bsrs);
|
||||
Arrays.sort(bsrs, BoundSheetRecord::compareRecords);
|
||||
return bsrs;
|
||||
}
|
||||
|
||||
private static int compareRecords(BoundSheetRecord bsr1, BoundSheetRecord bsr2) {
|
||||
return bsr1.getPositionOfBof() - bsr2.getPositionOfBof();
|
||||
}
|
||||
private static int compareRecords(BoundSheetRecord bsr1, BoundSheetRecord bsr2) {
|
||||
return bsr1.getPositionOfBof() - bsr2.getPositionOfBof();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundSheetRecord copy() {
|
||||
return new BoundSheetRecord(this);
|
||||
}
|
||||
@Override
|
||||
public BoundSheetRecord copy() {
|
||||
return new BoundSheetRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.BOUND_SHEET;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.BOUND_SHEET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"bof", this::getPositionOfBof,
|
||||
"optionFlags", () -> field_2_option_flags,
|
||||
"multiByte", this::isMultibyte,
|
||||
"sheetName", this::getSheetname,
|
||||
"hidden", this::isHidden,
|
||||
"veryHidden", this::isVeryHidden
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"bof", this::getPositionOfBof,
|
||||
"optionFlags", () -> field_2_option_flags,
|
||||
"multiByte", this::isMultibyte,
|
||||
"sheetName", this::getSheetname,
|
||||
"hidden", this::isHidden,
|
||||
"veryHidden", this::isVeryHidden
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,7 +352,7 @@ public final class CFRule12Record extends CFRuleBase implements FutureRecord {
|
||||
*
|
||||
* @return list of tokens (casts stack to a list and returns it!)
|
||||
* this method can return null is we are unable to create Ptgs from
|
||||
* existing excel file
|
||||
* existing excel file
|
||||
* callers should check for null!
|
||||
*/
|
||||
public Ptg[] getParsedExpressionScale() {
|
||||
|
||||
@ -401,7 +401,7 @@ public abstract class CFRuleBase extends StandardRecord {
|
||||
*
|
||||
* @return list of tokens (casts stack to a list and returns it!)
|
||||
* this method can return null is we are unable to create Ptgs from
|
||||
* existing excel file
|
||||
* existing excel file
|
||||
* callers should check for null!
|
||||
*/
|
||||
public Ptg[] getParsedExpression1() {
|
||||
|
||||
@ -26,63 +26,63 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* XCT - CRN Count
|
||||
*/
|
||||
public final class CRNCountRecord extends StandardRecord {
|
||||
public static final short sid = 0x59;
|
||||
public static final short sid = 0x59;
|
||||
|
||||
private static final short DATA_SIZE = 4;
|
||||
private static final short DATA_SIZE = 4;
|
||||
|
||||
private int field_1_number_crn_records;
|
||||
private int field_2_sheet_table_index;
|
||||
private int field_1_number_crn_records;
|
||||
private int field_2_sheet_table_index;
|
||||
|
||||
public CRNCountRecord(CRNCountRecord other) {
|
||||
super(other);
|
||||
field_1_number_crn_records = other.field_1_number_crn_records;
|
||||
field_2_sheet_table_index = other.field_2_sheet_table_index;
|
||||
}
|
||||
public CRNCountRecord(CRNCountRecord other) {
|
||||
super(other);
|
||||
field_1_number_crn_records = other.field_1_number_crn_records;
|
||||
field_2_sheet_table_index = other.field_2_sheet_table_index;
|
||||
}
|
||||
|
||||
public CRNCountRecord(RecordInputStream in) {
|
||||
field_1_number_crn_records = in.readShort();
|
||||
if(field_1_number_crn_records < 0) {
|
||||
// TODO - seems like the sign bit of this field might be used for some other purpose
|
||||
// see example file for test case "TestBugs.test19599()"
|
||||
field_1_number_crn_records = (short)-field_1_number_crn_records;
|
||||
}
|
||||
field_2_sheet_table_index = in.readShort();
|
||||
}
|
||||
public CRNCountRecord(RecordInputStream in) {
|
||||
field_1_number_crn_records = in.readShort();
|
||||
if(field_1_number_crn_records < 0) {
|
||||
// TODO - seems like the sign bit of this field might be used for some other purpose
|
||||
// see example file for test case "TestBugs.test19599()"
|
||||
field_1_number_crn_records = (short)-field_1_number_crn_records;
|
||||
}
|
||||
field_2_sheet_table_index = in.readShort();
|
||||
}
|
||||
|
||||
public int getNumberOfCRNs() {
|
||||
return field_1_number_crn_records;
|
||||
}
|
||||
public int getNumberOfCRNs() {
|
||||
return field_1_number_crn_records;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort((short)field_1_number_crn_records);
|
||||
out.writeShort((short)field_2_sheet_table_index);
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return DATA_SIZE;
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort((short)field_1_number_crn_records);
|
||||
out.writeShort((short)field_2_sheet_table_index);
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return DATA_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CRNCountRecord copy() {
|
||||
return new CRNCountRecord(this);
|
||||
}
|
||||
@Override
|
||||
public CRNCountRecord copy() {
|
||||
return new CRNCountRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.CRN_COUNT;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.CRN_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"numberOfCRNs", this::getNumberOfCRNs,
|
||||
"sheetTableIndex", () -> field_2_sheet_table_index
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"numberOfCRNs", this::getNumberOfCRNs,
|
||||
"sheetTableIndex", () -> field_2_sheet_table_index
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,70 +28,70 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* This record stores the contents of an external cell or cell range
|
||||
*/
|
||||
public final class CRNRecord extends StandardRecord {
|
||||
public static final short sid = 0x005A;
|
||||
public static final short sid = 0x005A;
|
||||
|
||||
private int field_1_last_column_index;
|
||||
private int field_2_first_column_index;
|
||||
private int field_3_row_index;
|
||||
private Object[] field_4_constant_values;
|
||||
private int field_1_last_column_index;
|
||||
private int field_2_first_column_index;
|
||||
private int field_3_row_index;
|
||||
private Object[] field_4_constant_values;
|
||||
|
||||
public CRNRecord(CRNRecord other) {
|
||||
super(other);
|
||||
field_1_last_column_index = other.field_1_last_column_index;
|
||||
field_2_first_column_index = other.field_2_first_column_index;
|
||||
field_3_row_index = other.field_3_row_index;
|
||||
// field_4_constant_values are instances of Double, Boolean, String, ErrorCode,
|
||||
// i.e. they are immutable and can their references can be simply cloned
|
||||
field_4_constant_values = (other.field_4_constant_values == null) ? null : other.field_4_constant_values.clone();
|
||||
}
|
||||
public CRNRecord(CRNRecord other) {
|
||||
super(other);
|
||||
field_1_last_column_index = other.field_1_last_column_index;
|
||||
field_2_first_column_index = other.field_2_first_column_index;
|
||||
field_3_row_index = other.field_3_row_index;
|
||||
// field_4_constant_values are instances of Double, Boolean, String, ErrorCode,
|
||||
// i.e. they are immutable and can their references can be simply cloned
|
||||
field_4_constant_values = (other.field_4_constant_values == null) ? null : other.field_4_constant_values.clone();
|
||||
}
|
||||
|
||||
public CRNRecord(RecordInputStream in) {
|
||||
field_1_last_column_index = in.readUByte();
|
||||
field_2_first_column_index = in.readUByte();
|
||||
field_3_row_index = in.readShort();
|
||||
int nValues = field_1_last_column_index - field_2_first_column_index + 1;
|
||||
field_4_constant_values = ConstantValueParser.parse(in, nValues);
|
||||
}
|
||||
public CRNRecord(RecordInputStream in) {
|
||||
field_1_last_column_index = in.readUByte();
|
||||
field_2_first_column_index = in.readUByte();
|
||||
field_3_row_index = in.readShort();
|
||||
int nValues = field_1_last_column_index - field_2_first_column_index + 1;
|
||||
field_4_constant_values = ConstantValueParser.parse(in, nValues);
|
||||
}
|
||||
|
||||
public int getNumberOfCRNs() {
|
||||
return field_1_last_column_index;
|
||||
}
|
||||
public int getNumberOfCRNs() {
|
||||
return field_1_last_column_index;
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 4 + ConstantValueParser.getEncodedSize(field_4_constant_values);
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 4 + ConstantValueParser.getEncodedSize(field_4_constant_values);
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeByte(field_1_last_column_index);
|
||||
out.writeByte(field_2_first_column_index);
|
||||
out.writeShort(field_3_row_index);
|
||||
ConstantValueParser.encode(out, field_4_constant_values);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeByte(field_1_last_column_index);
|
||||
out.writeByte(field_2_first_column_index);
|
||||
out.writeShort(field_3_row_index);
|
||||
ConstantValueParser.encode(out, field_4_constant_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CRNRecord copy() {
|
||||
return new CRNRecord(this);
|
||||
}
|
||||
@Override
|
||||
public CRNRecord copy() {
|
||||
return new CRNRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.CRN;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.CRN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", () -> field_3_row_index,
|
||||
"firstColumn", () -> field_2_first_column_index,
|
||||
"lastColumn", () -> field_1_last_column_index,
|
||||
"constantValues", () -> field_4_constant_values
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", () -> field_3_row_index,
|
||||
"firstColumn", () -> field_2_first_column_index,
|
||||
"lastColumn", () -> field_1_last_column_index,
|
||||
"constantValues", () -> field_4_constant_values
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,14 +122,14 @@ public final class CommonObjectDataSubRecord extends SubRecord {
|
||||
out.writeInt(field_6_reserved3);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 2 + 2 + 2 + 4 + 4 + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the record sid
|
||||
*/
|
||||
/**
|
||||
* @return the record sid
|
||||
*/
|
||||
public short getSid()
|
||||
{
|
||||
return sid;
|
||||
|
||||
@ -27,67 +27,67 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* This record is the list header of all data validation records (0x01BE) in the current sheet.
|
||||
*/
|
||||
public final class DVALRecord extends StandardRecord {
|
||||
public static final short sid = 0x01B2;
|
||||
public static final short sid = 0x01B2;
|
||||
|
||||
/** Options of the DVAL */
|
||||
private short field_1_options;
|
||||
/** Horizontal position of the dialog */
|
||||
private int field_2_horiz_pos;
|
||||
/** Vertical position of the dialog */
|
||||
private int field_3_vert_pos;
|
||||
/** Options of the DVAL */
|
||||
private short field_1_options;
|
||||
/** Horizontal position of the dialog */
|
||||
private int field_2_horiz_pos;
|
||||
/** Vertical position of the dialog */
|
||||
private int field_3_vert_pos;
|
||||
|
||||
/** Object ID of the drop down arrow object for list boxes ;
|
||||
* in our case this will be always FFFF , until
|
||||
* MSODrawingGroup and MSODrawing records are implemented */
|
||||
private int field_cbo_id;
|
||||
/** Object ID of the drop down arrow object for list boxes ;
|
||||
* in our case this will be always FFFF , until
|
||||
* MSODrawingGroup and MSODrawing records are implemented */
|
||||
private int field_cbo_id;
|
||||
|
||||
/** Number of following DV Records */
|
||||
private int field_5_dv_no;
|
||||
/** Number of following DV Records */
|
||||
private int field_5_dv_no;
|
||||
|
||||
public DVALRecord() {
|
||||
field_cbo_id = 0xFFFFFFFF;
|
||||
field_5_dv_no = 0x00000000;
|
||||
}
|
||||
|
||||
public DVALRecord(DVALRecord other) {
|
||||
super(other);
|
||||
field_1_options = other.field_1_options;
|
||||
field_2_horiz_pos = other.field_2_horiz_pos;
|
||||
field_3_vert_pos = other.field_3_vert_pos;
|
||||
field_cbo_id = other.field_cbo_id;
|
||||
field_5_dv_no = other.field_5_dv_no;
|
||||
}
|
||||
public DVALRecord(DVALRecord other) {
|
||||
super(other);
|
||||
field_1_options = other.field_1_options;
|
||||
field_2_horiz_pos = other.field_2_horiz_pos;
|
||||
field_3_vert_pos = other.field_3_vert_pos;
|
||||
field_cbo_id = other.field_cbo_id;
|
||||
field_5_dv_no = other.field_5_dv_no;
|
||||
}
|
||||
|
||||
public DVALRecord(RecordInputStream in) {
|
||||
field_1_options = in.readShort();
|
||||
field_2_horiz_pos = in.readInt();
|
||||
field_3_vert_pos = in.readInt();
|
||||
public DVALRecord(RecordInputStream in) {
|
||||
field_1_options = in.readShort();
|
||||
field_2_horiz_pos = in.readInt();
|
||||
field_3_vert_pos = in.readInt();
|
||||
field_cbo_id = in.readInt();
|
||||
field_5_dv_no = in.readInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options the options of the dialog
|
||||
*/
|
||||
public void setOptions(short options) {
|
||||
field_1_options = options;
|
||||
}
|
||||
* @param options the options of the dialog
|
||||
*/
|
||||
public void setOptions(short options) {
|
||||
field_1_options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param horiz_pos the Horizontal position of the dialog
|
||||
*/
|
||||
public void setHorizontalPos(int horiz_pos) {
|
||||
field_2_horiz_pos = horiz_pos;
|
||||
}
|
||||
/**
|
||||
* @param horiz_pos the Horizontal position of the dialog
|
||||
*/
|
||||
public void setHorizontalPos(int horiz_pos) {
|
||||
field_2_horiz_pos = horiz_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param vert_pos the Vertical position of the dialog
|
||||
*/
|
||||
public void setVerticalPos(int vert_pos) {
|
||||
field_3_vert_pos = vert_pos;
|
||||
}
|
||||
/**
|
||||
* @param vert_pos the Vertical position of the dialog
|
||||
*/
|
||||
public void setVerticalPos(int vert_pos) {
|
||||
field_3_vert_pos = vert_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* set the object ID of the drop down arrow object for list boxes
|
||||
* @param cboID - Object ID
|
||||
*/
|
||||
@ -104,27 +104,27 @@ public final class DVALRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the field_1_options
|
||||
*/
|
||||
public short getOptions() {
|
||||
return field_1_options;
|
||||
}
|
||||
* @return the field_1_options
|
||||
*/
|
||||
public short getOptions() {
|
||||
return field_1_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Horizontal position of the dialog
|
||||
*/
|
||||
public int getHorizontalPos() {
|
||||
return field_2_horiz_pos;
|
||||
}
|
||||
/**
|
||||
* @return the Horizontal position of the dialog
|
||||
*/
|
||||
public int getHorizontalPos() {
|
||||
return field_2_horiz_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the the Vertical position of the dialog
|
||||
*/
|
||||
public int getVerticalPos() {
|
||||
return field_3_vert_pos;
|
||||
}
|
||||
/**
|
||||
* @return the the Vertical position of the dialog
|
||||
*/
|
||||
public int getVerticalPos() {
|
||||
return field_3_vert_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @return the Object ID of the drop down arrow object for list boxes
|
||||
*/
|
||||
public int getObjectID() {
|
||||
@ -139,11 +139,11 @@ public final class DVALRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(getOptions());
|
||||
out.writeInt(getHorizontalPos());
|
||||
out.writeInt(getVerticalPos());
|
||||
out.writeInt(getObjectID());
|
||||
out.writeInt(getDVRecNo());
|
||||
out.writeShort(getOptions());
|
||||
out.writeInt(getHorizontalPos());
|
||||
out.writeInt(getVerticalPos());
|
||||
out.writeInt(getObjectID());
|
||||
out.writeInt(getDVRecNo());
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
@ -154,24 +154,24 @@ public final class DVALRecord extends StandardRecord {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public DVALRecord copy() {
|
||||
return new DVALRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DVAL;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"options", this::getOptions,
|
||||
"horizPos", this::getHorizontalPos,
|
||||
"vertPos", this::getVerticalPos,
|
||||
"comboObjectID", this::getObjectID,
|
||||
"dvRecordsNumber", this::getDVRecNo
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"options", this::getOptions,
|
||||
"horizPos", this::getHorizontalPos,
|
||||
"vertPos", this::getVerticalPos,
|
||||
"comboObjectID", this::getObjectID,
|
||||
"dvRecordsNumber", this::getDVRecNo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,201 +38,201 @@ import org.apache.poi.util.StringUtil;
|
||||
* This list is followed by DVAL record(s)
|
||||
*/
|
||||
public final class DVRecord extends StandardRecord {
|
||||
public static final short sid = 0x01BE;
|
||||
public static final short sid = 0x01BE;
|
||||
|
||||
/** the unicode string used for error/prompt title/text when not present */
|
||||
private static final UnicodeString NULL_TEXT_STRING = new UnicodeString("\0");
|
||||
/** the unicode string used for error/prompt title/text when not present */
|
||||
private static final UnicodeString NULL_TEXT_STRING = new UnicodeString("\0");
|
||||
|
||||
/**
|
||||
* Option flags field
|
||||
*
|
||||
* @see HSSFDataValidation utility class
|
||||
*/
|
||||
private static final BitField opt_data_type = new BitField(0x0000000F);
|
||||
private static final BitField opt_error_style = new BitField(0x00000070);
|
||||
private static final BitField opt_string_list_formula = new BitField(0x00000080);
|
||||
private static final BitField opt_empty_cell_allowed = new BitField(0x00000100);
|
||||
private static final BitField opt_suppress_dropdown_arrow = new BitField(0x00000200);
|
||||
private static final BitField opt_show_prompt_on_cell_selected = new BitField(0x00040000);
|
||||
private static final BitField opt_show_error_on_invalid_value = new BitField(0x00080000);
|
||||
private static final BitField opt_condition_operator = new BitField(0x00700000);
|
||||
/**
|
||||
* Option flags field
|
||||
*
|
||||
* @see HSSFDataValidation utility class
|
||||
*/
|
||||
private static final BitField opt_data_type = new BitField(0x0000000F);
|
||||
private static final BitField opt_error_style = new BitField(0x00000070);
|
||||
private static final BitField opt_string_list_formula = new BitField(0x00000080);
|
||||
private static final BitField opt_empty_cell_allowed = new BitField(0x00000100);
|
||||
private static final BitField opt_suppress_dropdown_arrow = new BitField(0x00000200);
|
||||
private static final BitField opt_show_prompt_on_cell_selected = new BitField(0x00040000);
|
||||
private static final BitField opt_show_error_on_invalid_value = new BitField(0x00080000);
|
||||
private static final BitField opt_condition_operator = new BitField(0x00700000);
|
||||
|
||||
private static final int[] FLAG_MASKS = { 0x0000000F,0x00000070,0x00000080,0x00000100,
|
||||
0x00000200,0x00040000,0x00080000,0x00700000 };
|
||||
private static final int[] FLAG_MASKS = { 0x0000000F,0x00000070,0x00000080,0x00000100,
|
||||
0x00000200,0x00040000,0x00080000,0x00700000 };
|
||||
|
||||
private static final String[] FLAG_NAMES = { "DATA_TYPE", "ERROR_STYLE", "STRING_LIST_FORMULA",
|
||||
"EMPTY_CELL_ALLOWED", "SUPPRESS_DROPDOWN_ARROW", "SHOW_PROMPT_ON_CELL_SELECTED",
|
||||
"SHOW_ERROR_ON_INVALID_VALUE", "CONDITION_OPERATOR" };
|
||||
private static final String[] FLAG_NAMES = { "DATA_TYPE", "ERROR_STYLE", "STRING_LIST_FORMULA",
|
||||
"EMPTY_CELL_ALLOWED", "SUPPRESS_DROPDOWN_ARROW", "SHOW_PROMPT_ON_CELL_SELECTED",
|
||||
"SHOW_ERROR_ON_INVALID_VALUE", "CONDITION_OPERATOR" };
|
||||
|
||||
|
||||
|
||||
/** Option flags */
|
||||
private int _option_flags;
|
||||
/** Title of the prompt box, cannot be longer than 32 chars */
|
||||
private final UnicodeString _promptTitle;
|
||||
/** Title of the error box, cannot be longer than 32 chars */
|
||||
private final UnicodeString _errorTitle;
|
||||
/** Text of the prompt box, cannot be longer than 255 chars */
|
||||
private final UnicodeString _promptText;
|
||||
/** Text of the error box, cannot be longer than 255 chars */
|
||||
private final UnicodeString _errorText;
|
||||
/** Not used - Excel seems to always write 0x3FE0 */
|
||||
private short _not_used_1 = 0x3FE0;
|
||||
/** Formula data for first condition (RPN token array without size field) */
|
||||
private final Formula _formula1;
|
||||
/** Not used - Excel seems to always write 0x0000 */
|
||||
@SuppressWarnings("RedundantFieldInitialization")
|
||||
private short _not_used_2 = 0x0000;
|
||||
/** Formula data for second condition (RPN token array without size field) */
|
||||
private final Formula _formula2;
|
||||
/** Cell range address list with all affected ranges */
|
||||
private final CellRangeAddressList _regions;
|
||||
/** Option flags */
|
||||
private int _option_flags;
|
||||
/** Title of the prompt box, cannot be longer than 32 chars */
|
||||
private final UnicodeString _promptTitle;
|
||||
/** Title of the error box, cannot be longer than 32 chars */
|
||||
private final UnicodeString _errorTitle;
|
||||
/** Text of the prompt box, cannot be longer than 255 chars */
|
||||
private final UnicodeString _promptText;
|
||||
/** Text of the error box, cannot be longer than 255 chars */
|
||||
private final UnicodeString _errorText;
|
||||
/** Not used - Excel seems to always write 0x3FE0 */
|
||||
private short _not_used_1 = 0x3FE0;
|
||||
/** Formula data for first condition (RPN token array without size field) */
|
||||
private final Formula _formula1;
|
||||
/** Not used - Excel seems to always write 0x0000 */
|
||||
@SuppressWarnings("RedundantFieldInitialization")
|
||||
private short _not_used_2 = 0x0000;
|
||||
/** Formula data for second condition (RPN token array without size field) */
|
||||
private final Formula _formula2;
|
||||
/** Cell range address list with all affected ranges */
|
||||
private final CellRangeAddressList _regions;
|
||||
|
||||
public DVRecord(DVRecord other) {
|
||||
super(other);
|
||||
_option_flags = other._option_flags;
|
||||
_promptTitle = other._promptTitle.copy();
|
||||
_errorTitle = other._errorTitle.copy();
|
||||
_promptText = other._promptText.copy();
|
||||
_errorText = other._errorText.copy();
|
||||
_not_used_1 = other._not_used_1;
|
||||
_formula1 = (other._formula1 == null) ? null : other._formula1.copy();
|
||||
_not_used_2 = other._not_used_2;
|
||||
_formula2 = (other._formula2 == null) ? null : other._formula2.copy();
|
||||
_regions = (other._regions == null) ? null : other._regions.copy();
|
||||
}
|
||||
public DVRecord(DVRecord other) {
|
||||
super(other);
|
||||
_option_flags = other._option_flags;
|
||||
_promptTitle = other._promptTitle.copy();
|
||||
_errorTitle = other._errorTitle.copy();
|
||||
_promptText = other._promptText.copy();
|
||||
_errorText = other._errorText.copy();
|
||||
_not_used_1 = other._not_used_1;
|
||||
_formula1 = (other._formula1 == null) ? null : other._formula1.copy();
|
||||
_not_used_2 = other._not_used_2;
|
||||
_formula2 = (other._formula2 == null) ? null : other._formula2.copy();
|
||||
_regions = (other._regions == null) ? null : other._regions.copy();
|
||||
}
|
||||
|
||||
public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
|
||||
boolean suppressDropDownArrow, boolean isExplicitList,
|
||||
boolean showPromptBox, String promptTitle, String promptText,
|
||||
boolean showErrorBox, String errorTitle, String errorText,
|
||||
Ptg[] formula1, Ptg[] formula2,
|
||||
CellRangeAddressList regions) {
|
||||
public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
|
||||
boolean suppressDropDownArrow, boolean isExplicitList,
|
||||
boolean showPromptBox, String promptTitle, String promptText,
|
||||
boolean showErrorBox, String errorTitle, String errorText,
|
||||
Ptg[] formula1, Ptg[] formula2,
|
||||
CellRangeAddressList regions) {
|
||||
|
||||
// check length-limits
|
||||
if(promptTitle != null && promptTitle.length() > 32) {
|
||||
throw new IllegalStateException("Prompt-title cannot be longer than 32 characters, but had: " + promptTitle);
|
||||
}
|
||||
if(promptText != null && promptText.length() > 255) {
|
||||
throw new IllegalStateException("Prompt-text cannot be longer than 255 characters, but had: " + promptText);
|
||||
}
|
||||
// check length-limits
|
||||
if(promptTitle != null && promptTitle.length() > 32) {
|
||||
throw new IllegalStateException("Prompt-title cannot be longer than 32 characters, but had: " + promptTitle);
|
||||
}
|
||||
if(promptText != null && promptText.length() > 255) {
|
||||
throw new IllegalStateException("Prompt-text cannot be longer than 255 characters, but had: " + promptText);
|
||||
}
|
||||
|
||||
if(errorTitle != null && errorTitle.length() > 32) {
|
||||
throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + errorTitle);
|
||||
}
|
||||
if(errorText != null && errorText.length() > 255) {
|
||||
throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + errorText);
|
||||
}
|
||||
if(errorTitle != null && errorTitle.length() > 32) {
|
||||
throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + errorTitle);
|
||||
}
|
||||
if(errorText != null && errorText.length() > 255) {
|
||||
throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + errorText);
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
flags = opt_data_type.setValue(flags, validationType);
|
||||
flags = opt_condition_operator.setValue(flags, operator);
|
||||
flags = opt_error_style.setValue(flags, errorStyle);
|
||||
flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
|
||||
flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
|
||||
flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
|
||||
flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
|
||||
flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
|
||||
_option_flags = flags;
|
||||
_promptTitle = resolveTitleText(promptTitle);
|
||||
_promptText = resolveTitleText(promptText);
|
||||
_errorTitle = resolveTitleText(errorTitle);
|
||||
_errorText = resolveTitleText(errorText);
|
||||
_formula1 = Formula.create(formula1);
|
||||
_formula2 = Formula.create(formula2);
|
||||
_regions = regions;
|
||||
}
|
||||
int flags = 0;
|
||||
flags = opt_data_type.setValue(flags, validationType);
|
||||
flags = opt_condition_operator.setValue(flags, operator);
|
||||
flags = opt_error_style.setValue(flags, errorStyle);
|
||||
flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
|
||||
flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
|
||||
flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
|
||||
flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
|
||||
flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
|
||||
_option_flags = flags;
|
||||
_promptTitle = resolveTitleText(promptTitle);
|
||||
_promptText = resolveTitleText(promptText);
|
||||
_errorTitle = resolveTitleText(errorTitle);
|
||||
_errorText = resolveTitleText(errorText);
|
||||
_formula1 = Formula.create(formula1);
|
||||
_formula2 = Formula.create(formula2);
|
||||
_regions = regions;
|
||||
}
|
||||
|
||||
public DVRecord(RecordInputStream in) {
|
||||
_option_flags = in.readInt();
|
||||
public DVRecord(RecordInputStream in) {
|
||||
_option_flags = in.readInt();
|
||||
|
||||
_promptTitle = readUnicodeString(in);
|
||||
_errorTitle = readUnicodeString(in);
|
||||
_promptText = readUnicodeString(in);
|
||||
_errorText = readUnicodeString(in);
|
||||
_promptTitle = readUnicodeString(in);
|
||||
_errorTitle = readUnicodeString(in);
|
||||
_promptText = readUnicodeString(in);
|
||||
_errorText = readUnicodeString(in);
|
||||
|
||||
int field_size_first_formula = in.readUShort();
|
||||
_not_used_1 = in.readShort();
|
||||
int field_size_first_formula = in.readUShort();
|
||||
_not_used_1 = in.readShort();
|
||||
|
||||
// "You may not use unions, intersections or array constants in Data Validation criteria"
|
||||
// "You may not use unions, intersections or array constants in Data Validation criteria"
|
||||
|
||||
// read first formula data condition
|
||||
_formula1 = Formula.read(field_size_first_formula, in);
|
||||
// read first formula data condition
|
||||
_formula1 = Formula.read(field_size_first_formula, in);
|
||||
|
||||
int field_size_sec_formula = in.readUShort();
|
||||
_not_used_2 = in.readShort();
|
||||
int field_size_sec_formula = in.readUShort();
|
||||
_not_used_2 = in.readShort();
|
||||
|
||||
// read sec formula data condition
|
||||
_formula2 = Formula.read(field_size_sec_formula, in);
|
||||
// read sec formula data condition
|
||||
_formula2 = Formula.read(field_size_sec_formula, in);
|
||||
|
||||
// read cell range address list with all affected ranges
|
||||
_regions = new CellRangeAddressList(in);
|
||||
}
|
||||
// read cell range address list with all affected ranges
|
||||
_regions = new CellRangeAddressList(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the condition data type
|
||||
* @see org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType
|
||||
*/
|
||||
public int getDataType() {
|
||||
return opt_data_type.getValue(_option_flags);
|
||||
}
|
||||
/**
|
||||
* @return the condition data type
|
||||
* @see org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType
|
||||
*/
|
||||
public int getDataType() {
|
||||
return opt_data_type.getValue(_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the condition error style
|
||||
* @see org.apache.poi.ss.usermodel.DataValidation.ErrorStyle
|
||||
*/
|
||||
public int getErrorStyle() {
|
||||
return opt_error_style.getValue(_option_flags);
|
||||
}
|
||||
/**
|
||||
* @return the condition error style
|
||||
* @see org.apache.poi.ss.usermodel.DataValidation.ErrorStyle
|
||||
*/
|
||||
public int getErrorStyle() {
|
||||
return opt_error_style.getValue(_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if in list validations the string list is explicitly given in the
|
||||
* formula, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getListExplicitFormula() {
|
||||
return (opt_string_list_formula.isSet(_option_flags));
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if in list validations the string list is explicitly given in the
|
||||
* formula, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getListExplicitFormula() {
|
||||
return (opt_string_list_formula.isSet(_option_flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getEmptyCellAllowed() {
|
||||
return (opt_empty_cell_allowed.isSet(_option_flags));
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getEmptyCellAllowed() {
|
||||
return (opt_empty_cell_allowed.isSet(_option_flags));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if drop down arrow should be suppressed when list validation is
|
||||
* used, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getSuppressDropdownArrow() {
|
||||
return (opt_suppress_dropdown_arrow.isSet(_option_flags));
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if drop down arrow should be suppressed when list validation is
|
||||
* used, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getSuppressDropdownArrow() {
|
||||
return (opt_suppress_dropdown_arrow.isSet(_option_flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getShowPromptOnCellSelected() {
|
||||
return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getShowPromptOnCellSelected() {
|
||||
return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if an error window should appear when an invalid value is entered
|
||||
* in the cell, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getShowErrorOnInvalidValue() {
|
||||
return (opt_show_error_on_invalid_value.isSet(_option_flags));
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if an error window should appear when an invalid value is entered
|
||||
* in the cell, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean getShowErrorOnInvalidValue() {
|
||||
return (opt_show_error_on_invalid_value.isSet(_option_flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* get the condition operator
|
||||
* @return the condition operator
|
||||
* @see HSSFDataValidation utility class
|
||||
*/
|
||||
public int getConditionOperator() {
|
||||
return opt_condition_operator.getValue(_option_flags);
|
||||
}
|
||||
// <-- end option flags
|
||||
/**
|
||||
* get the condition operator
|
||||
* @return the condition operator
|
||||
* @see HSSFDataValidation utility class
|
||||
*/
|
||||
public int getConditionOperator() {
|
||||
return opt_condition_operator.getValue(_option_flags);
|
||||
}
|
||||
// <-- end option flags
|
||||
|
||||
public String getPromptTitle() {
|
||||
return resolveTitleString(_promptTitle);
|
||||
@ -258,42 +258,42 @@ public final class DVRecord extends StandardRecord {
|
||||
return Formula.getTokens(_formula2);
|
||||
}
|
||||
|
||||
public CellRangeAddressList getCellRangeAddress() {
|
||||
return this._regions;
|
||||
}
|
||||
public CellRangeAddressList getCellRangeAddress() {
|
||||
return this._regions;
|
||||
}
|
||||
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
|
||||
out.writeInt(_option_flags);
|
||||
out.writeInt(_option_flags);
|
||||
|
||||
serializeUnicodeString(_promptTitle, out);
|
||||
serializeUnicodeString(_errorTitle, out);
|
||||
serializeUnicodeString(_promptText, out);
|
||||
serializeUnicodeString(_errorText, out);
|
||||
out.writeShort(_formula1.getEncodedTokenSize());
|
||||
out.writeShort(_not_used_1);
|
||||
_formula1.serializeTokens(out);
|
||||
serializeUnicodeString(_promptTitle, out);
|
||||
serializeUnicodeString(_errorTitle, out);
|
||||
serializeUnicodeString(_promptText, out);
|
||||
serializeUnicodeString(_errorText, out);
|
||||
out.writeShort(_formula1.getEncodedTokenSize());
|
||||
out.writeShort(_not_used_1);
|
||||
_formula1.serializeTokens(out);
|
||||
|
||||
out.writeShort(_formula2.getEncodedTokenSize());
|
||||
out.writeShort(_not_used_2);
|
||||
_formula2.serializeTokens(out);
|
||||
out.writeShort(_formula2.getEncodedTokenSize());
|
||||
out.writeShort(_not_used_2);
|
||||
_formula2.serializeTokens(out);
|
||||
|
||||
_regions.serialize(out);
|
||||
}
|
||||
_regions.serialize(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* When entered via the UI, Excel translates empty string into "\0"
|
||||
* While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
|
||||
* the resulting tool-tip text / message box looks wrong. It is best to do the same as the
|
||||
* Excel UI and encode 'not present' as "\0".
|
||||
*/
|
||||
private static UnicodeString resolveTitleText(String str) {
|
||||
if (str == null || str.length() < 1) {
|
||||
return NULL_TEXT_STRING;
|
||||
}
|
||||
return new UnicodeString(str);
|
||||
}
|
||||
/**
|
||||
* When entered via the UI, Excel translates empty string into "\0"
|
||||
* While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
|
||||
* the resulting tool-tip text / message box looks wrong. It is best to do the same as the
|
||||
* Excel UI and encode 'not present' as "\0".
|
||||
*/
|
||||
private static UnicodeString resolveTitleText(String str) {
|
||||
if (str == null || str.length() < 1) {
|
||||
return NULL_TEXT_STRING;
|
||||
}
|
||||
return new UnicodeString(str);
|
||||
}
|
||||
|
||||
private static String resolveTitleString(UnicodeString us) {
|
||||
if (us == null || us.equals(NULL_TEXT_STRING)) {
|
||||
@ -302,56 +302,56 @@ public final class DVRecord extends StandardRecord {
|
||||
return us.getString();
|
||||
}
|
||||
|
||||
private static UnicodeString readUnicodeString(RecordInputStream in) {
|
||||
return new UnicodeString(in);
|
||||
}
|
||||
private static UnicodeString readUnicodeString(RecordInputStream in) {
|
||||
return new UnicodeString(in);
|
||||
}
|
||||
|
||||
private static void serializeUnicodeString(UnicodeString us, LittleEndianOutput out) {
|
||||
StringUtil.writeUnicodeString(out, us.getString());
|
||||
}
|
||||
private static int getUnicodeStringSize(UnicodeString us) {
|
||||
String str = us.getString();
|
||||
return 3 + str.length() * (StringUtil.hasMultibyte(str) ? 2 : 1);
|
||||
}
|
||||
private static void serializeUnicodeString(UnicodeString us, LittleEndianOutput out) {
|
||||
StringUtil.writeUnicodeString(out, us.getString());
|
||||
}
|
||||
private static int getUnicodeStringSize(UnicodeString us) {
|
||||
String str = us.getString();
|
||||
return 3 + str.length() * (StringUtil.hasMultibyte(str) ? 2 : 1);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
int size = 4+2+2+2+2;//options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
|
||||
size += getUnicodeStringSize(_promptTitle);
|
||||
size += getUnicodeStringSize(_errorTitle);
|
||||
size += getUnicodeStringSize(_promptText);
|
||||
size += getUnicodeStringSize(_errorText);
|
||||
size += _formula1.getEncodedTokenSize();
|
||||
size += _formula2.getEncodedTokenSize();
|
||||
size += _regions.getSize();
|
||||
return size;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
int size = 4+2+2+2+2;//options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
|
||||
size += getUnicodeStringSize(_promptTitle);
|
||||
size += getUnicodeStringSize(_errorTitle);
|
||||
size += getUnicodeStringSize(_promptText);
|
||||
size += getUnicodeStringSize(_errorText);
|
||||
size += _formula1.getEncodedTokenSize();
|
||||
size += _formula2.getEncodedTokenSize();
|
||||
size += _regions.getSize();
|
||||
return size;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/** Clones the object. */
|
||||
@Override
|
||||
public DVRecord copy() {
|
||||
return new DVRecord(this);
|
||||
}
|
||||
/** Clones the object. */
|
||||
@Override
|
||||
public DVRecord copy() {
|
||||
return new DVRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DV;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"optionFlags", getBitsAsString(() -> _option_flags, FLAG_MASKS, FLAG_NAMES),
|
||||
"promptTitle", this::getPromptTitle,
|
||||
"errorTitle", this::getErrorTitle,
|
||||
"promptText", this::getPromptText,
|
||||
"errorText", this::getErrorText,
|
||||
"formula1", this::getFormula1,
|
||||
"formula2", this::getFormula2,
|
||||
"regions", () -> _regions
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"optionFlags", getBitsAsString(() -> _option_flags, FLAG_MASKS, FLAG_NAMES),
|
||||
"promptTitle", this::getPromptTitle,
|
||||
"errorTitle", this::getErrorTitle,
|
||||
"promptText", this::getPromptText,
|
||||
"errorText", this::getErrorText,
|
||||
"formula1", this::getFormula1,
|
||||
"formula2", this::getFormula2,
|
||||
"regions", () -> _regions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,17 +41,17 @@ public final class DrawingRecordForBiffViewer extends AbstractEscherHolderRecord
|
||||
|
||||
public DrawingRecordForBiffViewer(DrawingRecord r)
|
||||
{
|
||||
super(convertToInputStream(r));
|
||||
decode();
|
||||
super(convertToInputStream(r));
|
||||
decode();
|
||||
}
|
||||
private static RecordInputStream convertToInputStream(DrawingRecord r)
|
||||
{
|
||||
byte[] data = r.serialize();
|
||||
RecordInputStream rinp = new RecordInputStream(
|
||||
new ByteArrayInputStream(data)
|
||||
);
|
||||
rinp.nextRecord();
|
||||
return rinp;
|
||||
byte[] data = r.serialize();
|
||||
RecordInputStream rinp = new RecordInputStream(
|
||||
new ByteArrayInputStream(data)
|
||||
);
|
||||
rinp.nextRecord();
|
||||
return rinp;
|
||||
}
|
||||
|
||||
protected String getRecordName()
|
||||
|
||||
@ -31,107 +31,107 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* [MS-OGRAPH].pdf sec 2.4.69
|
||||
*/
|
||||
public final class DrawingSelectionRecord extends StandardRecord {
|
||||
public static final short sid = 0x00ED;
|
||||
public static final short sid = 0x00ED;
|
||||
|
||||
/**
|
||||
* From [MS-ODRAW].pdf sec 2.2.1<p>
|
||||
* TODO - make EscherRecordHeader {@link LittleEndianInput} aware and refactor with this
|
||||
*/
|
||||
private static final class OfficeArtRecordHeader implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 8;
|
||||
/**
|
||||
* lower 4 bits is 'version' usually 0x01 or 0x0F (for containers)
|
||||
* upper 12 bits is 'instance'
|
||||
*/
|
||||
private final int _verAndInstance;
|
||||
/** value should be between 0xF000 and 0xFFFF */
|
||||
private final int _type;
|
||||
private final int _length;
|
||||
/**
|
||||
* From [MS-ODRAW].pdf sec 2.2.1<p>
|
||||
* TODO - make EscherRecordHeader {@link LittleEndianInput} aware and refactor with this
|
||||
*/
|
||||
private static final class OfficeArtRecordHeader implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 8;
|
||||
/**
|
||||
* lower 4 bits is 'version' usually 0x01 or 0x0F (for containers)
|
||||
* upper 12 bits is 'instance'
|
||||
*/
|
||||
private final int _verAndInstance;
|
||||
/** value should be between 0xF000 and 0xFFFF */
|
||||
private final int _type;
|
||||
private final int _length;
|
||||
|
||||
public OfficeArtRecordHeader(LittleEndianInput in) {
|
||||
_verAndInstance = in.readUShort();
|
||||
_type = in.readUShort();
|
||||
_length = in.readInt();
|
||||
}
|
||||
public OfficeArtRecordHeader(LittleEndianInput in) {
|
||||
_verAndInstance = in.readUShort();
|
||||
_type = in.readUShort();
|
||||
_length = in.readInt();
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_verAndInstance);
|
||||
out.writeShort(_type);
|
||||
out.writeInt(_length);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_verAndInstance);
|
||||
out.writeShort(_type);
|
||||
out.writeInt(_length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"verAndInstance", () -> _verAndInstance,
|
||||
"type", () -> _type,
|
||||
"length", () -> _length
|
||||
);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"verAndInstance", () -> _verAndInstance,
|
||||
"type", () -> _type,
|
||||
"length", () -> _length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// [MS-OGRAPH].pdf says that the data of this record is an OfficeArtFDGSL structure
|
||||
// as described in[MS-ODRAW].pdf sec 2.2.33
|
||||
private OfficeArtRecordHeader _header;
|
||||
private int _cpsp;
|
||||
/** a MSODGSLK enum value for the current selection mode */
|
||||
private int _dgslk;
|
||||
private int _spidFocus;
|
||||
/** selected shape IDs (e.g. from EscherSpRecord.ShapeId) */
|
||||
private int[] _shapeIds;
|
||||
// [MS-OGRAPH].pdf says that the data of this record is an OfficeArtFDGSL structure
|
||||
// as described in[MS-ODRAW].pdf sec 2.2.33
|
||||
private OfficeArtRecordHeader _header;
|
||||
private int _cpsp;
|
||||
/** a MSODGSLK enum value for the current selection mode */
|
||||
private int _dgslk;
|
||||
private int _spidFocus;
|
||||
/** selected shape IDs (e.g. from EscherSpRecord.ShapeId) */
|
||||
private int[] _shapeIds;
|
||||
|
||||
public DrawingSelectionRecord(RecordInputStream in) {
|
||||
_header = new OfficeArtRecordHeader(in);
|
||||
_cpsp = in.readInt();
|
||||
_dgslk = in.readInt();
|
||||
_spidFocus = in.readInt();
|
||||
int nShapes = in.available() / 4;
|
||||
int[] shapeIds = new int[nShapes];
|
||||
for (int i = 0; i < nShapes; i++) {
|
||||
shapeIds[i] = in.readInt();
|
||||
}
|
||||
_shapeIds = shapeIds;
|
||||
}
|
||||
public DrawingSelectionRecord(RecordInputStream in) {
|
||||
_header = new OfficeArtRecordHeader(in);
|
||||
_cpsp = in.readInt();
|
||||
_dgslk = in.readInt();
|
||||
_spidFocus = in.readInt();
|
||||
int nShapes = in.available() / 4;
|
||||
int[] shapeIds = new int[nShapes];
|
||||
for (int i = 0; i < nShapes; i++) {
|
||||
shapeIds[i] = in.readInt();
|
||||
}
|
||||
_shapeIds = shapeIds;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return OfficeArtRecordHeader.ENCODED_SIZE
|
||||
+ 12 // 3 int fields
|
||||
+ _shapeIds.length * 4;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return OfficeArtRecordHeader.ENCODED_SIZE
|
||||
+ 12 // 3 int fields
|
||||
+ _shapeIds.length * 4;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
_header.serialize(out);
|
||||
out.writeInt(_cpsp);
|
||||
out.writeInt(_dgslk);
|
||||
out.writeInt(_spidFocus);
|
||||
for (int shapeId : _shapeIds) {
|
||||
out.writeInt(shapeId);
|
||||
}
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
_header.serialize(out);
|
||||
out.writeInt(_cpsp);
|
||||
out.writeInt(_dgslk);
|
||||
out.writeInt(_spidFocus);
|
||||
for (int shapeId : _shapeIds) {
|
||||
out.writeInt(shapeId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawingSelectionRecord copy() {
|
||||
// currently immutable
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public DrawingSelectionRecord copy() {
|
||||
// currently immutable
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DRAWING_SELECTION;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.DRAWING_SELECTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rh", () -> _header,
|
||||
"cpsp", () -> _cpsp,
|
||||
"dgslk", () -> _dgslk,
|
||||
"spidFocus", () -> _spidFocus,
|
||||
"shapeIds", () -> _shapeIds
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rh", () -> _header,
|
||||
"cpsp", () -> _cpsp,
|
||||
"dgslk", () -> _dgslk,
|
||||
"spidFocus", () -> _spidFocus,
|
||||
"shapeIds", () -> _shapeIds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,9 +29,9 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
*/
|
||||
public final class EOFRecord extends StandardRecord {
|
||||
public static final short sid = 0x0A;
|
||||
public static final int ENCODED_SIZE = 4;
|
||||
public static final int ENCODED_SIZE = 4;
|
||||
|
||||
public static final EOFRecord instance = new EOFRecord();
|
||||
public static final EOFRecord instance = new EOFRecord();
|
||||
|
||||
private EOFRecord() {}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
(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
|
||||
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,
|
||||
@ -45,315 +45,315 @@ import static org.apache.logging.log4j.util.Unbox.box;
|
||||
* stored in a separate entry within the OLE2 compound file.
|
||||
*/
|
||||
public final class EmbeddedObjectRefSubRecord extends SubRecord {
|
||||
private static final Logger LOG = LogManager.getLogger(EmbeddedObjectRefSubRecord.class);
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 100_000;
|
||||
private static final Logger LOG = LogManager.getLogger(EmbeddedObjectRefSubRecord.class);
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 100_000;
|
||||
|
||||
public static final short sid = 0x0009;
|
||||
public static final short sid = 0x0009;
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
|
||||
private int field_1_unknown_int;
|
||||
/** either an area or a cell ref */
|
||||
private Ptg field_2_refPtg;
|
||||
/** for when the 'formula' doesn't parse properly */
|
||||
private byte[] field_2_unknownFormulaData;
|
||||
/** note- this byte is not present in the encoding if the string length is zero */
|
||||
private boolean field_3_unicode_flag; // Flags whether the string is Unicode.
|
||||
private String field_4_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
|
||||
/** Formulas often have a single non-zero trailing byte.
|
||||
* This is in a similar position to he pre-streamId padding
|
||||
* It is unknown if the value is important (it seems to mirror a value a few bytes earlier)
|
||||
* */
|
||||
private Byte field_4_unknownByte;
|
||||
private Integer field_5_stream_id; // ID of the OLE stream containing the actual data.
|
||||
private byte[] field_6_unknown;
|
||||
private int field_1_unknown_int;
|
||||
/** either an area or a cell ref */
|
||||
private Ptg field_2_refPtg;
|
||||
/** for when the 'formula' doesn't parse properly */
|
||||
private byte[] field_2_unknownFormulaData;
|
||||
/** note- this byte is not present in the encoding if the string length is zero */
|
||||
private boolean field_3_unicode_flag; // Flags whether the string is Unicode.
|
||||
private String field_4_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
|
||||
/** Formulas often have a single non-zero trailing byte.
|
||||
* This is in a similar position to he pre-streamId padding
|
||||
* It is unknown if the value is important (it seems to mirror a value a few bytes earlier)
|
||||
* */
|
||||
private Byte field_4_unknownByte;
|
||||
private Integer field_5_stream_id; // ID of the OLE stream containing the actual data.
|
||||
private byte[] field_6_unknown;
|
||||
|
||||
|
||||
// currently for testing only - needs review
|
||||
public EmbeddedObjectRefSubRecord() {
|
||||
field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data. These values vary a lot
|
||||
field_6_unknown = EMPTY_BYTE_ARRAY;
|
||||
field_4_ole_classname = null;
|
||||
}
|
||||
// currently for testing only - needs review
|
||||
public EmbeddedObjectRefSubRecord() {
|
||||
field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data. These values vary a lot
|
||||
field_6_unknown = EMPTY_BYTE_ARRAY;
|
||||
field_4_ole_classname = null;
|
||||
}
|
||||
|
||||
public EmbeddedObjectRefSubRecord(EmbeddedObjectRefSubRecord other) {
|
||||
super(other);
|
||||
field_1_unknown_int = other.field_1_unknown_int;
|
||||
field_2_refPtg = (other.field_2_refPtg == null) ? null : other.field_2_refPtg.copy();
|
||||
field_2_unknownFormulaData = (other.field_2_unknownFormulaData == null) ? null : other.field_2_unknownFormulaData.clone();
|
||||
field_3_unicode_flag = other.field_3_unicode_flag;
|
||||
field_4_ole_classname = other.field_4_ole_classname;
|
||||
field_4_unknownByte = other.field_4_unknownByte;
|
||||
field_5_stream_id = other.field_5_stream_id;
|
||||
field_6_unknown = (other.field_6_unknown == null) ? null : other.field_6_unknown.clone();
|
||||
}
|
||||
public EmbeddedObjectRefSubRecord(EmbeddedObjectRefSubRecord other) {
|
||||
super(other);
|
||||
field_1_unknown_int = other.field_1_unknown_int;
|
||||
field_2_refPtg = (other.field_2_refPtg == null) ? null : other.field_2_refPtg.copy();
|
||||
field_2_unknownFormulaData = (other.field_2_unknownFormulaData == null) ? null : other.field_2_unknownFormulaData.clone();
|
||||
field_3_unicode_flag = other.field_3_unicode_flag;
|
||||
field_4_ole_classname = other.field_4_ole_classname;
|
||||
field_4_unknownByte = other.field_4_unknownByte;
|
||||
field_5_stream_id = other.field_5_stream_id;
|
||||
field_6_unknown = (other.field_6_unknown == null) ? null : other.field_6_unknown.clone();
|
||||
}
|
||||
|
||||
public EmbeddedObjectRefSubRecord(LittleEndianInput in, int size) {
|
||||
this(in,size,-1);
|
||||
}
|
||||
public EmbeddedObjectRefSubRecord(LittleEndianInput in, int size) {
|
||||
this(in,size,-1);
|
||||
}
|
||||
|
||||
EmbeddedObjectRefSubRecord(LittleEndianInput in, int size, int cmoOt) {
|
||||
EmbeddedObjectRefSubRecord(LittleEndianInput in, int size, int cmoOt) {
|
||||
|
||||
// Much guess-work going on here due to lack of any documentation.
|
||||
// See similar source code in OOO:
|
||||
// http://svn.services.openoffice.org/ooo/trunk/sc/source/filter/excel/xiescher.cxx
|
||||
// 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )
|
||||
// Much guess-work going on here due to lack of any documentation.
|
||||
// See similar source code in OOO:
|
||||
// http://svn.services.openoffice.org/ooo/trunk/sc/source/filter/excel/xiescher.cxx
|
||||
// 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )
|
||||
|
||||
int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
|
||||
int remaining = size - LittleEndianConsts.SHORT_SIZE;
|
||||
int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
|
||||
int remaining = size - LittleEndianConsts.SHORT_SIZE;
|
||||
|
||||
int dataLenAfterFormula = remaining - streamIdOffset;
|
||||
int formulaSize = in.readUShort();
|
||||
remaining -= LittleEndianConsts.SHORT_SIZE;
|
||||
field_1_unknown_int = in.readInt();
|
||||
remaining -= LittleEndianConsts.INT_SIZE;
|
||||
byte[] formulaRawBytes = readRawData(in, formulaSize);
|
||||
remaining -= formulaSize;
|
||||
field_2_refPtg = readRefPtg(formulaRawBytes);
|
||||
if (field_2_refPtg == null) {
|
||||
// common case
|
||||
// field_2_n16 seems to be 5 here
|
||||
// The formula almost looks like tTbl but the row/column values seem like garbage.
|
||||
field_2_unknownFormulaData = formulaRawBytes;
|
||||
} else {
|
||||
field_2_unknownFormulaData = null;
|
||||
}
|
||||
int dataLenAfterFormula = remaining - streamIdOffset;
|
||||
int formulaSize = in.readUShort();
|
||||
remaining -= LittleEndianConsts.SHORT_SIZE;
|
||||
field_1_unknown_int = in.readInt();
|
||||
remaining -= LittleEndianConsts.INT_SIZE;
|
||||
byte[] formulaRawBytes = readRawData(in, formulaSize);
|
||||
remaining -= formulaSize;
|
||||
field_2_refPtg = readRefPtg(formulaRawBytes);
|
||||
if (field_2_refPtg == null) {
|
||||
// common case
|
||||
// field_2_n16 seems to be 5 here
|
||||
// The formula almost looks like tTbl but the row/column values seem like garbage.
|
||||
field_2_unknownFormulaData = formulaRawBytes;
|
||||
} else {
|
||||
field_2_unknownFormulaData = null;
|
||||
}
|
||||
|
||||
int stringByteCount;
|
||||
if (remaining >= dataLenAfterFormula + 3) {
|
||||
int tag = in.readByte();
|
||||
stringByteCount = LittleEndianConsts.BYTE_SIZE;
|
||||
if (tag != 0x03) {
|
||||
throw new RecordFormatException("Expected byte 0x03 here");
|
||||
}
|
||||
int nChars = in.readUShort();
|
||||
stringByteCount += LittleEndianConsts.SHORT_SIZE;
|
||||
if (nChars > 0) {
|
||||
// OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
|
||||
field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0;
|
||||
stringByteCount += LittleEndianConsts.BYTE_SIZE;
|
||||
if (field_3_unicode_flag) {
|
||||
field_4_ole_classname = StringUtil.readUnicodeLE(in, nChars);
|
||||
stringByteCount += nChars * 2;
|
||||
} else {
|
||||
field_4_ole_classname = StringUtil.readCompressedUnicode(in, nChars);
|
||||
stringByteCount += nChars;
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = "";
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = null;
|
||||
stringByteCount = 0;
|
||||
}
|
||||
remaining -= stringByteCount;
|
||||
// Pad to next 2-byte boundary
|
||||
if (((stringByteCount + formulaSize) % 2) != 0) {
|
||||
int b = in.readByte();
|
||||
remaining -= LittleEndianConsts.BYTE_SIZE;
|
||||
if (field_2_refPtg != null && field_4_ole_classname == null) {
|
||||
field_4_unknownByte = (byte)b;
|
||||
}
|
||||
}
|
||||
int nUnexpectedPadding = remaining - dataLenAfterFormula;
|
||||
int stringByteCount;
|
||||
if (remaining >= dataLenAfterFormula + 3) {
|
||||
int tag = in.readByte();
|
||||
stringByteCount = LittleEndianConsts.BYTE_SIZE;
|
||||
if (tag != 0x03) {
|
||||
throw new RecordFormatException("Expected byte 0x03 here");
|
||||
}
|
||||
int nChars = in.readUShort();
|
||||
stringByteCount += LittleEndianConsts.SHORT_SIZE;
|
||||
if (nChars > 0) {
|
||||
// OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
|
||||
field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0;
|
||||
stringByteCount += LittleEndianConsts.BYTE_SIZE;
|
||||
if (field_3_unicode_flag) {
|
||||
field_4_ole_classname = StringUtil.readUnicodeLE(in, nChars);
|
||||
stringByteCount += nChars * 2;
|
||||
} else {
|
||||
field_4_ole_classname = StringUtil.readCompressedUnicode(in, nChars);
|
||||
stringByteCount += nChars;
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = "";
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = null;
|
||||
stringByteCount = 0;
|
||||
}
|
||||
remaining -= stringByteCount;
|
||||
// Pad to next 2-byte boundary
|
||||
if (((stringByteCount + formulaSize) % 2) != 0) {
|
||||
int b = in.readByte();
|
||||
remaining -= LittleEndianConsts.BYTE_SIZE;
|
||||
if (field_2_refPtg != null && field_4_ole_classname == null) {
|
||||
field_4_unknownByte = (byte)b;
|
||||
}
|
||||
}
|
||||
int nUnexpectedPadding = remaining - dataLenAfterFormula;
|
||||
|
||||
if (nUnexpectedPadding > 0) {
|
||||
LOG.atError().log("Discarding {} unexpected padding bytes", box(nUnexpectedPadding));
|
||||
readRawData(in, nUnexpectedPadding);
|
||||
remaining-=nUnexpectedPadding;
|
||||
}
|
||||
if (nUnexpectedPadding > 0) {
|
||||
LOG.atError().log("Discarding {} unexpected padding bytes", box(nUnexpectedPadding));
|
||||
readRawData(in, nUnexpectedPadding);
|
||||
remaining-=nUnexpectedPadding;
|
||||
}
|
||||
|
||||
// Fetch the stream ID
|
||||
if (dataLenAfterFormula >= 4) {
|
||||
field_5_stream_id = in.readInt();
|
||||
remaining -= LittleEndianConsts.INT_SIZE;
|
||||
} else {
|
||||
field_5_stream_id = null;
|
||||
}
|
||||
field_6_unknown = readRawData(in, remaining);
|
||||
}
|
||||
// Fetch the stream ID
|
||||
if (dataLenAfterFormula >= 4) {
|
||||
field_5_stream_id = in.readInt();
|
||||
remaining -= LittleEndianConsts.INT_SIZE;
|
||||
} else {
|
||||
field_5_stream_id = null;
|
||||
}
|
||||
field_6_unknown = readRawData(in, remaining);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
private static Ptg readRefPtg(byte[] formulaRawBytes) {
|
||||
LittleEndianInput in = new LittleEndianInputStream(new ByteArrayInputStream(formulaRawBytes));
|
||||
byte ptgSid = in.readByte();
|
||||
switch(ptgSid) {
|
||||
case AreaPtg.sid: return new AreaPtg(in);
|
||||
case Area3DPtg.sid: return new Area3DPtg(in);
|
||||
case RefPtg.sid: return new RefPtg(in);
|
||||
case Ref3DPtg.sid: return new Ref3DPtg(in);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static Ptg readRefPtg(byte[] formulaRawBytes) {
|
||||
LittleEndianInput in = new LittleEndianInputStream(new ByteArrayInputStream(formulaRawBytes));
|
||||
byte ptgSid = in.readByte();
|
||||
switch(ptgSid) {
|
||||
case AreaPtg.sid: return new AreaPtg(in);
|
||||
case Area3DPtg.sid: return new Area3DPtg(in);
|
||||
case RefPtg.sid: return new RefPtg(in);
|
||||
case Ref3DPtg.sid: return new Ref3DPtg(in);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[] readRawData(LittleEndianInput in, int size) {
|
||||
if (size < 0) {
|
||||
throw new IllegalArgumentException("Negative size (" + size + ")");
|
||||
}
|
||||
if (size == 0) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
byte[] result = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
in.readFully(result);
|
||||
return result;
|
||||
}
|
||||
private static byte[] readRawData(LittleEndianInput in, int size) {
|
||||
if (size < 0) {
|
||||
throw new IllegalArgumentException("Negative size (" + size + ")");
|
||||
}
|
||||
if (size == 0) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
byte[] result = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
in.readFully(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getStreamIDOffset(int formulaSize) {
|
||||
int result = 2 + 4; // formulaSize + f2unknown_int
|
||||
result += formulaSize;
|
||||
private int getStreamIDOffset(int formulaSize) {
|
||||
int result = 2 + 4; // formulaSize + f2unknown_int
|
||||
result += formulaSize;
|
||||
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
if (field_4_ole_classname != null) {
|
||||
result += 1 + 2; // 0x03, stringLen
|
||||
int stringLen = field_4_ole_classname.length();
|
||||
if (stringLen > 0) {
|
||||
result += 1; // flag
|
||||
if (field_3_unicode_flag) {
|
||||
result += stringLen * 2;
|
||||
} else {
|
||||
result += stringLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
// pad to next 2 byte boundary
|
||||
if ((result % 2) != 0) {
|
||||
result ++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
if (field_4_ole_classname != null) {
|
||||
result += 1 + 2; // 0x03, stringLen
|
||||
int stringLen = field_4_ole_classname.length();
|
||||
if (stringLen > 0) {
|
||||
result += 1; // flag
|
||||
if (field_3_unicode_flag) {
|
||||
result += stringLen * 2;
|
||||
} else {
|
||||
result += stringLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
// pad to next 2 byte boundary
|
||||
if ((result % 2) != 0) {
|
||||
result ++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getDataSize(int idOffset) {
|
||||
private int getDataSize(int idOffset) {
|
||||
|
||||
int result = 2 + idOffset; // 2 for idOffset short field itself
|
||||
if (field_5_stream_id != null) {
|
||||
result += 4;
|
||||
}
|
||||
return result + field_6_unknown.length;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
return getDataSize(idOffset);
|
||||
}
|
||||
int result = 2 + idOffset; // 2 for idOffset short field itself
|
||||
if (field_5_stream_id != null) {
|
||||
result += 4;
|
||||
}
|
||||
return result + field_6_unknown.length;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
return getDataSize(idOffset);
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
int dataSize = getDataSize(idOffset);
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
int dataSize = getDataSize(idOffset);
|
||||
|
||||
|
||||
out.writeShort(sid);
|
||||
out.writeShort(dataSize);
|
||||
out.writeShort(sid);
|
||||
out.writeShort(dataSize);
|
||||
|
||||
out.writeShort(idOffset);
|
||||
out.writeShort(formulaSize);
|
||||
out.writeInt(field_1_unknown_int);
|
||||
out.writeShort(idOffset);
|
||||
out.writeShort(formulaSize);
|
||||
out.writeInt(field_1_unknown_int);
|
||||
|
||||
int pos = 12;
|
||||
int pos = 12;
|
||||
|
||||
if (field_2_refPtg == null) {
|
||||
out.write(field_2_unknownFormulaData);
|
||||
} else {
|
||||
field_2_refPtg.write(out);
|
||||
}
|
||||
pos += formulaSize;
|
||||
if (field_2_refPtg == null) {
|
||||
out.write(field_2_unknownFormulaData);
|
||||
} else {
|
||||
field_2_refPtg.write(out);
|
||||
}
|
||||
pos += formulaSize;
|
||||
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
if (field_4_ole_classname != null) {
|
||||
out.writeByte(0x03);
|
||||
pos+=1;
|
||||
int stringLen = field_4_ole_classname.length();
|
||||
out.writeShort(stringLen);
|
||||
pos+=2;
|
||||
if (stringLen > 0) {
|
||||
out.writeByte(field_3_unicode_flag ? 0x01 : 0x00);
|
||||
pos+=1;
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
if (field_4_ole_classname != null) {
|
||||
out.writeByte(0x03);
|
||||
pos+=1;
|
||||
int stringLen = field_4_ole_classname.length();
|
||||
out.writeShort(stringLen);
|
||||
pos+=2;
|
||||
if (stringLen > 0) {
|
||||
out.writeByte(field_3_unicode_flag ? 0x01 : 0x00);
|
||||
pos+=1;
|
||||
|
||||
if (field_3_unicode_flag) {
|
||||
StringUtil.putUnicodeLE(field_4_ole_classname, out);
|
||||
pos += stringLen * 2;
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_4_ole_classname, out);
|
||||
pos += stringLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (field_3_unicode_flag) {
|
||||
StringUtil.putUnicodeLE(field_4_ole_classname, out);
|
||||
pos += stringLen * 2;
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_4_ole_classname, out);
|
||||
pos += stringLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pad to next 2-byte boundary (requires 0 or 1 bytes)
|
||||
switch(idOffset - (pos - 6)) { // 6 for 3 shorts: sid, dataSize, idOffset
|
||||
case 1:
|
||||
out.writeByte(field_4_unknownByte == null ? 0x00 : field_4_unknownByte.intValue());
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Bad padding calculation (" + idOffset + ", " + pos + ")");
|
||||
}
|
||||
// pad to next 2-byte boundary (requires 0 or 1 bytes)
|
||||
switch(idOffset - (pos - 6)) { // 6 for 3 shorts: sid, dataSize, idOffset
|
||||
case 1:
|
||||
out.writeByte(field_4_unknownByte == null ? 0x00 : field_4_unknownByte.intValue());
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Bad padding calculation (" + idOffset + ", " + pos + ")");
|
||||
}
|
||||
|
||||
if (field_5_stream_id != null) {
|
||||
out.writeInt(field_5_stream_id);
|
||||
}
|
||||
out.write(field_6_unknown);
|
||||
}
|
||||
if (field_5_stream_id != null) {
|
||||
out.writeInt(field_5_stream_id);
|
||||
}
|
||||
out.write(field_6_unknown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stream ID containing the actual data. The data itself
|
||||
* can be found under a top-level directory entry in the OLE2 filesystem
|
||||
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
|
||||
* this ID converted into hex (in big endian order, funnily enough.)
|
||||
*
|
||||
* @return the data stream ID. Possibly <code>null</code>
|
||||
*/
|
||||
public Integer getStreamId() {
|
||||
return field_5_stream_id;
|
||||
}
|
||||
/**
|
||||
* Gets the stream ID containing the actual data. The data itself
|
||||
* can be found under a top-level directory entry in the OLE2 filesystem
|
||||
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
|
||||
* this ID converted into hex (in big endian order, funnily enough.)
|
||||
*
|
||||
* @return the data stream ID. Possibly <code>null</code>
|
||||
*/
|
||||
public Integer getStreamId() {
|
||||
return field_5_stream_id;
|
||||
}
|
||||
|
||||
public String getOLEClassName() {
|
||||
return field_4_ole_classname;
|
||||
}
|
||||
public String getOLEClassName() {
|
||||
return field_4_ole_classname;
|
||||
}
|
||||
|
||||
public byte[] getObjectData() {
|
||||
return field_6_unknown;
|
||||
}
|
||||
public byte[] getObjectData() {
|
||||
return field_6_unknown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddedObjectRefSubRecord copy() {
|
||||
return new EmbeddedObjectRefSubRecord(this);
|
||||
}
|
||||
@Override
|
||||
public EmbeddedObjectRefSubRecord copy() {
|
||||
return new EmbeddedObjectRefSubRecord(this);
|
||||
}
|
||||
|
||||
public void setUnknownFormulaData(byte[] formularData) {
|
||||
field_2_unknownFormulaData = formularData;
|
||||
}
|
||||
public void setUnknownFormulaData(byte[] formularData) {
|
||||
field_2_unknownFormulaData = formularData;
|
||||
}
|
||||
|
||||
public void setOleClassname(String oleClassname) {
|
||||
field_4_ole_classname = oleClassname;
|
||||
}
|
||||
public void setOleClassname(String oleClassname) {
|
||||
field_4_ole_classname = oleClassname;
|
||||
}
|
||||
|
||||
public void setStorageId(int storageId) {
|
||||
field_5_stream_id = storageId;
|
||||
}
|
||||
public void setStorageId(int storageId) {
|
||||
field_5_stream_id = storageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubRecordTypes getGenericRecordType() {
|
||||
return SubRecordTypes.EMBEDDED_OBJECT_REF;
|
||||
}
|
||||
@Override
|
||||
public SubRecordTypes getGenericRecordType() {
|
||||
return SubRecordTypes.EMBEDDED_OBJECT_REF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"f2unknown", () -> field_1_unknown_int,
|
||||
"f3unknown", () -> field_2_unknownFormulaData,
|
||||
"formula", () -> field_2_refPtg,
|
||||
"unicodeFlag", () -> field_3_unicode_flag,
|
||||
"oleClassname", () -> field_4_ole_classname,
|
||||
"f4unknown", () -> field_4_unknownByte,
|
||||
"streamId", () -> field_5_stream_id,
|
||||
"f7unknown", () -> field_6_unknown
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"f2unknown", () -> field_1_unknown_int,
|
||||
"f3unknown", () -> field_2_unknownFormulaData,
|
||||
"formula", () -> field_2_refPtg,
|
||||
"unicodeFlag", () -> field_3_unicode_flag,
|
||||
"oleClassname", () -> field_4_ole_classname,
|
||||
"f4unknown", () -> field_4_unknownByte,
|
||||
"streamId", () -> field_5_stream_id,
|
||||
"f7unknown", () -> field_6_unknown
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ public final class EndSubRecord extends SubRecord {
|
||||
out.writeShort(ENCODED_SIZE);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
protected int getDataSize() {
|
||||
return ENCODED_SIZE;
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ public final class ExtSSTRecord extends ContinuableRecord {
|
||||
|
||||
|
||||
public static final class InfoSubRecord implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 8;
|
||||
public static final int ENCODED_SIZE = 8;
|
||||
private int field_1_stream_pos; // stream pointer to the SST record
|
||||
private int field_2_bucket_sst_offset; // don't really understand this yet.
|
||||
/** unused - supposed to be zero */
|
||||
@ -101,7 +101,7 @@ public final class ExtSSTRecord extends ContinuableRecord {
|
||||
|
||||
|
||||
public ExtSSTRecord() {
|
||||
_stringsPerBucket = DEFAULT_BUCKET_SIZE;
|
||||
_stringsPerBucket = DEFAULT_BUCKET_SIZE;
|
||||
_sstInfos = new InfoSubRecord[0];
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ public final class ExtSSTRecord extends ContinuableRecord {
|
||||
}
|
||||
|
||||
int getDataSize() {
|
||||
return 2 + InfoSubRecord.ENCODED_SIZE*_sstInfos.length;
|
||||
return 2 + InfoSubRecord.ENCODED_SIZE*_sstInfos.length;
|
||||
}
|
||||
|
||||
InfoSubRecord[] getInfoSubRecords() {
|
||||
@ -174,7 +174,7 @@ public final class ExtSSTRecord extends ContinuableRecord {
|
||||
}
|
||||
|
||||
public void setBucketOffsets(int[] bucketAbsoluteOffsets, int[] bucketRelativeOffsets) {
|
||||
// TODO - replace no-arg constructor with this logic
|
||||
// TODO - replace no-arg constructor with this logic
|
||||
_sstInfos = new InfoSubRecord[bucketAbsoluteOffsets.length];
|
||||
for (int i = 0; i < bucketAbsoluteOffsets.length; i++) {
|
||||
_sstInfos[i] = new InfoSubRecord(bucketAbsoluteOffsets[i], bucketRelativeOffsets[i]);
|
||||
|
||||
@ -1699,7 +1699,7 @@ public final class ExtendedFormatRecord extends StandardRecord {
|
||||
field_9_fill_palette_options = source.field_9_fill_palette_options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
field_1_font_index
|
||||
@ -1712,30 +1712,30 @@ public final class ExtendedFormatRecord extends StandardRecord {
|
||||
, field_8_adtl_palette_options
|
||||
, field_9_fill_palette_options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will consider two different records with the same
|
||||
* contents as equals, as the various indexes
|
||||
* that matter are embedded in the records
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Will consider two different records with the same
|
||||
* contents as equals, as the various indexes
|
||||
* that matter are embedded in the records
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj instanceof ExtendedFormatRecord) {
|
||||
final ExtendedFormatRecord other = (ExtendedFormatRecord) obj;
|
||||
return Arrays.equals(stateSummary(), other.stateSummary());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj instanceof ExtendedFormatRecord) {
|
||||
final ExtendedFormatRecord other = (ExtendedFormatRecord) obj;
|
||||
return Arrays.equals(stateSummary(), other.stateSummary());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int[] stateSummary() {
|
||||
return new int[] { field_1_font_index, field_2_format_index, field_3_cell_options, field_4_alignment_options,
|
||||
field_5_indention_options, field_6_border_options, field_7_palette_options, field_8_adtl_palette_options, field_9_fill_palette_options };
|
||||
}
|
||||
public int[] stateSummary() {
|
||||
return new int[] { field_1_font_index, field_2_format_index, field_3_cell_options, field_4_alignment_options,
|
||||
field_5_indention_options, field_6_border_options, field_7_palette_options, field_8_adtl_palette_options, field_9_fill_palette_options };
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@ -34,146 +34,146 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
public class ExternSheetRecord extends StandardRecord {
|
||||
|
||||
public static final short sid = 0x0017;
|
||||
private final List<RefSubRecord> _list = new ArrayList<>();
|
||||
private final List<RefSubRecord> _list = new ArrayList<>();
|
||||
|
||||
private static final class RefSubRecord implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 6;
|
||||
private static final class RefSubRecord implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 6;
|
||||
|
||||
/** index to External Book Block (which starts with a EXTERNALBOOK record) */
|
||||
private final int _extBookIndex;
|
||||
private int _firstSheetIndex; // may be -1 (0xFFFF)
|
||||
private int _lastSheetIndex; // may be -1 (0xFFFF)
|
||||
/** index to External Book Block (which starts with a EXTERNALBOOK record) */
|
||||
private final int _extBookIndex;
|
||||
private int _firstSheetIndex; // may be -1 (0xFFFF)
|
||||
private int _lastSheetIndex; // may be -1 (0xFFFF)
|
||||
|
||||
public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
_extBookIndex = extBookIndex;
|
||||
_firstSheetIndex = firstSheetIndex;
|
||||
_lastSheetIndex = lastSheetIndex;
|
||||
}
|
||||
public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
_extBookIndex = extBookIndex;
|
||||
_firstSheetIndex = firstSheetIndex;
|
||||
_lastSheetIndex = lastSheetIndex;
|
||||
}
|
||||
|
||||
public RefSubRecord(RefSubRecord other) {
|
||||
_extBookIndex = other._extBookIndex;
|
||||
_firstSheetIndex = other._firstSheetIndex;
|
||||
_lastSheetIndex = other._lastSheetIndex;
|
||||
}
|
||||
public RefSubRecord(RefSubRecord other) {
|
||||
_extBookIndex = other._extBookIndex;
|
||||
_firstSheetIndex = other._firstSheetIndex;
|
||||
_lastSheetIndex = other._lastSheetIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public RefSubRecord(RecordInputStream in) {
|
||||
this(in.readShort(), in.readShort(), in.readShort());
|
||||
}
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public RefSubRecord(RecordInputStream in) {
|
||||
this(in.readShort(), in.readShort(), in.readShort());
|
||||
}
|
||||
|
||||
public int getExtBookIndex(){
|
||||
return _extBookIndex;
|
||||
}
|
||||
public int getFirstSheetIndex(){
|
||||
return _firstSheetIndex;
|
||||
}
|
||||
public int getLastSheetIndex(){
|
||||
return _lastSheetIndex;
|
||||
}
|
||||
public int getExtBookIndex(){
|
||||
return _extBookIndex;
|
||||
}
|
||||
public int getFirstSheetIndex(){
|
||||
return _firstSheetIndex;
|
||||
}
|
||||
public int getLastSheetIndex(){
|
||||
return _lastSheetIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return GenericRecordJsonWriter.marshal(this);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return GenericRecordJsonWriter.marshal(this);
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_extBookIndex);
|
||||
out.writeShort(_firstSheetIndex);
|
||||
out.writeShort(_lastSheetIndex);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_extBookIndex);
|
||||
out.writeShort(_firstSheetIndex);
|
||||
out.writeShort(_lastSheetIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"extBookIndex", this::getExtBookIndex,
|
||||
"firstSheetIndex", this::getFirstSheetIndex,
|
||||
"lastSheetIndex", this::getLastSheetIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"extBookIndex", this::getExtBookIndex,
|
||||
"firstSheetIndex", this::getFirstSheetIndex,
|
||||
"lastSheetIndex", this::getLastSheetIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public ExternSheetRecord() {}
|
||||
public ExternSheetRecord() {}
|
||||
|
||||
public ExternSheetRecord(ExternSheetRecord other) {
|
||||
other._list.stream().map(RefSubRecord::new).forEach(_list::add);
|
||||
}
|
||||
public ExternSheetRecord(ExternSheetRecord other) {
|
||||
other._list.stream().map(RefSubRecord::new).forEach(_list::add);
|
||||
}
|
||||
|
||||
public ExternSheetRecord(RecordInputStream in) {
|
||||
int nItems = in.readShort();
|
||||
public ExternSheetRecord(RecordInputStream in) {
|
||||
int nItems = in.readShort();
|
||||
|
||||
for (int i = 0 ; i < nItems ; ++i) {
|
||||
RefSubRecord rec = new RefSubRecord(in);
|
||||
_list.add(rec);
|
||||
}
|
||||
}
|
||||
for (int i = 0 ; i < nItems ; ++i) {
|
||||
RefSubRecord rec = new RefSubRecord(in);
|
||||
_list.add(rec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return number of REF structures
|
||||
*/
|
||||
public int getNumOfRefs() {
|
||||
return _list.size();
|
||||
}
|
||||
/**
|
||||
* @return number of REF structures
|
||||
*/
|
||||
public int getNumOfRefs() {
|
||||
return _list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* adds REF struct (ExternSheetSubRecord)
|
||||
* @param rec REF struct
|
||||
*/
|
||||
public void addREFRecord(RefSubRecord rec) {
|
||||
_list.add(rec);
|
||||
}
|
||||
/**
|
||||
* adds REF struct (ExternSheetSubRecord)
|
||||
* @param rec REF struct
|
||||
*/
|
||||
public void addREFRecord(RefSubRecord rec) {
|
||||
_list.add(rec);
|
||||
}
|
||||
|
||||
/** returns the number of REF Records, which is in model
|
||||
* @return number of REF records
|
||||
*/
|
||||
public int getNumOfREFRecords() {
|
||||
return _list.size();
|
||||
}
|
||||
/** returns the number of REF Records, which is in model
|
||||
* @return number of REF records
|
||||
*/
|
||||
public int getNumOfREFRecords() {
|
||||
return _list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 2 + _list.size() * RefSubRecord.ENCODED_SIZE;
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 2 + _list.size() * RefSubRecord.ENCODED_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
int nItems = _list.size();
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
int nItems = _list.size();
|
||||
|
||||
out.writeShort(nItems);
|
||||
out.writeShort(nItems);
|
||||
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
getRef(i).serialize(out);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
getRef(i).serialize(out);
|
||||
}
|
||||
}
|
||||
|
||||
private RefSubRecord getRef(int i) {
|
||||
return _list.get(i);
|
||||
}
|
||||
private RefSubRecord getRef(int i) {
|
||||
return _list.get(i);
|
||||
}
|
||||
|
||||
public void removeSheet(int sheetIdx) {
|
||||
public void removeSheet(int sheetIdx) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
RefSubRecord refSubRecord = _list.get(i);
|
||||
if(refSubRecord.getFirstSheetIndex() == sheetIdx &&
|
||||
refSubRecord.getLastSheetIndex() == sheetIdx) {
|
||||
// removing the entry would mess up the sheet index in Formula of NameRecord
|
||||
_list.set(i, new RefSubRecord(refSubRecord.getExtBookIndex(), -1, -1));
|
||||
// removing the entry would mess up the sheet index in Formula of NameRecord
|
||||
_list.set(i, new RefSubRecord(refSubRecord.getExtBookIndex(), -1, -1));
|
||||
} else if (refSubRecord.getFirstSheetIndex() > sheetIdx &&
|
||||
refSubRecord.getLastSheetIndex() > sheetIdx) {
|
||||
_list.set(i, new RefSubRecord(refSubRecord.getExtBookIndex(), refSubRecord.getFirstSheetIndex()-1, refSubRecord.getLastSheetIndex()-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
/**
|
||||
* return the non static version of the id for this record.
|
||||
*/
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refIndex specifies the n-th refIndex
|
||||
@ -185,20 +185,20 @@ public class ExternSheetRecord extends StandardRecord {
|
||||
return refRec.getExtBookIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extBookIndex external sheet reference index
|
||||
*
|
||||
* @return -1 if not found
|
||||
*/
|
||||
public int findRefIndexFromExtBookIndex(int extBookIndex) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
if (getRef(i).getExtBookIndex() == extBookIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* @param extBookIndex external sheet reference index
|
||||
*
|
||||
* @return -1 if not found
|
||||
*/
|
||||
public int findRefIndexFromExtBookIndex(int extBookIndex) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
if (getRef(i).getExtBookIndex() == extBookIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first sheet that the reference applies to, or
|
||||
@ -211,9 +211,9 @@ public class ExternSheetRecord extends StandardRecord {
|
||||
* -1 if the referenced sheet can't be found, or -2 if the
|
||||
* reference is workbook scoped
|
||||
*/
|
||||
public int getFirstSheetIndexFromRefIndex(int extRefIndex) {
|
||||
return getRef(extRefIndex).getFirstSheetIndex();
|
||||
}
|
||||
public int getFirstSheetIndexFromRefIndex(int extRefIndex) {
|
||||
return getRef(extRefIndex).getFirstSheetIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last sheet that the reference applies to, or
|
||||
@ -232,7 +232,7 @@ public class ExternSheetRecord extends StandardRecord {
|
||||
return getRef(extRefIndex).getLastSheetIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Add a zero-based reference to a {@link org.apache.poi.hssf.record.SupBookRecord}.
|
||||
* <p>
|
||||
* If the type of the SupBook record is same-sheet referencing, Add-In referencing,
|
||||
@ -259,51 +259,51 @@ public class ExternSheetRecord extends StandardRecord {
|
||||
* @param extBookIndex the external book block index
|
||||
* @param firstSheetIndex the scope, must be -2 for add-in references
|
||||
* @param lastSheetIndex the scope, must be -2 for add-in references
|
||||
* @return index of newly added ref
|
||||
*/
|
||||
public int addRef(int extBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
_list.add(new RefSubRecord(extBookIndex, firstSheetIndex, lastSheetIndex));
|
||||
return _list.size() - 1;
|
||||
}
|
||||
* @return index of newly added ref
|
||||
*/
|
||||
public int addRef(int extBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
_list.add(new RefSubRecord(extBookIndex, firstSheetIndex, lastSheetIndex));
|
||||
return _list.size() - 1;
|
||||
}
|
||||
|
||||
public int getRefIxForSheet(int externalBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
RefSubRecord ref = getRef(i);
|
||||
if (ref.getExtBookIndex() != externalBookIndex) {
|
||||
continue;
|
||||
}
|
||||
if (ref.getFirstSheetIndex() == firstSheetIndex &&
|
||||
ref.getLastSheetIndex() == lastSheetIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public int getRefIxForSheet(int externalBookIndex, int firstSheetIndex, int lastSheetIndex) {
|
||||
int nItems = _list.size();
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
RefSubRecord ref = getRef(i);
|
||||
if (ref.getExtBookIndex() != externalBookIndex) {
|
||||
continue;
|
||||
}
|
||||
if (ref.getFirstSheetIndex() == firstSheetIndex &&
|
||||
ref.getLastSheetIndex() == lastSheetIndex) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static ExternSheetRecord combine(ExternSheetRecord[] esrs) {
|
||||
ExternSheetRecord result = new ExternSheetRecord();
|
||||
for (ExternSheetRecord esr : esrs) {
|
||||
int nRefs = esr.getNumOfREFRecords();
|
||||
for (int j=0; j<nRefs; j++) {
|
||||
result.addREFRecord(esr.getRef(j));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static ExternSheetRecord combine(ExternSheetRecord[] esrs) {
|
||||
ExternSheetRecord result = new ExternSheetRecord();
|
||||
for (ExternSheetRecord esr : esrs) {
|
||||
int nRefs = esr.getNumOfREFRecords();
|
||||
for (int j=0; j<nRefs; j++) {
|
||||
result.addREFRecord(esr.getRef(j));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternSheetRecord copy() {
|
||||
return new ExternSheetRecord(this);
|
||||
}
|
||||
@Override
|
||||
public ExternSheetRecord copy() {
|
||||
return new ExternSheetRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.EXTERN_SHEET;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.EXTERN_SHEET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("refrec", () -> _list);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("refrec", () -> _list);
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,150 +32,150 @@ import org.apache.poi.util.StringUtil;
|
||||
*/
|
||||
public final class ExternalNameRecord extends StandardRecord {
|
||||
|
||||
public static final short sid = 0x0023; // as per BIFF8. (some old versions used 0x223)
|
||||
public static final short sid = 0x0023; // as per BIFF8. (some old versions used 0x223)
|
||||
|
||||
private static final int OPT_BUILTIN_NAME = 0x0001;
|
||||
private static final int OPT_AUTOMATIC_LINK = 0x0002; // m$ doc calls this fWantAdvise
|
||||
private static final int OPT_PICTURE_LINK = 0x0004;
|
||||
private static final int OPT_STD_DOCUMENT_NAME = 0x0008; //fOle
|
||||
private static final int OPT_OLE_LINK = 0x0010; //fOleLink
|
||||
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
|
||||
private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;
|
||||
private static final int OPT_BUILTIN_NAME = 0x0001;
|
||||
private static final int OPT_AUTOMATIC_LINK = 0x0002; // m$ doc calls this fWantAdvise
|
||||
private static final int OPT_PICTURE_LINK = 0x0004;
|
||||
private static final int OPT_STD_DOCUMENT_NAME = 0x0008; //fOle
|
||||
private static final int OPT_OLE_LINK = 0x0010; //fOleLink
|
||||
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
|
||||
private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;
|
||||
|
||||
private static final int[] OPTION_FLAGS = {
|
||||
OPT_BUILTIN_NAME,OPT_AUTOMATIC_LINK,OPT_PICTURE_LINK,OPT_STD_DOCUMENT_NAME,OPT_OLE_LINK,OPT_ICONIFIED_PICTURE_LINK};
|
||||
private static final String[] OPTION_NAMES = {
|
||||
"BUILTIN_NAME","AUTOMATIC_LINK","PICTURE_LINK","STD_DOCUMENT_NAME","OLE_LINK","ICONIFIED_PICTURE_LINK"};
|
||||
private static final int[] OPTION_FLAGS = {
|
||||
OPT_BUILTIN_NAME,OPT_AUTOMATIC_LINK,OPT_PICTURE_LINK,OPT_STD_DOCUMENT_NAME,OPT_OLE_LINK,OPT_ICONIFIED_PICTURE_LINK};
|
||||
private static final String[] OPTION_NAMES = {
|
||||
"BUILTIN_NAME","AUTOMATIC_LINK","PICTURE_LINK","STD_DOCUMENT_NAME","OLE_LINK","ICONIFIED_PICTURE_LINK"};
|
||||
|
||||
|
||||
|
||||
private short field_1_option_flag;
|
||||
private short field_2_ixals;
|
||||
private short field_3_not_used;
|
||||
private String field_4_name;
|
||||
private Formula field_5_name_definition;
|
||||
private short field_1_option_flag;
|
||||
private short field_2_ixals;
|
||||
private short field_3_not_used;
|
||||
private String field_4_name;
|
||||
private Formula field_5_name_definition;
|
||||
|
||||
/**
|
||||
* 'rgoper' / 'Last received results of the DDE link'
|
||||
* (seems to be only applicable to DDE links)<br>
|
||||
* Logically this is a 2-D array, which has been flattened into 1-D array here.
|
||||
*/
|
||||
private Object[] _ddeValues;
|
||||
/**
|
||||
* (logical) number of columns in the {@link #_ddeValues} array
|
||||
*/
|
||||
private int _nColumns;
|
||||
/**
|
||||
* (logical) number of rows in the {@link #_ddeValues} array
|
||||
*/
|
||||
private int _nRows;
|
||||
/**
|
||||
* 'rgoper' / 'Last received results of the DDE link'
|
||||
* (seems to be only applicable to DDE links)<br>
|
||||
* Logically this is a 2-D array, which has been flattened into 1-D array here.
|
||||
*/
|
||||
private Object[] _ddeValues;
|
||||
/**
|
||||
* (logical) number of columns in the {@link #_ddeValues} array
|
||||
*/
|
||||
private int _nColumns;
|
||||
/**
|
||||
* (logical) number of rows in the {@link #_ddeValues} array
|
||||
*/
|
||||
private int _nRows;
|
||||
|
||||
public ExternalNameRecord() {
|
||||
field_2_ixals = 0;
|
||||
}
|
||||
public ExternalNameRecord() {
|
||||
field_2_ixals = 0;
|
||||
}
|
||||
|
||||
public ExternalNameRecord(ExternalNameRecord other) {
|
||||
super(other);
|
||||
field_1_option_flag = other.field_1_option_flag;
|
||||
field_2_ixals = other.field_2_ixals;
|
||||
field_3_not_used = other.field_3_not_used;
|
||||
field_4_name = other.field_4_name;
|
||||
field_5_name_definition = (other.field_5_name_definition == null) ? null : other.field_5_name_definition.copy();
|
||||
_ddeValues = (other._ddeValues == null) ? null : other._ddeValues.clone();
|
||||
_nColumns = other._nColumns;
|
||||
_nRows = other._nRows;
|
||||
}
|
||||
public ExternalNameRecord(ExternalNameRecord other) {
|
||||
super(other);
|
||||
field_1_option_flag = other.field_1_option_flag;
|
||||
field_2_ixals = other.field_2_ixals;
|
||||
field_3_not_used = other.field_3_not_used;
|
||||
field_4_name = other.field_4_name;
|
||||
field_5_name_definition = (other.field_5_name_definition == null) ? null : other.field_5_name_definition.copy();
|
||||
_ddeValues = (other._ddeValues == null) ? null : other._ddeValues.clone();
|
||||
_nColumns = other._nColumns;
|
||||
_nRows = other._nRows;
|
||||
}
|
||||
|
||||
public ExternalNameRecord(RecordInputStream in) {
|
||||
field_1_option_flag = in.readShort();
|
||||
field_2_ixals = in.readShort();
|
||||
field_3_not_used = in.readShort();
|
||||
public ExternalNameRecord(RecordInputStream in) {
|
||||
field_1_option_flag = in.readShort();
|
||||
field_2_ixals = in.readShort();
|
||||
field_3_not_used = in.readShort();
|
||||
|
||||
int numChars = in.readUByte();
|
||||
field_4_name = StringUtil.readUnicodeString(in, numChars);
|
||||
int numChars = in.readUByte();
|
||||
field_4_name = StringUtil.readUnicodeString(in, numChars);
|
||||
|
||||
// the record body can take different forms.
|
||||
// The form is dictated by the values of 3-th and 4-th bits in field_1_option_flag
|
||||
if(!isOLELink() && !isStdDocumentNameIdentifier()){
|
||||
// another switch: the fWantAdvise bit specifies whether the body describes
|
||||
// an external defined name or a DDE data item
|
||||
if(isAutomaticLink()){
|
||||
if(in.available() > 0) {
|
||||
//body specifies DDE data item
|
||||
int nColumns = in.readUByte() + 1;
|
||||
int nRows = in.readShort() + 1;
|
||||
// the record body can take different forms.
|
||||
// The form is dictated by the values of 3-th and 4-th bits in field_1_option_flag
|
||||
if(!isOLELink() && !isStdDocumentNameIdentifier()){
|
||||
// another switch: the fWantAdvise bit specifies whether the body describes
|
||||
// an external defined name or a DDE data item
|
||||
if(isAutomaticLink()){
|
||||
if(in.available() > 0) {
|
||||
//body specifies DDE data item
|
||||
int nColumns = in.readUByte() + 1;
|
||||
int nRows = in.readShort() + 1;
|
||||
|
||||
int totalCount = nRows * nColumns;
|
||||
_ddeValues = ConstantValueParser.parse(in, totalCount);
|
||||
_nColumns = nColumns;
|
||||
_nRows = nRows;
|
||||
}
|
||||
} else {
|
||||
//body specifies an external defined name
|
||||
int formulaLen = in.readUShort();
|
||||
field_5_name_definition = Formula.read(formulaLen, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
int totalCount = nRows * nColumns;
|
||||
_ddeValues = ConstantValueParser.parse(in, totalCount);
|
||||
_nColumns = nColumns;
|
||||
_nRows = nRows;
|
||||
}
|
||||
} else {
|
||||
//body specifies an external defined name
|
||||
int formulaLen = in.readUShort();
|
||||
field_5_name_definition = Formula.read(formulaLen, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the name is a built-in name
|
||||
*/
|
||||
public boolean isBuiltInName() {
|
||||
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
|
||||
}
|
||||
/**
|
||||
* For OLE and DDE, links can be either 'automatic' or 'manual'
|
||||
*
|
||||
* @return {@code true} if this is a automatic link
|
||||
*/
|
||||
public boolean isAutomaticLink() {
|
||||
return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* only for OLE and DDE
|
||||
*
|
||||
* @return {@code true} if this is a picture link
|
||||
*/
|
||||
public boolean isPicureLink() {
|
||||
return (field_1_option_flag & OPT_PICTURE_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* DDE links only. If <code>true</code>, this denotes the 'StdDocumentName'
|
||||
*
|
||||
* @return {@code true} if this denotes the 'StdDocumentName'
|
||||
*/
|
||||
public boolean isStdDocumentNameIdentifier() {
|
||||
return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
|
||||
}
|
||||
public boolean isOLELink() {
|
||||
return (field_1_option_flag & OPT_OLE_LINK) != 0;
|
||||
}
|
||||
public boolean isIconifiedPictureLink() {
|
||||
return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* @return the standard String representation of this name
|
||||
*/
|
||||
public String getText() {
|
||||
return field_4_name;
|
||||
}
|
||||
/**
|
||||
* @return {@code true} if the name is a built-in name
|
||||
*/
|
||||
public boolean isBuiltInName() {
|
||||
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
|
||||
}
|
||||
/**
|
||||
* For OLE and DDE, links can be either 'automatic' or 'manual'
|
||||
*
|
||||
* @return {@code true} if this is a automatic link
|
||||
*/
|
||||
public boolean isAutomaticLink() {
|
||||
return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* only for OLE and DDE
|
||||
*
|
||||
* @return {@code true} if this is a picture link
|
||||
*/
|
||||
public boolean isPicureLink() {
|
||||
return (field_1_option_flag & OPT_PICTURE_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* DDE links only. If <code>true</code>, this denotes the 'StdDocumentName'
|
||||
*
|
||||
* @return {@code true} if this denotes the 'StdDocumentName'
|
||||
*/
|
||||
public boolean isStdDocumentNameIdentifier() {
|
||||
return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
|
||||
}
|
||||
public boolean isOLELink() {
|
||||
return (field_1_option_flag & OPT_OLE_LINK) != 0;
|
||||
}
|
||||
public boolean isIconifiedPictureLink() {
|
||||
return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
|
||||
}
|
||||
/**
|
||||
* @return the standard String representation of this name
|
||||
*/
|
||||
public String getText() {
|
||||
return field_4_name;
|
||||
}
|
||||
|
||||
public void setText(String str) {
|
||||
field_4_name = str;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a local name, then this is the (1 based)
|
||||
* index of the name of the Sheet this refers to, as
|
||||
* defined in the preceding {@link SupBookRecord}.
|
||||
* If it isn't a local name, then it must be zero.
|
||||
*
|
||||
* @return the index of the name of the Sheet this refers to
|
||||
*/
|
||||
public short getIx() {
|
||||
return field_2_ixals;
|
||||
}
|
||||
/**
|
||||
* If this is a local name, then this is the (1 based)
|
||||
* index of the name of the Sheet this refers to, as
|
||||
* defined in the preceding {@link SupBookRecord}.
|
||||
* If it isn't a local name, then it must be zero.
|
||||
*
|
||||
* @return the index of the name of the Sheet this refers to
|
||||
*/
|
||||
public short getIx() {
|
||||
return field_2_ixals;
|
||||
}
|
||||
|
||||
public void setIx(short ix) {
|
||||
field_2_ixals = ix;
|
||||
@ -189,69 +189,69 @@ public final class ExternalNameRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDataSize(){
|
||||
int result = 2 + 4; // short and int
|
||||
@Override
|
||||
protected int getDataSize(){
|
||||
int result = 2 + 4; // short and int
|
||||
result += StringUtil.getEncodedSize(field_4_name) - 1; //size is byte, not short
|
||||
|
||||
if(!isOLELink() && !isStdDocumentNameIdentifier()){
|
||||
if(isAutomaticLink()){
|
||||
if(_ddeValues != null) {
|
||||
if(_ddeValues != null) {
|
||||
result += 3; // byte, short
|
||||
result += ConstantValueParser.getEncodedSize(_ddeValues);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += field_5_name_definition.getEncodedSize();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_option_flag);
|
||||
out.writeShort(field_2_ixals);
|
||||
out.writeShort(field_3_not_used);
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_option_flag);
|
||||
out.writeShort(field_2_ixals);
|
||||
out.writeShort(field_3_not_used);
|
||||
|
||||
out.writeByte(field_4_name.length());
|
||||
StringUtil.writeUnicodeStringFlagAndData(out, field_4_name);
|
||||
out.writeByte(field_4_name.length());
|
||||
StringUtil.writeUnicodeStringFlagAndData(out, field_4_name);
|
||||
|
||||
if(!isOLELink() && !isStdDocumentNameIdentifier()){
|
||||
if(isAutomaticLink()){
|
||||
if(_ddeValues != null) {
|
||||
if(_ddeValues != null) {
|
||||
out.writeByte(_nColumns-1);
|
||||
out.writeShort(_nRows-1);
|
||||
ConstantValueParser.encode(out, _ddeValues);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field_5_name_definition.serialize(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExternalNameRecord copy() {
|
||||
return new ExternalNameRecord(this);
|
||||
}
|
||||
@Override
|
||||
public ExternalNameRecord copy() {
|
||||
return new ExternalNameRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.EXTERNAL_NAME;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.EXTERNAL_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"options", GenericRecordUtil.getBitsAsString(() -> field_1_option_flag, OPTION_FLAGS, OPTION_NAMES),
|
||||
"ix", this::getIx,
|
||||
"name", this::getText,
|
||||
"nameDefinition", (field_5_name_definition == null ? () -> null : field_5_name_definition::getTokens)
|
||||
);
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"options", GenericRecordUtil.getBitsAsString(() -> field_1_option_flag, OPTION_FLAGS, OPTION_NAMES),
|
||||
"ix", this::getIx,
|
||||
"name", this::getText,
|
||||
"nameDefinition", (field_5_name_definition == null ? () -> null : field_5_name_definition::getTokens)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,103 +33,103 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* ABNF or worksheet substream ABNF) specifies Shared Feature data.
|
||||
*/
|
||||
public final class FeatHdrRecord extends StandardRecord {
|
||||
/**
|
||||
* Specifies the enhanced protection type. Used to protect a
|
||||
* shared workbook by restricting access to some areas of it
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFPROTECTION = 0x02;
|
||||
/**
|
||||
* Specifies that formula errors should be ignored
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFFEC2 = 0x03;
|
||||
/**
|
||||
* Specifies the smart tag type. Recognises certain
|
||||
* types of entries (proper names, dates/times etc) and
|
||||
* flags them for action
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFFACTOID = 0x04;
|
||||
/**
|
||||
* Specifies the shared list type. Used for a table
|
||||
* within a sheet
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFLIST = 0x05;
|
||||
/**
|
||||
* Specifies the enhanced protection type. Used to protect a
|
||||
* shared workbook by restricting access to some areas of it
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFPROTECTION = 0x02;
|
||||
/**
|
||||
* Specifies that formula errors should be ignored
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFFEC2 = 0x03;
|
||||
/**
|
||||
* Specifies the smart tag type. Recognises certain
|
||||
* types of entries (proper names, dates/times etc) and
|
||||
* flags them for action
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFFACTOID = 0x04;
|
||||
/**
|
||||
* Specifies the shared list type. Used for a table
|
||||
* within a sheet
|
||||
*/
|
||||
public static final int SHAREDFEATURES_ISFLIST = 0x05;
|
||||
|
||||
|
||||
public static final short sid = 0x0867;
|
||||
public static final short sid = 0x0867;
|
||||
|
||||
private final FtrHeader futureHeader;
|
||||
// See SHAREDFEATURES
|
||||
private int isf_sharedFeatureType;
|
||||
// Should always be one
|
||||
private byte reserved;
|
||||
/**
|
||||
* 0x00000000 = rgbHdrData not present
|
||||
* 0xffffffff = rgbHdrData present
|
||||
*/
|
||||
private long cbHdrData;
|
||||
/** We need a BOFRecord to make sense of this... */
|
||||
private byte[] rgbHdrData;
|
||||
private final FtrHeader futureHeader;
|
||||
// See SHAREDFEATURES
|
||||
private int isf_sharedFeatureType;
|
||||
// Should always be one
|
||||
private byte reserved;
|
||||
/**
|
||||
* 0x00000000 = rgbHdrData not present
|
||||
* 0xffffffff = rgbHdrData present
|
||||
*/
|
||||
private long cbHdrData;
|
||||
/** We need a BOFRecord to make sense of this... */
|
||||
private byte[] rgbHdrData;
|
||||
|
||||
public FeatHdrRecord() {
|
||||
futureHeader = new FtrHeader();
|
||||
futureHeader.setRecordType(sid);
|
||||
}
|
||||
public FeatHdrRecord() {
|
||||
futureHeader = new FtrHeader();
|
||||
futureHeader.setRecordType(sid);
|
||||
}
|
||||
|
||||
public FeatHdrRecord(FeatHdrRecord other) {
|
||||
super(other);
|
||||
futureHeader = other.futureHeader.copy();
|
||||
isf_sharedFeatureType = other.isf_sharedFeatureType;
|
||||
reserved = other.reserved;
|
||||
cbHdrData = other.cbHdrData;
|
||||
rgbHdrData = (other.rgbHdrData == null) ? null : other.rgbHdrData.clone();
|
||||
}
|
||||
public FeatHdrRecord(FeatHdrRecord other) {
|
||||
super(other);
|
||||
futureHeader = other.futureHeader.copy();
|
||||
isf_sharedFeatureType = other.isf_sharedFeatureType;
|
||||
reserved = other.reserved;
|
||||
cbHdrData = other.cbHdrData;
|
||||
rgbHdrData = (other.rgbHdrData == null) ? null : other.rgbHdrData.clone();
|
||||
}
|
||||
|
||||
public FeatHdrRecord(RecordInputStream in) {
|
||||
futureHeader = new FtrHeader(in);
|
||||
public FeatHdrRecord(RecordInputStream in) {
|
||||
futureHeader = new FtrHeader(in);
|
||||
|
||||
isf_sharedFeatureType = in.readShort();
|
||||
reserved = in.readByte();
|
||||
cbHdrData = in.readInt();
|
||||
// Don't process this just yet, need the BOFRecord
|
||||
rgbHdrData = in.readRemainder();
|
||||
}
|
||||
isf_sharedFeatureType = in.readShort();
|
||||
reserved = in.readByte();
|
||||
cbHdrData = in.readInt();
|
||||
// Don't process this just yet, need the BOFRecord
|
||||
rgbHdrData = in.readRemainder();
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
futureHeader.serialize(out);
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
futureHeader.serialize(out);
|
||||
|
||||
out.writeShort(isf_sharedFeatureType);
|
||||
out.writeByte(reserved);
|
||||
out.writeInt((int)cbHdrData);
|
||||
out.write(rgbHdrData);
|
||||
}
|
||||
out.writeShort(isf_sharedFeatureType);
|
||||
out.writeByte(reserved);
|
||||
out.writeInt((int)cbHdrData);
|
||||
out.write(rgbHdrData);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 12 + 2+1+4+rgbHdrData.length;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 12 + 2+1+4+rgbHdrData.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public FeatHdrRecord copy() {
|
||||
//HACK: do a "cheat" clone, see Record.java for more information
|
||||
//HACK: do a "cheat" clone, see Record.java for more information
|
||||
return new FeatHdrRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FEAT_HDR;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FEAT_HDR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"futureHeader", () -> futureHeader,
|
||||
"isf_sharedFeatureType", () -> isf_sharedFeatureType,
|
||||
"reserved", () -> reserved,
|
||||
"cbHdrData", () -> cbHdrData,
|
||||
"rgbHdrData", () -> rgbHdrData
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"futureHeader", () -> futureHeader,
|
||||
"isf_sharedFeatureType", () -> isf_sharedFeatureType,
|
||||
"reserved", () -> reserved,
|
||||
"cbHdrData", () -> cbHdrData,
|
||||
"rgbHdrData", () -> rgbHdrData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,167 +42,167 @@ import static org.apache.logging.log4j.util.Unbox.box;
|
||||
*/
|
||||
public final class FeatRecord extends StandardRecord {
|
||||
private static final Logger LOG = LogManager.getLogger(FeatRecord.class);
|
||||
public static final short sid = 0x0868;
|
||||
public static final short sid = 0x0868;
|
||||
// SIDs from newer versions
|
||||
public static final short v11_sid = 0x0872;
|
||||
public static final short v12_sid = 0x0878;
|
||||
|
||||
private final FtrHeader futureHeader;
|
||||
private final FtrHeader futureHeader;
|
||||
|
||||
/** See SHAREDFEATURES_* on {@link FeatHdrRecord} */
|
||||
private int isf_sharedFeatureType;
|
||||
private byte reserved1; // Should always be zero
|
||||
private long reserved2; // Should always be zero
|
||||
/** Only matters if type is ISFFEC2 */
|
||||
private long cbFeatData;
|
||||
private int reserved3; // Should always be zero
|
||||
private CellRangeAddress[] cellRefs;
|
||||
/** See SHAREDFEATURES_* on {@link FeatHdrRecord} */
|
||||
private int isf_sharedFeatureType;
|
||||
private byte reserved1; // Should always be zero
|
||||
private long reserved2; // Should always be zero
|
||||
/** Only matters if type is ISFFEC2 */
|
||||
private long cbFeatData;
|
||||
private int reserved3; // Should always be zero
|
||||
private CellRangeAddress[] cellRefs;
|
||||
|
||||
/**
|
||||
* Contents depends on isf_sharedFeatureType :
|
||||
* ISFPROTECTION -> FeatProtection
|
||||
* ISFFEC2 -> FeatFormulaErr2
|
||||
* ISFFACTOID -> FeatSmartTag
|
||||
*/
|
||||
private SharedFeature sharedFeature;
|
||||
/**
|
||||
* Contents depends on isf_sharedFeatureType :
|
||||
* ISFPROTECTION -> FeatProtection
|
||||
* ISFFEC2 -> FeatFormulaErr2
|
||||
* ISFFACTOID -> FeatSmartTag
|
||||
*/
|
||||
private SharedFeature sharedFeature;
|
||||
|
||||
public FeatRecord() {
|
||||
futureHeader = new FtrHeader();
|
||||
futureHeader.setRecordType(sid);
|
||||
}
|
||||
public FeatRecord() {
|
||||
futureHeader = new FtrHeader();
|
||||
futureHeader.setRecordType(sid);
|
||||
}
|
||||
|
||||
public FeatRecord(FeatRecord other) {
|
||||
super(other);
|
||||
futureHeader = other.futureHeader.copy();
|
||||
isf_sharedFeatureType = other.isf_sharedFeatureType;
|
||||
reserved1 = other.reserved1;
|
||||
reserved2 = other.reserved2;
|
||||
cbFeatData = other.cbFeatData;
|
||||
reserved3 = other.reserved3;
|
||||
cellRefs = (other.cellRefs == null) ? null :
|
||||
Stream.of(other.cellRefs).map(CellRangeAddress::copy).toArray(CellRangeAddress[]::new);
|
||||
sharedFeature = (other.sharedFeature == null) ? null : other.sharedFeature.copy();
|
||||
}
|
||||
public FeatRecord(FeatRecord other) {
|
||||
super(other);
|
||||
futureHeader = other.futureHeader.copy();
|
||||
isf_sharedFeatureType = other.isf_sharedFeatureType;
|
||||
reserved1 = other.reserved1;
|
||||
reserved2 = other.reserved2;
|
||||
cbFeatData = other.cbFeatData;
|
||||
reserved3 = other.reserved3;
|
||||
cellRefs = (other.cellRefs == null) ? null :
|
||||
Stream.of(other.cellRefs).map(CellRangeAddress::copy).toArray(CellRangeAddress[]::new);
|
||||
sharedFeature = (other.sharedFeature == null) ? null : other.sharedFeature.copy();
|
||||
}
|
||||
|
||||
public FeatRecord(RecordInputStream in) {
|
||||
futureHeader = new FtrHeader(in);
|
||||
public FeatRecord(RecordInputStream in) {
|
||||
futureHeader = new FtrHeader(in);
|
||||
|
||||
isf_sharedFeatureType = in.readShort();
|
||||
reserved1 = in.readByte();
|
||||
reserved2 = in.readInt();
|
||||
int cref = in.readUShort();
|
||||
cbFeatData = in.readInt();
|
||||
reserved3 = in.readShort();
|
||||
isf_sharedFeatureType = in.readShort();
|
||||
reserved1 = in.readByte();
|
||||
reserved2 = in.readInt();
|
||||
int cref = in.readUShort();
|
||||
cbFeatData = in.readInt();
|
||||
reserved3 = in.readShort();
|
||||
|
||||
cellRefs = new CellRangeAddress[cref];
|
||||
for(int i=0; i<cellRefs.length; i++) {
|
||||
cellRefs[i] = new CellRangeAddress(in);
|
||||
}
|
||||
cellRefs = new CellRangeAddress[cref];
|
||||
for(int i=0; i<cellRefs.length; i++) {
|
||||
cellRefs[i] = new CellRangeAddress(in);
|
||||
}
|
||||
|
||||
switch(isf_sharedFeatureType) {
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION:
|
||||
sharedFeature = new FeatProtection(in);
|
||||
break;
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFFEC2:
|
||||
sharedFeature = new FeatFormulaErr2(in);
|
||||
break;
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFFACTOID:
|
||||
sharedFeature = new FeatSmartTag(in);
|
||||
break;
|
||||
default:
|
||||
LOG.atError().log("Unknown Shared Feature {} found!", box(isf_sharedFeatureType));
|
||||
}
|
||||
}
|
||||
switch(isf_sharedFeatureType) {
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION:
|
||||
sharedFeature = new FeatProtection(in);
|
||||
break;
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFFEC2:
|
||||
sharedFeature = new FeatFormulaErr2(in);
|
||||
break;
|
||||
case FeatHdrRecord.SHAREDFEATURES_ISFFACTOID:
|
||||
sharedFeature = new FeatSmartTag(in);
|
||||
break;
|
||||
default:
|
||||
LOG.atError().log("Unknown Shared Feature {} found!", box(isf_sharedFeatureType));
|
||||
}
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
futureHeader.serialize(out);
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
futureHeader.serialize(out);
|
||||
|
||||
out.writeShort(isf_sharedFeatureType);
|
||||
out.writeByte(reserved1);
|
||||
out.writeInt((int)reserved2);
|
||||
out.writeShort(cellRefs.length);
|
||||
out.writeInt((int)cbFeatData);
|
||||
out.writeShort(reserved3);
|
||||
out.writeShort(isf_sharedFeatureType);
|
||||
out.writeByte(reserved1);
|
||||
out.writeInt((int)reserved2);
|
||||
out.writeShort(cellRefs.length);
|
||||
out.writeInt((int)cbFeatData);
|
||||
out.writeShort(reserved3);
|
||||
|
||||
for (CellRangeAddress cellRef : cellRefs) {
|
||||
cellRef.serialize(out);
|
||||
}
|
||||
for (CellRangeAddress cellRef : cellRefs) {
|
||||
cellRef.serialize(out);
|
||||
}
|
||||
|
||||
sharedFeature.serialize(out);
|
||||
}
|
||||
sharedFeature.serialize(out);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 12 + 2+1+4+2+4+2+
|
||||
(cellRefs.length * CellRangeAddress.ENCODED_SIZE)
|
||||
+sharedFeature.getDataSize();
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 12 + 2+1+4+2+4+2+
|
||||
(cellRefs.length * CellRangeAddress.ENCODED_SIZE)
|
||||
+sharedFeature.getDataSize();
|
||||
}
|
||||
|
||||
public int getIsf_sharedFeatureType() {
|
||||
return isf_sharedFeatureType;
|
||||
}
|
||||
public int getIsf_sharedFeatureType() {
|
||||
return isf_sharedFeatureType;
|
||||
}
|
||||
|
||||
public long getCbFeatData() {
|
||||
return cbFeatData;
|
||||
}
|
||||
public void setCbFeatData(long cbFeatData) {
|
||||
this.cbFeatData = cbFeatData;
|
||||
}
|
||||
public long getCbFeatData() {
|
||||
return cbFeatData;
|
||||
}
|
||||
public void setCbFeatData(long cbFeatData) {
|
||||
this.cbFeatData = cbFeatData;
|
||||
}
|
||||
|
||||
public CellRangeAddress[] getCellRefs() {
|
||||
return cellRefs;
|
||||
}
|
||||
public void setCellRefs(CellRangeAddress[] cellRefs) {
|
||||
this.cellRefs = cellRefs;
|
||||
}
|
||||
public CellRangeAddress[] getCellRefs() {
|
||||
return cellRefs;
|
||||
}
|
||||
public void setCellRefs(CellRangeAddress[] cellRefs) {
|
||||
this.cellRefs = cellRefs;
|
||||
}
|
||||
|
||||
public SharedFeature getSharedFeature() {
|
||||
return sharedFeature;
|
||||
}
|
||||
public void setSharedFeature(SharedFeature feature) {
|
||||
this.sharedFeature = feature;
|
||||
public SharedFeature getSharedFeature() {
|
||||
return sharedFeature;
|
||||
}
|
||||
public void setSharedFeature(SharedFeature feature) {
|
||||
this.sharedFeature = feature;
|
||||
|
||||
if(feature instanceof FeatProtection) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION;
|
||||
}
|
||||
if(feature instanceof FeatFormulaErr2) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFEC2;
|
||||
}
|
||||
if(feature instanceof FeatSmartTag) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFACTOID;
|
||||
}
|
||||
if(feature instanceof FeatProtection) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION;
|
||||
}
|
||||
if(feature instanceof FeatFormulaErr2) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFEC2;
|
||||
}
|
||||
if(feature instanceof FeatSmartTag) {
|
||||
isf_sharedFeatureType = FeatHdrRecord.SHAREDFEATURES_ISFFACTOID;
|
||||
}
|
||||
|
||||
if(isf_sharedFeatureType == FeatHdrRecord.SHAREDFEATURES_ISFFEC2) {
|
||||
cbFeatData = sharedFeature.getDataSize();
|
||||
} else {
|
||||
cbFeatData = 0;
|
||||
}
|
||||
}
|
||||
if(isf_sharedFeatureType == FeatHdrRecord.SHAREDFEATURES_ISFFEC2) {
|
||||
cbFeatData = sharedFeature.getDataSize();
|
||||
} else {
|
||||
cbFeatData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatRecord copy() {
|
||||
@Override
|
||||
public FeatRecord copy() {
|
||||
return new FeatRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FEAT;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FEAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"futureHeader", () -> futureHeader,
|
||||
"isf_sharedFeatureType", this::getIsf_sharedFeatureType,
|
||||
"reserved1", () -> reserved1,
|
||||
"reserved2", () -> reserved2,
|
||||
"cbFeatData", this::getCbFeatData,
|
||||
"reserved3", () -> reserved3,
|
||||
"cellRefs", this::getCellRefs,
|
||||
"sharedFeature", this::getSharedFeature
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"futureHeader", () -> futureHeader,
|
||||
"isf_sharedFeatureType", this::getIsf_sharedFeatureType,
|
||||
"reserved1", () -> reserved1,
|
||||
"reserved2", () -> reserved2,
|
||||
"cbFeatData", this::getCbFeatData,
|
||||
"reserved3", () -> reserved3,
|
||||
"cellRefs", this::getCellRefs,
|
||||
"sharedFeature", this::getSharedFeature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,28 +42,28 @@ import org.apache.poi.util.LittleEndianOutputStream;
|
||||
* Indicates that the record after this record are encrypted.
|
||||
*/
|
||||
public final class FilePassRecord extends StandardRecord {
|
||||
public static final short sid = 0x002F;
|
||||
public static final short sid = 0x002F;
|
||||
private static final int ENCRYPTION_XOR = 0;
|
||||
private static final int ENCRYPTION_OTHER = 1;
|
||||
|
||||
private final int encryptionType;
|
||||
private final int encryptionType;
|
||||
private final EncryptionInfo encryptionInfo;
|
||||
|
||||
private FilePassRecord(FilePassRecord other) {
|
||||
private FilePassRecord(FilePassRecord other) {
|
||||
super(other);
|
||||
encryptionType = other.encryptionType;
|
||||
encryptionType = other.encryptionType;
|
||||
encryptionInfo = other.encryptionInfo.copy();
|
||||
}
|
||||
}
|
||||
|
||||
public FilePassRecord(EncryptionMode encryptionMode) {
|
||||
encryptionType = (encryptionMode == EncryptionMode.xor) ? ENCRYPTION_XOR : ENCRYPTION_OTHER;
|
||||
encryptionInfo = new EncryptionInfo(encryptionMode);
|
||||
}
|
||||
public FilePassRecord(EncryptionMode encryptionMode) {
|
||||
encryptionType = (encryptionMode == EncryptionMode.xor) ? ENCRYPTION_XOR : ENCRYPTION_OTHER;
|
||||
encryptionInfo = new EncryptionInfo(encryptionMode);
|
||||
}
|
||||
|
||||
public FilePassRecord(RecordInputStream in) {
|
||||
encryptionType = in.readUShort();
|
||||
public FilePassRecord(RecordInputStream in) {
|
||||
encryptionType = in.readUShort();
|
||||
|
||||
EncryptionMode preferredMode;
|
||||
EncryptionMode preferredMode;
|
||||
switch (encryptionType) {
|
||||
case ENCRYPTION_XOR:
|
||||
preferredMode = EncryptionMode.xor;
|
||||
@ -75,14 +75,14 @@ public final class FilePassRecord extends StandardRecord {
|
||||
throw new EncryptedDocumentException("invalid encryption type");
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
encryptionInfo = new EncryptionInfo(in, preferredMode);
|
||||
} catch (IOException e) {
|
||||
throw new EncryptedDocumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(encryptionType);
|
||||
@ -118,29 +118,29 @@ public final class FilePassRecord extends StandardRecord {
|
||||
// should never happen in practice
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
|
||||
LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
|
||||
UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
|
||||
LittleEndianOutputStream leos = new LittleEndianOutputStream(bos);
|
||||
serialize(leos);
|
||||
return bos.size();
|
||||
}
|
||||
}
|
||||
|
||||
public EncryptionInfo getEncryptionInfo() {
|
||||
public EncryptionInfo getEncryptionInfo() {
|
||||
return encryptionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePassRecord copy() {
|
||||
return new FilePassRecord(this);
|
||||
}
|
||||
public FilePassRecord copy() {
|
||||
return new FilePassRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
|
||||
@ -115,7 +115,7 @@ public final class FileSharingRecord extends StandardRecord {
|
||||
out.writeShort(getPassword());
|
||||
out.writeShort(field_3_username_value.length());
|
||||
if(field_3_username_value.length() > 0) {
|
||||
out.writeByte(field_3_username_unicode_options);
|
||||
out.writeByte(field_3_username_unicode_options);
|
||||
StringUtil.putCompressedUnicode(getUsername(), out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,489 +29,489 @@ import org.apache.poi.util.StringUtil;
|
||||
|
||||
/** Describes a font in the workbook */
|
||||
public final class FontRecord extends StandardRecord {
|
||||
// docs are wrong (0x231 Microsoft Support site article Q184647)
|
||||
public static final short sid = 0x0031;
|
||||
public static final short SS_NONE = 0;
|
||||
public static final short SS_SUPER = 1;
|
||||
public static final short SS_SUB = 2;
|
||||
public static final byte U_NONE = 0;
|
||||
public static final byte U_SINGLE = 1;
|
||||
public static final byte U_DOUBLE = 2;
|
||||
public static final byte U_SINGLE_ACCOUNTING = 0x21;
|
||||
public static final byte U_DOUBLE_ACCOUNTING = 0x22;
|
||||
// docs are wrong (0x231 Microsoft Support site article Q184647)
|
||||
public static final short sid = 0x0031;
|
||||
public static final short SS_NONE = 0;
|
||||
public static final short SS_SUPER = 1;
|
||||
public static final short SS_SUB = 2;
|
||||
public static final byte U_NONE = 0;
|
||||
public static final byte U_SINGLE = 1;
|
||||
public static final byte U_DOUBLE = 2;
|
||||
public static final byte U_SINGLE_ACCOUNTING = 0x21;
|
||||
public static final byte U_DOUBLE_ACCOUNTING = 0x22;
|
||||
|
||||
// 0 0x01 - Reserved bit must be 0
|
||||
// is this font in italics
|
||||
private static final BitField italic = BitFieldFactory.getInstance(0x02);
|
||||
// 0 0x01 - Reserved bit must be 0
|
||||
// is this font in italics
|
||||
private static final BitField italic = BitFieldFactory.getInstance(0x02);
|
||||
|
||||
// 2 0x04 - reserved bit must be 0
|
||||
// is this font has a line through the center
|
||||
private static final BitField strikeout = BitFieldFactory.getInstance(0x08);
|
||||
// some weird macintosh thing....but who understands those mac people anyhow
|
||||
private static final BitField macoutline = BitFieldFactory.getInstance(0x10);
|
||||
private static final BitField macshadow = BitFieldFactory.getInstance(0x20);
|
||||
// 2 0x04 - reserved bit must be 0
|
||||
// is this font has a line through the center
|
||||
private static final BitField strikeout = BitFieldFactory.getInstance(0x08);
|
||||
// some weird macintosh thing....but who understands those mac people anyhow
|
||||
private static final BitField macoutline = BitFieldFactory.getInstance(0x10);
|
||||
private static final BitField macshadow = BitFieldFactory.getInstance(0x20);
|
||||
|
||||
// in units of .05 of a point
|
||||
private short field_1_font_height;
|
||||
private short field_2_attributes;
|
||||
// in units of .05 of a point
|
||||
private short field_1_font_height;
|
||||
private short field_2_attributes;
|
||||
|
||||
// 7-6 - reserved bits must be 0
|
||||
// the rest is unused
|
||||
private short field_3_color_palette_index;
|
||||
private short field_4_bold_weight;
|
||||
// 00none/01super/02sub
|
||||
private short field_5_super_sub_script;
|
||||
// 00none/01single/02double/21singleaccounting/22doubleaccounting
|
||||
private byte field_6_underline;
|
||||
// ?? defined by windows api logfont structure?
|
||||
private byte field_7_family;
|
||||
// ?? defined by windows api logfont structure?
|
||||
private byte field_8_charset;
|
||||
// must be 0
|
||||
private byte field_9_zero;
|
||||
/** possibly empty string never <code>null</code> */
|
||||
private String field_11_font_name;
|
||||
// 7-6 - reserved bits must be 0
|
||||
// the rest is unused
|
||||
private short field_3_color_palette_index;
|
||||
private short field_4_bold_weight;
|
||||
// 00none/01super/02sub
|
||||
private short field_5_super_sub_script;
|
||||
// 00none/01single/02double/21singleaccounting/22doubleaccounting
|
||||
private byte field_6_underline;
|
||||
// ?? defined by windows api logfont structure?
|
||||
private byte field_7_family;
|
||||
// ?? defined by windows api logfont structure?
|
||||
private byte field_8_charset;
|
||||
// must be 0
|
||||
private byte field_9_zero;
|
||||
/** possibly empty string never <code>null</code> */
|
||||
private String field_11_font_name;
|
||||
|
||||
public FontRecord() {
|
||||
}
|
||||
public FontRecord() {
|
||||
}
|
||||
|
||||
public FontRecord(FontRecord other) {
|
||||
super(other);
|
||||
field_1_font_height = other.field_1_font_height;
|
||||
field_2_attributes = other.field_2_attributes;
|
||||
field_3_color_palette_index = other.field_3_color_palette_index;
|
||||
field_4_bold_weight = other.field_4_bold_weight;
|
||||
field_5_super_sub_script = other.field_5_super_sub_script;
|
||||
field_6_underline = other.field_6_underline;
|
||||
field_7_family = other.field_7_family;
|
||||
field_8_charset = other.field_8_charset;
|
||||
field_9_zero = other.field_9_zero;
|
||||
field_11_font_name = other.field_11_font_name;
|
||||
}
|
||||
public FontRecord(FontRecord other) {
|
||||
super(other);
|
||||
field_1_font_height = other.field_1_font_height;
|
||||
field_2_attributes = other.field_2_attributes;
|
||||
field_3_color_palette_index = other.field_3_color_palette_index;
|
||||
field_4_bold_weight = other.field_4_bold_weight;
|
||||
field_5_super_sub_script = other.field_5_super_sub_script;
|
||||
field_6_underline = other.field_6_underline;
|
||||
field_7_family = other.field_7_family;
|
||||
field_8_charset = other.field_8_charset;
|
||||
field_9_zero = other.field_9_zero;
|
||||
field_11_font_name = other.field_11_font_name;
|
||||
}
|
||||
|
||||
public FontRecord(RecordInputStream in) {
|
||||
field_1_font_height = in.readShort();
|
||||
field_2_attributes = in.readShort();
|
||||
field_3_color_palette_index = in.readShort();
|
||||
field_4_bold_weight = in.readShort();
|
||||
field_5_super_sub_script = in.readShort();
|
||||
field_6_underline = in.readByte();
|
||||
field_7_family = in.readByte();
|
||||
field_8_charset = in.readByte();
|
||||
field_9_zero = in.readByte();
|
||||
int field_10_font_name_len = in.readUByte();
|
||||
int unicodeFlags = in.readUByte(); // options byte present always (even if no character data)
|
||||
public FontRecord(RecordInputStream in) {
|
||||
field_1_font_height = in.readShort();
|
||||
field_2_attributes = in.readShort();
|
||||
field_3_color_palette_index = in.readShort();
|
||||
field_4_bold_weight = in.readShort();
|
||||
field_5_super_sub_script = in.readShort();
|
||||
field_6_underline = in.readByte();
|
||||
field_7_family = in.readByte();
|
||||
field_8_charset = in.readByte();
|
||||
field_9_zero = in.readByte();
|
||||
int field_10_font_name_len = in.readUByte();
|
||||
int unicodeFlags = in.readUByte(); // options byte present always (even if no character data)
|
||||
|
||||
if (field_10_font_name_len > 0) {
|
||||
if (unicodeFlags == 0) { // is compressed unicode
|
||||
field_11_font_name = in.readCompressedUnicode(field_10_font_name_len);
|
||||
} else { // is not compressed unicode
|
||||
field_11_font_name = in.readUnicodeLEString(field_10_font_name_len);
|
||||
}
|
||||
} else {
|
||||
field_11_font_name = "";
|
||||
}
|
||||
}
|
||||
if (field_10_font_name_len > 0) {
|
||||
if (unicodeFlags == 0) { // is compressed unicode
|
||||
field_11_font_name = in.readCompressedUnicode(field_10_font_name_len);
|
||||
} else { // is not compressed unicode
|
||||
field_11_font_name = in.readUnicodeLEString(field_10_font_name_len);
|
||||
}
|
||||
} else {
|
||||
field_11_font_name = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the height of the font in 1/20th point units
|
||||
*
|
||||
* @param height fontheight (in points/20)
|
||||
*/
|
||||
public void setFontHeight(short height) {
|
||||
field_1_font_height = height;
|
||||
}
|
||||
/**
|
||||
* sets the height of the font in 1/20th point units
|
||||
*
|
||||
* @param height fontheight (in points/20)
|
||||
*/
|
||||
public void setFontHeight(short height) {
|
||||
field_1_font_height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the font attributes (see individual bit setters that reference this method)
|
||||
*
|
||||
* @param attributes the bitmask to set
|
||||
*/
|
||||
public void setAttributes(short attributes) {
|
||||
field_2_attributes = attributes;
|
||||
}
|
||||
/**
|
||||
* set the font attributes (see individual bit setters that reference this method)
|
||||
*
|
||||
* @param attributes the bitmask to set
|
||||
*/
|
||||
public void setAttributes(short attributes) {
|
||||
field_2_attributes = attributes;
|
||||
}
|
||||
|
||||
// attributes bitfields
|
||||
// attributes bitfields
|
||||
|
||||
/**
|
||||
* set the font to be italics or not
|
||||
*
|
||||
* @param italics - whether the font is italics or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setItalic(boolean italics) {
|
||||
field_2_attributes = italic.setShortBoolean(field_2_attributes, italics);
|
||||
}
|
||||
/**
|
||||
* set the font to be italics or not
|
||||
*
|
||||
* @param italics - whether the font is italics or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setItalic(boolean italics) {
|
||||
field_2_attributes = italic.setShortBoolean(field_2_attributes, italics);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the font to be stricken out or not
|
||||
*
|
||||
* @param strike - whether the font is stricken out or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setStrikeout(boolean strike) {
|
||||
field_2_attributes = strikeout.setShortBoolean(field_2_attributes, strike);
|
||||
}
|
||||
/**
|
||||
* set the font to be stricken out or not
|
||||
*
|
||||
* @param strike - whether the font is stricken out or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setStrikeout(boolean strike) {
|
||||
field_2_attributes = strikeout.setShortBoolean(field_2_attributes, strike);
|
||||
}
|
||||
|
||||
/**
|
||||
* whether to use the mac outline font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @param mac - whether to do that mac font outline thing or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setMacoutline(boolean mac) {
|
||||
field_2_attributes = macoutline.setShortBoolean(field_2_attributes, mac);
|
||||
}
|
||||
/**
|
||||
* whether to use the mac outline font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @param mac - whether to do that mac font outline thing or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setMacoutline(boolean mac) {
|
||||
field_2_attributes = macoutline.setShortBoolean(field_2_attributes, mac);
|
||||
}
|
||||
|
||||
/**
|
||||
* whether to use the mac shado font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @param mac - whether to do that mac font shadow thing or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setMacshadow(boolean mac) {
|
||||
field_2_attributes = macshadow.setShortBoolean(field_2_attributes, mac);
|
||||
}
|
||||
/**
|
||||
* whether to use the mac shado font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @param mac - whether to do that mac font shadow thing or not
|
||||
* @see #setAttributes(short)
|
||||
*/
|
||||
public void setMacshadow(boolean mac) {
|
||||
field_2_attributes = macshadow.setShortBoolean(field_2_attributes, mac);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the font's color palette index
|
||||
*
|
||||
* @param cpi - font color index
|
||||
*/
|
||||
public void setColorPaletteIndex(short cpi) {
|
||||
field_3_color_palette_index = cpi;
|
||||
}
|
||||
/**
|
||||
* set the font's color palette index
|
||||
*
|
||||
* @param cpi - font color index
|
||||
*/
|
||||
public void setColorPaletteIndex(short cpi) {
|
||||
field_3_color_palette_index = cpi;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the bold weight for this font (100-1000dec or 0x64-0x3e8). Default is
|
||||
* 0x190 for normal and 0x2bc for bold
|
||||
*
|
||||
* @param bw - a number between 100-1000 for the fonts "boldness"
|
||||
*/
|
||||
public void setBoldWeight(short bw) {
|
||||
field_4_bold_weight = bw;
|
||||
}
|
||||
/**
|
||||
* set the bold weight for this font (100-1000dec or 0x64-0x3e8). Default is
|
||||
* 0x190 for normal and 0x2bc for bold
|
||||
*
|
||||
* @param bw - a number between 100-1000 for the fonts "boldness"
|
||||
*/
|
||||
public void setBoldWeight(short bw) {
|
||||
field_4_bold_weight = bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the type of super or subscript for the font
|
||||
*
|
||||
* @param sss super or subscript option
|
||||
* @see #SS_NONE
|
||||
* @see #SS_SUPER
|
||||
* @see #SS_SUB
|
||||
*/
|
||||
public void setSuperSubScript(short sss) {
|
||||
field_5_super_sub_script = sss;
|
||||
}
|
||||
/**
|
||||
* set the type of super or subscript for the font
|
||||
*
|
||||
* @param sss super or subscript option
|
||||
* @see #SS_NONE
|
||||
* @see #SS_SUPER
|
||||
* @see #SS_SUB
|
||||
*/
|
||||
public void setSuperSubScript(short sss) {
|
||||
field_5_super_sub_script = sss;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the type of underlining for the font
|
||||
*
|
||||
* @param u super or subscript option
|
||||
*
|
||||
* @see #U_NONE
|
||||
* @see #U_SINGLE
|
||||
* @see #U_DOUBLE
|
||||
* @see #U_SINGLE_ACCOUNTING
|
||||
* @see #U_DOUBLE_ACCOUNTING
|
||||
*/
|
||||
public void setUnderline(byte u) {
|
||||
field_6_underline = u;
|
||||
}
|
||||
/**
|
||||
* set the type of underlining for the font
|
||||
*
|
||||
* @param u super or subscript option
|
||||
*
|
||||
* @see #U_NONE
|
||||
* @see #U_SINGLE
|
||||
* @see #U_DOUBLE
|
||||
* @see #U_SINGLE_ACCOUNTING
|
||||
* @see #U_DOUBLE_ACCOUNTING
|
||||
*/
|
||||
public void setUnderline(byte u) {
|
||||
field_6_underline = u;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the font family (TODO)
|
||||
*
|
||||
* @param f family
|
||||
*/
|
||||
public void setFamily(byte f) {
|
||||
field_7_family = f;
|
||||
}
|
||||
/**
|
||||
* set the font family (TODO)
|
||||
*
|
||||
* @param f family
|
||||
*/
|
||||
public void setFamily(byte f) {
|
||||
field_7_family = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the character set
|
||||
*
|
||||
* @param charset - character set
|
||||
*/
|
||||
public void setCharset(byte charset) {
|
||||
field_8_charset = charset;
|
||||
}
|
||||
/**
|
||||
* set the character set
|
||||
*
|
||||
* @param charset - character set
|
||||
*/
|
||||
public void setCharset(byte charset) {
|
||||
field_8_charset = charset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set the name of the font
|
||||
*
|
||||
* @param fn - name of the font (i.e. "Arial")
|
||||
*/
|
||||
public void setFontName(String fn) {
|
||||
field_11_font_name = fn;
|
||||
}
|
||||
/**
|
||||
* set the name of the font
|
||||
*
|
||||
* @param fn - name of the font (i.e. "Arial")
|
||||
*/
|
||||
public void setFontName(String fn) {
|
||||
field_11_font_name = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the height of the font in 1/20th point units
|
||||
*
|
||||
* @return fontheight (in points/20)
|
||||
*/
|
||||
public short getFontHeight() {
|
||||
return field_1_font_height;
|
||||
}
|
||||
/**
|
||||
* gets the height of the font in 1/20th point units
|
||||
*
|
||||
* @return fontheight (in points/20)
|
||||
*/
|
||||
public short getFontHeight() {
|
||||
return field_1_font_height;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the font attributes (see individual bit getters that reference this method)
|
||||
*
|
||||
* @return attribute - the bitmask
|
||||
*/
|
||||
public short getAttributes() {
|
||||
return field_2_attributes;
|
||||
}
|
||||
/**
|
||||
* get the font attributes (see individual bit getters that reference this method)
|
||||
*
|
||||
* @return attribute - the bitmask
|
||||
*/
|
||||
public short getAttributes() {
|
||||
return field_2_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* get whether the font is to be italics or not
|
||||
*
|
||||
* @return italics - whether the font is italics or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isItalic() {
|
||||
return italic.isSet(field_2_attributes);
|
||||
}
|
||||
/**
|
||||
* get whether the font is to be italics or not
|
||||
*
|
||||
* @return italics - whether the font is italics or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isItalic() {
|
||||
return italic.isSet(field_2_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* get whether the font is to be stricken out or not
|
||||
*
|
||||
* @return strike - whether the font is stricken out or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isStruckout(){
|
||||
return strikeout.isSet(field_2_attributes);
|
||||
}
|
||||
/**
|
||||
* get whether the font is to be stricken out or not
|
||||
*
|
||||
* @return strike - whether the font is stricken out or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isStruckout(){
|
||||
return strikeout.isSet(field_2_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* whether to use the mac outline font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @return mac - whether to do that mac font outline thing or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isMacoutlined(){
|
||||
return macoutline.isSet(field_2_attributes);
|
||||
}
|
||||
/**
|
||||
* whether to use the mac outline font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @return mac - whether to do that mac font outline thing or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isMacoutlined(){
|
||||
return macoutline.isSet(field_2_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* whether to use the mac shado font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @return mac - whether to do that mac font shadow thing or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isMacshadowed(){
|
||||
return macshadow.isSet(field_2_attributes);
|
||||
}
|
||||
/**
|
||||
* whether to use the mac shado font style thing (mac only) - Some mac person
|
||||
* should comment this instead of me doing it (since I have no idea)
|
||||
*
|
||||
* @return mac - whether to do that mac font shadow thing or not
|
||||
* @see #getAttributes()
|
||||
*/
|
||||
public boolean isMacshadowed(){
|
||||
return macshadow.isSet(field_2_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the font's color palette index
|
||||
*
|
||||
* @return cpi - font color index
|
||||
*/
|
||||
public short getColorPaletteIndex(){
|
||||
return field_3_color_palette_index;
|
||||
}
|
||||
/**
|
||||
* get the font's color palette index
|
||||
*
|
||||
* @return cpi - font color index
|
||||
*/
|
||||
public short getColorPaletteIndex(){
|
||||
return field_3_color_palette_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the bold weight for this font (100-1000dec or 0x64-0x3e8). Default is
|
||||
* 0x190 for normal and 0x2bc for bold
|
||||
*
|
||||
* @return bw - a number between 100-1000 for the fonts "boldness"
|
||||
*/
|
||||
public short getBoldWeight(){
|
||||
return field_4_bold_weight;
|
||||
}
|
||||
/**
|
||||
* get the bold weight for this font (100-1000dec or 0x64-0x3e8). Default is
|
||||
* 0x190 for normal and 0x2bc for bold
|
||||
*
|
||||
* @return bw - a number between 100-1000 for the fonts "boldness"
|
||||
*/
|
||||
public short getBoldWeight(){
|
||||
return field_4_bold_weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of super or subscript for the font
|
||||
*
|
||||
* @return super or subscript option
|
||||
* @see #SS_NONE
|
||||
* @see #SS_SUPER
|
||||
* @see #SS_SUB
|
||||
*/
|
||||
public short getSuperSubScript(){
|
||||
return field_5_super_sub_script;
|
||||
}
|
||||
/**
|
||||
* get the type of super or subscript for the font
|
||||
*
|
||||
* @return super or subscript option
|
||||
* @see #SS_NONE
|
||||
* @see #SS_SUPER
|
||||
* @see #SS_SUB
|
||||
*/
|
||||
public short getSuperSubScript(){
|
||||
return field_5_super_sub_script;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of underlining for the font
|
||||
*
|
||||
* @return super or subscript option
|
||||
*
|
||||
* @see #U_NONE
|
||||
* @see #U_SINGLE
|
||||
* @see #U_DOUBLE
|
||||
* @see #U_SINGLE_ACCOUNTING
|
||||
* @see #U_DOUBLE_ACCOUNTING
|
||||
*/
|
||||
public byte getUnderline() {
|
||||
return field_6_underline;
|
||||
}
|
||||
/**
|
||||
* get the type of underlining for the font
|
||||
*
|
||||
* @return super or subscript option
|
||||
*
|
||||
* @see #U_NONE
|
||||
* @see #U_SINGLE
|
||||
* @see #U_DOUBLE
|
||||
* @see #U_SINGLE_ACCOUNTING
|
||||
* @see #U_DOUBLE_ACCOUNTING
|
||||
*/
|
||||
public byte getUnderline() {
|
||||
return field_6_underline;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the font family (TODO)
|
||||
*
|
||||
* @return family
|
||||
*/
|
||||
public byte getFamily() {
|
||||
return field_7_family;
|
||||
}
|
||||
/**
|
||||
* get the font family (TODO)
|
||||
*
|
||||
* @return family
|
||||
*/
|
||||
public byte getFamily() {
|
||||
return field_7_family;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the character set
|
||||
*
|
||||
* @return charset - character set
|
||||
*/
|
||||
public byte getCharset() {
|
||||
return field_8_charset;
|
||||
}
|
||||
/**
|
||||
* get the character set
|
||||
*
|
||||
* @return charset - character set
|
||||
*/
|
||||
public byte getCharset() {
|
||||
return field_8_charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the name of the font
|
||||
*
|
||||
* @return fn - name of the font (i.e. "Arial")
|
||||
*/
|
||||
public String getFontName() {
|
||||
return field_11_font_name;
|
||||
}
|
||||
/**
|
||||
* get the name of the font
|
||||
*
|
||||
* @return fn - name of the font (i.e. "Arial")
|
||||
*/
|
||||
public String getFontName() {
|
||||
return field_11_font_name;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
|
||||
out.writeShort(getFontHeight());
|
||||
out.writeShort(getAttributes());
|
||||
out.writeShort(getColorPaletteIndex());
|
||||
out.writeShort(getBoldWeight());
|
||||
out.writeShort(getSuperSubScript());
|
||||
out.writeByte(getUnderline());
|
||||
out.writeByte(getFamily());
|
||||
out.writeByte(getCharset());
|
||||
out.writeByte(field_9_zero);
|
||||
int fontNameLen = field_11_font_name.length();
|
||||
out.writeByte(fontNameLen);
|
||||
boolean hasMultibyte = StringUtil.hasMultibyte(field_11_font_name);
|
||||
out.writeByte(hasMultibyte ? 0x01 : 0x00);
|
||||
if (fontNameLen > 0) {
|
||||
if (hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_11_font_name, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_11_font_name, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
protected int getDataSize() {
|
||||
int size = 16; // 5 shorts + 6 bytes
|
||||
int fontNameLen = field_11_font_name.length();
|
||||
if (fontNameLen < 1) {
|
||||
return size;
|
||||
}
|
||||
out.writeShort(getFontHeight());
|
||||
out.writeShort(getAttributes());
|
||||
out.writeShort(getColorPaletteIndex());
|
||||
out.writeShort(getBoldWeight());
|
||||
out.writeShort(getSuperSubScript());
|
||||
out.writeByte(getUnderline());
|
||||
out.writeByte(getFamily());
|
||||
out.writeByte(getCharset());
|
||||
out.writeByte(field_9_zero);
|
||||
int fontNameLen = field_11_font_name.length();
|
||||
out.writeByte(fontNameLen);
|
||||
boolean hasMultibyte = StringUtil.hasMultibyte(field_11_font_name);
|
||||
out.writeByte(hasMultibyte ? 0x01 : 0x00);
|
||||
if (fontNameLen > 0) {
|
||||
if (hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_11_font_name, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_11_font_name, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
protected int getDataSize() {
|
||||
int size = 16; // 5 shorts + 6 bytes
|
||||
int fontNameLen = field_11_font_name.length();
|
||||
if (fontNameLen < 1) {
|
||||
return size;
|
||||
}
|
||||
|
||||
boolean hasMultibyte = StringUtil.hasMultibyte(field_11_font_name);
|
||||
return size + fontNameLen * (hasMultibyte ? 2 : 1);
|
||||
}
|
||||
boolean hasMultibyte = StringUtil.hasMultibyte(field_11_font_name);
|
||||
return size + fontNameLen * (hasMultibyte ? 2 : 1);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones all the font style information from another
|
||||
* FontRecord, onto this one. This
|
||||
* will then hold all the same font style options.
|
||||
*
|
||||
* @param source the record to clone the properties from
|
||||
*/
|
||||
public void cloneStyleFrom(FontRecord source) {
|
||||
field_1_font_height = source.field_1_font_height;
|
||||
field_2_attributes = source.field_2_attributes;
|
||||
field_3_color_palette_index = source.field_3_color_palette_index;
|
||||
field_4_bold_weight = source.field_4_bold_weight;
|
||||
field_5_super_sub_script = source.field_5_super_sub_script;
|
||||
field_6_underline = source.field_6_underline;
|
||||
field_7_family = source.field_7_family;
|
||||
field_8_charset = source.field_8_charset;
|
||||
field_9_zero = source.field_9_zero;
|
||||
field_11_font_name = source.field_11_font_name;
|
||||
}
|
||||
/**
|
||||
* Clones all the font style information from another
|
||||
* FontRecord, onto this one. This
|
||||
* will then hold all the same font style options.
|
||||
*
|
||||
* @param source the record to clone the properties from
|
||||
*/
|
||||
public void cloneStyleFrom(FontRecord source) {
|
||||
field_1_font_height = source.field_1_font_height;
|
||||
field_2_attributes = source.field_2_attributes;
|
||||
field_3_color_palette_index = source.field_3_color_palette_index;
|
||||
field_4_bold_weight = source.field_4_bold_weight;
|
||||
field_5_super_sub_script = source.field_5_super_sub_script;
|
||||
field_6_underline = source.field_6_underline;
|
||||
field_7_family = source.field_7_family;
|
||||
field_8_charset = source.field_8_charset;
|
||||
field_9_zero = source.field_9_zero;
|
||||
field_11_font_name = source.field_11_font_name;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
field_1_font_height
|
||||
, field_2_attributes
|
||||
, field_3_color_palette_index
|
||||
, field_4_bold_weight
|
||||
, field_5_super_sub_script
|
||||
, field_6_underline
|
||||
, field_7_family
|
||||
, field_8_charset
|
||||
, field_9_zero
|
||||
, field_11_font_name
|
||||
);
|
||||
}
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
field_1_font_height
|
||||
, field_2_attributes
|
||||
, field_3_color_palette_index
|
||||
, field_4_bold_weight
|
||||
, field_5_super_sub_script
|
||||
, field_6_underline
|
||||
, field_7_family
|
||||
, field_8_charset
|
||||
, field_9_zero
|
||||
, field_11_font_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this FontRecord have all the same font
|
||||
* properties as the supplied FontRecord?
|
||||
* Note that {@link #equals(Object)} will check
|
||||
* for exact objects, while this will check
|
||||
* for exact contents, because normally the
|
||||
* font record's position makes a big
|
||||
* difference too.
|
||||
*
|
||||
* @param other the record to compare with
|
||||
*
|
||||
* @return true, if the properties match
|
||||
*/
|
||||
public boolean sameProperties(FontRecord other) {
|
||||
/**
|
||||
* Does this FontRecord have all the same font
|
||||
* properties as the supplied FontRecord?
|
||||
* Note that {@link #equals(Object)} will check
|
||||
* for exact objects, while this will check
|
||||
* for exact contents, because normally the
|
||||
* font record's position makes a big
|
||||
* difference too.
|
||||
*
|
||||
* @param other the record to compare with
|
||||
*
|
||||
* @return true, if the properties match
|
||||
*/
|
||||
public boolean sameProperties(FontRecord other) {
|
||||
|
||||
return
|
||||
field_1_font_height == other.field_1_font_height &&
|
||||
field_2_attributes == other.field_2_attributes &&
|
||||
field_3_color_palette_index == other.field_3_color_palette_index &&
|
||||
field_4_bold_weight == other.field_4_bold_weight &&
|
||||
field_5_super_sub_script == other.field_5_super_sub_script &&
|
||||
field_6_underline == other.field_6_underline &&
|
||||
field_7_family == other.field_7_family &&
|
||||
field_8_charset == other.field_8_charset &&
|
||||
field_9_zero == other.field_9_zero &&
|
||||
Objects.equals(this.field_11_font_name, other.field_11_font_name)
|
||||
;
|
||||
}
|
||||
return
|
||||
field_1_font_height == other.field_1_font_height &&
|
||||
field_2_attributes == other.field_2_attributes &&
|
||||
field_3_color_palette_index == other.field_3_color_palette_index &&
|
||||
field_4_bold_weight == other.field_4_bold_weight &&
|
||||
field_5_super_sub_script == other.field_5_super_sub_script &&
|
||||
field_6_underline == other.field_6_underline &&
|
||||
field_7_family == other.field_7_family &&
|
||||
field_8_charset == other.field_8_charset &&
|
||||
field_9_zero == other.field_9_zero &&
|
||||
Objects.equals(this.field_11_font_name, other.field_11_font_name)
|
||||
;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof FontRecord) && sameProperties((FontRecord) o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontRecord copy() {
|
||||
return new FontRecord(this);
|
||||
}
|
||||
@Override
|
||||
public FontRecord copy() {
|
||||
return new FontRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FONT;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FONT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"fontHeight", this::getFontHeight,
|
||||
"attributes", GenericRecordUtil.getBitsAsString(this::getAttributes,
|
||||
new BitField[]{italic,strikeout,macoutline,macshadow},
|
||||
new String[]{"ITALIC","STRIKEOUT","MACOUTLINE","MACSHADOW"}),
|
||||
"colorPalette", this::getColorPaletteIndex,
|
||||
"boldWeight", this::getBoldWeight,
|
||||
"superSubScript", this::getSuperSubScript,
|
||||
"underline", this::getUnderline,
|
||||
"family", this::getFamily,
|
||||
"charset", this::getCharset,
|
||||
"fontName", this::getFontName
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"fontHeight", this::getFontHeight,
|
||||
"attributes", GenericRecordUtil.getBitsAsString(this::getAttributes,
|
||||
new BitField[]{italic,strikeout,macoutline,macshadow},
|
||||
new String[]{"ITALIC","STRIKEOUT","MACOUTLINE","MACSHADOW"}),
|
||||
"colorPalette", this::getColorPaletteIndex,
|
||||
"boldWeight", this::getBoldWeight,
|
||||
"superSubScript", this::getSuperSubScript,
|
||||
"underline", this::getUnderline,
|
||||
"family", this::getFamily,
|
||||
"charset", this::getCharset,
|
||||
"fontName", this::getFontName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,31 +21,31 @@ package org.apache.poi.hssf.record;
|
||||
* Specifies the footer for a sheet
|
||||
*/
|
||||
public final class FooterRecord extends HeaderFooterBase {
|
||||
public static final short sid = 0x0015;
|
||||
public static final short sid = 0x0015;
|
||||
|
||||
public FooterRecord(String text) {
|
||||
super(text);
|
||||
}
|
||||
public FooterRecord(String text) {
|
||||
super(text);
|
||||
}
|
||||
|
||||
public FooterRecord(FooterRecord other) {
|
||||
super(other);
|
||||
}
|
||||
public FooterRecord(FooterRecord other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public FooterRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public FooterRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FooterRecord copy() {
|
||||
return new FooterRecord(this);
|
||||
}
|
||||
@Override
|
||||
public FooterRecord copy() {
|
||||
return new FooterRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FOOTER;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FOOTER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,241 +34,241 @@ import org.apache.poi.util.Removal;
|
||||
*/
|
||||
public final class FormulaRecord extends CellRecord {
|
||||
|
||||
// docs say 406...because of a bug Microsoft support site article #Q184647)
|
||||
public static final short sid = 0x0006;
|
||||
// double + short + int
|
||||
private static final int FIXED_SIZE = 14;
|
||||
// docs say 406...because of a bug Microsoft support site article #Q184647)
|
||||
public static final short sid = 0x0006;
|
||||
// double + short + int
|
||||
private static final int FIXED_SIZE = 14;
|
||||
|
||||
private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
|
||||
private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
|
||||
private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
|
||||
private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
|
||||
private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
|
||||
private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
|
||||
|
||||
private double field_4_value;
|
||||
private short field_5_options;
|
||||
/**
|
||||
* Unused field. As it turns out this field is often not zero..
|
||||
* According to Microsoft Excel Developer's Kit Page 318:
|
||||
* when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
|
||||
*/
|
||||
private int field_6_zero;
|
||||
private Formula field_8_parsed_expr;
|
||||
private double field_4_value;
|
||||
private short field_5_options;
|
||||
/**
|
||||
* Unused field. As it turns out this field is often not zero..
|
||||
* According to Microsoft Excel Developer's Kit Page 318:
|
||||
* when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
|
||||
*/
|
||||
private int field_6_zero;
|
||||
private Formula field_8_parsed_expr;
|
||||
|
||||
/**
|
||||
* Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
|
||||
*/
|
||||
private FormulaSpecialCachedValue specialCachedValue;
|
||||
/**
|
||||
* Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
|
||||
*/
|
||||
private FormulaSpecialCachedValue specialCachedValue;
|
||||
|
||||
/** Creates new FormulaRecord */
|
||||
public FormulaRecord() {
|
||||
field_8_parsed_expr = Formula.create(Ptg.EMPTY_PTG_ARRAY);
|
||||
}
|
||||
/** Creates new FormulaRecord */
|
||||
public FormulaRecord() {
|
||||
field_8_parsed_expr = Formula.create(Ptg.EMPTY_PTG_ARRAY);
|
||||
}
|
||||
|
||||
public FormulaRecord(FormulaRecord other) {
|
||||
super(other);
|
||||
field_4_value = other.field_4_value;
|
||||
field_5_options = other.field_5_options;
|
||||
field_6_zero = other.field_6_zero;
|
||||
field_8_parsed_expr = (other.field_8_parsed_expr == null) ? null : new Formula(other.field_8_parsed_expr);
|
||||
specialCachedValue = (other.specialCachedValue == null) ? null : new FormulaSpecialCachedValue(other.specialCachedValue);
|
||||
}
|
||||
public FormulaRecord(FormulaRecord other) {
|
||||
super(other);
|
||||
field_4_value = other.field_4_value;
|
||||
field_5_options = other.field_5_options;
|
||||
field_6_zero = other.field_6_zero;
|
||||
field_8_parsed_expr = (other.field_8_parsed_expr == null) ? null : new Formula(other.field_8_parsed_expr);
|
||||
specialCachedValue = (other.specialCachedValue == null) ? null : new FormulaSpecialCachedValue(other.specialCachedValue);
|
||||
}
|
||||
|
||||
public FormulaRecord(RecordInputStream ris) {
|
||||
super(ris);
|
||||
long valueLongBits = ris.readLong();
|
||||
field_5_options = ris.readShort();
|
||||
specialCachedValue = FormulaSpecialCachedValue.create(valueLongBits);
|
||||
if (specialCachedValue == null) {
|
||||
field_4_value = Double.longBitsToDouble(valueLongBits);
|
||||
}
|
||||
public FormulaRecord(RecordInputStream ris) {
|
||||
super(ris);
|
||||
long valueLongBits = ris.readLong();
|
||||
field_5_options = ris.readShort();
|
||||
specialCachedValue = FormulaSpecialCachedValue.create(valueLongBits);
|
||||
if (specialCachedValue == null) {
|
||||
field_4_value = Double.longBitsToDouble(valueLongBits);
|
||||
}
|
||||
|
||||
field_6_zero = ris.readInt();
|
||||
field_6_zero = ris.readInt();
|
||||
|
||||
int field_7_expression_len = ris.readShort(); // this length does not include any extra array data
|
||||
int nBytesAvailable = ris.available();
|
||||
field_8_parsed_expr = Formula.read(field_7_expression_len, ris, nBytesAvailable);
|
||||
}
|
||||
int field_7_expression_len = ris.readShort(); // this length does not include any extra array data
|
||||
int nBytesAvailable = ris.available();
|
||||
field_8_parsed_expr = Formula.read(field_7_expression_len, ris, nBytesAvailable);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the calculated value of the formula
|
||||
*
|
||||
* @param value calculated value
|
||||
*/
|
||||
public void setValue(double value) {
|
||||
field_4_value = value;
|
||||
specialCachedValue = null;
|
||||
}
|
||||
/**
|
||||
* set the calculated value of the formula
|
||||
*
|
||||
* @param value calculated value
|
||||
*/
|
||||
public void setValue(double value) {
|
||||
field_4_value = value;
|
||||
specialCachedValue = null;
|
||||
}
|
||||
|
||||
public void setCachedResultTypeEmptyString() {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedEmptyValue();
|
||||
}
|
||||
public void setCachedResultTypeString() {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createForString();
|
||||
}
|
||||
public void setCachedResultErrorCode(int errorCode) {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedErrorCode(errorCode);
|
||||
}
|
||||
public void setCachedResultBoolean(boolean value) {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedBoolean(value);
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if this {@link FormulaRecord} is followed by a
|
||||
* {@link StringRecord} representing the cached text result of the formula
|
||||
* evaluation.
|
||||
*/
|
||||
public boolean hasCachedResultString() {
|
||||
return specialCachedValue != null &&
|
||||
specialCachedValue.getTypeCode() == FormulaSpecialCachedValue.STRING;
|
||||
}
|
||||
public void setCachedResultTypeEmptyString() {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedEmptyValue();
|
||||
}
|
||||
public void setCachedResultTypeString() {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createForString();
|
||||
}
|
||||
public void setCachedResultErrorCode(int errorCode) {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedErrorCode(errorCode);
|
||||
}
|
||||
public void setCachedResultBoolean(boolean value) {
|
||||
specialCachedValue = FormulaSpecialCachedValue.createCachedBoolean(value);
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if this {@link FormulaRecord} is followed by a
|
||||
* {@link StringRecord} representing the cached text result of the formula
|
||||
* evaluation.
|
||||
*/
|
||||
public boolean hasCachedResultString() {
|
||||
return specialCachedValue != null &&
|
||||
specialCachedValue.getTypeCode() == FormulaSpecialCachedValue.STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated POI 5.0.0, will be removed in 6.0, use getCachedResultTypeEnum until switch to enum is fully done
|
||||
*/
|
||||
@Deprecated
|
||||
@Removal(version = "6.0.0")
|
||||
public int getCachedResultType() {
|
||||
if (specialCachedValue == null) {
|
||||
return CellType.NUMERIC.getCode();
|
||||
}
|
||||
return specialCachedValue.getValueType();
|
||||
}
|
||||
/**
|
||||
* @deprecated POI 5.0.0, will be removed in 6.0, use getCachedResultTypeEnum until switch to enum is fully done
|
||||
*/
|
||||
@Deprecated
|
||||
@Removal(version = "6.0.0")
|
||||
public int getCachedResultType() {
|
||||
if (specialCachedValue == null) {
|
||||
return CellType.NUMERIC.getCode();
|
||||
}
|
||||
return specialCachedValue.getValueType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the cached result
|
||||
* @return A CellType
|
||||
* @since POI 5.0.0
|
||||
*/
|
||||
public CellType getCachedResultTypeEnum() {
|
||||
if (specialCachedValue == null) {
|
||||
return CellType.NUMERIC;
|
||||
}
|
||||
return specialCachedValue.getValueTypeEnum();
|
||||
}
|
||||
/**
|
||||
* Returns the type of the cached result
|
||||
* @return A CellType
|
||||
* @since POI 5.0.0
|
||||
*/
|
||||
public CellType getCachedResultTypeEnum() {
|
||||
if (specialCachedValue == null) {
|
||||
return CellType.NUMERIC;
|
||||
}
|
||||
return specialCachedValue.getValueTypeEnum();
|
||||
}
|
||||
|
||||
public boolean getCachedBooleanValue() {
|
||||
return specialCachedValue.getBooleanValue();
|
||||
}
|
||||
public int getCachedErrorValue() {
|
||||
return specialCachedValue.getErrorValue();
|
||||
}
|
||||
public boolean getCachedBooleanValue() {
|
||||
return specialCachedValue.getBooleanValue();
|
||||
}
|
||||
public int getCachedErrorValue() {
|
||||
return specialCachedValue.getErrorValue();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set the option flags
|
||||
*
|
||||
* @param options bitmask
|
||||
*/
|
||||
public void setOptions(short options) {
|
||||
field_5_options = options;
|
||||
}
|
||||
/**
|
||||
* set the option flags
|
||||
*
|
||||
* @param options bitmask
|
||||
*/
|
||||
public void setOptions(short options) {
|
||||
field_5_options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the calculated value of the formula
|
||||
*
|
||||
* @return calculated value
|
||||
*/
|
||||
public double getValue() {
|
||||
return field_4_value;
|
||||
}
|
||||
/**
|
||||
* get the calculated value of the formula
|
||||
*
|
||||
* @return calculated value
|
||||
*/
|
||||
public double getValue() {
|
||||
return field_4_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the option flags
|
||||
*
|
||||
* @return bitmask
|
||||
*/
|
||||
public short getOptions() {
|
||||
return field_5_options;
|
||||
}
|
||||
/**
|
||||
* get the option flags
|
||||
*
|
||||
* @return bitmask
|
||||
*/
|
||||
public short getOptions() {
|
||||
return field_5_options;
|
||||
}
|
||||
|
||||
public boolean isSharedFormula() {
|
||||
return sharedFormula.isSet(field_5_options);
|
||||
}
|
||||
public void setSharedFormula(boolean flag) {
|
||||
field_5_options =
|
||||
sharedFormula.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
public boolean isSharedFormula() {
|
||||
return sharedFormula.isSet(field_5_options);
|
||||
}
|
||||
public void setSharedFormula(boolean flag) {
|
||||
field_5_options =
|
||||
sharedFormula.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
public boolean isAlwaysCalc() {
|
||||
return alwaysCalc.isSet(field_5_options);
|
||||
}
|
||||
public void setAlwaysCalc(boolean flag) {
|
||||
field_5_options =
|
||||
alwaysCalc.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
public boolean isAlwaysCalc() {
|
||||
return alwaysCalc.isSet(field_5_options);
|
||||
}
|
||||
public void setAlwaysCalc(boolean flag) {
|
||||
field_5_options =
|
||||
alwaysCalc.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
public boolean isCalcOnLoad() {
|
||||
return calcOnLoad.isSet(field_5_options);
|
||||
}
|
||||
public void setCalcOnLoad(boolean flag) {
|
||||
field_5_options =
|
||||
calcOnLoad.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
public boolean isCalcOnLoad() {
|
||||
return calcOnLoad.isSet(field_5_options);
|
||||
}
|
||||
public void setCalcOnLoad(boolean flag) {
|
||||
field_5_options =
|
||||
calcOnLoad.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the formula tokens. never <code>null</code>
|
||||
*/
|
||||
public Ptg[] getParsedExpression() {
|
||||
return field_8_parsed_expr.getTokens();
|
||||
}
|
||||
/**
|
||||
* @return the formula tokens. never <code>null</code>
|
||||
*/
|
||||
public Ptg[] getParsedExpression() {
|
||||
return field_8_parsed_expr.getTokens();
|
||||
}
|
||||
|
||||
public Formula getFormula() {
|
||||
return field_8_parsed_expr;
|
||||
}
|
||||
public Formula getFormula() {
|
||||
return field_8_parsed_expr;
|
||||
}
|
||||
|
||||
public void setParsedExpression(Ptg[] ptgs) {
|
||||
field_8_parsed_expr = Formula.create(ptgs);
|
||||
}
|
||||
public void setParsedExpression(Ptg[] ptgs) {
|
||||
field_8_parsed_expr = Formula.create(ptgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getValueDataSize() {
|
||||
return FIXED_SIZE + field_8_parsed_expr.getEncodedSize();
|
||||
}
|
||||
@Override
|
||||
protected void serializeValue(LittleEndianOutput out) {
|
||||
@Override
|
||||
protected int getValueDataSize() {
|
||||
return FIXED_SIZE + field_8_parsed_expr.getEncodedSize();
|
||||
}
|
||||
@Override
|
||||
protected void serializeValue(LittleEndianOutput out) {
|
||||
|
||||
if (specialCachedValue == null) {
|
||||
out.writeDouble(field_4_value);
|
||||
} else {
|
||||
specialCachedValue.serialize(out);
|
||||
}
|
||||
if (specialCachedValue == null) {
|
||||
out.writeDouble(field_4_value);
|
||||
} else {
|
||||
specialCachedValue.serialize(out);
|
||||
}
|
||||
|
||||
out.writeShort(getOptions());
|
||||
out.writeShort(getOptions());
|
||||
|
||||
out.writeInt(field_6_zero); // may as well write original data back so as to minimise differences from original
|
||||
field_8_parsed_expr.serialize(out);
|
||||
}
|
||||
out.writeInt(field_6_zero); // may as well write original data back so as to minimise differences from original
|
||||
field_8_parsed_expr.serialize(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "FORMULA";
|
||||
}
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "FORMULA";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormulaRecord copy() {
|
||||
return new FormulaRecord(this);
|
||||
}
|
||||
@Override
|
||||
public FormulaRecord copy() {
|
||||
return new FormulaRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FORMULA;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.FORMULA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"base", super::getGenericProperties,
|
||||
"options", this::getOptions,
|
||||
"alwaysCalc", this::isAlwaysCalc,
|
||||
"calcOnLoad", this::isCalcOnLoad,
|
||||
"shared", this::isSharedFormula,
|
||||
"zero", () -> field_6_zero,
|
||||
"value", () -> specialCachedValue == null ? field_4_value : specialCachedValue,
|
||||
"formula", this::getFormula
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"base", super::getGenericProperties,
|
||||
"options", this::getOptions,
|
||||
"alwaysCalc", this::isAlwaysCalc,
|
||||
"calcOnLoad", this::isCalcOnLoad,
|
||||
"shared", this::isSharedFormula,
|
||||
"zero", () -> field_6_zero,
|
||||
"value", () -> specialCachedValue == null ? field_4_value : specialCachedValue,
|
||||
"formula", this::getFormula
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ public final class FtCblsSubRecord extends SubRecord {
|
||||
out.write(reserved);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
protected int getDataSize() {
|
||||
return reserved.length;
|
||||
}
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ public final class GroupMarkerSubRecord extends SubRecord {
|
||||
out.write(reserved);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
protected int getDataSize() {
|
||||
return reserved.length;
|
||||
}
|
||||
|
||||
|
||||
@ -28,99 +28,99 @@ import org.apache.poi.util.StringUtil;
|
||||
* Common header/footer base class
|
||||
*/
|
||||
public abstract class HeaderFooterBase extends StandardRecord {
|
||||
private boolean field_2_hasMultibyte;
|
||||
private String field_3_text;
|
||||
private boolean field_2_hasMultibyte;
|
||||
private String field_3_text;
|
||||
|
||||
protected HeaderFooterBase(String text) {
|
||||
setText(text);
|
||||
}
|
||||
protected HeaderFooterBase(String text) {
|
||||
setText(text);
|
||||
}
|
||||
|
||||
protected HeaderFooterBase(HeaderFooterBase other) {
|
||||
super(other);
|
||||
field_2_hasMultibyte = other.field_2_hasMultibyte;
|
||||
field_3_text = other.field_3_text;
|
||||
}
|
||||
protected HeaderFooterBase(HeaderFooterBase other) {
|
||||
super(other);
|
||||
field_2_hasMultibyte = other.field_2_hasMultibyte;
|
||||
field_3_text = other.field_3_text;
|
||||
}
|
||||
|
||||
protected HeaderFooterBase(RecordInputStream in) {
|
||||
if (in.remaining() > 0) {
|
||||
int field_1_footer_len = in.readShort();
|
||||
//61287 -- if the footer_len == 0, there may not be a multibyte flag
|
||||
if (field_1_footer_len == 0) {
|
||||
field_3_text = "";
|
||||
if (in.remaining() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
field_2_hasMultibyte = in.readByte() != 0x00;
|
||||
protected HeaderFooterBase(RecordInputStream in) {
|
||||
if (in.remaining() > 0) {
|
||||
int field_1_footer_len = in.readShort();
|
||||
//61287 -- if the footer_len == 0, there may not be a multibyte flag
|
||||
if (field_1_footer_len == 0) {
|
||||
field_3_text = "";
|
||||
if (in.remaining() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
field_2_hasMultibyte = in.readByte() != 0x00;
|
||||
|
||||
if (field_2_hasMultibyte) {
|
||||
field_3_text = in.readUnicodeLEString(field_1_footer_len);
|
||||
} else {
|
||||
field_3_text = in.readCompressedUnicode(field_1_footer_len);
|
||||
}
|
||||
} else {
|
||||
// Note - this is unusual for BIFF records in general, but normal for header / footer records:
|
||||
// when the text is empty string, the whole record is empty (just the 4 byte BIFF header)
|
||||
field_3_text = "";
|
||||
}
|
||||
}
|
||||
if (field_2_hasMultibyte) {
|
||||
field_3_text = in.readUnicodeLEString(field_1_footer_len);
|
||||
} else {
|
||||
field_3_text = in.readCompressedUnicode(field_1_footer_len);
|
||||
}
|
||||
} else {
|
||||
// Note - this is unusual for BIFF records in general, but normal for header / footer records:
|
||||
// when the text is empty string, the whole record is empty (just the 4 byte BIFF header)
|
||||
field_3_text = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the footer string
|
||||
*
|
||||
* @param text string to display
|
||||
*/
|
||||
public final void setText(String text) {
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("text must not be null");
|
||||
}
|
||||
field_2_hasMultibyte = StringUtil.hasMultibyte(text);
|
||||
field_3_text = text;
|
||||
/**
|
||||
* set the footer string
|
||||
*
|
||||
* @param text string to display
|
||||
*/
|
||||
public final void setText(String text) {
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("text must not be null");
|
||||
}
|
||||
field_2_hasMultibyte = StringUtil.hasMultibyte(text);
|
||||
field_3_text = text;
|
||||
|
||||
// Check it'll fit into the space in the record
|
||||
if (getDataSize() > RecordInputStream.MAX_RECORD_DATA_SIZE) {
|
||||
throw new IllegalArgumentException("Header/Footer string too long (limit is "
|
||||
+ RecordInputStream.MAX_RECORD_DATA_SIZE + " bytes)");
|
||||
}
|
||||
}
|
||||
// Check it'll fit into the space in the record
|
||||
if (getDataSize() > RecordInputStream.MAX_RECORD_DATA_SIZE) {
|
||||
throw new IllegalArgumentException("Header/Footer string too long (limit is "
|
||||
+ RecordInputStream.MAX_RECORD_DATA_SIZE + " bytes)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the length of the footer string
|
||||
*
|
||||
* @return length of the footer string
|
||||
*/
|
||||
private int getTextLength() {
|
||||
return field_3_text.length();
|
||||
}
|
||||
/**
|
||||
* get the length of the footer string
|
||||
*
|
||||
* @return length of the footer string
|
||||
*/
|
||||
private int getTextLength() {
|
||||
return field_3_text.length();
|
||||
}
|
||||
|
||||
public final String getText() {
|
||||
return field_3_text;
|
||||
}
|
||||
public final String getText() {
|
||||
return field_3_text;
|
||||
}
|
||||
|
||||
public final void serialize(LittleEndianOutput out) {
|
||||
if (getTextLength() > 0) {
|
||||
out.writeShort(getTextLength());
|
||||
out.writeByte(field_2_hasMultibyte ? 0x01 : 0x00);
|
||||
if (field_2_hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_3_text, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_3_text, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
public final void serialize(LittleEndianOutput out) {
|
||||
if (getTextLength() > 0) {
|
||||
out.writeShort(getTextLength());
|
||||
out.writeByte(field_2_hasMultibyte ? 0x01 : 0x00);
|
||||
if (field_2_hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_3_text, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_3_text, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final int getDataSize() {
|
||||
if (getTextLength() < 1) {
|
||||
return 0;
|
||||
}
|
||||
return 3 + getTextLength() * (field_2_hasMultibyte ? 2 : 1);
|
||||
}
|
||||
protected final int getDataSize() {
|
||||
if (getTextLength() < 1) {
|
||||
return 0;
|
||||
}
|
||||
return 3 + getTextLength() * (field_2_hasMultibyte ? 2 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract HeaderFooterBase copy();
|
||||
@Override
|
||||
public abstract HeaderFooterBase copy();
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("text", this::getText);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("text", this::getText);
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ public final class HeaderFooterRecord extends StandardRecord {
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
private static final byte[] BLANK_GUID = new byte[16];
|
||||
|
||||
private byte[] _rawData;
|
||||
private byte[] _rawData;
|
||||
|
||||
public HeaderFooterRecord(byte[] data) {
|
||||
_rawData = data;
|
||||
@ -43,25 +43,25 @@ public final class HeaderFooterRecord extends StandardRecord {
|
||||
_rawData = (other._rawData == null) ? null : other._rawData.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* construct a HeaderFooterRecord record. No fields are interpreted and the record will
|
||||
* be serialized in its original form more or less
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public HeaderFooterRecord(RecordInputStream in) {
|
||||
_rawData = in.readRemainder();
|
||||
}
|
||||
/**
|
||||
* construct a HeaderFooterRecord record. No fields are interpreted and the record will
|
||||
* be serialized in its original form more or less
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public HeaderFooterRecord(RecordInputStream in) {
|
||||
_rawData = in.readRemainder();
|
||||
}
|
||||
|
||||
/**
|
||||
* spit the record out AS IS. no interpretation or identification
|
||||
*/
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.write(_rawData);
|
||||
}
|
||||
/**
|
||||
* spit the record out AS IS. no interpretation or identification
|
||||
*/
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.write(_rawData);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return _rawData.length;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return _rawData.length;
|
||||
}
|
||||
|
||||
public short getSid()
|
||||
{
|
||||
|
||||
@ -21,31 +21,31 @@ package org.apache.poi.hssf.record;
|
||||
* Specifies a header for a sheet
|
||||
*/
|
||||
public final class HeaderRecord extends HeaderFooterBase {
|
||||
public static final short sid = 0x0014;
|
||||
public static final short sid = 0x0014;
|
||||
|
||||
public HeaderRecord(String text) {
|
||||
super(text);
|
||||
}
|
||||
public HeaderRecord(String text) {
|
||||
super(text);
|
||||
}
|
||||
|
||||
public HeaderRecord(HeaderRecord other) {
|
||||
super(other);
|
||||
}
|
||||
public HeaderRecord(HeaderRecord other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public HeaderRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
public HeaderRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderRecord copy() {
|
||||
return new HeaderRecord(this);
|
||||
}
|
||||
@Override
|
||||
public HeaderRecord copy() {
|
||||
return new HeaderRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.HEADER;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,35 +24,35 @@ package org.apache.poi.hssf.record;
|
||||
*/
|
||||
public final class HorizontalPageBreakRecord extends PageBreakRecord {
|
||||
|
||||
public static final short sid = 0x001B;
|
||||
public static final short sid = 0x001B;
|
||||
|
||||
/**
|
||||
* Creates an empty horizontal page break record
|
||||
*/
|
||||
public HorizontalPageBreakRecord() {}
|
||||
/**
|
||||
* Creates an empty horizontal page break record
|
||||
*/
|
||||
public HorizontalPageBreakRecord() {}
|
||||
|
||||
public HorizontalPageBreakRecord(HorizontalPageBreakRecord other) {
|
||||
super(other);
|
||||
}
|
||||
public HorizontalPageBreakRecord(HorizontalPageBreakRecord other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public HorizontalPageBreakRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public HorizontalPageBreakRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HorizontalPageBreakRecord copy() {
|
||||
return new HorizontalPageBreakRecord(this);
|
||||
}
|
||||
@Override
|
||||
public HorizontalPageBreakRecord copy() {
|
||||
return new HorizontalPageBreakRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.HORIZONTAL_PAGE_BREAK;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.HORIZONTAL_PAGE_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ public final class IndexRecord extends StandardRecord {
|
||||
public IndexRecord(RecordInputStream in) {
|
||||
int field_1_zero = in.readInt();
|
||||
if (field_1_zero != 0) {
|
||||
throw new RecordFormatException("Expected zero for field 1 but got " + field_1_zero);
|
||||
throw new RecordFormatException("Expected zero for field 1 but got " + field_1_zero);
|
||||
}
|
||||
field_2_first_row = in.readInt();
|
||||
field_3_last_row_add1 = in.readInt();
|
||||
@ -117,14 +117,14 @@ public final class IndexRecord extends StandardRecord {
|
||||
out.writeInt(getLastRowAdd1());
|
||||
out.writeInt(field_4_zero);
|
||||
for (int k = 0; k < getNumDbcells(); k++) {
|
||||
out.writeInt(getDbcellAt(k));
|
||||
out.writeInt(getDbcellAt(k));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 16 // 4 ints
|
||||
+ getNumDbcells() * 4;
|
||||
+ getNumDbcells() * 4;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -152,8 +152,8 @@ public final class LabelRecord extends Record implements CellValueRecordInterfac
|
||||
}
|
||||
|
||||
/**
|
||||
* NO-OP!
|
||||
*/
|
||||
* NO-OP!
|
||||
*/
|
||||
@Override
|
||||
public void setColumn(short col)
|
||||
{
|
||||
|
||||
@ -65,7 +65,7 @@ public final class LabelSSTRecord extends CellRecord {
|
||||
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "LABELSST";
|
||||
return "LABELSST";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -22,18 +22,18 @@ package org.apache.poi.hssf.record;
|
||||
* This allows much of the code to be generic when it comes to handling margins.
|
||||
*/
|
||||
public interface Margin {
|
||||
// TODO - introduce MarginBaseRecord
|
||||
/**
|
||||
* Get the margin field for the Margin.
|
||||
*
|
||||
* @return the margin
|
||||
*/
|
||||
double getMargin();
|
||||
// TODO - introduce MarginBaseRecord
|
||||
/**
|
||||
* Get the margin field for the Margin.
|
||||
*
|
||||
* @return the margin
|
||||
*/
|
||||
double getMargin();
|
||||
|
||||
/**
|
||||
* Set the margin field for the Margin.
|
||||
*
|
||||
* @param field_1_margin the margin
|
||||
*/
|
||||
void setMargin(double field_1_margin);
|
||||
/**
|
||||
* Set the margin field for the Margin.
|
||||
*
|
||||
* @param field_1_margin the margin
|
||||
*/
|
||||
void setMargin(double field_1_margin);
|
||||
}
|
||||
|
||||
@ -48,23 +48,23 @@ public final class MergeCellsRecord extends StandardRecord {
|
||||
|
||||
|
||||
public MergeCellsRecord(CellRangeAddress[] regions, int startIndex, int numberOfRegions) {
|
||||
_regions = regions;
|
||||
_startIndex = startIndex;
|
||||
_numberOfRegions = numberOfRegions;
|
||||
_regions = regions;
|
||||
_startIndex = startIndex;
|
||||
_numberOfRegions = numberOfRegions;
|
||||
}
|
||||
/**
|
||||
* Constructs a MergedCellsRecord and sets its fields appropriately
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public MergeCellsRecord(RecordInputStream in) {
|
||||
int nRegions = in.readUShort();
|
||||
CellRangeAddress[] cras = new CellRangeAddress[nRegions];
|
||||
for (int i = 0; i < nRegions; i++) {
|
||||
cras[i] = new CellRangeAddress(in);
|
||||
}
|
||||
_numberOfRegions = nRegions;
|
||||
_startIndex = 0;
|
||||
_regions = cras;
|
||||
int nRegions = in.readUShort();
|
||||
CellRangeAddress[] cras = new CellRangeAddress[nRegions];
|
||||
for (int i = 0; i < nRegions; i++) {
|
||||
cras[i] = new CellRangeAddress(in);
|
||||
}
|
||||
_numberOfRegions = nRegions;
|
||||
_startIndex = 0;
|
||||
_regions = cras;
|
||||
}
|
||||
/**
|
||||
* get the number of merged areas. If this drops down to 0 you should just go
|
||||
@ -86,8 +86,8 @@ public final class MergeCellsRecord extends StandardRecord {
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return CellRangeAddressList.getEncodedSize(_numberOfRegions);
|
||||
}
|
||||
return CellRangeAddressList.getEncodedSize(_numberOfRegions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getSid() {
|
||||
@ -98,8 +98,8 @@ public final class MergeCellsRecord extends StandardRecord {
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_numberOfRegions);
|
||||
for (int i = 0; i < _numberOfRegions; i++) {
|
||||
_regions[_startIndex + i].serialize(out);
|
||||
}
|
||||
_regions[_startIndex + i].serialize(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -29,113 +29,113 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* @see BlankRecord
|
||||
*/
|
||||
public final class MulBlankRecord extends StandardRecord {
|
||||
public static final short sid = 0x00BE;
|
||||
public static final short sid = 0x00BE;
|
||||
|
||||
private final int _row;
|
||||
private final int _firstCol;
|
||||
private final short[] _xfs;
|
||||
private final int _lastCol;
|
||||
private final int _row;
|
||||
private final int _firstCol;
|
||||
private final short[] _xfs;
|
||||
private final int _lastCol;
|
||||
|
||||
public MulBlankRecord(int row, int firstCol, short[] xfs) {
|
||||
_row = row;
|
||||
_firstCol = firstCol;
|
||||
_xfs = xfs;
|
||||
_lastCol = firstCol + xfs.length - 1;
|
||||
}
|
||||
public MulBlankRecord(int row, int firstCol, short[] xfs) {
|
||||
_row = row;
|
||||
_firstCol = firstCol;
|
||||
_xfs = xfs;
|
||||
_lastCol = firstCol + xfs.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the row number of the cells this represents
|
||||
*/
|
||||
public int getRow() {
|
||||
return _row;
|
||||
}
|
||||
/**
|
||||
* @return the row number of the cells this represents
|
||||
*/
|
||||
public int getRow() {
|
||||
return _row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return starting column (first cell this holds in the row). Zero based
|
||||
*/
|
||||
public int getFirstColumn() {
|
||||
return _firstCol;
|
||||
}
|
||||
/**
|
||||
* @return starting column (first cell this holds in the row). Zero based
|
||||
*/
|
||||
public int getFirstColumn() {
|
||||
return _firstCol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ending column (last cell this holds in the row). Zero based
|
||||
*/
|
||||
public int getLastColumn() {
|
||||
return _lastCol;
|
||||
}
|
||||
/**
|
||||
* @return ending column (last cell this holds in the row). Zero based
|
||||
*/
|
||||
public int getLastColumn() {
|
||||
return _lastCol;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of columns this contains (last-first +1)
|
||||
* @return number of columns (last - first +1)
|
||||
*/
|
||||
public int getNumColumns() {
|
||||
return _lastCol - _firstCol + 1;
|
||||
}
|
||||
/**
|
||||
* get the number of columns this contains (last-first +1)
|
||||
* @return number of columns (last - first +1)
|
||||
*/
|
||||
public int getNumColumns() {
|
||||
return _lastCol - _firstCol + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the xf index for column (coffset = column - field_2_first_col)
|
||||
* @param coffset the column (coffset = column - field_2_first_col)
|
||||
* @return the XF index for the column
|
||||
*/
|
||||
public short getXFAt(int coffset) {
|
||||
return _xfs[coffset];
|
||||
}
|
||||
/**
|
||||
* returns the xf index for column (coffset = column - field_2_first_col)
|
||||
* @param coffset the column (coffset = column - field_2_first_col)
|
||||
* @return the XF index for the column
|
||||
*/
|
||||
public short getXFAt(int coffset) {
|
||||
return _xfs[coffset];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public MulBlankRecord(RecordInputStream in) {
|
||||
_row = in.readUShort();
|
||||
_firstCol = in.readShort();
|
||||
_xfs = parseXFs(in);
|
||||
_lastCol = in.readShort();
|
||||
}
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public MulBlankRecord(RecordInputStream in) {
|
||||
_row = in.readUShort();
|
||||
_firstCol = in.readShort();
|
||||
_xfs = parseXFs(in);
|
||||
_lastCol = in.readShort();
|
||||
}
|
||||
|
||||
private static short [] parseXFs(RecordInputStream in) {
|
||||
short[] retval = new short[(in.remaining() - 2) / 2];
|
||||
private static short [] parseXFs(RecordInputStream in) {
|
||||
short[] retval = new short[(in.remaining() - 2) / 2];
|
||||
|
||||
for (int idx = 0; idx < retval.length;idx++) {
|
||||
retval[idx] = in.readShort();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
for (int idx = 0; idx < retval.length;idx++) {
|
||||
retval[idx] = in.readShort();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_row);
|
||||
out.writeShort(_firstCol);
|
||||
for (short xf : _xfs) {
|
||||
out.writeShort(xf);
|
||||
}
|
||||
out.writeShort(_lastCol);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_row);
|
||||
out.writeShort(_firstCol);
|
||||
for (short xf : _xfs) {
|
||||
out.writeShort(xf);
|
||||
}
|
||||
out.writeShort(_lastCol);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
// 3 short fields + array of shorts
|
||||
return 6 + _xfs.length * 2;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
// 3 short fields + array of shorts
|
||||
return 6 + _xfs.length * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MulBlankRecord copy() {
|
||||
// immutable - so OK to return this
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public MulBlankRecord copy() {
|
||||
// immutable - so OK to return this
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.MUL_BLANK;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.MUL_BLANK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"firstColumn", this::getFirstColumn,
|
||||
"lastColumn", this::getLastColumn,
|
||||
"xf", () -> _xfs
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"firstColumn", this::getFirstColumn,
|
||||
"lastColumn", this::getLastColumn,
|
||||
"xf", () -> _xfs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,134 +33,134 @@ import org.apache.poi.util.RecordFormatException;
|
||||
* @since 2.0-pre
|
||||
*/
|
||||
public final class MulRKRecord extends StandardRecord {
|
||||
public static final short sid = 0x00BD;
|
||||
public static final short sid = 0x00BD;
|
||||
|
||||
private final int field_1_row;
|
||||
private final short field_2_first_col;
|
||||
private final RkRec[] field_3_rks;
|
||||
private final short field_4_last_col;
|
||||
private final int field_1_row;
|
||||
private final short field_2_first_col;
|
||||
private final RkRec[] field_3_rks;
|
||||
private final short field_4_last_col;
|
||||
|
||||
public int getRow() {
|
||||
return field_1_row;
|
||||
}
|
||||
public int getRow() {
|
||||
return field_1_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* starting column (first cell this holds in the row)
|
||||
* @return first column number
|
||||
*/
|
||||
public short getFirstColumn() {
|
||||
return field_2_first_col;
|
||||
}
|
||||
/**
|
||||
* starting column (first cell this holds in the row)
|
||||
* @return first column number
|
||||
*/
|
||||
public short getFirstColumn() {
|
||||
return field_2_first_col;
|
||||
}
|
||||
|
||||
/**
|
||||
* ending column (last cell this holds in the row)
|
||||
* @return first column number
|
||||
*/
|
||||
public short getLastColumn() {
|
||||
return field_4_last_col;
|
||||
}
|
||||
/**
|
||||
* ending column (last cell this holds in the row)
|
||||
* @return first column number
|
||||
*/
|
||||
public short getLastColumn() {
|
||||
return field_4_last_col;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of columns this contains (last-first +1)
|
||||
* @return number of columns (last - first +1)
|
||||
*/
|
||||
public int getNumColumns() {
|
||||
return field_4_last_col - field_2_first_col + 1;
|
||||
}
|
||||
/**
|
||||
* get the number of columns this contains (last-first +1)
|
||||
* @return number of columns (last - first +1)
|
||||
*/
|
||||
public int getNumColumns() {
|
||||
return field_4_last_col - field_2_first_col + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the xf index for column (coffset = column - field_2_first_col)
|
||||
*
|
||||
/**
|
||||
* returns the xf index for column (coffset = column - field_2_first_col)
|
||||
*
|
||||
* @param coffset the coffset = column - field_2_first_col
|
||||
*
|
||||
* @return the XF index for the column
|
||||
*/
|
||||
public short getXFAt(int coffset) {
|
||||
return field_3_rks[coffset].xf;
|
||||
}
|
||||
* @return the XF index for the column
|
||||
*/
|
||||
public short getXFAt(int coffset) {
|
||||
return field_3_rks[coffset].xf;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the rk number for column (coffset = column - field_2_first_col)
|
||||
*
|
||||
* @param coffset the coffset = column - field_2_first_col
|
||||
*
|
||||
* @return the value (decoded into a double)
|
||||
*/
|
||||
public double getRKNumberAt(int coffset) {
|
||||
return RKUtil.decodeNumber(field_3_rks[coffset].rk);
|
||||
}
|
||||
/**
|
||||
* returns the rk number for column (coffset = column - field_2_first_col)
|
||||
*
|
||||
* @param coffset the coffset = column - field_2_first_col
|
||||
*
|
||||
* @return the value (decoded into a double)
|
||||
*/
|
||||
public double getRKNumberAt(int coffset) {
|
||||
return RKUtil.decodeNumber(field_3_rks[coffset].rk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public MulRKRecord(RecordInputStream in) {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_first_col = in.readShort();
|
||||
field_3_rks = RkRec.parseRKs(in);
|
||||
field_4_last_col = in.readShort();
|
||||
}
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
public MulRKRecord(RecordInputStream in) {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_first_col = in.readShort();
|
||||
field_3_rks = RkRec.parseRKs(in);
|
||||
field_4_last_col = in.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public short getSid()
|
||||
{
|
||||
return sid;
|
||||
}
|
||||
{
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
throw new RecordFormatException( "Sorry, you can't serialize MulRK in this release");
|
||||
}
|
||||
@Override
|
||||
throw new RecordFormatException( "Sorry, you can't serialize MulRK in this release");
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
throw new RecordFormatException( "Sorry, you can't serialize MulRK in this release");
|
||||
}
|
||||
throw new RecordFormatException( "Sorry, you can't serialize MulRK in this release");
|
||||
}
|
||||
|
||||
private static final class RkRec implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 6;
|
||||
public final short xf;
|
||||
public final int rk;
|
||||
private static final class RkRec implements GenericRecord {
|
||||
public static final int ENCODED_SIZE = 6;
|
||||
public final short xf;
|
||||
public final int rk;
|
||||
|
||||
private RkRec(RecordInputStream in) {
|
||||
xf = in.readShort();
|
||||
rk = in.readInt();
|
||||
}
|
||||
private RkRec(RecordInputStream in) {
|
||||
xf = in.readShort();
|
||||
rk = in.readInt();
|
||||
}
|
||||
|
||||
public static RkRec[] parseRKs(RecordInputStream in) {
|
||||
int nItems = (in.remaining()-2) / ENCODED_SIZE;
|
||||
RkRec[] retval = new RkRec[nItems];
|
||||
for (int i=0; i<nItems; i++) {
|
||||
retval[i] = new RkRec(in);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
public static RkRec[] parseRKs(RecordInputStream in) {
|
||||
int nItems = (in.remaining()-2) / ENCODED_SIZE;
|
||||
RkRec[] retval = new RkRec[nItems];
|
||||
for (int i=0; i<nItems; i++) {
|
||||
retval[i] = new RkRec(in);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"xf", () -> xf,
|
||||
"rk", () -> rk
|
||||
);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"xf", () -> xf,
|
||||
"rk", () -> rk
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MulRKRecord copy() {
|
||||
// immutable - so OK to return this
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public MulRKRecord copy() {
|
||||
// immutable - so OK to return this
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.MUL_RK;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.MUL_RK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"firstColumn", this::getFirstColumn,
|
||||
"lastColumn", this::getLastColumn,
|
||||
"rk", () -> field_3_rks
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"firstColumn", this::getFirstColumn,
|
||||
"lastColumn", this::getLastColumn,
|
||||
"rk", () -> field_3_rks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -28,237 +28,237 @@ import org.apache.poi.util.StringUtil;
|
||||
* NOTE: Comment Associated with a Cell (0x001C)
|
||||
*/
|
||||
public final class NoteRecord extends StandardRecord {
|
||||
public static final short sid = 0x001C;
|
||||
public static final short sid = 0x001C;
|
||||
|
||||
public static final NoteRecord[] EMPTY_ARRAY = { };
|
||||
public static final NoteRecord[] EMPTY_ARRAY = { };
|
||||
|
||||
/**
|
||||
* Flag indicating that the comment is hidden (default)
|
||||
*/
|
||||
public static final short NOTE_HIDDEN = 0x0;
|
||||
/**
|
||||
* Flag indicating that the comment is hidden (default)
|
||||
*/
|
||||
public static final short NOTE_HIDDEN = 0x0;
|
||||
|
||||
/**
|
||||
* Flag indicating that the comment is visible
|
||||
*/
|
||||
public static final short NOTE_VISIBLE = 0x2;
|
||||
/**
|
||||
* Flag indicating that the comment is visible
|
||||
*/
|
||||
public static final short NOTE_VISIBLE = 0x2;
|
||||
|
||||
private static final Byte DEFAULT_PADDING = (byte) 0;
|
||||
private static final Byte DEFAULT_PADDING = (byte) 0;
|
||||
|
||||
private int field_1_row;
|
||||
private int field_2_col;
|
||||
private short field_3_flags;
|
||||
private int field_4_shapeid;
|
||||
private boolean field_5_hasMultibyte;
|
||||
private String field_6_author;
|
||||
private int field_1_row;
|
||||
private int field_2_col;
|
||||
private short field_3_flags;
|
||||
private int field_4_shapeid;
|
||||
private boolean field_5_hasMultibyte;
|
||||
private String field_6_author;
|
||||
|
||||
/**
|
||||
* Saves padding byte value to reduce delta during round-trip serialization.<br>
|
||||
*
|
||||
* The documentation is not clear about how padding should work. In any case
|
||||
* Excel(2007) does something different.
|
||||
*/
|
||||
private Byte field_7_padding;
|
||||
/**
|
||||
* Saves padding byte value to reduce delta during round-trip serialization.<br>
|
||||
*
|
||||
* The documentation is not clear about how padding should work. In any case
|
||||
* Excel(2007) does something different.
|
||||
*/
|
||||
private Byte field_7_padding;
|
||||
|
||||
/**
|
||||
* Construct a new <code>NoteRecord</code> and
|
||||
* fill its data with the default values
|
||||
*/
|
||||
public NoteRecord() {
|
||||
field_6_author = "";
|
||||
field_3_flags = 0;
|
||||
field_7_padding = DEFAULT_PADDING; // seems to be always present regardless of author text
|
||||
}
|
||||
/**
|
||||
* Construct a new <code>NoteRecord</code> and
|
||||
* fill its data with the default values
|
||||
*/
|
||||
public NoteRecord() {
|
||||
field_6_author = "";
|
||||
field_3_flags = 0;
|
||||
field_7_padding = DEFAULT_PADDING; // seems to be always present regardless of author text
|
||||
}
|
||||
|
||||
public NoteRecord(NoteRecord other) {
|
||||
super(other);
|
||||
field_1_row = other.field_1_row;
|
||||
field_2_col = other.field_2_col;
|
||||
field_3_flags = other.field_3_flags;
|
||||
field_4_shapeid = other.field_4_shapeid;
|
||||
field_5_hasMultibyte = other.field_5_hasMultibyte;
|
||||
field_6_author = other.field_6_author;
|
||||
field_7_padding = other.field_7_padding;
|
||||
}
|
||||
public NoteRecord(NoteRecord other) {
|
||||
super(other);
|
||||
field_1_row = other.field_1_row;
|
||||
field_2_col = other.field_2_col;
|
||||
field_3_flags = other.field_3_flags;
|
||||
field_4_shapeid = other.field_4_shapeid;
|
||||
field_5_hasMultibyte = other.field_5_hasMultibyte;
|
||||
field_6_author = other.field_6_author;
|
||||
field_7_padding = other.field_7_padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return id of this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
/**
|
||||
* @return id of this record.
|
||||
*/
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the record data from the supplied <code>RecordInputStream</code>
|
||||
*
|
||||
* @param in the RecordInputStream to read from
|
||||
*/
|
||||
public NoteRecord(RecordInputStream in) {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_col = in.readShort();
|
||||
field_3_flags = in.readShort();
|
||||
field_4_shapeid = in.readUShort();
|
||||
int length = in.readShort();
|
||||
field_5_hasMultibyte = in.readByte() != 0x00;
|
||||
if (field_5_hasMultibyte) {
|
||||
field_6_author = StringUtil.readUnicodeLE(in, length);
|
||||
} else {
|
||||
field_6_author = StringUtil.readCompressedUnicode(in, length);
|
||||
}
|
||||
if (in.available() == 1) {
|
||||
field_7_padding = in.readByte();
|
||||
} else if (in.available() == 2 && length == 0) {
|
||||
// If there's no author, may be double padded
|
||||
/**
|
||||
* Read the record data from the supplied <code>RecordInputStream</code>
|
||||
*
|
||||
* @param in the RecordInputStream to read from
|
||||
*/
|
||||
public NoteRecord(RecordInputStream in) {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_col = in.readShort();
|
||||
field_3_flags = in.readShort();
|
||||
field_4_shapeid = in.readUShort();
|
||||
int length = in.readShort();
|
||||
field_5_hasMultibyte = in.readByte() != 0x00;
|
||||
if (field_5_hasMultibyte) {
|
||||
field_6_author = StringUtil.readUnicodeLE(in, length);
|
||||
} else {
|
||||
field_6_author = StringUtil.readCompressedUnicode(in, length);
|
||||
}
|
||||
if (in.available() == 1) {
|
||||
field_7_padding = in.readByte();
|
||||
} else if (in.available() == 2 && length == 0) {
|
||||
// If there's no author, may be double padded
|
||||
field_7_padding = in.readByte();
|
||||
in.readByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_row);
|
||||
out.writeShort(field_2_col);
|
||||
out.writeShort(field_3_flags);
|
||||
out.writeShort(field_4_shapeid);
|
||||
out.writeShort(field_6_author.length());
|
||||
out.writeByte(field_5_hasMultibyte ? 0x01 : 0x00);
|
||||
if (field_5_hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_6_author, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_6_author, out);
|
||||
}
|
||||
if (field_7_padding != null) {
|
||||
out.writeByte(field_7_padding.intValue());
|
||||
}
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_row);
|
||||
out.writeShort(field_2_col);
|
||||
out.writeShort(field_3_flags);
|
||||
out.writeShort(field_4_shapeid);
|
||||
out.writeShort(field_6_author.length());
|
||||
out.writeByte(field_5_hasMultibyte ? 0x01 : 0x00);
|
||||
if (field_5_hasMultibyte) {
|
||||
StringUtil.putUnicodeLE(field_6_author, out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_6_author, out);
|
||||
}
|
||||
if (field_7_padding != null) {
|
||||
out.writeByte(field_7_padding.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 11 // 5 shorts + 1 byte
|
||||
+ field_6_author.length() * (field_5_hasMultibyte ? 2 : 1)
|
||||
+ (field_7_padding == null ? 0 : 1);
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 11 // 5 shorts + 1 byte
|
||||
+ field_6_author.length() * (field_5_hasMultibyte ? 2 : 1)
|
||||
+ (field_7_padding == null ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the row that contains the comment
|
||||
*
|
||||
* @return the row that contains the comment
|
||||
*/
|
||||
public int getRow() {
|
||||
return field_1_row;
|
||||
}
|
||||
/**
|
||||
* Return the row that contains the comment
|
||||
*
|
||||
* @return the row that contains the comment
|
||||
*/
|
||||
public int getRow() {
|
||||
return field_1_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the row that contains the comment
|
||||
*
|
||||
* @param row the row that contains the comment
|
||||
*/
|
||||
public void setRow(int row) {
|
||||
field_1_row = row;
|
||||
}
|
||||
/**
|
||||
* Specify the row that contains the comment
|
||||
*
|
||||
* @param row the row that contains the comment
|
||||
*/
|
||||
public void setRow(int row) {
|
||||
field_1_row = row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column that contains the comment
|
||||
*
|
||||
* @return the column that contains the comment
|
||||
*/
|
||||
public int getColumn() {
|
||||
return field_2_col;
|
||||
}
|
||||
/**
|
||||
* Return the column that contains the comment
|
||||
*
|
||||
* @return the column that contains the comment
|
||||
*/
|
||||
public int getColumn() {
|
||||
return field_2_col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the column that contains the comment
|
||||
*
|
||||
* @param col the column that contains the comment
|
||||
*/
|
||||
public void setColumn(int col) {
|
||||
field_2_col = col;
|
||||
}
|
||||
/**
|
||||
* Specify the column that contains the comment
|
||||
*
|
||||
* @param col the column that contains the comment
|
||||
*/
|
||||
public void setColumn(int col) {
|
||||
field_2_col = col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options flags.
|
||||
*
|
||||
* @return the options flag
|
||||
* @see #NOTE_VISIBLE
|
||||
* @see #NOTE_HIDDEN
|
||||
*/
|
||||
public short getFlags() {
|
||||
return field_3_flags;
|
||||
}
|
||||
/**
|
||||
* Options flags.
|
||||
*
|
||||
* @return the options flag
|
||||
* @see #NOTE_VISIBLE
|
||||
* @see #NOTE_HIDDEN
|
||||
*/
|
||||
public short getFlags() {
|
||||
return field_3_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options flag
|
||||
*
|
||||
* @param flags the options flag
|
||||
* @see #NOTE_VISIBLE
|
||||
* @see #NOTE_HIDDEN
|
||||
*/
|
||||
public void setFlags(short flags) {
|
||||
field_3_flags = flags;
|
||||
}
|
||||
/**
|
||||
* Options flag
|
||||
*
|
||||
* @param flags the options flag
|
||||
* @see #NOTE_VISIBLE
|
||||
* @see #NOTE_HIDDEN
|
||||
*/
|
||||
public void setFlags(short flags) {
|
||||
field_3_flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unit testing only!
|
||||
*
|
||||
* @return true, if author element uses multi byte
|
||||
*/
|
||||
boolean authorIsMultibyte() {
|
||||
return field_5_hasMultibyte;
|
||||
}
|
||||
/**
|
||||
* For unit testing only!
|
||||
*
|
||||
* @return true, if author element uses multi byte
|
||||
*/
|
||||
boolean authorIsMultibyte() {
|
||||
return field_5_hasMultibyte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object id for OBJ record that contains the comment
|
||||
*
|
||||
* @return the Object id for OBJ record that contains the comment
|
||||
*/
|
||||
public int getShapeId() {
|
||||
return field_4_shapeid;
|
||||
}
|
||||
/**
|
||||
* Object id for OBJ record that contains the comment
|
||||
*
|
||||
* @return the Object id for OBJ record that contains the comment
|
||||
*/
|
||||
public int getShapeId() {
|
||||
return field_4_shapeid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object id for OBJ record that contains the comment
|
||||
*
|
||||
* @param id the Object id for OBJ record that contains the comment
|
||||
*/
|
||||
public void setShapeId(int id) {
|
||||
field_4_shapeid = id;
|
||||
}
|
||||
/**
|
||||
* Object id for OBJ record that contains the comment
|
||||
*
|
||||
* @param id the Object id for OBJ record that contains the comment
|
||||
*/
|
||||
public void setShapeId(int id) {
|
||||
field_4_shapeid = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the original comment author
|
||||
*
|
||||
* @return the name of the original author of the comment
|
||||
*/
|
||||
public String getAuthor() {
|
||||
return field_6_author;
|
||||
}
|
||||
/**
|
||||
* Name of the original comment author
|
||||
*
|
||||
* @return the name of the original author of the comment
|
||||
*/
|
||||
public String getAuthor() {
|
||||
return field_6_author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the original comment author
|
||||
*
|
||||
* @param author the name of the original author of the comment
|
||||
*/
|
||||
public void setAuthor(String author) {
|
||||
field_6_author = author;
|
||||
field_5_hasMultibyte = StringUtil.hasMultibyte(author);
|
||||
}
|
||||
/**
|
||||
* Name of the original comment author
|
||||
*
|
||||
* @param author the name of the original author of the comment
|
||||
*/
|
||||
public void setAuthor(String author) {
|
||||
field_6_author = author;
|
||||
field_5_hasMultibyte = StringUtil.hasMultibyte(author);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NoteRecord copy() {
|
||||
return new NoteRecord(this);
|
||||
}
|
||||
@Override
|
||||
public NoteRecord copy() {
|
||||
return new NoteRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.NOTE;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.NOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"column", this::getColumn,
|
||||
"flags", this::getFlags,
|
||||
"shapeId", this::getShapeId,
|
||||
"author", this::getAuthor
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"row", this::getRow,
|
||||
"column", this::getColumn,
|
||||
"flags", this::getFlags,
|
||||
"shapeId", this::getShapeId,
|
||||
"author", this::getAuthor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public final class NoteStructureSubRecord extends SubRecord {
|
||||
out.write(reserved);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return reserved.length;
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ public abstract class PageBreakRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return _breaks.isEmpty();
|
||||
return _breaks.isEmpty();
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 2 + _breaks.size() * Break.ENCODED_SIZE;
|
||||
|
||||
@ -75,17 +75,17 @@ public final class RKRecord extends CellRecord {
|
||||
|
||||
@Override
|
||||
protected String getRecordName() {
|
||||
return "RK";
|
||||
return "RK";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void serializeValue(LittleEndianOutput out) {
|
||||
out.writeInt(field_4_rk_number);
|
||||
out.writeInt(field_4_rk_number);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getValueDataSize() {
|
||||
return 4;
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -56,9 +56,9 @@ public final class RecalcIdRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
public RecalcIdRecord(RecordInputStream in) {
|
||||
in.readUShort(); // field 'rt' should have value 0x01C1, but Excel doesn't care during reading
|
||||
_reserved0 = in.readUShort();
|
||||
_engineId = in.readInt();
|
||||
in.readUShort(); // field 'rt' should have value 0x01C1, but Excel doesn't care during reading
|
||||
_reserved0 = in.readUShort();
|
||||
_engineId = in.readInt();
|
||||
}
|
||||
|
||||
public boolean isNeeded() {
|
||||
|
||||
@ -21,22 +21,22 @@ package org.apache.poi.hssf.record;
|
||||
* Common base class of {@link Record} and {@link org.apache.poi.hssf.record.aggregates.RecordAggregate}
|
||||
*/
|
||||
public abstract class RecordBase {
|
||||
/**
|
||||
* called by the class that is responsible for writing this sucker.
|
||||
* Subclasses should implement this so that their data is passed back in a
|
||||
* byte array.
|
||||
*
|
||||
* @param offset to begin writing at
|
||||
* @param data byte array containing instance data
|
||||
* @return number of bytes written
|
||||
*/
|
||||
public abstract int serialize(int offset, byte[] data);
|
||||
/**
|
||||
* called by the class that is responsible for writing this sucker.
|
||||
* Subclasses should implement this so that their data is passed back in a
|
||||
* byte array.
|
||||
*
|
||||
* @param offset to begin writing at
|
||||
* @param data byte array containing instance data
|
||||
* @return number of bytes written
|
||||
*/
|
||||
public abstract int serialize(int offset, byte[] data);
|
||||
|
||||
/**
|
||||
* gives the current serialized size of the record. Should include the sid
|
||||
* and reclength (4 bytes).
|
||||
*
|
||||
* @return the record size
|
||||
*/
|
||||
public abstract int getRecordSize();
|
||||
/**
|
||||
* gives the current serialized size of the record. Should include the sid
|
||||
* and reclength (4 bytes).
|
||||
*
|
||||
* @return the record size
|
||||
*/
|
||||
public abstract int getRecordSize();
|
||||
}
|
||||
|
||||
@ -41,75 +41,75 @@ import org.apache.poi.util.RecordFormatException;
|
||||
*/
|
||||
public final class RecordFactoryInputStream {
|
||||
|
||||
/**
|
||||
* Keeps track of the sizes of the initial records up to and including {@link FilePassRecord}
|
||||
* Needed for protected files because each byte is encrypted with respect to its absolute
|
||||
* position from the start of the stream.
|
||||
*/
|
||||
private static final class StreamEncryptionInfo {
|
||||
private final int _initialRecordsSize;
|
||||
private final FilePassRecord _filePassRec;
|
||||
private final Record _lastRecord;
|
||||
private final boolean _hasBOFRecord;
|
||||
/**
|
||||
* Keeps track of the sizes of the initial records up to and including {@link FilePassRecord}
|
||||
* Needed for protected files because each byte is encrypted with respect to its absolute
|
||||
* position from the start of the stream.
|
||||
*/
|
||||
private static final class StreamEncryptionInfo {
|
||||
private final int _initialRecordsSize;
|
||||
private final FilePassRecord _filePassRec;
|
||||
private final Record _lastRecord;
|
||||
private final boolean _hasBOFRecord;
|
||||
|
||||
public StreamEncryptionInfo(RecordInputStream rs, List<org.apache.poi.hssf.record.Record> outputRecs) {
|
||||
Record rec;
|
||||
rs.nextRecord();
|
||||
int recSize = 4 + rs.remaining();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
outputRecs.add(rec);
|
||||
FilePassRecord fpr = null;
|
||||
if (rec instanceof BOFRecord) {
|
||||
_hasBOFRecord = true;
|
||||
|
||||
// Fetch the next record, and see if it indicates whether
|
||||
// the document is encrypted or not
|
||||
if (rs.hasNextRecord()) {
|
||||
rs.nextRecord();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
recSize += rec.getRecordSize();
|
||||
outputRecs.add(rec);
|
||||
|
||||
// Encrypted is normally BOF then FILEPASS
|
||||
// May sometimes be BOF, WRITEPROTECT, FILEPASS
|
||||
if (rec instanceof WriteProtectRecord && rs.hasNextRecord()) {
|
||||
rs.nextRecord();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
recSize += rec.getRecordSize();
|
||||
outputRecs.add(rec);
|
||||
}
|
||||
|
||||
// If it's a FILEPASS, track it specifically
|
||||
if (rec instanceof FilePassRecord) {
|
||||
fpr = (FilePassRecord) rec;
|
||||
}
|
||||
public StreamEncryptionInfo(RecordInputStream rs, List<org.apache.poi.hssf.record.Record> outputRecs) {
|
||||
Record rec;
|
||||
rs.nextRecord();
|
||||
int recSize = 4 + rs.remaining();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
outputRecs.add(rec);
|
||||
FilePassRecord fpr = null;
|
||||
if (rec instanceof BOFRecord) {
|
||||
_hasBOFRecord = true;
|
||||
|
||||
// Fetch the next record, and see if it indicates whether
|
||||
// the document is encrypted or not
|
||||
if (rs.hasNextRecord()) {
|
||||
rs.nextRecord();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
recSize += rec.getRecordSize();
|
||||
outputRecs.add(rec);
|
||||
|
||||
// Encrypted is normally BOF then FILEPASS
|
||||
// May sometimes be BOF, WRITEPROTECT, FILEPASS
|
||||
if (rec instanceof WriteProtectRecord && rs.hasNextRecord()) {
|
||||
rs.nextRecord();
|
||||
rec = RecordFactory.createSingleRecord(rs);
|
||||
recSize += rec.getRecordSize();
|
||||
outputRecs.add(rec);
|
||||
}
|
||||
|
||||
// If it's a FILEPASS, track it specifically
|
||||
if (rec instanceof FilePassRecord) {
|
||||
fpr = (FilePassRecord) rec;
|
||||
}
|
||||
|
||||
// workbook not encrypted (typical case)
|
||||
if (rec instanceof EOFRecord) {
|
||||
// A workbook stream is never empty, so crash instead
|
||||
// of trying to keep track of nesting level
|
||||
throw new IllegalStateException("Nothing between BOF and EOF");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Invalid in a normal workbook stream.
|
||||
// However, some test cases work on sub-sections of
|
||||
// the workbook stream that do not begin with BOF
|
||||
_hasBOFRecord = false;
|
||||
}
|
||||
_initialRecordsSize = recSize;
|
||||
_filePassRec = fpr;
|
||||
_lastRecord = rec;
|
||||
}
|
||||
// workbook not encrypted (typical case)
|
||||
if (rec instanceof EOFRecord) {
|
||||
// A workbook stream is never empty, so crash instead
|
||||
// of trying to keep track of nesting level
|
||||
throw new IllegalStateException("Nothing between BOF and EOF");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Invalid in a normal workbook stream.
|
||||
// However, some test cases work on sub-sections of
|
||||
// the workbook stream that do not begin with BOF
|
||||
_hasBOFRecord = false;
|
||||
}
|
||||
_initialRecordsSize = recSize;
|
||||
_filePassRec = fpr;
|
||||
_lastRecord = rec;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"squid:S2068"})
|
||||
public RecordInputStream createDecryptingStream(InputStream original) {
|
||||
@SuppressWarnings({"squid:S2068"})
|
||||
public RecordInputStream createDecryptingStream(InputStream original) {
|
||||
String userPassword = Biff8EncryptionKey.getCurrentUserPassword();
|
||||
if (userPassword == null) {
|
||||
userPassword = Decryptor.DEFAULT_PASSWORD;
|
||||
}
|
||||
if (userPassword == null) {
|
||||
userPassword = Decryptor.DEFAULT_PASSWORD;
|
||||
}
|
||||
|
||||
EncryptionInfo info = _filePassRec.getEncryptionInfo();
|
||||
EncryptionInfo info = _filePassRec.getEncryptionInfo();
|
||||
try {
|
||||
if (!info.getDecryptor().verifyPassword(userPassword)) {
|
||||
throw new EncryptedDocumentException(
|
||||
@ -120,250 +120,250 @@ public final class RecordFactoryInputStream {
|
||||
throw new EncryptedDocumentException(e);
|
||||
}
|
||||
|
||||
return new RecordInputStream(original, info, _initialRecordsSize);
|
||||
}
|
||||
return new RecordInputStream(original, info, _initialRecordsSize);
|
||||
}
|
||||
|
||||
public boolean hasEncryption() {
|
||||
return _filePassRec != null;
|
||||
}
|
||||
public boolean hasEncryption() {
|
||||
return _filePassRec != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return last record scanned while looking for encryption info.
|
||||
* This will typically be the first or second record read. Possibly <code>null</code>
|
||||
* if stream was empty
|
||||
*/
|
||||
public Record getLastRecord() {
|
||||
return _lastRecord;
|
||||
}
|
||||
/**
|
||||
* @return last record scanned while looking for encryption info.
|
||||
* This will typically be the first or second record read. Possibly <code>null</code>
|
||||
* if stream was empty
|
||||
*/
|
||||
public Record getLastRecord() {
|
||||
return _lastRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>false</code> in some test cases
|
||||
*/
|
||||
public boolean hasBOFRecord() {
|
||||
return _hasBOFRecord;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>false</code> in some test cases
|
||||
*/
|
||||
public boolean hasBOFRecord() {
|
||||
return _hasBOFRecord;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final RecordInputStream _recStream;
|
||||
private final boolean _shouldIncludeContinueRecords;
|
||||
private final RecordInputStream _recStream;
|
||||
private final boolean _shouldIncludeContinueRecords;
|
||||
|
||||
/**
|
||||
* Temporarily stores a group of {@link Record}s, for future return by {@link #nextRecord()}.
|
||||
* This is used at the start of the workbook stream, and also when the most recently read
|
||||
* underlying record is a {@link MulRKRecord}
|
||||
*/
|
||||
private Record[] _unreadRecordBuffer;
|
||||
/**
|
||||
* Temporarily stores a group of {@link Record}s, for future return by {@link #nextRecord()}.
|
||||
* This is used at the start of the workbook stream, and also when the most recently read
|
||||
* underlying record is a {@link MulRKRecord}
|
||||
*/
|
||||
private Record[] _unreadRecordBuffer;
|
||||
|
||||
/**
|
||||
* used to help iterating over the unread records
|
||||
*/
|
||||
private int _unreadRecordIndex = -1;
|
||||
/**
|
||||
* used to help iterating over the unread records
|
||||
*/
|
||||
private int _unreadRecordIndex = -1;
|
||||
|
||||
/**
|
||||
* The most recent record that we gave to the user
|
||||
*/
|
||||
private Record _lastRecord;
|
||||
/**
|
||||
* The most recent DrawingRecord seen
|
||||
*/
|
||||
private DrawingRecord _lastDrawingRecord = new DrawingRecord();
|
||||
/**
|
||||
* The most recent record that we gave to the user
|
||||
*/
|
||||
private Record _lastRecord;
|
||||
/**
|
||||
* The most recent DrawingRecord seen
|
||||
*/
|
||||
private DrawingRecord _lastDrawingRecord = new DrawingRecord();
|
||||
|
||||
private int _bofDepth;
|
||||
private int _bofDepth;
|
||||
|
||||
private boolean _lastRecordWasEOFLevelZero;
|
||||
private boolean _lastRecordWasEOFLevelZero;
|
||||
|
||||
|
||||
/**
|
||||
* @param in the InputStream to read from
|
||||
*
|
||||
* @param shouldIncludeContinueRecords caller can pass <code>false</code> if loose
|
||||
* {@link ContinueRecord}s should be skipped (this is sometimes useful in event based
|
||||
* processing).
|
||||
*/
|
||||
public RecordFactoryInputStream(InputStream in, boolean shouldIncludeContinueRecords) {
|
||||
RecordInputStream rs = new RecordInputStream(in);
|
||||
List<org.apache.poi.hssf.record.Record> records = new ArrayList<>();
|
||||
StreamEncryptionInfo sei = new StreamEncryptionInfo(rs, records);
|
||||
if (sei.hasEncryption()) {
|
||||
rs = sei.createDecryptingStream(in);
|
||||
} else {
|
||||
// typical case - non-encrypted stream
|
||||
}
|
||||
/**
|
||||
* @param in the InputStream to read from
|
||||
*
|
||||
* @param shouldIncludeContinueRecords caller can pass <code>false</code> if loose
|
||||
* {@link ContinueRecord}s should be skipped (this is sometimes useful in event based
|
||||
* processing).
|
||||
*/
|
||||
public RecordFactoryInputStream(InputStream in, boolean shouldIncludeContinueRecords) {
|
||||
RecordInputStream rs = new RecordInputStream(in);
|
||||
List<org.apache.poi.hssf.record.Record> records = new ArrayList<>();
|
||||
StreamEncryptionInfo sei = new StreamEncryptionInfo(rs, records);
|
||||
if (sei.hasEncryption()) {
|
||||
rs = sei.createDecryptingStream(in);
|
||||
} else {
|
||||
// typical case - non-encrypted stream
|
||||
}
|
||||
|
||||
if (!records.isEmpty()) {
|
||||
_unreadRecordBuffer = new Record[records.size()];
|
||||
records.toArray(_unreadRecordBuffer);
|
||||
_unreadRecordIndex =0;
|
||||
}
|
||||
_recStream = rs;
|
||||
_shouldIncludeContinueRecords = shouldIncludeContinueRecords;
|
||||
_lastRecord = sei.getLastRecord();
|
||||
if (!records.isEmpty()) {
|
||||
_unreadRecordBuffer = new Record[records.size()];
|
||||
records.toArray(_unreadRecordBuffer);
|
||||
_unreadRecordIndex =0;
|
||||
}
|
||||
_recStream = rs;
|
||||
_shouldIncludeContinueRecords = shouldIncludeContinueRecords;
|
||||
_lastRecord = sei.getLastRecord();
|
||||
|
||||
/*
|
||||
* How to recognise end of stream?
|
||||
* In the best case, the underlying input stream (in) ends just after the last EOF record
|
||||
* Usually however, the stream is padded with an arbitrary byte count. Excel and most apps
|
||||
* reliably use zeros for padding and if this were always the case, this code could just
|
||||
* skip all the (zero sized) records with sid==0. However, bug 46987 shows a file with
|
||||
* non-zero padding that is read OK by Excel (Excel also fixes the padding).
|
||||
*
|
||||
* So to properly detect the workbook end of stream, this code has to identify the last
|
||||
* EOF record. This is not so easy because the worbook bof+eof pair do not bracket the
|
||||
* whole stream. The worksheets follow the workbook, but it is not easy to tell how many
|
||||
* sheet sub-streams should be present. Hence we are looking for an EOF record that is not
|
||||
* immediately followed by a BOF record. One extra complication is that bof+eof sub-
|
||||
* streams can be nested within worksheet streams and it's not clear in these cases what
|
||||
* record might follow any EOF record. So we also need to keep track of the bof/eof
|
||||
* nesting level.
|
||||
*/
|
||||
_bofDepth = sei.hasBOFRecord() ? 1 : 0;
|
||||
_lastRecordWasEOFLevelZero = false;
|
||||
}
|
||||
/*
|
||||
* How to recognise end of stream?
|
||||
* In the best case, the underlying input stream (in) ends just after the last EOF record
|
||||
* Usually however, the stream is padded with an arbitrary byte count. Excel and most apps
|
||||
* reliably use zeros for padding and if this were always the case, this code could just
|
||||
* skip all the (zero sized) records with sid==0. However, bug 46987 shows a file with
|
||||
* non-zero padding that is read OK by Excel (Excel also fixes the padding).
|
||||
*
|
||||
* So to properly detect the workbook end of stream, this code has to identify the last
|
||||
* EOF record. This is not so easy because the worbook bof+eof pair do not bracket the
|
||||
* whole stream. The worksheets follow the workbook, but it is not easy to tell how many
|
||||
* sheet sub-streams should be present. Hence we are looking for an EOF record that is not
|
||||
* immediately followed by a BOF record. One extra complication is that bof+eof sub-
|
||||
* streams can be nested within worksheet streams and it's not clear in these cases what
|
||||
* record might follow any EOF record. So we also need to keep track of the bof/eof
|
||||
* nesting level.
|
||||
*/
|
||||
_bofDepth = sei.hasBOFRecord() ? 1 : 0;
|
||||
_lastRecordWasEOFLevelZero = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next (complete) record from the stream, or null if there are no more.
|
||||
*/
|
||||
public Record nextRecord() {
|
||||
Record r;
|
||||
r = getNextUnreadRecord();
|
||||
if (r != null) {
|
||||
// found an unread record
|
||||
return r;
|
||||
}
|
||||
while (true) {
|
||||
if (!_recStream.hasNextRecord()) {
|
||||
// recStream is exhausted;
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @return the next (complete) record from the stream, or null if there are no more.
|
||||
*/
|
||||
public Record nextRecord() {
|
||||
Record r;
|
||||
r = getNextUnreadRecord();
|
||||
if (r != null) {
|
||||
// found an unread record
|
||||
return r;
|
||||
}
|
||||
while (true) {
|
||||
if (!_recStream.hasNextRecord()) {
|
||||
// recStream is exhausted;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_lastRecordWasEOFLevelZero) {
|
||||
// Potential place for ending the workbook stream
|
||||
// Check that the next record is not BOFRecord(0x0809)
|
||||
// Normally the input stream contains only zero padding after the last EOFRecord,
|
||||
// but bug 46987 and 48068 suggests that the padding may be garbage.
|
||||
// This code relies on the padding bytes not starting with BOFRecord.sid
|
||||
if (_recStream.getNextSid() != BOFRecord.sid) {
|
||||
return null;
|
||||
}
|
||||
// else - another sheet substream starting here
|
||||
}
|
||||
if (_lastRecordWasEOFLevelZero) {
|
||||
// Potential place for ending the workbook stream
|
||||
// Check that the next record is not BOFRecord(0x0809)
|
||||
// Normally the input stream contains only zero padding after the last EOFRecord,
|
||||
// but bug 46987 and 48068 suggests that the padding may be garbage.
|
||||
// This code relies on the padding bytes not starting with BOFRecord.sid
|
||||
if (_recStream.getNextSid() != BOFRecord.sid) {
|
||||
return null;
|
||||
}
|
||||
// else - another sheet substream starting here
|
||||
}
|
||||
|
||||
// step underlying RecordInputStream to the next record
|
||||
_recStream.nextRecord();
|
||||
|
||||
r = readNextRecord();
|
||||
if (r == null) {
|
||||
// some record types may get skipped (e.g. DBCellRecord and ContinueRecord)
|
||||
continue;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
r = readNextRecord();
|
||||
if (r == null) {
|
||||
// some record types may get skipped (e.g. DBCellRecord and ContinueRecord)
|
||||
continue;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next {@link Record} from the multiple record group as expanded from
|
||||
* a recently read {@link MulRKRecord}. <code>null</code> if not present.
|
||||
*/
|
||||
private Record getNextUnreadRecord() {
|
||||
if (_unreadRecordBuffer != null) {
|
||||
int ix = _unreadRecordIndex;
|
||||
if (ix < _unreadRecordBuffer.length) {
|
||||
Record result = _unreadRecordBuffer[ix];
|
||||
_unreadRecordIndex = ix + 1;
|
||||
return result;
|
||||
}
|
||||
_unreadRecordIndex = -1;
|
||||
_unreadRecordBuffer = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @return the next {@link Record} from the multiple record group as expanded from
|
||||
* a recently read {@link MulRKRecord}. <code>null</code> if not present.
|
||||
*/
|
||||
private Record getNextUnreadRecord() {
|
||||
if (_unreadRecordBuffer != null) {
|
||||
int ix = _unreadRecordIndex;
|
||||
if (ix < _unreadRecordBuffer.length) {
|
||||
Record result = _unreadRecordBuffer[ix];
|
||||
_unreadRecordIndex = ix + 1;
|
||||
return result;
|
||||
}
|
||||
_unreadRecordIndex = -1;
|
||||
_unreadRecordBuffer = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next available record, or <code>null</code> if
|
||||
* this pass didn't return a record that's
|
||||
* suitable for returning (eg was a continue record).
|
||||
*/
|
||||
private Record readNextRecord() {
|
||||
/**
|
||||
* @return the next available record, or <code>null</code> if
|
||||
* this pass didn't return a record that's
|
||||
* suitable for returning (eg was a continue record).
|
||||
*/
|
||||
private Record readNextRecord() {
|
||||
|
||||
Record record = RecordFactory.createSingleRecord(_recStream);
|
||||
_lastRecordWasEOFLevelZero = false;
|
||||
Record record = RecordFactory.createSingleRecord(_recStream);
|
||||
_lastRecordWasEOFLevelZero = false;
|
||||
|
||||
if (record instanceof BOFRecord) {
|
||||
_bofDepth++;
|
||||
return record;
|
||||
}
|
||||
if (record instanceof BOFRecord) {
|
||||
_bofDepth++;
|
||||
return record;
|
||||
}
|
||||
|
||||
if (record instanceof EOFRecord) {
|
||||
_bofDepth--;
|
||||
if (_bofDepth < 1) {
|
||||
_lastRecordWasEOFLevelZero = true;
|
||||
}
|
||||
if (record instanceof EOFRecord) {
|
||||
_bofDepth--;
|
||||
if (_bofDepth < 1) {
|
||||
_lastRecordWasEOFLevelZero = true;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
if (record instanceof DBCellRecord) {
|
||||
// Not needed by POI. Regenerated from scratch by POI when spreadsheet is written
|
||||
return null;
|
||||
}
|
||||
if (record instanceof DBCellRecord) {
|
||||
// Not needed by POI. Regenerated from scratch by POI when spreadsheet is written
|
||||
return null;
|
||||
}
|
||||
|
||||
if (record instanceof RKRecord) {
|
||||
return RecordFactory.convertToNumberRecord((RKRecord) record);
|
||||
}
|
||||
if (record instanceof RKRecord) {
|
||||
return RecordFactory.convertToNumberRecord((RKRecord) record);
|
||||
}
|
||||
|
||||
if (record instanceof MulRKRecord) {
|
||||
Record[] records = RecordFactory.convertRKRecords((MulRKRecord) record);
|
||||
if (record instanceof MulRKRecord) {
|
||||
Record[] records = RecordFactory.convertRKRecords((MulRKRecord) record);
|
||||
|
||||
_unreadRecordBuffer = records;
|
||||
_unreadRecordIndex = 1;
|
||||
return records[0];
|
||||
}
|
||||
_unreadRecordBuffer = records;
|
||||
_unreadRecordIndex = 1;
|
||||
return records[0];
|
||||
}
|
||||
|
||||
if (record.getSid() == DrawingGroupRecord.sid
|
||||
&& _lastRecord instanceof DrawingGroupRecord) {
|
||||
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) _lastRecord;
|
||||
lastDGRecord.join((AbstractEscherHolderRecord) record);
|
||||
return null;
|
||||
}
|
||||
if (record.getSid() == ContinueRecord.sid) {
|
||||
ContinueRecord contRec = (ContinueRecord) record;
|
||||
if (record.getSid() == DrawingGroupRecord.sid
|
||||
&& _lastRecord instanceof DrawingGroupRecord) {
|
||||
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) _lastRecord;
|
||||
lastDGRecord.join((AbstractEscherHolderRecord) record);
|
||||
return null;
|
||||
}
|
||||
if (record.getSid() == ContinueRecord.sid) {
|
||||
ContinueRecord contRec = (ContinueRecord) record;
|
||||
|
||||
if (_lastRecord instanceof ObjRecord || _lastRecord instanceof TextObjectRecord) {
|
||||
// Drawing records have a very strange continue behaviour.
|
||||
//There can actually be OBJ records mixed between the continues.
|
||||
_lastDrawingRecord.processContinueRecord(contRec.getData());
|
||||
//we must remember the position of the continue record.
|
||||
//in the serialization procedure the original structure of records must be preserved
|
||||
if (_shouldIncludeContinueRecords) {
|
||||
return record;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (_lastRecord instanceof DrawingGroupRecord) {
|
||||
((DrawingGroupRecord) _lastRecord).processContinueRecord(contRec.getData());
|
||||
return null;
|
||||
}
|
||||
if (_lastRecord instanceof DrawingRecord) {
|
||||
// ((DrawingRecord) _lastRecord).appendContinueRecord(contRec.getData());
|
||||
return contRec;
|
||||
}
|
||||
if (_lastRecord instanceof UnknownRecord) {
|
||||
//Gracefully handle records that we don't know about,
|
||||
//that happen to be continued
|
||||
return record;
|
||||
}
|
||||
if (_lastRecord instanceof EOFRecord) {
|
||||
// This is really odd, but excel still sometimes
|
||||
// outputs a file like this all the same
|
||||
return record;
|
||||
}
|
||||
throw new RecordFormatException("Unhandled Continue Record followining " + _lastRecord.getClass());
|
||||
}
|
||||
_lastRecord = record;
|
||||
if (record instanceof DrawingRecord) {
|
||||
_lastDrawingRecord = (DrawingRecord) record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
if (_lastRecord instanceof ObjRecord || _lastRecord instanceof TextObjectRecord) {
|
||||
// Drawing records have a very strange continue behaviour.
|
||||
//There can actually be OBJ records mixed between the continues.
|
||||
_lastDrawingRecord.processContinueRecord(contRec.getData());
|
||||
//we must remember the position of the continue record.
|
||||
//in the serialization procedure the original structure of records must be preserved
|
||||
if (_shouldIncludeContinueRecords) {
|
||||
return record;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (_lastRecord instanceof DrawingGroupRecord) {
|
||||
((DrawingGroupRecord) _lastRecord).processContinueRecord(contRec.getData());
|
||||
return null;
|
||||
}
|
||||
if (_lastRecord instanceof DrawingRecord) {
|
||||
// ((DrawingRecord) _lastRecord).appendContinueRecord(contRec.getData());
|
||||
return contRec;
|
||||
}
|
||||
if (_lastRecord instanceof UnknownRecord) {
|
||||
//Gracefully handle records that we don't know about,
|
||||
//that happen to be continued
|
||||
return record;
|
||||
}
|
||||
if (_lastRecord instanceof EOFRecord) {
|
||||
// This is really odd, but excel still sometimes
|
||||
// outputs a file like this all the same
|
||||
return record;
|
||||
}
|
||||
throw new RecordFormatException("Unhandled Continue Record followining " + _lastRecord.getClass());
|
||||
}
|
||||
_lastRecord = record;
|
||||
if (record instanceof DrawingRecord) {
|
||||
_lastDrawingRecord = (DrawingRecord) record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,28 +40,28 @@ import org.apache.poi.util.RecordFormatException;
|
||||
public final class RecordInputStream implements LittleEndianInput {
|
||||
|
||||
|
||||
/** Maximum size of a single record (minus the 4 byte header) without a continue*/
|
||||
public static final short MAX_RECORD_DATA_SIZE = 8224;
|
||||
private static final int INVALID_SID_VALUE = -1;
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 100_000;
|
||||
/**
|
||||
* When {@link #_currentDataLength} has this value, it means that the previous BIFF record is
|
||||
* finished, the next sid has been properly read, but the data size field has not been read yet.
|
||||
*/
|
||||
private static final int DATA_LEN_NEEDS_TO_BE_READ = -1;
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
/** Maximum size of a single record (minus the 4 byte header) without a continue*/
|
||||
public static final short MAX_RECORD_DATA_SIZE = 8224;
|
||||
private static final int INVALID_SID_VALUE = -1;
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 100_000;
|
||||
/**
|
||||
* When {@link #_currentDataLength} has this value, it means that the previous BIFF record is
|
||||
* finished, the next sid has been properly read, but the data size field has not been read yet.
|
||||
*/
|
||||
private static final int DATA_LEN_NEEDS_TO_BE_READ = -1;
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
|
||||
/**
|
||||
* For use in {@link BiffViewer} which may construct {@link Record}s that don't completely
|
||||
* read all available data. This exception should never be thrown otherwise.
|
||||
*/
|
||||
public static final class LeftoverDataException extends RuntimeException {
|
||||
public LeftoverDataException(int sid, int remainingByteCount) {
|
||||
super("Initialisation of record 0x" + Integer.toHexString(sid).toUpperCase(Locale.ROOT)
|
||||
+ "(" + getRecordName(sid) + ") left " + remainingByteCount
|
||||
+ " bytes remaining still to be read.");
|
||||
}
|
||||
/**
|
||||
* For use in {@link BiffViewer} which may construct {@link Record}s that don't completely
|
||||
* read all available data. This exception should never be thrown otherwise.
|
||||
*/
|
||||
public static final class LeftoverDataException extends RuntimeException {
|
||||
public LeftoverDataException(int sid, int remainingByteCount) {
|
||||
super("Initialisation of record 0x" + Integer.toHexString(sid).toUpperCase(Locale.ROOT)
|
||||
+ "(" + getRecordName(sid) + ") left " + remainingByteCount
|
||||
+ " bytes remaining still to be read.");
|
||||
}
|
||||
|
||||
private static String getRecordName(int sid) {
|
||||
Class<? extends Record> recordClass = RecordFactory.getRecordClass(sid);
|
||||
@ -70,255 +70,255 @@ public final class RecordInputStream implements LittleEndianInput {
|
||||
}
|
||||
return recordClass.getSimpleName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Header {@link LittleEndianInput} facet of the wrapped {@link InputStream} */
|
||||
private final BiffHeaderInput _bhi;
|
||||
/** Data {@link LittleEndianInput} facet of the wrapped {@link InputStream} */
|
||||
private final LittleEndianInput _dataInput;
|
||||
/** the record identifier of the BIFF record currently being read */
|
||||
private int _currentSid;
|
||||
/**
|
||||
* Length of the data section of the current BIFF record (always 4 less than the total record size).
|
||||
* When uninitialised, this field is set to {@link #DATA_LEN_NEEDS_TO_BE_READ}.
|
||||
*/
|
||||
private int _currentDataLength;
|
||||
/**
|
||||
* The BIFF record identifier for the next record is read when just as the current record
|
||||
* is finished.
|
||||
* This field is only really valid during the time that ({@link #_currentDataLength} ==
|
||||
* {@link #DATA_LEN_NEEDS_TO_BE_READ}). At most other times its value is not really the
|
||||
* 'sid of the next record'. Wwhile mid-record, this field coincidentally holds the sid
|
||||
* of the current record.
|
||||
*/
|
||||
private int _nextSid;
|
||||
/**
|
||||
* index within the data section of the current BIFF record
|
||||
*/
|
||||
private int _currentDataOffset;
|
||||
/**
|
||||
* index within the data section when mark() was called
|
||||
*/
|
||||
private int _markedDataOffset;
|
||||
/** Header {@link LittleEndianInput} facet of the wrapped {@link InputStream} */
|
||||
private final BiffHeaderInput _bhi;
|
||||
/** Data {@link LittleEndianInput} facet of the wrapped {@link InputStream} */
|
||||
private final LittleEndianInput _dataInput;
|
||||
/** the record identifier of the BIFF record currently being read */
|
||||
private int _currentSid;
|
||||
/**
|
||||
* Length of the data section of the current BIFF record (always 4 less than the total record size).
|
||||
* When uninitialised, this field is set to {@link #DATA_LEN_NEEDS_TO_BE_READ}.
|
||||
*/
|
||||
private int _currentDataLength;
|
||||
/**
|
||||
* The BIFF record identifier for the next record is read when just as the current record
|
||||
* is finished.
|
||||
* This field is only really valid during the time that ({@link #_currentDataLength} ==
|
||||
* {@link #DATA_LEN_NEEDS_TO_BE_READ}). At most other times its value is not really the
|
||||
* 'sid of the next record'. Wwhile mid-record, this field coincidentally holds the sid
|
||||
* of the current record.
|
||||
*/
|
||||
private int _nextSid;
|
||||
/**
|
||||
* index within the data section of the current BIFF record
|
||||
*/
|
||||
private int _currentDataOffset;
|
||||
/**
|
||||
* index within the data section when mark() was called
|
||||
*/
|
||||
private int _markedDataOffset;
|
||||
|
||||
private static final class SimpleHeaderInput implements BiffHeaderInput {
|
||||
private static final class SimpleHeaderInput implements BiffHeaderInput {
|
||||
|
||||
private final LittleEndianInput _lei;
|
||||
private final LittleEndianInput _lei;
|
||||
|
||||
private SimpleHeaderInput(LittleEndianInput lei) {
|
||||
_lei = lei;
|
||||
}
|
||||
@Override
|
||||
private SimpleHeaderInput(LittleEndianInput lei) {
|
||||
_lei = lei;
|
||||
}
|
||||
@Override
|
||||
public int available() {
|
||||
return _lei.available();
|
||||
}
|
||||
@Override
|
||||
return _lei.available();
|
||||
}
|
||||
@Override
|
||||
public int readDataSize() {
|
||||
return _lei.readUShort();
|
||||
}
|
||||
@Override
|
||||
return _lei.readUShort();
|
||||
}
|
||||
@Override
|
||||
public int readRecordSID() {
|
||||
return _lei.readUShort();
|
||||
}
|
||||
}
|
||||
return _lei.readUShort();
|
||||
}
|
||||
}
|
||||
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
this (in, null, 0);
|
||||
}
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
this (in, null, 0);
|
||||
}
|
||||
|
||||
public RecordInputStream(InputStream in, EncryptionInfo key, int initialOffset) throws RecordFormatException {
|
||||
if (key == null) {
|
||||
_dataInput = (in instanceof LittleEndianInput)
|
||||
// accessing directly is an optimisation
|
||||
? (LittleEndianInput)in
|
||||
// less optimal, but should work OK just the same. Often occurs in junit tests.
|
||||
: new LittleEndianInputStream(in);
|
||||
_bhi = new SimpleHeaderInput(_dataInput);
|
||||
} else {
|
||||
Biff8DecryptingStream bds = new Biff8DecryptingStream(in, initialOffset, key);
|
||||
public RecordInputStream(InputStream in, EncryptionInfo key, int initialOffset) throws RecordFormatException {
|
||||
if (key == null) {
|
||||
_dataInput = (in instanceof LittleEndianInput)
|
||||
// accessing directly is an optimisation
|
||||
? (LittleEndianInput)in
|
||||
// less optimal, but should work OK just the same. Often occurs in junit tests.
|
||||
: new LittleEndianInputStream(in);
|
||||
_bhi = new SimpleHeaderInput(_dataInput);
|
||||
} else {
|
||||
Biff8DecryptingStream bds = new Biff8DecryptingStream(in, initialOffset, key);
|
||||
_dataInput = bds;
|
||||
_bhi = bds;
|
||||
}
|
||||
_nextSid = readNextSid();
|
||||
}
|
||||
_bhi = bds;
|
||||
}
|
||||
_nextSid = readNextSid();
|
||||
}
|
||||
|
||||
static LittleEndianInput getLEI(InputStream is) {
|
||||
if (is instanceof LittleEndianInput) {
|
||||
// accessing directly is an optimisation
|
||||
return (LittleEndianInput) is;
|
||||
}
|
||||
// less optimal, but should work OK just the same. Often occurs in junit tests.
|
||||
return new LittleEndianInputStream(is);
|
||||
}
|
||||
static LittleEndianInput getLEI(InputStream is) {
|
||||
if (is instanceof LittleEndianInput) {
|
||||
// accessing directly is an optimisation
|
||||
return (LittleEndianInput) is;
|
||||
}
|
||||
// less optimal, but should work OK just the same. Often occurs in junit tests.
|
||||
return new LittleEndianInputStream(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of bytes available in the current BIFF record
|
||||
* @see #remaining()
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* @return the number of bytes available in the current BIFF record
|
||||
* @see #remaining()
|
||||
*/
|
||||
@Override
|
||||
public int available() {
|
||||
return remaining();
|
||||
}
|
||||
return remaining();
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) {
|
||||
int limit = Math.min(len, remaining());
|
||||
if (limit == 0) {
|
||||
return 0;
|
||||
}
|
||||
readFully(b, off,limit);
|
||||
return limit;
|
||||
}
|
||||
public int read(byte[] b, int off, int len) {
|
||||
int limit = Math.min(len, remaining());
|
||||
if (limit == 0) {
|
||||
return 0;
|
||||
}
|
||||
readFully(b, off,limit);
|
||||
return limit;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return (short) _currentSid;
|
||||
}
|
||||
public short getSid() {
|
||||
return (short) _currentSid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - this method is expected to be called only when completed reading the current BIFF
|
||||
* record.
|
||||
*
|
||||
* @return true, if there's another record in the stream
|
||||
*
|
||||
* @throws LeftoverDataException if this method is called before reaching the end of the
|
||||
* current record.
|
||||
*/
|
||||
public boolean hasNextRecord() throws LeftoverDataException {
|
||||
if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) {
|
||||
throw new LeftoverDataException(_currentSid, remaining());
|
||||
}
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
_nextSid = readNextSid();
|
||||
}
|
||||
return _nextSid != INVALID_SID_VALUE;
|
||||
}
|
||||
/**
|
||||
* Note - this method is expected to be called only when completed reading the current BIFF
|
||||
* record.
|
||||
*
|
||||
* @return true, if there's another record in the stream
|
||||
*
|
||||
* @throws LeftoverDataException if this method is called before reaching the end of the
|
||||
* current record.
|
||||
*/
|
||||
public boolean hasNextRecord() throws LeftoverDataException {
|
||||
if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) {
|
||||
throw new LeftoverDataException(_currentSid, remaining());
|
||||
}
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
_nextSid = readNextSid();
|
||||
}
|
||||
return _nextSid != INVALID_SID_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sid of the next record or {@link #INVALID_SID_VALUE} if at end of stream
|
||||
*/
|
||||
private int readNextSid() {
|
||||
int nAvailable = _bhi.available();
|
||||
if (nAvailable < EOFRecord.ENCODED_SIZE) {
|
||||
// some scrap left over, if nAvailable > 0?
|
||||
// ex45582-22397.xls has one extra byte after the last record
|
||||
// Excel reads that file OK
|
||||
return INVALID_SID_VALUE;
|
||||
}
|
||||
int result = _bhi.readRecordSID();
|
||||
if (result == INVALID_SID_VALUE) {
|
||||
throw new RecordFormatException("Found invalid sid (" + result + ")");
|
||||
}
|
||||
_currentDataLength = DATA_LEN_NEEDS_TO_BE_READ;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @return the sid of the next record or {@link #INVALID_SID_VALUE} if at end of stream
|
||||
*/
|
||||
private int readNextSid() {
|
||||
int nAvailable = _bhi.available();
|
||||
if (nAvailable < EOFRecord.ENCODED_SIZE) {
|
||||
// some scrap left over, if nAvailable > 0?
|
||||
// ex45582-22397.xls has one extra byte after the last record
|
||||
// Excel reads that file OK
|
||||
return INVALID_SID_VALUE;
|
||||
}
|
||||
int result = _bhi.readRecordSID();
|
||||
if (result == INVALID_SID_VALUE) {
|
||||
throw new RecordFormatException("Found invalid sid (" + result + ")");
|
||||
}
|
||||
_currentDataLength = DATA_LEN_NEEDS_TO_BE_READ;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Moves to the next record in the stream.
|
||||
*
|
||||
* <i>Note: The auto continue flag is reset to true</i>
|
||||
*/
|
||||
public void nextRecord() throws RecordFormatException {
|
||||
if (_nextSid == INVALID_SID_VALUE) {
|
||||
throw new IllegalStateException("EOF - next record not available");
|
||||
}
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
throw new IllegalStateException("Cannot call nextRecord() without checking hasNextRecord() first");
|
||||
}
|
||||
_currentSid = _nextSid;
|
||||
_currentDataOffset = 0;
|
||||
_currentDataLength = _bhi.readDataSize();
|
||||
if (_currentDataLength > MAX_RECORD_DATA_SIZE) {
|
||||
throw new RecordFormatException("The content of an excel record cannot exceed "
|
||||
+ MAX_RECORD_DATA_SIZE + " bytes");
|
||||
}
|
||||
}
|
||||
/** Moves to the next record in the stream.
|
||||
*
|
||||
* <i>Note: The auto continue flag is reset to true</i>
|
||||
*/
|
||||
public void nextRecord() throws RecordFormatException {
|
||||
if (_nextSid == INVALID_SID_VALUE) {
|
||||
throw new IllegalStateException("EOF - next record not available");
|
||||
}
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
throw new IllegalStateException("Cannot call nextRecord() without checking hasNextRecord() first");
|
||||
}
|
||||
_currentSid = _nextSid;
|
||||
_currentDataOffset = 0;
|
||||
_currentDataLength = _bhi.readDataSize();
|
||||
if (_currentDataLength > MAX_RECORD_DATA_SIZE) {
|
||||
throw new RecordFormatException("The content of an excel record cannot exceed "
|
||||
+ MAX_RECORD_DATA_SIZE + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRecordPosition(int requiredByteCount) {
|
||||
private void checkRecordPosition(int requiredByteCount) {
|
||||
|
||||
int nAvailable = remaining();
|
||||
if (nAvailable >= requiredByteCount) {
|
||||
// all OK
|
||||
return;
|
||||
}
|
||||
if (nAvailable == 0 && isContinueNext()) {
|
||||
nextRecord();
|
||||
return;
|
||||
}
|
||||
throw new RecordFormatException("Not enough data (" + nAvailable
|
||||
+ ") to read requested (" + requiredByteCount +") bytes");
|
||||
}
|
||||
int nAvailable = remaining();
|
||||
if (nAvailable >= requiredByteCount) {
|
||||
// all OK
|
||||
return;
|
||||
}
|
||||
if (nAvailable == 0 && isContinueNext()) {
|
||||
nextRecord();
|
||||
return;
|
||||
}
|
||||
throw new RecordFormatException("Not enough data (" + nAvailable
|
||||
+ ") to read requested (" + requiredByteCount +") bytes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
public byte readByte() {
|
||||
checkRecordPosition(LittleEndianConsts.BYTE_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.BYTE_SIZE;
|
||||
return _dataInput.readByte();
|
||||
}
|
||||
checkRecordPosition(LittleEndianConsts.BYTE_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.BYTE_SIZE;
|
||||
return _dataInput.readByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
public short readShort() {
|
||||
checkRecordPosition(LittleEndianConsts.SHORT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.SHORT_SIZE;
|
||||
return _dataInput.readShort();
|
||||
}
|
||||
checkRecordPosition(LittleEndianConsts.SHORT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.SHORT_SIZE;
|
||||
return _dataInput.readShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 32 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads a 32 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
public int readInt() {
|
||||
checkRecordPosition(LittleEndianConsts.INT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.INT_SIZE;
|
||||
return _dataInput.readInt();
|
||||
}
|
||||
checkRecordPosition(LittleEndianConsts.INT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.INT_SIZE;
|
||||
return _dataInput.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 64 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads a 64 bit, signed value
|
||||
*/
|
||||
@Override
|
||||
public long readLong() {
|
||||
checkRecordPosition(LittleEndianConsts.LONG_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.LONG_SIZE;
|
||||
return _dataInput.readLong();
|
||||
}
|
||||
checkRecordPosition(LittleEndianConsts.LONG_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.LONG_SIZE;
|
||||
return _dataInput.readLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
@Override
|
||||
public int readUByte() {
|
||||
return readByte() & 0x00FF;
|
||||
}
|
||||
return readByte() & 0x00FF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, unsigned value.
|
||||
*/
|
||||
@Override
|
||||
/**
|
||||
* Reads a 16 bit, unsigned value.
|
||||
*/
|
||||
@Override
|
||||
public int readUShort() {
|
||||
checkRecordPosition(LittleEndianConsts.SHORT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.SHORT_SIZE;
|
||||
return _dataInput.readUShort();
|
||||
}
|
||||
checkRecordPosition(LittleEndianConsts.SHORT_SIZE);
|
||||
_currentDataOffset += LittleEndianConsts.SHORT_SIZE;
|
||||
return _dataInput.readUShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public double readDouble() {
|
||||
// YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}.
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readPlain(byte[] buf, int off, int len) {
|
||||
readFully(buf, 0, buf.length, true);
|
||||
}
|
||||
@Override
|
||||
public void readPlain(byte[] buf, int off, int len) {
|
||||
readFully(buf, 0, buf.length, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void readFully(byte[] buf) {
|
||||
readFully(buf, 0, buf.length, false);
|
||||
}
|
||||
readFully(buf, 0, buf.length, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] buf, int off, int len) {
|
||||
@ -326,126 +326,126 @@ public final class RecordInputStream implements LittleEndianInput {
|
||||
}
|
||||
|
||||
private void readFully(byte[] buf, int off, int len, boolean isPlain) {
|
||||
int origLen = len;
|
||||
if (buf == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (off < 0 || len < 0 || len > buf.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
int origLen = len;
|
||||
if (buf == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (off < 0 || len < 0 || len > buf.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
int nextChunk = Math.min(available(),len);
|
||||
if (nextChunk == 0) {
|
||||
if (!hasNextRecord()) {
|
||||
throw new RecordFormatException("Can't read the remaining "+len+" bytes of the requested "+origLen+" bytes. No further record exists.");
|
||||
} else {
|
||||
nextRecord();
|
||||
nextChunk = Math.min(available(),len);
|
||||
assert(nextChunk > 0);
|
||||
}
|
||||
}
|
||||
checkRecordPosition(nextChunk);
|
||||
if (isPlain) {
|
||||
while (len > 0) {
|
||||
int nextChunk = Math.min(available(),len);
|
||||
if (nextChunk == 0) {
|
||||
if (!hasNextRecord()) {
|
||||
throw new RecordFormatException("Can't read the remaining "+len+" bytes of the requested "+origLen+" bytes. No further record exists.");
|
||||
} else {
|
||||
nextRecord();
|
||||
nextChunk = Math.min(available(),len);
|
||||
assert(nextChunk > 0);
|
||||
}
|
||||
}
|
||||
checkRecordPosition(nextChunk);
|
||||
if (isPlain) {
|
||||
_dataInput.readPlain(buf, off, nextChunk);
|
||||
} else {
|
||||
} else {
|
||||
_dataInput.readFully(buf, off, nextChunk);
|
||||
}
|
||||
_currentDataOffset+=nextChunk;
|
||||
off += nextChunk;
|
||||
len -= nextChunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentDataOffset+=nextChunk;
|
||||
off += nextChunk;
|
||||
len -= nextChunk;
|
||||
}
|
||||
}
|
||||
|
||||
public String readString() {
|
||||
int requestedLength = readUShort();
|
||||
byte compressFlag = readByte();
|
||||
return readStringCommon(requestedLength, compressFlag == 0);
|
||||
}
|
||||
/**
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* @param requestedLength the length of the final string
|
||||
* @return the converted string
|
||||
* @exception IllegalArgumentException if len is too large (i.e.,
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
public String readUnicodeLEString(int requestedLength) {
|
||||
return readStringCommon(requestedLength, false);
|
||||
}
|
||||
public String readString() {
|
||||
int requestedLength = readUShort();
|
||||
byte compressFlag = readByte();
|
||||
return readStringCommon(requestedLength, compressFlag == 0);
|
||||
}
|
||||
/**
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* @param requestedLength the length of the final string
|
||||
* @return the converted string
|
||||
* @exception IllegalArgumentException if len is too large (i.e.,
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
public String readUnicodeLEString(int requestedLength) {
|
||||
return readStringCommon(requestedLength, false);
|
||||
}
|
||||
|
||||
public String readCompressedUnicode(int requestedLength) {
|
||||
return readStringCommon(requestedLength, true);
|
||||
}
|
||||
public String readCompressedUnicode(int requestedLength) {
|
||||
return readStringCommon(requestedLength, true);
|
||||
}
|
||||
|
||||
private String readStringCommon(int requestedLength, boolean pIsCompressedEncoding) {
|
||||
// Sanity check to detect garbage string lengths
|
||||
if (requestedLength < 0 || requestedLength > 0x100000) { // 16 million chars?
|
||||
throw new IllegalArgumentException("Bad requested string length (" + requestedLength + ")");
|
||||
}
|
||||
char[] buf = new char[requestedLength];
|
||||
boolean isCompressedEncoding = pIsCompressedEncoding;
|
||||
int curLen = 0;
|
||||
while(true) {
|
||||
int availableChars =isCompressedEncoding ? remaining() : remaining() / LittleEndianConsts.SHORT_SIZE;
|
||||
if (requestedLength - curLen <= availableChars) {
|
||||
// enough space in current record, so just read it out
|
||||
while(curLen < requestedLength) {
|
||||
char ch;
|
||||
if (isCompressedEncoding) {
|
||||
ch = (char)readUByte();
|
||||
} else {
|
||||
ch = (char)readShort();
|
||||
}
|
||||
buf[curLen] = ch;
|
||||
curLen++;
|
||||
}
|
||||
return new String(buf);
|
||||
}
|
||||
// else string has been spilled into next continue record
|
||||
// so read what's left of the current record
|
||||
while(availableChars > 0) {
|
||||
char ch;
|
||||
if (isCompressedEncoding) {
|
||||
ch = (char)readUByte();
|
||||
} else {
|
||||
ch = (char)readShort();
|
||||
}
|
||||
buf[curLen] = ch;
|
||||
curLen++;
|
||||
availableChars--;
|
||||
}
|
||||
if (!isContinueNext()) {
|
||||
throw new RecordFormatException("Expected to find a ContinueRecord in order to read remaining "
|
||||
+ (requestedLength-curLen) + " of " + requestedLength + " chars");
|
||||
}
|
||||
if(remaining() != 0) {
|
||||
throw new RecordFormatException("Odd number of bytes(" + remaining() + ") left behind");
|
||||
}
|
||||
nextRecord();
|
||||
// note - the compressed flag may change on the fly
|
||||
byte compressFlag = readByte();
|
||||
private String readStringCommon(int requestedLength, boolean pIsCompressedEncoding) {
|
||||
// Sanity check to detect garbage string lengths
|
||||
if (requestedLength < 0 || requestedLength > 0x100000) { // 16 million chars?
|
||||
throw new IllegalArgumentException("Bad requested string length (" + requestedLength + ")");
|
||||
}
|
||||
char[] buf = new char[requestedLength];
|
||||
boolean isCompressedEncoding = pIsCompressedEncoding;
|
||||
int curLen = 0;
|
||||
while(true) {
|
||||
int availableChars =isCompressedEncoding ? remaining() : remaining() / LittleEndianConsts.SHORT_SIZE;
|
||||
if (requestedLength - curLen <= availableChars) {
|
||||
// enough space in current record, so just read it out
|
||||
while(curLen < requestedLength) {
|
||||
char ch;
|
||||
if (isCompressedEncoding) {
|
||||
ch = (char)readUByte();
|
||||
} else {
|
||||
ch = (char)readShort();
|
||||
}
|
||||
buf[curLen] = ch;
|
||||
curLen++;
|
||||
}
|
||||
return new String(buf);
|
||||
}
|
||||
// else string has been spilled into next continue record
|
||||
// so read what's left of the current record
|
||||
while(availableChars > 0) {
|
||||
char ch;
|
||||
if (isCompressedEncoding) {
|
||||
ch = (char)readUByte();
|
||||
} else {
|
||||
ch = (char)readShort();
|
||||
}
|
||||
buf[curLen] = ch;
|
||||
curLen++;
|
||||
availableChars--;
|
||||
}
|
||||
if (!isContinueNext()) {
|
||||
throw new RecordFormatException("Expected to find a ContinueRecord in order to read remaining "
|
||||
+ (requestedLength-curLen) + " of " + requestedLength + " chars");
|
||||
}
|
||||
if(remaining() != 0) {
|
||||
throw new RecordFormatException("Odd number of bytes(" + remaining() + ") left behind");
|
||||
}
|
||||
nextRecord();
|
||||
// note - the compressed flag may change on the fly
|
||||
byte compressFlag = readByte();
|
||||
assert(compressFlag == 0 || compressFlag == 1);
|
||||
isCompressedEncoding = (compressFlag == 0);
|
||||
}
|
||||
}
|
||||
isCompressedEncoding = (compressFlag == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the remaining bytes for the current record.
|
||||
*
|
||||
* @return The remaining bytes of the current record.
|
||||
*/
|
||||
public byte[] readRemainder() {
|
||||
int size = remaining();
|
||||
if (size ==0) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
byte[] result = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
readFully(result);
|
||||
return result;
|
||||
}
|
||||
/** Returns the remaining bytes for the current record.
|
||||
*
|
||||
* @return The remaining bytes of the current record.
|
||||
*/
|
||||
public byte[] readRemainder() {
|
||||
int size = remaining();
|
||||
if (size ==0) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
byte[] result = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
readFully(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all byte data for the current record, including any that overlaps
|
||||
@ -472,36 +472,36 @@ public final class RecordInputStream implements LittleEndianInput {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/** The remaining number of bytes in the <i>current</i> record.
|
||||
*
|
||||
* @return The number of bytes remaining in the current record
|
||||
*/
|
||||
public int remaining() {
|
||||
if (_currentDataLength == DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
// already read sid of next record. so current one is finished
|
||||
return 0;
|
||||
}
|
||||
return _currentDataLength - _currentDataOffset;
|
||||
}
|
||||
/** The remaining number of bytes in the <i>current</i> record.
|
||||
*
|
||||
* @return The number of bytes remaining in the current record
|
||||
*/
|
||||
public int remaining() {
|
||||
if (_currentDataLength == DATA_LEN_NEEDS_TO_BE_READ) {
|
||||
// already read sid of next record. so current one is finished
|
||||
return 0;
|
||||
}
|
||||
return _currentDataLength - _currentDataOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@code true} when a {@link ContinueRecord} is next.
|
||||
*/
|
||||
private boolean isContinueNext() {
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ && _currentDataOffset != _currentDataLength) {
|
||||
throw new IllegalStateException("Should never be called before end of current record");
|
||||
}
|
||||
if (!hasNextRecord()) {
|
||||
return false;
|
||||
}
|
||||
// At what point are records continued?
|
||||
// - Often from within the char data of long strings (caller is within readStringCommon()).
|
||||
// - From UnicodeString construction (many different points - call via checkRecordPosition)
|
||||
// - During TextObjectRecord construction (just before the text, perhaps within the text,
|
||||
// and before the formatting run data)
|
||||
return _nextSid == ContinueRecord.sid;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return {@code true} when a {@link ContinueRecord} is next.
|
||||
*/
|
||||
private boolean isContinueNext() {
|
||||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ && _currentDataOffset != _currentDataLength) {
|
||||
throw new IllegalStateException("Should never be called before end of current record");
|
||||
}
|
||||
if (!hasNextRecord()) {
|
||||
return false;
|
||||
}
|
||||
// At what point are records continued?
|
||||
// - Often from within the char data of long strings (caller is within readStringCommon()).
|
||||
// - From UnicodeString construction (many different points - call via checkRecordPosition)
|
||||
// - During TextObjectRecord construction (just before the text, perhaps within the text,
|
||||
// and before the formatting run data)
|
||||
return _nextSid == ContinueRecord.sid;
|
||||
}
|
||||
|
||||
/**
|
||||
@return sid of next record. Can be called after hasNextRecord()
|
||||
|
||||
@ -81,9 +81,9 @@ public final class RowRecord extends StandardRecord {
|
||||
|
||||
|
||||
public RowRecord(int rowNumber) {
|
||||
if(rowNumber < 0) {
|
||||
throw new IllegalArgumentException("Invalid row number (" + rowNumber + ")");
|
||||
}
|
||||
if(rowNumber < 0) {
|
||||
throw new IllegalArgumentException("Invalid row number (" + rowNumber + ")");
|
||||
}
|
||||
field_1_row_number = rowNumber;
|
||||
field_4_height = (short)0xFF;
|
||||
field_5_optimize = ( short ) 0;
|
||||
@ -96,9 +96,9 @@ public final class RowRecord extends StandardRecord {
|
||||
|
||||
public RowRecord(RecordInputStream in) {
|
||||
field_1_row_number = in.readUShort();
|
||||
if(field_1_row_number < 0) {
|
||||
throw new IllegalArgumentException("Invalid row number " + field_1_row_number + " found in InputStream");
|
||||
}
|
||||
if(field_1_row_number < 0) {
|
||||
throw new IllegalArgumentException("Invalid row number " + field_1_row_number + " found in InputStream");
|
||||
}
|
||||
field_2_first_col = in.readShort();
|
||||
field_3_last_col = in.readShort();
|
||||
field_4_height = in.readShort();
|
||||
@ -209,7 +209,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @param index to the XF record
|
||||
*/
|
||||
public void setXFIndex(short index) {
|
||||
field_8_option_flags = xfIndex.setValue(field_8_option_flags, index);
|
||||
field_8_option_flags = xfIndex.setValue(field_8_option_flags, index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,7 +218,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @param f has thick top border
|
||||
*/
|
||||
public void setTopBorder(boolean f) {
|
||||
field_8_option_flags = topBorder.setBoolean(field_8_option_flags, f);
|
||||
field_8_option_flags = topBorder.setBoolean(field_8_option_flags, f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +228,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @param f has thick bottom border
|
||||
*/
|
||||
public void setBottomBorder(boolean f) {
|
||||
field_8_option_flags = bottomBorder.setBoolean(field_8_option_flags, f);
|
||||
field_8_option_flags = bottomBorder.setBoolean(field_8_option_flags, f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,7 +237,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @param f use phoenetic guide
|
||||
*/
|
||||
public void setPhoeneticGuide(boolean f) {
|
||||
field_8_option_flags = phoneticGuide.setBoolean(field_8_option_flags, f);
|
||||
field_8_option_flags = phoneticGuide.setBoolean(field_8_option_flags, f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,7 +353,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @return index to the XF record or bogus value (undefined) if isn't formatted
|
||||
*/
|
||||
public short getXFIndex() {
|
||||
return xfIndex.getShortValue((short)field_8_option_flags);
|
||||
return xfIndex.getShortValue((short)field_8_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -362,7 +362,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @return has cells with a thick top border
|
||||
*/
|
||||
public boolean getTopBorder() {
|
||||
return topBorder.isSet(field_8_option_flags);
|
||||
return topBorder.isSet(field_8_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,7 +371,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @return has cells with a thick bottom border
|
||||
*/
|
||||
public boolean getBottomBorder() {
|
||||
return bottomBorder.isSet(field_8_option_flags);
|
||||
return bottomBorder.isSet(field_8_option_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,7 +380,7 @@ public final class RowRecord extends StandardRecord {
|
||||
* @return has phoentic guide
|
||||
*/
|
||||
public boolean getPhoeneticGuide() {
|
||||
return phoneticGuide.isSet(field_8_option_flags);
|
||||
return phoneticGuide.isSet(field_8_option_flags);
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
|
||||
@ -31,7 +31,7 @@ import static org.apache.logging.log4j.util.Unbox.box;
|
||||
*/
|
||||
class SSTDeserializer
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(SSTDeserializer.class);
|
||||
private static final Logger LOG = LogManager.getLogger(SSTDeserializer.class);
|
||||
private IntMapper<UnicodeString> strings;
|
||||
|
||||
public SSTDeserializer( IntMapper<UnicodeString> strings )
|
||||
|
||||
@ -27,8 +27,8 @@ import org.apache.poi.util.IntMapper;
|
||||
*/
|
||||
final class SSTSerializer {
|
||||
|
||||
private final int _numStrings;
|
||||
private final int _numUniqueStrings;
|
||||
private final int _numStrings;
|
||||
private final int _numUniqueStrings;
|
||||
|
||||
private final IntMapper<UnicodeString> strings;
|
||||
|
||||
@ -40,8 +40,8 @@ final class SSTSerializer {
|
||||
public SSTSerializer( IntMapper<UnicodeString> strings, int numStrings, int numUniqueStrings )
|
||||
{
|
||||
this.strings = strings;
|
||||
_numStrings = numStrings;
|
||||
_numUniqueStrings = numUniqueStrings;
|
||||
_numStrings = numStrings;
|
||||
_numUniqueStrings = numUniqueStrings;
|
||||
|
||||
int infoRecs = ExtSSTRecord.getNumberOfInfoRecsForStrings(strings.size());
|
||||
this.bucketAbsoluteOffsets = new int[infoRecs];
|
||||
|
||||
@ -106,9 +106,9 @@ public final class SharedFormulaRecord extends SharedValueRecordBase {
|
||||
public SharedFormulaRecord copy() {
|
||||
return new SharedFormulaRecord(this);
|
||||
}
|
||||
public boolean isFormulaSame(SharedFormulaRecord other) {
|
||||
return field_7_parsed_expr.isSame(other.field_7_parsed_expr);
|
||||
}
|
||||
public boolean isFormulaSame(SharedFormulaRecord other) {
|
||||
return field_7_parsed_expr.isSame(other.field_7_parsed_expr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
|
||||
@ -27,95 +27,95 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
*/
|
||||
public abstract class SharedValueRecordBase extends StandardRecord {
|
||||
|
||||
private CellRangeAddress8Bit _range;
|
||||
private CellRangeAddress8Bit _range;
|
||||
|
||||
protected SharedValueRecordBase(SharedValueRecordBase other) {
|
||||
super(other);
|
||||
_range = (other._range == null) ? null : other._range.copy();
|
||||
}
|
||||
protected SharedValueRecordBase(SharedValueRecordBase other) {
|
||||
super(other);
|
||||
_range = (other._range == null) ? null : other._range.copy();
|
||||
}
|
||||
|
||||
protected SharedValueRecordBase(CellRangeAddress8Bit range) {
|
||||
if (range == null) {
|
||||
throw new IllegalArgumentException("range must be supplied.");
|
||||
}
|
||||
_range = range;
|
||||
}
|
||||
protected SharedValueRecordBase(CellRangeAddress8Bit range) {
|
||||
if (range == null) {
|
||||
throw new IllegalArgumentException("range must be supplied.");
|
||||
}
|
||||
_range = range;
|
||||
}
|
||||
|
||||
protected SharedValueRecordBase() {
|
||||
this(new CellRangeAddress8Bit(0, 0, 0, 0));
|
||||
}
|
||||
protected SharedValueRecordBase() {
|
||||
this(new CellRangeAddress8Bit(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* reads only the range (1 {@link CellRangeAddress8Bit}) from the stream
|
||||
*
|
||||
* @param in The interface for reading the record data.
|
||||
*/
|
||||
public SharedValueRecordBase(LittleEndianInput in) {
|
||||
_range = new CellRangeAddress8Bit(in);
|
||||
}
|
||||
/**
|
||||
* reads only the range (1 {@link CellRangeAddress8Bit}) from the stream
|
||||
*
|
||||
* @param in The interface for reading the record data.
|
||||
*/
|
||||
public SharedValueRecordBase(LittleEndianInput in) {
|
||||
_range = new CellRangeAddress8Bit(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the range of cells that this record is shared across. Never <code>null</code>.
|
||||
*/
|
||||
public final CellRangeAddress8Bit getRange() {
|
||||
return _range;
|
||||
}
|
||||
/**
|
||||
* @return the range of cells that this record is shared across. Never <code>null</code>.
|
||||
*/
|
||||
public final CellRangeAddress8Bit getRange() {
|
||||
return _range;
|
||||
}
|
||||
|
||||
public final int getFirstRow() {
|
||||
return _range.getFirstRow();
|
||||
}
|
||||
public final int getFirstRow() {
|
||||
return _range.getFirstRow();
|
||||
}
|
||||
|
||||
public final int getLastRow() {
|
||||
return _range.getLastRow();
|
||||
}
|
||||
public final int getLastRow() {
|
||||
return _range.getLastRow();
|
||||
}
|
||||
|
||||
public final int getFirstColumn() {
|
||||
return (short) _range.getFirstColumn();
|
||||
}
|
||||
public final int getFirstColumn() {
|
||||
return (short) _range.getFirstColumn();
|
||||
}
|
||||
|
||||
public final int getLastColumn() {
|
||||
return (short) _range.getLastColumn();
|
||||
}
|
||||
public final int getLastColumn() {
|
||||
return (short) _range.getLastColumn();
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return CellRangeAddress8Bit.ENCODED_SIZE + getExtraDataSize();
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return CellRangeAddress8Bit.ENCODED_SIZE + getExtraDataSize();
|
||||
}
|
||||
|
||||
protected abstract int getExtraDataSize();
|
||||
protected abstract int getExtraDataSize();
|
||||
|
||||
protected abstract void serializeExtraData(LittleEndianOutput out);
|
||||
protected abstract void serializeExtraData(LittleEndianOutput out);
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
_range.serialize(out);
|
||||
serializeExtraData(out);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
_range.serialize(out);
|
||||
serializeExtraData(out);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param rowIx the row index
|
||||
* @param colIx the column index
|
||||
*
|
||||
* @return {@code true} if (rowIx, colIx) is within the range of this shared value object.
|
||||
* @return {@code true} if (rowIx, colIx) is within the range of this shared value object.
|
||||
*
|
||||
* @see #getRange()
|
||||
*/
|
||||
public final boolean isInRange(int rowIx, int colIx) {
|
||||
CellRangeAddress8Bit r = _range;
|
||||
return r.getFirstRow() <= rowIx
|
||||
&& r.getLastRow() >= rowIx
|
||||
&& r.getFirstColumn() <= colIx
|
||||
&& r.getLastColumn() >= colIx;
|
||||
}
|
||||
/**
|
||||
* @param rowIx the row index
|
||||
* @param colIx the column index
|
||||
*
|
||||
* @return {@code true} if (rowIx, colIx) describes the first cell in this shared value
|
||||
* object's range
|
||||
*
|
||||
* @see #getRange()
|
||||
*/
|
||||
public final boolean isFirstCell(int rowIx, int colIx) {
|
||||
CellRangeAddress8Bit r = getRange();
|
||||
return r.getFirstRow() == rowIx && r.getFirstColumn() == colIx;
|
||||
}
|
||||
*/
|
||||
public final boolean isInRange(int rowIx, int colIx) {
|
||||
CellRangeAddress8Bit r = _range;
|
||||
return r.getFirstRow() <= rowIx
|
||||
&& r.getLastRow() >= rowIx
|
||||
&& r.getFirstColumn() <= colIx
|
||||
&& r.getLastColumn() >= colIx;
|
||||
}
|
||||
/**
|
||||
* @param rowIx the row index
|
||||
* @param colIx the column index
|
||||
*
|
||||
* @return {@code true} if (rowIx, colIx) describes the first cell in this shared value
|
||||
* object's range
|
||||
*
|
||||
* @see #getRange()
|
||||
*/
|
||||
public final boolean isFirstCell(int rowIx, int colIx) {
|
||||
CellRangeAddress8Bit r = getRange();
|
||||
return r.getFirstRow() == rowIx && r.getFirstColumn() == colIx;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,10 +31,10 @@ import org.apache.poi.util.StringUtil;
|
||||
* Stores the cached result of a text formula
|
||||
*/
|
||||
public final class StringRecord extends ContinuableRecord {
|
||||
public static final short sid = 0x0207;
|
||||
public static final short sid = 0x0207;
|
||||
|
||||
private boolean _is16bitUnicode;
|
||||
private String _text;
|
||||
private boolean _is16bitUnicode;
|
||||
private String _text;
|
||||
|
||||
public StringRecord() {}
|
||||
|
||||
|
||||
@ -31,174 +31,174 @@ import org.apache.poi.util.StringUtil;
|
||||
* Describes a builtin to the gui or user defined style
|
||||
*/
|
||||
public final class StyleRecord extends StandardRecord {
|
||||
public static final short sid = 0x0293;
|
||||
public static final short sid = 0x0293;
|
||||
|
||||
private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF);
|
||||
private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000);
|
||||
private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF);
|
||||
private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000);
|
||||
|
||||
/** shared by both user defined and built-in styles */
|
||||
private int field_1_xf_index;
|
||||
/** shared by both user defined and built-in styles */
|
||||
private int field_1_xf_index;
|
||||
|
||||
// only for built in styles
|
||||
private int field_2_builtin_style;
|
||||
private int field_3_outline_style_level;
|
||||
// only for built in styles
|
||||
private int field_2_builtin_style;
|
||||
private int field_3_outline_style_level;
|
||||
|
||||
// only for user defined styles
|
||||
private boolean field_3_stringHasMultibyte;
|
||||
private String field_4_name;
|
||||
// only for user defined styles
|
||||
private boolean field_3_stringHasMultibyte;
|
||||
private String field_4_name;
|
||||
|
||||
/**
|
||||
* creates a new style record, initially set to 'built-in'
|
||||
*/
|
||||
public StyleRecord() {
|
||||
field_1_xf_index = isBuiltinFlag.set(0);
|
||||
}
|
||||
/**
|
||||
* creates a new style record, initially set to 'built-in'
|
||||
*/
|
||||
public StyleRecord() {
|
||||
field_1_xf_index = isBuiltinFlag.set(0);
|
||||
}
|
||||
|
||||
public StyleRecord(StyleRecord other) {
|
||||
super(other);
|
||||
field_1_xf_index = other.field_1_xf_index;
|
||||
field_2_builtin_style = other.field_2_builtin_style;
|
||||
field_3_outline_style_level = other.field_3_outline_style_level;
|
||||
field_3_stringHasMultibyte = other.field_3_stringHasMultibyte;
|
||||
field_4_name = other.field_4_name;
|
||||
}
|
||||
public StyleRecord(StyleRecord other) {
|
||||
super(other);
|
||||
field_1_xf_index = other.field_1_xf_index;
|
||||
field_2_builtin_style = other.field_2_builtin_style;
|
||||
field_3_outline_style_level = other.field_3_outline_style_level;
|
||||
field_3_stringHasMultibyte = other.field_3_stringHasMultibyte;
|
||||
field_4_name = other.field_4_name;
|
||||
}
|
||||
|
||||
public StyleRecord(RecordInputStream in) {
|
||||
field_1_xf_index = in.readShort();
|
||||
if (isBuiltin()) {
|
||||
field_2_builtin_style = in.readByte();
|
||||
field_3_outline_style_level = in.readByte();
|
||||
} else {
|
||||
int field_2_name_length = in.readShort();
|
||||
public StyleRecord(RecordInputStream in) {
|
||||
field_1_xf_index = in.readShort();
|
||||
if (isBuiltin()) {
|
||||
field_2_builtin_style = in.readByte();
|
||||
field_3_outline_style_level = in.readByte();
|
||||
} else {
|
||||
int field_2_name_length = in.readShort();
|
||||
|
||||
if(in.remaining() < 1) {
|
||||
// Some files from Crystal Reports lack the is16BitUnicode byte
|
||||
// the remaining fields, which is naughty
|
||||
if (field_2_name_length != 0) {
|
||||
throw new RecordFormatException("Ran out of data reading style record");
|
||||
}
|
||||
// guess this is OK if the string length is zero
|
||||
field_4_name = "";
|
||||
} else {
|
||||
if(in.remaining() < 1) {
|
||||
// Some files from Crystal Reports lack the is16BitUnicode byte
|
||||
// the remaining fields, which is naughty
|
||||
if (field_2_name_length != 0) {
|
||||
throw new RecordFormatException("Ran out of data reading style record");
|
||||
}
|
||||
// guess this is OK if the string length is zero
|
||||
field_4_name = "";
|
||||
} else {
|
||||
|
||||
field_3_stringHasMultibyte = in.readByte() != 0x00;
|
||||
if (field_3_stringHasMultibyte) {
|
||||
field_4_name = StringUtil.readUnicodeLE(in, field_2_name_length);
|
||||
} else {
|
||||
field_4_name = StringUtil.readCompressedUnicode(in, field_2_name_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
field_3_stringHasMultibyte = in.readByte() != 0x00;
|
||||
if (field_3_stringHasMultibyte) {
|
||||
field_4_name = StringUtil.readUnicodeLE(in, field_2_name_length);
|
||||
} else {
|
||||
field_4_name = StringUtil.readCompressedUnicode(in, field_2_name_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the actual index of the style extended format record
|
||||
* @param xfIndex of the xf record
|
||||
*/
|
||||
public void setXFIndex(int xfIndex) {
|
||||
field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex);
|
||||
}
|
||||
/**
|
||||
* set the actual index of the style extended format record
|
||||
* @param xfIndex of the xf record
|
||||
*/
|
||||
public void setXFIndex(int xfIndex) {
|
||||
field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the actual index of the style extended format record
|
||||
* @see #getXFIndex()
|
||||
* @return index of the xf record
|
||||
*/
|
||||
public int getXFIndex() {
|
||||
return styleIndexMask.getValue(field_1_xf_index);
|
||||
}
|
||||
/**
|
||||
* get the actual index of the style extended format record
|
||||
* @see #getXFIndex()
|
||||
* @return index of the xf record
|
||||
*/
|
||||
public int getXFIndex() {
|
||||
return styleIndexMask.getValue(field_1_xf_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the style's name
|
||||
* @param name of the style
|
||||
*/
|
||||
public void setName(String name) {
|
||||
field_4_name = name;
|
||||
field_3_stringHasMultibyte = StringUtil.hasMultibyte(name);
|
||||
field_1_xf_index = isBuiltinFlag.clear(field_1_xf_index);
|
||||
}
|
||||
/**
|
||||
* set the style's name
|
||||
* @param name of the style
|
||||
*/
|
||||
public void setName(String name) {
|
||||
field_4_name = name;
|
||||
field_3_stringHasMultibyte = StringUtil.hasMultibyte(name);
|
||||
field_1_xf_index = isBuiltinFlag.clear(field_1_xf_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* if this is a builtin style set the number of the built in style
|
||||
* @param builtinStyleId style number (0-7)
|
||||
*
|
||||
*/
|
||||
public void setBuiltinStyle(int builtinStyleId) {
|
||||
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index);
|
||||
field_2_builtin_style = builtinStyleId;
|
||||
}
|
||||
/**
|
||||
* if this is a builtin style set the number of the built in style
|
||||
* @param builtinStyleId style number (0-7)
|
||||
*
|
||||
*/
|
||||
public void setBuiltinStyle(int builtinStyleId) {
|
||||
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index);
|
||||
field_2_builtin_style = builtinStyleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the row or column level of the style (if builtin 1||2)
|
||||
*
|
||||
* @param level The outline-level
|
||||
*/
|
||||
public void setOutlineStyleLevel(int level) {
|
||||
field_3_outline_style_level = level & 0x00FF;
|
||||
}
|
||||
/**
|
||||
* set the row or column level of the style (if builtin 1||2)
|
||||
*
|
||||
* @param level The outline-level
|
||||
*/
|
||||
public void setOutlineStyleLevel(int level) {
|
||||
field_3_outline_style_level = level & 0x00FF;
|
||||
}
|
||||
|
||||
public boolean isBuiltin(){
|
||||
return isBuiltinFlag.isSet(field_1_xf_index);
|
||||
}
|
||||
public boolean isBuiltin(){
|
||||
return isBuiltinFlag.isSet(field_1_xf_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the style's name
|
||||
* @return name of the style
|
||||
*/
|
||||
public String getName() {
|
||||
return field_4_name;
|
||||
}
|
||||
/**
|
||||
* get the style's name
|
||||
* @return name of the style
|
||||
*/
|
||||
public String getName() {
|
||||
return field_4_name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
if (isBuiltin()) {
|
||||
return 4; // short, byte, byte
|
||||
}
|
||||
return 2 // short xf index
|
||||
+ 3 // str len + flag
|
||||
+ field_4_name.length() * (field_3_stringHasMultibyte ? 2 : 1);
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
if (isBuiltin()) {
|
||||
return 4; // short, byte, byte
|
||||
}
|
||||
return 2 // short xf index
|
||||
+ 3 // str len + flag
|
||||
+ field_4_name.length() * (field_3_stringHasMultibyte ? 2 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_xf_index);
|
||||
if (isBuiltin()) {
|
||||
out.writeByte(field_2_builtin_style);
|
||||
out.writeByte(field_3_outline_style_level);
|
||||
} else {
|
||||
out.writeShort(field_4_name.length());
|
||||
out.writeByte(field_3_stringHasMultibyte ? 0x01 : 0x00);
|
||||
if (field_3_stringHasMultibyte) {
|
||||
StringUtil.putUnicodeLE(getName(), out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(getName(), out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(field_1_xf_index);
|
||||
if (isBuiltin()) {
|
||||
out.writeByte(field_2_builtin_style);
|
||||
out.writeByte(field_3_outline_style_level);
|
||||
} else {
|
||||
out.writeShort(field_4_name.length());
|
||||
out.writeByte(field_3_stringHasMultibyte ? 0x01 : 0x00);
|
||||
if (field_3_stringHasMultibyte) {
|
||||
StringUtil.putUnicodeLE(getName(), out);
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(getName(), out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleRecord copy() {
|
||||
return new StyleRecord(this);
|
||||
}
|
||||
@Override
|
||||
public StyleRecord copy() {
|
||||
return new StyleRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.STYLE;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.STYLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"xfIndex", this::getXFIndex,
|
||||
"type", () -> isBuiltin() ? "built-in" : "user-defined",
|
||||
"builtin_style", () -> field_2_builtin_style,
|
||||
"outline_level", () -> field_3_outline_style_level,
|
||||
"name", this::getName
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"xfIndex", this::getXFIndex,
|
||||
"type", () -> isBuiltin() ? "built-in" : "user-defined",
|
||||
"builtin_style", () -> field_2_builtin_style,
|
||||
"outline_level", () -> field_3_outline_style_level,
|
||||
"name", this::getName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,61 +38,61 @@ import org.apache.poi.util.LittleEndianOutputStream;
|
||||
*/
|
||||
public abstract class SubRecord implements Duplicatable, GenericRecord {
|
||||
|
||||
public enum SubRecordTypes {
|
||||
UNKNOWN(-1, UnknownSubRecord::new),
|
||||
END(0x0000, EndSubRecord::new),
|
||||
GROUP_MARKER(0x0006, GroupMarkerSubRecord::new),
|
||||
FT_CF(0x0007, FtCfSubRecord::new),
|
||||
FT_PIO_GRBIT(0x0008, FtPioGrbitSubRecord::new),
|
||||
EMBEDDED_OBJECT_REF(0x0009, EmbeddedObjectRefSubRecord::new),
|
||||
FT_CBLS(0x000C, FtCblsSubRecord::new),
|
||||
NOTE_STRUCTURE(0x000D, NoteStructureSubRecord::new),
|
||||
LBS_DATA(0x0013, LbsDataSubRecord::new),
|
||||
COMMON_OBJECT_DATA(0x0015, CommonObjectDataSubRecord::new),
|
||||
;
|
||||
public enum SubRecordTypes {
|
||||
UNKNOWN(-1, UnknownSubRecord::new),
|
||||
END(0x0000, EndSubRecord::new),
|
||||
GROUP_MARKER(0x0006, GroupMarkerSubRecord::new),
|
||||
FT_CF(0x0007, FtCfSubRecord::new),
|
||||
FT_PIO_GRBIT(0x0008, FtPioGrbitSubRecord::new),
|
||||
EMBEDDED_OBJECT_REF(0x0009, EmbeddedObjectRefSubRecord::new),
|
||||
FT_CBLS(0x000C, FtCblsSubRecord::new),
|
||||
NOTE_STRUCTURE(0x000D, NoteStructureSubRecord::new),
|
||||
LBS_DATA(0x0013, LbsDataSubRecord::new),
|
||||
COMMON_OBJECT_DATA(0x0015, CommonObjectDataSubRecord::new),
|
||||
;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RecordConstructor<T extends SubRecord> {
|
||||
/**
|
||||
* read a sub-record from the supplied stream
|
||||
*
|
||||
* @param in the stream to read from
|
||||
* @param cmoOt the objectType field of the containing CommonObjectDataSubRecord,
|
||||
* we need it to propagate to next sub-records as it defines what data follows
|
||||
* @return the created sub-record
|
||||
*/
|
||||
T apply(LittleEndianInput in, int size, int cmoOt);
|
||||
}
|
||||
@FunctionalInterface
|
||||
public interface RecordConstructor<T extends SubRecord> {
|
||||
/**
|
||||
* read a sub-record from the supplied stream
|
||||
*
|
||||
* @param in the stream to read from
|
||||
* @param cmoOt the objectType field of the containing CommonObjectDataSubRecord,
|
||||
* we need it to propagate to next sub-records as it defines what data follows
|
||||
* @return the created sub-record
|
||||
*/
|
||||
T apply(LittleEndianInput in, int size, int cmoOt);
|
||||
}
|
||||
|
||||
private static final Map<Short,SubRecordTypes> LOOKUP =
|
||||
Arrays.stream(values()).collect(Collectors.toMap(SubRecordTypes::getSid, Function.identity()));
|
||||
private static final Map<Short,SubRecordTypes> LOOKUP =
|
||||
Arrays.stream(values()).collect(Collectors.toMap(SubRecordTypes::getSid, Function.identity()));
|
||||
|
||||
public final short sid;
|
||||
public final RecordConstructor<?> recordConstructor;
|
||||
public final short sid;
|
||||
public final RecordConstructor<?> recordConstructor;
|
||||
|
||||
SubRecordTypes(int sid, RecordConstructor<?> recordConstructor) {
|
||||
this.sid = (short)sid;
|
||||
this.recordConstructor = recordConstructor;
|
||||
}
|
||||
SubRecordTypes(int sid, RecordConstructor<?> recordConstructor) {
|
||||
this.sid = (short)sid;
|
||||
this.recordConstructor = recordConstructor;
|
||||
}
|
||||
|
||||
public static SubRecordTypes forSID(int sid) {
|
||||
return LOOKUP.getOrDefault((short)sid, UNKNOWN);
|
||||
}
|
||||
public static SubRecordTypes forSID(int sid) {
|
||||
return LOOKUP.getOrDefault((short)sid, UNKNOWN);
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 1_000_000;
|
||||
//arbitrarily selected; may need to increase
|
||||
private static final int MAX_RECORD_LENGTH = 1_000_000;
|
||||
|
||||
protected SubRecord() {}
|
||||
protected SubRecord() {}
|
||||
|
||||
protected SubRecord(SubRecord other) {}
|
||||
protected SubRecord(SubRecord other) {}
|
||||
|
||||
/**
|
||||
/**
|
||||
* read a sub-record from the supplied stream
|
||||
*
|
||||
* @param in the stream to read from
|
||||
@ -101,35 +101,35 @@ public abstract class SubRecord implements Duplicatable, GenericRecord {
|
||||
* @return the created sub-record
|
||||
*/
|
||||
public static SubRecord createSubRecord(LittleEndianInput in, int cmoOt) {
|
||||
int sid = in.readUShort();
|
||||
// Often (but not always) the datasize for the sub-record
|
||||
int size = in.readUShort();
|
||||
SubRecordTypes srt = SubRecordTypes.forSID(sid);
|
||||
return srt.recordConstructor.apply(in, size, srt == SubRecordTypes.UNKNOWN ? sid : cmoOt);
|
||||
}
|
||||
int sid = in.readUShort();
|
||||
// Often (but not always) the datasize for the sub-record
|
||||
int size = in.readUShort();
|
||||
SubRecordTypes srt = SubRecordTypes.forSID(sid);
|
||||
return srt.recordConstructor.apply(in, size, srt == SubRecordTypes.UNKNOWN ? sid : cmoOt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return GenericRecordJsonWriter.marshal(this);
|
||||
}
|
||||
@Override
|
||||
public final String toString() {
|
||||
return GenericRecordJsonWriter.marshal(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the size of the data for this record (which is always 4 bytes less than the total
|
||||
* record size). Note however, that ushort encoded after the record sid is usually but not
|
||||
* always the data size.
|
||||
*/
|
||||
protected abstract int getDataSize();
|
||||
public byte[] serialize() {
|
||||
int size = getDataSize() + 4;
|
||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(size);
|
||||
serialize(new LittleEndianOutputStream(baos));
|
||||
if (baos.size() != size) {
|
||||
throw new RuntimeException("write size mismatch");
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
/**
|
||||
* @return the size of the data for this record (which is always 4 bytes less than the total
|
||||
* record size). Note however, that ushort encoded after the record sid is usually but not
|
||||
* always the data size.
|
||||
*/
|
||||
protected abstract int getDataSize();
|
||||
public byte[] serialize() {
|
||||
int size = getDataSize() + 4;
|
||||
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(size);
|
||||
serialize(new LittleEndianOutputStream(baos));
|
||||
if (baos.size() != size) {
|
||||
throw new RuntimeException("write size mismatch");
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public abstract void serialize(LittleEndianOutput out);
|
||||
public abstract void serialize(LittleEndianOutput out);
|
||||
|
||||
|
||||
/**
|
||||
@ -146,48 +146,48 @@ public abstract class SubRecord implements Duplicatable, GenericRecord {
|
||||
|
||||
private static final class UnknownSubRecord extends SubRecord {
|
||||
|
||||
private final int _sid;
|
||||
private final byte[] _data;
|
||||
private final int _sid;
|
||||
private final byte[] _data;
|
||||
|
||||
public UnknownSubRecord(LittleEndianInput in, int size, int sid) {
|
||||
_sid = sid;
|
||||
byte[] buf = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
in.readFully(buf);
|
||||
_data = buf;
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return _data.length;
|
||||
}
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_sid);
|
||||
out.writeShort(_data.length);
|
||||
out.write(_data);
|
||||
}
|
||||
public UnknownSubRecord(LittleEndianInput in, int size, int sid) {
|
||||
_sid = sid;
|
||||
byte[] buf = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
|
||||
in.readFully(buf);
|
||||
_data = buf;
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return _data.length;
|
||||
}
|
||||
@Override
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_sid);
|
||||
out.writeShort(_data.length);
|
||||
out.write(_data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnknownSubRecord copy() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public UnknownSubRecord copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubRecordTypes getGenericRecordType() {
|
||||
return SubRecordTypes.UNKNOWN;
|
||||
}
|
||||
@Override
|
||||
public SubRecordTypes getGenericRecordType() {
|
||||
return SubRecordTypes.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"sid", () -> _sid,
|
||||
"data", () -> _data
|
||||
);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"sid", () -> _sid,
|
||||
"data", () -> _data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract SubRecord copy();
|
||||
@Override
|
||||
public abstract SubRecord copy();
|
||||
|
||||
@Override
|
||||
public abstract SubRecordTypes getGenericRecordType();
|
||||
@Override
|
||||
public abstract SubRecordTypes getGenericRecordType();
|
||||
}
|
||||
|
||||
@ -200,39 +200,39 @@ public final class SupBookRecord extends StandardRecord {
|
||||
}
|
||||
private static String decodeFileName(String encodedUrl) {
|
||||
/* see "MICROSOFT OFFICE EXCEL 97-2007 BINARY FILE FORMAT SPECIFICATION" */
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i=1; i<encodedUrl.length(); i++) {
|
||||
char c = encodedUrl.charAt(i);
|
||||
switch (c) {
|
||||
case CH_VOLUME:
|
||||
char driveLetter = encodedUrl.charAt(++i);
|
||||
if (driveLetter == '@') {
|
||||
sb.append("\\\\");
|
||||
} else {
|
||||
//Windows notation for drive letters
|
||||
sb.append(driveLetter).append(":");
|
||||
}
|
||||
break;
|
||||
case CH_SAME_VOLUME:
|
||||
char c = encodedUrl.charAt(i);
|
||||
switch (c) {
|
||||
case CH_VOLUME:
|
||||
char driveLetter = encodedUrl.charAt(++i);
|
||||
if (driveLetter == '@') {
|
||||
sb.append("\\\\");
|
||||
} else {
|
||||
//Windows notation for drive letters
|
||||
sb.append(driveLetter).append(":");
|
||||
}
|
||||
break;
|
||||
case CH_SAME_VOLUME:
|
||||
case CH_DOWN_DIR:
|
||||
sb.append(PATH_SEPERATOR);
|
||||
break;
|
||||
case CH_UP_DIR:
|
||||
sb.append("..").append(PATH_SEPERATOR);
|
||||
break;
|
||||
case CH_LONG_VOLUME:
|
||||
//Don't known to handle...
|
||||
LOG.atWarn().log("Found unexpected key: ChLongVolume - IGNORING");
|
||||
break;
|
||||
case CH_STARTUP_DIR:
|
||||
case CH_ALT_STARTUP_DIR:
|
||||
case CH_LIB_DIR:
|
||||
LOG.atWarn().log("EXCEL.EXE path unknown - using this directory instead: .");
|
||||
sb.append(".").append(PATH_SEPERATOR);
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append(PATH_SEPERATOR);
|
||||
break;
|
||||
case CH_UP_DIR:
|
||||
sb.append("..").append(PATH_SEPERATOR);
|
||||
break;
|
||||
case CH_LONG_VOLUME:
|
||||
//Don't known to handle...
|
||||
LOG.atWarn().log("Found unexpected key: ChLongVolume - IGNORING");
|
||||
break;
|
||||
case CH_STARTUP_DIR:
|
||||
case CH_ALT_STARTUP_DIR:
|
||||
case CH_LIB_DIR:
|
||||
LOG.atWarn().log("EXCEL.EXE path unknown - using this directory instead: .");
|
||||
sb.append(".").append(PATH_SEPERATOR);
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@ -241,8 +241,8 @@ public final class SupBookRecord extends StandardRecord {
|
||||
}
|
||||
|
||||
public void setURL(String pUrl) {
|
||||
//Keep the first marker character!
|
||||
field_2_encoded_url = field_2_encoded_url.substring(0, 1) + pUrl;
|
||||
//Keep the first marker character!
|
||||
field_2_encoded_url = field_2_encoded_url.substring(0, 1) + pUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -35,163 +35,163 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* which should only contain a single {@link TblPtg Ptg}.
|
||||
*/
|
||||
public final class TableRecord extends SharedValueRecordBase {
|
||||
public static final short sid = 0x0236;
|
||||
public static final short sid = 0x0236;
|
||||
|
||||
private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
|
||||
private static final BitField calcOnOpen = BitFieldFactory.getInstance(0x0002);
|
||||
private static final BitField rowOrColInpCell = BitFieldFactory.getInstance(0x0004);
|
||||
private static final BitField oneOrTwoVar = BitFieldFactory.getInstance(0x0008);
|
||||
private static final BitField rowDeleted = BitFieldFactory.getInstance(0x0010);
|
||||
private static final BitField colDeleted = BitFieldFactory.getInstance(0x0020);
|
||||
private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
|
||||
private static final BitField calcOnOpen = BitFieldFactory.getInstance(0x0002);
|
||||
private static final BitField rowOrColInpCell = BitFieldFactory.getInstance(0x0004);
|
||||
private static final BitField oneOrTwoVar = BitFieldFactory.getInstance(0x0008);
|
||||
private static final BitField rowDeleted = BitFieldFactory.getInstance(0x0010);
|
||||
private static final BitField colDeleted = BitFieldFactory.getInstance(0x0020);
|
||||
|
||||
private int field_5_flags;
|
||||
private int field_6_res;
|
||||
private int field_7_rowInputRow;
|
||||
private int field_8_colInputRow;
|
||||
private int field_9_rowInputCol;
|
||||
private int field_10_colInputCol;
|
||||
private int field_5_flags;
|
||||
private int field_6_res;
|
||||
private int field_7_rowInputRow;
|
||||
private int field_8_colInputRow;
|
||||
private int field_9_rowInputCol;
|
||||
private int field_10_colInputCol;
|
||||
|
||||
public TableRecord(TableRecord other) {
|
||||
super(other);
|
||||
field_5_flags = other.field_5_flags;
|
||||
field_6_res = other.field_6_res;
|
||||
field_7_rowInputRow = other.field_7_rowInputRow;
|
||||
field_8_colInputRow = other.field_8_colInputRow;
|
||||
field_9_rowInputCol = other.field_9_rowInputCol;
|
||||
field_10_colInputCol = other.field_10_colInputCol;
|
||||
}
|
||||
public TableRecord(TableRecord other) {
|
||||
super(other);
|
||||
field_5_flags = other.field_5_flags;
|
||||
field_6_res = other.field_6_res;
|
||||
field_7_rowInputRow = other.field_7_rowInputRow;
|
||||
field_8_colInputRow = other.field_8_colInputRow;
|
||||
field_9_rowInputCol = other.field_9_rowInputCol;
|
||||
field_10_colInputCol = other.field_10_colInputCol;
|
||||
}
|
||||
|
||||
public TableRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
field_5_flags = in.readByte();
|
||||
field_6_res = in.readByte();
|
||||
field_7_rowInputRow = in.readShort();
|
||||
field_8_colInputRow = in.readShort();
|
||||
field_9_rowInputCol = in.readShort();
|
||||
field_10_colInputCol = in.readShort();
|
||||
}
|
||||
public TableRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
field_5_flags = in.readByte();
|
||||
field_6_res = in.readByte();
|
||||
field_7_rowInputRow = in.readShort();
|
||||
field_8_colInputRow = in.readShort();
|
||||
field_9_rowInputCol = in.readShort();
|
||||
field_10_colInputCol = in.readShort();
|
||||
}
|
||||
|
||||
public TableRecord(CellRangeAddress8Bit range) {
|
||||
super(range);
|
||||
field_6_res = 0;
|
||||
}
|
||||
public TableRecord(CellRangeAddress8Bit range) {
|
||||
super(range);
|
||||
field_6_res = 0;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return field_5_flags;
|
||||
}
|
||||
public void setFlags(int flags) {
|
||||
field_5_flags = flags;
|
||||
}
|
||||
public int getFlags() {
|
||||
return field_5_flags;
|
||||
}
|
||||
public void setFlags(int flags) {
|
||||
field_5_flags = flags;
|
||||
}
|
||||
|
||||
public int getRowInputRow() {
|
||||
return field_7_rowInputRow;
|
||||
}
|
||||
public void setRowInputRow(int rowInputRow) {
|
||||
field_7_rowInputRow = rowInputRow;
|
||||
}
|
||||
public int getRowInputRow() {
|
||||
return field_7_rowInputRow;
|
||||
}
|
||||
public void setRowInputRow(int rowInputRow) {
|
||||
field_7_rowInputRow = rowInputRow;
|
||||
}
|
||||
|
||||
public int getColInputRow() {
|
||||
return field_8_colInputRow;
|
||||
}
|
||||
public void setColInputRow(int colInputRow) {
|
||||
field_8_colInputRow = colInputRow;
|
||||
}
|
||||
public int getColInputRow() {
|
||||
return field_8_colInputRow;
|
||||
}
|
||||
public void setColInputRow(int colInputRow) {
|
||||
field_8_colInputRow = colInputRow;
|
||||
}
|
||||
|
||||
public int getRowInputCol() {
|
||||
return field_9_rowInputCol;
|
||||
}
|
||||
public void setRowInputCol(int rowInputCol) {
|
||||
field_9_rowInputCol = rowInputCol;
|
||||
}
|
||||
public int getRowInputCol() {
|
||||
return field_9_rowInputCol;
|
||||
}
|
||||
public void setRowInputCol(int rowInputCol) {
|
||||
field_9_rowInputCol = rowInputCol;
|
||||
}
|
||||
|
||||
public int getColInputCol() {
|
||||
return field_10_colInputCol;
|
||||
}
|
||||
public void setColInputCol(int colInputCol) {
|
||||
field_10_colInputCol = colInputCol;
|
||||
}
|
||||
public int getColInputCol() {
|
||||
return field_10_colInputCol;
|
||||
}
|
||||
public void setColInputCol(int colInputCol) {
|
||||
field_10_colInputCol = colInputCol;
|
||||
}
|
||||
|
||||
|
||||
public boolean isAlwaysCalc() {
|
||||
return alwaysCalc.isSet(field_5_flags);
|
||||
}
|
||||
public void setAlwaysCalc(boolean flag) {
|
||||
field_5_flags = alwaysCalc.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
public boolean isAlwaysCalc() {
|
||||
return alwaysCalc.isSet(field_5_flags);
|
||||
}
|
||||
public void setAlwaysCalc(boolean flag) {
|
||||
field_5_flags = alwaysCalc.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
|
||||
public boolean isRowOrColInpCell() {
|
||||
return rowOrColInpCell.isSet(field_5_flags);
|
||||
}
|
||||
public void setRowOrColInpCell(boolean flag) {
|
||||
field_5_flags = rowOrColInpCell.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
public boolean isRowOrColInpCell() {
|
||||
return rowOrColInpCell.isSet(field_5_flags);
|
||||
}
|
||||
public void setRowOrColInpCell(boolean flag) {
|
||||
field_5_flags = rowOrColInpCell.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
|
||||
public boolean isOneNotTwoVar() {
|
||||
return oneOrTwoVar.isSet(field_5_flags);
|
||||
}
|
||||
public void setOneNotTwoVar(boolean flag) {
|
||||
field_5_flags = oneOrTwoVar.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
public boolean isOneNotTwoVar() {
|
||||
return oneOrTwoVar.isSet(field_5_flags);
|
||||
}
|
||||
public void setOneNotTwoVar(boolean flag) {
|
||||
field_5_flags = oneOrTwoVar.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
|
||||
public boolean isColDeleted() {
|
||||
return colDeleted.isSet(field_5_flags);
|
||||
}
|
||||
public void setColDeleted(boolean flag) {
|
||||
field_5_flags = colDeleted.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
public boolean isColDeleted() {
|
||||
return colDeleted.isSet(field_5_flags);
|
||||
}
|
||||
public void setColDeleted(boolean flag) {
|
||||
field_5_flags = colDeleted.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
|
||||
public boolean isRowDeleted() {
|
||||
return rowDeleted.isSet(field_5_flags);
|
||||
}
|
||||
public void setRowDeleted(boolean flag) {
|
||||
field_5_flags = rowDeleted.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
public boolean isRowDeleted() {
|
||||
return rowDeleted.isSet(field_5_flags);
|
||||
}
|
||||
public void setRowDeleted(boolean flag) {
|
||||
field_5_flags = rowDeleted.setBoolean(field_5_flags, flag);
|
||||
}
|
||||
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
protected int getExtraDataSize() {
|
||||
return
|
||||
2 // 2 byte fields
|
||||
+ 8; // 4 short fields
|
||||
}
|
||||
protected void serializeExtraData(LittleEndianOutput out) {
|
||||
out.writeByte(field_5_flags);
|
||||
out.writeByte(field_6_res);
|
||||
out.writeShort(field_7_rowInputRow);
|
||||
out.writeShort(field_8_colInputRow);
|
||||
out.writeShort(field_9_rowInputCol);
|
||||
out.writeShort(field_10_colInputCol);
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
protected int getExtraDataSize() {
|
||||
return
|
||||
2 // 2 byte fields
|
||||
+ 8; // 4 short fields
|
||||
}
|
||||
protected void serializeExtraData(LittleEndianOutput out) {
|
||||
out.writeByte(field_5_flags);
|
||||
out.writeByte(field_6_res);
|
||||
out.writeShort(field_7_rowInputRow);
|
||||
out.writeShort(field_8_colInputRow);
|
||||
out.writeShort(field_9_rowInputCol);
|
||||
out.writeShort(field_10_colInputCol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableRecord copy() {
|
||||
return new TableRecord(this);
|
||||
}
|
||||
@Override
|
||||
public TableRecord copy() {
|
||||
return new TableRecord(this);
|
||||
}
|
||||
|
||||
private static CellReference cr(int rowIx, int colIxAndFlags) {
|
||||
int colIx = colIxAndFlags & 0x00FF;
|
||||
boolean isRowAbs = (colIxAndFlags & 0x8000) == 0;
|
||||
boolean isColAbs = (colIxAndFlags & 0x4000) == 0;
|
||||
return new CellReference(rowIx, colIx, isRowAbs, isColAbs);
|
||||
}
|
||||
private static CellReference cr(int rowIx, int colIxAndFlags) {
|
||||
int colIx = colIxAndFlags & 0x00FF;
|
||||
boolean isRowAbs = (colIxAndFlags & 0x8000) == 0;
|
||||
boolean isColAbs = (colIxAndFlags & 0x4000) == 0;
|
||||
return new CellReference(rowIx, colIx, isRowAbs, isColAbs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TABLE;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"range", this::getRange,
|
||||
"flags", getBitsAsString(this::getFlags,
|
||||
new BitField[]{alwaysCalc, calcOnOpen, rowOrColInpCell, oneOrTwoVar, rowDeleted, colDeleted},
|
||||
new String[]{"ALWAYS_CALC","CALC_ON_OPEN","ROW_OR_COL_INP_CELL","ONE_OR_TWO_VAR","ROW_DELETED","COL_DELETED"}),
|
||||
"reserved", () -> field_6_res,
|
||||
"rowInput", () -> cr(field_7_rowInputRow, field_8_colInputRow),
|
||||
"colInput", () -> cr(field_9_rowInputCol, field_10_colInputCol)
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"range", this::getRange,
|
||||
"flags", getBitsAsString(this::getFlags,
|
||||
new BitField[]{alwaysCalc, calcOnOpen, rowOrColInpCell, oneOrTwoVar, rowDeleted, colDeleted},
|
||||
new String[]{"ALWAYS_CALC","CALC_ON_OPEN","ROW_OR_COL_INP_CELL","ONE_OR_TWO_VAR","ROW_DELETED","COL_DELETED"}),
|
||||
"reserved", () -> field_6_res,
|
||||
"rowInput", () -> cr(field_7_rowInputRow, field_8_colInputRow),
|
||||
"colInput", () -> cr(field_9_rowInputCol, field_10_colInputCol)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,83 +28,83 @@ import org.apache.poi.util.StringUtil;
|
||||
* TABLESTYLES (0x088E)
|
||||
*/
|
||||
public final class TableStylesRecord extends StandardRecord {
|
||||
public static final short sid = 0x088E;
|
||||
public static final short sid = 0x088E;
|
||||
|
||||
private int rt;
|
||||
private int grbitFrt;
|
||||
private final byte[] unused = new byte[8];
|
||||
private int cts;
|
||||
private int rt;
|
||||
private int grbitFrt;
|
||||
private final byte[] unused = new byte[8];
|
||||
private int cts;
|
||||
|
||||
private String rgchDefListStyle;
|
||||
private String rgchDefPivotStyle;
|
||||
private String rgchDefListStyle;
|
||||
private String rgchDefPivotStyle;
|
||||
|
||||
|
||||
public TableStylesRecord(TableStylesRecord other) {
|
||||
super(other);
|
||||
rt = other.rt;
|
||||
grbitFrt = other.grbitFrt;
|
||||
System.arraycopy(other.unused, 0, unused, 0, unused.length);
|
||||
cts = other.cts;
|
||||
rgchDefListStyle = other.rgchDefListStyle;
|
||||
rgchDefPivotStyle = other.rgchDefPivotStyle;
|
||||
}
|
||||
public TableStylesRecord(TableStylesRecord other) {
|
||||
super(other);
|
||||
rt = other.rt;
|
||||
grbitFrt = other.grbitFrt;
|
||||
System.arraycopy(other.unused, 0, unused, 0, unused.length);
|
||||
cts = other.cts;
|
||||
rgchDefListStyle = other.rgchDefListStyle;
|
||||
rgchDefPivotStyle = other.rgchDefPivotStyle;
|
||||
}
|
||||
|
||||
public TableStylesRecord(RecordInputStream in) {
|
||||
rt = in.readUShort();
|
||||
grbitFrt = in.readUShort();
|
||||
in.readFully(unused);
|
||||
cts = in.readInt();
|
||||
int cchDefListStyle = in.readUShort();
|
||||
int cchDefPivotStyle = in.readUShort();
|
||||
public TableStylesRecord(RecordInputStream in) {
|
||||
rt = in.readUShort();
|
||||
grbitFrt = in.readUShort();
|
||||
in.readFully(unused);
|
||||
cts = in.readInt();
|
||||
int cchDefListStyle = in.readUShort();
|
||||
int cchDefPivotStyle = in.readUShort();
|
||||
|
||||
rgchDefListStyle = in.readUnicodeLEString(cchDefListStyle);
|
||||
rgchDefPivotStyle = in.readUnicodeLEString(cchDefPivotStyle);
|
||||
}
|
||||
rgchDefListStyle = in.readUnicodeLEString(cchDefListStyle);
|
||||
rgchDefPivotStyle = in.readUnicodeLEString(cchDefPivotStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(rt);
|
||||
out.writeShort(grbitFrt);
|
||||
out.write(unused);
|
||||
out.writeInt(cts);
|
||||
@Override
|
||||
protected void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(rt);
|
||||
out.writeShort(grbitFrt);
|
||||
out.write(unused);
|
||||
out.writeInt(cts);
|
||||
|
||||
out.writeShort(rgchDefListStyle.length());
|
||||
out.writeShort(rgchDefPivotStyle.length());
|
||||
out.writeShort(rgchDefListStyle.length());
|
||||
out.writeShort(rgchDefPivotStyle.length());
|
||||
|
||||
StringUtil.putUnicodeLE(rgchDefListStyle, out);
|
||||
StringUtil.putUnicodeLE(rgchDefPivotStyle, out);
|
||||
}
|
||||
StringUtil.putUnicodeLE(rgchDefListStyle, out);
|
||||
StringUtil.putUnicodeLE(rgchDefPivotStyle, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 2 + 2 + 8 + 4 + 2 + 2
|
||||
+ (2*rgchDefListStyle.length()) + (2*rgchDefPivotStyle.length());
|
||||
}
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 2 + 2 + 8 + 4 + 2 + 2
|
||||
+ (2*rgchDefListStyle.length()) + (2*rgchDefPivotStyle.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
@Override
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableStylesRecord copy() {
|
||||
return new TableStylesRecord(this);
|
||||
}
|
||||
@Override
|
||||
public TableStylesRecord copy() {
|
||||
return new TableStylesRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TABLE_STYLES;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TABLE_STYLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rt", () -> rt,
|
||||
"grbitFrt", () -> grbitFrt,
|
||||
"unused", () -> unused,
|
||||
"cts", () -> cts,
|
||||
"rgchDefListStyle", () -> rgchDefListStyle,
|
||||
"rgchDefPivotStyle", () -> rgchDefPivotStyle
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties(
|
||||
"rt", () -> rt,
|
||||
"grbitFrt", () -> grbitFrt,
|
||||
"unused", () -> unused,
|
||||
"cts", () -> cts,
|
||||
"rgchDefListStyle", () -> rgchDefListStyle,
|
||||
"rgchDefPivotStyle", () -> rgchDefPivotStyle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,306 +38,306 @@ import org.apache.poi.util.RecordFormatException;
|
||||
* contains the formatting runs.
|
||||
*/
|
||||
public final class TextObjectRecord extends ContinuableRecord {
|
||||
public static final short sid = 0x01B6;
|
||||
public static final short sid = 0x01B6;
|
||||
|
||||
private static final int FORMAT_RUN_ENCODED_SIZE = 8; // 2 shorts and 4 bytes reserved
|
||||
private static final int FORMAT_RUN_ENCODED_SIZE = 8; // 2 shorts and 4 bytes reserved
|
||||
|
||||
private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
|
||||
private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
|
||||
private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
|
||||
private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
|
||||
private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
|
||||
private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
|
||||
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
|
||||
public static final short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
|
||||
public static final short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
|
||||
|
||||
public static final short TEXT_ORIENTATION_NONE = 0;
|
||||
public static final short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
|
||||
public static final short TEXT_ORIENTATION_ROT_RIGHT = 2;
|
||||
public static final short TEXT_ORIENTATION_ROT_LEFT = 3;
|
||||
public static final short TEXT_ORIENTATION_NONE = 0;
|
||||
public static final short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
|
||||
public static final short TEXT_ORIENTATION_ROT_RIGHT = 2;
|
||||
public static final short TEXT_ORIENTATION_ROT_LEFT = 3;
|
||||
|
||||
private int field_1_options;
|
||||
private int field_2_textOrientation;
|
||||
private int field_3_reserved4;
|
||||
private int field_4_reserved5;
|
||||
private int field_5_reserved6;
|
||||
private int field_8_reserved7;
|
||||
private int field_1_options;
|
||||
private int field_2_textOrientation;
|
||||
private int field_3_reserved4;
|
||||
private int field_4_reserved5;
|
||||
private int field_5_reserved6;
|
||||
private int field_8_reserved7;
|
||||
|
||||
private HSSFRichTextString _text;
|
||||
private HSSFRichTextString _text;
|
||||
|
||||
/*
|
||||
* Note - the next three fields are very similar to those on
|
||||
* EmbededObjectRefSubRecord(ftPictFmla 0x0009)
|
||||
*
|
||||
* some observed values for the 4 bytes preceding the formula: C0 5E 86 03
|
||||
* C0 11 AC 02 80 F1 8A 03 D4 F0 8A 03
|
||||
*/
|
||||
private int _unknownPreFormulaInt;
|
||||
/** expect tRef, tRef3D, tArea, tArea3D or tName */
|
||||
private OperandPtg _linkRefPtg;
|
||||
/**
|
||||
* Not clear if needed . Excel seems to be OK if this byte is not present.
|
||||
* Value is often the same as the earlier firstColumn byte. */
|
||||
private Byte _unknownPostFormulaByte;
|
||||
/*
|
||||
* Note - the next three fields are very similar to those on
|
||||
* EmbededObjectRefSubRecord(ftPictFmla 0x0009)
|
||||
*
|
||||
* some observed values for the 4 bytes preceding the formula: C0 5E 86 03
|
||||
* C0 11 AC 02 80 F1 8A 03 D4 F0 8A 03
|
||||
*/
|
||||
private int _unknownPreFormulaInt;
|
||||
/** expect tRef, tRef3D, tArea, tArea3D or tName */
|
||||
private OperandPtg _linkRefPtg;
|
||||
/**
|
||||
* Not clear if needed . Excel seems to be OK if this byte is not present.
|
||||
* Value is often the same as the earlier firstColumn byte. */
|
||||
private Byte _unknownPostFormulaByte;
|
||||
|
||||
public TextObjectRecord() {}
|
||||
public TextObjectRecord() {}
|
||||
|
||||
public TextObjectRecord(TextObjectRecord other) {
|
||||
super(other);
|
||||
field_1_options = other.field_1_options;
|
||||
field_2_textOrientation = other.field_2_textOrientation;
|
||||
field_3_reserved4 = other.field_3_reserved4;
|
||||
field_4_reserved5 = other.field_4_reserved5;
|
||||
field_5_reserved6 = other.field_5_reserved6;
|
||||
field_8_reserved7 = other.field_8_reserved7;
|
||||
public TextObjectRecord(TextObjectRecord other) {
|
||||
super(other);
|
||||
field_1_options = other.field_1_options;
|
||||
field_2_textOrientation = other.field_2_textOrientation;
|
||||
field_3_reserved4 = other.field_3_reserved4;
|
||||
field_4_reserved5 = other.field_4_reserved5;
|
||||
field_5_reserved6 = other.field_5_reserved6;
|
||||
field_8_reserved7 = other.field_8_reserved7;
|
||||
|
||||
_text = other._text;
|
||||
_text = other._text;
|
||||
|
||||
if (other._linkRefPtg != null) {
|
||||
_unknownPreFormulaInt = other._unknownPreFormulaInt;
|
||||
_linkRefPtg = other._linkRefPtg.copy();
|
||||
_unknownPostFormulaByte = other._unknownPostFormulaByte;
|
||||
}
|
||||
}
|
||||
if (other._linkRefPtg != null) {
|
||||
_unknownPreFormulaInt = other._unknownPreFormulaInt;
|
||||
_linkRefPtg = other._linkRefPtg.copy();
|
||||
_unknownPostFormulaByte = other._unknownPostFormulaByte;
|
||||
}
|
||||
}
|
||||
|
||||
public TextObjectRecord(RecordInputStream in) {
|
||||
field_1_options = in.readUShort();
|
||||
field_2_textOrientation = in.readUShort();
|
||||
field_3_reserved4 = in.readUShort();
|
||||
field_4_reserved5 = in.readUShort();
|
||||
field_5_reserved6 = in.readUShort();
|
||||
int field_6_textLength = in.readUShort();
|
||||
int field_7_formattingDataLength = in.readUShort();
|
||||
field_8_reserved7 = in.readInt();
|
||||
public TextObjectRecord(RecordInputStream in) {
|
||||
field_1_options = in.readUShort();
|
||||
field_2_textOrientation = in.readUShort();
|
||||
field_3_reserved4 = in.readUShort();
|
||||
field_4_reserved5 = in.readUShort();
|
||||
field_5_reserved6 = in.readUShort();
|
||||
int field_6_textLength = in.readUShort();
|
||||
int field_7_formattingDataLength = in.readUShort();
|
||||
field_8_reserved7 = in.readInt();
|
||||
|
||||
if (in.remaining() > 0) {
|
||||
// Text Objects can have simple reference formulas
|
||||
// (This bit not mentioned in the MS document)
|
||||
if (in.remaining() < 11) {
|
||||
throw new RecordFormatException("Not enough remaining data for a link formula");
|
||||
}
|
||||
int formulaSize = in.readUShort();
|
||||
_unknownPreFormulaInt = in.readInt();
|
||||
Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
|
||||
if (ptgs.length != 1) {
|
||||
throw new RecordFormatException("Read " + ptgs.length
|
||||
+ " tokens but expected exactly 1");
|
||||
}
|
||||
_linkRefPtg = (OperandPtg) ptgs[0];
|
||||
_unknownPostFormulaByte = in.remaining() > 0 ? in.readByte() : null;
|
||||
} else {
|
||||
_linkRefPtg = null;
|
||||
}
|
||||
if (in.remaining() > 0) {
|
||||
throw new RecordFormatException("Unused " + in.remaining() + " bytes at end of record");
|
||||
}
|
||||
if (in.remaining() > 0) {
|
||||
// Text Objects can have simple reference formulas
|
||||
// (This bit not mentioned in the MS document)
|
||||
if (in.remaining() < 11) {
|
||||
throw new RecordFormatException("Not enough remaining data for a link formula");
|
||||
}
|
||||
int formulaSize = in.readUShort();
|
||||
_unknownPreFormulaInt = in.readInt();
|
||||
Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
|
||||
if (ptgs.length != 1) {
|
||||
throw new RecordFormatException("Read " + ptgs.length
|
||||
+ " tokens but expected exactly 1");
|
||||
}
|
||||
_linkRefPtg = (OperandPtg) ptgs[0];
|
||||
_unknownPostFormulaByte = in.remaining() > 0 ? in.readByte() : null;
|
||||
} else {
|
||||
_linkRefPtg = null;
|
||||
}
|
||||
if (in.remaining() > 0) {
|
||||
throw new RecordFormatException("Unused " + in.remaining() + " bytes at end of record");
|
||||
}
|
||||
|
||||
String text;
|
||||
if (field_6_textLength > 0) {
|
||||
text = readRawString(in, field_6_textLength);
|
||||
} else {
|
||||
text = "";
|
||||
}
|
||||
_text = new HSSFRichTextString(text);
|
||||
String text;
|
||||
if (field_6_textLength > 0) {
|
||||
text = readRawString(in, field_6_textLength);
|
||||
} else {
|
||||
text = "";
|
||||
}
|
||||
_text = new HSSFRichTextString(text);
|
||||
|
||||
if (field_7_formattingDataLength > 0) {
|
||||
processFontRuns(in, _text, field_7_formattingDataLength);
|
||||
}
|
||||
}
|
||||
if (field_7_formattingDataLength > 0) {
|
||||
processFontRuns(in, _text, field_7_formattingDataLength);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readRawString(RecordInputStream in, int textLength) {
|
||||
byte compressByte = in.readByte();
|
||||
boolean isCompressed = (compressByte & 0x01) == 0;
|
||||
if (isCompressed) {
|
||||
return in.readCompressedUnicode(textLength);
|
||||
}
|
||||
return in.readUnicodeLEString(textLength);
|
||||
}
|
||||
private static String readRawString(RecordInputStream in, int textLength) {
|
||||
byte compressByte = in.readByte();
|
||||
boolean isCompressed = (compressByte & 0x01) == 0;
|
||||
if (isCompressed) {
|
||||
return in.readCompressedUnicode(textLength);
|
||||
}
|
||||
return in.readUnicodeLEString(textLength);
|
||||
}
|
||||
|
||||
private static void processFontRuns(RecordInputStream in, HSSFRichTextString str,
|
||||
int formattingRunDataLength) {
|
||||
if (formattingRunDataLength % FORMAT_RUN_ENCODED_SIZE != 0) {
|
||||
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength
|
||||
+ ")");
|
||||
}
|
||||
int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
|
||||
for (int i = 0; i < nRuns; i++) {
|
||||
short index = in.readShort();
|
||||
short iFont = in.readShort();
|
||||
in.readInt(); // skip reserved.
|
||||
str.applyFont(index, str.length(), iFont);
|
||||
}
|
||||
}
|
||||
private static void processFontRuns(RecordInputStream in, HSSFRichTextString str,
|
||||
int formattingRunDataLength) {
|
||||
if (formattingRunDataLength % FORMAT_RUN_ENCODED_SIZE != 0) {
|
||||
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength
|
||||
+ ")");
|
||||
}
|
||||
int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
|
||||
for (int i = 0; i < nRuns; i++) {
|
||||
short index = in.readShort();
|
||||
short iFont = in.readShort();
|
||||
in.readInt(); // skip reserved.
|
||||
str.applyFont(index, str.length(), iFont);
|
||||
}
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
private void serializeTXORecord(ContinuableRecordOutput out) {
|
||||
private void serializeTXORecord(ContinuableRecordOutput out) {
|
||||
|
||||
out.writeShort(field_1_options);
|
||||
out.writeShort(field_2_textOrientation);
|
||||
out.writeShort(field_3_reserved4);
|
||||
out.writeShort(field_4_reserved5);
|
||||
out.writeShort(field_5_reserved6);
|
||||
out.writeShort(_text.length());
|
||||
out.writeShort(getFormattingDataLength());
|
||||
out.writeInt(field_8_reserved7);
|
||||
out.writeShort(field_1_options);
|
||||
out.writeShort(field_2_textOrientation);
|
||||
out.writeShort(field_3_reserved4);
|
||||
out.writeShort(field_4_reserved5);
|
||||
out.writeShort(field_5_reserved6);
|
||||
out.writeShort(_text.length());
|
||||
out.writeShort(getFormattingDataLength());
|
||||
out.writeInt(field_8_reserved7);
|
||||
|
||||
if (_linkRefPtg != null) {
|
||||
int formulaSize = _linkRefPtg.getSize();
|
||||
out.writeShort(formulaSize);
|
||||
out.writeInt(_unknownPreFormulaInt);
|
||||
_linkRefPtg.write(out);
|
||||
if (_unknownPostFormulaByte != null) {
|
||||
out.writeByte(_unknownPostFormulaByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_linkRefPtg != null) {
|
||||
int formulaSize = _linkRefPtg.getSize();
|
||||
out.writeShort(formulaSize);
|
||||
out.writeInt(_unknownPreFormulaInt);
|
||||
_linkRefPtg.write(out);
|
||||
if (_unknownPostFormulaByte != null) {
|
||||
out.writeByte(_unknownPostFormulaByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void serializeTrailingRecords(ContinuableRecordOutput out) {
|
||||
out.writeContinue();
|
||||
out.writeStringData(_text.getString());
|
||||
out.writeContinue();
|
||||
writeFormatData(out, _text);
|
||||
}
|
||||
private void serializeTrailingRecords(ContinuableRecordOutput out) {
|
||||
out.writeContinue();
|
||||
out.writeStringData(_text.getString());
|
||||
out.writeContinue();
|
||||
writeFormatData(out, _text);
|
||||
}
|
||||
|
||||
protected void serialize(ContinuableRecordOutput out) {
|
||||
protected void serialize(ContinuableRecordOutput out) {
|
||||
|
||||
serializeTXORecord(out);
|
||||
if (_text.getString().length() > 0) {
|
||||
serializeTrailingRecords(out);
|
||||
}
|
||||
}
|
||||
serializeTXORecord(out);
|
||||
if (_text.getString().length() > 0) {
|
||||
serializeTrailingRecords(out);
|
||||
}
|
||||
}
|
||||
|
||||
private int getFormattingDataLength() {
|
||||
if (_text.length() < 1) {
|
||||
// important - no formatting data if text is empty
|
||||
return 0;
|
||||
}
|
||||
return (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE;
|
||||
}
|
||||
private int getFormattingDataLength() {
|
||||
if (_text.length() < 1) {
|
||||
// important - no formatting data if text is empty
|
||||
return 0;
|
||||
}
|
||||
return (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
private static void writeFormatData(ContinuableRecordOutput out , HSSFRichTextString str) {
|
||||
int nRuns = str.numFormattingRuns();
|
||||
for (int i = 0; i < nRuns; i++) {
|
||||
out.writeShort(str.getIndexOfFormattingRun(i));
|
||||
int fontIndex = str.getFontOfFormattingRun(i);
|
||||
out.writeShort(fontIndex == HSSFRichTextString.NO_FONT ? 0 : fontIndex);
|
||||
out.writeInt(0); // skip reserved
|
||||
}
|
||||
out.writeShort(str.length());
|
||||
out.writeShort(0);
|
||||
out.writeInt(0); // skip reserved
|
||||
}
|
||||
private static void writeFormatData(ContinuableRecordOutput out , HSSFRichTextString str) {
|
||||
int nRuns = str.numFormattingRuns();
|
||||
for (int i = 0; i < nRuns; i++) {
|
||||
out.writeShort(str.getIndexOfFormattingRun(i));
|
||||
int fontIndex = str.getFontOfFormattingRun(i);
|
||||
out.writeShort(fontIndex == HSSFRichTextString.NO_FONT ? 0 : fontIndex);
|
||||
out.writeInt(0); // skip reserved
|
||||
}
|
||||
out.writeShort(str.length());
|
||||
out.writeShort(0);
|
||||
out.writeInt(0); // skip reserved
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Horizontal text alignment field value.
|
||||
*
|
||||
* @param value The horizontal alignment, use one of the HORIZONTAL_TEXT_ALIGNMENT_... constants in this class
|
||||
*/
|
||||
public void setHorizontalTextAlignment(int value) {
|
||||
field_1_options = HorizontalTextAlignment.setValue(field_1_options, value);
|
||||
}
|
||||
/**
|
||||
* Sets the Horizontal text alignment field value.
|
||||
*
|
||||
* @param value The horizontal alignment, use one of the HORIZONTAL_TEXT_ALIGNMENT_... constants in this class
|
||||
*/
|
||||
public void setHorizontalTextAlignment(int value) {
|
||||
field_1_options = HorizontalTextAlignment.setValue(field_1_options, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Horizontal text alignment field value.
|
||||
*/
|
||||
public int getHorizontalTextAlignment() {
|
||||
return HorizontalTextAlignment.getValue(field_1_options);
|
||||
}
|
||||
/**
|
||||
* @return the Horizontal text alignment field value.
|
||||
*/
|
||||
public int getHorizontalTextAlignment() {
|
||||
return HorizontalTextAlignment.getValue(field_1_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Vertical text alignment field value.
|
||||
*
|
||||
* @param value The vertical alignment, use one of the VERTIUCAL_TEST_ALIGNMENT_... constants in this class
|
||||
*/
|
||||
public void setVerticalTextAlignment(int value) {
|
||||
field_1_options = VerticalTextAlignment.setValue(field_1_options, value);
|
||||
}
|
||||
/**
|
||||
* Sets the Vertical text alignment field value.
|
||||
*
|
||||
* @param value The vertical alignment, use one of the VERTIUCAL_TEST_ALIGNMENT_... constants in this class
|
||||
*/
|
||||
public void setVerticalTextAlignment(int value) {
|
||||
field_1_options = VerticalTextAlignment.setValue(field_1_options, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Vertical text alignment field value.
|
||||
*/
|
||||
public int getVerticalTextAlignment() {
|
||||
return VerticalTextAlignment.getValue(field_1_options);
|
||||
}
|
||||
/**
|
||||
* @return the Vertical text alignment field value.
|
||||
*/
|
||||
public int getVerticalTextAlignment() {
|
||||
return VerticalTextAlignment.getValue(field_1_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text locked field value.
|
||||
*
|
||||
* @param value If the text should be locked
|
||||
*/
|
||||
public void setTextLocked(boolean value) {
|
||||
field_1_options = textLocked.setBoolean(field_1_options, value);
|
||||
}
|
||||
/**
|
||||
* Sets the text locked field value.
|
||||
*
|
||||
* @param value If the text should be locked
|
||||
*/
|
||||
public void setTextLocked(boolean value) {
|
||||
field_1_options = textLocked.setBoolean(field_1_options, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the text locked field value.
|
||||
*/
|
||||
public boolean isTextLocked() {
|
||||
return textLocked.isSet(field_1_options);
|
||||
}
|
||||
/**
|
||||
* @return the text locked field value.
|
||||
*/
|
||||
public boolean isTextLocked() {
|
||||
return textLocked.isSet(field_1_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text orientation field for the TextObjectBase record.
|
||||
*
|
||||
* @return One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
|
||||
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
|
||||
*/
|
||||
public int getTextOrientation() {
|
||||
return field_2_textOrientation;
|
||||
}
|
||||
/**
|
||||
* Get the text orientation field for the TextObjectBase record.
|
||||
*
|
||||
* @return One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
|
||||
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
|
||||
*/
|
||||
public int getTextOrientation() {
|
||||
return field_2_textOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text orientation field for the TextObjectBase record.
|
||||
*
|
||||
* @param textOrientation
|
||||
* One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
|
||||
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
|
||||
*/
|
||||
public void setTextOrientation(int textOrientation) {
|
||||
this.field_2_textOrientation = textOrientation;
|
||||
}
|
||||
/**
|
||||
* Set the text orientation field for the TextObjectBase record.
|
||||
*
|
||||
* @param textOrientation
|
||||
* One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
|
||||
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
|
||||
*/
|
||||
public void setTextOrientation(int textOrientation) {
|
||||
this.field_2_textOrientation = textOrientation;
|
||||
}
|
||||
|
||||
public HSSFRichTextString getStr() {
|
||||
return _text;
|
||||
}
|
||||
public HSSFRichTextString getStr() {
|
||||
return _text;
|
||||
}
|
||||
|
||||
public void setStr(HSSFRichTextString str) {
|
||||
_text = str;
|
||||
}
|
||||
public void setStr(HSSFRichTextString str) {
|
||||
_text = str;
|
||||
}
|
||||
|
||||
public Ptg getLinkRefPtg() {
|
||||
return _linkRefPtg;
|
||||
}
|
||||
public Ptg getLinkRefPtg() {
|
||||
return _linkRefPtg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextObjectRecord copy() {
|
||||
return new TextObjectRecord(this);
|
||||
}
|
||||
@Override
|
||||
public TextObjectRecord copy() {
|
||||
return new TextObjectRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TEXT_OBJECT;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.TEXT_OBJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
final Map<String,Supplier<?>> m = new LinkedHashMap<>();
|
||||
m.put("isHorizontal", this::getHorizontalTextAlignment);
|
||||
m.put("isVertical", this::getVerticalTextAlignment);
|
||||
m.put("textLocked", this::isTextLocked);
|
||||
m.put("textOrientation", this::getTextOrientation);
|
||||
m.put("string", this::getStr);
|
||||
m.put("reserved4", () -> field_3_reserved4);
|
||||
m.put("reserved5", () -> field_4_reserved5);
|
||||
m.put("reserved6", () -> field_5_reserved6);
|
||||
m.put("reserved7", () -> field_8_reserved7);
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
final Map<String,Supplier<?>> m = new LinkedHashMap<>();
|
||||
m.put("isHorizontal", this::getHorizontalTextAlignment);
|
||||
m.put("isVertical", this::getVerticalTextAlignment);
|
||||
m.put("textLocked", this::isTextLocked);
|
||||
m.put("textOrientation", this::getTextOrientation);
|
||||
m.put("string", this::getStr);
|
||||
m.put("reserved4", () -> field_3_reserved4);
|
||||
m.put("reserved5", () -> field_4_reserved5);
|
||||
m.put("reserved6", () -> field_5_reserved6);
|
||||
m.put("reserved7", () -> field_8_reserved7);
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,51 +28,51 @@ import org.apache.poi.util.LittleEndianOutput;
|
||||
* been recalculated before the document was saved.
|
||||
*/
|
||||
public final class UncalcedRecord extends StandardRecord {
|
||||
public static final short sid = 0x005E;
|
||||
public static final short sid = 0x005E;
|
||||
|
||||
private short _reserved;
|
||||
|
||||
public UncalcedRecord() {
|
||||
public UncalcedRecord() {
|
||||
_reserved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public UncalcedRecord(UncalcedRecord other) {
|
||||
super(other);
|
||||
_reserved = other._reserved;
|
||||
}
|
||||
public UncalcedRecord(UncalcedRecord other) {
|
||||
super(other);
|
||||
_reserved = other._reserved;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public UncalcedRecord(RecordInputStream in) {
|
||||
_reserved = in.readShort(); // unused
|
||||
}
|
||||
public UncalcedRecord(RecordInputStream in) {
|
||||
_reserved = in.readShort(); // unused
|
||||
}
|
||||
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_reserved);
|
||||
}
|
||||
public void serialize(LittleEndianOutput out) {
|
||||
out.writeShort(_reserved);
|
||||
}
|
||||
|
||||
protected int getDataSize() {
|
||||
return 2;
|
||||
}
|
||||
protected int getDataSize() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public static int getStaticRecordSize() {
|
||||
return 6;
|
||||
}
|
||||
public static int getStaticRecordSize() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UncalcedRecord copy() {
|
||||
return new UncalcedRecord(this);
|
||||
}
|
||||
@Override
|
||||
public UncalcedRecord copy() {
|
||||
return new UncalcedRecord(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.UNCALCED;
|
||||
}
|
||||
@Override
|
||||
public HSSFRecordTypes getGenericRecordType() {
|
||||
return HSSFRecordTypes.UNCALCED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("reserved", () -> _reserved);
|
||||
}
|
||||
@Override
|
||||
public Map<String, Supplier<?>> getGenericProperties() {
|
||||
return GenericRecordUtil.getGenericProperties("reserved", () -> _reserved);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user