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:
PJ Fanning 2021-05-22 20:56:44 +00:00
parent d3899d4744
commit 3890b7013b
620 changed files with 37423 additions and 37423 deletions

View File

@ -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");
}
}

View File

@ -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);

View File

@ -24,6 +24,6 @@ public abstract class OldFileFormatException extends UnsupportedFileFormatExcept
private static final long serialVersionUID = 7849681804154571175L;
public OldFileFormatException(String s) {
super(s);
}
super(s);
}
}

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -53,7 +53,7 @@ public class DocumentSummaryInformation extends PropertySet {
@Override
public PropertyIDMap getPropertySetIDMap() {
return PropertyIDMap.getDocumentSummaryInformationProperties();
return PropertyIDMap.getDocumentSummaryInformationProperties();
}

View File

@ -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;

View File

@ -46,7 +46,7 @@ public final class SummaryInformation extends PropertySet {
@Override
public PropertyIDMap getPropertySetIDMap() {
return PropertyIDMap.getSummaryInformationProperties();
return PropertyIDMap.getSummaryInformationProperties();
}
/**

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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] &lt;fileName&gt;<p>
* BiffViewer --rawhex [--out] &lt;fileName&gt;
*
* <table>
/**
* Method main with 1 argument just run straight biffview against given
* file<p>
*
* <b>Usage</b>:<p>
*
* BiffViewer [--biffhex] [--noint] [--noescher] [--out] &lt;fileName&gt;<p>
* BiffViewer --rawhex [--out] &lt;fileName&gt;
*
* <table>
* <caption>BiffViewer options</caption>
* <tr><td>--biffhex</td><td>show hex dump of each BIFF record</td></tr>
* <tr><td>--noint</td><td>do not output interpretation of BIFF records</td></tr>
* <tr><td>--out</td><td>send output to &lt;fileName&gt;.out</td></tr>
* <tr><td>--rawhex</td><td>output raw hex dump of whole workbook stream</td></tr>
* <tr><td>--escher</td><td>turn on deserialization of escher records (default is off)</td></tr>
* <tr><td>--noheader</td><td>do not print record header (default is on)</td></tr>
* </table>
*
* @param args the command line arguments
*
* @throws IOException if the file doesn't exist or contained errors
* @throws CommandParseException if the command line contained errors
*/
public static void main(String[] args) throws IOException, CommandParseException {
// args = new String[] { "--out", "", };
CommandArgs cmdArgs = CommandArgs.parse(args);
* <tr><td>--biffhex</td><td>show hex dump of each BIFF record</td></tr>
* <tr><td>--noint</td><td>do not output interpretation of BIFF records</td></tr>
* <tr><td>--out</td><td>send output to &lt;fileName&gt;.out</td></tr>
* <tr><td>--rawhex</td><td>output raw hex dump of whole workbook stream</td></tr>
* <tr><td>--escher</td><td>turn on deserialization of escher records (default is off)</td></tr>
* <tr><td>--noheader</td><td>do not print record header (default is on)</td></tr>
* </table>
*
* @param args the command line arguments
*
* @throws IOException if the file doesn't exist or contained errors
* @throws CommandParseException if the command line contained errors
*/
public static void main(String[] args) throws IOException, CommandParseException {
// args = new String[] { "--out", "", };
CommandArgs cmdArgs = CommandArgs.parse(args);
try (POIFSFileSystem fs = new POIFSFileSystem(cmdArgs.getFile(), true);
InputStream is = getPOIFSInputStream(fs);
@ -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;
}
}
}

View File

@ -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)");

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -40,7 +40,7 @@ public class DrawingManager2 {
* Clears the cached list of drawing groups
*/
public void clearDrawingGroups() {
drawingGroups.clear();
drawingGroups.clear();
}
/**

View File

@ -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();
}
/**

View File

@ -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);
}
/**

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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
);
}
}

View File

@ -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();
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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() {

View File

@ -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() {

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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;

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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()

View File

@ -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
);
}
}

View File

@ -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() {}

View File

@ -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
);
}
}

View File

@ -60,7 +60,7 @@ public final class EndSubRecord extends SubRecord {
out.writeShort(ENCODED_SIZE);
}
protected int getDataSize() {
protected int getDataSize() {
return ENCODED_SIZE;
}

View File

@ -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]);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)
);
}
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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
);
}
}

View File

@ -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;
}
}

View File

@ -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
);
}
}

View File

@ -73,7 +73,7 @@ public final class FtCblsSubRecord extends SubRecord {
out.write(reserved);
}
protected int getDataSize() {
protected int getDataSize() {
return reserved.length;
}

View File

@ -65,7 +65,7 @@ public final class GroupMarkerSubRecord extends SubRecord {
out.write(reserved);
}
protected int getDataSize() {
protected int getDataSize() {
return reserved.length;
}

View File

@ -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);
}
}

View File

@ -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()
{

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
/**

View File

@ -152,8 +152,8 @@ public final class LabelRecord extends Record implements CellValueRecordInterfac
}
/**
* NO-OP!
*/
* NO-OP!
*/
@Override
public void setColumn(short col)
{

View File

@ -65,7 +65,7 @@ public final class LabelSSTRecord extends CellRecord {
@Override
protected String getRecordName() {
return "LABELSST";
return "LABELSST";
}
@Override

View File

@ -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);
}

View File

@ -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

View File

@ -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
);
}
}

View File

@ -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

View File

@ -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
);
}
}

View File

@ -85,7 +85,7 @@ public final class NoteStructureSubRecord extends SubRecord {
out.write(reserved);
}
@Override
@Override
protected int getDataSize() {
return reserved.length;
}

View File

@ -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;

View File

@ -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

View File

@ -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() {

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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) {

View File

@ -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 )

View File

@ -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];

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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() {}

View File

@ -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
);
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -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)
);
}
}

View File

@ -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
);
}
}

View File

@ -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);
}
}

View File

@ -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