Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646820 via svnmerge from
https://svn.apache.org:443/repos/asf/poi/trunk ........ r646405 | nick | 2008-04-09 16:36:39 +0100 (Wed, 09 Apr 2008) | 1 line Implement a proxy HSSFListener which tracks the format records, and lets you lookup the format string for a given cell. Convert the xls to csv example to use it ........ r646666 | josh | 2008-04-10 08:06:55 +0100 (Thu, 10 Apr 2008) | 1 line bugzilla 44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord. ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@646826 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
958e1ffb71
commit
077fb6c6f9
@ -37,6 +37,7 @@
|
||||
|
||||
<!-- Don't forget to update status.xml too! -->
|
||||
<release version="3.0.3-beta1" date="2008-04-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord.</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">refactored all junits' usage of HSSF.testdata.path to one place</action>
|
||||
|
@ -34,6 +34,7 @@
|
||||
<!-- Don't forget to update changes.xml too! -->
|
||||
<changes>
|
||||
<release version="3.0.3-beta1" date="2008-04-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord.</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">refactored all junits' usage of HSSF.testdata.path to one place</action>
|
||||
|
@ -0,0 +1,117 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
package org.apache.poi.hssf.eventusermodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.ExtendedFormatRecord;
|
||||
import org.apache.poi.hssf.record.FormatRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
|
||||
|
||||
/**
|
||||
* A proxy HSSFListener that keeps track of the document
|
||||
* formatting records, and provides an easy way to look
|
||||
* up the format strings used by cells from their ids.
|
||||
*/
|
||||
public class FormatTrackingHSSFListener implements HSSFListener {
|
||||
private HSSFListener childListener;
|
||||
private Map customFormatRecords = new Hashtable();
|
||||
private List xfRecords = new ArrayList();
|
||||
|
||||
public FormatTrackingHSSFListener(HSSFListener childListener) {
|
||||
this.childListener = childListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process this record ourselves, and then
|
||||
* pass it on to our child listener
|
||||
*/
|
||||
public void processRecord(Record record) {
|
||||
// Handle it ourselves
|
||||
processRecordInternally(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
|
||||
*/
|
||||
public void processRecordInternally(Record record) {
|
||||
if(record instanceof FormatRecord) {
|
||||
FormatRecord fr = (FormatRecord) record;
|
||||
customFormatRecords.put(new Integer(fr.getIndexCode()), fr);
|
||||
}
|
||||
if(record instanceof ExtendedFormatRecord) {
|
||||
ExtendedFormatRecord xr = (ExtendedFormatRecord) record;
|
||||
xfRecords.add(xr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string, eg $##.##, for the
|
||||
* given number format index.
|
||||
*/
|
||||
public String getFormatString(int formatIndex) {
|
||||
String format = null;
|
||||
if(formatIndex >= HSSFDataFormat.getNumberOfBuiltinBuiltinFormats()) {
|
||||
FormatRecord tfr = (FormatRecord)customFormatRecords.get(new Integer(formatIndex));
|
||||
if(tfr == null) {
|
||||
System.err.println("Requested format at index " + formatIndex + ", but it wasn't found");
|
||||
} else {
|
||||
format = tfr.getFormatString();
|
||||
}
|
||||
} else {
|
||||
format = HSSFDataFormat.getBuiltinFormat((short)formatIndex);
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format string, eg $##.##, used
|
||||
* by your cell
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public int getFormatIndex(CellValueRecordInterface cell) {
|
||||
ExtendedFormatRecord xfr = (ExtendedFormatRecord)
|
||||
xfRecords.get(cell.getXFIndex());
|
||||
if(xfr == null) {
|
||||
System.err.println("Cell " + cell.getRow() + "," + cell.getColumn() + " uses XF with index " + cell.getXFIndex() + ", but we don't have that");
|
||||
return -1;
|
||||
}
|
||||
return xfr.getFormatIndex();
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ public final class CRNRecord extends Record {
|
||||
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 String toString() {
|
||||
@ -83,6 +83,7 @@ public final class CRNRecord extends Record {
|
||||
LittleEndian.putByte(data, 4 + offset, field_1_last_column_index);
|
||||
LittleEndian.putByte(data, 5 + offset, field_2_first_column_index);
|
||||
LittleEndian.putShort(data, 6 + offset, (short) field_3_row_index);
|
||||
ConstantValueParser.encode(data, 8 + offset, field_4_constant_values);
|
||||
return getRecordSize();
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,12 @@ import org.apache.poi.util.StringUtil;
|
||||
*/
|
||||
public final class ExternalNameRecord extends Record {
|
||||
|
||||
private static final Ptg[] EMPTY_PTG_ARRAY = { };
|
||||
|
||||
public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223)
|
||||
|
||||
private static final int OPT_BUILTIN_NAME = 0x0001;
|
||||
private static final int OPT_AUTOMATIC_LINK = 0x0002;
|
||||
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;
|
||||
private static final int OPT_OLE_LINK = 0x0010;
|
||||
@ -51,8 +53,8 @@ public final class ExternalNameRecord extends Record {
|
||||
super(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience Function to determine if the name is a built-in name
|
||||
/**
|
||||
* Convenience Function to determine if the name is a built-in name
|
||||
*/
|
||||
public boolean isBuiltInName() {
|
||||
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
|
||||
@ -102,9 +104,12 @@ public final class ExternalNameRecord extends Record {
|
||||
}
|
||||
|
||||
private int getDataSize(){
|
||||
return 3 * 2 // 3 short fields
|
||||
+ 2 + field_4_name.length() // nameLen and name
|
||||
+ 2 + getNameDefinitionSize(); // nameDefLen and nameDef
|
||||
int result = 3 * 2 // 3 short fields
|
||||
+ 2 + field_4_name.length(); // nameLen and name
|
||||
if(hasFormula()) {
|
||||
result += 2 + getNameDefinitionSize(); // nameDefLen and nameDef
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,9 +132,11 @@ public final class ExternalNameRecord extends Record {
|
||||
short nameLen = (short) field_4_name.length();
|
||||
LittleEndian.putShort( data, 10 + offset, nameLen );
|
||||
StringUtil.putCompressedUnicode( field_4_name, data, 12 + offset );
|
||||
short defLen = (short) getNameDefinitionSize();
|
||||
LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
|
||||
Ptg.serializePtgStack(toStack(field_5_name_definition), data, 14 + nameLen + offset );
|
||||
if(hasFormula()) {
|
||||
short defLen = (short) getNameDefinitionSize();
|
||||
LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
|
||||
Ptg.serializePtgStack(toStack(field_5_name_definition), data, 14 + nameLen + offset );
|
||||
}
|
||||
return dataSize + 4;
|
||||
}
|
||||
|
||||
@ -149,13 +156,58 @@ public final class ExternalNameRecord extends Record {
|
||||
|
||||
protected void fillFields(RecordInputStream in) {
|
||||
field_1_option_flag = in.readShort();
|
||||
field_2_index = in.readShort();
|
||||
field_3_not_used = in.readShort();
|
||||
short nameLength = in.readShort();
|
||||
field_4_name = in.readCompressedUnicode(nameLength);
|
||||
short formulaLen = in.readShort();
|
||||
field_2_index = in.readShort();
|
||||
field_3_not_used = in.readShort();
|
||||
short nameLength = in.readShort();
|
||||
field_4_name = in.readCompressedUnicode(nameLength);
|
||||
if(!hasFormula()) {
|
||||
if(in.remaining() > 0) {
|
||||
throw readFail("Some unread data (is formula present?)");
|
||||
}
|
||||
field_5_name_definition = EMPTY_PTG_ARRAY;
|
||||
return;
|
||||
}
|
||||
if(in.remaining() <= 0) {
|
||||
throw readFail("Ran out of record data trying to read formula.");
|
||||
}
|
||||
short formulaLen = in.readShort();
|
||||
field_5_name_definition = toPtgArray(Ptg.createParsedExpressionTokens(formulaLen, in));
|
||||
}
|
||||
/*
|
||||
* Makes better error messages (while hasFormula() is not reliable)
|
||||
* Remove this when hasFormula() is stable.
|
||||
*/
|
||||
private RuntimeException readFail(String msg) {
|
||||
String fullMsg = msg + " fields: (option=" + field_1_option_flag + " index=" + field_2_index
|
||||
+ " not_used=" + field_3_not_used + " name='" + field_4_name + "')";
|
||||
return new RuntimeException(fullMsg);
|
||||
}
|
||||
|
||||
private boolean hasFormula() {
|
||||
// TODO - determine exact conditions when formula is present
|
||||
if (false) {
|
||||
// "Microsoft Office Excel 97-2007 Binary File Format (.xls) Specification"
|
||||
// m$'s document suggests logic like this, but bugzilla 44774 att 21790 seems to disagree
|
||||
if (isStdDocumentNameIdentifier()) {
|
||||
if (isOLELink()) {
|
||||
// seems to be not possible according to m$ document
|
||||
throw new IllegalStateException(
|
||||
"flags (std-doc-name and ole-link) cannot be true at the same time");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (isOLELink()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This was derived by trial and error, but doesn't seem quite right
|
||||
if (isAutomaticLink()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Ptg[] toPtgArray(Stack s) {
|
||||
Ptg[] result = new Ptg[s.size()];
|
||||
|
@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.constant;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.hssf.record.UnicodeString;
|
||||
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* To support Constant Values (2.5.7) as required by the CRN record.
|
||||
@ -30,11 +31,12 @@ import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class ConstantValueParser {
|
||||
// note - value 3 seems to be unused
|
||||
// note - these (non-combinable) enum values are sparse.
|
||||
private static final int TYPE_EMPTY = 0;
|
||||
private static final int TYPE_NUMBER = 1;
|
||||
private static final int TYPE_STRING = 2;
|
||||
private static final int TYPE_BOOLEAN = 4;
|
||||
private static final int TYPE_ERROR_CODE = 16; // TODO - update OOO document to include this value
|
||||
|
||||
private static final int TRUE_ENCODING = 1;
|
||||
private static final int FALSE_ENCODING = 0;
|
||||
@ -47,11 +49,11 @@ public final class ConstantValueParser {
|
||||
}
|
||||
|
||||
public static Object[] parse(RecordInputStream in, int nValues) {
|
||||
Object[] result = new Object[nValues];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
Object[] result = new Object[nValues];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = readAConstantValue(in);
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object readAConstantValue(RecordInputStream in) {
|
||||
@ -66,13 +68,18 @@ public final class ConstantValueParser {
|
||||
return in.readUnicodeString();
|
||||
case TYPE_BOOLEAN:
|
||||
return readBoolean(in);
|
||||
case TYPE_ERROR_CODE:
|
||||
int errCode = in.readUShort();
|
||||
// next 6 bytes are unused
|
||||
in.readUShort();
|
||||
in.readInt();
|
||||
return ErrorConstant.valueOf(errCode);
|
||||
}
|
||||
return null;
|
||||
throw new RuntimeException("Unknown grbit value (" + grbit + ")");
|
||||
}
|
||||
|
||||
private static Object readBoolean(RecordInputStream in) {
|
||||
byte val = in.readByte();
|
||||
in.readLong(); // 8 byte 'not used' field
|
||||
byte val = (byte)in.readLong(); // 7 bytes 'not used'
|
||||
switch(val) {
|
||||
case FALSE_ENCODING:
|
||||
return Boolean.FALSE;
|
||||
@ -89,7 +96,7 @@ public final class ConstantValueParser {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
result += getEncodedSize(values[i]);
|
||||
}
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +107,8 @@ public final class ConstantValueParser {
|
||||
return 8;
|
||||
}
|
||||
Class cls = object.getClass();
|
||||
if(cls == Boolean.class || cls == Double.class) {
|
||||
|
||||
if(cls == Boolean.class || cls == Double.class || cls == ErrorConstant.class) {
|
||||
return 8;
|
||||
}
|
||||
UnicodeString strVal = (UnicodeString)object;
|
||||
@ -108,4 +116,49 @@ public final class ConstantValueParser {
|
||||
strVal.getRecordSize(urs);
|
||||
return urs.recordSize;
|
||||
}
|
||||
|
||||
public static void encode(byte[] data, int offset, Object[] values) {
|
||||
int currentOffset = offset;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
currentOffset += encodeSingleValue(data, currentOffset, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static int encodeSingleValue(byte[] data, int offset, Object value) {
|
||||
if (value == EMPTY_REPRESENTATION) {
|
||||
LittleEndian.putByte(data, offset, TYPE_EMPTY);
|
||||
LittleEndian.putLong(data, offset+1, 0L);
|
||||
return 9;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
Boolean bVal = ((Boolean)value);
|
||||
LittleEndian.putByte(data, offset, TYPE_BOOLEAN);
|
||||
long longVal = bVal.booleanValue() ? 1L : 0L;
|
||||
LittleEndian.putLong(data, offset+1, longVal);
|
||||
return 9;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
Double dVal = (Double) value;
|
||||
LittleEndian.putByte(data, offset, TYPE_NUMBER);
|
||||
LittleEndian.putDouble(data, offset+1, dVal.doubleValue());
|
||||
return 9;
|
||||
}
|
||||
if (value instanceof UnicodeString) {
|
||||
UnicodeString usVal = (UnicodeString) value;
|
||||
LittleEndian.putByte(data, offset, TYPE_STRING);
|
||||
UnicodeRecordStats urs = new UnicodeRecordStats();
|
||||
usVal.serialize(urs, offset +1, data);
|
||||
return 1 + urs.recordSize;
|
||||
}
|
||||
if (value instanceof ErrorConstant) {
|
||||
ErrorConstant ecVal = (ErrorConstant) value;
|
||||
LittleEndian.putByte(data, offset, TYPE_ERROR_CODE);
|
||||
LittleEndian.putUShort(data, offset+1, ecVal.getErrorCode());
|
||||
LittleEndian.putUShort(data, offset+3, 0);
|
||||
LittleEndian.putInt(data, offset+5, 0);
|
||||
return 9;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.record.constant;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
/**
|
||||
* Represents a constant error code value as encoded in a constant values array. <p/>
|
||||
*
|
||||
* This class is a type-safe wrapper for a 16-bit int value performing a similar job to
|
||||
* <tt>ErrorEval</tt>.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class ErrorConstant {
|
||||
// convenient access to name space
|
||||
private static final HSSFErrorConstants EC = null;
|
||||
|
||||
private static final ErrorConstant NULL = new ErrorConstant(EC.ERROR_NULL);
|
||||
private static final ErrorConstant DIV_0 = new ErrorConstant(EC.ERROR_DIV_0);
|
||||
private static final ErrorConstant VALUE = new ErrorConstant(EC.ERROR_VALUE);
|
||||
private static final ErrorConstant REF = new ErrorConstant(EC.ERROR_REF);
|
||||
private static final ErrorConstant NAME = new ErrorConstant(EC.ERROR_NAME);
|
||||
private static final ErrorConstant NUM = new ErrorConstant(EC.ERROR_NUM);
|
||||
private static final ErrorConstant NA = new ErrorConstant(EC.ERROR_NA);
|
||||
|
||||
private final int _errorCode;
|
||||
|
||||
private ErrorConstant(int errorCode) {
|
||||
_errorCode = errorCode;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return _errorCode;
|
||||
}
|
||||
|
||||
public static ErrorConstant valueOf(int errorCode) {
|
||||
switch (errorCode) {
|
||||
case HSSFErrorConstants.ERROR_NULL: return NULL;
|
||||
case HSSFErrorConstants.ERROR_DIV_0: return DIV_0;
|
||||
case HSSFErrorConstants.ERROR_VALUE: return VALUE;
|
||||
case HSSFErrorConstants.ERROR_REF: return REF;
|
||||
case HSSFErrorConstants.ERROR_NAME: return NAME;
|
||||
case HSSFErrorConstants.ERROR_NUM: return NUM;
|
||||
case HSSFErrorConstants.ERROR_NA: return NA;
|
||||
}
|
||||
System.err.println("Warning - unexpected error code (" + errorCode + ")");
|
||||
return new ErrorConstant(errorCode);
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
|
||||
@ -70,8 +71,7 @@ public class XLS2CSVmra implements HSSFListener {
|
||||
|
||||
// Records we pick up as we process
|
||||
private SSTRecord sstRecord;
|
||||
private Map customFormatRecords = new Hashtable();
|
||||
private List xfRecords = new ArrayList();
|
||||
private FormatTrackingHSSFListener formatListener;
|
||||
|
||||
/**
|
||||
* Creates a new XLS -> CSV converter
|
||||
@ -104,9 +104,11 @@ public class XLS2CSVmra implements HSSFListener {
|
||||
*/
|
||||
public void process() throws IOException {
|
||||
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
|
||||
formatListener = new FormatTrackingHSSFListener(listener);
|
||||
|
||||
HSSFEventFactory factory = new HSSFEventFactory();
|
||||
HSSFRequest request = new HSSFRequest();
|
||||
request.addListenerForAllRecords(listener);
|
||||
request.addListenerForAllRecords(formatListener);
|
||||
|
||||
factory.processWorkbookEvents(request, fs);
|
||||
}
|
||||
@ -125,14 +127,6 @@ public class XLS2CSVmra implements HSSFListener {
|
||||
case SSTRecord.sid:
|
||||
sstRecord = (SSTRecord) record;
|
||||
break;
|
||||
case FormatRecord.sid:
|
||||
FormatRecord fr = (FormatRecord) record;
|
||||
customFormatRecords.put(new Integer(fr.getIndexCode()), fr);
|
||||
break;
|
||||
case ExtendedFormatRecord.sid:
|
||||
ExtendedFormatRecord xr = (ExtendedFormatRecord) record;
|
||||
xfRecords.add(xr);
|
||||
break;
|
||||
|
||||
case BlankRecord.sid:
|
||||
BlankRecord brec = (BlankRecord) record;
|
||||
@ -259,41 +253,32 @@ public class XLS2CSVmra implements HSSFListener {
|
||||
*/
|
||||
private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
|
||||
// Get the built in format, if there is one
|
||||
ExtendedFormatRecord xfr = (ExtendedFormatRecord)
|
||||
xfRecords.get(cell.getXFIndex());
|
||||
if(xfr == null) {
|
||||
System.err.println("Cell " + cell.getRow() + "," + cell.getColumn() + " uses XF with index " + cell.getXFIndex() + ", but we don't have that");
|
||||
int formatIndex = formatListener.getFormatIndex(cell);
|
||||
String formatString = formatListener.getFormatString(cell);
|
||||
|
||||
if(formatString == null) {
|
||||
return Double.toString(value);
|
||||
} else {
|
||||
int formatIndex = xfr.getFormatIndex();
|
||||
String format;
|
||||
if(formatIndex >= HSSFDataFormat.getNumberOfBuiltinBuiltinFormats()) {
|
||||
FormatRecord tfr = (FormatRecord)customFormatRecords.get(new Integer(formatIndex));
|
||||
format = tfr.getFormatString();
|
||||
} else {
|
||||
format = HSSFDataFormat.getBuiltinFormat(xfr.getFormatIndex());
|
||||
}
|
||||
|
||||
// Is it a date?
|
||||
if(HSSFDateUtil.isADateFormat(formatIndex,format) &&
|
||||
if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
|
||||
HSSFDateUtil.isValidExcelDate(value)) {
|
||||
// Java wants M not m for month
|
||||
format = format.replace('m','M');
|
||||
formatString = formatString.replace('m','M');
|
||||
// Change \- into -, if it's there
|
||||
format = format.replaceAll("\\\\-","-");
|
||||
formatString = formatString.replaceAll("\\\\-","-");
|
||||
|
||||
// Format as a date
|
||||
Date d = HSSFDateUtil.getJavaDate(value, false);
|
||||
DateFormat df = new SimpleDateFormat(format);
|
||||
DateFormat df = new SimpleDateFormat(formatString);
|
||||
return df.format(d);
|
||||
} else {
|
||||
if(format == "General") {
|
||||
if(formatString == "General") {
|
||||
// Some sort of wierd default
|
||||
return Double.toString(value);
|
||||
}
|
||||
|
||||
// Format as a number
|
||||
DecimalFormat df = new DecimalFormat(format);
|
||||
DecimalFormat df = new DecimalFormat(formatString);
|
||||
return df.format(value);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.eventusermodel;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
/**
|
||||
* Tests for FormatTrackingHSSFListener
|
||||
*/
|
||||
public final class TestFormatTrackingHSSFListener extends TestCase {
|
||||
private FormatTrackingHSSFListener listener;
|
||||
|
||||
public void setUp() {
|
||||
HSSFRequest req = new HSSFRequest();
|
||||
MockHSSFListener mockListen = new MockHSSFListener();
|
||||
listener = new FormatTrackingHSSFListener(mockListen);
|
||||
req.addListenerForAllRecords(listener);
|
||||
|
||||
HSSFEventFactory factory = new HSSFEventFactory();
|
||||
try {
|
||||
InputStream is = HSSFTestDataSamples.openSampleFileStream("MissingBits.xls");
|
||||
POIFSFileSystem fs = new POIFSFileSystem(is);
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void testFormats() throws Exception {
|
||||
assertEquals("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)", listener.getFormatString(41));
|
||||
assertEquals("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)", listener.getFormatString(42));
|
||||
assertEquals("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)", listener.getFormatString(43));
|
||||
}
|
||||
|
||||
private static final class MockHSSFListener implements HSSFListener {
|
||||
public MockHSSFListener() {}
|
||||
private final List _records = new ArrayList();
|
||||
|
||||
public void processRecord(Record record) {
|
||||
_records.add(record);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import junit.framework.TestSuite;
|
||||
|
||||
import org.apache.poi.hssf.record.aggregates.AllRecordAggregateTests;
|
||||
import org.apache.poi.hssf.record.cf.TestCellRange;
|
||||
import org.apache.poi.hssf.record.constant.TestConstantValueParser;
|
||||
import org.apache.poi.hssf.record.formula.AllFormulaTests;
|
||||
|
||||
/**
|
||||
@ -105,6 +106,7 @@ public final class AllRecordTests {
|
||||
result.addTestSuite(TestUnitsRecord.class);
|
||||
result.addTestSuite(TestValueRangeRecord.class);
|
||||
result.addTestSuite(TestCellRange.class);
|
||||
result.addTestSuite(TestConstantValueParser.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -28,13 +28,25 @@ public final class TestExternalNameRecord extends TestCase {
|
||||
private static final byte[] dataFDS = {
|
||||
0, 0, 0, 0, 0, 0, 3, 0, 70, 68, 83, 0, 0,
|
||||
};
|
||||
private static ExternalNameRecord createSimpleENR() {
|
||||
return new ExternalNameRecord(new TestcaseRecordInputStream((short)0x0023, dataFDS));
|
||||
|
||||
// data taken from bugzilla 44774 att 21790
|
||||
private static final byte[] dataAutoDocName = {
|
||||
-22, 127, 0, 0, 0, 0, 29, 0, 39, 49, 57, 49, 50, 49, 57, 65, 87, 52, 32, 67, 111, 114,
|
||||
112, 44, 91, 87, 79, 82, 75, 79, 85, 84, 95, 80, 88, 93, 39,
|
||||
};
|
||||
|
||||
// data taken from bugzilla 44774 att 21790
|
||||
private static final byte[] dataPlainName = {
|
||||
0, 0, 0, 0, 0, 0, 9, 0, 82, 97, 116, 101, 95, 68, 97, 116, 101, 9, 0, 58, 0, 0, 0, 0, 4, 0, 8, 0
|
||||
};
|
||||
|
||||
private static ExternalNameRecord createSimpleENR(byte[] data) {
|
||||
return new ExternalNameRecord(new TestcaseRecordInputStream((short)0x0023, data));
|
||||
}
|
||||
public void testBasicDeserializeReserialize() {
|
||||
|
||||
ExternalNameRecord enr = createSimpleENR();
|
||||
assertEquals( "FDS", enr.getText());
|
||||
ExternalNameRecord enr = createSimpleENR(dataFDS);
|
||||
assertEquals("FDS", enr.getText());
|
||||
|
||||
try {
|
||||
TestcaseRecordInputStream.confirmRecordEncoding(0x0023, dataFDS, enr.serialize());
|
||||
@ -46,10 +58,50 @@ public final class TestExternalNameRecord extends TestCase {
|
||||
}
|
||||
|
||||
public void testBasicSize() {
|
||||
ExternalNameRecord enr = createSimpleENR();
|
||||
ExternalNameRecord enr = createSimpleENR(dataFDS);
|
||||
if(enr.getRecordSize() == 13) {
|
||||
throw new AssertionFailedError("Identified bug 44695");
|
||||
}
|
||||
assertEquals(17, enr.getRecordSize());
|
||||
}
|
||||
|
||||
public void testAutoStdDocName() {
|
||||
|
||||
ExternalNameRecord enr;
|
||||
try {
|
||||
enr = createSimpleENR(dataAutoDocName);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
if(e.getMessage() == null) {
|
||||
throw new AssertionFailedError("Identified bug XXXX");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
assertEquals("'191219AW4 Corp,[WORKOUT_PX]'", enr.getText());
|
||||
assertTrue(enr.isAutomaticLink());
|
||||
assertFalse(enr.isBuiltInName());
|
||||
assertFalse(enr.isIconifiedPictureLink());
|
||||
assertFalse(enr.isInValueSection());
|
||||
assertFalse(enr.isOLELink());
|
||||
assertFalse(enr.isPicureLink());
|
||||
assertTrue(enr.isStdDocumentNameIdentifier());
|
||||
assertFalse(enr.isValue());
|
||||
|
||||
TestcaseRecordInputStream.confirmRecordEncoding(0x0023, dataAutoDocName, enr.serialize());
|
||||
}
|
||||
|
||||
public void testPlainName() {
|
||||
|
||||
ExternalNameRecord enr = createSimpleENR(dataPlainName);
|
||||
assertEquals("Rate_Date", enr.getText());
|
||||
assertFalse(enr.isAutomaticLink());
|
||||
assertFalse(enr.isBuiltInName());
|
||||
assertFalse(enr.isIconifiedPictureLink());
|
||||
assertFalse(enr.isInValueSection());
|
||||
assertFalse(enr.isOLELink());
|
||||
assertFalse(enr.isPicureLink());
|
||||
assertFalse(enr.isStdDocumentNameIdentifier());
|
||||
assertFalse(enr.isValue());
|
||||
|
||||
TestcaseRecordInputStream.confirmRecordEncoding(0x0023, dataPlainName, enr.serialize());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.record.constant;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
|
||||
import org.apache.poi.hssf.record.UnicodeString;
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestConstantValueParser extends TestCase {
|
||||
private static final Object[] SAMPLE_VALUES = {
|
||||
Boolean.TRUE,
|
||||
null,
|
||||
new Double(1.1),
|
||||
new UnicodeString("Sample text"),
|
||||
ErrorConstant.valueOf(HSSFErrorConstants.ERROR_DIV_0),
|
||||
};
|
||||
private static final byte[] SAMPLE_ENCODING = {
|
||||
4, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, -102, -103, -103, -103, -103, -103, -15, 63,
|
||||
2, 11, 0, 0, 83, 97, 109, 112, 108, 101, 32, 116, 101, 120, 116,
|
||||
16, 7, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
public void testGetEncodedSize() {
|
||||
int actual = ConstantValueParser.getEncodedSize(SAMPLE_VALUES);
|
||||
assertEquals(51, actual);
|
||||
}
|
||||
public void testEncode() {
|
||||
int size = ConstantValueParser.getEncodedSize(SAMPLE_VALUES);
|
||||
byte[] data = new byte[size];
|
||||
ConstantValueParser.encode(data, 0, SAMPLE_VALUES);
|
||||
|
||||
if (!Arrays.equals(data, SAMPLE_ENCODING)) {
|
||||
fail("Encoding differs");
|
||||
}
|
||||
}
|
||||
public void testDecode() {
|
||||
RecordInputStream in = new TestcaseRecordInputStream(0x0001, SAMPLE_ENCODING);
|
||||
|
||||
Object[] values = ConstantValueParser.parse(in, 4);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if(!isEqual(SAMPLE_VALUES[i], values[i])) {
|
||||
fail("Decoded result differs");
|
||||
}
|
||||
}
|
||||
}
|
||||
private static boolean isEqual(Object a, Object b) {
|
||||
if (a == null) {
|
||||
return b == null;
|
||||
}
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user