mirror of https://github.com/apache/poi.git
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684883,684885-684937,684940-684958,684960-684970,684972-684985,684987-685053,685055-685063,685065-685259,685261-685262,685264-685266,685268-685282,685285-686035,686037-686045,686047-686052,686054-686206,686208-686215,686217-686277,686279-686289,686291-686620,686622-686623,686626-686627,686629-686639,686641-686843,686845-686976,686978-687402,687404-687422,687424-687428,687430-687442,687444-688425,688427-688641,688643-688649,688651-688654,688656-688824,688826-688909,688911-689543,689545-689558,689560-689635,689637-689703,689705-689715,689717-689718,689720,689722-690280 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r689973 | josh | 2008-08-28 21:39:41 +0100 (Thu, 28 Aug 2008) | 1 line Consolidated TableRecord inside FormulaRecordAggregate. Simplifications to FormulaRecord ........ r690091 | josh | 2008-08-29 04:25:23 +0100 (Fri, 29 Aug 2008) | 1 line Changed FormulaRecord.getParsedExpression to return Ptg array ........ r690094 | josh | 2008-08-29 04:52:51 +0100 (Fri, 29 Aug 2008) | 1 line Removing calls to AreaEval.getValues() ........ r690112 | josh | 2008-08-29 06:29:56 +0100 (Fri, 29 Aug 2008) | 1 line Removing calls to AreaEval.getValues() from count and lookup functions ........ r690259 | yegor | 2008-08-29 14:58:56 +0100 (Fri, 29 Aug 2008) | 1 line utility to dump POIFS into filesystem ........ r690262 | yegor | 2008-08-29 15:01:04 +0100 (Fri, 29 Aug 2008) | 1 line initial support for embedded movies and controls. ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@690299 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9958668438
commit
add1569750
|
@ -15,25 +15,21 @@
|
|||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
* FormulaViewer.java - finds formulas in a BIFF8 file and attempts to parse them and
|
||||
* display info about them.
|
||||
*
|
||||
* Created on November 18, 2001, 7:58 AM
|
||||
*/
|
||||
package org.apache.poi.hssf.dev;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
|
||||
//import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.hssf.record.*;
|
||||
import org.apache.poi.hssf.record.formula.*;
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RecordFactory;
|
||||
import org.apache.poi.hssf.record.formula.ExpPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.hssf.model.*;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
|
||||
/**
|
||||
* FormulaViewer - finds formulas in a BIFF8 file and attempts to read them/display
|
||||
|
@ -87,20 +83,21 @@ public class FormulaViewer
|
|||
|
||||
private void listFormula(FormulaRecord record) {
|
||||
String sep="~";
|
||||
List tokens= record.getParsedExpression();
|
||||
int numptgs = record.getNumberOfExpressionTokens();
|
||||
Ptg token = null;
|
||||
String name,numArg;
|
||||
if (tokens != null) {
|
||||
token = (Ptg) tokens.get(numptgs-1);
|
||||
Ptg[] tokens= record.getParsedExpression();
|
||||
Ptg token;
|
||||
int numptgs = tokens.length;
|
||||
String numArg;
|
||||
token = tokens[numptgs-1];
|
||||
if (token instanceof FuncPtg) {
|
||||
numArg = String.valueOf(numptgs-1);
|
||||
} else { numArg = String.valueOf(-1);}
|
||||
} else {
|
||||
numArg = String.valueOf(-1);
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
if (token instanceof ExpPtg) return;
|
||||
buf.append(name=((OperationPtg) token).toFormulaString((HSSFWorkbook)null));
|
||||
buf.append(((OperationPtg) token).toFormulaString((HSSFWorkbook)null));
|
||||
buf.append(sep);
|
||||
switch (token.getPtgClass()) {
|
||||
case Ptg.CLASS_REF :
|
||||
|
@ -116,7 +113,7 @@ public class FormulaViewer
|
|||
|
||||
buf.append(sep);
|
||||
if (numptgs>1) {
|
||||
token = (Ptg) tokens.get(numptgs-2);
|
||||
token = tokens[numptgs-2];
|
||||
switch (token.getPtgClass()) {
|
||||
case Ptg.CLASS_REF :
|
||||
buf.append("REF");
|
||||
|
@ -134,9 +131,6 @@ public class FormulaViewer
|
|||
buf.append(sep);
|
||||
buf.append(numArg);
|
||||
System.out.println(buf.toString());
|
||||
} else {
|
||||
System.out.println("#NAME");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,20 +149,18 @@ public class FormulaViewer
|
|||
System.out.println("value = " + record.getValue());
|
||||
System.out.print("xf = " + record.getXFIndex());
|
||||
System.out.print(", number of ptgs = "
|
||||
+ record.getNumberOfExpressionTokens());
|
||||
+ record.getParsedExpression().length);
|
||||
System.out.println(", options = " + record.getOptions());
|
||||
System.out.println("RPN List = "+formulaString(record));
|
||||
System.out.println("Formula text = "+ composeFormula(record));
|
||||
}
|
||||
|
||||
private String formulaString(FormulaRecord record) {
|
||||
StringBuffer formula = new StringBuffer("=");
|
||||
int numptgs = record.getNumberOfExpressionTokens();
|
||||
List tokens = record.getParsedExpression();
|
||||
Ptg token;
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i=0;i<numptgs;i++) {
|
||||
token = (Ptg) tokens.get(i);
|
||||
Ptg[] tokens = record.getParsedExpression();
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
Ptg token = tokens[i];
|
||||
buf.append( token.toFormulaString((HSSFWorkbook)null));
|
||||
switch (token.getPtgClass()) {
|
||||
case Ptg.CLASS_REF :
|
||||
|
@ -187,9 +179,9 @@ public class FormulaViewer
|
|||
}
|
||||
|
||||
|
||||
private String composeFormula(FormulaRecord record)
|
||||
private static String composeFormula(FormulaRecord record)
|
||||
{
|
||||
return org.apache.poi.hssf.model.FormulaParser.toFormulaString((HSSFWorkbook)null,record.getParsedExpression());
|
||||
return FormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,19 +30,28 @@ public final class RecordStream {
|
|||
private final List _list;
|
||||
private int _nextIndex;
|
||||
private int _countRead;
|
||||
private final int _endIx;
|
||||
|
||||
public RecordStream(List inputList, int startIndex) {
|
||||
/**
|
||||
* Creates a RecordStream bounded by startIndex and endIndex
|
||||
*/
|
||||
public RecordStream(List inputList, int startIndex, int endIx) {
|
||||
_list = inputList;
|
||||
_nextIndex = startIndex;
|
||||
_endIx = endIx;
|
||||
_countRead = 0;
|
||||
}
|
||||
|
||||
public RecordStream(List records, int startIx) {
|
||||
this(records, startIx, records.size());
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return _nextIndex < _list.size();
|
||||
return _nextIndex < _endIx;
|
||||
}
|
||||
|
||||
public Record getNext() {
|
||||
if(_nextIndex >= _list.size()) {
|
||||
if(!hasNext()) {
|
||||
throw new RuntimeException("Attempt to read past end of record stream");
|
||||
}
|
||||
_countRead ++;
|
||||
|
@ -53,14 +62,17 @@ public final class RecordStream {
|
|||
* @return the {@link Class} of the next Record. <code>null</code> if this stream is exhausted.
|
||||
*/
|
||||
public Class peekNextClass() {
|
||||
if(_nextIndex >= _list.size()) {
|
||||
if(!hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return _list.get(_nextIndex).getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return -1 if at end of records
|
||||
*/
|
||||
public int peekNextSid() {
|
||||
if(_nextIndex >= _list.size()) {
|
||||
if(!hasNext()) {
|
||||
return -1;
|
||||
}
|
||||
return ((Record)_list.get(_nextIndex)).getSid();
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.HexDump;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
|
@ -33,69 +34,60 @@ import org.apache.poi.util.LittleEndian;
|
|||
* @version 2.0-pre
|
||||
*/
|
||||
public final class FormulaRecord extends Record implements CellValueRecordInterface {
|
||||
|
||||
|
||||
public static final short sid = 0x0006; // docs say 406...because of a bug Microsoft support site article #Q184647)
|
||||
private static int FIXED_SIZE = 22;
|
||||
|
||||
private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
|
||||
private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
|
||||
private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
|
||||
private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
|
||||
|
||||
private int field_1_row;
|
||||
private int field_1_row;
|
||||
private short field_2_column;
|
||||
private short field_3_xf;
|
||||
private double field_4_value;
|
||||
private short field_5_options;
|
||||
private int field_6_zero;
|
||||
private short field_7_expression_len;
|
||||
private Stack field_8_parsed_expr;
|
||||
|
||||
private Ptg[] field_8_parsed_expr;
|
||||
|
||||
/**
|
||||
* Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
|
||||
*/
|
||||
private byte[] value_data;
|
||||
private byte[] all_data; //if formula support is not enabled then
|
||||
//we'll just store/reserialize
|
||||
private byte[] value_data;
|
||||
|
||||
/** Creates new FormulaRecord */
|
||||
|
||||
public FormulaRecord()
|
||||
{
|
||||
field_8_parsed_expr = new Stack();
|
||||
public FormulaRecord() {
|
||||
field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Formula record and sets its fields appropriately.
|
||||
* Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an
|
||||
* Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an
|
||||
* "explanation of this bug in the documentation) or an exception
|
||||
* will be throw upon validation
|
||||
*
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
|
||||
public FormulaRecord(RecordInputStream in)
|
||||
{
|
||||
public FormulaRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
protected void fillFields(RecordInputStream in)
|
||||
{
|
||||
try {
|
||||
protected void fillFields(RecordInputStream in) {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_column = in.readShort();
|
||||
field_3_xf = in.readShort();
|
||||
field_4_value = in.readDouble();
|
||||
field_5_options = in.readShort();
|
||||
|
||||
|
||||
if (Double.isNaN(field_4_value)) {
|
||||
value_data = in.getNANData();
|
||||
}
|
||||
|
||||
|
||||
field_6_zero = in.readInt();
|
||||
field_7_expression_len = in.readShort();
|
||||
field_8_parsed_expr = Ptg.createParsedExpressionTokens(field_7_expression_len, in);
|
||||
} catch (java.lang.UnsupportedOperationException uoe) {
|
||||
throw new RecordFormatException(uoe);
|
||||
}
|
||||
int field_7_expression_len = in.readShort(); // this length does not include any extra array data
|
||||
field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in);
|
||||
if (in.remaining() == 10) {
|
||||
// TODO - this seems to occur when IntersectionPtg is present
|
||||
// 10 extra bytes are just 0x01 and 0x00
|
||||
|
@ -103,19 +95,15 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
}
|
||||
}
|
||||
|
||||
//public void setRow(short row)
|
||||
public void setRow(int row)
|
||||
{
|
||||
public void setRow(int row) {
|
||||
field_1_row = row;
|
||||
}
|
||||
|
||||
public void setColumn(short column)
|
||||
{
|
||||
public void setColumn(short column) {
|
||||
field_2_column = column;
|
||||
}
|
||||
|
||||
public void setXFIndex(short xf)
|
||||
{
|
||||
public void setXFIndex(short xf) {
|
||||
field_3_xf = xf;
|
||||
}
|
||||
|
||||
|
@ -124,9 +112,7 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
*
|
||||
* @param value calculated value
|
||||
*/
|
||||
|
||||
public void setValue(double value)
|
||||
{
|
||||
public void setValue(double value) {
|
||||
field_4_value = value;
|
||||
}
|
||||
|
||||
|
@ -135,35 +121,19 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
*
|
||||
* @param options bitmask
|
||||
*/
|
||||
|
||||
public void setOptions(short options)
|
||||
{
|
||||
public void setOptions(short options) {
|
||||
field_5_options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the length (in number of tokens) of the expression
|
||||
* @param len length
|
||||
*/
|
||||
|
||||
public void setExpressionLength(short len)
|
||||
{
|
||||
field_7_expression_len = len;
|
||||
}
|
||||
|
||||
//public short getRow()
|
||||
public int getRow()
|
||||
{
|
||||
public int getRow() {
|
||||
return field_1_row;
|
||||
}
|
||||
|
||||
public short getColumn()
|
||||
{
|
||||
public short getColumn() {
|
||||
return field_2_column;
|
||||
}
|
||||
|
||||
public short getXFIndex()
|
||||
{
|
||||
public short getXFIndex() {
|
||||
return field_3_xf;
|
||||
}
|
||||
|
||||
|
@ -172,8 +142,7 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
*
|
||||
* @return calculated value
|
||||
*/
|
||||
public double getValue()
|
||||
{
|
||||
public double getValue() {
|
||||
return field_4_value;
|
||||
}
|
||||
|
||||
|
@ -182,108 +151,43 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
*
|
||||
* @return bitmask
|
||||
*/
|
||||
public short getOptions()
|
||||
{
|
||||
public short getOptions() {
|
||||
return field_5_options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isSharedFormula() {
|
||||
return sharedFormula.isSet(field_5_options);
|
||||
}
|
||||
public void setSharedFormula(boolean flag) {
|
||||
field_5_options =
|
||||
sharedFormula.setShortBoolean(field_5_options, flag);
|
||||
field_5_options =
|
||||
sharedFormula.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
|
||||
public boolean isAlwaysCalc() {
|
||||
return alwaysCalc.isSet(field_5_options);
|
||||
return alwaysCalc.isSet(field_5_options);
|
||||
}
|
||||
public void setAlwaysCalc(boolean flag) {
|
||||
field_5_options =
|
||||
alwaysCalc.setShortBoolean(field_5_options, flag);
|
||||
field_5_options =
|
||||
alwaysCalc.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
|
||||
public boolean isCalcOnLoad() {
|
||||
return calcOnLoad.isSet(field_5_options);
|
||||
return calcOnLoad.isSet(field_5_options);
|
||||
}
|
||||
public void setCalcOnLoad(boolean flag) {
|
||||
field_5_options =
|
||||
calcOnLoad.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the length (in number of tokens) of the expression
|
||||
* @return expression length
|
||||
*/
|
||||
|
||||
public short getExpressionLength()
|
||||
{
|
||||
return field_7_expression_len;
|
||||
field_5_options =
|
||||
calcOnLoad.setShortBoolean(field_5_options, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* push a token onto the stack
|
||||
*
|
||||
* @param ptg the token
|
||||
* @return the formula tokens. never <code>null</code>
|
||||
*/
|
||||
|
||||
public void pushExpressionToken(Ptg ptg)
|
||||
{
|
||||
field_8_parsed_expr.push(ptg);
|
||||
public Ptg[] getParsedExpression() {
|
||||
return (Ptg[]) field_8_parsed_expr.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* pop a token off of the stack
|
||||
*
|
||||
* @return Ptg - the token
|
||||
*/
|
||||
|
||||
public Ptg popExpressionToken()
|
||||
{
|
||||
return ( Ptg ) field_8_parsed_expr.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* peek at the token on the top of stack
|
||||
*
|
||||
* @return Ptg - the token
|
||||
*/
|
||||
|
||||
public Ptg peekExpressionToken()
|
||||
{
|
||||
return ( Ptg ) field_8_parsed_expr.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the size of the stack
|
||||
* @return size of the stack
|
||||
*/
|
||||
|
||||
public int getNumberOfExpressionTokens()
|
||||
{
|
||||
if (this.field_8_parsed_expr == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return field_8_parsed_expr.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the stack as a list
|
||||
*
|
||||
* @return list of tokens (casts stack to a list and returns it!)
|
||||
* this method can return null is we are unable to create Ptgs from
|
||||
* existing excel file
|
||||
* callers should check for null!
|
||||
*/
|
||||
|
||||
public List getParsedExpression()
|
||||
{
|
||||
return field_8_parsed_expr;
|
||||
}
|
||||
|
||||
public void setParsedExpression(Stack ptgs) {
|
||||
field_8_parsed_expr = ptgs;
|
||||
public void setParsedExpression(Ptg[] ptgs) {
|
||||
field_8_parsed_expr = ptgs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,156 +196,86 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
*
|
||||
* @param id alleged id for this record
|
||||
*/
|
||||
|
||||
protected void validateSid(short id)
|
||||
{
|
||||
if (id != sid)
|
||||
{
|
||||
protected void validateSid(short id) {
|
||||
if (id != sid) {
|
||||
throw new RecordFormatException("NOT A FORMULA RECORD");
|
||||
}
|
||||
}
|
||||
|
||||
public short getSid()
|
||||
{
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* called by the class that is responsible for writing this sucker.
|
||||
* Subclasses should implement this so that their data is passed back in a
|
||||
* byte array.
|
||||
*
|
||||
* @return byte array containing instance data
|
||||
*/
|
||||
private int getDataSize() {
|
||||
return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr);
|
||||
}
|
||||
public int serialize(int offset, byte [] data) {
|
||||
|
||||
public int serialize(int offset, byte [] data)
|
||||
{
|
||||
if (this.field_8_parsed_expr != null) {
|
||||
int ptgSize = getTotalPtgSize();
|
||||
int dataSize = getDataSize();
|
||||
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset, ( short ) (22 + ptgSize));
|
||||
//LittleEndian.putShort(data, 4 + offset, getRow());
|
||||
LittleEndian.putShort(data, 4 + offset, ( short ) getRow());
|
||||
LittleEndian.putUShort(data, 2 + offset, dataSize);
|
||||
LittleEndian.putUShort(data, 4 + offset, getRow());
|
||||
LittleEndian.putShort(data, 6 + offset, getColumn());
|
||||
LittleEndian.putShort(data, 8 + offset, getXFIndex());
|
||||
|
||||
|
||||
//only reserialize if the value is still NaN and we have old nan data
|
||||
if (Double.isNaN(this.getValue()) && value_data != null) {
|
||||
System.arraycopy(value_data,0,data,10 + offset,value_data.length);
|
||||
if (Double.isNaN(getValue()) && value_data != null) {
|
||||
System.arraycopy(value_data,0,data,10 + offset,value_data.length);
|
||||
} else {
|
||||
LittleEndian.putDouble(data, 10 + offset, field_4_value);
|
||||
LittleEndian.putDouble(data, 10 + offset, field_4_value);
|
||||
}
|
||||
|
||||
|
||||
LittleEndian.putShort(data, 18 + offset, getOptions());
|
||||
|
||||
|
||||
//when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
|
||||
//Microsoft Excel Developer's Kit Page 318
|
||||
LittleEndian.putInt(data, 20 + offset, 0);
|
||||
LittleEndian.putShort(data, 24 + offset, getExpressionLength());
|
||||
Ptg.serializePtgStack(field_8_parsed_expr, data, 26+offset);
|
||||
} else {
|
||||
System.arraycopy(all_data,0,data,offset,all_data.length);
|
||||
}
|
||||
return getRecordSize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getRecordSize()
|
||||
{
|
||||
int retval =0;
|
||||
|
||||
if (this.field_8_parsed_expr != null) {
|
||||
retval = getTotalPtgSize() + 26;
|
||||
} else {
|
||||
retval =all_data.length;
|
||||
}
|
||||
return retval;
|
||||
|
||||
// return getTotalPtgSize() + 28;
|
||||
int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr);
|
||||
LittleEndian.putUShort(data, 24 + offset, formulaTokensSize);
|
||||
Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset);
|
||||
return 4 + dataSize;
|
||||
}
|
||||
|
||||
private int getTotalPtgSize()
|
||||
{
|
||||
List list = getParsedExpression();
|
||||
int retval = 0;
|
||||
|
||||
for (int k = 0; k < list.size(); k++)
|
||||
{
|
||||
Ptg ptg = ( Ptg ) list.get(k);
|
||||
|
||||
retval += ptg.getSize();
|
||||
}
|
||||
return retval;
|
||||
public int getRecordSize() {
|
||||
return 4 + getDataSize();
|
||||
}
|
||||
|
||||
public boolean isInValueSection()
|
||||
{
|
||||
public boolean isInValueSection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValue()
|
||||
{
|
||||
public boolean isValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("[FORMULA]\n");
|
||||
buffer.append(" .row = ")
|
||||
.append(Integer.toHexString(getRow())).append("\n");
|
||||
buffer.append(" .column = ")
|
||||
.append(Integer.toHexString(getColumn()))
|
||||
.append("\n");
|
||||
buffer.append(" .xf = ")
|
||||
.append(Integer.toHexString(getXFIndex())).append("\n");
|
||||
if (Double.isNaN(this.getValue()) && value_data != null)
|
||||
buffer.append(" .value (NaN) = ")
|
||||
.append(org.apache.poi.util.HexDump.dump(value_data,0,0))
|
||||
.append("\n");
|
||||
else
|
||||
buffer.append(" .value = ").append(getValue())
|
||||
.append("\n");
|
||||
buffer.append(" .options = ").append(getOptions())
|
||||
.append("\n");
|
||||
buffer.append(" .alwaysCalc = ").append(alwaysCalc.isSet(getOptions()))
|
||||
.append("\n");
|
||||
buffer.append(" .calcOnLoad = ").append(calcOnLoad.isSet(getOptions()))
|
||||
.append("\n");
|
||||
buffer.append(" .sharedFormula = ").append(sharedFormula.isSet(getOptions()))
|
||||
.append("\n");
|
||||
buffer.append(" .zero = ").append(field_6_zero)
|
||||
.append("\n");
|
||||
buffer.append(" .expressionlength= ").append(getExpressionLength())
|
||||
.append("\n");
|
||||
|
||||
if (field_8_parsed_expr != null) {
|
||||
buffer.append(" .numptgsinarray = ").append(field_8_parsed_expr.size())
|
||||
.append("\n");
|
||||
|
||||
public String toString() {
|
||||
|
||||
for (int k = 0; k < field_8_parsed_expr.size(); k++ ) {
|
||||
buffer.append(" Ptg(")
|
||||
.append(k)
|
||||
.append(")=")
|
||||
.append(field_8_parsed_expr.get(k).toString())
|
||||
.append("\n")
|
||||
.append(((Ptg)field_8_parsed_expr.get(k)).toDebugString())
|
||||
.append("\n");
|
||||
}
|
||||
}else {
|
||||
buffer.append("Formula full data \n")
|
||||
.append(org.apache.poi.util.HexDump.dump(this.all_data,0,0));
|
||||
}
|
||||
|
||||
|
||||
buffer.append("[/FORMULA]\n");
|
||||
return buffer.toString();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("[FORMULA]\n");
|
||||
sb.append(" .row = ").append(HexDump.shortToHex(getRow())).append("\n");
|
||||
sb.append(" .column = ").append(HexDump.shortToHex(getColumn())).append("\n");
|
||||
sb.append(" .xf = ").append(HexDump.shortToHex(getXFIndex())).append("\n");
|
||||
sb.append(" .value = ");
|
||||
if (Double.isNaN(this.getValue()) && value_data != null) {
|
||||
sb.append("(NaN)").append(HexDump.dump(value_data,0,0)).append("\n");
|
||||
} else {
|
||||
sb.append(getValue()).append("\n");
|
||||
}
|
||||
sb.append(" .options = ").append(HexDump.shortToHex(getOptions())).append("\n");
|
||||
sb.append(" .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n");
|
||||
sb.append(" .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n");
|
||||
sb.append(" .shared = ").append(sharedFormula.isSet(getOptions())).append("\n");
|
||||
sb.append(" .zero = ").append(HexDump.intToHex(field_6_zero)).append("\n");
|
||||
|
||||
for (int k = 0; k < field_8_parsed_expr.length; k++ ) {
|
||||
sb.append(" Ptg[").append(k).append("]=");
|
||||
sb.append(field_8_parsed_expr[k].toString()).append("\n");
|
||||
}
|
||||
sb.append("[/FORMULA]\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public Object clone() {
|
||||
FormulaRecord rec = new FormulaRecord();
|
||||
rec.field_1_row = field_1_row;
|
||||
|
@ -450,18 +284,14 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
|
|||
rec.field_4_value = field_4_value;
|
||||
rec.field_5_options = field_5_options;
|
||||
rec.field_6_zero = field_6_zero;
|
||||
rec.field_7_expression_len = field_7_expression_len;
|
||||
rec.field_8_parsed_expr = new Stack();
|
||||
int size = 0;
|
||||
if (field_8_parsed_expr != null)
|
||||
size = field_8_parsed_expr.size();
|
||||
for (int i=0; i< size; i++) {
|
||||
Ptg ptg = ((Ptg)field_8_parsed_expr.get(i)).copy();
|
||||
rec.field_8_parsed_expr.add(i, ptg);
|
||||
int nTokens = field_8_parsed_expr.length;
|
||||
Ptg[] ptgs = new Ptg[nTokens];
|
||||
for (int i=0; i< nTokens; i++) {
|
||||
ptgs[i] = field_8_parsed_expr[i].copy();
|
||||
}
|
||||
rec.field_8_parsed_expr = ptgs;
|
||||
rec.value_data = value_data;
|
||||
rec.all_data = all_data;
|
||||
return rec;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -14,17 +14,22 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.*;
|
||||
import org.apache.poi.hssf.record.formula.AreaNPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.RefNPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
|
||||
/**
|
||||
* Title: SharedFormulaRecord
|
||||
* Description: Primarily used as an excel optimization so that multiple similar formulas
|
||||
* are not written out too many times. We should recognize this record and
|
||||
* are not written out too many times. We should recognize this record and
|
||||
* serialize as is since this is used when reading templates.
|
||||
* <p>
|
||||
* Note: the documentation says that the SID is BC where biffviewer reports 4BC. The hex dump shows
|
||||
|
@ -33,15 +38,15 @@ import org.apache.poi.hssf.record.formula.*;
|
|||
* @author Danny Mui at apache dot org
|
||||
*/
|
||||
public final class SharedFormulaRecord extends Record {
|
||||
public final static short sid = 0x4BC;
|
||||
|
||||
public final static short sid = 0x04BC;
|
||||
|
||||
private int field_1_first_row;
|
||||
private int field_2_last_row;
|
||||
private short field_3_first_column;
|
||||
private short field_4_last_column;
|
||||
private int field_5_reserved;
|
||||
private short field_6_expression_len;
|
||||
private Stack field_7_parsed_expr;
|
||||
private Stack field_7_parsed_expr;
|
||||
|
||||
public SharedFormulaRecord()
|
||||
{
|
||||
|
@ -55,15 +60,15 @@ public final class SharedFormulaRecord extends Record {
|
|||
{
|
||||
super(in);
|
||||
}
|
||||
|
||||
|
||||
protected void validateSid(short id)
|
||||
{
|
||||
if (id != this.sid)
|
||||
{
|
||||
throw new RecordFormatException("Not a valid SharedFormula");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int getFirstRow() {
|
||||
return field_1_first_row;
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ public final class SharedFormulaRecord extends Record {
|
|||
.append(field_7_parsed_expr.get(k).toString())
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
|
||||
buffer.append("[/SHARED FORMULA RECORD]\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
@ -163,7 +168,7 @@ public final class SharedFormulaRecord extends Record {
|
|||
private Stack getParsedExpressionTokens(RecordInputStream in)
|
||||
{
|
||||
Stack stack = new Stack();
|
||||
|
||||
|
||||
while (in.remaining() != 0) {
|
||||
Ptg ptg = Ptg.createPtg(in);
|
||||
stack.push(ptg);
|
||||
|
@ -180,15 +185,15 @@ public final class SharedFormulaRecord extends Record {
|
|||
return ((getFirstRow() <= formulaRow) && (getLastRow() >= formulaRow) &&
|
||||
(getFirstColumn() <= formulaColumn) && (getLastColumn() >= formulaColumn));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a non shared formula from the shared formula
|
||||
* Creates a non shared formula from the shared formula
|
||||
* counter part
|
||||
*/
|
||||
protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
|
||||
if(false) {
|
||||
/*
|
||||
* TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
|
||||
* TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
|
||||
* If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records.
|
||||
* Disabling this code breaks one existing junit.
|
||||
* Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly.
|
||||
|
@ -225,31 +230,30 @@ public final class SharedFormulaRecord extends Record {
|
|||
if (!ptg.isBaseToken()) {
|
||||
ptg.setClass(originalOperandClass);
|
||||
}
|
||||
|
||||
|
||||
newPtgStack.add(ptg);
|
||||
}
|
||||
return newPtgStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a non shared formula from the shared formula
|
||||
/**
|
||||
* Creates a non shared formula from the shared formula
|
||||
* counter part
|
||||
*/
|
||||
public void convertSharedFormulaRecord(FormulaRecord formula) {
|
||||
//Sanity checks
|
||||
final int formulaRow = formula.getRow();
|
||||
final int formulaColumn = formula.getColumn();
|
||||
if (isFormulaInShared(formula)) {
|
||||
formula.setExpressionLength(getExpressionLength());
|
||||
|
||||
Stack newPtgStack =
|
||||
convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
|
||||
formula.setParsedExpression(newPtgStack);
|
||||
if (!isFormulaInShared(formula)) {
|
||||
throw new RuntimeException("Shared Formula Conversion: Coding Error");
|
||||
}
|
||||
final int formulaRow = formula.getRow();
|
||||
final int formulaColumn = formula.getColumn();
|
||||
|
||||
List ptgList = convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
|
||||
Ptg[] ptgs = new Ptg[ptgList.size()];
|
||||
ptgList.toArray(ptgs);
|
||||
formula.setParsedExpression(ptgs);
|
||||
//Now its not shared!
|
||||
formula.setSharedFormula(false);
|
||||
} else {
|
||||
throw new RuntimeException("Shared Formula Conversion: Coding Error");
|
||||
}
|
||||
}
|
||||
|
||||
private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
package org.apache.poi.hssf.record.aggregates;
|
||||
|
||||
import org.apache.poi.hssf.model.RecordStream;
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
||||
import org.apache.poi.hssf.record.StringRecord;
|
||||
import org.apache.poi.hssf.record.TableRecord;
|
||||
|
||||
/**
|
||||
* The formula record aggregate is used to join together the formula record and it's
|
||||
|
@ -29,61 +32,67 @@ import org.apache.poi.hssf.record.StringRecord;
|
|||
*/
|
||||
public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
|
||||
|
||||
private FormulaRecord _formulaRecord;
|
||||
private final FormulaRecord _formulaRecord;
|
||||
/** caches the calculated result of the formula */
|
||||
private StringRecord _stringRecord;
|
||||
private TableRecord _tableRecord;
|
||||
|
||||
public FormulaRecordAggregate( FormulaRecord formulaRecord, StringRecord stringRecord )
|
||||
{
|
||||
public FormulaRecordAggregate(FormulaRecord formulaRecord) {
|
||||
_formulaRecord = formulaRecord;
|
||||
_stringRecord = stringRecord;
|
||||
_stringRecord = null;
|
||||
}
|
||||
public FormulaRecordAggregate(FormulaRecord formulaRecord, RecordStream rs) {
|
||||
_formulaRecord = formulaRecord;
|
||||
Class nextClass = rs.peekNextClass();
|
||||
if (nextClass == SharedFormulaRecord.class) {
|
||||
// For (text) shared formulas, the SharedFormulaRecord comes before the StringRecord.
|
||||
// In any case it is OK to skip SharedFormulaRecords because they were collected
|
||||
// before constructing the ValueRecordsAggregate.
|
||||
rs.getNext(); // skip the shared formula record
|
||||
nextClass = rs.peekNextClass();
|
||||
}
|
||||
if (nextClass == StringRecord.class) {
|
||||
_stringRecord = (StringRecord) rs.getNext();
|
||||
} else if (nextClass == TableRecord.class) {
|
||||
_tableRecord = (TableRecord) rs.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStringRecord( StringRecord stringRecord ) {
|
||||
public void setStringRecord(StringRecord stringRecord) {
|
||||
_stringRecord = stringRecord;
|
||||
}
|
||||
|
||||
public void setFormulaRecord( FormulaRecord formulaRecord )
|
||||
{
|
||||
_formulaRecord = formulaRecord;
|
||||
_tableRecord = null; // probably can't have both present at the same time
|
||||
// TODO - establish rules governing when each of these sub records may exist
|
||||
}
|
||||
|
||||
public FormulaRecord getFormulaRecord()
|
||||
{
|
||||
public FormulaRecord getFormulaRecord() {
|
||||
return _formulaRecord;
|
||||
}
|
||||
|
||||
public StringRecord getStringRecord()
|
||||
{
|
||||
public StringRecord getStringRecord() {
|
||||
return _stringRecord;
|
||||
}
|
||||
|
||||
public short getXFIndex()
|
||||
{
|
||||
public short getXFIndex() {
|
||||
return _formulaRecord.getXFIndex();
|
||||
}
|
||||
|
||||
public void setXFIndex(short xf)
|
||||
{
|
||||
_formulaRecord.setXFIndex( xf );
|
||||
public void setXFIndex(short xf) {
|
||||
_formulaRecord.setXFIndex(xf);
|
||||
}
|
||||
|
||||
public void setColumn(short col)
|
||||
{
|
||||
_formulaRecord.setColumn( col );
|
||||
public void setColumn(short col) {
|
||||
_formulaRecord.setColumn(col);
|
||||
}
|
||||
|
||||
public void setRow(int row)
|
||||
{
|
||||
_formulaRecord.setRow( row );
|
||||
public void setRow(int row) {
|
||||
_formulaRecord.setRow(row);
|
||||
}
|
||||
|
||||
public short getColumn()
|
||||
{
|
||||
public short getColumn() {
|
||||
return _formulaRecord.getColumn();
|
||||
}
|
||||
|
||||
public int getRow()
|
||||
{
|
||||
public int getRow() {
|
||||
return _formulaRecord.getRow();
|
||||
}
|
||||
|
||||
|
@ -94,8 +103,11 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
|
|||
public void visitContainedRecords(RecordVisitor rv) {
|
||||
rv.visitRecord(_formulaRecord);
|
||||
if (_stringRecord != null) {
|
||||
rv.visitRecord(_stringRecord);
|
||||
rv.visitRecord(_stringRecord);
|
||||
}
|
||||
if (_tableRecord != null) {
|
||||
rv.visitRecord(_tableRecord);
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringValue() {
|
||||
|
|
|
@ -82,7 +82,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
|
|||
if (!rec.isValue()) {
|
||||
throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")");
|
||||
}
|
||||
i += _valuesAgg.construct(recs, i, endIx, sfh);
|
||||
i += _valuesAgg.construct(recs, i, endIx, sfh)-1;
|
||||
}
|
||||
"".length();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.RecordStream;
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.DBCellRecord;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
|
@ -111,12 +112,12 @@ public final class ValueRecordsAggregate {
|
|||
|
||||
public void removeAllCellsValuesForRow(int rowIndex) {
|
||||
if (rowIndex >= records.length) {
|
||||
throw new IllegalArgumentException("Specified rowIndex " + rowIndex
|
||||
throw new IllegalArgumentException("Specified rowIndex " + rowIndex
|
||||
+ " is outside the allowable range (0.." +records.length + ")");
|
||||
}
|
||||
records[rowIndex] = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getPhysicalNumberOfCells()
|
||||
{
|
||||
|
@ -142,62 +143,48 @@ public final class ValueRecordsAggregate {
|
|||
}
|
||||
|
||||
/**
|
||||
* Processes a sequential group of cell value records. Stops at endIx or the first
|
||||
* Processes a sequential group of cell value records. Stops at endIx or the first
|
||||
* non-value record encountered.
|
||||
* @param sfh used to resolve any shared formulas for the current sheet
|
||||
* @return the number of records consumed
|
||||
*/
|
||||
public int construct(List records, int offset, int endIx, SharedFormulaHolder sfh) {
|
||||
int k = 0;
|
||||
RecordStream rs = new RecordStream(records, offset, endIx);
|
||||
|
||||
FormulaRecordAggregate lastFormulaAggregate = null;
|
||||
|
||||
// Now do the main processing sweep
|
||||
for (k = offset; k < endIx; k++) {
|
||||
Record rec = ( Record ) records.get(k);
|
||||
|
||||
if (rec instanceof StringRecord) {
|
||||
if (lastFormulaAggregate == null) {
|
||||
throw new RuntimeException("StringRecord found without preceding FormulaRecord");
|
||||
}
|
||||
if (lastFormulaAggregate.getStringRecord() != null) {
|
||||
throw new RuntimeException("Multiple StringRecords found after FormulaRecord");
|
||||
}
|
||||
lastFormulaAggregate.setStringRecord((StringRecord)rec);
|
||||
lastFormulaAggregate = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec instanceof TableRecord) {
|
||||
// TODO - don't loose this record
|
||||
// DATATABLE probably belongs in formula record aggregate
|
||||
if (lastFormulaAggregate == null) {
|
||||
throw new RuntimeException("No preceding formula record found");
|
||||
}
|
||||
lastFormulaAggregate = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec instanceof SharedFormulaRecord) {
|
||||
// Already handled, not to worry
|
||||
continue;
|
||||
while (rs.hasNext()) {
|
||||
Class recClass = rs.peekNextClass();
|
||||
if (recClass == StringRecord.class) {
|
||||
throw new RuntimeException("Loose StringRecord found without preceding FormulaRecord");
|
||||
}
|
||||
|
||||
if (rec instanceof UnknownRecord) {
|
||||
if (recClass == TableRecord.class) {
|
||||
throw new RuntimeException("Loose TableRecord found without preceding FormulaRecord");
|
||||
}
|
||||
|
||||
if (recClass == UnknownRecord.class) {
|
||||
break;
|
||||
}
|
||||
if (rec instanceof RowRecord) {
|
||||
break;
|
||||
if (recClass == RowRecord.class) {
|
||||
break;
|
||||
}
|
||||
if (rec instanceof DBCellRecord) {
|
||||
if (recClass == DBCellRecord.class) {
|
||||
// end of 'Row Block'. This record is ignored by POI
|
||||
break;
|
||||
}
|
||||
if (rec instanceof MergeCellsRecord) {
|
||||
|
||||
Record rec = rs.getNext();
|
||||
|
||||
if (recClass == SharedFormulaRecord.class) {
|
||||
// Already handled, not to worry
|
||||
continue;
|
||||
}
|
||||
if (recClass == MergeCellsRecord.class) {
|
||||
// doesn't really belong here
|
||||
// can safely be ignored, because it has been processed in a higher method
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rec.isValue()) {
|
||||
throw new RuntimeException("bad record type");
|
||||
}
|
||||
|
@ -206,14 +193,13 @@ public final class ValueRecordsAggregate {
|
|||
if (formula.isSharedFormula()) {
|
||||
sfh.convertSharedFormulaRecord(formula);
|
||||
}
|
||||
|
||||
lastFormulaAggregate = new FormulaRecordAggregate((FormulaRecord)rec, null);
|
||||
insertCell( lastFormulaAggregate );
|
||||
|
||||
insertCell(new FormulaRecordAggregate((FormulaRecord)rec, rs));
|
||||
continue;
|
||||
}
|
||||
insertCell(( CellValueRecordInterface ) rec);
|
||||
}
|
||||
return k - offset - 1;
|
||||
return rs.getCountRead();
|
||||
}
|
||||
|
||||
/** Tallies a count of the size of the cell records
|
||||
|
@ -235,7 +221,7 @@ public final class ValueRecordsAggregate {
|
|||
|
||||
/** Returns true if the row has cells attached to it */
|
||||
public boolean rowHasCells(int row) {
|
||||
if (row > records.length-1) //previously this said row > records.length which means if
|
||||
if (row > records.length-1) //previously this said row > records.length which means if
|
||||
return false; // if records.length == 60 and I pass "60" here I get array out of bounds
|
||||
CellValueRecordInterface[] rowCells=records[row]; //because a 60 length array has the last index = 59
|
||||
if(rowCells==null) return false;
|
||||
|
@ -260,7 +246,7 @@ public final class ValueRecordsAggregate {
|
|||
}
|
||||
return pos - offset;
|
||||
}
|
||||
|
||||
|
||||
public int visitCellsForRow(int rowIndex, RecordVisitor rv) {
|
||||
int result = 0;
|
||||
CellValueRecordInterface[] cellRecs = records[rowIndex];
|
||||
|
@ -292,7 +278,7 @@ public final class ValueRecordsAggregate {
|
|||
|
||||
public CellValueRecordInterface[] getValueRecords() {
|
||||
List temp = new ArrayList();
|
||||
|
||||
|
||||
for (int i = 0; i < records.length; i++) {
|
||||
CellValueRecordInterface[] rowCells = records[i];
|
||||
if (rowCells == null) {
|
||||
|
@ -305,7 +291,7 @@ public final class ValueRecordsAggregate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()];
|
||||
temp.toArray(result);
|
||||
return result;
|
||||
|
@ -314,7 +300,7 @@ public final class ValueRecordsAggregate {
|
|||
{
|
||||
return new MyIterator();
|
||||
}
|
||||
|
||||
|
||||
private final class MyIterator implements Iterator {
|
||||
short nextColumn=-1;
|
||||
int nextRow,lastRow;
|
||||
|
@ -325,7 +311,7 @@ public final class ValueRecordsAggregate {
|
|||
this.lastRow=records.length-1;
|
||||
findNext();
|
||||
}
|
||||
|
||||
|
||||
public MyIterator(int firstRow,int lastRow)
|
||||
{
|
||||
this.nextRow=firstRow;
|
||||
|
|
|
@ -39,6 +39,11 @@ public final class ArrayPtg extends Ptg {
|
|||
public static final byte sid = 0x20;
|
||||
|
||||
private static final int RESERVED_FIELD_LEN = 7;
|
||||
/**
|
||||
* The size of the plain tArray token written within the standard formula tokens
|
||||
* (not including the data which comes after all formula tokens)
|
||||
*/
|
||||
public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN;
|
||||
// TODO - fix up field visibility and subclasses
|
||||
private byte[] field_1_reserved;
|
||||
|
||||
|
@ -123,7 +128,7 @@ public final class ArrayPtg extends Ptg {
|
|||
public int writeTokenValueBytes(byte[] data, int offset) {
|
||||
|
||||
LittleEndian.putByte(data, offset + 0, token_1_columns-1);
|
||||
LittleEndian.putShort(data, offset + 1, (short)(token_2_rows-1));
|
||||
LittleEndian.putUShort(data, offset + 1, token_2_rows-1);
|
||||
ConstantValueParser.encode(data, offset + 3, token_3_arrayValues);
|
||||
return 3 + ConstantValueParser.getEncodedSize(token_3_arrayValues);
|
||||
}
|
||||
|
@ -137,11 +142,11 @@ public final class ArrayPtg extends Ptg {
|
|||
}
|
||||
|
||||
/** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
|
||||
public int getSize()
|
||||
{
|
||||
int size = 1+7+1+2;
|
||||
size += ConstantValueParser.getEncodedSize(token_3_arrayValues);
|
||||
return size;
|
||||
public int getSize() {
|
||||
return PLAIN_TOKEN_SIZE
|
||||
// data written after the all tokens:
|
||||
+ 1 + 2 // column, row
|
||||
+ ConstantValueParser.getEncodedSize(token_3_arrayValues);
|
||||
}
|
||||
|
||||
public String toFormulaString(Workbook book)
|
||||
|
|
|
@ -221,13 +221,6 @@ public abstract class Ptg implements Cloneable {
|
|||
}
|
||||
throw new RuntimeException("Unexpected base token id (" + id + ")");
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static int getEncodedSize(Stack ptgs) {
|
||||
return getEncodedSize(toPtgArray(ptgs));
|
||||
}
|
||||
/**
|
||||
* @return a distinct copy of this <tt>Ptg</tt> if the class is mutable, or the same instance
|
||||
* if the class is immutable.
|
||||
|
@ -265,6 +258,11 @@ public abstract class Ptg implements Cloneable {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* This method will return the same result as {@link #getEncodedSizeWithoutArrayData(Ptg[])}
|
||||
* if there are no array tokens present.
|
||||
* @return the full size taken to encode the specified <tt>Ptg</tt>s
|
||||
*/
|
||||
// TODO - several duplicates of this code should be refactored here
|
||||
public static int getEncodedSize(Ptg[] ptgs) {
|
||||
int result = 0;
|
||||
|
@ -273,6 +271,22 @@ public abstract class Ptg implements Cloneable {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Used to calculate value that should be encoded at the start of the encoded Ptg token array;
|
||||
* @return the size of the encoded Ptg tokens not including any trailing array data.
|
||||
*/
|
||||
public static int getEncodedSizeWithoutArrayData(Ptg[] ptgs) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < ptgs.length; i++) {
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof ArrayPtg) {
|
||||
result += ArrayPtg.PLAIN_TOKEN_SIZE;
|
||||
} else {
|
||||
result += ptg.getSize();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Writes the ptgs to the data buffer, starting at the specified offset.
|
||||
*
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.eval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface AreaEval extends ValueEval {
|
||||
|
||||
|
@ -30,76 +27,81 @@ public interface AreaEval extends ValueEval {
|
|||
* returns the 0-based index of the first row in
|
||||
* this area.
|
||||
*/
|
||||
public int getFirstRow();
|
||||
int getFirstRow();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the last row in
|
||||
* this area.
|
||||
*/
|
||||
public int getLastRow();
|
||||
int getLastRow();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the first col in
|
||||
* this area.
|
||||
*/
|
||||
public int getFirstColumn();
|
||||
int getFirstColumn();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the last col in
|
||||
* this area.
|
||||
*/
|
||||
public int getLastColumn();
|
||||
|
||||
int getLastColumn();
|
||||
|
||||
/**
|
||||
* returns true if the Area's start and end row indexes
|
||||
* are same. This result of this method should agree
|
||||
* with getFirstRow() == getLastRow().
|
||||
*/
|
||||
public boolean isRow();
|
||||
|
||||
boolean isRow();
|
||||
|
||||
/**
|
||||
* returns true if the Area's start and end col indexes
|
||||
* are same. This result of this method should agree
|
||||
* with getFirstColumn() == getLastColumn().
|
||||
*/
|
||||
public boolean isColumn();
|
||||
boolean isColumn();
|
||||
|
||||
/**
|
||||
* The array of values in this area. Although the area
|
||||
* maybe 1D (ie. isRow() or isColumn() returns true) or 2D
|
||||
* the returned array is 1D.
|
||||
*/
|
||||
public ValueEval[] getValues();
|
||||
ValueEval[] getValues();
|
||||
|
||||
/**
|
||||
* returns the ValueEval from the values array at the specified
|
||||
* row and col index. The specified indexes should be absolute indexes
|
||||
* in the sheet and not relative indexes within the area. Also,
|
||||
* if contains(row, col) evaluates to true, a null value will
|
||||
* bre returned.
|
||||
* @param row
|
||||
* @param col
|
||||
* @return the ValueEval from within this area at the specified row and col index. Never
|
||||
* <code>null</code> (possibly {@link BlankEval}). The specified indexes should be absolute
|
||||
* indexes in the sheet and not relative indexes within the area.
|
||||
*/
|
||||
public ValueEval getValueAt(int row, int col);
|
||||
|
||||
ValueEval getValueAt(int row, int col);
|
||||
|
||||
/**
|
||||
* returns true if the cell at row and col specified
|
||||
* as absolute indexes in the sheet is contained in
|
||||
* returns true if the cell at row and col specified
|
||||
* as absolute indexes in the sheet is contained in
|
||||
* this area.
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
public boolean contains(int row, int col);
|
||||
|
||||
boolean contains(int row, int col);
|
||||
|
||||
/**
|
||||
* returns true if the specified col is in range
|
||||
* @param col
|
||||
*/
|
||||
public boolean containsColumn(short col);
|
||||
|
||||
boolean containsColumn(short col);
|
||||
|
||||
/**
|
||||
* returns true if the specified row is in range
|
||||
* @param row
|
||||
*/
|
||||
public boolean containsRow(int row);
|
||||
boolean containsRow(int row);
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
/**
|
||||
* @return the ValueEval from within this area at the specified relativeRowIndex and
|
||||
* relativeColumnIndex. Never <code>null</code> (possibly {@link BlankEval}). The
|
||||
* specified indexes should relative to the top left corner of this area.
|
||||
*/
|
||||
ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.eval;
|
||||
|
||||
|
@ -94,9 +94,7 @@ abstract class AreaEvalBase implements AreaEval {
|
|||
throw new IllegalArgumentException("Specified column index (" + col
|
||||
+ ") is outside the allowed range (" + _firstColumn + ".." + col + ")");
|
||||
}
|
||||
|
||||
int index = rowOffsetIx * _nColumns + colOffsetIx;
|
||||
return _values[index];
|
||||
return getRelativeValue(rowOffsetIx, colOffsetIx);
|
||||
}
|
||||
|
||||
public final boolean contains(int row, int col) {
|
||||
|
@ -119,4 +117,20 @@ abstract class AreaEvalBase implements AreaEval {
|
|||
public final boolean isRow() {
|
||||
return _firstRow == _lastRow;
|
||||
}
|
||||
public int getHeight() {
|
||||
return _lastRow-_firstRow+1;
|
||||
}
|
||||
|
||||
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
|
||||
int index = relativeRowIndex * _nColumns + relativeColumnIndex;
|
||||
ValueEval result = _values[index];
|
||||
if (result == null) {
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return _lastColumn-_firstColumn+1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.eval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > This class is a
|
||||
* marker class. It is a special value for empty cells.
|
||||
*/
|
||||
public class BlankEval implements ValueEval {
|
||||
public final class BlankEval implements ValueEval {
|
||||
|
||||
public static BlankEval INSTANCE = new BlankEval();
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public final class OperandResolver {
|
|||
private OperandResolver() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a single value from a variety of different argument types according to standard
|
||||
* Excel rules. Does not perform any type conversion.
|
||||
|
@ -113,17 +113,17 @@ public final class OperandResolver {
|
|||
}
|
||||
if (result instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) result);
|
||||
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return possibly <tt>ErrorEval</tt>, and <code>null</code>
|
||||
*/
|
||||
private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae,
|
||||
int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
|
||||
|
||||
if(false) {
|
||||
// this is too simplistic
|
||||
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
|
||||
|
@ -131,25 +131,25 @@ public final class OperandResolver {
|
|||
}
|
||||
/*
|
||||
Circular references are not dealt with directly here, but it is worth noting some issues.
|
||||
|
||||
|
||||
ANY one of the return statements in this method could return a cell that is identical
|
||||
to the one immediately being evaluated. The evaluating cell is identified by srcCellRow,
|
||||
srcCellRow AND sheet. The sheet is not available in any nearby calling method, so that's
|
||||
one reason why circular references are not easy to detect here. (The sheet of the returned
|
||||
cell can be obtained from ae if it is an Area3DEval.)
|
||||
|
||||
|
||||
Another reason there's little value in attempting to detect circular references here is
|
||||
that only direct circular references could be detected. If the cycle involved two or more
|
||||
cells this method could not detect it.
|
||||
|
||||
|
||||
Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector
|
||||
(and HSSFFormulaEvaluator).
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
if (ae.isColumn()) {
|
||||
if(ae.isRow()) {
|
||||
return ae.getValues()[0];
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
if(!ae.containsRow(srcCellRow)) {
|
||||
throw EvaluationException.invalidValue();
|
||||
|
@ -199,20 +199,20 @@ public final class OperandResolver {
|
|||
*/
|
||||
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
|
||||
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
}
|
||||
if (ev instanceof StringEval) {
|
||||
Double dd = parseDouble(((StringEval) ev).getStringValue());
|
||||
if (dd == null) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return dd.doubleValue();
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
}
|
||||
if (ev instanceof StringEval) {
|
||||
Double dd = parseDouble(((StringEval) ev).getStringValue());
|
||||
if (dd == null) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return dd.doubleValue();
|
||||
}
|
||||
throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string to a double using standard rules that Excel would use.<br/>
|
||||
* Tolerates currency prefixes, commas, leading and trailing spaces.<p/>
|
||||
|
@ -245,7 +245,7 @@ public final class OperandResolver {
|
|||
return null;
|
||||
}
|
||||
// TODO - support notation like '1E3' (==1000)
|
||||
|
||||
|
||||
double val;
|
||||
try {
|
||||
val = Double.parseDouble(text);
|
||||
|
@ -254,7 +254,7 @@ public final class OperandResolver {
|
|||
}
|
||||
return new Double(isPositive ? +val : -val);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
|
||||
* @return the converted string value. never <code>null</code>
|
||||
|
@ -274,4 +274,51 @@ public final class OperandResolver {
|
|||
}
|
||||
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>null</code> to represent blank values
|
||||
* @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted
|
||||
*/
|
||||
public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException {
|
||||
|
||||
if (ve == null || ve instanceof BlankEval) {
|
||||
// TODO - remove 've == null' condition once AreaEval is fixed
|
||||
return null;
|
||||
}
|
||||
if (ve instanceof BoolEval) {
|
||||
return Boolean.valueOf(((BoolEval) ve).getBooleanValue());
|
||||
}
|
||||
|
||||
if (ve instanceof BlankEval) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ve instanceof StringEval) {
|
||||
if (stringsAreBlanks) {
|
||||
return null;
|
||||
}
|
||||
String str = ((StringEval) ve).getStringValue();
|
||||
if (str.equalsIgnoreCase("true")) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (str.equalsIgnoreCase("false")) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
// else - string cannot be converted to boolean
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
|
||||
if (ve instanceof NumericValueEval) {
|
||||
NumericValueEval ne = (NumericValueEval) ve;
|
||||
double d = ne.getNumberValue();
|
||||
if (Double.isNaN(d)) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return Boolean.valueOf(d != 0);
|
||||
}
|
||||
if (ve instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) ve);
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval (" + ve.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,85 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.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.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final class And extends BooleanFunction {
|
||||
|
||||
public class And extends BooleanFunction {
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
boolean b = true;
|
||||
boolean atleastOneNonBlank = false;
|
||||
|
||||
/*
|
||||
* Note: do not abort the loop if b is false, since we could be
|
||||
* dealing with errorevals later.
|
||||
*/
|
||||
outer:
|
||||
for (int i=0, iSize=operands.length; i<iSize; i++) {
|
||||
if (operands[i] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[i];
|
||||
ValueEval[] values = ae.getValues();
|
||||
for (int j=0, jSize=values.length; j<jSize; j++) {
|
||||
ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
|
||||
if (tempVe instanceof BoolEval) {
|
||||
b = b && ((BoolEval) tempVe).getBooleanValue();
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
|
||||
if (tempVe instanceof BoolEval) {
|
||||
b = b && ((BoolEval) tempVe).getBooleanValue();
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
else if (tempVe instanceof StringEval) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // if no error
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
protected boolean getInitialResultValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult && currentValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,100 +1,108 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.eval.BlankEval;
|
||||
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.NumericValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* Here are the general rules concerning Boolean functions:
|
||||
* <ol>
|
||||
* <li> Blanks are not either true or false
|
||||
* <li> Strings are not either true or false (even strings "true"
|
||||
* or "TRUE" or "0" etc.)
|
||||
* <li> Numbers: 0 is false. Any other number is TRUE.
|
||||
* <li> References are evaluated and above rules apply.
|
||||
* <li> Areas: Individual cells in area are evaluated and checked to
|
||||
* see if they are blanks, strings etc.
|
||||
* <li> Blanks are ignored (not either true or false) </li>
|
||||
* <li> Strings are ignored if part of an area ref or cell ref, otherwise they must be 'true' or 'false'</li>
|
||||
* <li> Numbers: 0 is false. Any other number is TRUE </li>
|
||||
* <li> Areas: *all* cells in area are evaluated according to the above rules</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public abstract class BooleanFunction implements Function {
|
||||
|
||||
protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol, boolean stringsAreBlanks) {
|
||||
ValueEval retval;
|
||||
|
||||
if (eval instanceof RefEval) {
|
||||
RefEval re = (RefEval) eval;
|
||||
ValueEval ve = re.getInnerValueEval();
|
||||
retval = internalResolve(ve, true);
|
||||
}
|
||||
else {
|
||||
retval = internalResolve(eval, stringsAreBlanks);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
private ValueEval internalResolve(Eval ve, boolean stringsAreBlanks) {
|
||||
ValueEval retval = null;
|
||||
|
||||
// blankeval is returned as is
|
||||
if (ve instanceof BlankEval) {
|
||||
retval = BlankEval.INSTANCE;
|
||||
}
|
||||
|
||||
// stringeval
|
||||
else if (ve instanceof StringEval) {
|
||||
retval = stringsAreBlanks ? (ValueEval) BlankEval.INSTANCE : (StringEval) ve;
|
||||
}
|
||||
|
||||
// bools are bools :)
|
||||
else if (ve instanceof BoolEval) {
|
||||
retval = (BoolEval) ve;
|
||||
}
|
||||
|
||||
// convert numbers to bool
|
||||
else if (ve instanceof NumericValueEval) {
|
||||
NumericValueEval ne = (NumericValueEval) ve;
|
||||
double d = ne.getNumberValue();
|
||||
retval = Double.isNaN(d)
|
||||
? (ValueEval) ErrorEval.VALUE_INVALID
|
||||
: (d != 0)
|
||||
? BoolEval.TRUE
|
||||
: BoolEval.FALSE;
|
||||
}
|
||||
|
||||
// since refevals
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
public final Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if (args.length < 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
boolean boolResult;
|
||||
try {
|
||||
boolResult = calculate(args);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return BoolEval.valueOf(boolResult);
|
||||
}
|
||||
|
||||
private boolean calculate(Eval[] args) throws EvaluationException {
|
||||
|
||||
boolean result = getInitialResultValue();
|
||||
boolean atleastOneNonBlank = false;
|
||||
|
||||
/*
|
||||
* Note: no short-circuit boolean loop exit because any ErrorEvals will override the result
|
||||
*/
|
||||
for (int i=0, iSize=args.length; i<iSize; i++) {
|
||||
Eval arg = args[i];
|
||||
if (arg instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) arg;
|
||||
int height = ae.getHeight();
|
||||
int width = ae.getWidth();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = ae.getRelativeValue(rrIx, rcIx);
|
||||
Boolean tempVe = OperandResolver.coerceValueToBoolean(ve, true);
|
||||
if (tempVe != null) {
|
||||
result = partialEvaluate(result, tempVe.booleanValue());
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Boolean tempVe;
|
||||
if (arg instanceof RefEval) {
|
||||
ValueEval ve = ((RefEval) arg).getInnerValueEval();
|
||||
tempVe = OperandResolver.coerceValueToBoolean(ve, true);
|
||||
} else if (arg instanceof ValueEval) {
|
||||
ValueEval ve = (ValueEval) arg;
|
||||
tempVe = OperandResolver.coerceValueToBoolean(ve, false);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected eval (" + arg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
if (tempVe != null) {
|
||||
result = partialEvaluate(result, tempVe.booleanValue());
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
protected abstract boolean getInitialResultValue();
|
||||
protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue);
|
||||
}
|
||||
|
|
|
@ -1,32 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.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.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
|
||||
/**
|
||||
* Counts the number of cells that contain numeric data within
|
||||
|
@ -39,7 +33,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|||
* TODO: Check this properly matches excel on edge cases
|
||||
* like formula cells, error cells etc
|
||||
*/
|
||||
public class Count implements Function {
|
||||
public final class Count implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
int nArgs = args.length;
|
||||
|
@ -56,63 +50,23 @@ public class Count implements Function {
|
|||
int temp = 0;
|
||||
|
||||
for(int i=0; i<nArgs; i++) {
|
||||
temp += countArg(args[i]);
|
||||
temp += CountUtils.countArg(args[i], predicate);
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
private static final I_MatchPredicate predicate = new I_MatchPredicate() {
|
||||
|
||||
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;
|
||||
public boolean matches(Eval valueEval) {
|
||||
|
||||
if(valueEval instanceof NumberEval) {
|
||||
// only numbers are counted
|
||||
return true;
|
||||
}
|
||||
temp += countValue(val);
|
||||
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static int countValue(ValueEval valueEval) {
|
||||
|
||||
if(valueEval == BlankEval.INSTANCE) {
|
||||
return 0;
|
||||
// error values and string values not counted
|
||||
return false;
|
||||
}
|
||||
|
||||
if(valueEval instanceof BlankEval) {
|
||||
// wouldn't need this if BlankEval was final
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(valueEval instanceof ErrorEval) {
|
||||
// note - error values not counted
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(valueEval instanceof NumberEval)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* ====================================================================
|
||||
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.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Common logic for COUNT, COUNTA and COUNTIF
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class CountUtils {
|
||||
|
||||
private CountUtils() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface for the matching criteria.
|
||||
*/
|
||||
public interface I_MatchPredicate {
|
||||
boolean matches(Eval x);
|
||||
}
|
||||
/**
|
||||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
public static int countMatchingCellsInArea(AreaEval areaEval, I_MatchPredicate criteriaPredicate) {
|
||||
int result = 0;
|
||||
|
||||
int height = areaEval.getHeight();
|
||||
int width = areaEval.getWidth();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
|
||||
if(criteriaPredicate.matches(ve)) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @return 1 if the evaluated cell matches the specified criteria
|
||||
*/
|
||||
public static int countMatchingCell(RefEval refEval, I_MatchPredicate criteriaPredicate) {
|
||||
if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) {
|
||||
if (eval instanceof AreaEval) {
|
||||
return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate);
|
||||
}
|
||||
if (eval instanceof RefEval) {
|
||||
return CountUtils.countMatchingCell((RefEval) eval, criteriaPredicate);
|
||||
}
|
||||
return criteriaPredicate.matches(eval) ? 1 : 0;
|
||||
}
|
||||
}
|
|
@ -1,31 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.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;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
|
||||
/**
|
||||
* Counts the number of cells that contain data within the list of arguments.
|
||||
|
@ -51,70 +47,26 @@ public final class Counta implements Function {
|
|||
}
|
||||
|
||||
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]);
|
||||
temp += CountUtils.countArg(args[i], predicate);
|
||||
|
||||
}
|
||||
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 final I_MatchPredicate predicate = new I_MatchPredicate() {
|
||||
|
||||
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;
|
||||
public boolean matches(Eval valueEval) {
|
||||
// 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
|
||||
|
||||
if(valueEval == BlankEval.INSTANCE) {
|
||||
return false;
|
||||
}
|
||||
temp += countValue(val);
|
||||
|
||||
// Note - everything but BlankEval counts
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
|
@ -28,7 +28,7 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
|||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
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;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
|
||||
/**
|
||||
* Implementation for the function COUNTIF<p/>
|
||||
|
@ -144,12 +144,6 @@ public final class Countif implements Function {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface for the matching criteria.
|
||||
*/
|
||||
/* package */ interface I_MatchPredicate {
|
||||
boolean matches(Eval x);
|
||||
}
|
||||
|
||||
private static final class NumberMatcher implements I_MatchPredicate {
|
||||
|
||||
|
@ -360,21 +354,12 @@ public final class Countif implements Function {
|
|||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
private Eval countMatchingCellsInArea(Eval rangeArg, I_MatchPredicate criteriaPredicate) {
|
||||
int result = 0;
|
||||
|
||||
int result;
|
||||
if (rangeArg instanceof RefEval) {
|
||||
RefEval refEval = (RefEval) rangeArg;
|
||||
if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
|
||||
result++;
|
||||
}
|
||||
result = CountUtils.countMatchingCell((RefEval) rangeArg, criteriaPredicate);
|
||||
} else if (rangeArg instanceof AreaEval) {
|
||||
|
||||
AreaEval range = (AreaEval) rangeArg;
|
||||
ValueEval[] values = range.getValues();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if(criteriaPredicate.matches(values[i])) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
result = CountUtils.countMatchingCellsInArea((AreaEval) rangeArg, criteriaPredicate);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad range arg type (" + rangeArg.getClass().getName() + ")");
|
||||
}
|
||||
|
|
|
@ -42,40 +42,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
|||
*/
|
||||
public final class Hlookup implements Function {
|
||||
|
||||
private static final class RowVector implements ValueVector {
|
||||
|
||||
private final AreaEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _rowAbsoluteIndex;
|
||||
private final int _firstColumnAbsoluteIndex;
|
||||
|
||||
public RowVector(AreaEval tableArray, int rowIndex) {
|
||||
_rowAbsoluteIndex = tableArray.getFirstRow() + rowIndex;
|
||||
if(!tableArray.containsRow(_rowAbsoluteIndex)) {
|
||||
int lastRowIx = tableArray.getLastRow() - tableArray.getFirstRow();
|
||||
throw new IllegalArgumentException("Specified row index (" + rowIndex
|
||||
+ ") is outside the allowed range (0.." + lastRowIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = tableArray.getLastColumn() - tableArray.getFirstColumn() + 1;
|
||||
if(_size < 1) {
|
||||
throw new RuntimeException("bad table array size zero");
|
||||
}
|
||||
_firstColumnAbsoluteIndex = tableArray.getFirstColumn();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index>_size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getValueAt(_rowAbsoluteIndex, (short) (_firstColumnAbsoluteIndex + index));
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
Eval arg3 = null;
|
||||
switch(args.length) {
|
||||
|
@ -93,7 +59,7 @@ public final class Hlookup implements Function {
|
|||
ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
|
||||
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
|
||||
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, new RowVector(tableArray, 0), isRangeLookup);
|
||||
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
|
||||
ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
|
||||
int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
|
||||
ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
|
||||
|
@ -113,11 +79,9 @@ public final class Hlookup implements Function {
|
|||
if(colIndex < 0) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
int nCols = tableArray.getLastColumn() - tableArray.getFirstRow() + 1;
|
||||
|
||||
if(colIndex >= nCols) {
|
||||
if(colIndex >= tableArray.getWidth()) {
|
||||
throw EvaluationException.invalidRef();
|
||||
}
|
||||
return new RowVector(tableArray, colIndex);
|
||||
return LookupUtils.createRowVector(tableArray, colIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,19 +40,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
|||
* @author Josh Micich
|
||||
*/
|
||||
public final class Lookup implements Function {
|
||||
private static final class SimpleValueVector implements ValueVector {
|
||||
private final ValueEval[] _values;
|
||||
|
||||
public SimpleValueVector(ValueEval[] values) {
|
||||
_values = values;
|
||||
}
|
||||
public ValueEval getItem(int index) {
|
||||
return _values[index];
|
||||
}
|
||||
public int getSize() {
|
||||
return _values.length;
|
||||
}
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
switch(args.length) {
|
||||
|
@ -86,11 +73,11 @@ public final class Lookup implements Function {
|
|||
}
|
||||
|
||||
private static ValueVector createVector(AreaEval ae) {
|
||||
|
||||
if(!ae.isRow() && !ae.isColumn()) {
|
||||
// extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
|
||||
throw new RuntimeException("non-vector lookup or result areas not supported yet");
|
||||
ValueVector result = LookupUtils.createVector(ae);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return new SimpleValueVector(ae.getValues());
|
||||
// extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
|
||||
throw new RuntimeException("non-vector lookup or result areas not supported yet");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,11 +34,11 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|||
|
||||
/**
|
||||
* Common functionality used by VLOOKUP, HLOOKUP, LOOKUP and MATCH
|
||||
*
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class LookupUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Represents a single row or column within an <tt>AreaEval</tt>.
|
||||
*/
|
||||
|
@ -46,14 +46,95 @@ final class LookupUtils {
|
|||
ValueEval getItem(int index);
|
||||
int getSize();
|
||||
}
|
||||
|
||||
|
||||
private static final class RowVector implements ValueVector {
|
||||
|
||||
private final AreaEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _rowIndex;
|
||||
|
||||
public RowVector(AreaEval tableArray, int rowIndex) {
|
||||
_rowIndex = rowIndex;
|
||||
int _rowAbsoluteIndex = tableArray.getFirstRow() + rowIndex;
|
||||
if(!tableArray.containsRow(_rowAbsoluteIndex)) {
|
||||
int lastRowIx = tableArray.getLastRow() - tableArray.getFirstRow();
|
||||
throw new IllegalArgumentException("Specified row index (" + rowIndex
|
||||
+ ") is outside the allowed range (0.." + lastRowIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = tableArray.getWidth();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index > _size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getRelativeValue(_rowIndex, index);
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ColumnVector implements ValueVector {
|
||||
|
||||
private final AreaEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _columnIndex;
|
||||
|
||||
public ColumnVector(AreaEval tableArray, int columnIndex) {
|
||||
_columnIndex = columnIndex;
|
||||
int _columnAbsoluteIndex = tableArray.getFirstColumn() + columnIndex;
|
||||
if(!tableArray.containsColumn((short)_columnAbsoluteIndex)) {
|
||||
int lastColIx = tableArray.getLastColumn() - tableArray.getFirstColumn();
|
||||
throw new IllegalArgumentException("Specified column index (" + columnIndex
|
||||
+ ") is outside the allowed range (0.." + lastColIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = _tableArray.getHeight();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index > _size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getRelativeValue(index, _columnIndex);
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
public static ValueVector createRowVector(AreaEval tableArray, int relativeRowIndex) {
|
||||
return new RowVector(tableArray, relativeRowIndex);
|
||||
}
|
||||
public static ValueVector createColumnVector(AreaEval tableArray, int relativeColumnIndex) {
|
||||
return new ColumnVector(tableArray, relativeColumnIndex);
|
||||
}
|
||||
/**
|
||||
* @return <code>null</code> if the supplied area is neither a single row nor a single colum
|
||||
*/
|
||||
public static ValueVector createVector(AreaEval ae) {
|
||||
if (ae.isColumn()) {
|
||||
return createColumnVector(ae, 0);
|
||||
}
|
||||
if (ae.isRow()) {
|
||||
return createRowVector(ae, 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration to support <b>4</b> valued comparison results.<p/>
|
||||
* Excel lookup functions have complex behaviour in the case where the lookup array has mixed
|
||||
* Excel lookup functions have complex behaviour in the case where the lookup array has mixed
|
||||
* types, and/or is unordered. Contrary to suggestions in some Excel documentation, there
|
||||
* does not appear to be a universal ordering across types. The binary search algorithm used
|
||||
* changes behaviour when the evaluated 'mid' value has a different type to the lookup value.<p/>
|
||||
*
|
||||
* A simple int might have done the same job, but there is risk in confusion with the well
|
||||
*
|
||||
* A simple int might have done the same job, but there is risk in confusion with the well
|
||||
* known <tt>Comparable.compareTo()</tt> and <tt>Comparator.compare()</tt> which both use
|
||||
* a ubiquitous 3 value result encoding.
|
||||
*/
|
||||
|
@ -80,7 +161,7 @@ final class LookupUtils {
|
|||
public static final CompareResult LESS_THAN = new CompareResult(false, -1);
|
||||
public static final CompareResult EQUAL = new CompareResult(false, 0);
|
||||
public static final CompareResult GREATER_THAN = new CompareResult(false, +1);
|
||||
|
||||
|
||||
public static final CompareResult valueOf(int simpleCompareResult) {
|
||||
if(simpleCompareResult < 0) {
|
||||
return LESS_THAN;
|
||||
|
@ -90,7 +171,7 @@ final class LookupUtils {
|
|||
}
|
||||
return EQUAL;
|
||||
}
|
||||
|
||||
|
||||
public boolean isTypeMismatch() {
|
||||
return _isTypeMismatch;
|
||||
}
|
||||
|
@ -128,17 +209,17 @@ final class LookupUtils {
|
|||
return "??error??";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface LookupValueComparer {
|
||||
/**
|
||||
* @return one of 4 instances or <tt>CompareResult</tt>: <tt>LESS_THAN</tt>, <tt>EQUAL</tt>,
|
||||
* @return one of 4 instances or <tt>CompareResult</tt>: <tt>LESS_THAN</tt>, <tt>EQUAL</tt>,
|
||||
* <tt>GREATER_THAN</tt> or <tt>TYPE_MISMATCH</tt>
|
||||
*/
|
||||
CompareResult compareTo(ValueEval other);
|
||||
}
|
||||
|
||||
|
||||
private static abstract class LookupValueComparerBase implements LookupValueComparer {
|
||||
|
||||
|
||||
private final Class _targetClass;
|
||||
protected LookupValueComparerBase(ValueEval targetValue) {
|
||||
if(targetValue == null) {
|
||||
|
@ -154,7 +235,7 @@ final class LookupUtils {
|
|||
return CompareResult.TYPE_MISMATCH;
|
||||
}
|
||||
if (_targetClass == StringEval.class) {
|
||||
|
||||
|
||||
}
|
||||
return compareSameType(other);
|
||||
}
|
||||
|
@ -169,7 +250,7 @@ final class LookupUtils {
|
|||
/** used only for debug purposes */
|
||||
protected abstract String getValueAsString();
|
||||
}
|
||||
|
||||
|
||||
private static final class StringLookupComparer extends LookupValueComparerBase {
|
||||
private String _value;
|
||||
|
||||
|
@ -223,9 +304,9 @@ final class LookupUtils {
|
|||
return String.valueOf(_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b>
|
||||
* Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b>
|
||||
* or <b>row_index_num</b> respectively).<br>
|
||||
* Sample behaviour:
|
||||
* <table border="0" cellpadding="1" cellspacing="2" summary="Sample behaviour">
|
||||
|
@ -242,17 +323,17 @@ final class LookupUtils {
|
|||
* <tr><td>""</td><td> </td><td>#REF!</td></tr>
|
||||
* <tr><td><blank></td><td> </td><td>#VALUE!</td></tr>
|
||||
* </table><br/>
|
||||
*
|
||||
* * Note - out of range errors (both too high and too low) are handled by the caller.
|
||||
*
|
||||
* * Note - out of range errors (both too high and too low) are handled by the caller.
|
||||
* @return column or row index as a zero-based value
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static int resolveRowOrColIndexArg(ValueEval veRowColIndexArg) throws EvaluationException {
|
||||
if(veRowColIndexArg == null) {
|
||||
throw new IllegalArgumentException("argument must not be null");
|
||||
}
|
||||
if(veRowColIndexArg instanceof BlankEval) {
|
||||
throw EvaluationException.invalidValue();
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
if(veRowColIndexArg instanceof StringEval) {
|
||||
StringEval se = (StringEval) veRowColIndexArg;
|
||||
|
@ -260,7 +341,7 @@ final class LookupUtils {
|
|||
Double dVal = OperandResolver.parseDouble(strVal);
|
||||
if(dVal == null) {
|
||||
// String does not resolve to a number. Raise #VALUE! error.
|
||||
throw EvaluationException.invalidRef();
|
||||
throw EvaluationException.invalidRef();
|
||||
// This includes text booleans "TRUE" and "FALSE". They are not valid.
|
||||
}
|
||||
// else - numeric value parses OK
|
||||
|
@ -268,9 +349,9 @@ final class LookupUtils {
|
|||
// actual BoolEval values get interpreted as FALSE->0 and TRUE->1
|
||||
return OperandResolver.coerceValueToInt(veRowColIndexArg) - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The second argument (table_array) should be an area ref, but can actually be a cell ref, in
|
||||
* which case it is interpreted as a 1x1 area ref. Other scalar values cause #VALUE! error.
|
||||
|
@ -279,13 +360,13 @@ final class LookupUtils {
|
|||
if (eval instanceof AreaEval) {
|
||||
return (AreaEval) eval;
|
||||
}
|
||||
|
||||
|
||||
if(eval instanceof RefEval) {
|
||||
RefEval refEval = (RefEval) eval;
|
||||
// Make this cell ref look like a 1x1 area ref.
|
||||
|
||||
|
||||
// It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval.
|
||||
// This code only requires the value array item.
|
||||
// This code only requires the value array item.
|
||||
// anything would be ok for rowIx and colIx, but may as well get it right.
|
||||
int rowIx = refEval.getRow();
|
||||
int colIx = refEval.getColumn();
|
||||
|
@ -295,10 +376,10 @@ final class LookupUtils {
|
|||
}
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions.
|
||||
* Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions.
|
||||
* @param rangeLookupArg
|
||||
* @param srcCellRow
|
||||
* @param srcCellCol
|
||||
|
@ -318,7 +399,7 @@ final class LookupUtils {
|
|||
return false;
|
||||
}
|
||||
if(valEval instanceof BoolEval) {
|
||||
// Happy day flow
|
||||
// Happy day flow
|
||||
BoolEval boolEval = (BoolEval) valEval;
|
||||
return boolEval.getBooleanValue();
|
||||
}
|
||||
|
@ -327,7 +408,7 @@ final class LookupUtils {
|
|||
String stringValue = ((StringEval) valEval).getStringValue();
|
||||
if(stringValue.length() < 1) {
|
||||
// More trickiness:
|
||||
// Empty string is not the same as BlankEval. It causes #VALUE! error
|
||||
// Empty string is not the same as BlankEval. It causes #VALUE! error
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
// TODO move parseBoolean to OperandResolver
|
||||
|
@ -337,10 +418,10 @@ final class LookupUtils {
|
|||
return b.booleanValue();
|
||||
}
|
||||
// Even more trickiness:
|
||||
// Note - even if the StringEval represents a number value (for example "1"),
|
||||
// Excel does not resolve it to a boolean.
|
||||
// Note - even if the StringEval represents a number value (for example "1"),
|
||||
// Excel does not resolve it to a boolean.
|
||||
throw EvaluationException.invalidValue();
|
||||
// This is in contrast to the code below,, where NumberEvals values (for
|
||||
// This is in contrast to the code below,, where NumberEvals values (for
|
||||
// example 0.01) *do* resolve to equivalent boolean values.
|
||||
}
|
||||
if (valEval instanceof NumericValueEval) {
|
||||
|
@ -350,7 +431,7 @@ final class LookupUtils {
|
|||
}
|
||||
throw new RuntimeException("Unexpected eval type (" + valEval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
public static int lookupIndexOfValue(ValueEval lookupValue, ValueVector vector, boolean isRangeLookup) throws EvaluationException {
|
||||
LookupValueComparer lookupComparer = createLookupComparer(lookupValue);
|
||||
int result;
|
||||
|
@ -364,13 +445,13 @@ final class LookupUtils {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Finds first (lowest index) exact occurrence of specified value.
|
||||
* @param lookupValue the value to be found in column or row vector
|
||||
* @param vector the values to be searched. For VLOOKUP this is the first column of the
|
||||
* tableArray. For HLOOKUP this is the first row of the tableArray.
|
||||
* @param vector the values to be searched. For VLOOKUP this is the first column of the
|
||||
* tableArray. For HLOOKUP this is the first row of the tableArray.
|
||||
* @return zero based index into the vector, -1 if value cannot be found
|
||||
*/
|
||||
private static int lookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) {
|
||||
|
@ -385,10 +466,10 @@ final class LookupUtils {
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates some standard binary search functionality so the unusual Excel behaviour can
|
||||
* be clearly distinguished.
|
||||
* be clearly distinguished.
|
||||
*/
|
||||
private static final class BinarySearchIndexes {
|
||||
|
||||
|
@ -427,7 +508,7 @@ final class LookupUtils {
|
|||
}
|
||||
/**
|
||||
* Excel has funny behaviour when the some elements in the search vector are the wrong type.
|
||||
*
|
||||
*
|
||||
*/
|
||||
private static int performBinarySearch(ValueVector vector, LookupValueComparer lookupComparer) {
|
||||
// both low and high indexes point to values assumed too low and too high.
|
||||
|
@ -435,7 +516,7 @@ final class LookupUtils {
|
|||
|
||||
while(true) {
|
||||
int midIx = bsi.getMidIx();
|
||||
|
||||
|
||||
if(midIx < 0) {
|
||||
return bsi.getLowIx();
|
||||
}
|
||||
|
@ -455,17 +536,17 @@ final class LookupUtils {
|
|||
}
|
||||
}
|
||||
/**
|
||||
* Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the
|
||||
* Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the
|
||||
* first compatible value.
|
||||
* @param midIx 'mid' index (value which has the wrong type)
|
||||
* @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid
|
||||
* @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid
|
||||
* index. Zero or greater signifies that an exact match for the lookup value was found
|
||||
*/
|
||||
private static int handleMidValueTypeMismatch(LookupValueComparer lookupComparer, ValueVector vector,
|
||||
BinarySearchIndexes bsi, int midIx) {
|
||||
int newMid = midIx;
|
||||
int highIx = bsi.getHighIx();
|
||||
|
||||
|
||||
while(true) {
|
||||
newMid++;
|
||||
if(newMid == highIx) {
|
||||
|
@ -511,9 +592,9 @@ final class LookupUtils {
|
|||
}
|
||||
|
||||
public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException {
|
||||
|
||||
|
||||
if (lookupValue instanceof BlankEval) {
|
||||
// blank eval can never be found in a lookup array
|
||||
// blank eval can never be found in a lookup array
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
if (lookupValue instanceof StringEval) {
|
||||
|
|
|
@ -29,18 +29,19 @@ import org.apache.poi.hssf.record.formula.eval.StringEval;
|
|||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.CompareResult;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.LookupValueComparer;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
||||
|
||||
/**
|
||||
* Implementation for the MATCH() Excel function.<p/>
|
||||
*
|
||||
*
|
||||
* <b>Syntax:</b><br/>
|
||||
* <b>MATCH</b>(<b>lookup_value</b>, <b>lookup_array</b>, match_type)<p/>
|
||||
*
|
||||
* Returns a 1-based index specifying at what position in the <b>lookup_array</b> the specified
|
||||
*
|
||||
* Returns a 1-based index specifying at what position in the <b>lookup_array</b> the specified
|
||||
* <b>lookup_value</b> is found.<p/>
|
||||
*
|
||||
*
|
||||
* Specific matching behaviour can be modified with the optional <b>match_type</b> parameter.
|
||||
*
|
||||
*
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="match_type parameter description">
|
||||
* <tr><th>Value</th><th>Matching Behaviour</th></tr>
|
||||
* <tr><td>1</td><td>(default) find the largest value that is less than or equal to lookup_value.
|
||||
|
@ -50,26 +51,26 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.LookupValueCompa
|
|||
* <tr><td>-1</td><td>find the smallest value that is greater than or equal to lookup_value.
|
||||
* The lookup_array must be in descending <i>order</i>*.</td></tr>
|
||||
* </table>
|
||||
*
|
||||
*
|
||||
* * Note regarding <i>order</i> - For the <b>match_type</b> cases that require the lookup_array to
|
||||
* be ordered, MATCH() can produce incorrect results if this requirement is not met. Observed
|
||||
* behaviour in Excel is to return the lowest index value for which every item after that index
|
||||
* breaks the match rule.<br>
|
||||
* The (ascending) sort order expected by MATCH() is:<br/>
|
||||
* numbers (low to high), strings (A to Z), boolean (FALSE to TRUE)<br/>
|
||||
* MATCH() ignores all elements in the lookup_array with a different type to the lookup_value.
|
||||
* MATCH() ignores all elements in the lookup_array with a different type to the lookup_value.
|
||||
* Type conversion of the lookup_array elements is never performed.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Match implements Function {
|
||||
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
|
||||
|
||||
double match_type = 1; // default
|
||||
|
||||
|
||||
switch(args.length) {
|
||||
case 3:
|
||||
try {
|
||||
|
@ -85,15 +86,15 @@ public final class Match implements Function {
|
|||
default:
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
|
||||
boolean matchExact = match_type == 0;
|
||||
// Note - Excel does not strictly require -1 and +1
|
||||
boolean findLargestLessThanOrEqual = match_type > 0;
|
||||
|
||||
|
||||
|
||||
|
||||
try {
|
||||
ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
ValueEval[] lookupRange = evaluateLookupRange(args[1]);
|
||||
ValueVector lookupRange = evaluateLookupRange(args[1]);
|
||||
int index = findIndexOfValue(lookupValue, lookupRange, matchExact, findLargestLessThanOrEqual);
|
||||
return new NumberEval(index + 1); // +1 to convert to 1-based
|
||||
} catch (EvaluationException e) {
|
||||
|
@ -101,19 +102,40 @@ public final class Match implements Function {
|
|||
}
|
||||
}
|
||||
|
||||
private static ValueEval[] evaluateLookupRange(Eval eval) throws EvaluationException {
|
||||
private static final class SingleValueVector implements ValueVector {
|
||||
|
||||
private final ValueEval _value;
|
||||
|
||||
public SingleValueVector(ValueEval value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if (index != 0) {
|
||||
throw new RuntimeException("Invalid index ("
|
||||
+ index + ") only zero is allowed");
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueVector evaluateLookupRange(Eval eval) throws EvaluationException {
|
||||
if (eval instanceof RefEval) {
|
||||
RefEval re = (RefEval) eval;
|
||||
return new ValueEval[] { re.getInnerValueEval(), };
|
||||
return new SingleValueVector(re.getInnerValueEval());
|
||||
}
|
||||
if (eval instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) eval;
|
||||
if(!ae.isColumn() && !ae.isRow()) {
|
||||
ValueVector result = LookupUtils.createVector((AreaEval)eval);
|
||||
if (result == null) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
return ae.getValues();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Error handling for lookup_range arg is also unusual
|
||||
if(eval instanceof NumericValueEval) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
|
@ -133,7 +155,7 @@ public final class Match implements Function {
|
|||
|
||||
|
||||
|
||||
private static double evaluateMatchTypeArg(Eval arg, int srcCellRow, short srcCellCol)
|
||||
private static double evaluateMatchTypeArg(Eval arg, int srcCellRow, short srcCellCol)
|
||||
throws EvaluationException {
|
||||
Eval match_type = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
|
||||
|
@ -156,28 +178,29 @@ public final class Match implements Function {
|
|||
}
|
||||
throw new RuntimeException("Unexpected match_type type (" + match_type.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return zero based index
|
||||
*/
|
||||
private static int findIndexOfValue(ValueEval lookupValue, ValueEval[] lookupRange,
|
||||
private static int findIndexOfValue(ValueEval lookupValue, ValueVector lookupRange,
|
||||
boolean matchExact, boolean findLargestLessThanOrEqual) throws EvaluationException {
|
||||
|
||||
LookupValueComparer lookupComparer = createLookupComparer(lookupValue, matchExact);
|
||||
|
||||
|
||||
int size = lookupRange.getSize();
|
||||
if(matchExact) {
|
||||
for (int i = 0; i < lookupRange.length; i++) {
|
||||
if(lookupComparer.compareTo(lookupRange[i]).isEqual()) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if(lookupComparer.compareTo(lookupRange.getItem(i)).isEqual()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
|
||||
if(findLargestLessThanOrEqual) {
|
||||
// Note - backward iteration
|
||||
for (int i = lookupRange.length - 1; i>=0; i--) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange[i]);
|
||||
for (int i = size - 1; i>=0; i--) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
|
||||
if(cmp.isTypeMismatch()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -187,11 +210,11 @@ public final class Match implements Function {
|
|||
}
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
|
||||
// else - find smallest greater than or equal to
|
||||
// TODO - is binary search used for (match_type==+1) ?
|
||||
for (int i = 0; i<lookupRange.length; i++) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange[i]);
|
||||
for (int i = 0; i<size; i++) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
|
||||
if(cmp.isEqual()) {
|
||||
return i;
|
||||
}
|
||||
|
@ -212,7 +235,7 @@ public final class Match implements Function {
|
|||
if(isLookupValueWild(stringValue)) {
|
||||
throw new RuntimeException("Wildcard lookup values '" + stringValue + "' not supported yet");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return LookupUtils.createLookupComparer(lookupValue);
|
||||
}
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.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.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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
|
||||
|
@ -37,87 +31,21 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|||
* (treated as a boolean). If the specified arg is a number,
|
||||
* then it is true <=> 'number is non-zero'
|
||||
*/
|
||||
public class Not extends BooleanFunction {
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
boolean b = true;
|
||||
ValueEval tempVe = null;
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 1:
|
||||
if (operands[0] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[0];
|
||||
if (ae.isRow() && ae.containsColumn(srcCol)) {
|
||||
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
|
||||
tempVe = singleOperandEvaluate(ve);
|
||||
} else if (ae.isColumn() && ae.containsRow(srcRow)) {
|
||||
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
|
||||
tempVe = singleOperandEvaluate(ve);
|
||||
} else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
tempVe = singleOperandEvaluate(operands[0]);
|
||||
if (tempVe instanceof StringEval) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) { // if no error
|
||||
if (tempVe instanceof BoolEval) {
|
||||
b = b && ((BoolEval) tempVe).getBooleanValue();
|
||||
}
|
||||
else if (tempVe instanceof StringEval) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
}
|
||||
retval = b ? BoolEval.FALSE : BoolEval.TRUE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
protected ValueEval singleOperandEvaluate(Eval ve) {
|
||||
ValueEval retval = ErrorEval.VALUE_INVALID;
|
||||
if (ve instanceof RefEval) {
|
||||
RefEval re = (RefEval) ve;
|
||||
retval = singleOperandEvaluate(re.getInnerValueEval());
|
||||
}
|
||||
else if (ve instanceof BoolEval) {
|
||||
retval = (BoolEval) ve;
|
||||
}
|
||||
else if (ve instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) ve;
|
||||
retval = ne.getNumberValue() != 0 ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
else if (ve instanceof StringEval) {
|
||||
StringEval se = (StringEval) ve;
|
||||
String str = se.getStringValue();
|
||||
retval = str.equalsIgnoreCase("true")
|
||||
? BoolEval.TRUE
|
||||
: str.equalsIgnoreCase("false")
|
||||
? BoolEval.FALSE
|
||||
: (ValueEval) ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
retval = BoolEval.FALSE;
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
public final class Not implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
boolean boolArgVal;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
|
||||
boolArgVal = b == null ? false : b.booleanValue();
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return BoolEval.valueOf(!boolArgVal);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.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.ValueEval;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final class Or extends BooleanFunction {
|
||||
|
||||
public class Or extends BooleanFunction {
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
boolean b = false;
|
||||
boolean atleastOneNonBlank = false;
|
||||
|
||||
/*
|
||||
* Note: do not abort the loop if b is true, since we could be
|
||||
* dealing with errorevals later.
|
||||
*/
|
||||
outer:
|
||||
for (int i=0, iSize=operands.length; i<iSize; i++) {
|
||||
if (operands[i] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[i];
|
||||
ValueEval[] values = ae.getValues();
|
||||
for (int j=0, jSize=values.length; j<jSize; j++) {
|
||||
ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
|
||||
if (tempVe instanceof BoolEval) {
|
||||
b = b || ((BoolEval) tempVe).getBooleanValue();
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
|
||||
if (tempVe instanceof BoolEval) {
|
||||
b = b || ((BoolEval) tempVe).getBooleanValue();
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
else if (tempVe instanceof ErrorEval) {
|
||||
retval = tempVe;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // if no error
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
protected boolean getInitialResultValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult || currentValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ 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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
|
@ -53,16 +54,6 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|||
*/
|
||||
public final class Sumproduct implements Function {
|
||||
|
||||
private static final class EvalEx extends Exception {
|
||||
private final ErrorEval _error;
|
||||
|
||||
public EvalEx(ErrorEval error) {
|
||||
_error = error;
|
||||
}
|
||||
public ErrorEval getError() {
|
||||
return _error;
|
||||
}
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
|
||||
|
@ -86,14 +77,14 @@ public final class Sumproduct implements Function {
|
|||
}
|
||||
return evaluateAreaSumProduct(args);
|
||||
}
|
||||
} catch (EvalEx e) {
|
||||
return e.getError();
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
throw new RuntimeException("Invalid arg type for SUMPRODUCT: ("
|
||||
+ firstArg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
private Eval evaluateSingleProduct(Eval[] evalArgs) throws EvalEx {
|
||||
private static Eval evaluateSingleProduct(Eval[] evalArgs) throws EvaluationException {
|
||||
int maxN = evalArgs.length;
|
||||
|
||||
double term = 1D;
|
||||
|
@ -104,7 +95,7 @@ public final class Sumproduct implements Function {
|
|||
return new NumberEval(term);
|
||||
}
|
||||
|
||||
private double getScalarValue(Eval arg) throws EvalEx {
|
||||
private static double getScalarValue(Eval arg) throws EvaluationException {
|
||||
|
||||
Eval eval;
|
||||
if (arg instanceof RefEval) {
|
||||
|
@ -121,9 +112,9 @@ public final class Sumproduct implements Function {
|
|||
AreaEval ae = (AreaEval) eval;
|
||||
// an area ref can work as a scalar value if it is 1x1
|
||||
if(!ae.isColumn() || !ae.isRow()) {
|
||||
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
eval = ae.getValues()[0];
|
||||
eval = ae.getRelativeValue(0, 0);
|
||||
}
|
||||
|
||||
if (!(eval instanceof ValueEval)) {
|
||||
|
@ -134,7 +125,7 @@ public final class Sumproduct implements Function {
|
|||
return getProductTerm((ValueEval) eval, true);
|
||||
}
|
||||
|
||||
private Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvalEx {
|
||||
private static Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvaluationException {
|
||||
int maxN = evalArgs.length;
|
||||
AreaEval[] args = new AreaEval[maxN];
|
||||
try {
|
||||
|
@ -147,23 +138,27 @@ public final class Sumproduct implements Function {
|
|||
|
||||
AreaEval firstArg = args[0];
|
||||
|
||||
int height = firstArg.getLastRow() - firstArg.getFirstRow() + 1;
|
||||
int width = firstArg.getLastColumn() - firstArg.getFirstColumn() + 1; // TODO - junit
|
||||
int height = firstArg.getHeight();
|
||||
int width = firstArg.getWidth(); // TODO - junit
|
||||
|
||||
|
||||
|
||||
double[][][] elements = new double[maxN][][];
|
||||
|
||||
for (int n = 0; n < maxN; n++) {
|
||||
elements[n] = evaluateArea(args[n], height, width);
|
||||
// first check dimensions
|
||||
if (!areasAllSameSize(args, height, width)) {
|
||||
// normally this results in #VALUE!,
|
||||
// but errors in individual cells take precedence
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
throwFirstError(args[i]);
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
double acc = 0;
|
||||
|
||||
for(int r=0; r<height; r++) {
|
||||
for(int c=0; c<width; c++) {
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
double term = 1D;
|
||||
for(int n=0; n<maxN; n++) {
|
||||
term *= elements[n][r][c];
|
||||
double val = getProductTerm(args[n].getRelativeValue(rrIx, rcIx), false);
|
||||
term *= val;
|
||||
}
|
||||
acc += term;
|
||||
}
|
||||
|
@ -172,60 +167,61 @@ public final class Sumproduct implements Function {
|
|||
return new NumberEval(acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a 2-D array of the specified height and width corresponding to the evaluated cell
|
||||
* values of the specified areaEval
|
||||
* @throws EvalEx if any ErrorEval value was encountered while evaluating the area
|
||||
*/
|
||||
private static double[][] evaluateArea(AreaEval areaEval, int height, int width) throws EvalEx {
|
||||
int fr =areaEval.getFirstRow();
|
||||
int fc =areaEval.getFirstColumn();
|
||||
|
||||
// check that height and width match
|
||||
if(areaEval.getLastRow() - fr + 1 != height) {
|
||||
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
if(areaEval.getLastColumn() - fc + 1 != width) {
|
||||
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
ValueEval[] values = areaEval.getValues();
|
||||
double[][] result = new double[height][width];
|
||||
for(int r=0; r<height; r++) {
|
||||
for(int c=0; c<width; c++) {
|
||||
ValueEval ve = values[r*width + c];
|
||||
result[r][c] = getProductTerm(ve, false);
|
||||
private static void throwFirstError(AreaEval areaEval) throws EvaluationException {
|
||||
int height = areaEval.getHeight();
|
||||
int width = areaEval.getWidth();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
|
||||
if (ve instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean areasAllSameSize(AreaEval[] args, int height, int width) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
AreaEval areaEval = args[i];
|
||||
// check that height and width match
|
||||
if(areaEval.getHeight() != height) {
|
||||
return false;
|
||||
}
|
||||
if(areaEval.getWidth() != width) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines a <code>double</code> value for the specified <code>ValueEval</code>.
|
||||
* @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs.
|
||||
* @throws EvalEx if <code>ve</code> represents an error value.
|
||||
* @throws EvaluationException if <code>ve</code> represents an error value.
|
||||
* <p/>
|
||||
* Note - string values and empty cells are interpreted differently depending on
|
||||
* <code>isScalarProduct</code>. For scalar products, if any term is blank or a string, the
|
||||
* error (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the
|
||||
* result is zero.
|
||||
*/
|
||||
private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvalEx {
|
||||
private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvaluationException {
|
||||
|
||||
if(ve instanceof BlankEval || ve == null) {
|
||||
// TODO - shouldn't BlankEval.INSTANCE be used always instead of null?
|
||||
// null seems to occur when the blank cell is part of an area ref (but not reliably)
|
||||
if(isScalarProduct) {
|
||||
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(ve instanceof ErrorEval) {
|
||||
throw new EvalEx((ErrorEval)ve);
|
||||
throw new EvaluationException((ErrorEval)ve);
|
||||
}
|
||||
if(ve instanceof StringEval) {
|
||||
if(isScalarProduct) {
|
||||
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
// Note for area SUMPRODUCTs, string values are interpreted as zero
|
||||
// even if they would parse as valid numeric values
|
||||
|
|
|
@ -42,40 +42,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
|||
*/
|
||||
public final class Vlookup implements Function {
|
||||
|
||||
private static final class ColumnVector implements ValueVector {
|
||||
|
||||
private final AreaEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _columnAbsoluteIndex;
|
||||
private final int _firstRowAbsoluteIndex;
|
||||
|
||||
public ColumnVector(AreaEval tableArray, int columnIndex) {
|
||||
_columnAbsoluteIndex = tableArray.getFirstColumn() + columnIndex;
|
||||
if(!tableArray.containsColumn((short)_columnAbsoluteIndex)) {
|
||||
int lastColIx = tableArray.getLastColumn() - tableArray.getFirstColumn();
|
||||
throw new IllegalArgumentException("Specified column index (" + columnIndex
|
||||
+ ") is outside the allowed range (0.." + lastColIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = tableArray.getLastRow() - tableArray.getFirstRow() + 1;
|
||||
if(_size < 1) {
|
||||
throw new RuntimeException("bad table array size zero");
|
||||
}
|
||||
_firstRowAbsoluteIndex = tableArray.getFirstRow();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index>_size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getValueAt(_firstRowAbsoluteIndex + index, (short)_columnAbsoluteIndex);
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
Eval arg3 = null;
|
||||
switch(args.length) {
|
||||
|
@ -93,7 +59,7 @@ public final class Vlookup implements Function {
|
|||
ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
|
||||
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
|
||||
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, new ColumnVector(tableArray, 0), isRangeLookup);
|
||||
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
|
||||
ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
|
||||
int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
|
||||
ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
|
||||
|
@ -113,11 +79,9 @@ public final class Vlookup implements Function {
|
|||
if(colIndex < 0) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
int nCols = tableArray.getLastColumn() - tableArray.getFirstColumn() + 1;
|
||||
|
||||
if(colIndex >= nCols) {
|
||||
if(colIndex >= tableArray.getWidth()) {
|
||||
throw EvaluationException.invalidRef();
|
||||
}
|
||||
return new ColumnVector(tableArray, colIndex);
|
||||
return LookupUtils.createColumnVector(tableArray, colIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -296,7 +296,7 @@ public class HSSFCell implements Cell {
|
|||
|
||||
if (cellType != this.cellType)
|
||||
{
|
||||
frec = new FormulaRecordAggregate(new FormulaRecord(),null);
|
||||
frec = new FormulaRecordAggregate(new FormulaRecord());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -592,41 +592,27 @@ public class HSSFCell implements Cell {
|
|||
int row=record.getRow();
|
||||
short col=record.getColumn();
|
||||
short styleIndex=record.getXFIndex();
|
||||
//Workbook.currentBook=book;
|
||||
|
||||
if (formula==null) {
|
||||
setCellType(CELL_TYPE_BLANK,false,row,col,styleIndex);
|
||||
} else {
|
||||
setCellType(CELL_TYPE_FORMULA,false,row,col,styleIndex);
|
||||
FormulaRecordAggregate rec = (FormulaRecordAggregate) record;
|
||||
FormulaRecord frec = rec.getFormulaRecord();
|
||||
frec.setOptions(( short ) 2);
|
||||
frec.setValue(0);
|
||||
|
||||
//only set to default if there is no extended format index already set
|
||||
if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, book);
|
||||
int size = 0;
|
||||
|
||||
// clear the Ptg Stack
|
||||
for (int i=0, iSize=frec.getNumberOfExpressionTokens(); i<iSize; i++) {
|
||||
frec.popExpressionToken();
|
||||
}
|
||||
|
||||
// fill the Ptg Stack with Ptgs of new formula
|
||||
for (int k = 0; k < ptgs.length; k++) {
|
||||
size += ptgs[ k ].getSize();
|
||||
frec.pushExpressionToken(ptgs[ k ]);
|
||||
}
|
||||
rec.getFormulaRecord().setExpressionLength(( short ) size);
|
||||
//Workbook.currentBook = null;
|
||||
setCellType(CELL_TYPE_BLANK, false, row, col, styleIndex);
|
||||
return;
|
||||
}
|
||||
setCellType(CELL_TYPE_FORMULA, false, row, col, styleIndex);
|
||||
FormulaRecordAggregate rec = (FormulaRecordAggregate) record;
|
||||
FormulaRecord frec = rec.getFormulaRecord();
|
||||
frec.setOptions((short) 2);
|
||||
frec.setValue(0);
|
||||
|
||||
//only set to default if there is no extended format index already set
|
||||
if (rec.getXFIndex() == (short)0) {
|
||||
rec.setXFIndex((short) 0x0f);
|
||||
}
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, book);
|
||||
frec.setParsedExpression(ptgs);
|
||||
}
|
||||
|
||||
public String getCellFormula() {
|
||||
//Workbook.currentBook=book;
|
||||
String retval = FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
|
||||
//Workbook.currentBook=null;
|
||||
return retval;
|
||||
return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* ====================================================================
|
||||
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.poifs.dev;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.*;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* Dump internal structure of a OLE2 file into file system
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class POIFSDump {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
System.out.println("Dumping " + args[i]);
|
||||
FileInputStream is = new FileInputStream(args[i]);
|
||||
POIFSFileSystem fs = new POIFSFileSystem(is);
|
||||
is.close();
|
||||
|
||||
DirectoryEntry root = fs.getRoot();
|
||||
File file = new File(root.getName());
|
||||
file.mkdir();
|
||||
|
||||
dump(root, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void dump(DirectoryEntry root, File parent) throws IOException {
|
||||
for(Iterator it = root.getEntries(); it.hasNext();){
|
||||
Entry entry = (Entry)it.next();
|
||||
if(entry instanceof DocumentNode){
|
||||
DocumentNode node = (DocumentNode)entry;
|
||||
DocumentInputStream is = new DocumentInputStream(node);
|
||||
byte[] bytes = new byte[node.getSize()];
|
||||
is.read(bytes);
|
||||
is.close();
|
||||
|
||||
FileOutputStream out = new FileOutputStream(new File(parent, node.getName().trim()));
|
||||
out.write(bytes);
|
||||
out.close();
|
||||
} else if (entry instanceof DirectoryEntry){
|
||||
DirectoryEntry dir = (DirectoryEntry)entry;
|
||||
File file = new File(parent, entry.getName());
|
||||
file.mkdir();
|
||||
dump(dir, file);
|
||||
} else {
|
||||
System.err.println("Skipping unsupported POIFS entry: " + entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -102,7 +102,7 @@ public class FormulaEvaluator {
|
|||
|
||||
/**
|
||||
* Does nothing
|
||||
* @deprecated - not needed, since the current row can be derived from the cell
|
||||
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
||||
*/
|
||||
public void setCurrentRow(Row row) {}
|
||||
|
||||
|
@ -437,7 +437,7 @@ public class FormulaEvaluator {
|
|||
AreaEval ae = (AreaEval) evaluationResult;
|
||||
if (ae.isRow()) {
|
||||
if(ae.isColumn()) {
|
||||
return ae.getValues()[0];
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -15,11 +14,17 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
|
@ -29,27 +34,16 @@ import java.text.DecimalFormat;
|
|||
* @author Marc Johnson
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
|
||||
public class HexDump
|
||||
{
|
||||
public static final String EOL =
|
||||
System.getProperty("line.separator");
|
||||
// private static final StringBuffer _lbuffer = new StringBuffer(8);
|
||||
// private static final StringBuffer _cbuffer = new StringBuffer(2);
|
||||
private static final char _hexcodes[] =
|
||||
{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
|
||||
'E', 'F'
|
||||
};
|
||||
private static final int _shifts[] =
|
||||
public class HexDump {
|
||||
public static final String EOL = System.getProperty("line.separator");
|
||||
private static final char _hexcodes[] = "0123456789ABCDEF".toCharArray();
|
||||
private static final int _shifts[] =
|
||||
{
|
||||
60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0
|
||||
};
|
||||
|
||||
|
||||
// all static methods, so no need for a public constructor
|
||||
private HexDump()
|
||||
{
|
||||
private HexDump() {
|
||||
// all static methods, so no need for a public constructor
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +63,7 @@ public class HexDump
|
|||
* @exception IllegalArgumentException if the output stream is
|
||||
* null
|
||||
*/
|
||||
public synchronized static void dump(final byte [] data, final long offset,
|
||||
public static void dump(final byte [] data, final long offset,
|
||||
final OutputStream stream, final int index, final int length)
|
||||
throws IOException, ArrayIndexOutOfBoundsException,
|
||||
IllegalArgumentException
|
||||
|
@ -413,6 +407,50 @@ public class HexDump
|
|||
byte[] data = buf.toByteArray();
|
||||
dump(data, 0, out, start, data.length);
|
||||
}
|
||||
/**
|
||||
* @return char array of uppercase hex chars, zero padded and prefixed with '0x'
|
||||
*/
|
||||
private static char[] toHexChars(long pValue, int nBytes) {
|
||||
int charPos = 2 + nBytes*2;
|
||||
// The return type is char array because most callers will probably append the value to a
|
||||
// StringBuffer, or write it to a Stream / Writer so there is no need to create a String;
|
||||
char[] result = new char[charPos];
|
||||
|
||||
long value = pValue;
|
||||
do {
|
||||
result[--charPos] = _hexcodes[(int) (value & 0x0F)];
|
||||
value >>>= 4;
|
||||
} while (charPos > 1);
|
||||
|
||||
// Prefix added to avoid ambiguity
|
||||
result[0] = '0';
|
||||
result[1] = 'x';
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @return char array of 4 (zero padded) uppercase hex chars and prefixed with '0x'
|
||||
*/
|
||||
public static char[] longToHex(long value) {
|
||||
return toHexChars(value, 8);
|
||||
}
|
||||
/**
|
||||
* @return char array of 4 (zero padded) uppercase hex chars and prefixed with '0x'
|
||||
*/
|
||||
public static char[] intToHex(int value) {
|
||||
return toHexChars(value, 4);
|
||||
}
|
||||
/**
|
||||
* @return char array of 2 (zero padded) uppercase hex chars and prefixed with '0x'
|
||||
*/
|
||||
public static char[] shortToHex(int value) {
|
||||
return toHexChars(value, 2);
|
||||
}
|
||||
/**
|
||||
* @return char array of 1 (zero padded) uppercase hex chars and prefixed with '0x'
|
||||
*/
|
||||
public static char[] byteToHex(int value) {
|
||||
return toHexChars(value, 1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
File file = new File(args[0]);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.poi.hslf.blip;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.hslf.model.Picture;
|
||||
import org.apache.poi.hslf.model.Shape;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
|
@ -76,7 +77,7 @@ public class WMF extends Metafile {
|
|||
Header header = new Header();
|
||||
header.wmfsize = data.length - aldus.getSize();
|
||||
header.bounds = new java.awt.Rectangle((short)aldus.left, (short)aldus.top, (short)aldus.right-(short)aldus.left, (short)aldus.bottom-(short)aldus.top);
|
||||
//coefficiaent to translate from WMF dpi to 96pdi
|
||||
//coefficient to translate from WMF dpi to 96pdi
|
||||
int coeff = 96*Shape.EMU_PER_POINT/aldus.inch;
|
||||
header.size = new java.awt.Dimension(header.bounds.width*coeff, header.bounds.height*coeff);
|
||||
header.zipsize = compressed.length;
|
||||
|
@ -119,7 +120,7 @@ public class WMF extends Metafile {
|
|||
* <li>short Checksum; Checksum value for previous 10 shorts
|
||||
* </ul>
|
||||
*/
|
||||
public static class AldusHeader{
|
||||
public class AldusHeader{
|
||||
public static final int APMHEADER_KEY = 0x9AC6CDD7;
|
||||
|
||||
public int handle;
|
||||
|
@ -143,8 +144,9 @@ public class WMF extends Metafile {
|
|||
reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;
|
||||
|
||||
checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
|
||||
if (checksum != getChecksum())
|
||||
throw new HSLFException("WMF checksum does not match the header data");
|
||||
if (checksum != getChecksum()){
|
||||
logger.log(POILogger.WARN, "WMF checksum does not match the header data");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -125,7 +125,7 @@ public class PPTXMLDump {
|
|||
dump(data, pos, size, padding);
|
||||
} else {
|
||||
//dump first 100 bytes of the atom data
|
||||
dump(out, data, pos, Math.min(size, 100), padding, true);
|
||||
dump(out, data, pos, size, padding, true);
|
||||
}
|
||||
padding--;
|
||||
write(out, "</"+recname + ">" + CR, padding);
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
package org.apache.poi.hslf.model;
|
||||
|
||||
import org.apache.poi.ddf.*;
|
||||
import org.apache.poi.hslf.record.*;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Represents an ActiveX control in a PowerPoint document.
|
||||
*
|
||||
* TODO: finish
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class ActiveXShape extends Picture {
|
||||
public static final int DEFAULT_ACTIVEX_THUMBNAIL = -1;
|
||||
|
||||
/**
|
||||
* Create a new <code>Picture</code>
|
||||
*
|
||||
* @param pictureIdx the index of the picture
|
||||
*/
|
||||
public ActiveXShape(int movieIdx, int pictureIdx){
|
||||
super(pictureIdx, null);
|
||||
setActiveXIndex(movieIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>Picture</code> object
|
||||
*
|
||||
* @param escherRecord the <code>EscherSpContainer</code> record which holds information about
|
||||
* this picture in the <code>Slide</code>
|
||||
* @param parent the parent shape of this picture
|
||||
*/
|
||||
protected ActiveXShape(EscherContainerRecord escherRecord, Shape parent){
|
||||
super(escherRecord, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Placeholder and initialize internal structures
|
||||
*
|
||||
* @return the created <code>EscherContainerRecord</code> which holds shape data
|
||||
*/
|
||||
protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {
|
||||
_escherContainer = super.createSpContainer(idx, isChild);
|
||||
|
||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
||||
spRecord.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE | EscherSpRecord.FLAG_OLESHAPE);
|
||||
|
||||
setShapeType(ShapeTypes.HostControl);
|
||||
setEscherProperty(EscherProperties.BLIP__PICTUREID, idx);
|
||||
setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001);
|
||||
setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008);
|
||||
setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);
|
||||
setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, -1);
|
||||
|
||||
EscherClientDataRecord cldata = new EscherClientDataRecord();
|
||||
cldata.setOptions((short)0xF);
|
||||
_escherContainer.getChildRecords().add(cldata);
|
||||
|
||||
OEShapeAtom oe = new OEShapeAtom();
|
||||
|
||||
//convert hslf into ddf
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
oe.writeOut(out);
|
||||
} catch(Exception e){
|
||||
throw new HSLFException(e);
|
||||
}
|
||||
cldata.setRemainingData(out.toByteArray());
|
||||
|
||||
return _escherContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a control to this shape
|
||||
*
|
||||
* @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}
|
||||
* @param idx the index of the movie
|
||||
*/
|
||||
public void setActiveXIndex(int idx){
|
||||
EscherContainerRecord spContainer = getSpContainer();
|
||||
for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {
|
||||
EscherRecord obj = (EscherRecord) it.next();
|
||||
if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
|
||||
EscherClientDataRecord clientRecord = (EscherClientDataRecord)obj;
|
||||
byte[] recdata = clientRecord.getRemainingData();
|
||||
LittleEndian.putInt(recdata, 8, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getControlIndex(){
|
||||
int idx = -1;
|
||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||
if(oe != null) idx = oe.getOptions();
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a property of this ActiveX control
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setProperty(String key, String value){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Document-level container that specifies information about an ActiveX control
|
||||
*
|
||||
* @return container that specifies information about an ActiveX control
|
||||
*/
|
||||
public ExControl getExControl(){
|
||||
int idx = getControlIndex();
|
||||
ExControl ctrl = null;
|
||||
Document doc = getSheet().getSlideShow().getDocumentRecord();
|
||||
ExObjList lst = (ExObjList)doc.findFirstOfType(RecordTypes.ExObjList.typeID);
|
||||
if(lst != null){
|
||||
Record[] ch = lst.getChildRecords();
|
||||
for (int i = 0; i < ch.length; i++) {
|
||||
if(ch[i] instanceof ExControl){
|
||||
ExControl c = (ExControl)ch[i];
|
||||
if(c.getExOleObjAtom().getObjID() == idx){
|
||||
ctrl = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
protected void afterInsert(Sheet sheet){
|
||||
ExControl ctrl = getExControl();
|
||||
ctrl.getExControlAtom().setSlideId(sheet._getSheetNumber());
|
||||
|
||||
try {
|
||||
String name = ctrl.getProgId() + "-" + getControlIndex();
|
||||
byte[] data = (name + '\u0000').getBytes("UTF-16LE");
|
||||
EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.GROUPSHAPE__SHAPENAME, false, data);
|
||||
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
|
||||
opt.addEscherProperty(prop);
|
||||
} catch (UnsupportedEncodingException e){
|
||||
throw new HSLFException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package org.apache.poi.hslf.model;
|
||||
|
||||
import org.apache.poi.ddf.*;
|
||||
import org.apache.poi.hslf.record.*;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Represents a movie in a PowerPoint document.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class MovieShape extends Picture {
|
||||
public static final int DEFAULT_MOVIE_THUMBNAIL = -1;
|
||||
|
||||
public static final int MOVIE_MPEG = 1;
|
||||
public static final int MOVIE_AVI = 2;
|
||||
|
||||
/**
|
||||
* Create a new <code>Picture</code>
|
||||
*
|
||||
* @param pictureIdx the index of the picture
|
||||
*/
|
||||
public MovieShape(int movieIdx, int pictureIdx){
|
||||
super(pictureIdx, null);
|
||||
setMovieIndex(movieIdx);
|
||||
setAutoPlay(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>Picture</code>
|
||||
*
|
||||
* @param idx the index of the picture
|
||||
* @param parent the parent shape
|
||||
*/
|
||||
public MovieShape(int movieIdx, int idx, Shape parent) {
|
||||
super(idx, parent);
|
||||
setMovieIndex(movieIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>Picture</code> object
|
||||
*
|
||||
* @param escherRecord the <code>EscherSpContainer</code> record which holds information about
|
||||
* this picture in the <code>Slide</code>
|
||||
* @param parent the parent shape of this picture
|
||||
*/
|
||||
protected MovieShape(EscherContainerRecord escherRecord, Shape parent){
|
||||
super(escherRecord, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Placeholder and initialize internal structures
|
||||
*
|
||||
* @return the created <code>EscherContainerRecord</code> which holds shape data
|
||||
*/
|
||||
protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {
|
||||
_escherContainer = super.createSpContainer(idx, isChild);
|
||||
|
||||
setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x1000100);
|
||||
setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x10001);
|
||||
|
||||
EscherClientDataRecord cldata = new EscherClientDataRecord();
|
||||
cldata.setOptions((short)0xF);
|
||||
_escherContainer.getChildRecords().add(cldata);
|
||||
|
||||
OEShapeAtom oe = new OEShapeAtom();
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_MEDIA);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NULL);
|
||||
|
||||
AnimationInfo an = new AnimationInfo();
|
||||
AnimationInfoAtom anAtom = an.getAnimationInfoAtom();
|
||||
anAtom.setFlag(AnimationInfoAtom.Automatic, true);
|
||||
|
||||
//convert hslf into ddf
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
oe.writeOut(out);
|
||||
an.writeOut(out);
|
||||
info.writeOut(out);
|
||||
} catch(Exception e){
|
||||
throw new HSLFException(e);
|
||||
}
|
||||
cldata.setRemainingData(out.toByteArray());
|
||||
|
||||
return _escherContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a movie to this shape
|
||||
*
|
||||
* @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}
|
||||
* @param idx the index of the movie
|
||||
*/
|
||||
public void setMovieIndex(int idx){
|
||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||
oe.setOptions(idx);
|
||||
|
||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||
if(an != null) {
|
||||
AnimationInfoAtom ai = an.getAnimationInfoAtom();
|
||||
ai.setDimColor(0x07000000);
|
||||
ai.setFlag(AnimationInfoAtom.Automatic, true);
|
||||
ai.setFlag(AnimationInfoAtom.Play, true);
|
||||
ai.setFlag(AnimationInfoAtom.Synchronous, true);
|
||||
ai.setOrderID(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoPlay(boolean flag){
|
||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||
if(an != null){
|
||||
an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag);
|
||||
updateClientData();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(){
|
||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||
if(an != null){
|
||||
return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns UNC or local path to a video file
|
||||
*
|
||||
* @return UNC or local path to a video file
|
||||
*/
|
||||
public String getPath(){
|
||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||
int idx = oe.getOptions();
|
||||
|
||||
SlideShow ppt = getSheet().getSlideShow();
|
||||
ExObjList lst = (ExObjList)ppt.getDocumentRecord().findFirstOfType(RecordTypes.ExObjList.typeID);
|
||||
if(lst == null) return null;
|
||||
|
||||
Record[] r = lst.getChildRecords();
|
||||
for (int i = 0; i < r.length; i++) {
|
||||
if(r[i] instanceof ExMCIMovie){
|
||||
ExMCIMovie mci = (ExMCIMovie)r[i];
|
||||
ExVideoContainer exVideo = mci.getExVideo();
|
||||
int objectId = exVideo.getExMediaAtom().getObjectId();
|
||||
if(objectId == idx){
|
||||
return exVideo.getPathAtom().getText();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -19,8 +19,10 @@ package org.apache.poi.hslf.model;
|
|||
import org.apache.poi.ddf.*;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.hslf.record.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Create a <code>Shape</code> object depending on its type
|
||||
|
@ -45,14 +47,14 @@ public class ShapeFactory {
|
|||
|
||||
public static ShapeGroup createShapeGroup(EscherContainerRecord spContainer, Shape parent){
|
||||
ShapeGroup group = null;
|
||||
UnknownEscherRecord opt = (UnknownEscherRecord)Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
|
||||
EscherRecord opt = Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
|
||||
if(opt != null){
|
||||
try {
|
||||
EscherPropertyFactory f = new EscherPropertyFactory();
|
||||
List props = f.createProperties( opt.getData(), 0, opt.getInstance() );
|
||||
List props = f.createProperties( opt.serialize(), 8, opt.getInstance() );
|
||||
EscherSimpleProperty p = (EscherSimpleProperty)props.get(0);
|
||||
if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){
|
||||
group = new ShapeGroup(spContainer, parent);
|
||||
group = new Table(spContainer, parent);
|
||||
} else {
|
||||
group = new ShapeGroup(spContainer, parent);
|
||||
}
|
||||
|
@ -68,7 +70,7 @@ public class ShapeFactory {
|
|||
}
|
||||
|
||||
public static Shape createSimpeShape(EscherContainerRecord spContainer, Shape parent){
|
||||
Shape shape;
|
||||
Shape shape = null;
|
||||
EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
|
||||
|
||||
int type = spRecord.getOptions() >> 4;
|
||||
|
@ -76,14 +78,26 @@ public class ShapeFactory {
|
|||
case ShapeTypes.TextBox:
|
||||
shape = new TextBox(spContainer, parent);
|
||||
break;
|
||||
case ShapeTypes.HostControl:
|
||||
case ShapeTypes.HostControl:
|
||||
case ShapeTypes.PictureFrame: {
|
||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
|
||||
EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID);
|
||||
if(prop != null)
|
||||
shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object
|
||||
else
|
||||
shape = new Picture(spContainer, parent);
|
||||
InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID);
|
||||
OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID);
|
||||
if(info != null && info.getInteractiveInfoAtom() != null){
|
||||
switch(info.getInteractiveInfoAtom().getAction()){
|
||||
case InteractiveInfoAtom.ACTION_OLE:
|
||||
shape = new OLEShape(spContainer, parent);
|
||||
break;
|
||||
case InteractiveInfoAtom.ACTION_MEDIA:
|
||||
shape = new MovieShape(spContainer, parent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (oes != null){
|
||||
shape = new OLEShape(spContainer, parent);
|
||||
}
|
||||
|
||||
if(shape == null) shape = new Picture(spContainer, parent);
|
||||
break;
|
||||
}
|
||||
case ShapeTypes.Line:
|
||||
|
@ -108,4 +122,22 @@ public class ShapeFactory {
|
|||
return shape;
|
||||
|
||||
}
|
||||
|
||||
protected static Record getClientDataRecord(EscherContainerRecord spContainer, int recordType) {
|
||||
Record oep = null;
|
||||
for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {
|
||||
EscherRecord obj = (EscherRecord) it.next();
|
||||
if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
|
||||
byte[] data = obj.serialize();
|
||||
Record[] records = Record.findChildRecords(data, 8, data.length - 8);
|
||||
for (int j = 0; j < records.length; j++) {
|
||||
if (records[j].getRecordType() == recordType) {
|
||||
return records[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return oep;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,13 @@ import org.apache.poi.ddf.*;
|
|||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.hslf.record.ColorSchemeAtom;
|
||||
import org.apache.poi.hslf.record.Record;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Iterator;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* An abstract simple (non-group) shape.
|
||||
|
@ -35,6 +37,12 @@ import java.util.Iterator;
|
|||
*/
|
||||
public class SimpleShape extends Shape {
|
||||
|
||||
/**
|
||||
* Records stored in EscherClientDataRecord
|
||||
*/
|
||||
protected Record[] _clientRecords;
|
||||
protected EscherClientDataRecord _clientData;
|
||||
|
||||
/**
|
||||
* Create a SimpleShape object and initialize it from the supplied Record container.
|
||||
*
|
||||
|
@ -293,21 +301,46 @@ public class SimpleShape extends Shape {
|
|||
* @param recordType type of the record to search
|
||||
*/
|
||||
protected Record getClientDataRecord(int recordType) {
|
||||
Record oep = null;
|
||||
EscherContainerRecord spContainer = getSpContainer();
|
||||
for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {
|
||||
EscherRecord obj = (EscherRecord) it.next();
|
||||
if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
|
||||
byte[] data = obj.serialize();
|
||||
Record[] records = Record.findChildRecords(data, 8, data.length - 8);
|
||||
for (int j = 0; j < records.length; j++) {
|
||||
if (records[j].getRecordType() == recordType) {
|
||||
return records[j];
|
||||
}
|
||||
}
|
||||
|
||||
Record[] records = getClientRecords();
|
||||
if(records != null) for (int i = 0; i < records.length; i++) {
|
||||
if(records[i].getRecordType() == recordType){
|
||||
return records[i];
|
||||
}
|
||||
}
|
||||
return oep;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Record[] getClientRecords() {
|
||||
if(_clientData == null){
|
||||
EscherRecord r = Shape.getEscherChild(getSpContainer(), EscherClientDataRecord.RECORD_ID);
|
||||
//ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID
|
||||
//convert in to EscherClientDataRecord on the fly
|
||||
if(!(r instanceof EscherClientDataRecord)){
|
||||
byte[] data = r.serialize();
|
||||
r = new EscherClientDataRecord();
|
||||
r.fillFields(data, 0, new DefaultEscherRecordFactory());
|
||||
}
|
||||
_clientData = (EscherClientDataRecord)r;
|
||||
}
|
||||
if(_clientData != null && _clientRecords == null){
|
||||
byte[] data = _clientData.getRemainingData();
|
||||
_clientRecords = Record.findChildRecords(data, 0, data.length);
|
||||
}
|
||||
return _clientRecords;
|
||||
}
|
||||
|
||||
protected void updateClientData() {
|
||||
if(_clientData != null && _clientRecords != null){
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
for (int i = 0; i < _clientRecords.length; i++) {
|
||||
_clientRecords[i].writeOut(out);
|
||||
}
|
||||
} catch(Exception e){
|
||||
throw new HSLFException(e);
|
||||
}
|
||||
_clientData.setRemainingData(out.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ public class Table extends ShapeGroup {
|
|||
* @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape
|
||||
* @param parent the parent of the shape
|
||||
*/
|
||||
protected Table(EscherContainerRecord escherRecord, Shape parent) {
|
||||
public Table(EscherContainerRecord escherRecord, Shape parent) {
|
||||
super(escherRecord, parent);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ import org.apache.poi.util.POILogFactory;
|
|||
|
||||
import java.text.AttributedString;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.BreakIterator;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.LineBreakMeasurer;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.Point2D;
|
||||
|
@ -89,11 +91,69 @@ public class TextPainter {
|
|||
}
|
||||
|
||||
public void paint(Graphics2D graphics){
|
||||
Rectangle2D anchor = _shape.getLogicalAnchor2D();
|
||||
TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext());
|
||||
if(elem == null) return;
|
||||
|
||||
float textHeight = 0;
|
||||
for (int i = 0; i < elem.length; i++) {
|
||||
textHeight += elem[i].ascent + elem[i].descent;
|
||||
}
|
||||
|
||||
int valign = _shape.getVerticalAlignment();
|
||||
double y0 = anchor.getY();
|
||||
switch (valign){
|
||||
case TextShape.AnchorTopBaseline:
|
||||
case TextShape.AnchorTop:
|
||||
y0 += _shape.getMarginTop();
|
||||
break;
|
||||
case TextShape.AnchorBottom:
|
||||
y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();
|
||||
break;
|
||||
default:
|
||||
case TextShape.AnchorMiddle:
|
||||
float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();
|
||||
y0 += _shape.getMarginTop() + delta/2;
|
||||
break;
|
||||
}
|
||||
|
||||
//finally draw the text fragments
|
||||
for (int i = 0; i < elem.length; i++) {
|
||||
y0 += elem[i].ascent;
|
||||
|
||||
Point2D.Double pen = new Point2D.Double();
|
||||
pen.y = y0;
|
||||
switch (elem[i]._align) {
|
||||
default:
|
||||
case TextShape.AlignLeft:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft();
|
||||
break;
|
||||
case TextShape.AlignCenter:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft() +
|
||||
(anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;
|
||||
break;
|
||||
case TextShape.AlignRight:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft() +
|
||||
(anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight());
|
||||
break;
|
||||
}
|
||||
if(elem[i]._bullet != null){
|
||||
graphics.drawString(elem[i]._bullet.getIterator(), (float)(pen.x + elem[i]._bulletOffset), (float)pen.y);
|
||||
}
|
||||
AttributedCharacterIterator chIt = elem[i]._text.getIterator();
|
||||
if(chIt.getEndIndex() > chIt.getBeginIndex()) {
|
||||
graphics.drawString(chIt, (float)(pen.x + elem[i]._textOffset), (float)pen.y);
|
||||
}
|
||||
y0 += elem[i].descent;
|
||||
}
|
||||
}
|
||||
|
||||
public TextElement[] getTextElements(float textWidth, FontRenderContext frc){
|
||||
TextRun run = _shape.getTextRun();
|
||||
if (run == null) return;
|
||||
if (run == null) return null;
|
||||
|
||||
String text = run.getText();
|
||||
if (text == null || text.equals("")) return;
|
||||
if (text == null || text.equals("")) return null;
|
||||
|
||||
AttributedString at = getAttributedString(run);
|
||||
|
||||
|
@ -101,11 +161,8 @@ public class TextPainter {
|
|||
int paragraphStart = it.getBeginIndex();
|
||||
int paragraphEnd = it.getEndIndex();
|
||||
|
||||
Rectangle2D anchor = _shape.getLogicalAnchor2D();
|
||||
|
||||
float textHeight = 0;
|
||||
ArrayList lines = new ArrayList();
|
||||
LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
|
||||
LineBreakMeasurer measurer = new LineBreakMeasurer(it, frc);
|
||||
measurer.setPosition(paragraphStart);
|
||||
while (measurer.getPosition() < paragraphEnd) {
|
||||
int startIndex = measurer.getPosition();
|
||||
|
@ -120,7 +177,7 @@ public class TextPainter {
|
|||
break;
|
||||
}
|
||||
|
||||
float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();
|
||||
float wrappingWidth = textWidth - _shape.getMarginLeft() - _shape.getMarginRight();
|
||||
int bulletOffset = rt.getBulletOffset();
|
||||
int textOffset = rt.getTextOffset();
|
||||
int indent = rt.getIndentLevel();
|
||||
|
@ -138,7 +195,7 @@ public class TextPainter {
|
|||
if(text_val != 0) textOffset = text_val;
|
||||
}
|
||||
|
||||
wrappingWidth -= textOffset;
|
||||
if(bulletOffset > 0 || prStart || startIndex == 0) wrappingWidth -= textOffset;
|
||||
|
||||
if (_shape.getWordWrap() == TextShape.WrapNone) {
|
||||
wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;
|
||||
|
@ -147,7 +204,7 @@ public class TextPainter {
|
|||
TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1,
|
||||
nextBreak == -1 ? paragraphEnd : nextBreak, true);
|
||||
if (textLayout == null) {
|
||||
textLayout = measurer.nextLayout((float)anchor.getWidth(),
|
||||
textLayout = measurer.nextLayout(textWidth,
|
||||
nextBreak == -1 ? paragraphEnd : nextBreak, false);
|
||||
}
|
||||
if(textLayout == null){
|
||||
|
@ -173,6 +230,8 @@ public class TextPainter {
|
|||
el.advance = textLayout.getAdvance();
|
||||
el._textOffset = textOffset;
|
||||
el._text = new AttributedString(it, startIndex, endIndex);
|
||||
el.textStartIndex = startIndex;
|
||||
el.textEndIndex = endIndex;
|
||||
|
||||
if (prStart){
|
||||
int sp = rt.getSpaceBefore();
|
||||
|
@ -203,8 +262,6 @@ public class TextPainter {
|
|||
}
|
||||
el.descent = descent;
|
||||
|
||||
textHeight += el.ascent + el.descent;
|
||||
|
||||
if(rt.isBullet() && (prStart || startIndex == 0)){
|
||||
it.setIndex(startIndex);
|
||||
|
||||
|
@ -236,56 +293,11 @@ public class TextPainter {
|
|||
lines.add(el);
|
||||
}
|
||||
|
||||
int valign = _shape.getVerticalAlignment();
|
||||
double y0 = anchor.getY();
|
||||
switch (valign){
|
||||
case TextShape.AnchorTopBaseline:
|
||||
case TextShape.AnchorTop:
|
||||
y0 += _shape.getMarginTop();
|
||||
break;
|
||||
case TextShape.AnchorBottom:
|
||||
y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();
|
||||
break;
|
||||
default:
|
||||
case TextShape.AnchorMiddle:
|
||||
float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();
|
||||
y0 += _shape.getMarginTop() + delta/2;
|
||||
break;
|
||||
}
|
||||
|
||||
//finally draw the text fragments
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
TextElement elem = (TextElement)lines.get(i);
|
||||
y0 += elem.ascent;
|
||||
|
||||
Point2D.Double pen = new Point2D.Double();
|
||||
pen.y = y0;
|
||||
switch (elem._align) {
|
||||
default:
|
||||
case TextShape.AlignLeft:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft();
|
||||
break;
|
||||
case TextShape.AlignCenter:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft() +
|
||||
(anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;
|
||||
break;
|
||||
case TextShape.AlignRight:
|
||||
pen.x = anchor.getX() + _shape.getMarginLeft() +
|
||||
(anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight());
|
||||
break;
|
||||
}
|
||||
if(elem._bullet != null){
|
||||
graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y);
|
||||
}
|
||||
AttributedCharacterIterator chIt = elem._text.getIterator();
|
||||
if(chIt.getEndIndex() > chIt.getBeginIndex()) {
|
||||
graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y);
|
||||
}
|
||||
y0 += elem.descent;
|
||||
}
|
||||
TextElement[] elems = new TextElement[lines.size()];
|
||||
return (TextElement[])lines.toArray(elems);
|
||||
}
|
||||
|
||||
|
||||
public static class TextElement {
|
||||
public AttributedString _text;
|
||||
public int _textOffset;
|
||||
|
@ -294,5 +306,6 @@ public class TextPainter {
|
|||
public int _align;
|
||||
public float ascent, descent;
|
||||
public float advance;
|
||||
public int textStartIndex, textEndIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.apache.poi.hslf.model;
|
|||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Vector;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
|
||||
import org.apache.poi.hslf.record.*;
|
||||
|
@ -45,7 +46,8 @@ public class TextRun
|
|||
protected TextBytesAtom _byteAtom;
|
||||
protected TextCharsAtom _charAtom;
|
||||
protected StyleTextPropAtom _styleAtom;
|
||||
protected boolean _isUnicode;
|
||||
protected TextRulerAtom _ruler;
|
||||
protected boolean _isUnicode;
|
||||
protected RichTextRun[] _rtRuns;
|
||||
private SlideShow slideShow;
|
||||
private Sheet sheet;
|
||||
|
@ -103,155 +105,159 @@ public class TextRun
|
|||
pStyles = _styleAtom.getParagraphStyles();
|
||||
cStyles = _styleAtom.getCharacterStyles();
|
||||
}
|
||||
|
||||
// Handle case of no current style, with a default
|
||||
if(pStyles.size() == 0 || cStyles.size() == 0) {
|
||||
_rtRuns = new RichTextRun[1];
|
||||
_rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
|
||||
} else {
|
||||
// Build up Rich Text Runs, one for each
|
||||
// character/paragraph style pair
|
||||
Vector rtrs = new Vector();
|
||||
|
||||
int pos = 0;
|
||||
|
||||
int curP = 0;
|
||||
int curC = 0;
|
||||
int pLenRemain = -1;
|
||||
int cLenRemain = -1;
|
||||
|
||||
// Build one for each run with the same style
|
||||
while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
|
||||
// Get the Props to use
|
||||
TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
|
||||
TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
|
||||
|
||||
int pLen = pProps.getCharactersCovered();
|
||||
int cLen = cProps.getCharactersCovered();
|
||||
|
||||
// Handle new pass
|
||||
boolean freshSet = false;
|
||||
if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
|
||||
if(pLenRemain == -1) { pLenRemain = pLen; }
|
||||
if(cLenRemain == -1) { cLenRemain = cLen; }
|
||||
|
||||
// So we know how to build the eventual run
|
||||
int runLen = -1;
|
||||
boolean pShared = false;
|
||||
boolean cShared = false;
|
||||
|
||||
// Same size, new styles - neither shared
|
||||
if(pLen == cLen && freshSet) {
|
||||
runLen = cLen;
|
||||
pShared = false;
|
||||
cShared = false;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else {
|
||||
// Some sharing
|
||||
|
||||
// See if we are already in a shared block
|
||||
if(pLenRemain < pLen) {
|
||||
// Existing shared p block
|
||||
pShared = true;
|
||||
|
||||
// Do we end with the c block, or either side of it?
|
||||
if(pLenRemain == cLenRemain) {
|
||||
// We end at the same time
|
||||
cShared = false;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else if(pLenRemain < cLenRemain) {
|
||||
// We end before the c block
|
||||
cShared = true;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
} else {
|
||||
// We end after the c block
|
||||
cShared = false;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
}
|
||||
} else if(cLenRemain < cLen) {
|
||||
// Existing shared c block
|
||||
cShared = true;
|
||||
|
||||
// Do we end with the p block, or either side of it?
|
||||
if(pLenRemain == cLenRemain) {
|
||||
// We end at the same time
|
||||
pShared = false;
|
||||
runLen = cLenRemain;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else if(cLenRemain < pLenRemain) {
|
||||
// We end before the p block
|
||||
pShared = true;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
} else {
|
||||
// We end after the p block
|
||||
pShared = false;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
}
|
||||
} else {
|
||||
// Start of a shared block
|
||||
if(pLenRemain < cLenRemain) {
|
||||
// Shared c block
|
||||
pShared = false;
|
||||
cShared = true;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
} else {
|
||||
// Shared p block
|
||||
pShared = true;
|
||||
cShared = false;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wind on
|
||||
int prevPos = pos;
|
||||
pos += runLen;
|
||||
// Adjust for end-of-run extra 1 length
|
||||
if(pos > runRawText.length()) {
|
||||
runLen--;
|
||||
}
|
||||
|
||||
// Save
|
||||
RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
|
||||
rtrs.add(rtr);
|
||||
}
|
||||
|
||||
// Build the array
|
||||
_rtRuns = new RichTextRun[rtrs.size()];
|
||||
rtrs.copyInto(_rtRuns);
|
||||
}
|
||||
buildRichTextRuns(pStyles, cStyles, runRawText);
|
||||
}
|
||||
|
||||
|
||||
// Update methods follow
|
||||
public void buildRichTextRuns(LinkedList pStyles, LinkedList cStyles, String runRawText){
|
||||
|
||||
// Handle case of no current style, with a default
|
||||
if(pStyles.size() == 0 || cStyles.size() == 0) {
|
||||
_rtRuns = new RichTextRun[1];
|
||||
_rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
|
||||
} else {
|
||||
// Build up Rich Text Runs, one for each
|
||||
// character/paragraph style pair
|
||||
Vector rtrs = new Vector();
|
||||
|
||||
int pos = 0;
|
||||
|
||||
int curP = 0;
|
||||
int curC = 0;
|
||||
int pLenRemain = -1;
|
||||
int cLenRemain = -1;
|
||||
|
||||
// Build one for each run with the same style
|
||||
while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
|
||||
// Get the Props to use
|
||||
TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
|
||||
TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
|
||||
|
||||
int pLen = pProps.getCharactersCovered();
|
||||
int cLen = cProps.getCharactersCovered();
|
||||
|
||||
// Handle new pass
|
||||
boolean freshSet = false;
|
||||
if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
|
||||
if(pLenRemain == -1) { pLenRemain = pLen; }
|
||||
if(cLenRemain == -1) { cLenRemain = cLen; }
|
||||
|
||||
// So we know how to build the eventual run
|
||||
int runLen = -1;
|
||||
boolean pShared = false;
|
||||
boolean cShared = false;
|
||||
|
||||
// Same size, new styles - neither shared
|
||||
if(pLen == cLen && freshSet) {
|
||||
runLen = cLen;
|
||||
pShared = false;
|
||||
cShared = false;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else {
|
||||
// Some sharing
|
||||
|
||||
// See if we are already in a shared block
|
||||
if(pLenRemain < pLen) {
|
||||
// Existing shared p block
|
||||
pShared = true;
|
||||
|
||||
// Do we end with the c block, or either side of it?
|
||||
if(pLenRemain == cLenRemain) {
|
||||
// We end at the same time
|
||||
cShared = false;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else if(pLenRemain < cLenRemain) {
|
||||
// We end before the c block
|
||||
cShared = true;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
} else {
|
||||
// We end after the c block
|
||||
cShared = false;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
}
|
||||
} else if(cLenRemain < cLen) {
|
||||
// Existing shared c block
|
||||
cShared = true;
|
||||
|
||||
// Do we end with the p block, or either side of it?
|
||||
if(pLenRemain == cLenRemain) {
|
||||
// We end at the same time
|
||||
pShared = false;
|
||||
runLen = cLenRemain;
|
||||
curP++;
|
||||
curC++;
|
||||
pLenRemain = -1;
|
||||
cLenRemain = -1;
|
||||
} else if(cLenRemain < pLenRemain) {
|
||||
// We end before the p block
|
||||
pShared = true;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
} else {
|
||||
// We end after the p block
|
||||
pShared = false;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
}
|
||||
} else {
|
||||
// Start of a shared block
|
||||
if(pLenRemain < cLenRemain) {
|
||||
// Shared c block
|
||||
pShared = false;
|
||||
cShared = true;
|
||||
runLen = pLenRemain;
|
||||
curP++;
|
||||
cLenRemain -= pLenRemain;
|
||||
pLenRemain = -1;
|
||||
} else {
|
||||
// Shared p block
|
||||
pShared = true;
|
||||
cShared = false;
|
||||
runLen = cLenRemain;
|
||||
curC++;
|
||||
pLenRemain -= cLenRemain;
|
||||
cLenRemain = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wind on
|
||||
int prevPos = pos;
|
||||
pos += runLen;
|
||||
// Adjust for end-of-run extra 1 length
|
||||
if(pos > runRawText.length()) {
|
||||
runLen--;
|
||||
}
|
||||
|
||||
// Save
|
||||
RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
|
||||
rtrs.add(rtr);
|
||||
}
|
||||
|
||||
// Build the array
|
||||
_rtRuns = new RichTextRun[rtrs.size()];
|
||||
rtrs.copyInto(_rtRuns);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update methods follow
|
||||
|
||||
/**
|
||||
* Adds the supplied text onto the end of the TextRun,
|
||||
|
@ -379,7 +385,7 @@ public class TextRun
|
|||
* @param run
|
||||
* @param s
|
||||
*/
|
||||
public synchronized void changeTextInRichTextRun(RichTextRun run, String s) {
|
||||
public void changeTextInRichTextRun(RichTextRun run, String s) {
|
||||
// Figure out which run it is
|
||||
int runID = -1;
|
||||
for(int i=0; i<_rtRuns.length; i++) {
|
||||
|
@ -457,7 +463,7 @@ public class TextRun
|
|||
* as the the first character has.
|
||||
* If you care about styling, do setText on a RichTextRun instead
|
||||
*/
|
||||
public synchronized void setRawText(String s) {
|
||||
public void setRawText(String s) {
|
||||
// Save the new text to the atoms
|
||||
storeText(s);
|
||||
RichTextRun fst = _rtRuns[0];
|
||||
|
@ -491,7 +497,7 @@ public class TextRun
|
|||
* Changes the text.
|
||||
* Converts '\r' into '\n'
|
||||
*/
|
||||
public synchronized void setText(String s) {
|
||||
public void setText(String s) {
|
||||
String text = normalize(s);
|
||||
setRawText(text);
|
||||
}
|
||||
|
@ -500,7 +506,7 @@ public class TextRun
|
|||
* Ensure a StyleTextPropAtom is present for this run,
|
||||
* by adding if required. Normally for internal TextRun use.
|
||||
*/
|
||||
public synchronized void ensureStyleAtomPresent() {
|
||||
public void ensureStyleAtomPresent() {
|
||||
if(_styleAtom != null) {
|
||||
// All there
|
||||
return;
|
||||
|
@ -669,11 +675,26 @@ public class TextRun
|
|||
}
|
||||
|
||||
public TextRulerAtom getTextRuler(){
|
||||
for (int i = 0; i < _records.length; i++) {
|
||||
if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i];
|
||||
}
|
||||
return null;
|
||||
if(_ruler == null){
|
||||
if(_records != null) for (int i = 0; i < _records.length; i++) {
|
||||
if(_records[i] instanceof TextRulerAtom) {
|
||||
_ruler = (TextRulerAtom)_records[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return _ruler;
|
||||
|
||||
}
|
||||
|
||||
public TextRulerAtom createTextRuler(){
|
||||
_ruler = getTextRuler();
|
||||
if(_ruler == null){
|
||||
_ruler = TextRulerAtom.getParagraphInstance();
|
||||
_headerAtom.getParentRecord().appendChildRecord(_ruler);
|
||||
}
|
||||
return _ruler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -692,4 +713,5 @@ public class TextRun
|
|||
public Record[] getRecords(){
|
||||
return _records;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ public abstract class TextShape extends SimpleShape {
|
|||
_txtbox.appendChildRecord(sta);
|
||||
|
||||
_txtrun = new TextRun(tha,tca,sta);
|
||||
_txtrun._records = new Record[]{tha, tca, sta};
|
||||
_txtrun.setText("");
|
||||
|
||||
_escherContainer.addChildRecord(_txtbox.getEscherRecord());
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* A container record that specifies information about animation information for a shape.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class AnimationInfo extends RecordContainer {
|
||||
private byte[] _header;
|
||||
|
||||
// Links to our more interesting children
|
||||
private AnimationInfoAtom animationAtom;
|
||||
|
||||
/**
|
||||
* Set things up, and find our more interesting children
|
||||
*/
|
||||
protected AnimationInfo(byte[] source, int start, int len) {
|
||||
// Grab the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source,start,_header,0,8);
|
||||
|
||||
// Find our children
|
||||
_children = Record.findChildRecords(source,start+8,len-8);
|
||||
findInterestingChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through our child records, picking out the ones that are
|
||||
* interesting, and saving those for use by the easy helper
|
||||
* methods.
|
||||
*/
|
||||
private void findInterestingChildren() {
|
||||
|
||||
// First child should be the ExMediaAtom
|
||||
if(_children[0] instanceof AnimationInfoAtom) {
|
||||
animationAtom = (AnimationInfoAtom)_children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a AnimationInfoAtom, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AnimationInfo, with blank fields
|
||||
*/
|
||||
public AnimationInfo() {
|
||||
// Setup our header block
|
||||
_header = new byte[8];
|
||||
_header[0] = 0x0f; // We are a container record
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
|
||||
_children = new Record[1];
|
||||
_children[0] = animationAtom = new AnimationInfoAtom();
|
||||
}
|
||||
|
||||
/**
|
||||
* We are of type 4103
|
||||
*/
|
||||
public long getRecordType() { return RecordTypes.AnimationInfo.typeID; }
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
writeOut(_header[0],_header[1],getRecordType(),_children,out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AnimationInfo
|
||||
*/
|
||||
public AnimationInfoAtom getAnimationInfoAtom() {
|
||||
return animationAtom;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hslf.util.SystemTimeUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* An atom record that specifies the animation information for a shape.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class AnimationInfoAtom extends RecordAtom
|
||||
{
|
||||
|
||||
/**
|
||||
* whether the animation plays in the reverse direction
|
||||
*/
|
||||
public static final int Reverse = 1;
|
||||
/**
|
||||
* whether the animation starts automatically
|
||||
*/
|
||||
public static final int Automatic = 4;
|
||||
/**
|
||||
* whether the animation has an associated sound
|
||||
*/
|
||||
public static final int Sound = 16;
|
||||
/**
|
||||
* whether all playing sounds are stopped when this animation begins
|
||||
*/
|
||||
public static final int StopSound = 64;
|
||||
/**
|
||||
* whether an associated sound, media or action verb is activated when the shape is clicked.
|
||||
*/
|
||||
public static final int Play = 256;
|
||||
/**
|
||||
* specifies that the animation, while playing, stops other slide show actions.
|
||||
*/
|
||||
public static final int Synchronous = 1024;
|
||||
/**
|
||||
* whether the shape is hidden while the animation is not playing
|
||||
*/
|
||||
public static final int Hide = 4096;
|
||||
/**
|
||||
* whether the background of the shape is animated
|
||||
*/
|
||||
public static final int AnimateBg = 16384;
|
||||
|
||||
/**
|
||||
* Record header.
|
||||
*/
|
||||
private byte[] _header;
|
||||
|
||||
/**
|
||||
* record data
|
||||
*/
|
||||
private byte[] _recdata;
|
||||
|
||||
/**
|
||||
* Constructs a brand new link related atom record.
|
||||
*/
|
||||
protected AnimationInfoAtom() {
|
||||
_recdata = new byte[28];
|
||||
|
||||
_header = new byte[8];
|
||||
LittleEndian.putShort(_header, 0, (short)0x01);
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
LittleEndian.putInt(_header, 4, _recdata.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the link related atom record from its
|
||||
* source data.
|
||||
*
|
||||
* @param source the source data as a byte array.
|
||||
* @param start the start offset into the byte array.
|
||||
* @param len the length of the slice in the byte array.
|
||||
*/
|
||||
protected AnimationInfoAtom(byte[] source, int start, int len) {
|
||||
// Get the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source,start,_header,0,8);
|
||||
|
||||
// Grab the record data
|
||||
_recdata = new byte[len-8];
|
||||
System.arraycopy(source,start+8,_recdata,0,len-8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the record type.
|
||||
* @return the record type.
|
||||
*/
|
||||
public long getRecordType() {
|
||||
return RecordTypes.AnimationInfoAtom.typeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*
|
||||
* @param out the output stream to write to.
|
||||
* @throws java.io.IOException if an error occurs.
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
out.write(_header);
|
||||
out.write(_recdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* A rgb structure that specifies a color for the dim effect after the animation is complete.
|
||||
*
|
||||
* @return color for the dim effect after the animation is complete
|
||||
*/
|
||||
public int getDimColor(){
|
||||
return LittleEndian.getInt(_recdata, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A rgb structure that specifies a color for the dim effect after the animation is complete.
|
||||
*
|
||||
* @param rgb color for the dim effect after the animation is complete
|
||||
*/
|
||||
public void setDimColor(int rgb){
|
||||
LittleEndian.putInt(_recdata, 0, rgb);
|
||||
}
|
||||
|
||||
/**
|
||||
* A bit mask specifying options for displaying headers and footers
|
||||
*
|
||||
* @return A bit mask specifying options for displaying headers and footers
|
||||
*/
|
||||
public int getMask(){
|
||||
return LittleEndian.getInt(_recdata, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* A bit mask specifying options for displaying video
|
||||
*
|
||||
* @param mask A bit mask specifying options for displaying video
|
||||
*/
|
||||
public void setMask(int mask){
|
||||
LittleEndian.putInt(_recdata, 4, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bit the bit to check
|
||||
* @return whether the specified flag is set
|
||||
*/
|
||||
public boolean getFlag(int bit){
|
||||
return (getMask() & bit) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bit the bit to set
|
||||
* @param value whether the specified bit is set
|
||||
*/
|
||||
public void setFlag(int bit, boolean value){
|
||||
int mask = getMask();
|
||||
if(value) mask |= bit;
|
||||
else mask &= ~bit;
|
||||
setMask(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* A 4-byte unsigned integer that specifies a reference to a sound
|
||||
* in the SoundCollectionContainer record to locate the embedded audio
|
||||
*
|
||||
* @return reference to a sound
|
||||
*/
|
||||
public int getSoundIdRef(){
|
||||
return LittleEndian.getInt(_recdata, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* A 4-byte unsigned integer that specifies a reference to a sound
|
||||
* in the SoundCollectionContainer record to locate the embedded audio
|
||||
*
|
||||
* @param id reference to a sound
|
||||
*/
|
||||
public void setSoundIdRef(int id){
|
||||
LittleEndian.putInt(_recdata, 8, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.
|
||||
* If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.
|
||||
*/
|
||||
public int getDelayTime(){
|
||||
return LittleEndian.getInt(_recdata, 12);
|
||||
}
|
||||
/**
|
||||
* A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.
|
||||
* If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.
|
||||
*/
|
||||
public void setDelayTime(int id){
|
||||
LittleEndian.putInt(_recdata, 12, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed integer that specifies the order of the animation in the slide.
|
||||
* It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of
|
||||
* the corresponding placeholder shape on the main master slide or title master slide.
|
||||
* The value -1 SHOULD NOT <105> be used.
|
||||
*/
|
||||
public int getOrderID(){
|
||||
return LittleEndian.getInt(_recdata, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed integer that specifies the order of the animation in the slide.
|
||||
* It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of
|
||||
* the corresponding placeholder shape on the main master slide or title master slide.
|
||||
* The value -1 SHOULD NOT <105> be used.
|
||||
*/
|
||||
public void setOrderID(int id){
|
||||
LittleEndian.putInt(_recdata, 16, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned integer that specifies the number of slides that this animation continues playing.
|
||||
* This field is utilized only in conjunction with media.
|
||||
* The value 0xFFFFFFFF specifies that the animation plays for one slide.
|
||||
*/
|
||||
public int getSlideCount(){
|
||||
return LittleEndian.getInt(_recdata, 18);
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned integer that specifies the number of slides that this animation continues playing.
|
||||
* This field is utilized only in conjunction with media.
|
||||
* The value 0xFFFFFFFF specifies that the animation plays for one slide.
|
||||
*/
|
||||
public void setSlideCount(int id){
|
||||
LittleEndian.putInt(_recdata, 18, id);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("AnimationInfoAtom\n");
|
||||
buf.append("\tDimColor: " + getDimColor() + "\n");
|
||||
int mask = getMask();
|
||||
buf.append("\tMask: " + mask + ", 0x"+Integer.toHexString(mask)+"\n");
|
||||
buf.append("\t Reverse: " + getFlag(Reverse)+"\n");
|
||||
buf.append("\t Automatic: " + getFlag(Automatic)+"\n");
|
||||
buf.append("\t Sound: " + getFlag(Sound)+"\n");
|
||||
buf.append("\t StopSound: " + getFlag(StopSound)+"\n");
|
||||
buf.append("\t Play: " + getFlag(Play)+"\n");
|
||||
buf.append("\t Synchronous: " + getFlag(Synchronous)+"\n");
|
||||
buf.append("\t Hide: " + getFlag(Hide)+"\n");
|
||||
buf.append("\t AnimateBg: " + getFlag(AnimateBg)+"\n");
|
||||
buf.append("\tSoundIdRef: " + getSoundIdRef() + "\n");
|
||||
buf.append("\tDelayTime: " + getDelayTime() + "\n");
|
||||
buf.append("\tOrderID: " + getOrderID() + "\n");
|
||||
buf.append("\tSlideCount: " + getSlideCount() + "\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* A container record that specifies information about a movie stored externally.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class ExAviMovie extends ExMCIMovie {
|
||||
|
||||
/**
|
||||
* Set things up, and find our more interesting children
|
||||
*/
|
||||
protected ExAviMovie(byte[] source, int start, int len) {
|
||||
super(source, start, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ExAviMovie, with blank fields
|
||||
*/
|
||||
public ExAviMovie() {
|
||||
super();
|
||||
|
||||
}
|
||||
/**
|
||||
* We are of type 4102
|
||||
*/
|
||||
public long getRecordType() {
|
||||
return RecordTypes.ExAviMovie.typeID;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ import org.apache.poi.util.LittleEndian;
|
|||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* Container for OLE Control object. It contains:
|
||||
* A container record that specifies information about an ActiveX control. It contains:
|
||||
* <p>
|
||||
* 1. ExControlAtom (4091)
|
||||
* 2. ExOleObjAtom (4035)
|
||||
|
@ -40,9 +40,6 @@ import org.apache.poi.util.POILogger;
|
|||
*/
|
||||
public class ExControl extends ExEmbed {
|
||||
|
||||
// Links to our more interesting children
|
||||
private ExControlAtom ctrlAtom;
|
||||
|
||||
/**
|
||||
* Set things up, and find our more interesting children
|
||||
*
|
||||
|
@ -60,7 +57,7 @@ public class ExControl extends ExEmbed {
|
|||
public ExControl() {
|
||||
super();
|
||||
|
||||
_children[0] = ctrlAtom = new ExControlAtom();
|
||||
_children[0] = embedAtom = new ExControlAtom();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,7 +67,7 @@ public class ExControl extends ExEmbed {
|
|||
*/
|
||||
public ExControlAtom getExControlAtom()
|
||||
{
|
||||
return ctrlAtom;
|
||||
return (ExControlAtom)_children[0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,14 +79,4 @@ public class ExControl extends ExEmbed {
|
|||
public long getRecordType() {
|
||||
return RecordTypes.ExControl.typeID;
|
||||
}
|
||||
|
||||
protected RecordAtom getEmbedAtom(Record[] children){
|
||||
RecordAtom atom = null;
|
||||
if(_children[0] instanceof ExControlAtom) {
|
||||
atom = (ExControlAtom)_children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a ExControlAtom, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ import java.io.OutputStream;
|
|||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* Contains a long integer, slideID, which stores the unique slide identifier of the slide
|
||||
* where this control resides.
|
||||
* An atom record that specifies an ActiveX control.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
|
@ -67,10 +66,32 @@ public class ExControlAtom extends RecordAtom {
|
|||
_id = LittleEndian.getInt(source, start + 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer that specifies which presentation slide is associated with the ActiveX control.
|
||||
* <p>
|
||||
* It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.
|
||||
* The value 0x00000000 specifies a null reference.
|
||||
* </p>
|
||||
*
|
||||
* @return an integer that specifies which presentation slide is associated with the ActiveX control
|
||||
*/
|
||||
public int getSlideId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets which presentation slide is associated with the ActiveX control.
|
||||
*
|
||||
* @param id an integer that specifies which presentation slide is associated with the ActiveX control
|
||||
* <p>
|
||||
* It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.
|
||||
* The value 0x00000000 specifies a null reference.
|
||||
* </p>
|
||||
*/
|
||||
public void setSlideId(int id) {
|
||||
_id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the record type.
|
||||
* @return the record type.
|
||||
|
|
|
@ -36,7 +36,7 @@ public class ExEmbed extends RecordContainer {
|
|||
private byte[] _header;
|
||||
|
||||
// Links to our more interesting children
|
||||
private RecordAtom embedAtom;
|
||||
protected RecordAtom embedAtom;
|
||||
private ExOleObjAtom oleObjAtom;
|
||||
private CString menuName;
|
||||
private CString progId;
|
||||
|
@ -72,10 +72,11 @@ public class ExEmbed extends RecordContainer {
|
|||
|
||||
// Setup our child records
|
||||
CString cs1 = new CString();
|
||||
cs1.setOptions(0x1 << 4);
|
||||
CString cs2 = new CString();
|
||||
cs2.setOptions(0x2 << 4);
|
||||
CString cs3 = new CString();
|
||||
// cs1.setOptions(0x00);
|
||||
// cs2.setOptions(0x10);
|
||||
cs3.setOptions(0x3 << 4);
|
||||
_children[0] = new ExEmbedAtom();
|
||||
_children[1] = new ExOleObjAtom();
|
||||
_children[2] = cs1;
|
||||
|
@ -91,7 +92,11 @@ public class ExEmbed extends RecordContainer {
|
|||
private void findInterestingChildren() {
|
||||
|
||||
// First child should be the ExHyperlinkAtom
|
||||
embedAtom = getEmbedAtom(_children);
|
||||
if(_children[0] instanceof ExEmbedAtom) {
|
||||
embedAtom = (ExEmbedAtom)_children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
|
||||
// Second child should be the ExOleObjAtom
|
||||
if (_children[1] instanceof ExOleObjAtom) {
|
||||
|
@ -102,25 +107,17 @@ public class ExEmbed extends RecordContainer {
|
|||
|
||||
for (int i = 2; i < _children.length; i++) {
|
||||
if (_children[i] instanceof CString){
|
||||
if (menuName == null) menuName = (CString)_children[i];
|
||||
else if (progId == null) progId = (CString)_children[i];
|
||||
else if (clipboardName == null) clipboardName = (CString)_children[i];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "Record after atoms wasn't a CString, was of type " + _children[i].getRecordType());
|
||||
CString cs = (CString)_children[i];
|
||||
int opts = cs.getOptions() >> 4;
|
||||
switch(opts){
|
||||
case 0x1: menuName = cs; break;
|
||||
case 0x2: progId = cs; break;
|
||||
case 0x3: clipboardName = cs; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected RecordAtom getEmbedAtom(Record[] children){
|
||||
RecordAtom atom = null;
|
||||
if(_children[0] instanceof ExEmbedAtom) {
|
||||
atom = (ExEmbedAtom)_children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ExEmbedAtom}.
|
||||
*
|
||||
|
@ -151,6 +148,11 @@ public class ExEmbed extends RecordContainer {
|
|||
return menuName == null ? null : menuName.getText();
|
||||
}
|
||||
|
||||
public void setMenuName(String s)
|
||||
{
|
||||
if(menuName != null) menuName.setText(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OLE Programmatic Identifier.
|
||||
*
|
||||
|
@ -161,6 +163,10 @@ public class ExEmbed extends RecordContainer {
|
|||
return progId == null ? null : progId.getText();
|
||||
}
|
||||
|
||||
public void setProgId(String s)
|
||||
{
|
||||
if(progId != null) progId.setText(s);
|
||||
}
|
||||
/**
|
||||
* Gets the name that appears in the paste special dialog.
|
||||
*
|
||||
|
@ -171,6 +177,10 @@ public class ExEmbed extends RecordContainer {
|
|||
return clipboardName == null ? null : clipboardName.getText();
|
||||
}
|
||||
|
||||
public void setClipboardName(String s)
|
||||
{
|
||||
if(clipboardName != null) clipboardName.setText(s);
|
||||
}
|
||||
/**
|
||||
* Returns the type (held as a little endian in bytes 3 and 4)
|
||||
* that this class handles.
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* A container record that specifies information about a movie stored externally.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class ExMCIMovie extends RecordContainer {
|
||||
private byte[] _header;
|
||||
|
||||
//An ExVideoContainer record that specifies information about the MCI movie
|
||||
private ExVideoContainer exVideo;
|
||||
|
||||
/**
|
||||
* Set things up, and find our more interesting children
|
||||
*/
|
||||
protected ExMCIMovie(byte[] source, int start, int len) {
|
||||
// Grab the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source, start, _header, 0, 8);
|
||||
|
||||
// Find our children
|
||||
_children = Record.findChildRecords(source, start + 8, len - 8);
|
||||
findInterestingChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ExMCIMovie, with blank fields
|
||||
*/
|
||||
public ExMCIMovie() {
|
||||
_header = new byte[8];
|
||||
// Setup our header block
|
||||
_header[0] = 0x0f; // We are a container record
|
||||
LittleEndian.putShort(_header, 2, (short) getRecordType());
|
||||
|
||||
exVideo = new ExVideoContainer();
|
||||
_children = new Record[]{exVideo};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through our child records, picking out the ones that are
|
||||
* interesting, and saving those for use by the easy helper
|
||||
* methods.
|
||||
*/
|
||||
private void findInterestingChildren() {
|
||||
|
||||
// First child should be the ExVideoContainer
|
||||
if (_children[0] instanceof ExVideoContainer) {
|
||||
exVideo = (ExVideoContainer) _children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a ExVideoContainer, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We are of type 4103
|
||||
*/
|
||||
public long getRecordType() {
|
||||
return RecordTypes.ExMCIMovie.typeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
writeOut(_header[0], _header[1], getRecordType(), _children, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ExVideoContainer that specifies information about the MCI movie
|
||||
*/
|
||||
public ExVideoContainer getExVideo() {
|
||||
return exVideo; }
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hslf.util.SystemTimeUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* An atom record that specifies information about external audio or video data.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class ExMediaAtom extends RecordAtom
|
||||
{
|
||||
|
||||
/**
|
||||
* A bit that specifies whether the audio or video data is repeated continuously during playback.
|
||||
*/
|
||||
public static final int fLoop = 1;
|
||||
/**
|
||||
* A bit that specifies whether the audio or video data is rewound after playing.
|
||||
*/
|
||||
public static final int fRewind = 2;
|
||||
/**
|
||||
* A bit that specifies whether the audio data is recorded narration for the slide show. It MUST be FALSE if this ExMediaAtom record is contained by an ExVideoContainer record.
|
||||
*/
|
||||
public static final int fNarration = 4;
|
||||
|
||||
/**
|
||||
* Record header.
|
||||
*/
|
||||
private byte[] _header;
|
||||
|
||||
/**
|
||||
* record data
|
||||
*/
|
||||
private byte[] _recdata;
|
||||
|
||||
/**
|
||||
* Constructs a brand new link related atom record.
|
||||
*/
|
||||
protected ExMediaAtom() {
|
||||
_recdata = new byte[8];
|
||||
|
||||
_header = new byte[8];
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
LittleEndian.putInt(_header, 4, _recdata.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the link related atom record from its
|
||||
* source data.
|
||||
*
|
||||
* @param source the source data as a byte array.
|
||||
* @param start the start offset into the byte array.
|
||||
* @param len the length of the slice in the byte array.
|
||||
*/
|
||||
protected ExMediaAtom(byte[] source, int start, int len) {
|
||||
// Get the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source,start,_header,0,8);
|
||||
|
||||
// Grab the record data
|
||||
_recdata = new byte[len-8];
|
||||
System.arraycopy(source,start+8,_recdata,0,len-8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the record type.
|
||||
* @return the record type.
|
||||
*/
|
||||
public long getRecordType() { return RecordTypes.ExMediaAtom.typeID; }
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*
|
||||
* @param out the output stream to write to.
|
||||
* @throws java.io.IOException if an error occurs.
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
out.write(_header);
|
||||
out.write(_recdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* A 4-byte unsigned integer that specifies an ID for an external object.
|
||||
*
|
||||
* @return A 4-byte unsigned integer that specifies an ID for an external object.
|
||||
*/
|
||||
public int getObjectId(){
|
||||
return LittleEndian.getInt(_recdata, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A 4-byte unsigned integer that specifies an ID for an external object.
|
||||
*
|
||||
* @param id A 4-byte unsigned integer that specifies an ID for an external object.
|
||||
*/
|
||||
public void setObjectId(int id){
|
||||
LittleEndian.putInt(_recdata, 0, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A bit mask specifying options for displaying headers and footers
|
||||
*
|
||||
* @return A bit mask specifying options for displaying headers and footers
|
||||
*/
|
||||
public int getMask(){
|
||||
return LittleEndian.getInt(_recdata, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* A bit mask specifying options for displaying video
|
||||
*
|
||||
* @param mask A bit mask specifying options for displaying video
|
||||
*/
|
||||
public void setMask(int mask){
|
||||
LittleEndian.putInt(_recdata, 4, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bit the bit to check
|
||||
* @return whether the specified flag is set
|
||||
*/
|
||||
public boolean getFlag(int bit){
|
||||
return (getMask() & bit) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bit the bit to set
|
||||
* @param value whether the specified bit is set
|
||||
*/
|
||||
public void setFlag(int bit, boolean value){
|
||||
int mask = getMask();
|
||||
if(value) mask |= bit;
|
||||
else mask &= ~bit;
|
||||
setMask(mask);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("ExMediaAtom\n");
|
||||
buf.append("\tObjectId: " + getObjectId() + "\n");
|
||||
buf.append("\tMask : " + getMask() + "\n");
|
||||
buf.append("\t fLoop : " + getFlag(fLoop) + "\n");
|
||||
buf.append("\t fRewind : " + getFlag(fRewind) + "\n");
|
||||
buf.append("\t fNarration : " + getFlag(fNarration) + "\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -65,11 +65,35 @@ import org.apache.poi.util.LittleEndian;
|
|||
*/
|
||||
public class ExOleObjAtom extends RecordAtom {
|
||||
|
||||
/**
|
||||
* The object) is displayed as an embedded object inside of a container,
|
||||
*/
|
||||
public static final int DRAW_ASPECT_VISIBLE = 1;
|
||||
/**
|
||||
* The object is displayed as a thumbnail image.
|
||||
*/
|
||||
public static final int DRAW_ASPECT_THUMBNAIL = 2;
|
||||
/**
|
||||
* The object is displayed as an icon.
|
||||
*/
|
||||
public static final int DRAW_ASPECT_ICON = 4;
|
||||
/**
|
||||
* The object is displayed on the screen as though it were printed to a printer.
|
||||
*/
|
||||
public static final int DRAW_ASPECT_DOCPRINT = 8;
|
||||
|
||||
/**
|
||||
* An embedded OLE object; the object is serialized and saved within the file.
|
||||
*/
|
||||
public static final int TYPE_EMBEDDED = 0;
|
||||
/**
|
||||
* A linked OLE object; the object is saved outside of the file.
|
||||
*/
|
||||
public static final int TYPE_LINKED = 1;
|
||||
/**
|
||||
* The OLE object is an ActiveX control.
|
||||
*/
|
||||
public static final int TYPE_CONTROL = 2;
|
||||
|
||||
public static final int SUBTYPE_DEFAULT = 0;
|
||||
public static final int SUBTYPE_CLIPART_GALLERY = 1;
|
||||
|
@ -101,14 +125,13 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
/**
|
||||
* Constructs a brand new link related atom record.
|
||||
*/
|
||||
protected ExOleObjAtom() {
|
||||
public ExOleObjAtom() {
|
||||
_header = new byte[8];
|
||||
_data = new byte[18];
|
||||
_data = new byte[24];
|
||||
|
||||
LittleEndian.putShort(_header, 0, (short)1); //MUST be 0x1
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
LittleEndian.putInt(_header, 4, _data.length);
|
||||
|
||||
// I hope it is fine for the other values to be zero.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +167,16 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the object can be completely seen, or if only the
|
||||
* icon is visible.
|
||||
*
|
||||
* @param aspect the draw aspect, one of the {@code DRAW_ASPECT_*} constants.
|
||||
*/
|
||||
public void setDrawAspect(int aspect) {
|
||||
LittleEndian.putInt(_data, 0, aspect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the object is embedded or linked.
|
||||
*
|
||||
|
@ -153,6 +186,15 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the object is embedded or linked.
|
||||
*
|
||||
* @param type the type, one of the {@code TYPE_EMBEDDED_*} constants.
|
||||
*/
|
||||
public void setType(int type) {
|
||||
LittleEndian.putInt(_data, 4, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier for the OLE object.
|
||||
*
|
||||
|
@ -162,6 +204,15 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier for the OLE object.
|
||||
*
|
||||
* @param id the object ID.
|
||||
*/
|
||||
public void setObjID(int id) {
|
||||
LittleEndian.putInt(_data, 8, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of OLE object.
|
||||
*
|
||||
|
@ -171,6 +222,15 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of OLE object.
|
||||
*
|
||||
* @param type the sub-type, one of the {@code SUBTYPE_*} constants.
|
||||
*/
|
||||
public void setSubType(int type) {
|
||||
LittleEndian.putInt(_data, 12, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reference to the persistent object
|
||||
*
|
||||
|
@ -181,6 +241,16 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reference to the persistent object
|
||||
*
|
||||
* @param ref the reference to the persistent object, corresponds with an
|
||||
* {@code ExOleObjStg} storage container.
|
||||
*/
|
||||
public void setObjStgDataRef(int ref) {
|
||||
LittleEndian.putInt(_data, 16, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the object's image is blank.
|
||||
*
|
||||
|
@ -191,6 +261,24 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
return LittleEndian.getInt(_data, 20) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets misc options (the last four bytes in the atom).
|
||||
*
|
||||
* @return {@code true} if the object's image is blank.
|
||||
*/
|
||||
public int getOptions() {
|
||||
// Even though this is a mere boolean, KOffice's code says it's an int.
|
||||
return LittleEndian.getInt(_data, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets misc options (the last four bytes in the atom).
|
||||
*/
|
||||
public void setOptions(int opts) {
|
||||
// Even though this is a mere boolean, KOffice's code says it's an int.
|
||||
LittleEndian.putInt(_data, 20, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type (held as a little endian in bytes 3 and 4)
|
||||
* that this class handles.
|
||||
|
@ -210,4 +298,16 @@ public class ExOleObjAtom extends RecordAtom {
|
|||
out.write(_header);
|
||||
out.write(_data);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("ExOleObjAtom\n");
|
||||
buf.append(" drawAspect: " + getDrawAspect() + "\n");
|
||||
buf.append(" type: " + getType() + "\n");
|
||||
buf.append(" objID: " + getObjID() + "\n");
|
||||
buf.append(" subType: " + getSubType() + "\n");
|
||||
buf.append(" objStgDataRef: " + getObjStgDataRef() + "\n");
|
||||
buf.append(" options: " + getOptions() + "\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.poi.hslf.record;
|
|||
import java.io.*;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
|
@ -28,7 +29,7 @@ import org.apache.poi.util.LittleEndian;
|
|||
*
|
||||
* @author Daniel Noll
|
||||
*/
|
||||
public class ExOleObjStg extends RecordAtom implements PersistRecord {
|
||||
public class ExOleObjStg extends RecordAtom implements PositionDependentRecord, PersistRecord {
|
||||
|
||||
private int _persistId; // Found from PersistPtrHolder
|
||||
|
||||
|
@ -45,10 +46,11 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
|
|||
/**
|
||||
* Constructs a new empty storage container.
|
||||
*/
|
||||
protected ExOleObjStg() {
|
||||
public ExOleObjStg() {
|
||||
_header = new byte[8];
|
||||
_data = new byte[0];
|
||||
|
||||
LittleEndian.putShort(_header, 0, (short)0x10);
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
LittleEndian.putInt(_header, 4, _data.length);
|
||||
}
|
||||
|
@ -90,6 +92,10 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
|
|||
return new InflaterInputStream(compressedStream);
|
||||
}
|
||||
|
||||
public byte[] getRawData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the embedded data.
|
||||
*
|
||||
|
@ -144,4 +150,23 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
|
|||
public void setPersistId(int id) {
|
||||
_persistId = id;
|
||||
}
|
||||
|
||||
/** Our location on the disk, as of the last write out */
|
||||
protected int myLastOnDiskOffset;
|
||||
|
||||
/** Fetch our location on the disk, as of the last write out */
|
||||
public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
|
||||
|
||||
/**
|
||||
* Update the Record's idea of where on disk it lives, after a write out.
|
||||
* Use with care...
|
||||
*/
|
||||
public void setLastOnDiskOffset(int offset) {
|
||||
myLastOnDiskOffset = offset;
|
||||
}
|
||||
|
||||
public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* A container record that specifies information about external video data.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class ExVideoContainer extends RecordContainer {
|
||||
private byte[] _header;
|
||||
|
||||
// Links to our more interesting children
|
||||
private ExMediaAtom mediaAtom;
|
||||
//the UNC or local path to a video file.
|
||||
private CString pathAtom;
|
||||
|
||||
/**
|
||||
* Set things up, and find our more interesting children
|
||||
*/
|
||||
protected ExVideoContainer(byte[] source, int start, int len) {
|
||||
// Grab the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source,start,_header,0,8);
|
||||
|
||||
// Find our children
|
||||
_children = Record.findChildRecords(source,start+8,len-8);
|
||||
findInterestingChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through our child records, picking out the ones that are
|
||||
* interesting, and saving those for use by the easy helper
|
||||
* methods.
|
||||
*/
|
||||
private void findInterestingChildren() {
|
||||
|
||||
// First child should be the ExMediaAtom
|
||||
if(_children[0] instanceof ExMediaAtom) {
|
||||
mediaAtom = (ExMediaAtom)_children[0];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "First child record wasn't a ExMediaAtom, was of type " + _children[0].getRecordType());
|
||||
}
|
||||
if(_children[1] instanceof CString) {
|
||||
pathAtom = (CString)_children[1];
|
||||
} else {
|
||||
logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ExVideoContainer, with blank fields
|
||||
*/
|
||||
public ExVideoContainer() {
|
||||
// Setup our header block
|
||||
_header = new byte[8];
|
||||
_header[0] = 0x0f; // We are a container record
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
|
||||
_children = new Record[2];
|
||||
_children[0] = mediaAtom = new ExMediaAtom();
|
||||
_children[1] = pathAtom = new CString();
|
||||
}
|
||||
|
||||
/**
|
||||
* We are of type 4103
|
||||
*/
|
||||
public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; }
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
writeOut(_header[0],_header[1],getRecordType(),_children,out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ExMediaAtom of this link
|
||||
*/
|
||||
public ExMediaAtom getExMediaAtom() { return mediaAtom; }
|
||||
|
||||
/**
|
||||
* Returns the Path Atom (CString) of this link
|
||||
*/
|
||||
public CString getPathAtom() { return pathAtom; }
|
||||
|
||||
}
|
|
@ -39,27 +39,39 @@ public class InteractiveInfoAtom extends RecordAtom
|
|||
/**
|
||||
* Action Table
|
||||
*/
|
||||
public static final int ACTION_NONE = 0;
|
||||
public static final int ACTION_MACRO = 1;
|
||||
public static final int ACTION_RUNPROGRAM = 2;
|
||||
public static final int ACTION_JUMP = 3;
|
||||
public static final int ACTION_HYPERLINK = 4;
|
||||
public static final int ACTION_OLE = 5;
|
||||
public static final int ACTION_MEDIA = 6;
|
||||
public static final int ACTION_CUSTOMSHOW = 7;
|
||||
public static final byte ACTION_NONE = 0;
|
||||
public static final byte ACTION_MACRO = 1;
|
||||
public static final byte ACTION_RUNPROGRAM = 2;
|
||||
public static final byte ACTION_JUMP = 3;
|
||||
public static final byte ACTION_HYPERLINK = 4;
|
||||
public static final byte ACTION_OLE = 5;
|
||||
public static final byte ACTION_MEDIA = 6;
|
||||
public static final byte ACTION_CUSTOMSHOW = 7;
|
||||
|
||||
/**
|
||||
* Jump Table
|
||||
*/
|
||||
public static final int JUMP_NONE = 0;
|
||||
public static final int JUMP_NEXTSLIDE = 1;
|
||||
public static final int JUMP_PREVIOUSSLIDE = 2;
|
||||
public static final int JUMP_FIRSTSLIDE = 3;
|
||||
public static final int JUMP_LASTSLIDE = 4;
|
||||
public static final int JUMP_LASTSLIDEVIEWED = 5;
|
||||
public static final int JUMP_ENDSHOW = 6;
|
||||
|
||||
public static final byte JUMP_NONE = 0;
|
||||
public static final byte JUMP_NEXTSLIDE = 1;
|
||||
public static final byte JUMP_PREVIOUSSLIDE = 2;
|
||||
public static final byte JUMP_FIRSTSLIDE = 3;
|
||||
public static final byte JUMP_LASTSLIDE = 4;
|
||||
public static final byte JUMP_LASTSLIDEVIEWED = 5;
|
||||
public static final byte JUMP_ENDSHOW = 6;
|
||||
|
||||
/**
|
||||
* Types of hyperlinks
|
||||
*/
|
||||
public static final byte LINK_NextSlide = 0x00;
|
||||
public static final byte LINK_PreviousSlide = 0x01;
|
||||
public static final byte LINK_FirstSlide = 0x02;
|
||||
public static final byte LINK_LastSlide = 0x03;
|
||||
public static final byte LINK_CustomShow = 0x06;
|
||||
public static final byte LINK_SlideNumber = 0x07;
|
||||
public static final byte LINK_Url = 0x08;
|
||||
public static final byte LINK_OtherPresentation = 0x09;
|
||||
public static final byte LINK_OtherFile = 0x0A;
|
||||
public static final byte LINK_NULL = (byte)0xFF;
|
||||
|
||||
/**
|
||||
* Record header.
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hslf.util.SystemTimeUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* Atom that contains information that describes shape client data.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class OEShapeAtom extends RecordAtom
|
||||
{
|
||||
|
||||
/**
|
||||
* Record header.
|
||||
*/
|
||||
private byte[] _header;
|
||||
|
||||
/**
|
||||
* record data
|
||||
*/
|
||||
private byte[] _recdata;
|
||||
|
||||
/**
|
||||
* Constructs a brand new link related atom record.
|
||||
*/
|
||||
public OEShapeAtom() {
|
||||
_recdata = new byte[4];
|
||||
|
||||
_header = new byte[8];
|
||||
LittleEndian.putShort(_header, 2, (short)getRecordType());
|
||||
LittleEndian.putInt(_header, 4, _recdata.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the link related atom record from its
|
||||
* source data.
|
||||
*
|
||||
* @param source the source data as a byte array.
|
||||
* @param start the start offset into the byte array.
|
||||
* @param len the length of the slice in the byte array.
|
||||
*/
|
||||
protected OEShapeAtom(byte[] source, int start, int len) {
|
||||
// Get the header
|
||||
_header = new byte[8];
|
||||
System.arraycopy(source,start,_header,0,8);
|
||||
|
||||
// Grab the record data
|
||||
_recdata = new byte[len-8];
|
||||
System.arraycopy(source,start+8,_recdata,0,len-8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the record type.
|
||||
* @return the record type.
|
||||
*/
|
||||
public long getRecordType() { return RecordTypes.OEShapeAtom.typeID; }
|
||||
|
||||
/**
|
||||
* Write the contents of the record back, so it can be written
|
||||
* to disk
|
||||
*
|
||||
* @param out the output stream to write to.
|
||||
* @throws java.io.IOException if an error occurs.
|
||||
*/
|
||||
public void writeOut(OutputStream out) throws IOException {
|
||||
out.write(_header);
|
||||
out.write(_recdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* shape flags.
|
||||
*
|
||||
* @return shape flags.
|
||||
*/
|
||||
public int getOptions(){
|
||||
return LittleEndian.getInt(_recdata, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* shape flags.
|
||||
*
|
||||
* @param id shape flags.
|
||||
*/
|
||||
public void setOptions(int id){
|
||||
LittleEndian.putInt(_recdata, 0, id);
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ public class RecordTypes {
|
|||
public static final Type BookmarkSeedAtom = new Type(2025,null);
|
||||
public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class);
|
||||
public static final Type ExObjRefAtom = new Type(3009,null);
|
||||
public static final Type OEShapeAtom = new Type(3009,null);
|
||||
public static final Type OEShapeAtom = new Type(3009,OEShapeAtom.class);
|
||||
public static final Type OEPlaceholderAtom = new Type(3011,OEPlaceholderAtom.class);
|
||||
public static final Type GPopublicintAtom = new Type(3024,null);
|
||||
public static final Type GRatioAtom = new Type(3031,null);
|
||||
|
@ -128,11 +128,11 @@ public class RecordTypes {
|
|||
public static final Type DateTimeMCAtom = new Type(4087,null);
|
||||
public static final Type GenericDateMCAtom = new Type(4088,null);
|
||||
public static final Type FooterMCAtom = new Type(4090,null);
|
||||
public static final Type ExControlAtom = new Type(4091,null);
|
||||
public static final Type ExMediaAtom = new Type(4100,null);
|
||||
public static final Type ExVideo = new Type(4101,null);
|
||||
public static final Type ExAviMovie = new Type(4102,null);
|
||||
public static final Type ExMCIMovie = new Type(4103,null);
|
||||
public static final Type ExControlAtom = new Type(4091,ExControlAtom.class);
|
||||
public static final Type ExMediaAtom = new Type(4100,ExMediaAtom.class);
|
||||
public static final Type ExVideoContainer = new Type(4101,ExVideoContainer.class);
|
||||
public static final Type ExAviMovie = new Type(4102,ExAviMovie.class);
|
||||
public static final Type ExMCIMovie = new Type(4103,ExMCIMovie.class);
|
||||
public static final Type ExMIDIAudio = new Type(4109,null);
|
||||
public static final Type ExCDAudio = new Type(4110,null);
|
||||
public static final Type ExWAVAudioEmbedded = new Type(4111,null);
|
||||
|
@ -140,7 +140,8 @@ public class RecordTypes {
|
|||
public static final Type ExOleObjStg = new Type(4113,ExOleObjStg.class);
|
||||
public static final Type ExCDAudioAtom = new Type(4114,null);
|
||||
public static final Type ExWAVAudioEmbeddedAtom = new Type(4115,null);
|
||||
public static final Type AnimationInfoAtom = new Type(4116,null);
|
||||
public static final Type AnimationInfo = new Type(4116,AnimationInfo.class);
|
||||
public static final Type AnimationInfoAtom = new Type(4081,AnimationInfoAtom.class);
|
||||
public static final Type RTFDateTimeMCAtom = new Type(4117,null);
|
||||
public static final Type ProgTags = new Type(5000,DummyPositionSensitiveRecordWithChildren.class);
|
||||
public static final Type ProgStringTag = new Type(5001,null);
|
||||
|
|
|
@ -367,8 +367,14 @@ public class StyleTextPropAtom extends RecordAtom
|
|||
|
||||
rawContents = baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
public void setRawContents(byte[] bytes) {
|
||||
rawContents = bytes;
|
||||
reserved = new byte[0];
|
||||
initialised = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Paragraph TextPropCollection, and add it to the list
|
||||
* @param charactersCovered The number of characters this TextPropCollection will cover
|
||||
* @return the new TextPropCollection, which will then be in the list
|
||||
|
|
|
@ -53,7 +53,7 @@ public class TextRulerAtom extends RecordAtom {
|
|||
/**
|
||||
* Constructs a new empty ruler atom.
|
||||
*/
|
||||
protected TextRulerAtom() {
|
||||
public TextRulerAtom() {
|
||||
_header = new byte[8];
|
||||
_data = new byte[0];
|
||||
|
||||
|
@ -191,4 +191,19 @@ public class TextRulerAtom extends RecordAtom {
|
|||
public int[] getBulletOffsets(){
|
||||
return bulletOffsets;
|
||||
}
|
||||
|
||||
public static TextRulerAtom getParagraphInstance(){
|
||||
byte[] data = new byte[] {
|
||||
0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,
|
||||
0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01
|
||||
};
|
||||
TextRulerAtom ruler = new TextRulerAtom(data, 0, data.length);
|
||||
return ruler;
|
||||
}
|
||||
|
||||
public void setParagraphIndent(short tetxOffset, short bulletOffset){
|
||||
LittleEndian.putShort(_data, 4, tetxOffset);
|
||||
LittleEndian.putShort(_data, 6, bulletOffset);
|
||||
LittleEndian.putShort(_data, 8, bulletOffset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -687,6 +687,7 @@ public class RichTextRun {
|
|||
*/
|
||||
public void setBulletFont(int idx) {
|
||||
setParaTextPropVal("bullet.font", idx);
|
||||
setFlag(false, ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,11 +24,7 @@ import java.io.FileInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.poi.ddf.EscherBSERecord;
|
||||
import org.apache.poi.ddf.EscherContainerRecord;
|
||||
|
@ -38,21 +34,9 @@ import org.apache.poi.hslf.HSLFSlideShow;
|
|||
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
|
||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||
import org.apache.poi.hslf.model.*;
|
||||
import org.apache.poi.hslf.record.Document;
|
||||
import org.apache.poi.hslf.record.DocumentAtom;
|
||||
import org.apache.poi.hslf.record.FontCollection;
|
||||
import org.apache.poi.hslf.record.FontEntityAtom;
|
||||
import org.apache.poi.hslf.record.HeadersFootersContainer;
|
||||
import org.apache.poi.hslf.record.ParentAwareRecord;
|
||||
import org.apache.poi.hslf.record.PersistPtrHolder;
|
||||
import org.apache.poi.hslf.record.PositionDependentRecord;
|
||||
import org.apache.poi.hslf.record.PositionDependentRecordContainer;
|
||||
import org.apache.poi.hslf.record.Record;
|
||||
import org.apache.poi.hslf.record.RecordContainer;
|
||||
import org.apache.poi.hslf.record.RecordTypes;
|
||||
import org.apache.poi.hslf.record.SlideListWithText;
|
||||
import org.apache.poi.hslf.record.SlidePersistAtom;
|
||||
import org.apache.poi.hslf.record.UserEditAtom;
|
||||
import org.apache.poi.hslf.model.Notes;
|
||||
import org.apache.poi.hslf.model.Slide;
|
||||
import org.apache.poi.hslf.record.*;
|
||||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
|
||||
import org.apache.poi.util.ArrayUtil;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
|
@ -590,9 +574,8 @@ public final class SlideShow {
|
|||
* Create a blank <code>Slide</code>.
|
||||
*
|
||||
* @return the created <code>Slide</code>
|
||||
* @throws IOException
|
||||
*/
|
||||
public Slide createSlide() throws IOException {
|
||||
public Slide createSlide() {
|
||||
SlideListWithText slist = null;
|
||||
|
||||
// We need to add the records to the SLWT that deals
|
||||
|
@ -660,9 +643,13 @@ public final class SlideShow {
|
|||
for (int i = 0; i < _records.length; i++) {
|
||||
Record record = _records[i];
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
record.writeOut(out);
|
||||
try {
|
||||
record.writeOut(out);
|
||||
} catch (IOException e){
|
||||
throw new HSLFException(e);
|
||||
}
|
||||
|
||||
// Grab interesting records as they come past
|
||||
// Grab interesting records as they come past
|
||||
if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){
|
||||
ptr = (PersistPtrHolder)_records[i];
|
||||
}
|
||||
|
@ -888,4 +875,72 @@ public final class SlideShow {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a movie in this presentation
|
||||
*
|
||||
* @param path the path or url to the movie
|
||||
* @return 0-based index of the movie
|
||||
*/
|
||||
public int addMovie(String path, int type) {
|
||||
ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
|
||||
if(lst == null){
|
||||
lst = new ExObjList();
|
||||
_documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
|
||||
}
|
||||
|
||||
ExObjListAtom objAtom = lst.getExObjListAtom();
|
||||
//increment the object ID seed
|
||||
int objectId = (int)objAtom.getObjectIDSeed() + 1;
|
||||
objAtom.setObjectIDSeed(objectId);
|
||||
ExMCIMovie mci;
|
||||
switch (type){
|
||||
case MovieShape.MOVIE_MPEG:
|
||||
mci = new ExMCIMovie();
|
||||
break;
|
||||
case MovieShape.MOVIE_AVI:
|
||||
mci = new ExAviMovie();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported Movie: " + type);
|
||||
}
|
||||
|
||||
lst.appendChildRecord(mci);
|
||||
ExVideoContainer exVideo = mci.getExVideo();
|
||||
exVideo.getExMediaAtom().setObjectId(objectId);
|
||||
exVideo.getExMediaAtom().setMask(0xE80000);
|
||||
exVideo.getPathAtom().setText(path);
|
||||
return objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a control in this presentation
|
||||
*
|
||||
* @param name name of the control, e.g. "Shockwave Flash Object"
|
||||
* @param progId OLE Programmatic Identifier, e.g. "ShockwaveFlash.ShockwaveFlash.9"
|
||||
* @return 0-based index of the control
|
||||
*/
|
||||
public int addControl(String name, String progId) {
|
||||
ExObjList lst = _documentRecord.getExObjList();
|
||||
if (lst == null) {
|
||||
lst = new ExObjList();
|
||||
_documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
|
||||
}
|
||||
ExObjListAtom objAtom = lst.getExObjListAtom();
|
||||
//increment the object ID seed
|
||||
int objectId = (int) objAtom.getObjectIDSeed() + 1;
|
||||
objAtom.setObjectIDSeed(objectId);
|
||||
ExControl ctrl = new ExControl();
|
||||
ExOleObjAtom oleObj = ctrl.getExOleObjAtom();
|
||||
oleObj.setObjID(objectId);
|
||||
oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
|
||||
oleObj.setType(ExOleObjAtom.TYPE_CONTROL);
|
||||
oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
|
||||
|
||||
ctrl.setProgId(progId);
|
||||
ctrl.setMenuName(name);
|
||||
ctrl.setClipboardName(name);
|
||||
lst.addChildAfter(ctrl, objAtom);
|
||||
|
||||
return objectId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* ====================================================================
|
||||
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.hslf.model;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||
import org.apache.poi.hslf.HSLFSlideShow;
|
||||
|
||||
/**
|
||||
* Test <code>MovieShape</code> object.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestMovieShape extends TestCase {
|
||||
|
||||
protected String cwd = System.getProperty("HSLF.testdata.path");
|
||||
|
||||
public void testCreate() throws Exception {
|
||||
SlideShow ppt = new SlideShow();
|
||||
|
||||
Slide slide = ppt.createSlide();
|
||||
|
||||
String path = cwd + "/test-movie.mpg";
|
||||
int movieIdx = ppt.addMovie(path, MovieShape.MOVIE_MPEG);
|
||||
int thumbnailIdx = ppt.addPicture(new File(cwd, "tomcat.png"), Picture.PNG);
|
||||
|
||||
MovieShape shape = new MovieShape(movieIdx, thumbnailIdx);
|
||||
shape.setAnchor(new Rectangle2D.Float(300,225,120,90));
|
||||
slide.addShape(shape);
|
||||
|
||||
assertEquals(path, shape.getPath());
|
||||
assertTrue(shape.isAutoPlay());
|
||||
shape.setAutoPlay(false);
|
||||
assertFalse(shape.isAutoPlay());
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ppt.write(out);
|
||||
|
||||
ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));
|
||||
slide = ppt.getSlides()[0];
|
||||
shape = (MovieShape)slide.getShapes()[0];
|
||||
assertEquals(path, shape.getPath());
|
||||
assertFalse(shape.isAutoPlay());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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.hslf.model;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||
import org.apache.poi.hslf.HSLFSlideShow;
|
||||
|
||||
/**
|
||||
* Test <code>Table</code> object.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestTable extends TestCase {
|
||||
|
||||
/**
|
||||
* Test that ShapeFactory works properly and returns <code>Table</code>
|
||||
*/
|
||||
public void testShapeFactory() throws Exception {
|
||||
SlideShow ppt = new SlideShow();
|
||||
|
||||
Slide slide = ppt.createSlide();
|
||||
|
||||
Table tbl = new Table(2, 5);
|
||||
slide.addShape(tbl);
|
||||
|
||||
assertTrue(slide.getShapes()[0] instanceof Table);
|
||||
Table tbl2 = (Table)slide.getShapes()[0];
|
||||
assertEquals(tbl.getNumberOfColumns(), tbl2.getNumberOfColumns());
|
||||
assertEquals(tbl.getNumberOfRows(), tbl2.getNumberOfRows());
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ppt.write(out);
|
||||
out.close();
|
||||
|
||||
ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));
|
||||
slide = ppt.getSlides()[0];
|
||||
assertTrue(slide.getShapes()[0] instanceof Table);
|
||||
Table tbl3 = (Table)slide.getShapes()[0];
|
||||
assertEquals(tbl.getNumberOfColumns(), tbl3.getNumberOfColumns());
|
||||
assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.poi.util.HexDump;
|
||||
|
||||
/**
|
||||
* Tests that {@link HeadersFootersAtom} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestAnimationInfoAtom extends TestCase {
|
||||
// From a real file
|
||||
/*
|
||||
<AnimationInfoAtom info="1" type="4081" size="28" offset="4015" header="01 00 F1 0F 1C 00 00 00 ">
|
||||
00 00 00 07 04 05 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00
|
||||
00 00 00
|
||||
</AnimationInfoAtom>
|
||||
*/
|
||||
private byte[] data = new byte[] {
|
||||
0x01, 0x00, (byte)0xF1, 0x0F, 0x1C, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
public void testRead() throws Exception {
|
||||
AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);
|
||||
assertEquals(RecordTypes.AnimationInfoAtom.typeID, record.getRecordType());
|
||||
assertTrue(record.getFlag(AnimationInfoAtom.Automatic));
|
||||
assertTrue(record.getFlag(AnimationInfoAtom.Play));
|
||||
assertTrue(record.getFlag(AnimationInfoAtom.Synchronous));
|
||||
assertFalse(record.getFlag(AnimationInfoAtom.Reverse));
|
||||
assertFalse(record.getFlag(AnimationInfoAtom.Sound));
|
||||
assertFalse(record.getFlag(AnimationInfoAtom.StopSound));
|
||||
assertFalse(record.getFlag(AnimationInfoAtom.Hide));
|
||||
assertFalse(record.getFlag(AnimationInfoAtom.AnimateBg));
|
||||
assertEquals(0x07000000, record.getDimColor());
|
||||
assertEquals(0, record.getSoundIdRef());
|
||||
assertEquals(0, record.getDelayTime());
|
||||
assertEquals(2, record.getOrderID());
|
||||
assertEquals(0, record.getSlideCount());
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
AnimationInfoAtom record = new AnimationInfoAtom();
|
||||
record.setDimColor(0x07000000);
|
||||
record.setOrderID(2);
|
||||
record.setFlag(AnimationInfoAtom.Automatic, true);
|
||||
record.setFlag(AnimationInfoAtom.Play, true);
|
||||
record.setFlag(AnimationInfoAtom.Synchronous, true);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests that {@link org.apache.poi.hslf.record.ExControl} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestExControl extends TestCase {
|
||||
|
||||
// From a real file (embedded SWF control)
|
||||
/*
|
||||
<ExControl info="15" type="4078" size="218" offset="76" header="0F 00 EE 0F DA 00 00 00 ">
|
||||
<ExControlAtom info="0" type="4091" size="4" offset="84" header="00 00 FB 0F 04 00 00 00 ">
|
||||
00 01 00 00
|
||||
</ExControlAtom>
|
||||
<ExOleObjAtom info="1" type="4035" size="24" offset="96" header="01 00 C3 0F 18 00 00 00 ">
|
||||
01 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 96 13 00
|
||||
</ExOleObjAtom>
|
||||
<CString info="16" type="4026" size="44" offset="128" header="10 00 BA 0F 2C 00 00 00 ">
|
||||
53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61
|
||||
00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00
|
||||
</CString>
|
||||
<CString info="32" type="4026" size="62" offset="180" header="20 00 BA 0F 3E 00 00 00 ">
|
||||
53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00 6C 00 61 00 73
|
||||
00 68 00 2E 00 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00
|
||||
6C 00 61 00 73 00 68 00 2E 00 39 00
|
||||
</CString>
|
||||
<CString info="48" type="4026" size="44" offset="250" header="30 00 BA 0F 2C 00 00 00 ">
|
||||
53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61
|
||||
00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00
|
||||
</CString>
|
||||
</ExControl>
|
||||
*/
|
||||
private byte[] data = new byte[] {
|
||||
0x0F, 0x00, (byte)0xEE, 0x0F, (byte)0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xFB, 0x0F, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00,
|
||||
0x10, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00,
|
||||
0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68,
|
||||
0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, (byte)0xBA,
|
||||
0x0F, 0x3E, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00,
|
||||
0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x53, 0x00, 0x68,
|
||||
0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00,
|
||||
0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x39, 0x00, 0x30, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00,
|
||||
0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20,
|
||||
0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00,
|
||||
0x65, 0x00, 0x63, 0x00, 0x74, 0x00
|
||||
};
|
||||
|
||||
public void testRead() throws Exception {
|
||||
ExControl record = new ExControl(data, 0, data.length);
|
||||
assertEquals(RecordTypes.ExControl.typeID, record.getRecordType());
|
||||
|
||||
assertNotNull(record.getExControlAtom());
|
||||
assertEquals(256, record.getExControlAtom().getSlideId());
|
||||
|
||||
ExOleObjAtom oleObj = record.getExOleObjAtom();
|
||||
assertNotNull(oleObj);
|
||||
assertEquals(oleObj.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);
|
||||
assertEquals(oleObj.getType(), ExOleObjAtom.TYPE_CONTROL);
|
||||
assertEquals(oleObj.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);
|
||||
|
||||
assertEquals("Shockwave Flash Object", record.getMenuName());
|
||||
assertEquals("ShockwaveFlash.ShockwaveFlash.9", record.getProgId());
|
||||
assertEquals("Shockwave Flash Object", record.getClipboardName());
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
ExControl record = new ExControl(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
ExControl record = new ExControl();
|
||||
ExControlAtom ctrl = record.getExControlAtom();
|
||||
ctrl.setSlideId(256);
|
||||
|
||||
ExOleObjAtom oleObj = record.getExOleObjAtom();
|
||||
oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
|
||||
oleObj.setType(ExOleObjAtom.TYPE_CONTROL);
|
||||
oleObj.setObjID(1);
|
||||
oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
|
||||
oleObj.setObjStgDataRef(2);
|
||||
oleObj.setOptions(1283584);
|
||||
|
||||
record.setMenuName("Shockwave Flash Object");
|
||||
record.setProgId("ShockwaveFlash.ShockwaveFlash.9");
|
||||
record.setClipboardName("Shockwave Flash Object");
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertEquals(data.length, b.length);
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests that {@link org.apache.poi.hslf.record.HeadersFootersAtom} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestExMediaAtom extends TestCase {
|
||||
// From a real file
|
||||
private byte[] data = new byte[] {
|
||||
0x00, 0x00, (byte)0x04, 0x10, 0x08, 0x00, 0x00, 00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
public void testRead() throws Exception {
|
||||
ExMediaAtom record = new ExMediaAtom(data, 0, data.length);
|
||||
assertEquals(RecordTypes.ExMediaAtom.typeID, record.getRecordType());
|
||||
|
||||
assertEquals(1, record.getObjectId());
|
||||
assertFalse(record.getFlag(ExMediaAtom.fLoop));
|
||||
assertFalse(record.getFlag(ExMediaAtom.fNarration));
|
||||
assertFalse(record.getFlag(ExMediaAtom.fRewind));
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
ExMediaAtom record = new ExMediaAtom(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
ExMediaAtom ref = new ExMediaAtom(data, 0, data.length);
|
||||
System.out.println(ref.getMask());
|
||||
|
||||
ExMediaAtom record = new ExMediaAtom();
|
||||
record.setObjectId(1);
|
||||
record.setFlag(HeadersFootersAtom.fHasDate, false);
|
||||
record.setFlag(HeadersFootersAtom.fHasTodayDate, false);
|
||||
record.setFlag(HeadersFootersAtom.fHasFooter, false);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testFlags() throws Exception {
|
||||
ExMediaAtom record = new ExMediaAtom();
|
||||
|
||||
//in a new record all the bits are 0
|
||||
for(int i = 0; i < 3; i++) assertFalse(record.getFlag(1 << i));
|
||||
|
||||
record.setFlag(ExMediaAtom.fLoop, true);
|
||||
assertTrue(record.getFlag(ExMediaAtom.fLoop));
|
||||
|
||||
record.setFlag(ExMediaAtom.fNarration, true);
|
||||
assertTrue(record.getFlag(ExMediaAtom.fNarration));
|
||||
|
||||
record.setFlag(ExMediaAtom.fNarration, false);
|
||||
assertFalse(record.getFlag(ExMediaAtom.fNarration));
|
||||
|
||||
record.setFlag(ExMediaAtom.fNarration, false);
|
||||
assertFalse(record.getFlag(ExMediaAtom.fNarration));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests that {@link ExOleObjAtom} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestExOleObjAtom extends TestCase {
|
||||
// From a real file (embedded SWF control)
|
||||
private byte[] data = new byte[] {
|
||||
0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00 };
|
||||
|
||||
public void testRead() throws Exception {
|
||||
ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);
|
||||
assertEquals(RecordTypes.ExOleObjAtom.typeID, record.getRecordType());
|
||||
System.out.println(record);
|
||||
|
||||
assertEquals(record.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);
|
||||
assertEquals(record.getType(), ExOleObjAtom.TYPE_CONTROL);
|
||||
assertEquals(record.getObjID(), 1);
|
||||
assertEquals(record.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);
|
||||
assertEquals(record.getObjStgDataRef(), 2);
|
||||
assertEquals(record.getOptions(), 1283584); //ther meaning is unknown
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
ExOleObjAtom record = new ExOleObjAtom();
|
||||
record.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
|
||||
record.setType(ExOleObjAtom.TYPE_CONTROL);
|
||||
record.setObjID(1);
|
||||
record.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
|
||||
record.setObjStgDataRef(2);
|
||||
record.setOptions(1283584);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
||||
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
||||
|
||||
/**
|
||||
* Tests that {@link ExOleObjStg} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestExOleObjStg extends TestCase {
|
||||
|
||||
// From a real file (embedded SWF control)
|
||||
/*
|
||||
<ExOleObjStg info="16" type="4113" size="347" offset="4322" header="10 00 11 10 5B 01 00 00 ">
|
||||
00 0E 00 00 78 9C BB 70 5E F0 C1 C2 8D 52 0F 19 D0 80 1D 03 33 C3 BF FF 9C
|
||||
0C 6C 48 62 8C 40 CC 04 E3 08 30 30 B0 40 C5 FE FD FF FF 1F 24 C4 0C C4 FF
|
||||
47 C1 90 02 41 0C F9 40 58 C2 A0 C0 E0 CA 90 07 A4 8B 18 2A D1 93 02 5E 20
|
||||
C6 C0 0A 8F 73 50 5A C8 BB 5D 73 29 77 DD 79 C1 69 3B 5C 5C 83 43 50 D5 06
|
||||
BC 48 2F 2B 66 38 C9 C8 0E 64 3B 30 42 C4 9C 81 B6 83 EC 4D 05 93 C5 24 D9
|
||||
0D 02 42 0C 4C 8C C8 FE 21 56 9F 02 23 C9 56 E1 04 E4 D8 4F 4D 40 89 FD A0
|
||||
BC FB 17 4B BA F8 07 C5 A3 60 78 03 7A E6 FF 09 67 59 1B 41 F9 9F 95 61 34
|
||||
FF 53 13 50 62 3F 4C 1F AC 1C 18 CD F7 23 0B C0 DA 74 A0 B6 1B A8 3D 37 1A
|
||||
F7 23 0B A4 87 A6 85 0A 00 1B 64 6F 38 21 98 03 DA C2 E7 60 90 01 92 69 0C
|
||||
39 0C 65 0C 05 40 32 11 58 2F A4 02 6B 07 3D 60 19 5D 0E 14 27 4E 05 1F 90
|
||||
0C 67 C8 04 96 ED 29 C0 72 BE 1C C8 E3 06 E3 FF FF 39 18 B8 80 2C 0F A0 5C
|
||||
3A 43 06 58 2D A8 A7 E1 C3 10 02 97 87 B8 02 E6 1A 60 77 83 21 18 A8 12 64
|
||||
8A 23 D0 B6 1C B8 59 C8 AA 90 F5 F0 62 94 75 DC C0 DE 0A 37 5C 1D 33 54 35
|
||||
88 97 08 35 91 83 81 07 EC 27 10 BF 18 E8 9B E1 0F 00 BD 65 3D D4
|
||||
</ExOleObjStg>
|
||||
*/
|
||||
private byte[] data = new byte[] {
|
||||
0x10, 0x00, 0x11, 0x10, 0x5B, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x78, (byte)0x9C, (byte)0xBB, 0x70,
|
||||
0x5E, (byte)0xF0, (byte)0xC1, (byte)0xC2, (byte)0x8D, 0x52, 0x0F, 0x19, (byte)0xD0, (byte)0x80, 0x1D, 0x03,
|
||||
0x33, (byte)0xC3, (byte)0xBF, (byte)0xFF, (byte)0x9C, 0x0C, 0x6C, 0x48, 0x62, (byte)0x8C, 0x40, (byte)0xCC,
|
||||
0x04, (byte)0xE3, 0x08, 0x30, 0x30, (byte)0xB0, 0x40, (byte)0xC5, (byte)0xFE, (byte)0xFD, (byte)0xFF, (byte)0xFF,
|
||||
0x1F, 0x24, (byte)0xC4, (byte)0x0C, (byte)0xC4, (byte)0xFF, 0x47, (byte)0xC1, (byte)0x90, 0x02, 0x41, 0x0C,
|
||||
(byte)0xF9, 0x40, 0x58, (byte)0xC2, (byte)0xA0, (byte)0xC0, (byte)0xE0, (byte)0xCA, (byte)0x90, 0x07, (byte)0xA4,
|
||||
(byte)0x8B, 0x18, 0x2A, (byte)0xD1, (byte)0x93, 0x02, 0x5E, 0x20, (byte)0xC6, (byte)0xC0, 0x0A, (byte)0x8F,
|
||||
0x73, 0x50, 0x5A, (byte)0xC8, (byte)0xBB, 0x5D, 0x73, 0x29, 0x77, (byte)0xDD, 0x79, (byte)0xC1, 0x69, 0x3B,
|
||||
0x5C, 0x5C, (byte)0x83, 0x43, 0x50, (byte)0xD5, 0x06, (byte)0xBC, 0x48, 0x2F, 0x2B, 0x66, 0x38, (byte)0xC9,
|
||||
(byte)0xC8, 0x0E, 0x64, 0x3B, 0x30, 0x42, (byte)0xC4, (byte)0x9C, (byte)0x81, (byte)0xB6, (byte)0x83, (byte)0xEC,
|
||||
0x4D, 0x05, (byte)0x93, (byte)0xC5, 0x24, (byte)0xD9, 0x0D, 0x02, 0x42, 0x0C, 0x4C, (byte)0x8C, (byte)0xC8,
|
||||
(byte)0xFE, 0x21, 0x56, (byte)0x9F, 0x02, 0x23, (byte)0xC9, 0x56, (byte)0xE1, 0x04, (byte)0xE4, (byte)0xD8,
|
||||
0x4F, 0x4D, 0x40, (byte)0x89, (byte)0xFD, (byte)0xA0, (byte)0xBC, (byte)0xFB, 0x17, 0x4B, (byte)0xBA, (byte)0xF8,
|
||||
0x07, (byte)0xC5, (byte)0xA3, 0x60, 0x78, 0x03, 0x7A, (byte)0xE6, (byte)0xFF, 0x09, 0x67, 0x59, 0x1B, 0x41,
|
||||
(byte)0xF9, (byte)0x9F, (byte)0x95, 0x61, 0x34, (byte)0xFF, 0x53, 0x13, 0x50, 0x62, 0x3F, 0x4C, 0x1F, (byte)0xAC,
|
||||
0x1C, 0x18, (byte)0xCD, (byte)0xF7, 0x23, 0x0B, (byte)0xC0, (byte)0xDA, 0x74, (byte)0xA0, (byte)0xB6, 0x1B,
|
||||
(byte)0xA8, 0x3D, 0x37, 0x1A, (byte)0xF7, 0x23, 0x0B, (byte)0xA4, (byte)0x87, (byte)0xA6, (byte)0x85, 0x0A,
|
||||
0x00, 0x1B, 0x64, 0x6F, 0x38, 0x21, (byte)0x98, 0x03, (byte)0xDA, (byte)0xC2, (byte)0xE7, 0x60, (byte)0x90,
|
||||
0x01, (byte)0x92, 0x69, 0x0C, 0x39, 0x0C, 0x65, 0x0C, 0x05, 0x40, 0x32, 0x11, 0x58, 0x2F, (byte)0xA4, 0x02,
|
||||
0x6B, 0x07, 0x3D, 0x60, 0x19, 0x5D, 0x0E, 0x14, 0x27, 0x4E, 0x05, 0x1F, (byte)0x90, 0x0C, 0x67, (byte)0xC8,
|
||||
0x04, (byte)0x96, (byte)0xED, 0x29, (byte)0xC0, 0x72, (byte)0xBE, 0x1C, (byte)0xC8, (byte)0xE3, 0x06, (byte)0xE3,
|
||||
(byte)0xFF, (byte)0xFF, 0x39, 0x18, (byte)0xB8, (byte)0x80, 0x2C, 0x0F, (byte)0xA0, 0x5C, 0x3A, 0x43, 0x06, 0x58,
|
||||
0x2D, (byte)0xA8, (byte)0xA7, (byte)0xE1, (byte)0xC3, 0x10, 0x02, (byte)0x97, (byte)0x87, (byte)0xB8, 0x02,
|
||||
(byte)0xE6, 0x1A, 0x60, 0x77, (byte)0x83, 0x21, 0x18, (byte)0xA8, 0x12, 0x64, (byte)0x8A, 0x23, (byte)0xD0,
|
||||
(byte)0xB6, 0x1C, (byte)0xB8, 0x59, (byte)0xC8, (byte)0xAA, (byte)0x90, (byte)0xF5, (byte)0xF0, 0x62, (byte)0x94,
|
||||
0x75, (byte)0xDC, (byte)0xC0, (byte)0xDE, 0x0A, 0x37, 0x5C, 0x1D, 0x33, 0x54, 0x35, (byte)0x88, (byte)0x97, 0x08,
|
||||
0x35, (byte)0x91, (byte)0x83, (byte)0x81, 0x07, (byte)0xEC, 0x27, 0x10, (byte)0xBF, 0x18, (byte)0xE8, (byte)0x9B,
|
||||
(byte)0xE1, 0x0F, 0x00, (byte)0xBD, 0x65, 0x3D, (byte)0xD4
|
||||
};
|
||||
|
||||
public void testRead() throws Exception {
|
||||
ExOleObjStg record = new ExOleObjStg(data, 0, data.length);
|
||||
assertEquals(RecordTypes.ExOleObjStg.typeID, record.getRecordType());
|
||||
|
||||
int len = record.getDataLength();
|
||||
byte[] oledata = readAll(record.getData());
|
||||
assertEquals(len, oledata.length);
|
||||
|
||||
POIFSFileSystem fs = new POIFSFileSystem(record.getData());
|
||||
assertTrue("Constructed POIFS from ExOleObjStg data", true);
|
||||
DocumentEntry doc = (DocumentEntry)fs.getRoot().getEntry("Contents");
|
||||
assertNotNull(doc);
|
||||
assertTrue("Fetched the Contents stream containing OLE properties", true);
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
ExOleObjStg record = new ExOleObjStg(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
ExOleObjStg src = new ExOleObjStg(data, 0, data.length);
|
||||
byte[] oledata = readAll(src.getData());
|
||||
|
||||
ExOleObjStg tgt = new ExOleObjStg();
|
||||
tgt.setData(oledata);
|
||||
|
||||
|
||||
assertEquals(src.getDataLength(), tgt.getDataLength());
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
tgt.writeOut(out);
|
||||
byte[] b = out.toByteArray();
|
||||
|
||||
assertEquals(data.length, b.length);
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
private byte[] readAll(InputStream is) throws IOException {
|
||||
int pos;
|
||||
byte[] chunk = new byte[1024];
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
while((pos = is.read(chunk)) > 0){
|
||||
out.write(chunk, 0, pos);
|
||||
}
|
||||
return out.toByteArray();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
/* ====================================================================
|
||||
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.hslf.record;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests that {@link HeadersFootersAtom} works properly
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestExVideoContainer extends TestCase {
|
||||
|
||||
// From a real file
|
||||
private byte[] data = new byte[]{
|
||||
0x0F, 0x00, 0x05, 0x10, (byte) 0x9E, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x04, 0x10, 0x08, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, (byte)0xBA, 0x0F, (byte)0x86, 0x00, 0x00, 0x00,
|
||||
0x44, 0x00, 0x3A, 0x00, 0x5C, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6A, 0x00, 0x65, 0x00,
|
||||
0x63, 0x00, 0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x75, 0x00,
|
||||
0x6C, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x47, 0x00, 0x5C, 0x00, 0x6D, 0x00, 0x63, 0x00,
|
||||
0x6F, 0x00, 0x6D, 0x00, 0x5F, 0x00, 0x76, 0x00, 0x5F, 0x00, 0x31, 0x00, 0x5F, 0x00, 0x30, 0x00,
|
||||
0x5F, 0x00, 0x34, 0x00, 0x5C, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x5C, 0x00,
|
||||
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61, 0x00, 0x5C, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
|
||||
0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00,
|
||||
0x73, 0x00, 0x5C, 0x00, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x2E, 0x00,
|
||||
0x6D, 0x00, 0x70, 0x00, 0x67, 0x00};
|
||||
|
||||
|
||||
|
||||
|
||||
public void testRead() throws Exception {
|
||||
ExVideoContainer record = new ExVideoContainer(data, 0, data.length);
|
||||
assertEquals(RecordTypes.ExVideoContainer.typeID, record.getRecordType());
|
||||
|
||||
ExMediaAtom exMedia = record.getExMediaAtom();
|
||||
assertEquals(1, exMedia.getObjectId());
|
||||
assertNotNull(exMedia);
|
||||
assertFalse(exMedia.getFlag(ExMediaAtom.fLoop));
|
||||
assertFalse(exMedia.getFlag(ExMediaAtom.fNarration));
|
||||
assertFalse(exMedia.getFlag(ExMediaAtom.fRewind));
|
||||
|
||||
CString path = record.getPathAtom();
|
||||
assertNotNull(exMedia);
|
||||
assertEquals("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg", path.getText());
|
||||
}
|
||||
|
||||
public void testWrite() throws Exception {
|
||||
ExVideoContainer record = new ExVideoContainer(data, 0, data.length);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
|
||||
public void testNewRecord() throws Exception {
|
||||
ExVideoContainer record = new ExVideoContainer();
|
||||
record.getExMediaAtom().setObjectId(1);
|
||||
record.getPathAtom().setText("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg");
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
record.writeOut(baos);
|
||||
byte[] b = baos.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(data, b));
|
||||
}
|
||||
}
|
|
@ -48,6 +48,10 @@ public class TestTextRulerAtom extends TestCase {
|
|||
0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05
|
||||
};
|
||||
|
||||
private byte[] data_2 = new byte[] {
|
||||
0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,
|
||||
0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01
|
||||
};
|
||||
|
||||
public void testReadRuler() throws Exception {
|
||||
TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);
|
||||
|
@ -73,4 +77,16 @@ public class TestTextRulerAtom extends TestCase {
|
|||
byte[] result = out.toByteArray();
|
||||
assertTrue(Arrays.equals(result, data_1));
|
||||
}
|
||||
|
||||
public void testRead2() throws Exception {
|
||||
TextRulerAtom ruler = TextRulerAtom.getParagraphInstance();
|
||||
ruler.setParagraphIndent((short)249, (short)321);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ruler.writeOut(out);
|
||||
|
||||
byte[] result = out.toByteArray();
|
||||
assertTrue(Arrays.equals(result, data_2));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -29,6 +29,7 @@ import org.apache.poi.hssf.model.FormulaParser;
|
|||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.hssf.util.SheetReferences;
|
||||
|
@ -56,13 +57,13 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testBasics() throws Exception {
|
||||
public void testBasics() {
|
||||
assertNotNull(listener.getSSTRecord());
|
||||
assertNotNull(listener.getBoundSheetRecords());
|
||||
assertNotNull(listener.getExternSheetRecords());
|
||||
}
|
||||
|
||||
public void testGetStubWorkbooks() throws Exception {
|
||||
public void testGetStubWorkbooks() {
|
||||
assertNotNull(listener.getStubWorkbook());
|
||||
assertNotNull(listener.getStubHSSFWorkbook());
|
||||
|
||||
|
@ -70,7 +71,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
|||
assertNotNull(listener.getStubHSSFWorkbook().getSheetReferences());
|
||||
}
|
||||
|
||||
public void testContents() throws Exception {
|
||||
public void testContents() {
|
||||
assertEquals(2, listener.getSSTRecord().getNumStrings());
|
||||
assertEquals(3, listener.getBoundSheetRecords().length);
|
||||
assertEquals(1, listener.getExternSheetRecords().length);
|
||||
|
@ -83,11 +84,12 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
|||
assertEquals("S2", ref.getSheetName(2));
|
||||
}
|
||||
|
||||
public void testFormulas() throws Exception {
|
||||
FormulaRecord fr;
|
||||
public void testFormulas() {
|
||||
|
||||
FormulaRecord[] fRecs = mockListen.getFormulaRecords();
|
||||
|
||||
// Check our formula records
|
||||
assertEquals(6, mockListen._frecs.size());
|
||||
assertEquals(6, fRecs.length);
|
||||
|
||||
Workbook stubWB = listener.getStubWorkbook();
|
||||
assertNotNull(stubWB);
|
||||
|
@ -100,47 +102,45 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
|||
assertEquals("Sh3", stubWB.getSheetName(2));
|
||||
|
||||
// Check we can get the formula without breaking
|
||||
for(int i=0; i<mockListen._frecs.size(); i++) {
|
||||
fr = (FormulaRecord)mockListen._frecs.get(i);
|
||||
FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression());
|
||||
for(int i=0; i<fRecs.length; i++) {
|
||||
FormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
|
||||
}
|
||||
|
||||
// Peer into just one formula, and check that
|
||||
// all the ptgs give back the right things
|
||||
List ptgs = ((FormulaRecord)mockListen._frecs.get(0)).getParsedExpression();
|
||||
assertEquals(1, ptgs.size());
|
||||
assertTrue(ptgs.get(0) instanceof Ref3DPtg);
|
||||
Ptg[] ptgs = fRecs[0].getParsedExpression();
|
||||
assertEquals(1, ptgs.length);
|
||||
assertTrue(ptgs[0] instanceof Ref3DPtg);
|
||||
|
||||
Ref3DPtg ptg = (Ref3DPtg)ptgs.get(0);
|
||||
Ref3DPtg ptg = (Ref3DPtg)ptgs[0];
|
||||
assertEquals("Sheet1!A1", ptg.toFormulaString(stubHSSF));
|
||||
|
||||
|
||||
// Now check we get the right formula back for
|
||||
// a few sample ones
|
||||
FormulaRecord fr;
|
||||
|
||||
// Sheet 1 A2 is on same sheet
|
||||
fr = (FormulaRecord)mockListen._frecs.get(0);
|
||||
fr = fRecs[0];
|
||||
assertEquals(1, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
// Sheet 1 A5 is to another sheet
|
||||
fr = (FormulaRecord)mockListen._frecs.get(3);
|
||||
fr = fRecs[3];
|
||||
assertEquals(4, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
// Sheet 1 A7 is to another sheet, range
|
||||
fr = (FormulaRecord)mockListen._frecs.get(5);
|
||||
fr = fRecs[5];
|
||||
assertEquals(6, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
|
||||
// Now, load via Usermodel and re-check
|
||||
InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
|
||||
POIFSFileSystem fs = new POIFSFileSystem(is);
|
||||
HSSFWorkbook wb = new HSSFWorkbook(fs);
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("3dFormulas.xls");
|
||||
assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
|
||||
assertEquals("SUM(Sh3!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
|
||||
}
|
||||
|
@ -156,5 +156,10 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
|||
_frecs.add(record);
|
||||
}
|
||||
}
|
||||
public FormulaRecord[] getFormulaRecords() {
|
||||
FormulaRecord[] result = new FormulaRecord[_frecs.size()];
|
||||
_frecs.toArray(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,15 +17,14 @@
|
|||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
|
||||
/**
|
||||
|
@ -39,14 +38,12 @@ public final class TestFormulaRecord extends TestCase {
|
|||
public void testCreateFormulaRecord () {
|
||||
FormulaRecord record = new FormulaRecord();
|
||||
record.setColumn((short)0);
|
||||
//record.setRow((short)1);
|
||||
record.setRow(1);
|
||||
record.setXFIndex((short)4);
|
||||
|
||||
assertEquals(record.getColumn(),(short)0);
|
||||
//assertEquals(record.getRow(),(short)1);
|
||||
assertEquals((short)record.getRow(),(short)1);
|
||||
assertEquals(record.getXFIndex(),(short)4);
|
||||
assertEquals(record.getColumn(),0);
|
||||
assertEquals(record.getRow(), 1);
|
||||
assertEquals(record.getXFIndex(),4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +100,7 @@ public final class TestFormulaRecord extends TestCase {
|
|||
assertEquals("Offset 22", 1, output[26]);
|
||||
}
|
||||
|
||||
public void testWithConcat() throws Exception {
|
||||
public void testWithConcat() {
|
||||
// =CHOOSE(2,A2,A3,A4)
|
||||
byte[] data = {
|
||||
6, 0, 68, 0,
|
||||
|
@ -126,23 +123,19 @@ public final class TestFormulaRecord extends TestCase {
|
|||
|
||||
FormulaRecord fr = new FormulaRecord(inp);
|
||||
|
||||
List ptgs = fr.getParsedExpression();
|
||||
assertEquals(9, ptgs.size());
|
||||
assertEquals(IntPtg.class, ptgs.get(0).getClass());
|
||||
assertEquals(AttrPtg.class, ptgs.get(1).getClass());
|
||||
assertEquals(RefPtg.class, ptgs.get(2).getClass());
|
||||
assertEquals(AttrPtg.class, ptgs.get(3).getClass());
|
||||
assertEquals(RefPtg.class, ptgs.get(4).getClass());
|
||||
assertEquals(AttrPtg.class, ptgs.get(5).getClass());
|
||||
assertEquals(RefPtg.class, ptgs.get(6).getClass());
|
||||
assertEquals(AttrPtg.class, ptgs.get(7).getClass());
|
||||
assertEquals(FuncVarPtg.class, ptgs.get(8).getClass());
|
||||
Ptg[] ptgs = fr.getParsedExpression();
|
||||
assertEquals(9, ptgs.length);
|
||||
assertEquals(IntPtg.class, ptgs[0].getClass());
|
||||
assertEquals(AttrPtg.class, ptgs[1].getClass());
|
||||
assertEquals(RefPtg.class, ptgs[2].getClass());
|
||||
assertEquals(AttrPtg.class, ptgs[3].getClass());
|
||||
assertEquals(RefPtg.class, ptgs[4].getClass());
|
||||
assertEquals(AttrPtg.class, ptgs[5].getClass());
|
||||
assertEquals(RefPtg.class, ptgs[6].getClass());
|
||||
assertEquals(AttrPtg.class, ptgs[7].getClass());
|
||||
assertEquals(FuncVarPtg.class, ptgs[8].getClass());
|
||||
|
||||
FuncVarPtg choose = (FuncVarPtg)ptgs.get(8);
|
||||
FuncVarPtg choose = (FuncVarPtg)ptgs[8];
|
||||
assertEquals("CHOOSE", choose.getName());
|
||||
}
|
||||
|
||||
public static void main(String [] ignored_args) {
|
||||
junit.textui.TestRunner.run(TestFormulaRecord.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ public final class TestFormulaRecordAggregate extends junit.framework.TestCase {
|
|||
FormulaRecord f = new FormulaRecord();
|
||||
StringRecord s = new StringRecord();
|
||||
s.setString("abc");
|
||||
FormulaRecordAggregate fagg = new FormulaRecordAggregate(f,s);
|
||||
FormulaRecordAggregate fagg = new FormulaRecordAggregate(f);
|
||||
fagg.setStringRecord(s);
|
||||
assertEquals("abc", fagg.getStringValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,31 +33,35 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
|||
* @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
|
||||
* Creates a dummy AreaEval
|
||||
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
|
||||
*/
|
||||
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;
|
||||
public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
|
||||
AreaPtg areaPtg = new AreaPtg(areaRefStr);
|
||||
int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1;
|
||||
int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1;
|
||||
int nExpected = nRows * nCols;
|
||||
if (values.length != nExpected) {
|
||||
throw new RuntimeException("Expected " + nExpected + " values but got " + values.length);
|
||||
}
|
||||
|
||||
return new Area2DEval(new AreaPtg(areaRefStr), values);
|
||||
for (int i = 0; i < nExpected; i++) {
|
||||
if (values[i] == null) {
|
||||
values[i] = NumberEval.ZERO;
|
||||
}
|
||||
}
|
||||
return new Area2DEval(areaPtg, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single RefEval (with value zero)
|
||||
*/
|
||||
public static RefEval createRefEval(String refStr) {
|
||||
return new Ref2DEval(new RefPtg(refStr), ZERO);
|
||||
return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
|
@ -33,7 +32,7 @@ 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;
|
||||
import org.apache.poi.hssf.record.formula.functions.Countif.I_MatchPredicate;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
|
@ -43,53 +42,49 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
|||
|
||||
/**
|
||||
* 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),
|
||||
new NumberEval(0),
|
||||
};
|
||||
confirmCountA(1, args);
|
||||
|
||||
|
||||
args = new Eval[] {
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new StringEval(""),
|
||||
};
|
||||
confirmCountA(3, args);
|
||||
|
||||
|
||||
args = new Eval[] {
|
||||
EvalFactory.createAreaEval("D2:F5", 3, 4),
|
||||
EvalFactory.createAreaEval("D2:F5", new ValueEval[12]),
|
||||
};
|
||||
confirmCountA(12, args);
|
||||
|
||||
|
||||
args = new Eval[] {
|
||||
EvalFactory.createAreaEval("D1:F5", 3, 5), // 15
|
||||
EvalFactory.createRefEval("A1"),
|
||||
EvalFactory.createAreaEval("A1:G6", 7, 6), // 42
|
||||
EvalFactory.createAreaEval("D1:F5", new ValueEval[15]),
|
||||
EvalFactory.createRefEval("A1"),
|
||||
EvalFactory.createAreaEval("A1:G6", new ValueEval[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 NumberEval(0),
|
||||
new StringEval("TRUE"), // note - does not match boolean TRUE
|
||||
BoolEval.TRUE,
|
||||
BoolEval.FALSE,
|
||||
|
@ -98,22 +93,22 @@ public final class TestCountFuncs extends TestCase {
|
|||
};
|
||||
range = createAreaEval("A1:B3", 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),
|
||||
new NumberEval(0),
|
||||
new StringEval("2"),
|
||||
new StringEval("2.001"),
|
||||
new NumberEval(2),
|
||||
new NumberEval(2),
|
||||
BoolEval.TRUE,
|
||||
};
|
||||
range = createAreaEval("A1:B3", 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:
|
||||
|
||||
if (false) { // not supported yet:
|
||||
// when criteria is an expression (starting with a comparison operator)
|
||||
confirmCountIf(4, range, new StringEval(">1"));
|
||||
}
|
||||
|
@ -123,7 +118,7 @@ public final class TestCountFuncs extends TestCase {
|
|||
*/
|
||||
public void testCountIfWithCriteriaReference() {
|
||||
|
||||
ValueEval[] values = {
|
||||
ValueEval[] values = {
|
||||
new NumberEval(22),
|
||||
new NumberEval(25),
|
||||
new NumberEval(21),
|
||||
|
@ -132,14 +127,14 @@ public final class TestCountFuncs extends TestCase {
|
|||
new NumberEval(25),
|
||||
};
|
||||
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
|
||||
|
||||
|
||||
Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25));
|
||||
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);
|
||||
|
@ -150,15 +145,15 @@ public final class TestCountFuncs extends TestCase {
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
public void testCountIfEmptyStringCriteria() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
// pred '=' matches blank cell but not empty string
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("="));
|
||||
confirmPredicate(false, mp, "");
|
||||
|
@ -168,21 +163,21 @@ public final class TestCountFuncs extends TestCase {
|
|||
mp = Countif.createCriteriaPredicate(new StringEval(""));
|
||||
confirmPredicate(true, mp, "");
|
||||
confirmPredicate(true, mp, null);
|
||||
|
||||
// pred '<>' matches empty string but not blank cell
|
||||
|
||||
// pred '<>' matches empty string but not blank cell
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("<>"));
|
||||
confirmPredicate(false, mp, null);
|
||||
confirmPredicate(true, mp, "");
|
||||
}
|
||||
|
||||
|
||||
public void testCountifComparisons() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(">5"));
|
||||
confirmPredicate(false, mp, 4);
|
||||
confirmPredicate(false, mp, 5);
|
||||
confirmPredicate(true, mp, 6);
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("<=5"));
|
||||
confirmPredicate(true, mp, 4);
|
||||
confirmPredicate(true, mp, 5);
|
||||
|
@ -194,7 +189,7 @@ public final class TestCountFuncs extends TestCase {
|
|||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("=abc"));
|
||||
confirmPredicate(true, mp, "abc");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("=42"));
|
||||
confirmPredicate(false, mp, 41);
|
||||
confirmPredicate(true, mp, 42);
|
||||
|
@ -211,28 +206,28 @@ public final class TestCountFuncs extends TestCase {
|
|||
confirmPredicate(true, mp, "500");
|
||||
confirmPredicate(true, mp, "4t4");
|
||||
}
|
||||
|
||||
|
||||
public void testWildCards() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a*b"));
|
||||
confirmPredicate(false, mp, "abc");
|
||||
confirmPredicate(true, mp, "ab");
|
||||
confirmPredicate(true, mp, "axxb");
|
||||
confirmPredicate(false, mp, "xab");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a?b"));
|
||||
confirmPredicate(false, mp, "abc");
|
||||
confirmPredicate(false, mp, "ab");
|
||||
confirmPredicate(false, mp, "axxb");
|
||||
confirmPredicate(false, mp, "xab");
|
||||
confirmPredicate(true, mp, "axb");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a~?"));
|
||||
confirmPredicate(false, mp, "a~a");
|
||||
confirmPredicate(false, mp, "a~?");
|
||||
confirmPredicate(true, mp, "a?");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("~*a"));
|
||||
confirmPredicate(false, mp, "~aa");
|
||||
confirmPredicate(false, mp, "~*a");
|
||||
|
@ -245,40 +240,40 @@ public final class TestCountFuncs extends TestCase {
|
|||
}
|
||||
public void testNotQuiteWildCards() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
// make sure special reg-ex chars are treated like normal chars
|
||||
|
||||
// make sure special reg-ex chars are treated like normal chars
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a.b"));
|
||||
confirmPredicate(false, mp, "aab");
|
||||
confirmPredicate(true, mp, "a.b");
|
||||
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a~b"));
|
||||
confirmPredicate(false, mp, "ab");
|
||||
confirmPredicate(false, mp, "axb");
|
||||
confirmPredicate(false, mp, "a~~b");
|
||||
confirmPredicate(true, mp, "a~b");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(">a*b"));
|
||||
confirmPredicate(false, mp, "a(b");
|
||||
confirmPredicate(true, mp, "aab");
|
||||
confirmPredicate(false, mp, "a*a");
|
||||
confirmPredicate(true, mp, "a*c");
|
||||
}
|
||||
|
||||
|
||||
private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) {
|
||||
assertEquals(expectedResult, matchPredicate.matches(new NumberEval(value)));
|
||||
}
|
||||
private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, String value) {
|
||||
Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value);
|
||||
Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value);
|
||||
assertEquals(expectedResult, matchPredicate.matches(ev));
|
||||
}
|
||||
|
||||
|
||||
public void testCountifFromSpreadsheet() {
|
||||
final String FILE_NAME = "countifExamples.xls";
|
||||
final int START_ROW_IX = 1;
|
||||
final int COL_IX_ACTUAL = 2;
|
||||
final int COL_IX_EXPECTED = 3;
|
||||
|
||||
|
||||
int failureCount = 0;
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME);
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
|
@ -299,7 +294,7 @@ public final class TestCountFuncs extends TestCase {
|
|||
failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (failureCount > 0) {
|
||||
throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details");
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
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;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Tests for ROW(), ROWS(), COLUMN(), COLUMNS()
|
||||
|
@ -29,10 +29,6 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
|
|||
*/
|
||||
public final class TestRowCol extends TestCase {
|
||||
|
||||
public TestRowCol(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
public void testCol() {
|
||||
Function target = new Column();
|
||||
{
|
||||
|
@ -41,7 +37,7 @@ public final class TestRowCol extends TestCase {
|
|||
assertEquals(3, actual, 0D);
|
||||
}
|
||||
{
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(5, actual, 0D);
|
||||
}
|
||||
|
@ -55,7 +51,7 @@ public final class TestRowCol extends TestCase {
|
|||
assertEquals(5, actual, 0D);
|
||||
}
|
||||
{
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
|
||||
Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
|
||||
double actual = NumericFunctionInvoker.invoke(target, args);
|
||||
assertEquals(2, actual, 0D);
|
||||
}
|
||||
|
@ -86,7 +82,7 @@ public final class TestRowCol extends TestCase {
|
|||
}
|
||||
|
||||
private static void confirmRowsFunc(String areaRefStr, int nCols, int nRows) {
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Rows(), args);
|
||||
assertEquals(nRows, actual, 0D);
|
||||
|
@ -94,7 +90,7 @@ public final class TestRowCol extends TestCase {
|
|||
|
||||
|
||||
private static void confirmColumnsFunc(String areaRefStr, int nCols, int nRows) {
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
|
||||
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Columns(), args);
|
||||
assertEquals(nCols, actual, 0D);
|
||||
|
|
|
@ -49,7 +49,7 @@ public final class TestSumproduct extends TestCase {
|
|||
}
|
||||
|
||||
public void testScalarSimple() {
|
||||
|
||||
|
||||
RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3));
|
||||
Eval[] args = {
|
||||
refEval,
|
||||
|
@ -59,19 +59,19 @@ public final class TestSumproduct extends TestCase {
|
|||
confirmDouble(6D, result);
|
||||
}
|
||||
|
||||
|
||||
public void testAreaSimple() {
|
||||
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", 1, 3);
|
||||
ValueEval[] aValues = aeA.getValues();
|
||||
ValueEval[] bValues = aeB.getValues();
|
||||
aValues[0] = new NumberEval(2);
|
||||
aValues[1] = new NumberEval(4);
|
||||
aValues[2] = new NumberEval(5);
|
||||
bValues[0] = new NumberEval(3);
|
||||
bValues[1] = new NumberEval(6);
|
||||
bValues[2] = new NumberEval(7);
|
||||
ValueEval[] aValues = {
|
||||
new NumberEval(2),
|
||||
new NumberEval(4),
|
||||
new NumberEval(5),
|
||||
};
|
||||
ValueEval[] bValues = {
|
||||
new NumberEval(3),
|
||||
new NumberEval(6),
|
||||
new NumberEval(7),
|
||||
};
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", aValues);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", bValues);
|
||||
|
||||
Eval[] args = { aeA, aeB, };
|
||||
Eval result = invokeSumproduct(args);
|
||||
|
@ -82,10 +82,9 @@ public final class TestSumproduct extends TestCase {
|
|||
* For scalar products, the terms may be 1x1 area refs
|
||||
*/
|
||||
public void testOneByOneArea() {
|
||||
|
||||
AreaEval ae = EvalFactory.createAreaEval("A1:A1", 1, 1);
|
||||
ae.getValues()[0] = new NumberEval(7);
|
||||
|
||||
|
||||
AreaEval ae = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(7), });
|
||||
|
||||
Eval[] args = {
|
||||
ae,
|
||||
new NumberEval(2),
|
||||
|
@ -94,27 +93,29 @@ public final class TestSumproduct extends TestCase {
|
|||
confirmDouble(14D, result);
|
||||
}
|
||||
|
||||
|
||||
public void testMismatchAreaDimensions() {
|
||||
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:D1", 3, 1);
|
||||
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", new ValueEval[3]);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:D1", new ValueEval[3]);
|
||||
|
||||
Eval[] args;
|
||||
args = new Eval[] { aeA, aeB, };
|
||||
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
|
||||
|
||||
|
||||
args = new Eval[] { aeA, new NumberEval(5), };
|
||||
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
|
||||
}
|
||||
|
||||
public void testAreaWithErrorCell() {
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", 1, 2);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", 1, 2);
|
||||
ValueEval[] aValues = {
|
||||
ErrorEval.REF_INVALID,
|
||||
null,
|
||||
};
|
||||
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues);
|
||||
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]);
|
||||
aeB.getValues()[1] = ErrorEval.REF_INVALID;
|
||||
|
||||
|
||||
Eval[] args = { aeA, aeB, };
|
||||
assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
@ -40,10 +38,6 @@ public final class FormulaExtractor {
|
|||
throw new IllegalArgumentException("Not a formula cell");
|
||||
}
|
||||
FormulaRecordAggregate fra = (FormulaRecordAggregate) vr;
|
||||
List tokens = fra.getFormulaRecord().getParsedExpression();
|
||||
Ptg[] result = new Ptg[tokens.size()];
|
||||
tokens.toArray(result);
|
||||
return result;
|
||||
return fra.getFormulaRecord().getParsedExpression();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
|
@ -65,15 +65,14 @@ public final class TestBug42464 extends TestCase {
|
|||
}
|
||||
FormulaRecordAggregate record = (FormulaRecordAggregate) cell.getCellValueRecord();
|
||||
FormulaRecord r = record.getFormulaRecord();
|
||||
List ptgs = r.getParsedExpression();
|
||||
Ptg[] ptgs = r.getParsedExpression();
|
||||
|
||||
String cellRef = new CellReference(row.getRowNum(), cell.getCellNum(), false, false).formatAsString();
|
||||
if(false && cellRef.equals("BP24")) { // TODO - replace System.out.println()s with asserts
|
||||
System.out.print(cellRef);
|
||||
System.out.println(" - has " + r.getNumberOfExpressionTokens()
|
||||
+ " ptgs over " + r.getExpressionLength() + " tokens:");
|
||||
for(int i=0; i<ptgs.size(); i++) {
|
||||
String c = ptgs.get(i).getClass().toString();
|
||||
System.out.println(" - has " + ptgs.length + " ptgs:");
|
||||
for(int i=0; i<ptgs.length; i++) {
|
||||
String c = ptgs[i].getClass().toString();
|
||||
System.out.println("\t" + c.substring(c.lastIndexOf('.')+1) );
|
||||
}
|
||||
System.out.println("-> " + cell.getCellFormula());
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.poi.hssf.usermodel;
|
|||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
@ -29,6 +28,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
|
|||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -183,14 +183,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
|
|||
HSSFCell cellSUM = rowSUM.getCell(0);
|
||||
|
||||
FormulaRecordAggregate frec = (FormulaRecordAggregate) cellSUM.getCellValueRecord();
|
||||
List ops = frec.getFormulaRecord().getParsedExpression();
|
||||
assertEquals(2, ops.size());
|
||||
assertEquals(AreaPtg.class, ops.get(0).getClass());
|
||||
assertEquals(FuncVarPtg.class, ops.get(1).getClass());
|
||||
Ptg[] ops = frec.getFormulaRecord().getParsedExpression();
|
||||
assertEquals(2, ops.length);
|
||||
assertEquals(AreaPtg.class, ops[0].getClass());
|
||||
assertEquals(FuncVarPtg.class, ops[1].getClass());
|
||||
|
||||
// Actually stored as C1 to C65536
|
||||
// (last row is -1 === 65535)
|
||||
AreaPtg ptg = (AreaPtg) ops.get(0);
|
||||
AreaPtg ptg = (AreaPtg) ops[0];
|
||||
assertEquals(2, ptg.getFirstColumn());
|
||||
assertEquals(2, ptg.getLastColumn());
|
||||
assertEquals(0, ptg.getFirstRow());
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -15,54 +14,26 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.util;
|
||||
|
||||
import junit.framework.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.*;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
* @author Marc Johnson (mjohnson at apache dot org)
|
||||
*/
|
||||
public final class TestHexDump extends TestCase {
|
||||
|
||||
public class TestHexDump
|
||||
extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates new TestHexDump
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
|
||||
public TestHexDump(String name)
|
||||
{
|
||||
super(name);
|
||||
private static char toHex(int n) {
|
||||
return Character.toUpperCase(Character.forDigit(n & 0x0F, 16));
|
||||
}
|
||||
|
||||
private char toHex(final int n)
|
||||
{
|
||||
char[] hexChars =
|
||||
{
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
|
||||
'D', 'E', 'F'
|
||||
};
|
||||
|
||||
return hexChars[ n % 16 ];
|
||||
}
|
||||
|
||||
/**
|
||||
* test dump method
|
||||
*
|
||||
* @exception IOException
|
||||
*/
|
||||
|
||||
public void testDump()
|
||||
throws IOException
|
||||
{
|
||||
public void testDump() throws IOException {
|
||||
byte[] testArray = new byte[ 256 ];
|
||||
|
||||
for (int j = 0; j < 256; j++)
|
||||
|
@ -245,8 +216,7 @@ public class TestHexDump
|
|||
// verify proper behavior with negative index
|
||||
try
|
||||
{
|
||||
HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(),
|
||||
-1);
|
||||
HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(), -1);
|
||||
fail("should have caught ArrayIndexOutOfBoundsException on negative index");
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException ignored_exception)
|
||||
|
@ -287,37 +257,33 @@ public class TestHexDump
|
|||
|
||||
}
|
||||
|
||||
public void testToHex()
|
||||
throws Exception
|
||||
{
|
||||
assertEquals( "000A", HexDump.toHex((short)0xA));
|
||||
assertEquals( "0A", HexDump.toHex((byte)0xA));
|
||||
assertEquals( "0000000A", HexDump.toHex(0xA));
|
||||
|
||||
assertEquals( "FFFF", HexDump.toHex((short)0xFFFF));
|
||||
public void testToHex() {
|
||||
assertEquals("000A", HexDump.toHex((short)0xA));
|
||||
assertEquals("0A", HexDump.toHex((byte)0xA));
|
||||
assertEquals("0000000A", HexDump.toHex(0xA));
|
||||
|
||||
assertEquals("FFFF", HexDump.toHex((short)0xFFFF));
|
||||
|
||||
confirmStr("0xFE", HexDump.byteToHex(-2));
|
||||
confirmStr("0x25", HexDump.byteToHex(37));
|
||||
confirmStr("0xFFFE", HexDump.shortToHex(-2));
|
||||
confirmStr("0x0005", HexDump.shortToHex(5));
|
||||
confirmStr("0xFFFFFF9C", HexDump.intToHex(-100));
|
||||
confirmStr("0x00001001", HexDump.intToHex(4097));
|
||||
confirmStr("0xFFFFFFFFFFFF0006", HexDump.longToHex(-65530));
|
||||
confirmStr("0x0000000000003FCD", HexDump.longToHex(16333));
|
||||
}
|
||||
|
||||
private char toAscii(final int c)
|
||||
{
|
||||
private static void confirmStr(String expected, char[] actualChars) {
|
||||
assertEquals(expected, new String(actualChars));
|
||||
}
|
||||
|
||||
private static char toAscii(int c) {
|
||||
char rval = '.';
|
||||
|
||||
if ((c >= 32) && (c <= 126))
|
||||
{
|
||||
if (c >= 32 && c <= 126) {
|
||||
rval = ( char ) c;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* main method to run the unit tests
|
||||
*
|
||||
* @param ignored_args
|
||||
*/
|
||||
|
||||
public static void main(String [] ignored_args)
|
||||
{
|
||||
System.out.println("Testing util.HexDump functionality");
|
||||
junit.textui.TestRunner.run(TestHexDump.class);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue