Merged revisions 615190-618235 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r615190 | nick | 2008-01-25 12:52:39 +0100 (Fri, 25 Jan 2008) | 1 line Correctly handle the last paragraph via a fix to TableCell - patch from bug #44292 ........ r615255 | nick | 2008-01-25 17:15:49 +0100 (Fri, 25 Jan 2008) | 1 line Don't swap AreaPtg references from relative to absolute, by correctly processing the fields. Patch from bug #44293 ........ r615259 | nick | 2008-01-25 17:33:59 +0100 (Fri, 25 Jan 2008) | 1 line Add a test to show the bug #42618 appears to be incorrect ........ r615310 | yegor | 2008-01-25 20:27:56 +0100 (Fri, 25 Jan 2008) | 1 line commented failing test42618() ........ r615315 | yegor | 2008-01-25 20:37:22 +0100 (Fri, 25 Jan 2008) | 1 line fix bug #44296: HSLF Not Extracting Slide Background Image ........ r615610 | yegor | 2008-01-27 15:55:32 +0100 (Sun, 27 Jan 2008) | 1 line fix bug #44297: IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation. ........ r615769 | yegor | 2008-01-28 09:53:19 +0100 (Mon, 28 Jan 2008) | 1 line start a new POI 3.1 section in the change log ........ r615859 | nick | 2008-01-28 13:18:12 +0100 (Mon, 28 Jan 2008) | 1 line Mostly fix bug 42618 (really this time...) - can now open the file properly, but getCellFormula() is still playing up (bug #44306 opened for this) ........ r617156 | nick | 2008-01-31 17:41:53 +0100 (Thu, 31 Jan 2008) | 1 line Lots of documentation updates, to make it clearer how the code actually works ........ r617167 | nick | 2008-01-31 18:30:16 +0100 (Thu, 31 Jan 2008) | 1 line Convert HSSFEventFactory to using the new HSSFRecordStream, which returns fully-formed HSSFRecords. HSSFRecordStream allows for pull-style eventusermodel processing ........ r617483 | nick | 2008-02-01 13:13:08 +0100 (Fri, 01 Feb 2008) | 1 line Tweak the javadoc so it's clearer on the overview what the getFormat method does ........ r617487 | nick | 2008-02-01 13:29:38 +0100 (Fri, 01 Feb 2008) | 1 line Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions - triggered by bug #44326 ........ r617491 | nick | 2008-02-01 14:02:06 +0100 (Fri, 01 Feb 2008) | 1 line Patch from bug #44336 - correctly escape sheet names in formula references, including tests for this, and fixes to old tests that were expecting the un-escaped sheet names ........ r617516 | nick | 2008-02-01 16:20:55 +0100 (Fri, 01 Feb 2008) | 1 line Make a start on the hyperlink record support - not finished yet though, so not enabled ........ r617523 | nick | 2008-02-01 16:41:32 +0100 (Fri, 01 Feb 2008) | 1 line Get the Hyperlink record code so that it doesn't break any existing tests, and add in (no usermodel support yet though) ........ r617555 | nick | 2008-02-01 17:52:58 +0100 (Fri, 01 Feb 2008) | 1 line More Hyperlink support. Doesn't end up in HSSFCell just yet, as the records are in the wrong bit of the file, so don't get associated with the sheet. All tests still passing though ........ r617834 | yegor | 2008-02-02 18:06:14 +0100 (Sat, 02 Feb 2008) | 1 line usermodel support for excel hyperlinks ........ r618230 | nick | 2008-02-04 11:48:29 +0100 (Mon, 04 Feb 2008) | 1 line Implement CountA, CountIf, Index, Rows and Columns functions. Patch from Josh Micich in bug #44345 ........ r618235 | nick | 2008-02-04 12:14:49 +0100 (Mon, 04 Feb 2008) | 1 line Test file with hyperlinks on many sheets, of different types ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@618325 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
df2b666ebd
commit
3cae9cc891
@ -167,7 +167,7 @@ under the License.
|
||||
<property name="mavendist.poi.dir" location="build/maven-dist/poi"/>
|
||||
<property name="mavendist.oap.dir" location="build/maven-dist/org.apache.poi"/>
|
||||
<property name="jar.name" value="poi"/>
|
||||
<property name="version.id" value="3.0.2-beta1"/>
|
||||
<property name="version.id" value="3.1-alpha1"/>
|
||||
<property name="halt.on.test.failure" value="true"/>
|
||||
|
||||
<property name="jdk.version.source" value="1.5"
|
||||
@ -279,6 +279,7 @@ under the License.
|
||||
<antcall target="with.clover"/>
|
||||
|
||||
<mkdir dir="build"/>
|
||||
<mkdir dir="build/non-ant-classes"/>
|
||||
<mkdir dir="${main.output.dir}"/>
|
||||
<mkdir dir="${main14.output.dir}"/>
|
||||
<mkdir dir="${scratchpad.output.dir}"/>
|
||||
|
@ -35,7 +35,17 @@
|
||||
</devs>
|
||||
|
||||
<!-- Don't forget to update status.xml too! -->
|
||||
<release version="3.0.2-FINAL" date="2008-??-??">
|
||||
<release version="3.1-beta1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Support for a completed Record based "pull" stream, via org.apache.poi.hssf.eventusermodel.HSSFRecordStream, to complement the existing "push" Event User Model listener stuff</action>
|
||||
</release>
|
||||
<release version="3.0.2-FINAL" date="2008-02-04">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44297 - IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44296 - Fix for reading slide background images</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44293 - Avoid swapping AreaPtgs from relative to absolute</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44292 - Correctly process the last paragraph in a word file</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44254 - Avoid some unread byte warnings, and properly understand DVALRecord</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">41726 - Fix how we handle signed cell offsets in relative areas and references</action>
|
||||
|
@ -32,7 +32,17 @@
|
||||
|
||||
<!-- Don't forget to update changes.xml too! -->
|
||||
<changes>
|
||||
<release version="3.0.2-FINAL" date="2008-??-??">
|
||||
<release version="3.1-beta1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44345 - Implement CountA, CountIf, Index, Rows and Columns functions</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44336 - Properly escape sheet names as required when figuring out the text of formulas</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">44326 - Improvements to how SystemOutLogger and CommonsLogger log messages with exceptions, and avoid an infinite loop with certain log messages with exceptions</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Support for a completed Record based "pull" stream, via org.apache.poi.hssf.eventusermodel.HSSFRecordStream, to complement the existing "push" Event User Model listener stuff</action>
|
||||
</release>
|
||||
<release version="3.0.2-FINAL" date="2008-02-04">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44297 - IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44296 - Fix for reading slide background images</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44293 - Avoid swapping AreaPtgs from relative to absolute</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44292 - Correctly process the last paragraph in a word file</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44254 - Avoid some unread byte warnings, and properly understand DVALRecord</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">41726 - Fix how we handle signed cell offsets in relative areas and references</action>
|
||||
|
@ -515,6 +515,9 @@ public class BiffViewer {
|
||||
case FileSharingRecord.sid:
|
||||
retval = new FileSharingRecord( in );
|
||||
break;
|
||||
case HyperlinkRecord.sid:
|
||||
retval = new HyperlinkRecord( in );
|
||||
break;
|
||||
default:
|
||||
retval = new UnknownRecord( in );
|
||||
}
|
||||
|
@ -129,109 +129,25 @@ public class HSSFEventFactory
|
||||
protected short genericProcessEvents(HSSFRequest req, RecordInputStream in)
|
||||
throws IOException, HSSFUserException
|
||||
{
|
||||
boolean going = true;
|
||||
short userCode = 0;
|
||||
|
||||
short sid = 0;
|
||||
process:
|
||||
{
|
||||
|
||||
Record rec = null;
|
||||
Record lastRec = null;
|
||||
DrawingRecord lastDrawingRecord = new DrawingRecord();
|
||||
|
||||
while (in.hasNextRecord())
|
||||
{
|
||||
in.nextRecord();
|
||||
sid = in.getSid();;
|
||||
|
||||
//
|
||||
// for some reasons we have to make the workbook to be at least 4096 bytes
|
||||
// but if we have such workbook we fill the end of it with zeros (many zeros)
|
||||
//
|
||||
// it is not good:
|
||||
// if the length( all zero records ) % 4 = 1
|
||||
// e.g.: any zero record would be readed as 4 bytes at once ( 2 - id and 2 - size ).
|
||||
// And the last 1 byte will be readed WRONG ( the id must be 2 bytes )
|
||||
//
|
||||
// So we should better to check if the sid is zero and not to read more data
|
||||
// The zero sid shows us that rest of the stream data is a fake to make workbook
|
||||
// certain size
|
||||
//
|
||||
if ( sid == 0 )
|
||||
break;
|
||||
|
||||
|
||||
if ((rec != null) && (sid != ContinueRecord.sid))
|
||||
{
|
||||
userCode = req.processRecord(rec);
|
||||
if (userCode != 0) break process;
|
||||
}
|
||||
if (sid != ContinueRecord.sid)
|
||||
{
|
||||
//System.out.println("creating "+sid);
|
||||
Record[] recs = RecordFactory.createRecord(in);
|
||||
|
||||
if (recs.length > 1)
|
||||
{ // we know that the multiple
|
||||
for (int k = 0; k < (recs.length - 1); k++)
|
||||
{ // record situations do not
|
||||
userCode = req.processRecord(
|
||||
recs[ k ]); // contain continue records
|
||||
if (userCode != 0) break process;
|
||||
}
|
||||
}
|
||||
rec = recs[ recs.length - 1 ]; // regardless we'll process
|
||||
|
||||
// the last record as though
|
||||
// it might be continued
|
||||
// if there is only one
|
||||
// records, it will go here too.
|
||||
}
|
||||
else {
|
||||
// Normally, ContinueRecords are handled internally
|
||||
// However, in a few cases, there is a gap between a record at
|
||||
// its Continue, so we have to handle them specially
|
||||
// This logic is much like in RecordFactory.createRecords()
|
||||
Record[] recs = RecordFactory.createRecord(in);
|
||||
ContinueRecord crec = (ContinueRecord)recs[0];
|
||||
if((lastRec instanceof ObjRecord) || (lastRec instanceof TextObjectRecord)) {
|
||||
// You can have Obj records between a DrawingRecord
|
||||
// and its continue!
|
||||
lastDrawingRecord.processContinueRecord( crec.getData() );
|
||||
// Trigger them on the drawing record, now it's complete
|
||||
rec = lastDrawingRecord;
|
||||
}
|
||||
else if((lastRec instanceof DrawingGroupRecord)) {
|
||||
((DrawingGroupRecord)lastRec).processContinueRecord(crec.getData());
|
||||
// Trigger them on the drawing record, now it's complete
|
||||
rec = lastRec;
|
||||
}
|
||||
else {
|
||||
if (rec instanceof UnknownRecord) {
|
||||
;//silently skip records we don't know about
|
||||
} else {
|
||||
throw new RecordFormatException("Records should handle ContinueRecord internally. Should not see this exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update our tracking of the last record
|
||||
lastRec = rec;
|
||||
if(rec instanceof DrawingRecord) {
|
||||
lastDrawingRecord = (DrawingRecord)rec;
|
||||
}
|
||||
}
|
||||
if (rec != null)
|
||||
{
|
||||
userCode = req.processRecord(rec);
|
||||
if (userCode != 0) break process;
|
||||
Record r = null;
|
||||
|
||||
// Create a new RecordStream and use that
|
||||
HSSFRecordStream recordStream = new HSSFRecordStream(in);
|
||||
|
||||
// Process each record as they come in
|
||||
while(going) {
|
||||
r = recordStream.nextRecord();
|
||||
if(r != null) {
|
||||
userCode = req.processRecord(r);
|
||||
if (userCode != 0) break;
|
||||
} else {
|
||||
going = false;
|
||||
}
|
||||
}
|
||||
|
||||
// All done, return our last code
|
||||
return userCode;
|
||||
|
||||
// Record[] retval = new Record[ records.size() ];
|
||||
// retval = ( Record [] ) records.toArray(retval);
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,234 @@
|
||||
/* ====================================================================
|
||||
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.Vector;
|
||||
|
||||
import org.apache.poi.hssf.record.ContinueRecord;
|
||||
import org.apache.poi.hssf.record.DrawingGroupRecord;
|
||||
import org.apache.poi.hssf.record.DrawingRecord;
|
||||
import org.apache.poi.hssf.record.ObjRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RecordFactory;
|
||||
import org.apache.poi.hssf.record.RecordFormatException;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.hssf.record.TextObjectRecord;
|
||||
import org.apache.poi.hssf.record.UnknownRecord;
|
||||
|
||||
/**
|
||||
* A stream based way to get at complete records, with
|
||||
* as low a memory footprint as possible.
|
||||
* This handles reading from a RecordInputStream, turning
|
||||
* the data into full records, processing continue records
|
||||
* etc.
|
||||
* Most users should use {@link HSSFEventFactory} /
|
||||
* {@link HSSFListener} and have new records pushed to
|
||||
* them, but this does allow for a "pull" style of coding.
|
||||
*/
|
||||
public class HSSFRecordStream {
|
||||
private RecordInputStream in;
|
||||
|
||||
/** Have we run out of records on the stream? */
|
||||
private boolean hitEOS = false;
|
||||
/** Have we returned all the records there are? */
|
||||
private boolean complete = false;
|
||||
|
||||
/**
|
||||
* Sometimes we end up with a bunch of
|
||||
* records. When we do, these should
|
||||
* be returned before the next normal
|
||||
* record processing occurs (i.e. before
|
||||
* we check for continue records and
|
||||
* return rec)
|
||||
*/
|
||||
private Vector bonusRecords = null;
|
||||
|
||||
/**
|
||||
* The next record to return, which may need to have its
|
||||
* continue records passed to it before we do
|
||||
*/
|
||||
private Record rec = null;
|
||||
/**
|
||||
* The most recent record that we gave to the user
|
||||
*/
|
||||
private Record lastRec = null;
|
||||
/**
|
||||
* The most recent DrawingRecord seen
|
||||
*/
|
||||
private DrawingRecord lastDrawingRecord = new DrawingRecord();
|
||||
|
||||
public HSSFRecordStream(RecordInputStream inp) {
|
||||
this.in = inp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next (complete) record from the
|
||||
* stream, or null if there are no more.
|
||||
*/
|
||||
public Record nextRecord() {
|
||||
Record r = null;
|
||||
|
||||
// Loop until we get something
|
||||
while(r == null && !complete) {
|
||||
// Are there any bonus records that we need to
|
||||
// return?
|
||||
r = getBonusRecord();
|
||||
|
||||
// If not, ask for the next real record
|
||||
if(r == null) {
|
||||
r = getNextRecord();
|
||||
}
|
||||
}
|
||||
|
||||
// All done
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any "bonus" records, that should
|
||||
* be returned before processing new ones,
|
||||
* grabs the next and returns it.
|
||||
* If not, returns null;
|
||||
*/
|
||||
private Record getBonusRecord() {
|
||||
if(bonusRecords != null) {
|
||||
Record r = (Record)bonusRecords.remove(0);
|
||||
if(bonusRecords.size() == 0) {
|
||||
bonusRecords = null;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available record, or null if
|
||||
* this pass didn't return a record that's
|
||||
* suitable for returning (eg was a continue record).
|
||||
*/
|
||||
private Record getNextRecord() {
|
||||
Record toReturn = null;
|
||||
|
||||
if(in.hasNextRecord()) {
|
||||
// Grab our next record
|
||||
in.nextRecord();
|
||||
short sid = in.getSid();
|
||||
|
||||
//
|
||||
// for some reasons we have to make the workbook to be at least 4096 bytes
|
||||
// but if we have such workbook we fill the end of it with zeros (many zeros)
|
||||
//
|
||||
// it is not good:
|
||||
// if the length( all zero records ) % 4 = 1
|
||||
// e.g.: any zero record would be readed as 4 bytes at once ( 2 - id and 2 - size ).
|
||||
// And the last 1 byte will be readed WRONG ( the id must be 2 bytes )
|
||||
//
|
||||
// So we should better to check if the sid is zero and not to read more data
|
||||
// The zero sid shows us that rest of the stream data is a fake to make workbook
|
||||
// certain size
|
||||
//
|
||||
if ( sid == 0 )
|
||||
return null;
|
||||
|
||||
|
||||
// If we had a last record, and this one
|
||||
// isn't a continue record, then pass
|
||||
// it on to the listener
|
||||
if ((rec != null) && (sid != ContinueRecord.sid))
|
||||
{
|
||||
// This last record ought to be returned
|
||||
toReturn = rec;
|
||||
}
|
||||
|
||||
// If this record isn't a continue record,
|
||||
// then build it up
|
||||
if (sid != ContinueRecord.sid)
|
||||
{
|
||||
//System.out.println("creating "+sid);
|
||||
Record[] recs = RecordFactory.createRecord(in);
|
||||
|
||||
// We know that the multiple record situations
|
||||
// don't contain continue records, so just
|
||||
// pass those on to the listener now
|
||||
if (recs.length > 1) {
|
||||
bonusRecords = new Vector(recs.length-1);
|
||||
for (int k = 0; k < (recs.length - 1); k++) {
|
||||
bonusRecords.add(recs[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of the number we created, always hold
|
||||
// onto the last record to be processed on the next
|
||||
// loop, in case it has any continue records
|
||||
rec = recs[ recs.length - 1 ];
|
||||
// Don't return it just yet though, as we probably have
|
||||
// a record from the last round to return
|
||||
}
|
||||
else {
|
||||
// Normally, ContinueRecords are handled internally
|
||||
// However, in a few cases, there is a gap between a record at
|
||||
// its Continue, so we have to handle them specially
|
||||
// This logic is much like in RecordFactory.createRecords()
|
||||
Record[] recs = RecordFactory.createRecord(in);
|
||||
ContinueRecord crec = (ContinueRecord)recs[0];
|
||||
if((lastRec instanceof ObjRecord) || (lastRec instanceof TextObjectRecord)) {
|
||||
// You can have Obj records between a DrawingRecord
|
||||
// and its continue!
|
||||
lastDrawingRecord.processContinueRecord( crec.getData() );
|
||||
// Trigger them on the drawing record, now it's complete
|
||||
rec = lastDrawingRecord;
|
||||
}
|
||||
else if((lastRec instanceof DrawingGroupRecord)) {
|
||||
((DrawingGroupRecord)lastRec).processContinueRecord(crec.getData());
|
||||
// Trigger them on the drawing record, now it's complete
|
||||
rec = lastRec;
|
||||
}
|
||||
else {
|
||||
if (rec instanceof UnknownRecord) {
|
||||
;//silently skip records we don't know about
|
||||
} else {
|
||||
throw new RecordFormatException("Records should handle ContinueRecord internally. Should not see this exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update our tracking of the last record
|
||||
lastRec = rec;
|
||||
if(rec instanceof DrawingRecord) {
|
||||
lastDrawingRecord = (DrawingRecord)rec;
|
||||
}
|
||||
} else {
|
||||
// No more records
|
||||
hitEOS = true;
|
||||
}
|
||||
|
||||
// If we've hit the end-of-stream, then
|
||||
// finish off the last record and be done
|
||||
if(hitEOS) {
|
||||
complete = true;
|
||||
|
||||
// Return the last record if there was
|
||||
// one, otherwise null
|
||||
if(rec != null) {
|
||||
toReturn = rec;
|
||||
rec = null;
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
@ -93,6 +93,8 @@ public class Workbook implements Model
|
||||
protected ArrayList formats = new ArrayList();
|
||||
|
||||
protected ArrayList names = new ArrayList();
|
||||
|
||||
protected ArrayList hyperlinks = new ArrayList();
|
||||
|
||||
protected int numxfs = 0; // hold the number of extended format records
|
||||
protected int numfonts = 0; // hold the number of font records
|
||||
@ -133,7 +135,8 @@ public class Workbook implements Model
|
||||
Workbook retval = new Workbook();
|
||||
ArrayList records = new ArrayList(recs.size() / 3);
|
||||
|
||||
for (int k = 0; k < recs.size(); k++) {
|
||||
int k;
|
||||
for (k = 0; k < recs.size(); k++) {
|
||||
Record rec = ( Record ) recs.get(k);
|
||||
|
||||
if (rec.getSid() == EOFRecord.sid) {
|
||||
@ -248,6 +251,17 @@ public class Workbook implements Model
|
||||
// retval.records.supbookpos = retval.records.bspos + 1;
|
||||
// retval.records.namepos = retval.records.supbookpos + 1;
|
||||
// }
|
||||
|
||||
// Look for other interesting values that
|
||||
// follow the EOFRecord
|
||||
for ( ; k < recs.size(); k++) {
|
||||
Record rec = ( Record ) recs.get(k);
|
||||
switch (rec.getSid()) {
|
||||
case HyperlinkRecord.sid:
|
||||
retval.hyperlinks.add(rec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
retval.records.setRecords(records);
|
||||
|
||||
@ -2116,6 +2130,11 @@ public class Workbook implements Model
|
||||
return null;
|
||||
}
|
||||
|
||||
public List getHyperlinks()
|
||||
{
|
||||
return hyperlinks;
|
||||
}
|
||||
|
||||
public List getRecords()
|
||||
{
|
||||
return records.getRecords();
|
||||
|
370
src/java/org/apache/poi/hssf/record/HyperlinkRecord.java
Normal file
370
src/java/org/apache/poi/hssf/record/HyperlinkRecord.java
Normal file
@ -0,0 +1,370 @@
|
||||
/* ====================================================================
|
||||
Copyright 2002-2004 Apache Software Foundation
|
||||
|
||||
Licensed 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The <code>HyperlinkRecord</code> wraps an HLINK-record
|
||||
* from the Excel-97 format.
|
||||
* Supports only external links for now (eg http://)
|
||||
*
|
||||
* @author Mark Hissink Muller <a href="mailto:mark@hissinkmuller.nl >mark&064;hissinkmuller.nl</a>
|
||||
*/
|
||||
public class HyperlinkRecord extends Record implements CellValueRecordInterface
|
||||
{
|
||||
/** Indicates the URL in the Record */
|
||||
private static byte[] GUID_OF_URL_MONIKER =
|
||||
{ -32, -55, -22, 121, -7, -70, -50, 17, -116, -126, 0, -86, 0, 75, -87, 11 };
|
||||
|
||||
/** Indicates the STD_LINK in the Record */
|
||||
// MHM: to be added when necessary
|
||||
private static byte[] GUID_OF_STD_LINK = {};
|
||||
|
||||
/** Logger */
|
||||
public static final Log log = LogFactory.getLog(HyperlinkRecord.class);
|
||||
|
||||
// quick and dirty
|
||||
private static final boolean _DEBUG_ = true;
|
||||
|
||||
public final static short sid = 0x1b8;
|
||||
|
||||
private short field_1_unknown;
|
||||
private int field_2_row;
|
||||
private short field_3_column;
|
||||
private short field_4_xf_index;
|
||||
private byte[] field_5_unknown;
|
||||
private int field_6_label_opts;
|
||||
private int field_7_url_len;
|
||||
private int field_8_label_len;
|
||||
private String field_9_label;
|
||||
private byte[] field_10_unknown;
|
||||
private int field_11_url_opts;
|
||||
private String field_12_url;
|
||||
|
||||
/** Blank Constructor */
|
||||
public HyperlinkRecord()
|
||||
{
|
||||
}
|
||||
|
||||
/** Real Constructor */
|
||||
public HyperlinkRecord(RecordInputStream in)
|
||||
{
|
||||
super(in);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getColumn()
|
||||
*/
|
||||
public short getColumn()
|
||||
{
|
||||
return field_3_column;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getRow()
|
||||
*/
|
||||
public int getRow()
|
||||
{
|
||||
return field_2_row;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#getXFIndex()
|
||||
*/
|
||||
public short getXFIndex()
|
||||
{
|
||||
return field_4_xf_index;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isAfter(org.apache.poi.hssf.record.CellValueRecordInterface)
|
||||
*/
|
||||
public boolean isAfter(CellValueRecordInterface i)
|
||||
{
|
||||
if (this.getRow() < i.getRow())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((this.getRow() == i.getRow()) && (this.getColumn() < i.getColumn()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isBefore(org.apache.poi.hssf.record.CellValueRecordInterface)
|
||||
*/
|
||||
public boolean isBefore(CellValueRecordInterface i)
|
||||
{
|
||||
if (this.getRow() > i.getRow())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((this.getRow() == i.getRow()) && (this.getColumn() > i.getColumn()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#isEqual(org.apache.poi.hssf.record.CellValueRecordInterface)
|
||||
*/
|
||||
public boolean isEqual(CellValueRecordInterface i)
|
||||
{
|
||||
return ((this.getRow() == i.getRow()) && (this.getColumn() == i.getColumn()));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setColumn(short)
|
||||
*/
|
||||
public void setColumn(short col)
|
||||
{
|
||||
this.field_3_column = col;
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setRow(int)
|
||||
*/
|
||||
public void setRow(int row)
|
||||
{
|
||||
this.field_2_row = row;
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.CellValueRecordInterface#setXFIndex(short)
|
||||
*/
|
||||
public void setXFIndex(short xf)
|
||||
{
|
||||
this.field_4_xf_index = xf;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
protected void fillFields(RecordInputStream in)
|
||||
{
|
||||
// System.err.println(in.currentSid);
|
||||
// System.err.println(in.currentLength);
|
||||
// for(int i=0; i<300; i++) {
|
||||
// System.err.println(in.readByte());
|
||||
// }
|
||||
// if(1==1)
|
||||
// throw new IllegalArgumentException("");
|
||||
|
||||
field_1_unknown = in.readShort();
|
||||
field_2_row = in.readUShort();
|
||||
field_3_column = in.readShort();
|
||||
field_4_xf_index = in.readShort();
|
||||
|
||||
// Next up is 16 bytes we don't get
|
||||
field_5_unknown = new byte[16];
|
||||
try {
|
||||
in.read(field_5_unknown);
|
||||
} catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
|
||||
|
||||
// Some sort of opts
|
||||
field_6_label_opts = in.readInt();
|
||||
|
||||
// Now for lengths, in characters
|
||||
field_7_url_len = in.readInt();
|
||||
field_8_label_len = in.readInt();
|
||||
|
||||
// Now we have the label, as little endian unicode,
|
||||
// with a trailing \0
|
||||
field_9_label = in.readUnicodeLEString(field_8_label_len);
|
||||
|
||||
// Next up is some more data we can't make sense of
|
||||
field_10_unknown = new byte[16];
|
||||
try {
|
||||
in.read(field_10_unknown);
|
||||
} catch(IOException e) { throw new IllegalStateException(e.getMessage()); }
|
||||
|
||||
// Might need to nudge the length by one byte
|
||||
// This is an empirical hack!
|
||||
field_11_url_opts = in.readInt();
|
||||
if(field_11_url_opts == 44) {
|
||||
field_7_url_len--;
|
||||
}
|
||||
|
||||
// Finally it's the URL
|
||||
int strlen = field_7_url_len > (in.remaining()/2) ? (in.remaining()/2) : field_7_url_len;
|
||||
field_12_url = in.readUnicodeLEString(strlen);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.poi.hssf.record.Record#getSid()
|
||||
*/
|
||||
public short getSid()
|
||||
{
|
||||
return HyperlinkRecord.sid;
|
||||
}
|
||||
|
||||
protected void validateSid(short id)
|
||||
{
|
||||
if (id != sid)
|
||||
{
|
||||
throw new RecordFormatException("NOT A HYPERLINKRECORD!");
|
||||
}
|
||||
}
|
||||
|
||||
public int serialize(int offset, byte[] data)
|
||||
{
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset,
|
||||
( short )(getRecordSize()-4));
|
||||
LittleEndian.putShort(data, 4 + offset, field_1_unknown);
|
||||
LittleEndian.putUShort(data, 6 + offset, field_2_row);
|
||||
LittleEndian.putShort(data, 8 + offset, field_3_column);
|
||||
LittleEndian.putShort(data, 10 + offset, field_4_xf_index);
|
||||
|
||||
offset += 12;
|
||||
for(int i=0; i<field_5_unknown.length; i++) {
|
||||
data[offset] = field_5_unknown[i];
|
||||
offset++;
|
||||
}
|
||||
|
||||
LittleEndian.putInt(data, offset, field_6_label_opts);
|
||||
offset += 4;
|
||||
LittleEndian.putInt(data, offset, field_7_url_len);
|
||||
offset += 4;
|
||||
LittleEndian.putInt(data, offset, field_8_label_len);
|
||||
offset += 4;
|
||||
StringUtil.putUnicodeLE(field_9_label, data, offset);
|
||||
offset += field_9_label.length()*2;
|
||||
|
||||
for(int i=0; i<field_10_unknown.length; i++) {
|
||||
data[offset] = field_10_unknown[i];
|
||||
offset++;
|
||||
}
|
||||
|
||||
LittleEndian.putInt(data, offset, field_11_url_opts);
|
||||
offset += 4;
|
||||
StringUtil.putUnicodeLE(field_12_url, data, offset);
|
||||
|
||||
return getRecordSize();
|
||||
}
|
||||
|
||||
public int getRecordSize()
|
||||
{
|
||||
// We have:
|
||||
// 4 shorts
|
||||
// junk
|
||||
// 3 ints
|
||||
// label
|
||||
// junk
|
||||
// int
|
||||
// url
|
||||
return 4 + 4*2 + field_5_unknown.length +
|
||||
3*4 + field_9_label.length()*2 +
|
||||
field_10_unknown.length + 4 +
|
||||
field_12_url.length()*2;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer.append("[HYPERLINK RECORD]\n");
|
||||
buffer.append(" .row = ").append(Integer.toHexString(getRow())).append("\n");
|
||||
buffer.append(" .column = ").append(Integer.toHexString(getColumn())).append("\n");
|
||||
buffer.append(" .xfindex = ").append(Integer.toHexString(getXFIndex())).append("\n");
|
||||
buffer.append(" .label = ").append(field_9_label).append("\n");
|
||||
buffer.append(" .url = ").append(field_12_url).append("\n");
|
||||
buffer.append("[/HYPERLINK RECORD]\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the label.
|
||||
*/
|
||||
public String getLabel()
|
||||
{
|
||||
if(field_9_label.length() == 0) {
|
||||
return "";
|
||||
} else {
|
||||
// Trim off \0
|
||||
return field_9_label.substring(0, field_9_label.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label The label to set.
|
||||
*/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.field_9_label = label + '\u0000';
|
||||
this.field_8_label_len = field_9_label.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Url.
|
||||
*/
|
||||
public URL getUrl() throws MalformedURLException
|
||||
{
|
||||
return new URL(getUrlString());
|
||||
}
|
||||
public String getUrlString()
|
||||
{
|
||||
if(field_12_url.length() == 0) {
|
||||
return "";
|
||||
} else {
|
||||
// Trim off \0
|
||||
return field_12_url.substring(0, field_12_url.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url The url to set.
|
||||
*/
|
||||
public void setUrl(URL url)
|
||||
{
|
||||
setUrl(url.toString());
|
||||
}
|
||||
/**
|
||||
* @param url The url to set.
|
||||
*/
|
||||
public void setUrl(String url)
|
||||
{
|
||||
this.field_12_url = url + '\u0000';
|
||||
this.field_7_url_len = field_12_url.length();
|
||||
}
|
||||
|
||||
public int getOptions(){
|
||||
return field_11_url_opts;
|
||||
}
|
||||
}
|
@ -76,7 +76,8 @@ public class RecordFactory
|
||||
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
|
||||
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
|
||||
FileSharingRecord.class, ChartTitleFormatRecord.class,
|
||||
DVRecord.class, DVALRecord.class, UncalcedRecord.class
|
||||
DVRecord.class, DVALRecord.class, UncalcedRecord.class,
|
||||
HyperlinkRecord.class
|
||||
};
|
||||
}
|
||||
private static Map recordsMap = recordsToMap(records);
|
||||
|
@ -249,7 +249,7 @@ public class RecordInputStream extends InputStream
|
||||
*/
|
||||
public String readUnicodeLEString(int length) {
|
||||
if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length");
|
||||
throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(length);
|
||||
|
@ -261,13 +261,16 @@ public class Area3DPtg extends Ptg
|
||||
setLastRowRelative( !lastCell.isRowAbsolute() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return text representation of this area reference that can be used in text
|
||||
* formulas. The sheet name will get properly delimited if required.
|
||||
*/
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
SheetReferences refs = book == null ? null : book.getSheetReferences();
|
||||
StringBuffer retval = new StringBuffer();
|
||||
if ( refs != null )
|
||||
{
|
||||
retval.append( refs.getSheetName( this.field_1_index_extern_sheet ) );
|
||||
String sheetName = Ref3DPtg.getSheetName(book, field_1_index_extern_sheet);
|
||||
if(sheetName != null) {
|
||||
SheetNameFormatter.appendFormat(retval, sheetName);
|
||||
retval.append( '!' );
|
||||
}
|
||||
retval.append( ( new CellReference( getFirstRow(), getFirstColumn(), !isFirstRowRelative(), !isFirstColRelative() ) ).toString() );
|
||||
|
@ -43,9 +43,9 @@ public class AreaPtg
|
||||
private short field_3_first_column;
|
||||
private short field_4_last_column;
|
||||
|
||||
private BitField rowRelative = BitFieldFactory.getInstance(0x8000);
|
||||
private BitField colRelative = BitFieldFactory.getInstance(0x4000);
|
||||
private BitField column = BitFieldFactory.getInstance(0x3FFF);
|
||||
private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000);
|
||||
private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
|
||||
private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
|
||||
|
||||
protected AreaPtg() {
|
||||
//Required for clone methods
|
||||
@ -157,7 +157,7 @@ public class AreaPtg
|
||||
*/
|
||||
public short getFirstColumn()
|
||||
{
|
||||
return column.getShortValue(field_3_first_column);
|
||||
return columnMask.getShortValue(field_3_first_column);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,7 +204,7 @@ public class AreaPtg
|
||||
*/
|
||||
public void setFirstColumn(short column)
|
||||
{
|
||||
field_3_first_column = column; // fixme
|
||||
field_3_first_column=columnMask.setShortValue(field_3_first_column, column);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +220,7 @@ public class AreaPtg
|
||||
*/
|
||||
public short getLastColumn()
|
||||
{
|
||||
return column.getShortValue(field_4_last_column);
|
||||
return columnMask.getShortValue(field_4_last_column);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -269,7 +269,7 @@ public class AreaPtg
|
||||
*/
|
||||
public void setLastColumn(short column)
|
||||
{
|
||||
field_4_last_column = column; // fixme
|
||||
field_4_last_column=columnMask.setShortValue(field_4_last_column, column);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,11 +34,10 @@ public class ConcatPtg
|
||||
public final static byte sid = 0x08;
|
||||
|
||||
private final static String CONCAT = "&";
|
||||
|
||||
|
||||
public ConcatPtg(RecordInputStream in)
|
||||
{
|
||||
|
||||
// doesn't need anything
|
||||
// No contents
|
||||
}
|
||||
|
||||
public ConcatPtg() {
|
||||
|
@ -40,7 +40,7 @@ public class IntPtg
|
||||
{
|
||||
public final static int SIZE = 3;
|
||||
public final static byte sid = 0x1e;
|
||||
private short field_1_value;
|
||||
private int field_1_value;
|
||||
|
||||
private IntPtg() {
|
||||
//Required for clone methods
|
||||
@ -48,42 +48,31 @@ public class IntPtg
|
||||
|
||||
public IntPtg(RecordInputStream in)
|
||||
{
|
||||
setValue(in.readShort());
|
||||
setValue(in.readUShort());
|
||||
}
|
||||
|
||||
|
||||
// IntPtg should be able to create itself, shouldnt have to call setValue
|
||||
public IntPtg(String formulaToken) {
|
||||
setValue(Short.parseShort(formulaToken));
|
||||
setValue(Integer.parseInt(formulaToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the wrapped value.
|
||||
* Normally you should call with a positive int.
|
||||
*/
|
||||
public void setValue(short value)
|
||||
{
|
||||
field_1_value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unsigned value.
|
||||
* (Handles conversion to the internal short value)
|
||||
*/
|
||||
public void setValue(int value)
|
||||
{
|
||||
if(value > Short.MAX_VALUE) {
|
||||
// Need to wrap
|
||||
value -= (Short.MAX_VALUE+1)*2;
|
||||
}
|
||||
field_1_value = (short)value;
|
||||
if(value < 0 || value > (Short.MAX_VALUE + 1)*2 )
|
||||
throw new IllegalArgumentException("Unsigned short is out of range: " + value);
|
||||
field_1_value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as a short, which may have
|
||||
* been wrapped into negative numbers
|
||||
*/
|
||||
public short getValue()
|
||||
public int getValue()
|
||||
{
|
||||
return field_1_value;
|
||||
}
|
||||
@ -102,7 +91,7 @@ public class IntPtg
|
||||
public void writeBytes(byte [] array, int offset)
|
||||
{
|
||||
array[ offset + 0 ] = sid;
|
||||
LittleEndian.putShort(array, offset + 1, getValue());
|
||||
LittleEndian.putUShort(array, offset + 1, getValue());
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
|
@ -137,8 +137,8 @@ public abstract class Ptg
|
||||
break;
|
||||
|
||||
case DividePtg.sid : // 0x06
|
||||
retval = new DividePtg(in);
|
||||
break;
|
||||
retval = new DividePtg(in);
|
||||
break;
|
||||
|
||||
case PowerPtg.sid : // 0x07
|
||||
retval = new PowerPtg(in);
|
||||
@ -208,6 +208,7 @@ public abstract class Ptg
|
||||
break;
|
||||
|
||||
case AttrPtg.sid : // 0x19
|
||||
case 0x1a :
|
||||
retval = new AttrPtg(in);
|
||||
break;
|
||||
|
||||
@ -224,8 +225,8 @@ public abstract class Ptg
|
||||
break;
|
||||
|
||||
case NumberPtg.sid : // 0x1f
|
||||
retval = new NumberPtg(in);
|
||||
break;
|
||||
retval = new NumberPtg(in);
|
||||
break;
|
||||
|
||||
case ArrayPtg.sid : // 0x20
|
||||
retval = new ArrayPtg(in);
|
||||
@ -350,9 +351,12 @@ public abstract class Ptg
|
||||
case DeletedArea3DPtg.sid + 0x40 : // 0x7d
|
||||
retval = new DeletedArea3DPtg(in);
|
||||
break;
|
||||
|
||||
|
||||
case 0x00:
|
||||
retval = new UnknownPtg();
|
||||
break;
|
||||
|
||||
default :
|
||||
|
||||
//retval = new UnknownPtg();
|
||||
throw new java.lang.UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+
|
||||
Integer.toHexString(( int ) id) + " (" + ( int ) id + ")");
|
||||
|
@ -25,22 +25,22 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*/
|
||||
public class RangePtg extends OperationPtg
|
||||
{
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x11;
|
||||
|
||||
|
||||
public RangePtg()
|
||||
{
|
||||
}
|
||||
|
||||
public RangePtg(RecordInputStream in)
|
||||
{
|
||||
// doesn't need anything
|
||||
// No contents
|
||||
}
|
||||
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return 1;
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public void writeBytes( byte[] array, int offset )
|
||||
|
@ -157,18 +157,31 @@ public class Ref3DPtg extends Ptg {
|
||||
|
||||
}
|
||||
|
||||
public String toFormulaString(Workbook book) {
|
||||
// TODO - find a home for this method
|
||||
// There is already a method on Workbook called getSheetName but it seems to do something different.
|
||||
static String getSheetName(Workbook book, int externSheetIndex) {
|
||||
// TODO - there are 3 ways this method can return null. Is each valid?
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SheetReferences refs = book.getSheetReferences();
|
||||
if (refs == null) {
|
||||
return null;
|
||||
}
|
||||
return refs.getSheetName(externSheetIndex);
|
||||
}
|
||||
/**
|
||||
* @return text representation of this cell reference that can be used in text
|
||||
* formulas. The sheet name will get properly delimited if required.
|
||||
*/
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
StringBuffer retval = new StringBuffer();
|
||||
SheetReferences refs = book == null ? null : book.getSheetReferences();
|
||||
if (refs != null) {
|
||||
String sheetName =refs.getSheetName((int)this.field_1_index_extern_sheet);
|
||||
boolean appendQuotes = sheetName.indexOf(" ") >= 0;
|
||||
if (appendQuotes)
|
||||
retval.append("'");
|
||||
retval.append(sheetName);
|
||||
if (appendQuotes)
|
||||
retval.append("'");
|
||||
retval.append('!');
|
||||
String sheetName = getSheetName(book, field_1_index_extern_sheet);
|
||||
if(sheetName != null) {
|
||||
SheetNameFormatter.appendFormat(retval, sheetName);
|
||||
retval.append( '!' );
|
||||
}
|
||||
retval.append((new CellReference(getRow(),getColumn(),!isRowRelative(),!isColRelative())).toString());
|
||||
return retval.toString();
|
||||
|
245
src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
Executable file
245
src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
Executable file
@ -0,0 +1,245 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formats sheet names for use in formula expressions.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class SheetNameFormatter {
|
||||
|
||||
private static final String BIFF8_LAST_COLUMN = "IV";
|
||||
private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
|
||||
private static final String BIFF8_LAST_ROW = String.valueOf(0x10000);
|
||||
private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
|
||||
|
||||
private static final char DELIMITER = '\'';
|
||||
|
||||
private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z])+[0-9]+");
|
||||
|
||||
private SheetNameFormatter() {
|
||||
// no instances of this class
|
||||
}
|
||||
/**
|
||||
* Used to format sheet names as they would appear in cell formula expressions.
|
||||
* @return the sheet name unchanged if there is no need for delimiting. Otherwise the sheet
|
||||
* name is enclosed in single quotes ('). Any single quotes which were already present in the
|
||||
* sheet name will be converted to double single quotes ('').
|
||||
*/
|
||||
public static String format(String rawSheetName) {
|
||||
StringBuffer sb = new StringBuffer(rawSheetName.length() + 2);
|
||||
appendFormat(sb, rawSheetName);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for when a StringBuffer is already available
|
||||
*
|
||||
* @param out - sheet name will be appended here possibly with delimiting quotes
|
||||
*/
|
||||
public static void appendFormat(StringBuffer out, String rawSheetName) {
|
||||
boolean needsQuotes = needsDelimiting(rawSheetName);
|
||||
if(needsQuotes) {
|
||||
out.append(DELIMITER);
|
||||
appendAndEscape(out, rawSheetName);
|
||||
out.append(DELIMITER);
|
||||
} else {
|
||||
out.append(rawSheetName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
|
||||
int len = rawSheetName.length();
|
||||
for(int i=0; i<len; i++) {
|
||||
char ch = rawSheetName.charAt(i);
|
||||
if(ch == DELIMITER) {
|
||||
// single quotes (') are encoded as ('')
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needsDelimiting(String rawSheetName) {
|
||||
int len = rawSheetName.length();
|
||||
if(len < 1) {
|
||||
throw new RuntimeException("Zero length string is an invalid sheet name");
|
||||
}
|
||||
if(Character.isDigit(rawSheetName.charAt(0))) {
|
||||
// sheet name with digit in the first position always requires delimiting
|
||||
return true;
|
||||
}
|
||||
for(int i=0; i<len; i++) {
|
||||
char ch = rawSheetName.charAt(i);
|
||||
if(isSpecialChar(ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(Character.isLetter(rawSheetName.charAt(0))
|
||||
&& Character.isDigit(rawSheetName.charAt(len-1))) {
|
||||
// note - values like "A$1:$C$20" don't get this far
|
||||
if(nameLooksLikePlainCellReference(rawSheetName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the presence of the specified character in a sheet name would
|
||||
* require the sheet name to be delimited in formulas. This includes every non-alphanumeric
|
||||
* character besides underscore '_'.
|
||||
*/
|
||||
/* package */ static boolean isSpecialChar(char ch) {
|
||||
// note - Character.isJavaIdentifierPart() would allow dollars '$'
|
||||
if(Character.isLetterOrDigit(ch)) {
|
||||
return false;
|
||||
}
|
||||
switch(ch) {
|
||||
case '_': // underscore is ok
|
||||
return false;
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
throw new RuntimeException("Illegal character (0x"
|
||||
+ Integer.toHexString(ch) + ") found in sheet name");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to decide whether sheet names like 'AB123' need delimiting due to the fact that they
|
||||
* look like cell references.
|
||||
* <p/>
|
||||
* This code is currently being used for translating formulas represented with <code>Ptg</code>
|
||||
* tokens into human readable text form. In formula expressions, a sheet name always has a
|
||||
* trailing '!' so there is little chance for ambiguity. It doesn't matter too much what this
|
||||
* method returns but it is worth noting the likely consumers of these formula text strings:
|
||||
* <ol>
|
||||
* <li>POI's own formula parser</li>
|
||||
* <li>Visual reading by human</li>
|
||||
* <li>VBA automation entry into Excel cell contents e.g. ActiveCell.Formula = "=c64!A1"</li>
|
||||
* <li>Manual entry into Excel cell contents</li>
|
||||
* <li>Some third party formula parser</li>
|
||||
* </ol>
|
||||
*
|
||||
* At the time of writing, POI's formula parser tolerates cell-like sheet names in formulas
|
||||
* with or without delimiters. The same goes for Excel(2007), both manual and automated entry.
|
||||
* <p/>
|
||||
* For better or worse this implementation attempts to replicate Excel's formula renderer.
|
||||
* Excel uses range checking on the apparent 'row' and 'column' components. Note however that
|
||||
* the maximum sheet size varies across versions:
|
||||
* <p/>
|
||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0"
|
||||
* summary="Notable cases.">
|
||||
* <tr><th>Version </th><th>File Format </th>
|
||||
* <th>Last Column </th><th>Last Row</th></tr>
|
||||
* <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr>
|
||||
* <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr>
|
||||
* </table></blockquote>
|
||||
* POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for
|
||||
* this method:
|
||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0"
|
||||
* summary="Notable cases.">
|
||||
* <tr><th>Input </th>
|
||||
* <th>Result </th></tr>
|
||||
* <tr><td>"A1", 1</td><td>true</td></tr>
|
||||
* <tr><td>"a111", 1</td><td>true</td></tr>
|
||||
* <tr><td>"A65536", 1</td><td>true</td></tr>
|
||||
* <tr><td>"A65537", 1</td><td>false</td></tr>
|
||||
* <tr><td>"iv1", 2</td><td>true</td></tr>
|
||||
* <tr><td>"IW1", 2</td><td>false</td></tr>
|
||||
* <tr><td>"AAA1", 3</td><td>false</td></tr>
|
||||
* <tr><td>"a111", 1</td><td>true</td></tr>
|
||||
* <tr><td>"Sheet1", 6</td><td>false</td></tr>
|
||||
* </table></blockquote>
|
||||
*/
|
||||
/* package */ static boolean cellReferenceIsWithinRange(String rawSheetName, int numberOfLetters) {
|
||||
|
||||
if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
// "Sheet1" case etc
|
||||
return false; // that was easy
|
||||
}
|
||||
int nDigits = rawSheetName.length() - numberOfLetters;
|
||||
if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
|
||||
return false;
|
||||
}
|
||||
if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
|
||||
String colStr = rawSheetName.substring(0, BIFF8_LAST_COLUMN_TEXT_LEN).toUpperCase();
|
||||
if(colStr.compareTo(BIFF8_LAST_COLUMN) > 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// apparent column name has less chars than max
|
||||
// no need to check range
|
||||
}
|
||||
|
||||
if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
|
||||
String colStr = rawSheetName.substring(numberOfLetters);
|
||||
// ASCII comparison is valid if digit count is same
|
||||
if(colStr.compareTo(BIFF8_LAST_ROW) > 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// apparent row has less chars than max
|
||||
// no need to check range
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - this method assumes the specified rawSheetName has only letters and digits. It
|
||||
* cannot be used to match absolute or range references (using the dollar or colon char).
|
||||
* <p/>
|
||||
* Some notable cases:
|
||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0"
|
||||
* summary="Notable cases.">
|
||||
* <tr><th>Input </th><th>Result </th><th>Comments</th></tr>
|
||||
* <tr><td>"A1" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"a111" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"AA" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"aa1" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"A1A" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"A1A1" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"A$1:$C$20" </td><td>false</td><td>Not a plain cell reference</td></tr>
|
||||
* <tr><td>"SALES20080101" </td><td>true</td>
|
||||
* <td>Still needs delimiting even though well out of range</td></tr>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* @return <code>true</code> if there is any possible ambiguity that the specified rawSheetName
|
||||
* could be interpreted as a valid cell name.
|
||||
*/
|
||||
/* package */ static boolean nameLooksLikePlainCellReference(String rawSheetName) {
|
||||
Matcher matcher = CELL_REF_PATTERN.matcher(rawSheetName);
|
||||
if(!matcher.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// rawSheetName == "Sheet1" gets this far.
|
||||
String lettersPrefix = matcher.group(1);
|
||||
return cellReferenceIsWithinRange(rawSheetName, lettersPrefix.length());
|
||||
}
|
||||
|
||||
}
|
@ -28,7 +28,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
public class UnknownPtg
|
||||
extends Ptg
|
||||
{
|
||||
private short size;
|
||||
private short size = 1;
|
||||
|
||||
/** Creates new UnknownPtg */
|
||||
|
||||
|
@ -34,20 +34,7 @@ import java.util.Iterator;
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.Sheet;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.BlankRecord;
|
||||
import org.apache.poi.hssf.record.BoolErrRecord;
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
|
||||
import org.apache.poi.hssf.record.ExtendedFormatRecord;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.LabelSSTRecord;
|
||||
import org.apache.poi.hssf.record.NoteRecord;
|
||||
import org.apache.poi.hssf.record.NumberRecord;
|
||||
import org.apache.poi.hssf.record.ObjRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.SubRecord;
|
||||
import org.apache.poi.hssf.record.TextObjectRecord;
|
||||
import org.apache.poi.hssf.record.UnicodeString;
|
||||
import org.apache.poi.hssf.record.*;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
@ -1073,4 +1060,31 @@ public class HSSFCell implements Cell
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hyperlink associated with this cell
|
||||
*
|
||||
* @return hyperlink associated with this cell or null if not found
|
||||
*/
|
||||
public HSSFHyperlink getHyperlink(){
|
||||
for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) {
|
||||
Record rec = ( Record ) it.next();
|
||||
if (rec instanceof HyperlinkRecord){
|
||||
HyperlinkRecord link = (HyperlinkRecord)rec;
|
||||
if(link.getColumn() == record.getColumn() && link.getRow() == record.getRow()){
|
||||
return new HSSFHyperlink(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a hypelrink to this cell
|
||||
*
|
||||
* @param link hypelrink associated with this cell
|
||||
*/
|
||||
public void setHyperlink(HSSFHyperlink link){
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -206,12 +206,12 @@ public class HSSFDataFormat implements DataFormat
|
||||
}
|
||||
|
||||
/**
|
||||
* get the format index that matches the given format string.
|
||||
* Creates a new format if one is not found. Aliases text to the proper format.
|
||||
* Get the format index that matches the given format
|
||||
* string, creating a new format entry if required.
|
||||
* Aliases text to the proper format as required.
|
||||
* @param format string matching a built in format
|
||||
* @return index of format.
|
||||
*/
|
||||
|
||||
public short getFormat( String format )
|
||||
{
|
||||
ListIterator i;
|
||||
|
128
src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
Executable file
128
src/java/org/apache/poi/hssf/usermodel/HSSFHyperlink.java
Executable file
@ -0,0 +1,128 @@
|
||||
/* ====================================================================
|
||||
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.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.record.EscherAggregate;
|
||||
import org.apache.poi.hssf.record.NoteRecord;
|
||||
import org.apache.poi.hssf.record.TextObjectRecord;
|
||||
import org.apache.poi.hssf.record.HyperlinkRecord;
|
||||
import org.apache.poi.ddf.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Represents a hyperlink.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class HSSFHyperlink {
|
||||
|
||||
/**
|
||||
* Link to a existing file or web page
|
||||
*/
|
||||
public static final int LINK_URL = 1;
|
||||
|
||||
/**
|
||||
* Link to a place in this document
|
||||
*/
|
||||
public static final int LINK_DOCUMENT = 2;
|
||||
|
||||
/**
|
||||
* Link to an E-mail address
|
||||
*/
|
||||
public static final int LINK_EMAIL = 3;
|
||||
|
||||
/**
|
||||
* Unknown type
|
||||
*/
|
||||
public static final int LINK_UNKNOWN = 4;
|
||||
|
||||
/**
|
||||
* Low-level record object that stores the actual hyperlink data
|
||||
*/
|
||||
private HyperlinkRecord record = null;
|
||||
|
||||
protected HSSFHyperlink( HyperlinkRecord record )
|
||||
{
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the row of the cell that contains the hyperlink
|
||||
*
|
||||
* @return the 0-based row of the cell that contains the hyperlink
|
||||
*/
|
||||
public int getRow(){
|
||||
return record.getRow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the row of the cell that contains the hyperlink
|
||||
*
|
||||
* @param row the 0-based row of the cell that contains the hyperlink
|
||||
*/
|
||||
public void setRow(int row){
|
||||
record.setRow(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column of the cell that contains the hyperlink
|
||||
*
|
||||
* @return the 0-based column of the cell that contains the hyperlink
|
||||
*/
|
||||
public short getColumn(){
|
||||
return record.getColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the column of the cell that contains the hyperlink
|
||||
*
|
||||
* @param col the 0-based column of the cell that contains the hyperlink
|
||||
*/
|
||||
public void setColumn(short col){
|
||||
record.setColumn(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hypelink address. Depending on the hyperlink type it can be URL, e-mail, etc.
|
||||
*
|
||||
* @return the address of this hyperlink
|
||||
*/
|
||||
public String getAddress(){
|
||||
return record.getUrlString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return text to display for this hyperlink
|
||||
*
|
||||
* @return text to display
|
||||
*/
|
||||
public String getLabel(){
|
||||
return record.getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of this hyperlink
|
||||
*
|
||||
* @return the type of this hyperlink
|
||||
*/
|
||||
public int getType(){
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
@ -22,8 +22,6 @@ package org.apache.poi.util;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A logger class that strives to make it as easy as possible for
|
||||
* developers to write log calls, while simultaneously making those
|
||||
@ -53,7 +51,6 @@ public class CommonsLogger extends POILogger
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log.
|
||||
*/
|
||||
|
||||
public void log(final int level, final Object obj1)
|
||||
{
|
||||
if(level==FATAL)
|
||||
@ -98,6 +95,78 @@ public class CommonsLogger extends POILogger
|
||||
log.trace(obj1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log. This is converted to a string.
|
||||
* @param exception An exception to be logged
|
||||
*/
|
||||
public void log(final int level, final Object obj1,
|
||||
final Throwable exception)
|
||||
{
|
||||
if(level==FATAL)
|
||||
{
|
||||
if(log.isFatalEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.fatal(obj1, exception);
|
||||
else
|
||||
log.fatal(exception);
|
||||
}
|
||||
}
|
||||
else if(level==ERROR)
|
||||
{
|
||||
if(log.isErrorEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.error(obj1, exception);
|
||||
else
|
||||
log.error(exception);
|
||||
}
|
||||
}
|
||||
else if(level==WARN)
|
||||
{
|
||||
if(log.isWarnEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.warn(obj1, exception);
|
||||
else
|
||||
log.warn(exception);
|
||||
}
|
||||
}
|
||||
else if(level==INFO)
|
||||
{
|
||||
if(log.isInfoEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.info(obj1, exception);
|
||||
else
|
||||
log.info(exception);
|
||||
}
|
||||
}
|
||||
else if(level==DEBUG)
|
||||
{
|
||||
if(log.isDebugEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.debug(obj1, exception);
|
||||
else
|
||||
log.debug(exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(log.isTraceEnabled())
|
||||
{
|
||||
if(obj1 != null)
|
||||
log.trace(obj1, exception);
|
||||
else
|
||||
log.trace(exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,24 @@ public abstract class POILogger
|
||||
|
||||
abstract public void initialize(final String cat);
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log. This is converted to a string.
|
||||
*/
|
||||
abstract public void log(final int level, final Object obj1);
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log. This is converted to a string.
|
||||
* @param exception An exception to be logged
|
||||
*/
|
||||
abstract public void log(final int level, final Object obj1,
|
||||
final Throwable exception);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a logger is enabled to log at the specified level
|
||||
@ -237,17 +254,15 @@ public abstract class POILogger
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
* Log an exception, without a message
|
||||
*
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log. This is converted to a string.
|
||||
* @param exception An exception to be logged
|
||||
*/
|
||||
|
||||
public void log(final int level, final Object obj1,
|
||||
final Throwable exception)
|
||||
public void log(final int level, final Throwable exception)
|
||||
{
|
||||
log(level , obj1, exception);
|
||||
log(level, null, exception);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,8 +49,24 @@ public class SystemOutLogger extends POILogger
|
||||
|
||||
public void log(final int level, final Object obj1)
|
||||
{
|
||||
if (check(level))
|
||||
log(level, obj1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param level One of DEBUG, INFO, WARN, ERROR, FATAL
|
||||
* @param obj1 The object to log. This is converted to a string.
|
||||
* @param exception An exception to be logged
|
||||
*/
|
||||
public void log(final int level, final Object obj1,
|
||||
final Throwable exception) {
|
||||
if (check(level)) {
|
||||
System.out.println("["+cat+"] "+obj1);
|
||||
if(exception != null) {
|
||||
exception.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,8 @@ import org.apache.poi.hslf.record.*;
|
||||
import org.apache.poi.hslf.usermodel.PictureData;
|
||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
@ -33,6 +35,9 @@ import java.util.*;
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class Fill {
|
||||
// For logging
|
||||
protected POILogger logger = POILogFactory.getLogger(this.getClass());
|
||||
|
||||
/**
|
||||
* Fill with a solid color
|
||||
*/
|
||||
@ -208,15 +213,18 @@ public class Fill {
|
||||
|
||||
java.util.List lst = bstore.getChildRecords();
|
||||
int idx = p.getPropertyValue();
|
||||
EscherBSERecord bse = (EscherBSERecord)lst.get(idx);
|
||||
for ( int i = 0; i < pict.length; i++ ) {
|
||||
if (pict[i].getOffset() == bse.getOffset()){
|
||||
return pict[i];
|
||||
if (idx == 0){
|
||||
logger.log(POILogger.ERROR, "no reference to picture data found ");
|
||||
} else {
|
||||
EscherBSERecord bse = (EscherBSERecord)lst.get(idx - 1);
|
||||
for ( int i = 0; i < pict.length; i++ ) {
|
||||
if (pict[i].getOffset() == bse.getOffset()){
|
||||
return pict[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new HSLFException("Picture data not found: \n" +
|
||||
" bse: " + bse + " at " + bse.getOffset() );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +109,7 @@ public class Picture extends SimpleShape {
|
||||
*/
|
||||
public int getPictureIndex(){
|
||||
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
|
||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY + 0x4000);
|
||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY);
|
||||
return prop == null ? 0 : prop.getPropertyValue();
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ public abstract class Shape {
|
||||
for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
|
||||
{
|
||||
EscherProperty prop = (EscherProperty) iterator.next();
|
||||
if (prop.getId() == propId)
|
||||
if (prop.getPropertyNumber() == propId)
|
||||
return prop;
|
||||
}
|
||||
return null;
|
||||
|
@ -262,4 +262,11 @@ public class Slide extends Sheet
|
||||
SlideAtom sa = getSlideRecord().getSlideAtom();
|
||||
return sa.getFollowMasterBackground();
|
||||
}
|
||||
|
||||
public Background getBackground() {
|
||||
if(getFollowMasterBackground())
|
||||
return getMasterSheet().getBackground();
|
||||
else
|
||||
return super.getBackground();
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,46 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
public class Columns extends NotImplementedFunction {
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
|
||||
/**
|
||||
* Implementation for Excel COLUMNS function.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Columns implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
switch(args.length) {
|
||||
case 1:
|
||||
// expected
|
||||
break;
|
||||
case 0:
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
default:
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval firstArg = args[0];
|
||||
|
||||
int result;
|
||||
if (firstArg instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) firstArg;
|
||||
result = ae.getLastColumn() - ae.getFirstColumn() + 1;
|
||||
} else if (firstArg instanceof RefEval) {
|
||||
result = 1;
|
||||
} else { // anything else is not valid argument
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,107 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
public class Counta extends NotImplementedFunction {
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Counts the number of cells that contain data within the list of arguments.
|
||||
*
|
||||
* Excel Syntax
|
||||
* COUNTA(value1,value2,...)
|
||||
* Value1, value2, ... are 1 to 30 arguments representing the values or ranges to be counted.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Counta implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
int nArgs = args.length;
|
||||
if (nArgs < 1) {
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (nArgs > 30) {
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
int temp = 0;
|
||||
// Note - observed behavior of Excel:
|
||||
// Error values like #VALUE!, #REF!, #DIV/0!, #NAME? etc don't cause this COUNTA to return an error
|
||||
// in fact, they seem to get counted
|
||||
|
||||
for(int i=0; i<nArgs; i++) {
|
||||
temp += countArg(args[i]);
|
||||
|
||||
}
|
||||
return new NumberEval(temp);
|
||||
}
|
||||
|
||||
private static int countArg(Eval eval) {
|
||||
if (eval instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) eval;
|
||||
return countAreaEval(ae);
|
||||
}
|
||||
if (eval instanceof RefEval) {
|
||||
RefEval refEval = (RefEval)eval;
|
||||
return countValue(refEval.getInnerValueEval());
|
||||
}
|
||||
if (eval instanceof NumberEval) {
|
||||
return 1;
|
||||
}
|
||||
if (eval instanceof StringEval) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
private static int countAreaEval(AreaEval ae) {
|
||||
|
||||
int temp = 0;
|
||||
ValueEval[] values = ae.getValues();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
ValueEval val = values[i];
|
||||
if(val == null) {
|
||||
// seems to occur. Really we would have expected BlankEval
|
||||
continue;
|
||||
}
|
||||
temp += countValue(val);
|
||||
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static int countValue(ValueEval valueEval) {
|
||||
|
||||
if(valueEval == BlankEval.INSTANCE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(valueEval instanceof BlankEval) {
|
||||
// wouldn't need this if BlankEval was final
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(valueEval instanceof ErrorEval) {
|
||||
// note - error values are counted
|
||||
return 1;
|
||||
}
|
||||
// also empty strings and zeros are counted too
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,231 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
public class Countif extends NotImplementedFunction {
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implementation for the function COUNTIF<p/>
|
||||
*
|
||||
* Syntax: COUNTIF ( range, criteria )
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
|
||||
* <tr><th>range </th><td>is the range of cells to be counted based on the criteria</td></tr>
|
||||
* <tr><th>criteria</th><td>is used to determine which cells to count</td></tr>
|
||||
* </table>
|
||||
* <p/>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Countif implements Function {
|
||||
|
||||
/**
|
||||
* Common interface for the matching criteria.
|
||||
*/
|
||||
private interface I_MatchPredicate {
|
||||
boolean matches(Eval x);
|
||||
}
|
||||
|
||||
private static final class NumberMatcher implements I_MatchPredicate {
|
||||
|
||||
private final double _value;
|
||||
|
||||
public NumberMatcher(double value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public boolean matches(Eval x) {
|
||||
if(x instanceof StringEval) {
|
||||
// if the target(x) is a string, but parses as a number
|
||||
// it may still count as a match
|
||||
StringEval se = (StringEval)x;
|
||||
Double val = parseDouble(se.getStringValue());
|
||||
if(val == null) {
|
||||
// x is text that is not a number
|
||||
return false;
|
||||
}
|
||||
return val.doubleValue() == _value;
|
||||
}
|
||||
if(!(x instanceof NumberEval)) {
|
||||
return false;
|
||||
}
|
||||
NumberEval ne = (NumberEval) x;
|
||||
return ne.getNumberValue() == _value;
|
||||
}
|
||||
}
|
||||
private static final class BooleanMatcher implements I_MatchPredicate {
|
||||
|
||||
private final boolean _value;
|
||||
|
||||
public BooleanMatcher(boolean value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public boolean matches(Eval x) {
|
||||
if(x instanceof StringEval) {
|
||||
StringEval se = (StringEval)x;
|
||||
Boolean val = parseBoolean(se.getStringValue());
|
||||
if(val == null) {
|
||||
// x is text that is not a boolean
|
||||
return false;
|
||||
}
|
||||
if (true) { // change to false to observe more intuitive behaviour
|
||||
// Note - Unlike with numbers, it seems that COUNTA never matches
|
||||
// boolean values when the target(x) is a string
|
||||
return false;
|
||||
}
|
||||
return val.booleanValue() == _value;
|
||||
}
|
||||
if(!(x instanceof BoolEval)) {
|
||||
return false;
|
||||
}
|
||||
BoolEval be = (BoolEval) x;
|
||||
return be.getBooleanValue() == _value;
|
||||
}
|
||||
}
|
||||
private static final class StringMatcher implements I_MatchPredicate {
|
||||
|
||||
private final String _value;
|
||||
|
||||
public StringMatcher(String value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public boolean matches(Eval x) {
|
||||
if(!(x instanceof StringEval)) {
|
||||
return false;
|
||||
}
|
||||
StringEval se = (StringEval) x;
|
||||
return se.getStringValue() == _value;
|
||||
}
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
switch(args.length) {
|
||||
case 2:
|
||||
// expected
|
||||
break;
|
||||
default:
|
||||
// TODO - it doesn't seem to be possible to enter COUNTIF() into Excel with the wrong arg count
|
||||
// perhaps this should be an exception
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
AreaEval range = (AreaEval) args[0];
|
||||
Eval criteriaArg = args[1];
|
||||
if(criteriaArg instanceof RefEval) {
|
||||
// criteria is not a literal value, but a cell reference
|
||||
// for example COUNTIF(B2:D4, E1)
|
||||
RefEval re = (RefEval)criteriaArg;
|
||||
criteriaArg = re.getInnerValueEval();
|
||||
} else {
|
||||
// other non literal tokens such as function calls, have been fully evaluated
|
||||
// for example COUNTIF(B2:D4, COLUMN(E1))
|
||||
}
|
||||
I_MatchPredicate mp = createCriteriaPredicate(criteriaArg);
|
||||
return countMatchingCellsInArea(range, mp);
|
||||
}
|
||||
/**
|
||||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
private Eval countMatchingCellsInArea(AreaEval range, I_MatchPredicate criteriaPredicate) {
|
||||
ValueEval[] values = range.getValues();
|
||||
int result = 0;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if(criteriaPredicate.matches(values[i])) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static I_MatchPredicate createCriteriaPredicate(Eval evaluatedCriteriaArg) {
|
||||
if(evaluatedCriteriaArg instanceof NumberEval) {
|
||||
return new NumberMatcher(((NumberEval)evaluatedCriteriaArg).getNumberValue());
|
||||
}
|
||||
if(evaluatedCriteriaArg instanceof BoolEval) {
|
||||
return new BooleanMatcher(((BoolEval)evaluatedCriteriaArg).getBooleanValue());
|
||||
}
|
||||
|
||||
if(evaluatedCriteriaArg instanceof StringEval) {
|
||||
return createGeneralMatchPredicate((StringEval)evaluatedCriteriaArg);
|
||||
}
|
||||
throw new RuntimeException("Unexpected type for criteria ("
|
||||
+ evaluatedCriteriaArg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* When the second argument is a string, many things are possible
|
||||
*/
|
||||
private static I_MatchPredicate createGeneralMatchPredicate(StringEval stringEval) {
|
||||
String value = stringEval.getStringValue();
|
||||
char firstChar = value.charAt(0);
|
||||
Boolean booleanVal = parseBoolean(value);
|
||||
if(booleanVal != null) {
|
||||
return new BooleanMatcher(booleanVal.booleanValue());
|
||||
}
|
||||
|
||||
Double doubleVal = parseDouble(value);
|
||||
if(doubleVal != null) {
|
||||
return new NumberMatcher(doubleVal.doubleValue());
|
||||
}
|
||||
switch(firstChar) {
|
||||
case '>':
|
||||
case '<':
|
||||
case '=':
|
||||
throw new RuntimeException("Incomplete code - criteria expressions such as '"
|
||||
+ value + "' not supported yet");
|
||||
}
|
||||
|
||||
//else - just a plain string with no interpretation.
|
||||
return new StringMatcher(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Under certain circumstances COUNTA will equate a plain number with a string representation of that number
|
||||
*/
|
||||
/* package */ static Double parseDouble(String strRep) {
|
||||
if(!Character.isDigit(strRep.charAt(0))) {
|
||||
// avoid using NumberFormatException to tell when string is not a number
|
||||
return null;
|
||||
}
|
||||
// TODO - support notation like '1E3' (==1000)
|
||||
|
||||
double val;
|
||||
try {
|
||||
val = Double.parseDouble(strRep);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return new Double(val);
|
||||
}
|
||||
/**
|
||||
* Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers.
|
||||
*/
|
||||
/* package */ static Boolean parseBoolean(String strRep) {
|
||||
switch(strRep.charAt(0)) {
|
||||
case 't':
|
||||
case 'T':
|
||||
if("TRUE".equalsIgnoreCase(strRep)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
if("FALSE".equalsIgnoreCase(strRep)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,95 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
public class Index extends NotImplementedFunction {
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
|
||||
/**
|
||||
* Implementation for the Excel function INDEX<p/>
|
||||
*
|
||||
* Syntax : <br/>
|
||||
* INDEX ( reference, row_num[, column_num [, area_num]])</br>
|
||||
* INDEX ( array, row_num[, column_num])
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
|
||||
* <tr><th>reference</th><td>typically an area reference, possibly a union of areas</td></tr>
|
||||
* <tr><th>array</th><td>a literal array value (currently not supported)</td></tr>
|
||||
* <tr><th>row_num</th><td>selects the row within the array or area reference</td></tr>
|
||||
* <tr><th>column_num</th><td>selects column within the array or area reference. default is 1</td></tr>
|
||||
* <tr><th>area_num</th><td>used when reference is a union of areas</td></tr>
|
||||
* </table>
|
||||
* <p/>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Index implements Function {
|
||||
|
||||
// TODO - javadoc for interface method
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
int nArgs = args.length;
|
||||
if(nArgs < 2) {
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval firstArg = args[0];
|
||||
if(firstArg instanceof AreaEval) {
|
||||
AreaEval reference = (AreaEval) firstArg;
|
||||
|
||||
int rowIx = 0;
|
||||
int columnIx = 0;
|
||||
int areaIx = 0;
|
||||
switch(nArgs) {
|
||||
case 4:
|
||||
areaIx = convertIndexArgToZeroBase(args[3]);
|
||||
throw new RuntimeException("Incomplete code" +
|
||||
" - don't know how to support the 'area_num' parameter yet)");
|
||||
// Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3)
|
||||
// In this example, the 3rd area would be used i.e. D2:E5, and the overall result would be E2
|
||||
// Token array might be encoded like this: MemAreaPtg, AreaPtg, AreaPtg, UnionPtg, UnionPtg, ParenthesesPtg
|
||||
// The formula parser doesn't seem to support this yet. Not sure if the evaluator does either
|
||||
|
||||
case 3:
|
||||
columnIx = convertIndexArgToZeroBase(args[2]);
|
||||
case 2:
|
||||
rowIx = convertIndexArgToZeroBase(args[1]);
|
||||
break;
|
||||
default:
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
int nColumns = reference.getLastColumn()-reference.getFirstColumn()+1;
|
||||
int index = rowIx * nColumns + columnIx;
|
||||
|
||||
return reference.getValues()[index];
|
||||
}
|
||||
|
||||
// else the other variation of this function takes an array as the first argument
|
||||
// it seems like interface 'ArrayEval' does not even exist yet
|
||||
|
||||
throw new RuntimeException("Incomplete code - cannot handle first arg of type ("
|
||||
+ firstArg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* takes a NumberEval representing a 1-based index and returns the zero-based int value
|
||||
*/
|
||||
private static int convertIndexArgToZeroBase(Eval ev) {
|
||||
NumberEval ne;
|
||||
if(ev instanceof RefEval) {
|
||||
// TODO - write junit to justify this
|
||||
RefEval re = (RefEval) ev;
|
||||
ne = (NumberEval) re.getInnerValueEval();
|
||||
} else {
|
||||
ne = (NumberEval)ev;
|
||||
}
|
||||
|
||||
return (int)ne.getNumberValue() - 1;
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,46 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
public class Rows extends NotImplementedFunction {
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
|
||||
/**
|
||||
* Implementation for Excel COLUMNS function.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Rows implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
switch(args.length) {
|
||||
case 1:
|
||||
// expected
|
||||
break;
|
||||
case 0:
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
default:
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval firstArg = args[0];
|
||||
|
||||
int result;
|
||||
if (firstArg instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) firstArg;
|
||||
result = ae.getLastRow() - ae.getFirstRow() + 1;
|
||||
} else if (firstArg instanceof RefEval) {
|
||||
result = 1;
|
||||
} else { // anything else is not valid argument
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public class TableRow
|
||||
p = getParagraph(end);
|
||||
s = p.text();
|
||||
}
|
||||
_cells[cellIndex] = new TableCell(start, end, this, levelNum,
|
||||
_cells[cellIndex] = new TableCell(start, end+1, this, levelNum,
|
||||
_tprops.getRgtc()[cellIndex],
|
||||
_tprops.getRgdxaCenter()[cellIndex],
|
||||
_tprops.getRgdxaCenter()[cellIndex+1]-_tprops.getRgdxaCenter()[cellIndex]);
|
||||
|
BIN
src/scratchpad/testcases/org/apache/poi/hslf/data/44296.ppt
Executable file
BIN
src/scratchpad/testcases/org/apache/poi/hslf/data/44296.ppt
Executable file
Binary file not shown.
@ -330,4 +330,24 @@ public class TestBugs extends TestCase {
|
||||
assertEquals(tr1[i].getText(), tr2[i].getText());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 44296: HSLF Not Extracting Slide Background Image
|
||||
*/
|
||||
public void test44296 () throws Exception {
|
||||
FileInputStream is = new FileInputStream(new File(cwd, "44296.ppt"));
|
||||
SlideShow ppt = new SlideShow(is);
|
||||
is.close();
|
||||
|
||||
Slide slide = ppt.getSlides()[0];
|
||||
|
||||
Background b = slide.getBackground();
|
||||
Fill f = b.getFill();
|
||||
assertEquals(Fill.FILL_PICTURE, f.getFillType());
|
||||
|
||||
PictureData pict = f.getPictureData();
|
||||
assertNotNull(pict);
|
||||
assertEquals(Picture.JPEG, pict.getType());
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
src/scratchpad/testcases/org/apache/poi/hssf/data/44297.xls
Executable file
BIN
src/scratchpad/testcases/org/apache/poi/hssf/data/44297.xls
Executable file
Binary file not shown.
@ -0,0 +1,44 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Direct tests for all implementors of <code>Function</code>.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class AllIndividualFunctionEvaluationTests {
|
||||
|
||||
// TODO - have this suite incorporated into a higher level one
|
||||
public static Test suite() {
|
||||
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions");
|
||||
result.addTestSuite(TestCountFuncs.class);
|
||||
result.addTestSuite(TestDate.class);
|
||||
result.addTestSuite(TestFinanceLib.class);
|
||||
result.addTestSuite(TestIndex.class);
|
||||
result.addTestSuite(TestMathX.class);
|
||||
result.addTestSuite(TestRowCol.class);
|
||||
result.addTestSuite(TestStatsLib.class);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Test helper class for creating mock <code>Eval</code> objects
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class EvalFactory {
|
||||
private static final NumberEval ZERO = new NumberEval(0);
|
||||
|
||||
private EvalFactory() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dummy AreaEval (filled with zeros)
|
||||
* <p/>
|
||||
* nCols and nRows could have been derived
|
||||
*/
|
||||
public static AreaEval createAreaEval(String areaRefStr, int nCols, int nRows) {
|
||||
int nValues = nCols * nRows;
|
||||
ValueEval[] values = new ValueEval[nValues];
|
||||
for (int i = 0; i < nValues; i++) {
|
||||
values[i] = ZERO;
|
||||
}
|
||||
|
||||
return new Area2DEval(new AreaPtg(areaRefStr), values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single RefEval (with value zero)
|
||||
*/
|
||||
public static RefEval createRefEval(String refStr) {
|
||||
return new Ref2DEval(new ReferencePtg(refStr), ZERO, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
|
||||
|
||||
/**
|
||||
* Test helper class for invoking functions with numeric results.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class NumericFunctionInvoker {
|
||||
|
||||
private NumericFunctionInvoker() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
private static final class NumericEvalEx extends Exception {
|
||||
public NumericEvalEx(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the specified function with the arguments.
|
||||
* <p/>
|
||||
* Assumes that the cell coordinate parameters of
|
||||
* <code>Function.evaluate(args, srcCellRow, srcCellCol)</code>
|
||||
* are not required.
|
||||
* <p/>
|
||||
* This method cannot be used for confirming error return codes. Any non-numeric evaluation
|
||||
* result causes the current junit test to fail.
|
||||
*/
|
||||
public static double invoke(Function f, Eval[] args) {
|
||||
try {
|
||||
return invokeInternal(f, args, -1, -1);
|
||||
} catch (NumericEvalEx e) {
|
||||
throw new AssertionFailedError("Evaluation of function (" + f.getClass().getName()
|
||||
+ ") failed: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Formats nicer error messages for the junit output
|
||||
*/
|
||||
private static double invokeInternal(Function f, Eval[] args, int srcCellRow, int srcCellCol)
|
||||
throws NumericEvalEx {
|
||||
Eval evalResult = f.evaluate(args, srcCellRow, (short)srcCellCol);
|
||||
if(evalResult == null) {
|
||||
throw new NumericEvalEx("Result object was null");
|
||||
}
|
||||
if(evalResult instanceof ErrorEval) {
|
||||
ErrorEval ee = (ErrorEval) evalResult;
|
||||
throw new NumericEvalEx(formatErrorMessage(ee));
|
||||
}
|
||||
if(!(evalResult instanceof NumericValueEval)) {
|
||||
throw new NumericEvalEx("Result object type (" + evalResult.getClass().getName()
|
||||
+ ") is invalid. Expected implementor of ("
|
||||
+ NumericValueEval.class.getName() + ")");
|
||||
}
|
||||
|
||||
NumericValueEval result = (NumericValueEval) evalResult;
|
||||
return result.getNumberValue();
|
||||
}
|
||||
private static String formatErrorMessage(ErrorEval ee) {
|
||||
if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) {
|
||||
return "Function not implemented";
|
||||
}
|
||||
if(errorCodesAreEqual(ee, ErrorEval.UNKNOWN_ERROR)) {
|
||||
return "Unknown error";
|
||||
}
|
||||
return "Error code=" + ee.getErrorCode();
|
||||
}
|
||||
private static boolean errorCodesAreEqual(ErrorEval a, ErrorEval b) {
|
||||
if(a==b) {
|
||||
return true;
|
||||
}
|
||||
return a.getErrorCode() == b.getErrorCode();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestCountFuncs extends TestCase {
|
||||
|
||||
public TestCountFuncs(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
public void testCountA() {
|
||||
|
||||
Eval[] args;
|
||||
|
||||
args = new Eval[] {
|
||||
new NumberEval(0),
|
||||
};
|
||||
confirmCountA(1, args);
|
||||
|
||||
args = new Eval[] {
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new StringEval(""),
|
||||
};
|
||||
confirmCountA(3, args);
|
||||
|
||||
args = new Eval[] {
|
||||
EvalFactory.createAreaEval("D2:F5", 3, 4),
|
||||
};
|
||||
confirmCountA(12, args);
|
||||
|
||||
args = new Eval[] {
|
||||
EvalFactory.createAreaEval("D1:F5", 3, 5), // 15
|
||||
EvalFactory.createRefEval("A1"),
|
||||
EvalFactory.createAreaEval("A1:F6", 7, 6), // 42
|
||||
new NumberEval(0),
|
||||
};
|
||||
confirmCountA(59, args);
|
||||
}
|
||||
|
||||
public void testCountIf() {
|
||||
|
||||
AreaEval range;
|
||||
ValueEval[] values;
|
||||
|
||||
// when criteria is a boolean value
|
||||
values = new ValueEval[] {
|
||||
new NumberEval(0),
|
||||
new StringEval("TRUE"), // note - does not match boolean TRUE
|
||||
BoolEval.TRUE,
|
||||
BoolEval.FALSE,
|
||||
BoolEval.TRUE,
|
||||
BlankEval.INSTANCE,
|
||||
};
|
||||
range = createAreaEval("A1:B2", values);
|
||||
confirmCountIf(2, range, BoolEval.TRUE);
|
||||
|
||||
// when criteria is numeric
|
||||
values = new ValueEval[] {
|
||||
new NumberEval(0),
|
||||
new StringEval("2"),
|
||||
new StringEval("2.001"),
|
||||
new NumberEval(2),
|
||||
new NumberEval(2),
|
||||
BoolEval.TRUE,
|
||||
BlankEval.INSTANCE,
|
||||
};
|
||||
range = createAreaEval("A1:B2", values);
|
||||
confirmCountIf(3, range, new NumberEval(2));
|
||||
// note - same results when criteria is a string that parses as the number with the same value
|
||||
confirmCountIf(3, range, new StringEval("2.00"));
|
||||
|
||||
if (false) { // not supported yet:
|
||||
// when criteria is an expression (starting with a comparison operator)
|
||||
confirmCountIf(4, range, new StringEval(">1"));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* special case where the criteria argument is a cell reference
|
||||
*/
|
||||
public void testCountIfWithCriteriaReference() {
|
||||
|
||||
ValueEval[] values = {
|
||||
new NumberEval(22),
|
||||
new NumberEval(25),
|
||||
new NumberEval(21),
|
||||
new NumberEval(25),
|
||||
new NumberEval(25),
|
||||
new NumberEval(25),
|
||||
};
|
||||
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
|
||||
|
||||
Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25), true);
|
||||
Eval[] args= { arg0, criteriaArg, };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
assertEquals(4, actual, 0D);
|
||||
}
|
||||
|
||||
|
||||
private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
|
||||
return new Area2DEval(new AreaPtg(areaRefStr), values);
|
||||
}
|
||||
|
||||
private static void confirmCountA(int expected, Eval[] args) {
|
||||
double result = NumericFunctionInvoker.invoke(new Counta(), args);
|
||||
assertEquals(expected, result, 0);
|
||||
}
|
||||
private static void confirmCountIf(int expected, AreaEval range, Eval criteria) {
|
||||
|
||||
Eval[] args = { range, criteria, };
|
||||
double result = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
assertEquals(expected, result, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Tests for the INDEX() function
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestIndex extends TestCase {
|
||||
|
||||
public TestIndex(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
private static final double[] TEST_VALUES0 = {
|
||||
1, 2,
|
||||
3, 4,
|
||||
5, 6,
|
||||
7, 8,
|
||||
9, 10,
|
||||
11, 12,
|
||||
13, // excess array element. TODO - Area2DEval currently has no validation to ensure correct size of values array
|
||||
};
|
||||
|
||||
/**
|
||||
* For the case when the first argument to INDEX() is an area reference
|
||||
*/
|
||||
public void testEvaluateAreaReference() {
|
||||
|
||||
double[] values = TEST_VALUES0;
|
||||
confirmAreaEval("C1:D6", values, 4, 1, 7);
|
||||
confirmAreaEval("C1:D6", values, 6, 2, 12);
|
||||
confirmAreaEval("C1:D6", values, 3, -1, 5);
|
||||
|
||||
// now treat same data as 3 columns, 4 rows
|
||||
confirmAreaEval("C10:E13", values, 2, 2, 5);
|
||||
confirmAreaEval("C10:E13", values, 4, -1, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param areaRefString in Excel notation e.g. 'D2:E97'
|
||||
* @param dValues array of evaluated values for the area reference
|
||||
* @param rowNum 1-based
|
||||
* @param colNum 1-based, pass -1 to signify argument not present
|
||||
*/
|
||||
private static void confirmAreaEval(String areaRefString, double[] dValues,
|
||||
int rowNum, int colNum, double expectedResult) {
|
||||
ValueEval[] values = new ValueEval[dValues.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = new NumberEval(dValues[i]);
|
||||
}
|
||||
Area2DEval arg0 = new Area2DEval(new AreaPtg(areaRefString), values);
|
||||
|
||||
Eval[] args;
|
||||
if (colNum > 0) {
|
||||
args = new Eval[] { arg0, new NumberEval(rowNum), new NumberEval(colNum), };
|
||||
} else {
|
||||
args = new Eval[] { arg0, new NumberEval(rowNum), };
|
||||
}
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Index(), args);
|
||||
assertEquals(expectedResult, actual, 0D);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/* ====================================================================
|
||||
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.formula.functions;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
|
||||
/**
|
||||
* Tests for ROW(), ROWS(), COLUMN(), COLUMNS()
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestRowCol extends TestCase {
|
||||
|
||||
public TestRowCol(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
public void testCol() {
|
||||
Function target = new Column();
|
||||
{
|
||||
Eval[] args = { EvalFactory.createRefEval("C5"), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(3, actual, 0D);
|
||||
}
|
||||
{
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(5, actual, 0D);
|
||||
}
|
||||
}
|
||||
|
||||
public void testRow() {
|
||||
Function target = new Row();
|
||||
{
|
||||
Eval[] args = { EvalFactory.createRefEval("C5"), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(5, actual, 0D);
|
||||
}
|
||||
{
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(2, actual, 0D);
|
||||
}
|
||||
}
|
||||
|
||||
public void testColumns() {
|
||||
|
||||
confirmColumnsFunc("A1:F1", 6, 1);
|
||||
confirmColumnsFunc("A1:C2", 3, 2);
|
||||
confirmColumnsFunc("A1:B3", 2, 3);
|
||||
confirmColumnsFunc("A1:A6", 1, 6);
|
||||
|
||||
Eval[] args = { EvalFactory.createRefEval("C5"), };
|
||||
double actual = NumericFunctionInvoker.invoke(new Columns(), args);
|
||||
assertEquals(1, actual, 0D);
|
||||
}
|
||||
|
||||
public void testRows() {
|
||||
|
||||
confirmRowsFunc("A1:F1", 6, 1);
|
||||
confirmRowsFunc("A1:C2", 3, 2);
|
||||
confirmRowsFunc("A1:B3", 2, 3);
|
||||
confirmRowsFunc("A1:A6", 1, 6);
|
||||
|
||||
Eval[] args = { EvalFactory.createRefEval("C5"), };
|
||||
double actual = NumericFunctionInvoker.invoke(new Rows(), args);
|
||||
assertEquals(1, actual, 0D);
|
||||
}
|
||||
|
||||
private static void confirmRowsFunc(String areaRefStr, int nCols, int nRows) {
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Rows(), args);
|
||||
assertEquals(nRows, actual, 0D);
|
||||
}
|
||||
|
||||
|
||||
private static void confirmColumnsFunc(String areaRefStr, int nCols, int nRows) {
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Columns(), args);
|
||||
assertEquals(nCols, actual, 0D);
|
||||
}
|
||||
}
|
103
src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug44297.java
Executable file
103
src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug44297.java
Executable file
@ -0,0 +1,103 @@
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
/* ====================================================================
|
||||
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.
|
||||
==================================================================== */
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Bug 44297: 32767+32768 is evaluated to -1
|
||||
* Fix: IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation
|
||||
* if a formula has values in the interval [Short.MAX_VALUE, (Short.MAX_VALUE+1)*2]
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
|
||||
public class TestBug44297 extends TestCase {
|
||||
protected String cwd = System.getProperty("HSSF.testdata.path");
|
||||
|
||||
public void test44297() throws IOException {
|
||||
FileInputStream in = new FileInputStream(new File(cwd, "44297.xls"));
|
||||
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||
in.close();
|
||||
|
||||
HSSFRow row;
|
||||
HSSFCell cell;
|
||||
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
|
||||
HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(0);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("31+46", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(77, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(1);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("30+53", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(83, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(2);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("SUM(A1:A2)", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(160, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(4);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("32767+32768", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(65535, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(7);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("32744+42333", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(75077, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(8);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("327680.0/32768", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(10, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(9);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("32767+32769", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(65536, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(10);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("35000+36000", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(71000, eva.evaluate(cell).getNumberValue(), 0);
|
||||
|
||||
row = (HSSFRow)sheet.getRow(11);
|
||||
cell = row.getCell((short)0);
|
||||
assertEquals("-1000000.0-3000000.0", cell.getCellFormula());
|
||||
eva.setCurrentRow(row);
|
||||
assertEquals(-4000000, eva.evaluate(cell).getNumberValue(), 0);
|
||||
}
|
||||
|
||||
}
|
@ -82,7 +82,7 @@ public class TestFormulaEvaluatorDocs extends TestCase {
|
||||
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(0).getRow(1).getCell((short)2).getCellType());
|
||||
|
||||
assertEquals(22.3, wb.getSheetAt(1).getRow(0).getCell((short)0).getNumericCellValue(), 0);
|
||||
assertEquals("S1!A1", wb.getSheetAt(1).getRow(0).getCell((short)0).getCellFormula());
|
||||
assertEquals("'S1'!A1", wb.getSheetAt(1).getRow(0).getCell((short)0).getCellFormula());
|
||||
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(1).getRow(0).getCell((short)0).getCellType());
|
||||
|
||||
|
||||
|
BIN
src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44292.doc
Normal file
BIN
src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44292.doc
Normal file
Binary file not shown.
@ -74,4 +74,34 @@ public class TestProblems extends TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for TableCell not skipping the last paragraph
|
||||
*/
|
||||
public void testTableCellLastParagraph() throws Exception {
|
||||
HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/Bug44292.doc"));
|
||||
Range r = doc.getRange();
|
||||
|
||||
//get the table
|
||||
Paragraph p = r.getParagraph(0);
|
||||
Table t = r.getTable(p);
|
||||
|
||||
//get the only row
|
||||
TableRow row = t.getRow(0);
|
||||
|
||||
//get the first cell
|
||||
TableCell cell = row.getCell(0);
|
||||
// First cell should have one paragraph
|
||||
assertEquals(1, cell.numParagraphs());
|
||||
|
||||
//get the second
|
||||
cell = row.getCell(1);
|
||||
// Second cell should be detected as having two paragraphs
|
||||
assertEquals(2, cell.numParagraphs());
|
||||
|
||||
//get the last cell
|
||||
cell = row.getCell(2);
|
||||
// Last cell should have one paragraph
|
||||
assertEquals(1, cell.numParagraphs());
|
||||
}
|
||||
}
|
||||
|
@ -75,13 +75,7 @@ import org.apache.poi.hssf.record.TestUnitsRecord;
|
||||
import org.apache.poi.hssf.record.TestValueRangeRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.TestRowRecordsAggregate;
|
||||
import org.apache.poi.hssf.record.aggregates.TestValueRecordsAggregate;
|
||||
import org.apache.poi.hssf.record.formula.TestAreaErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.TestErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.TestFuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.TestIntersectionPtg;
|
||||
import org.apache.poi.hssf.record.formula.TestPercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.TestRangePtg;
|
||||
import org.apache.poi.hssf.record.formula.TestUnionPtg;
|
||||
import org.apache.poi.hssf.record.formula.AllFormulaTests;
|
||||
import org.apache.poi.hssf.usermodel.TestBugs;
|
||||
import org.apache.poi.hssf.usermodel.TestCellStyle;
|
||||
import org.apache.poi.hssf.usermodel.TestCloneSheet;
|
||||
@ -215,13 +209,7 @@ public class HSSFTests
|
||||
suite.addTest(new TestSuite(TestSheetReferences.class));
|
||||
|
||||
|
||||
suite.addTest(new TestSuite(TestAreaErrPtg.class));
|
||||
suite.addTest(new TestSuite(TestErrPtg.class));
|
||||
suite.addTest(new TestSuite(TestFuncPtg.class));
|
||||
suite.addTest(new TestSuite(TestIntersectionPtg.class));
|
||||
suite.addTest(new TestSuite(TestPercentPtg.class));
|
||||
suite.addTest(new TestSuite(TestRangePtg.class));
|
||||
suite.addTest(new TestSuite(TestUnionPtg.class));
|
||||
suite.addTest(AllFormulaTests.suite());
|
||||
suite.addTest(new TestSuite(TestValueRecordsAggregate.class));
|
||||
suite.addTest(new TestSuite(TestNameRecord.class));
|
||||
suite.addTest(new TestSuite(TestEventRecordFactory.class));
|
||||
|
BIN
src/testcases/org/apache/poi/hssf/data/HyperlinksOnManySheets.xls
Executable file
BIN
src/testcases/org/apache/poi/hssf/data/HyperlinksOnManySheets.xls
Executable file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/SimpleWithChoose.xls
Executable file
BIN
src/testcases/org/apache/poi/hssf/data/SimpleWithChoose.xls
Executable file
Binary file not shown.
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/WithHyperlink.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/WithHyperlink.xls
Normal file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/WithTwoHyperLinks.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/WithTwoHyperLinks.xls
Normal file
Binary file not shown.
@ -23,8 +23,13 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.poi.hssf.record.DVALRecord;
|
||||
import org.apache.poi.hssf.record.DVRecord;
|
||||
import org.apache.poi.hssf.record.EOFRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.ContinueRecord;
|
||||
import org.apache.poi.hssf.record.SelectionRecord;
|
||||
import org.apache.poi.hssf.record.WindowTwoRecord;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@ -48,7 +53,15 @@ public class TestHSSFEventFactory extends TestCase {
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
|
||||
// Check we got the records
|
||||
System.out.println("Processed, found " + mockListen.records.size() + " records");
|
||||
assertTrue( mockListen.records.size() > 100 );
|
||||
|
||||
// Check that the last few records are as we expect
|
||||
// (Makes sure we don't accidently skip the end ones)
|
||||
int numRec = mockListen.records.size();
|
||||
assertEquals(WindowTwoRecord.class, mockListen.records.get(numRec-3).getClass());
|
||||
assertEquals(SelectionRecord.class, mockListen.records.get(numRec-2).getClass());
|
||||
assertEquals(EOFRecord.class, mockListen.records.get(numRec-1).getClass());
|
||||
}
|
||||
|
||||
public void testWithCrazyContinueRecords() throws Exception {
|
||||
@ -66,6 +79,7 @@ public class TestHSSFEventFactory extends TestCase {
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
|
||||
// Check we got the records
|
||||
System.out.println("Processed, found " + mockListen.records.size() + " records");
|
||||
assertTrue( mockListen.records.size() > 100 );
|
||||
|
||||
// And none of them are continue ones
|
||||
@ -74,6 +88,13 @@ public class TestHSSFEventFactory extends TestCase {
|
||||
for(int i=0; i<r.length; i++) {
|
||||
assertFalse( r[i] instanceof ContinueRecord );
|
||||
}
|
||||
|
||||
// Check that the last few records are as we expect
|
||||
// (Makes sure we don't accidently skip the end ones)
|
||||
int numRec = mockListen.records.size();
|
||||
assertEquals(DVALRecord.class, mockListen.records.get(numRec-3).getClass());
|
||||
assertEquals(DVRecord.class, mockListen.records.get(numRec-2).getClass());
|
||||
assertEquals(EOFRecord.class, mockListen.records.get(numRec-1).getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,15 +379,16 @@ public class TestFormulaParser extends TestCase {
|
||||
fp = new FormulaParser("40000", null);
|
||||
fp.parse();
|
||||
ptg=fp.getRPNPtg();
|
||||
assertTrue("ptg should be Number, is "+ptg[0].getClass(), ptg[0] instanceof NumberPtg);
|
||||
assertTrue("ptg should be IntPtg, is "+ptg[0].getClass(), ptg[0] instanceof IntPtg);
|
||||
}
|
||||
|
||||
/** bug 33160, testcase by Amol Deshmukh*/
|
||||
public void testSimpleLongFormula() {
|
||||
FormulaParser fp = new FormulaParser("40000/2", null);
|
||||
fp.parse();
|
||||
Ptg[] ptgs = fp.getRPNPtg();
|
||||
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
|
||||
assertTrue("NumberPtg",(ptgs[0] instanceof NumberPtg));
|
||||
assertTrue("IntPtg",(ptgs[0] instanceof IntPtg));
|
||||
assertTrue("IntPtg",(ptgs[1] instanceof IntPtg));
|
||||
assertTrue("DividePtg",(ptgs[2] instanceof DividePtg));
|
||||
}
|
||||
|
@ -21,6 +21,16 @@
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.ConcatPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.RangePtg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.record.formula.UnknownPtg;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
@ -108,6 +118,52 @@ public class TestFormulaRecord
|
||||
assertEquals("Offset 22", 1, output[26]);
|
||||
}
|
||||
|
||||
public void testWithConcat() throws Exception {
|
||||
// =CHOOSE(2,A2,A3,A4)
|
||||
byte[] data = new byte[] {
|
||||
6, 0, 68, 0,
|
||||
1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57,
|
||||
64, 0, 0, 12, 0, 12, -4, 46, 0,
|
||||
30, 2, 0, // Int - 2
|
||||
25, 4, 3, 0, // Attr
|
||||
8, 0, // Concat
|
||||
17, 0, // Range
|
||||
26, 0, 35, 0, // Bit like an attr
|
||||
36, 1, 0, 0, -64, // Ref - A2
|
||||
25, 8, 21, 0, // Attr
|
||||
36, 2, 0, 0, -64, // Ref - A3
|
||||
25, 8, 12, 0, // Attr
|
||||
36, 3, 0, 0, -64, // Ref - A4
|
||||
25, 8, 3, 0, // Attr
|
||||
66, 4, 100, 0 // CHOOSE
|
||||
};
|
||||
RecordInputStream inp = new RecordInputStream(
|
||||
new ByteArrayInputStream(data)
|
||||
);
|
||||
inp.nextRecord();
|
||||
|
||||
FormulaRecord fr = new FormulaRecord(inp);
|
||||
|
||||
assertEquals(14, fr.getNumberOfExpressionTokens());
|
||||
assertEquals(IntPtg.class, fr.getParsedExpression().get(0).getClass());
|
||||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(1).getClass());
|
||||
assertEquals(ConcatPtg.class, fr.getParsedExpression().get(2).getClass());
|
||||
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(3).getClass());
|
||||
assertEquals(RangePtg.class, fr.getParsedExpression().get(4).getClass());
|
||||
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(5).getClass());
|
||||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(6).getClass());
|
||||
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(7).getClass());
|
||||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(8).getClass());
|
||||
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(9).getClass());
|
||||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(10).getClass());
|
||||
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(11).getClass());
|
||||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(12).getClass());
|
||||
assertEquals(FuncVarPtg.class, fr.getParsedExpression().get(13).getClass());
|
||||
|
||||
FuncVarPtg choose = (FuncVarPtg)fr.getParsedExpression().get(13);
|
||||
assertEquals("CHOOSE", choose.getName());
|
||||
}
|
||||
|
||||
|
||||
public static void main(String [] ignored_args)
|
||||
{
|
||||
|
@ -0,0 +1,131 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TestHyperlinkRecord extends TestCase {
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
private byte[] data = new byte[] {
|
||||
-72, 1, 110, 0,
|
||||
// ??, Row, col, xf
|
||||
6, 0, 3, 0, 2, 0, 2, 0,
|
||||
|
||||
// ??
|
||||
-48, -55, -22, 121, -7, -70, -50, 17,
|
||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
||||
2, 0, 0, 0,
|
||||
|
||||
// URL length
|
||||
23, 0, 0, 0,
|
||||
|
||||
// Label length
|
||||
4, 0, 0, 0,
|
||||
|
||||
// Label
|
||||
76, 0, 44, 0, 65, 0, 0, 0,
|
||||
|
||||
// ??
|
||||
-32, -55, -22, 121, -7, -70, -50, 17,
|
||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
||||
46, 0, 0, 0,
|
||||
|
||||
// URL
|
||||
104, 0, 116, 0, 116, 0, 112, 0, 58, 0, 47, 0, 47, 0, 119,
|
||||
0, 119, 0, 119, 0, 46, 0, 108, 0, 97, 0, 107, 0, 105,
|
||||
0, 110, 0, 103, 0, 115, 0, 46, 0, 99, 0, 111, 0,
|
||||
109, 0,
|
||||
0, 0 };
|
||||
|
||||
private byte[] data2 = new byte[] {
|
||||
-72, 1, -126, 0,
|
||||
// ??, Row, col, xf
|
||||
2, 0, 2, 0, 4, 0, 4, 0,
|
||||
|
||||
// ??
|
||||
-48, -55, -22, 121, -7, -70, -50, 17,
|
||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
||||
2, 0, 0, 0,
|
||||
|
||||
// URL and Label lengths
|
||||
23, 0, 0, 0,
|
||||
15, 0, 0, 0,
|
||||
|
||||
// Label
|
||||
83, 0, 116, 0, 97, 0, 99, 0, 105, 0,
|
||||
101, 0, 64, 0, 65, 0, 66, 0, 67, 0,
|
||||
46, 0, 99, 0, 111, 0, 109, 0, 0, 0,
|
||||
|
||||
// ??
|
||||
-32, -55, -22, 121, -7, -70, -50, 17,
|
||||
-116, -126, 0, -86, 0, 75, -87, 11,
|
||||
44, 0, 0, 0,
|
||||
|
||||
// URL
|
||||
109, 0, 97, 0, 105, 0, 108, 0, 116, 0,
|
||||
111, 0, 58, 0, 83, 0, 116, 0, 97, 0,
|
||||
99, 0, 105, 0, 101, 0, 64, 0, 65, 0,
|
||||
66, 0, 67, 0, 46, 0, 99, 0, 111, 0,
|
||||
109, 0, 0, 0 };
|
||||
|
||||
public void testRecordParsing() throws Exception {
|
||||
RecordInputStream inp = new RecordInputStream(
|
||||
new ByteArrayInputStream(data)
|
||||
);
|
||||
inp.nextRecord();
|
||||
|
||||
HyperlinkRecord r = new HyperlinkRecord(inp);
|
||||
|
||||
assertEquals(3, r.getRow());
|
||||
assertEquals(2, r.getColumn());
|
||||
assertEquals(2, r.getXFIndex());
|
||||
|
||||
assertEquals("L,A", r.getLabel());
|
||||
assertEquals("http://www.lakings.com", r.getUrlString());
|
||||
assertEquals(new URL("http://www.lakings.com"), r.getUrl());
|
||||
|
||||
// Check it serialises as expected
|
||||
assertEquals(data.length, r.getRecordSize());
|
||||
byte[] d = r.serialize();
|
||||
assertEquals(data.length, d.length);
|
||||
for(int i=0; i<data.length; i++) {
|
||||
assertEquals(data[i], d[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSecondRecord() throws Exception {
|
||||
RecordInputStream inp = new RecordInputStream(
|
||||
new ByteArrayInputStream(data2)
|
||||
);
|
||||
inp.nextRecord();
|
||||
|
||||
HyperlinkRecord r = new HyperlinkRecord(inp);
|
||||
|
||||
assertEquals(2, r.getRow());
|
||||
assertEquals(4, r.getColumn());
|
||||
assertEquals(4, r.getXFIndex());
|
||||
|
||||
assertEquals("Stacie@ABC.com", r.getLabel());
|
||||
assertEquals("mailto:Stacie@ABC.com", r.getUrlString());
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import java.io.InputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
@ -34,7 +35,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
*
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public class AbstractPtgTestCase extends TestCase
|
||||
public abstract class AbstractPtgTestCase extends TestCase
|
||||
{
|
||||
/** Directory containing the test data. */
|
||||
private static String dataDir = System.getProperty("HSSF.testdata.path");
|
||||
@ -46,7 +47,7 @@ public class AbstractPtgTestCase extends TestCase
|
||||
* @return the loaded workbook.
|
||||
* @throws IOException if an error occurs loading the workbook.
|
||||
*/
|
||||
protected static HSSFWorkbook loadWorkbook(String filename)
|
||||
protected static final HSSFWorkbook loadWorkbook(String filename)
|
||||
throws IOException {
|
||||
File file = new File(dataDir, filename);
|
||||
InputStream stream = new BufferedInputStream(new FileInputStream(file));
|
||||
@ -59,4 +60,18 @@ public class AbstractPtgTestCase extends TestCase
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Workbook and adds one sheet with the specified name
|
||||
*/
|
||||
protected static final Workbook createWorkbookWithSheet(String sheetName) {
|
||||
|
||||
Workbook book = Workbook.createWorkbook();
|
||||
// this creates sheet if it doesn't exist
|
||||
book.checkExternSheet(0);
|
||||
// TODO - this call alone does not create the sheet even though the javadoc says it does
|
||||
book.setSheetName(0, sheetName);
|
||||
return book;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Collects all tests for this package.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class AllFormulaTests {
|
||||
|
||||
public static Test suite() {
|
||||
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula");
|
||||
result.addTestSuite(TestArea3DPtg.class);
|
||||
result.addTestSuite(TestAreaErrPtg.class);
|
||||
result.addTestSuite(TestAreaPtg.class);
|
||||
result.addTestSuite(TestErrPtg.class);
|
||||
result.addTestSuite(TestFuncPtg.class);
|
||||
result.addTestSuite(TestIntersectionPtg.class);
|
||||
result.addTestSuite(TestPercentPtg.class);
|
||||
result.addTestSuite(TestRangePtg.class);
|
||||
result.addTestSuite(TestRef3DPtg.class);
|
||||
result.addTestSuite(TestReferencePtg.class);
|
||||
result.addTestSuite(TestSheetNameFormatter.class);
|
||||
result.addTestSuite(TestUnionPtg.class);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
|
||||
/**
|
||||
* Tests for Area3DPtg
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestArea3DPtg extends AbstractPtgTestCase {
|
||||
|
||||
/**
|
||||
* confirms that sheet names get properly escaped
|
||||
*/
|
||||
public void testToFormulaString() {
|
||||
|
||||
Area3DPtg target = new Area3DPtg("A1:B1", (short)0);
|
||||
|
||||
String sheetName = "my sheet";
|
||||
Workbook book = createWorkbookWithSheet(sheetName);
|
||||
assertEquals("'my sheet'!A1:B1", target.toFormulaString(book));
|
||||
|
||||
book.setSheetName(0, "Sheet1");
|
||||
assertEquals("Sheet1!A1:B1", target.toFormulaString(book));
|
||||
|
||||
book.setSheetName(0, "C64");
|
||||
assertEquals("'C64'!A1:B1", target.toFormulaString(book));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
|
||||
/**
|
||||
* Tests for {@link AreaPtg}.
|
||||
*
|
||||
* @author Dmitriy Kumshayev
|
||||
*/
|
||||
public class TestAreaPtg extends TestCase
|
||||
{
|
||||
|
||||
AreaPtg relative;
|
||||
AreaPtg absolute;
|
||||
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
short firstRow=5;
|
||||
short lastRow=13;
|
||||
short firstCol=7;
|
||||
short lastCol=17;
|
||||
relative = new AreaPtg(firstRow,lastRow,firstCol,lastCol,true,true,true,true);
|
||||
absolute = new AreaPtg(firstRow,lastRow,firstCol,lastCol,false,false,false,false);
|
||||
}
|
||||
|
||||
public void testSetColumnsAbsolute()
|
||||
{
|
||||
resetColumns(absolute);
|
||||
validateReference(true, absolute);
|
||||
}
|
||||
public void testSetColumnsRelative()
|
||||
{
|
||||
resetColumns(relative);
|
||||
validateReference(false, relative);
|
||||
}
|
||||
|
||||
private void validateReference(boolean abs, AreaPtg ref)
|
||||
{
|
||||
assertEquals("First column reference is not "+(abs?"absolute":"relative"),abs,!ref.isFirstColRelative());
|
||||
assertEquals("Last column reference is not "+(abs?"absolute":"relative"),abs,!ref.isLastColRelative());
|
||||
assertEquals("First row reference is not "+(abs?"absolute":"relative"),abs,!ref.isFirstRowRelative());
|
||||
assertEquals("Last row reference is not "+(abs?"absolute":"relative"),abs,!ref.isLastRowRelative());
|
||||
}
|
||||
|
||||
|
||||
public void resetColumns(AreaPtg aptg)
|
||||
{
|
||||
short fc = aptg.getFirstColumn();
|
||||
short lc = aptg.getLastColumn();
|
||||
aptg.setFirstColumn(fc);
|
||||
aptg.setLastColumn(lc);
|
||||
assertEquals(fc , aptg.getFirstColumn() );
|
||||
assertEquals(lc , aptg.getLastColumn() );
|
||||
}
|
||||
|
||||
public void testFormulaParser()
|
||||
{
|
||||
String formula1="SUM($E$5:$E$6)";
|
||||
String expectedFormula1="SUM($F$5:$F$6)";
|
||||
String newFormula1 = shiftAllColumnsBy1(formula1);
|
||||
assertEquals("Absolute references changed", expectedFormula1, newFormula1);
|
||||
|
||||
String formula2="SUM(E5:E6)";
|
||||
String expectedFormula2="SUM(F5:F6)";
|
||||
String newFormula2 = shiftAllColumnsBy1(formula2);
|
||||
assertEquals("Relative references changed", expectedFormula2, newFormula2);
|
||||
}
|
||||
|
||||
private String shiftAllColumnsBy1(String formula)
|
||||
{
|
||||
int letUsShiftColumn1By1Column=1;
|
||||
|
||||
FormulaParser parser = new FormulaParser(formula,null);
|
||||
parser.parse();
|
||||
|
||||
final Ptg[] ptgs = parser.getRPNPtg();
|
||||
for(int i=0; i<ptgs.length; i++)
|
||||
{
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof AreaPtg )
|
||||
{
|
||||
AreaPtg aptg = (AreaPtg)ptg;
|
||||
aptg.setFirstColumn((short)(aptg.getFirstColumn()+letUsShiftColumn1By1Column));
|
||||
aptg.setLastColumn((short)(aptg.getLastColumn()+letUsShiftColumn1By1Column));
|
||||
}
|
||||
}
|
||||
String newFormula = parser.toFormulaString(ptgs);
|
||||
return newFormula;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
0
src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java
Executable file → Normal file
0
src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java
Executable file → Normal file
@ -0,0 +1,44 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
|
||||
/**
|
||||
* Tests for Ref3DPtg
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestRef3DPtg extends AbstractPtgTestCase {
|
||||
|
||||
public void testToFormulaString() {
|
||||
|
||||
Ref3DPtg target = new Ref3DPtg("A1", (short)0);
|
||||
|
||||
Workbook book = createWorkbookWithSheet("my sheet");
|
||||
|
||||
assertEquals("'my sheet'!A1", target.toFormulaString(book));
|
||||
|
||||
book.setSheetName(0, "ProfitAndLoss");
|
||||
assertEquals("ProfitAndLoss!A1", target.toFormulaString(book));
|
||||
|
||||
book.setSheetName(0, "profit+loss");
|
||||
assertEquals("'profit+loss'!A1", target.toFormulaString(book));
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/* ====================================================================
|
||||
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.formula;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for SheetNameFormatter
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestSheetNameFormatter extends TestCase {
|
||||
|
||||
public TestSheetNameFormatter(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
private static void confirmFormat(String rawSheetName, String expectedSheetNameEncoding) {
|
||||
assertEquals(expectedSheetNameEncoding, SheetNameFormatter.format(rawSheetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests main public method 'format'
|
||||
*/
|
||||
public void testFormat() {
|
||||
|
||||
confirmFormat("abc", "abc");
|
||||
confirmFormat("123", "'123'");
|
||||
|
||||
confirmFormat("my sheet", "'my sheet'"); // space
|
||||
confirmFormat("A:MEM", "'A:MEM'"); // colon
|
||||
|
||||
confirmFormat("O'Brian", "'O''Brian'"); // single quote gets doubled
|
||||
|
||||
|
||||
confirmFormat("3rdTimeLucky", "'3rdTimeLucky'"); // digit in first pos
|
||||
confirmFormat("_", "_"); // plain underscore OK
|
||||
confirmFormat("my_3rd_sheet", "my_3rd_sheet"); // underscores and digits OK
|
||||
confirmFormat("A12220", "'A12220'");
|
||||
confirmFormat("TAXRETURN19980415", "TAXRETURN19980415");
|
||||
}
|
||||
|
||||
private static void confirmCellNameMatch(String rawSheetName, boolean expected) {
|
||||
assertEquals(expected, SheetNameFormatter.nameLooksLikePlainCellReference(rawSheetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests functionality to determine whether a sheet name containing only letters and digits
|
||||
* would look (to Excel) like a cell name.
|
||||
*/
|
||||
public void testLooksLikePlainCellReference() {
|
||||
|
||||
confirmCellNameMatch("A1", true);
|
||||
confirmCellNameMatch("a111", true);
|
||||
confirmCellNameMatch("AA", false);
|
||||
confirmCellNameMatch("aa1", true);
|
||||
confirmCellNameMatch("A1A", false);
|
||||
confirmCellNameMatch("A1A1", false);
|
||||
confirmCellNameMatch("SALES20080101", false); // out of range
|
||||
}
|
||||
|
||||
private static void confirmCellRange(String text, int numberOfPrefixLetters, boolean expected) {
|
||||
assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(text, numberOfPrefixLetters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exact boundaries for names that look very close to cell names (i.e. contain 1 or more
|
||||
* letters followed by one or more digits).
|
||||
*/
|
||||
public void testCellRange() {
|
||||
confirmCellRange("A1", 1, true);
|
||||
confirmCellRange("a111", 1, true);
|
||||
confirmCellRange("A65536", 1, true);
|
||||
confirmCellRange("A65537", 1, false);
|
||||
confirmCellRange("iv1", 2, true);
|
||||
confirmCellRange("IW1", 2, false);
|
||||
confirmCellRange("AAA1", 3, false);
|
||||
confirmCellRange("a111", 1, true);
|
||||
confirmCellRange("Sheet1", 6, false);
|
||||
confirmCellRange("iV65536", 2, true); // max cell in Excel 97-2003
|
||||
confirmCellRange("IW65537", 2, false);
|
||||
}
|
||||
}
|
@ -1008,9 +1008,45 @@ extends TestCase {
|
||||
|
||||
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||
assertTrue("No Exceptions while reading file", true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 42618: RecordFormatException reading a file containing
|
||||
* =CHOOSE(2,A2,A3,A4)
|
||||
* TODO - support getCellFormula too!
|
||||
*/
|
||||
public void test42618() throws Exception {
|
||||
FileInputStream in = new FileInputStream(new File(cwd, "SimpleWithChoose.xls"));
|
||||
HSSFWorkbook wb = new HSSFWorkbook(in);
|
||||
in.close();
|
||||
|
||||
assertTrue("No Exceptions while reading file", true);
|
||||
|
||||
//serialize and read again
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
|
||||
assertTrue("No Exceptions while reading file", true);
|
||||
|
||||
// Check we detect the string properly too
|
||||
HSSFSheet s = wb.getSheetAt(0);
|
||||
|
||||
// Textual value
|
||||
HSSFRow r1 = s.getRow(0);
|
||||
HSSFCell c1 = r1.getCell((short)1);
|
||||
assertEquals("=CHOOSE(2,A2,A3,A4)", c1.getRichStringCellValue().toString());
|
||||
|
||||
// Formula Value
|
||||
HSSFRow r2 = s.getRow(1);
|
||||
HSSFCell c2 = r2.getCell((short)1);
|
||||
assertEquals(25, (int)c2.getNumericCellValue());
|
||||
|
||||
// This will blow up with a
|
||||
// "EmptyStackException"
|
||||
//assertEquals("=CHOOSE(2,A2,A3,A4)", c2.getCellFormula());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,25 +19,20 @@
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.hssf.model.Sheet;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.BOFRecord;
|
||||
import org.apache.poi.hssf.record.EOFRecord;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.util.TempFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.Sheet;
|
||||
import org.apache.poi.hssf.record.HyperlinkRecord;
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.util.TempFile;
|
||||
|
||||
/**
|
||||
* Tests various functionity having to do with HSSFCell. For instance support for
|
||||
@ -308,7 +303,54 @@ extends TestCase {
|
||||
assertTrue("Bottom Border", (cs.getBorderBottom() == (short)1));
|
||||
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading hyperlinks
|
||||
*/
|
||||
public void testWithHyperlink() throws Exception {
|
||||
String dir = System.getProperty("HSSF.testdata.path");
|
||||
File f = new File(dir, "WithHyperlink.xls");
|
||||
HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(f));
|
||||
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
HSSFCell cell = sheet.getRow(4).getCell((short)0);
|
||||
HSSFHyperlink link = cell.getHyperlink();
|
||||
assertNotNull(link);
|
||||
|
||||
assertEquals("Foo", link.getLabel());
|
||||
assertEquals("http://poi.apache.org/", link.getAddress());
|
||||
assertEquals(4, link.getRow());
|
||||
assertEquals(0, link.getColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading hyperlinks
|
||||
*/
|
||||
public void testWithTwoHyperlinks() throws Exception {
|
||||
String dir = System.getProperty("HSSF.testdata.path");
|
||||
File f = new File(dir, "WithTwoHyperLinks.xls");
|
||||
HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(f));
|
||||
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
|
||||
HSSFCell cell1 = sheet.getRow(4).getCell((short)0);
|
||||
HSSFHyperlink link1 = cell1.getHyperlink();
|
||||
assertNotNull(link1);
|
||||
assertEquals("Foo", link1.getLabel());
|
||||
assertEquals("http://poi.apache.org/", link1.getAddress());
|
||||
assertEquals(4, link1.getRow());
|
||||
assertEquals(0, link1.getColumn());
|
||||
|
||||
HSSFCell cell2 = sheet.getRow(8).getCell((short)1);
|
||||
HSSFHyperlink link2 = cell2.getHyperlink();
|
||||
assertNotNull(link2);
|
||||
assertEquals("Bar", link2.getLabel());
|
||||
assertEquals("http://poi.apache.org/", link2.getAddress());
|
||||
assertEquals(8, link2.getRow());
|
||||
assertEquals(1, link2.getColumn());
|
||||
|
||||
}
|
||||
|
||||
/*tests the toString() method of HSSFCell*/
|
||||
public void testToString() throws Exception {
|
||||
|
@ -351,7 +351,7 @@ public class TestNamedRange
|
||||
String retrievedPrintArea = workbook.getPrintArea(0);
|
||||
|
||||
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
|
||||
assertEquals(reference, retrievedPrintArea);
|
||||
assertEquals("'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
|
||||
|
||||
}
|
||||
|
||||
@ -370,7 +370,7 @@ public class TestNamedRange
|
||||
String retrievedPrintArea = workbook.getPrintArea(0);
|
||||
|
||||
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
|
||||
assertEquals(sheetName+"!"+reference, retrievedPrintArea);
|
||||
assertEquals("'" + sheetName + "'!" + reference, retrievedPrintArea);
|
||||
|
||||
}
|
||||
|
||||
@ -437,7 +437,7 @@ public class TestNamedRange
|
||||
|
||||
String retrievedPrintArea = workbook.getPrintArea(0);
|
||||
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
|
||||
assertEquals("References Match", reference, retrievedPrintArea);
|
||||
assertEquals("References Match", "'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
|
||||
|
||||
}
|
||||
|
||||
@ -449,9 +449,9 @@ public class TestNamedRange
|
||||
{
|
||||
HSSFWorkbook workbook = new HSSFWorkbook();
|
||||
|
||||
HSSFSheet sheet = workbook.createSheet("Sheet 1");
|
||||
sheet = workbook.createSheet("Sheet 2");
|
||||
sheet = workbook.createSheet("Sheet 3");
|
||||
HSSFSheet sheet = workbook.createSheet("Sheet1");
|
||||
sheet = workbook.createSheet("Sheet2");
|
||||
sheet = workbook.createSheet("Sheet3");
|
||||
|
||||
String sheetName = workbook.getSheetName(0);
|
||||
String reference = null;
|
||||
@ -508,9 +508,30 @@ public class TestNamedRange
|
||||
String retrievedPrintArea = workbook.getPrintArea(0);
|
||||
|
||||
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
|
||||
assertEquals(reference, retrievedPrintArea);
|
||||
assertEquals("'" + sheetName + "'!$A$1:$B$1", retrievedPrintArea);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests the parsing of union area expressions, and re-display in the presence of sheet names
|
||||
* with special characters.
|
||||
*/
|
||||
public void testPrintAreaUnion(){
|
||||
HSSFWorkbook workbook = new HSSFWorkbook();
|
||||
HSSFSheet sheet = workbook.createSheet("Test Print Area");
|
||||
String sheetName = workbook.getSheetName(0);
|
||||
|
||||
|
||||
String reference = sheetName + "!$A$1:$B$1, " + sheetName + "!$D$1:$F$2";
|
||||
String expResult = "'" + sheetName + "'!$A$1:$B$1,'" + sheetName + "'!$D$1:$F$2";
|
||||
workbook.setPrintArea(0, reference);
|
||||
|
||||
String retrievedPrintArea = workbook.getPrintArea(0);
|
||||
|
||||
assertNotNull("Print Area not defined for first sheet", retrievedPrintArea);
|
||||
assertEquals(expResult, retrievedPrintArea);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies an existing print area is deleted
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user