diff options
Diffstat (limited to 'src/java/org')
37 files changed, 1210 insertions, 1611 deletions
diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 6c6c53a92b..71f3bf6fb7 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -27,8 +27,8 @@ import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.hssf.util.RangeAddress; +import org.apache.poi.ss.util.AreaReference; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; @@ -626,7 +626,7 @@ public final class NameRecord extends Record { } private Ptg createNewPtg(){ - Ptg ptg = new Area3DPtg(); + Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised field_13_name_definition.push(ptg); return ptg; @@ -673,9 +673,7 @@ public final class NameRecord extends Record { // Add the area reference(s) for(int i=0; i<refs.length; i++) { - ptg = new Area3DPtg(); - ((Area3DPtg) ptg).setExternSheetIndex(externSheetIndex); - ((Area3DPtg) ptg).setArea(refs[i].formatAsString()); + ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex); field_13_name_definition.push(ptg); this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) ); } diff --git a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java new file mode 100644 index 0000000000..2193854f47 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java @@ -0,0 +1,59 @@ +/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Common superclass of 2-D area refs
+ */
+public abstract class Area2DPtgBase extends AreaPtgBase {
+ private final static int SIZE = 9;
+
+ protected Area2DPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
+ super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
+ }
+ protected Area2DPtgBase(RecordInputStream in) {
+ readCoordinates(in);
+ }
+ protected abstract byte getSid();
+
+ public final void writeBytes(byte [] array, int offset) {
+ LittleEndian.putByte(array, offset+0, getSid() + getPtgClass());
+ writeCoordinates(array, offset+1);
+ }
+ public Area2DPtgBase(String arearef) {
+ super(arearef);
+ }
+ public final int getSize() {
+ return SIZE;
+ }
+ public final String toFormulaString(HSSFWorkbook book) {
+ return formatReferenceAsString();
+ }
+ public final String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName());
+ sb.append(" [");
+ sb.append(formatReferenceAsString());
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java index 3c9404ac39..ba207ab5c6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java @@ -18,11 +18,9 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.util.CellReference; -import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.LittleEndian; /** @@ -34,238 +32,63 @@ import org.apache.poi.util.LittleEndian; * @author Jason Height (jheight at chariot dot net dot au) * @version 1.0-pre */ -public final class Area3DPtg extends OperandPtg implements AreaI { +public final class Area3DPtg extends AreaPtgBase { public final static byte sid = 0x3b; private final static int SIZE = 11; // 10 + 1 for Ptg - private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); - private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); - - private short field_1_index_extern_sheet; - private int field_2_first_row; - private int field_3_last_row; - private int field_4_first_column; - private int field_5_last_column; + private int field_1_index_extern_sheet; - /** Creates new AreaPtg */ - public Area3DPtg() - { - } - - public Area3DPtg( String arearef, short externIdx ) - { - setArea(arearef); + public Area3DPtg( String arearef, int externIdx ) { + super(arearef); setExternSheetIndex( externIdx ); - } - public Area3DPtg(RecordInputStream in) - { + public Area3DPtg(RecordInputStream in) { field_1_index_extern_sheet = in.readShort(); - field_2_first_row = in.readUShort(); - field_3_last_row = in.readUShort(); - field_4_first_column = in.readUShort(); - field_5_last_column = in.readUShort(); + readCoordinates(in); } - public Area3DPtg(short firstRow, short lastRow, short firstColumn, short lastColumn, + public Area3DPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative, - short externalSheetIndex) { - setFirstRow(firstRow); - setLastRow(lastRow); - setFirstColumn(firstColumn); - setLastColumn(lastColumn); - setFirstRowRelative(firstRowRelative); - setLastRowRelative(lastRowRelative); - setFirstColRelative(firstColRelative); - setLastColRelative(lastColRelative); - setExternSheetIndex(externalSheetIndex); + int externalSheetIndex) { + super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); + setExternSheetIndex(externalSheetIndex); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append(getClass().getName()); - sb.append(" ["); sb.append("sheetIx=").append(getExternSheetIndex()); sb.append(" ! "); - sb.append(AreaReference.formatAsString(this)); + sb.append(formatReferenceAsString()); sb.append("]"); return sb.toString(); } - public void writeBytes( byte[] array, int offset ) - { - array[0 + offset] = (byte) ( sid + getPtgClass() ); - LittleEndian.putShort( array, 1 + offset, getExternSheetIndex() ); - LittleEndian.putShort( array, 3 + offset, (short)getFirstRow() ); - LittleEndian.putShort( array, 5 + offset, (short)getLastRow() ); - LittleEndian.putShort( array, 7 + offset, (short)getFirstColumnRaw() ); - LittleEndian.putShort( array, 9 + offset, (short)getLastColumnRaw() ); + public void writeBytes(byte[] array, int offset) { + LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); + LittleEndian.putUShort(array, 1 + offset, field_1_index_extern_sheet); + writeCoordinates(array, offset+3); } - public int getSize() - { + public int getSize() { return SIZE; } - public short getExternSheetIndex() - { - return field_1_index_extern_sheet; + public short getExternSheetIndex() { + return (short)field_1_index_extern_sheet; } - public void setExternSheetIndex( short index ) - { + public void setExternSheetIndex(int index) { field_1_index_extern_sheet = index; } - public int getFirstRow() - { - return field_2_first_row; - } - - public void setFirstRow( int row ) - { - field_2_first_row = row; - } - - public int getLastRow() - { - return field_3_last_row; - } - - public void setLastRow( int row ) - { - field_3_last_row = row; - } - - public int getFirstColumn() - { - return field_4_first_column & 0xFF; - } - - public int getFirstColumnRaw() - { - return field_4_first_column; - } - - public boolean isFirstRowRelative() - { - return rowRelative.isSet( field_4_first_column ); - } - - public boolean isFirstColRelative() - { - return colRelative.isSet( field_4_first_column ); - } - - public void setFirstColumn( short column ) - { - field_4_first_column &= 0xFF00; - field_4_first_column |= column & 0xFF; - } - - public void setFirstColumnRaw( short column ) - { - field_4_first_column = column; - } - - public int getLastColumn() - { - return field_5_last_column & 0xFF; - } - - public int getLastColumnRaw() - { - return field_5_last_column; - } - - public boolean isLastRowRelative() - { - return rowRelative.isSet( field_5_last_column ); - } - - public boolean isLastColRelative() - { - return colRelative.isSet( field_5_last_column ); - } - - public void setLastColumn( short column ) - { - field_5_last_column &= 0xFF00; - field_5_last_column |= column & 0xFF; - } - - public void setLastColumnRaw( short column ) - { - field_5_last_column = column; - } - - /** - * sets the first row to relative or not - * @param rel FIXME: Document this! - */ - public void setFirstRowRelative( boolean rel ) - { - field_4_first_column = rowRelative.setBoolean( field_4_first_column, rel ); - } - - /** - * set whether the first column is relative - */ - public void setFirstColRelative( boolean rel ) - { - field_4_first_column = colRelative.setBoolean( field_4_first_column, rel ); - } - - /** - * set whether the last row is relative or not - * @param rel FIXME: Document this! - */ - public void setLastRowRelative( boolean rel ) - { - field_5_last_column = rowRelative.setBoolean( field_5_last_column, rel ); - } - - /** - * set whether the last column should be relative or not - */ - public void setLastColRelative( boolean rel ) - { - field_5_last_column = colRelative.setBoolean( field_5_last_column, rel ); - } - - - /*public String getArea(){ - RangeAddress ra = new RangeAddress( getFirstColumn(),getFirstRow() + 1, getLastColumn(), getLastRow() + 1); - String result = ra.getAddress(); - - return result; - }*/ - - public void setArea( String ref ) - { - AreaReference ar = new AreaReference( ref ); - - CellReference frstCell = ar.getFirstCell(); - CellReference lastCell = ar.getLastCell(); - - setFirstRow( (short) frstCell.getRow() ); - setFirstColumn( (short) frstCell.getCol() ); - setLastRow( (short) lastCell.getRow() ); - setLastColumn( (short) lastCell.getCol() ); - setFirstColRelative( !frstCell.isColAbsolute() ); - setLastColRelative( !lastCell.isColAbsolute() ); - setFirstRowRelative( !frstCell.isRowAbsolute() ); - setLastRowRelative( !lastCell.isRowAbsolute() ); - } - /** * @return text representation of this area reference that can be used in text * formulas. The sheet name will get properly delimited if required. */ - public String toFormulaString(Workbook book) - { + public String toFormulaString(Workbook book) { // First do the sheet name StringBuffer retval = new StringBuffer(); String sheetName = Ref3DPtg.getSheetName(book, field_1_index_extern_sheet); @@ -282,29 +105,8 @@ public final class Area3DPtg extends OperandPtg implements AreaI { } // Now the normal area bit - retval.append(AreaReference.formatAsString(this)); + retval.append(formatReferenceAsString()); - // All done return retval.toString(); } - - public byte getDefaultOperandClass() { - return Ptg.CLASS_REF; - } - // TODO - one junit relies on this. remove - public boolean equals( Object o ) - { - if ( this == o ) return true; - if ( !( o instanceof Area3DPtg ) ) return false; - - final Area3DPtg area3DPtg = (Area3DPtg) o; - - if ( field_1_index_extern_sheet != area3DPtg.field_1_index_extern_sheet ) return false; - if ( field_2_first_row != area3DPtg.field_2_first_row ) return false; - if ( field_3_last_row != area3DPtg.field_3_last_row ) return false; - if ( field_4_first_column != area3DPtg.field_4_first_column ) return false; - if ( field_5_last_column != area3DPtg.field_5_last_column ) return false; - - return true; - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaI.java b/src/java/org/apache/poi/hssf/record/formula/AreaI.java index 5a0a21e861..477d32f677 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaI.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaI.java @@ -14,47 +14,63 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.hssf.record.formula; /** - * Common interface for AreaPtg and Area3DPtg, and their - * child classes. + * Common interface for AreaPtg and Area3DPtg, and their child classes. */ public interface AreaI { - /** - * @return the first row in the area - */ - public int getFirstRow(); - - /** - * @return last row in the range (x2 in x1,y1-x2,y2) - */ - public int getLastRow(); - - /** - * @return the first column number in the area. - */ - public int getFirstColumn(); - - /** - * @return lastcolumn in the area - */ - public int getLastColumn(); - - /** - * @return isrelative first column to relative or not - */ - public boolean isFirstColRelative(); - /** - * @return lastcol relative or not - */ - public boolean isLastColRelative(); - /** - * @return whether or not the first row is a relative reference or not. - */ - public boolean isFirstRowRelative(); - /** - * @return last row relative or not - */ - public boolean isLastRowRelative(); + /** + * @return the first row in the area + */ + public int getFirstRow(); + + /** + * @return last row in the range (x2 in x1,y1-x2,y2) + */ + public int getLastRow(); + + /** + * @return the first column number in the area. + */ + public int getFirstColumn(); + + /** + * @return lastcolumn in the area + */ + public int getLastColumn(); + + class OffsetArea implements AreaI { + + private final int _firstColumn; + private final int _firstRow; + private final int _lastColumn; + private final int _lastRow; + + public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx, + int relFirstColIx, int relLastColIx) { + _firstRow = baseRow + relFirstRowIx; + _lastRow = baseRow + relLastRowIx; + _firstColumn = baseColumn + relFirstColIx; + _lastColumn = baseColumn + relLastColIx; + } + + public int getFirstColumn() { + return _firstColumn; + } + + public int getFirstRow() { + return _firstRow; + } + + public int getLastColumn() { + return _lastColumn; + } + + public int getLastRow() { + return _lastRow; + } + } + }
\ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java index bf013838b2..fc391b801d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java @@ -23,7 +23,7 @@ import org.apache.poi.hssf.record.RecordInputStream; * Specifies a rectangular area of cells A1:A4 for instance. * @author Jason Height (jheight at chariot dot net dot au) */ -public final class AreaNPtg extends AreaPtgBase { +public final class AreaNPtg extends Area2DPtgBase { public final static short sid = 0x2D; public AreaNPtg(RecordInputStream in) { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java index 0839e5570a..77e9fa0b5e 100755 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java @@ -23,16 +23,16 @@ import org.apache.poi.hssf.record.RecordInputStream; * Specifies a rectangular area of cells A1:A4 for instance. * @author Jason Height (jheight at chariot dot net dot au) */ -public final class AreaPtg extends AreaPtgBase { - public final static short sid = 0x25; +public final class AreaPtg extends Area2DPtgBase { + public final static short sid = 0x25; - public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { - super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); - } - public AreaPtg(RecordInputStream in) { - super(in); - } - public AreaPtg(String arearef) { + public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { + super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); + } + public AreaPtg(RecordInputStream in) { + super(in); + } + public AreaPtg(String arearef) { super(arearef); } protected byte getSid() { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java index 9ac406b319..cf3d82cacf 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java @@ -23,7 +23,7 @@ import org.apache.poi.util.BitFieldFactory; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.hssf.util.AreaReference; +import org.apache.poi.ss.util.AreaReference; import org.apache.poi.hssf.record.RecordInputStream; /** @@ -37,11 +37,9 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { * see similar comment in ReferencePtg */ protected final RuntimeException notImplemented() { - return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); } - public final static short sid = 0x25; - private final static int SIZE = 9; /** zero based, unsigned 16 bit */ private int field_1_first_row; /** zero based, unsigned 16 bit */ @@ -55,6 +53,10 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { private final static BitField colRelative = BitFieldFactory.getInstance(0x4000); private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF); + protected AreaPtgBase() { + // do nothing + } + protected AreaPtgBase(String arearef) { AreaReference ar = new AreaReference(arearef); CellReference firstCell = ar.getFirstCell(); @@ -97,35 +99,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { } } - protected AreaPtgBase(RecordInputStream in) - { - field_1_first_row = in.readUShort(); - field_2_last_row = in.readUShort(); + protected final void readCoordinates(RecordInputStream in) { + field_1_first_row = in.readUShort(); + field_2_last_row = in.readUShort(); field_3_first_column = in.readUShort(); - field_4_last_column = in.readUShort(); - } - - public final String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(getClass().getName()); - sb.append(" ["); - sb.append(AreaReference.formatAsString(this)); - sb.append("]"); - return sb.toString(); + field_4_last_column = in.readUShort(); } - protected abstract byte getSid(); - - public final void writeBytes(byte [] array, int offset) { - array[offset] = (byte) (getSid() + getPtgClass()); - LittleEndian.putShort(array,offset+1,(short)field_1_first_row); - LittleEndian.putShort(array,offset+3,(short)field_2_last_row); - LittleEndian.putShort(array,offset+5,(short)field_3_first_column); - LittleEndian.putShort(array,offset+7,(short)field_4_last_column); - } - - - public final int getSize() { - return SIZE; + protected final void writeCoordinates(byte[] array, int offset) { + LittleEndian.putUShort(array, offset + 0, field_1_first_row); + LittleEndian.putUShort(array, offset + 2, field_2_last_row); + LittleEndian.putUShort(array, offset + 4, field_3_first_column); + LittleEndian.putUShort(array, offset + 6, field_4_last_column); } /** @@ -207,7 +191,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { */ public final void setFirstColumn(int colIx) { checkColumnBounds(colIx); - field_3_first_column=columnMask.setValue(field_3_first_column, colIx); + field_3_first_column=columnMask.setValue(field_3_first_column, colIx); } /** @@ -266,7 +250,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { */ public final void setLastColumn(int colIx) { checkColumnBounds(colIx); - field_4_last_column=columnMask.setValue(field_4_last_column, colIx); + field_4_last_column=columnMask.setValue(field_4_last_column, colIx); } /** @@ -275,9 +259,18 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { public final void setLastColumnRaw(short column) { field_4_last_column = column; } + protected final String formatReferenceAsString() { + CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative()); + CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative()); + + if(AreaReference.isWholeColumnReference(topLeft, botRight)) { + return (new AreaReference(topLeft, botRight)).formatAsString(); + } + return topLeft.formatAsString() + ":" + botRight.formatAsString(); + } public String toFormulaString(Workbook book) { - return AreaReference.formatAsString(this); + return formatReferenceAsString(); } public byte getDefaultOperandClass() { diff --git a/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java index 87207f6a07..33dff9ec04 100644 --- a/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java @@ -131,14 +131,6 @@ public abstract class RefPtgBase extends OperandPtg { field_2_col=colRelative.setBoolean(field_2_col,rel); } - public final void setColumnRawX(int col) { // TODO - field_2_col = col; - } - - public int getColumnRawX() { // TODO - return field_2_col; - } - public final void setColumn(int col) { if(col < 0 || col >= 0x100) { throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java deleted file mode 100644 index 5ae98b39fe..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java +++ /dev/null @@ -1,32 +0,0 @@ -/* -* 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; - -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.Ptg; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Area2DEval extends AreaEvalBase { - - public Area2DEval(Ptg ptg, ValueEval[] values) { - super((AreaPtg) ptg, values); - } -}
\ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java deleted file mode 100644 index 89209e21b6..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java +++ /dev/null @@ -1,39 +0,0 @@ -/* -* 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; - -import org.apache.poi.hssf.record.formula.Area3DPtg; -import org.apache.poi.hssf.record.formula.Ptg; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Area3DEval extends AreaEvalBase { - - private final int _externSheetIndex; - - public Area3DEval(Ptg ptg, ValueEval[] values) { - super((Area3DPtg) ptg, values); - _externSheetIndex = ((Area3DPtg) ptg).getExternSheetIndex(); - } - - public int getExternSheetIndex() { - return _externSheetIndex; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java index 182b9b618b..b528eb784e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java @@ -62,13 +62,6 @@ public interface AreaEval extends ValueEval { 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. - */ - ValueEval[] getValues(); - - /** * @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. @@ -104,4 +97,10 @@ public interface AreaEval extends ValueEval { * specified indexes should relative to the top left corner of this area. */ ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); + + /** + * Creates an {@link AreaEval} offset by a relative amount from from the upper left cell + * of this area + */ + AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java index 1686e75f33..94f6c9fe4a 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java @@ -22,20 +22,16 @@ import org.apache.poi.hssf.record.formula.AreaI; /** * @author Josh Micich */ -abstract class AreaEvalBase implements AreaEval { +public abstract class AreaEvalBase implements AreaEval { private final int _firstColumn; private final int _firstRow; private final int _lastColumn; private final int _lastRow; - private final ValueEval[] _values; private final int _nColumns; private final int _nRows; - protected AreaEvalBase(AreaI ptg, ValueEval[] values) { - if (values == null) { - throw new IllegalArgumentException("values must not be null"); - } + protected AreaEvalBase(AreaI ptg) { _firstRow = ptg.getFirstRow(); _firstColumn = ptg.getFirstColumn(); _lastRow = ptg.getLastRow(); @@ -43,22 +39,6 @@ abstract class AreaEvalBase implements AreaEval { _nColumns = _lastColumn - _firstColumn + 1; _nRows = _lastRow - _firstRow + 1; - - int expectedItemCount = _nRows * _nColumns; - if ((values.length != expectedItemCount)) { - // Note - this math may need alteration when POI starts to support full column or full row refs - throw new IllegalArgumentException("Array size should be (" + expectedItemCount - + ") but was (" + values.length + ")"); - } - - - - for (int i = values.length - 1; i >= 0; i--) { - if (values[i] == null) { - throw new IllegalArgumentException("value array elements must not be null"); - } - } - _values = values; } public final int getFirstColumn() { @@ -77,11 +57,6 @@ abstract class AreaEvalBase implements AreaEval { return _lastRow; } - public final ValueEval[] getValues() { - // TODO - clone() - but some junits rely on not cloning at the moment - return _values; - } - public final ValueEval getValueAt(int row, int col) { int rowOffsetIx = row - _firstRow; int colOffsetIx = col - _firstColumn; @@ -121,14 +96,7 @@ abstract class AreaEvalBase implements AreaEval { 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 abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); public int getWidth() { return _lastColumn-_firstColumn+1; diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java index 533c604a0c..fe58c69c09 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java @@ -50,7 +50,6 @@ public abstract class FunctionEval implements OperationEval { static { Map m = new HashMap(); - addMapping(m, ID.OFFSET, new Offset()); addMapping(m, ID.INDIRECT, new Indirect()); addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction()); freeRefFunctionsByIdMap = m; @@ -155,7 +154,7 @@ public abstract class FunctionEval implements OperationEval { retval[75] = new Areas(); // AREAS retval[76] = new Rows(); // ROWS retval[77] = new Columns(); // COLUMNS - retval[ID.OFFSET] = null; // Offset.evaluate has a different signature + retval[ID.OFFSET] = new Offset(); // OFFSET retval[79] = new Absref(); // ABSREF retval[80] = new Relref(); // RELREF retval[81] = new Argument(); // ARGUMENT diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java new file mode 100644 index 0000000000..b5c6336170 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java @@ -0,0 +1,65 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package org.apache.poi.hssf.record.formula.eval; + +import org.apache.poi.hssf.record.formula.AreaI; +import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * + */ +public final class LazyAreaEval extends AreaEvalBase { + + private final Sheet _sheet; + private Workbook _workbook; + + public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) { + super(ptg); + _sheet = sheet; + _workbook = workbook; + } + + public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { + + int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF; + int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF; + + Row row = _sheet.getRow(rowIx); + if (row == null) { + return BlankEval.INSTANCE; + } + Cell cell = row.getCell(colIx); + if (cell == null) { + return BlankEval.INSTANCE; + } + return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook); + } + + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(), + relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); + + return new LazyAreaEval(area, _sheet, _workbook); + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java new file mode 100644 index 0000000000..7daf964240 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java @@ -0,0 +1,52 @@ +package org.apache.poi.hssf.record.formula.eval;
+
+import org.apache.poi.hssf.record.formula.AreaI;
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+public final class LazyRefEval extends RefEvalBase {
+
+ private final Sheet _sheet;
+ private final Workbook _workbook;
+
+
+ public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) {
+ super(ptg.getRow(), ptg.getColumn());
+ _sheet = sheet;
+ _workbook = workbook;
+ }
+ public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) {
+ super(ptg.getRow(), ptg.getColumn());
+ _sheet = sheet;
+ _workbook = workbook;
+ }
+
+ public ValueEval getInnerValueEval() {
+ int rowIx = getRow();
+ int colIx = getColumn();
+
+ Row row = _sheet.getRow(rowIx);
+ if (row == null) {
+ return BlankEval.INSTANCE;
+ }
+ Cell cell = row.getCell(colIx);
+ if (cell == null) {
+ return BlankEval.INSTANCE;
+ }
+ return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
+ }
+
+ public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
+
+ AreaI area = new OffsetArea(getRow(), getColumn(),
+ relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+ return new LazyAreaEval(area, _sheet, _workbook);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java index 44a017f631..e22957bd4a 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java @@ -69,5 +69,11 @@ public class NumberEval implements NumericValueEval, StringValueEval { } } } - + public final String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(getStringValue()); + sb.append("]"); + return sb.toString(); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java index 033ecc1f86..e4bb47a43d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java @@ -47,4 +47,7 @@ public final class Ref2DEval implements RefEval { public int getColumn() { return delegate.getColumn(); } + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + throw new RuntimeException("should not be called"); // TODO - delete this whole class + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java deleted file mode 100644 index 622d686329..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java +++ /dev/null @@ -1,53 +0,0 @@ -/* -* 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; - -import org.apache.poi.hssf.record.formula.Ref3DPtg; - -/** - * @author Amol S. Deshmukh - * - */ -public final class Ref3DEval implements RefEval { - - private final ValueEval value; - private final Ref3DPtg delegate; - - public Ref3DEval(Ref3DPtg ptg, ValueEval ve) { - if(ve == null) { - throw new IllegalArgumentException("ve must not be null"); - } - if(ptg == null) { - throw new IllegalArgumentException("ptg must not be null"); - } - value = ve; - delegate = ptg; - } - public ValueEval getInnerValueEval() { - return value; - } - public int getRow() { - return delegate.getRow(); - } - public int getColumn() { - return delegate.getColumn(); - } - public int getExternSheetIndex() { - return delegate.getExternSheetIndex(); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java index e462586d72..397d1f1c94 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java @@ -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; @@ -30,22 +30,22 @@ package org.apache.poi.hssf.record.formula.eval; public interface RefEval extends ValueEval { /** - * The (possibly evaluated) ValueEval contained - * in this RefEval. eg. if cell A1 contains "test" - * then in a formula referring to cell A1 - * the RefEval representing - * A1 will return as the getInnerValueEval() the - * object of concrete type StringEval + * @return the evaluated value of the cell referred to by this RefEval. */ - public ValueEval getInnerValueEval(); + ValueEval getInnerValueEval(); /** * returns the zero based column index. */ - public int getColumn(); + int getColumn(); /** * returns the zero based row index. */ - public int getRow(); + int getRow(); + + /** + * Creates an {@link AreaEval} offset by a relative amount from this RefEval + */ + AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java b/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java new file mode 100644 index 0000000000..37d20f8082 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java @@ -0,0 +1,18 @@ +package org.apache.poi.hssf.record.formula.eval;
+
+public abstract class RefEvalBase implements RefEval {
+
+ private final int _rowIndex;
+ private final int _columnIndex;
+
+ protected RefEvalBase(int rowIndex, int columnIndex) {
+ _rowIndex = rowIndex;
+ _columnIndex = columnIndex;
+ }
+ public final int getRow() {
+ return _rowIndex;
+ }
+ public final int getColumn() {
+ return _columnIndex;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java index f8d8883825..27dfcc1b64 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java @@ -50,9 +50,9 @@ final class CountUtils { 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++; - } + if(criteriaPredicate.matches(ve)) { + result++; + } } } return result; @@ -67,6 +67,9 @@ final class CountUtils { return 0; } public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) { + if (eval == null) { + throw new IllegalArgumentException("eval must not be null"); + } if (eval instanceof AreaEval) { return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/If.java b/src/java/org/apache/poi/hssf/record/formula/functions/If.java index 7aba5db72a..4b066b8226 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/If.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/If.java @@ -1,28 +1,28 @@ -/* -* 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 Nov 25, 2006 - * - */ +/* ==================================================================== + 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.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.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; +import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -30,20 +30,37 @@ import org.apache.poi.hssf.record.formula.eval.Eval; */ public final class If implements Function { - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + Eval falseResult; + switch (args.length) { + case 3: + falseResult = args[2]; + break; + case 2: + falseResult = BoolEval.FALSE; + break; + default: + return ErrorEval.VALUE_INVALID; + } + boolean b; + try { + b = evaluateFirstArg(args[0], srcCellRow, srcCellCol); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + if (b) { + return args[1]; + } + return falseResult; + } - Eval evalWhenFalse = BoolEval.FALSE; - switch (args.length) { - case 3: - evalWhenFalse = args[2]; - case 2: - BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception - if (beval.getBooleanValue()) { - return args[1]; - } - return evalWhenFalse; - default: - return ErrorEval.VALUE_INVALID; - } - } + private static boolean evaluateFirstArg(Eval arg, int srcCellRow, short srcCellCol) + throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + Boolean b = OperandResolver.coerceValueToBoolean(ve, false); + if (b == null) { + return false; + } + return b.booleanValue(); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java index aebf6aab0d..3c93c0846b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java @@ -1,28 +1,28 @@ -/* -* 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.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.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; +import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * Implementation for the Excel function INDEX<p/> @@ -51,15 +51,23 @@ public final class Index implements Function { return ErrorEval.VALUE_INVALID; } Eval firstArg = args[0]; - if(firstArg instanceof AreaEval) { - AreaEval reference = (AreaEval) firstArg; + if(!(firstArg instanceof AreaEval)) { + + // else the other variation of this function takes an array as the first argument + // it seems like interface 'ArrayEval' does not even exist yet - int rowIx = 0; - int columnIx = 0; - int areaIx = 0; + throw new RuntimeException("Incomplete code - cannot handle first arg of type (" + + firstArg.getClass().getName() + ")"); + } + AreaEval reference = (AreaEval) firstArg; + + int rowIx = 0; + int columnIx = 0; + int areaIx = 0; + try { switch(nArgs) { case 4: - areaIx = convertIndexArgToZeroBase(args[3]); + areaIx = convertIndexArgToZeroBase(args[3], srcCellRow, srcCellCol); throw new RuntimeException("Incomplete code" + " - don't know how to support the 'area_num' parameter yet)"); // Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3) @@ -68,41 +76,41 @@ public final class Index implements Function { // The formula parser doesn't seem to support this yet. Not sure if the evaluator does either case 3: - columnIx = convertIndexArgToZeroBase(args[2]); + columnIx = convertIndexArgToZeroBase(args[2], srcCellRow, srcCellCol); case 2: - rowIx = convertIndexArgToZeroBase(args[1]); + rowIx = convertIndexArgToZeroBase(args[1], srcCellRow, srcCellCol); break; default: // too many arguments return ErrorEval.VALUE_INVALID; } - - int nColumns = reference.getLastColumn()-reference.getFirstColumn()+1; - int index = rowIx * nColumns + columnIx; - - return reference.getValues()[index]; + return getValueFromArea(reference, rowIx, columnIx); + } catch (EvaluationException e) { + return e.getErrorEval(); } - - // else the other variation of this function takes an array as the first argument - // it seems like interface 'ArrayEval' does not even exist yet - - throw new RuntimeException("Incomplete code - cannot handle first arg of type (" - + firstArg.getClass().getName() + ")"); } + private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException { + int width = ae.getWidth(); + int height = ae.getHeight(); + + // Slightly irregular logic for bounds checking errors + if (rowIx >= height || columnIx >= width) { + throw new EvaluationException(ErrorEval.REF_INVALID); + } + if (rowIx < 0 || columnIx < 0) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + return ae.getRelativeValue(rowIx, columnIx); + } + /** * takes a NumberEval representing a 1-based index and returns the zero-based int value */ - private static int convertIndexArgToZeroBase(Eval ev) { - NumberEval ne; - if(ev instanceof RefEval) { - // TODO - write junit to justify this - RefEval re = (RefEval) ev; - ne = (NumberEval) re.getInnerValueEval(); - } else { - ne = (NumberEval)ev; - } + private static int convertIndexArgToZeroBase(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - return (int)ne.getNumberValue() - 1; + ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + int oneBasedVal = OperandResolver.coerceValueToInt(ev); + return oneBasedVal - 1; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java index e8c083dc5a..e6a3ec81c2 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java @@ -17,8 +17,6 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; @@ -366,13 +364,7 @@ final class LookupUtils { // 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. - // anything would be ok for rowIx and colIx, but may as well get it right. - int rowIx = refEval.getRow(); - int colIx = refEval.getColumn(); - AreaPtg ap = new AreaPtg(rowIx, rowIx, colIx, colIx, false, false, false, false); - ValueEval value = refEval.getInnerValueEval(); - return new Area2DEval(ap, new ValueEval[] { value, }); + return refEval.offset(0, 0, 0, 0); } throw EvaluationException.invalidValue(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java b/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java index 593998f1e9..311707665e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java @@ -455,85 +455,6 @@ public final class MathX { return d; } - /** - * returns the sum of difference of squares of corresponding double - * value in each subarray: ie. sigma (xarr[i]^2-yarr[i]^2) - * <br/> - * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumx2my2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i<iSize; i++) { - d += (xarr[i] + yarr[i]) * (xarr[i] - yarr[i]); - } - } - catch (ArrayIndexOutOfBoundsException ae) { - d = Double.NaN; - } - - return d; - } - - /** - * returns the sum of sum of squares of corresponding double - * value in each subarray: ie. sigma (xarr[i]^2 + yarr[i]^2) - * <br/> - * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumx2py2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i<iSize; i++) { - d += (xarr[i] * xarr[i]) + (yarr[i] * yarr[i]); - } - } - catch (ArrayIndexOutOfBoundsException ae) { - d = Double.NaN; - } - - return d; - } - - - /** - * returns the sum of squares of difference of corresponding double - * value in each subarray: ie. sigma ( (xarr[i]-yarr[i])^2 ) - * <br/> - * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumxmy2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i<iSize; i++) { - double t = (xarr[i] - yarr[i]); - d += t * t; - } - } - catch (ArrayIndexOutOfBoundsException ae) { - d = Double.NaN; - } - - return d; - } /** * returns the total number of combinations possible when diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java index 0e7cce217e..a9e98ae313 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java @@ -126,30 +126,34 @@ public abstract class MultiOperandNumericFunction extends NumericFunction { if (operand instanceof AreaEval) { AreaEval ae = (AreaEval) operand; - ValueEval[] values = ae.getValues(); DoubleList retval = new DoubleList(); - for (int j=0, jSize=values.length; j<jSize; j++) { - /* - * TODO: For an AreaEval, we are constructing a RefEval - * per element. - * For now this is a tempfix solution since this may - * require a more generic fix at the level of - * HSSFFormulaEvaluator where we store an array - * of RefEvals as the "values" array. - */ - RefEval re = new Ref2DEval(null, values[j]); - ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol); - - if (ve instanceof NumericValueEval) { - NumericValueEval nve = (NumericValueEval) ve; - retval.add(nve.getNumberValue()); - } - else if (ve instanceof BlankEval) { - // note - blanks are ignored, so returned array will be smaller. - } - else { - return null; // indicate to calling subclass that error occurred - } + int width = ae.getWidth(); + int height = ae.getHeight(); + for (int rrIx=0; rrIx<height; rrIx++) { + for (int rcIx=0; rcIx<width; rcIx++) { + ValueEval ve1 = ae.getRelativeValue(rrIx, rcIx); + /* + * TODO: For an AreaEval, we are constructing a RefEval + * per element. + * For now this is a tempfix solution since this may + * require a more generic fix at the level of + * HSSFFormulaEvaluator where we store an array + * of RefEvals as the "values" array. + */ + RefEval re = new Ref2DEval(null, ve1); + ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol); + + if (ve instanceof NumericValueEval) { + NumericValueEval nve = (NumericValueEval) ve; + retval.add(nve.getNumberValue()); + } + else if (ve instanceof BlankEval) { + // note - blanks are ignored, so returned array will be smaller. + } + else { + return null; // indicate to calling subclass that error occurred + } + } } return retval.toArray(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java b/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java index 08bdf3d64c..086596ae21 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java @@ -1,26 +1,22 @@ -/* -* 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.Area3DPtg; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area3DEval; 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; @@ -28,13 +24,9 @@ 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.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.Ref3DEval; 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.usermodel.HSSFFormulaEvaluator; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; /** * Implementation for Excel function OFFSET()<p/> * @@ -51,7 +43,7 @@ import org.apache.poi.ss.usermodel.Workbook; * * @author Josh Micich */ -public final class Offset implements FreeRefFunction { +public final class Offset implements Function { // These values are specific to BIFF8 private static final int LAST_VALID_ROW_INDEX = 0xFFFF; private static final int LAST_VALID_COLUMN_INDEX = 0xFF; @@ -125,37 +117,29 @@ public final class Offset implements FreeRefFunction { * Encapsulates either an area or cell reference which may be 2d or 3d. */ private static final class BaseRef { - private static final int INVALID_SHEET_INDEX = -1; private final int _firstRowIndex; private final int _firstColumnIndex; private final int _width; private final int _height; - private final int _externalSheetIndex; + private final RefEval _refEval; + private final AreaEval _areaEval; public BaseRef(RefEval re) { + _refEval = re; + _areaEval = null; _firstRowIndex = re.getRow(); _firstColumnIndex = re.getColumn(); _height = 1; _width = 1; - if (re instanceof Ref3DEval) { - Ref3DEval r3e = (Ref3DEval) re; - _externalSheetIndex = r3e.getExternSheetIndex(); - } else { - _externalSheetIndex = INVALID_SHEET_INDEX; - } } public BaseRef(AreaEval ae) { + _refEval = null; + _areaEval = ae; _firstRowIndex = ae.getFirstRow(); _firstColumnIndex = ae.getFirstColumn(); _height = ae.getLastRow() - ae.getFirstRow() + 1; _width = ae.getLastColumn() - ae.getFirstColumn() + 1; - if (ae instanceof Area3DEval) { - Area3DEval a3e = (Area3DEval) ae; - _externalSheetIndex = a3e.getExternSheetIndex(); - } else { - _externalSheetIndex = INVALID_SHEET_INDEX; - } } public int getWidth() { @@ -170,20 +154,17 @@ public final class Offset implements FreeRefFunction { public int getFirstColumnIndex() { return _firstColumnIndex; } - public boolean isIs3d() { - return _externalSheetIndex > 0; - } - public short getExternalSheetIndex() { - if(_externalSheetIndex < 0) { - throw new IllegalStateException("external sheet index only available for 3d refs"); + public AreaEval offset(int relFirstRowIx, int relLastRowIx, + int relFirstColIx, int relLastColIx) { + if (_refEval == null) { + return _areaEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); } - return (short) _externalSheetIndex; + return _refEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); } } - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { - + public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { if(args.length < 3 || args.length > 5) { return ErrorEval.VALUE_INVALID; } @@ -206,37 +187,24 @@ public final class Offset implements FreeRefFunction { } LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height); LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width); - return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet); + return createOffset(baseRef, rowOffsetRange, colOffsetRange); } catch (EvaluationException e) { return e.getErrorEval(); } } private static AreaEval createOffset(BaseRef baseRef, - LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange, - Workbook workbook, Sheet sheet) throws EvaluationException { - - LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex()); - LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex()); + LinearOffsetRange orRow, LinearOffsetRange orCol) throws EvaluationException { + LinearOffsetRange absRows = orRow.normaliseAndTranslate(baseRef.getFirstRowIndex()); + LinearOffsetRange absCols = orCol.normaliseAndTranslate(baseRef.getFirstColumnIndex()); - if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { + if(absRows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { throw new EvaluationException(ErrorEval.REF_INVALID); } - if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { + if(absCols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { throw new EvaluationException(ErrorEval.REF_INVALID); } - if(baseRef.isIs3d()) { - Area3DPtg a3dp = new Area3DPtg(rows.getFirstIndex(), rows.getLastIndex(), - cols.getFirstIndex(), cols.getLastIndex(), - false, false, false, false, - baseRef.getExternalSheetIndex()); - return HSSFFormulaEvaluator.evaluateArea3dPtg(workbook, a3dp); - } - - AreaPtg ap = new AreaPtg(rows.getFirstIndex(), rows.getLastIndex(), - cols.getFirstIndex(), cols.getLastIndex(), - false, false, false, false); - return HSSFFormulaEvaluator.evaluateAreaPtg(sheet, workbook, ap); + return baseRef.offset(orRow.getFirstIndex(), orRow.getLastIndex(), orCol.getFirstIndex(), orCol.getLastIndex()); } private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java index 30ad5ec230..4dedf26574 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java @@ -1,22 +1,23 @@ -/* -* 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; + /** * Implementation of Excel function SUMX2MY2()<p/> * @@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumx2my2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumx2my2(xArray, yArray); - } + private static final Accumulator XSquaredMinusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + return x * x - y * y; + } + }; + + protected Accumulator createAccumulator() { + return XSquaredMinusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java index dfd730d12c..c3af0bd380 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java @@ -1,22 +1,23 @@ -/* -* 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; + /** * Implementation of Excel function SUMX2PY2()<p/> * @@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumx2py2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumx2py2(xArray, yArray); - } + private static final Accumulator XSquaredPlusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + return x * x + y * y; + } + }; + + protected Accumulator createAccumulator() { + return XSquaredPlusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java index a1b2fec9b2..4fa11e36ba 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java @@ -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; @@ -30,7 +30,14 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumxmy2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumxmy2(xArray, yArray); - } + private static final Accumulator XMinusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + double xmy = x - y; + return xmy * xmy; + } + }; + + protected Accumulator createAccumulator() { + return XMinusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java index b989c33a26..1d83b363fd 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java @@ -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; @@ -24,180 +24,162 @@ 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.RefEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ public abstract class XYNumericFunction implements Function { - protected static final int X = 0; - protected static final int Y = 1; - - protected static final class DoubleArrayPair { - private final double[] _xArray; - private final double[] _yArray; + private static abstract class ValueArray implements ValueVector { + private final int _size; + protected ValueArray(int size) { + _size = size; + } + public ValueEval getItem(int index) { + if (index < 0 || index > _size) { + throw new IllegalArgumentException("Specified index " + index + + " is outside range (0.." + (_size - 1) + ")"); + } + return getItemInternal(index); + } + protected abstract ValueEval getItemInternal(int index); + public final int getSize() { + return _size; + } + } - public DoubleArrayPair(double[] xArray, double[] yArray) { - _xArray = xArray; - _yArray = yArray; + private static final class SingleCellValueArray extends ValueArray { + private final ValueEval _value; + public SingleCellValueArray(ValueEval value) { + super(1); + _value = value; } - public double[] getXArray() { - return _xArray; + protected ValueEval getItemInternal(int index) { + return _value; + } + } + + private static final class RefValueArray extends ValueArray { + private final RefEval _ref; + public RefValueArray(RefEval ref) { + super(1); + _ref = ref; } - public double[] getYArray() { - return _yArray; + protected ValueEval getItemInternal(int index) { + return _ref.getInnerValueEval(); } - } + } + private static final class AreaValueArray extends ValueArray { + private final AreaEval _ae; + private final int _width; + + public AreaValueArray(AreaEval ae) { + super(ae.getWidth() * ae.getHeight()); + _ae = ae; + _width = ae.getWidth(); + } + protected ValueEval getItemInternal(int index) { + int rowIx = index / _width; + int colIx = index % _width; + return _ae.getRelativeValue(rowIx, colIx); + } + } + + protected static interface Accumulator { + double accumulate(double x, double y); + } + + /** + * Constructs a new instance of the Accumulator used to calculated this function + */ + protected abstract Accumulator createAccumulator(); + + public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } - public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double[][] values; + double result; try { - values = getValues(args[0], args[1]); + ValueVector vvX = createValueVector(args[0]); + ValueVector vvY = createValueVector(args[1]); + int size = vvX.getSize(); + if (size == 0 || vvY.getSize() != size) { + return ErrorEval.NA; + } + result = evaluateInternal(vvX, vvY, size); } catch (EvaluationException e) { return e.getErrorEval(); } - if (values==null - || values[X] == null || values[Y] == null - || values[X].length == 0 || values[Y].length == 0 - || values[X].length != values[Y].length) { - return ErrorEval.VALUE_INVALID; - } - - double d = evaluate(values[X], values[Y]); - if (Double.isNaN(d) || Double.isInfinite(d)) { + if (Double.isNaN(result) || Double.isInfinite(result)) { return ErrorEval.NUM_ERROR; } - return new NumberEval(d); - } - protected abstract double evaluate(double[] xArray, double[] yArray); - - /** - * Returns a double array that contains values for the numeric cells - * from among the list of operands. Blanks and Blank equivalent cells - * are ignored. Error operands or cells containing operands of type - * that are considered invalid and would result in #VALUE! error in - * excel cause this function to return null. - */ - private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException { - - // check for errors first: size mismatch, value errors in x, value errors in y - - int nArrayItems = xops.length; - if(nArrayItems != yops.length) { - throw new EvaluationException(ErrorEval.NA); - } - for (int i = 0; i < xops.length; i++) { - Eval eval = xops[i]; - if (eval instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) eval); + return new NumberEval(result); + } + + private double evaluateInternal(ValueVector x, ValueVector y, int size) + throws EvaluationException { + Accumulator acc = createAccumulator(); + + // error handling is as if the x is fully evaluated before y + ErrorEval firstXerr = null; + ErrorEval firstYerr = null; + boolean accumlatedSome = false; + double result = 0.0; + + for (int i = 0; i < size; i++) { + ValueEval vx = x.getItem(i); + ValueEval vy = y.getItem(i); + if (vx instanceof ErrorEval) { + if (firstXerr == null) { + firstXerr = (ErrorEval) vx; + continue; + } } - } - for (int i = 0; i < yops.length; i++) { - Eval eval = yops[i]; - if (eval instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) eval); + if (vy instanceof ErrorEval) { + if (firstYerr == null) { + firstYerr = (ErrorEval) vy; + continue; + } + } + // only count pairs if both elements are numbers + if (vx instanceof NumberEval && vy instanceof NumberEval) { + accumlatedSome = true; + NumberEval nx = (NumberEval) vx; + NumberEval ny = (NumberEval) vy; + result += acc.accumulate(nx.getNumberValue(), ny.getNumberValue()); + } else { + // all other combinations of value types are silently ignored } } - - double[] xResult = new double[nArrayItems]; - double[] yResult = new double[nArrayItems]; - - int count = 0; - - for (int i=0, iSize=nArrayItems; i<iSize; i++) { - Eval xEval = xops[i]; - Eval yEval = yops[i]; - - if (isNumberEval(xEval) && isNumberEval(yEval)) { - xResult[count] = getDoubleValue(xEval); - yResult[count] = getDoubleValue(yEval); - if (Double.isNaN(xResult[count]) || Double.isNaN(xResult[count])) { - throw new EvaluationException(ErrorEval.NUM_ERROR); - } - count++; - } - } - - return new double[][] { - trimToSize(xResult, count), - trimToSize(yResult, count), - }; - } - - private static double[][] getValues(Eval argX, Eval argY) throws EvaluationException { - - if (argX instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) argX); - } - if (argY instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) argY); - } - - Eval[] xEvals; - Eval[] yEvals; - if (argX instanceof AreaEval) { - AreaEval ae = (AreaEval) argX; - xEvals = ae.getValues(); - } else { - xEvals = new Eval[] { argX, }; - } - - if (argY instanceof AreaEval) { - AreaEval ae = (AreaEval) argY; - yEvals = ae.getValues(); - } else { - yEvals = new Eval[] { argY, }; - } - - return getNumberArray(xEvals, yEvals); - } - - private static double[] trimToSize(double[] arr, int len) { - double[] tarr = arr; - if (arr.length > len) { - tarr = new double[len]; - System.arraycopy(arr, 0, tarr, 0, len); - } - return tarr; - } - - private static boolean isNumberEval(Eval eval) { - boolean retval = false; - - if (eval instanceof NumberEval) { - retval = true; - } - else if (eval instanceof RefEval) { - RefEval re = (RefEval) eval; - ValueEval ve = re.getInnerValueEval(); - retval = (ve instanceof NumberEval); - } - - return retval; - } - - private static double getDoubleValue(Eval eval) { - double retval = 0; - if (eval instanceof NumberEval) { - NumberEval ne = (NumberEval) eval; - retval = ne.getNumberValue(); - } - else if (eval instanceof RefEval) { - RefEval re = (RefEval) eval; - ValueEval ve = re.getInnerValueEval(); - retval = (ve instanceof NumberEval) - ? ((NumberEval) ve).getNumberValue() - : Double.NaN; - } - else if (eval instanceof ErrorEval) { - retval = Double.NaN; - } - return retval; - } + if (firstXerr != null) { + throw new EvaluationException(firstXerr); + } + if (firstYerr != null) { + throw new EvaluationException(firstYerr); + } + if (!accumlatedSome) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return result; + } + + private static ValueVector createValueVector(Eval arg) throws EvaluationException { + if (arg instanceof ErrorEval) { + throw new EvaluationException((ErrorEval) arg); + } + if (arg instanceof AreaEval) { + return new AreaValueArray((AreaEval) arg); + } + if (arg instanceof RefEval) { + return new RefValueArray((RefEval) arg); + } + if (arg instanceof ValueEval) { + return new SingleCellValueArray((ValueEval) arg); + } + throw new RuntimeException("Unexpected eval class (" + arg.getClass().getName() + ")"); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java index 97e605928d..4fb31606ea 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java @@ -43,19 +43,11 @@ public class HSSFFormulaEvaluator extends FormulaEvaluator { return new FormulaParser(formula, workbook); } - /** * debug method - * - * @param formula - * @param sheet - * @param workbook */ void inspectPtgs(String formula) { - HSSFWorkbook hssfWb = (HSSFWorkbook)_workbook; - FormulaParser fp = new FormulaParser(formula, hssfWb); - fp.parse(); - Ptg[] ptgs = fp.getRPNPtg(); + Ptg[] ptgs = FormulaParser.parse(formula, _workbook); System.out.println("<ptg-group>"); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { System.out.println("<ptg>"); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 280284439b..6670b20414 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -35,17 +35,11 @@ import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.CFRuleRecord; -import org.apache.poi.hssf.record.DVALRecord; import org.apache.poi.hssf.record.DVRecord; -import org.apache.poi.hssf.record.EOFRecord; import org.apache.poi.hssf.record.EscherAggregate; -import org.apache.poi.hssf.record.HCenterRecord; -import org.apache.poi.hssf.record.PageBreakRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.SCLRecord; -import org.apache.poi.hssf.record.VCenterRecord; import org.apache.poi.hssf.record.WSBoolRecord; import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.DataValidityTable; @@ -385,6 +379,22 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet DVRecord dvRecord = dataValidation.createDVRecord(workbook); dvt.addDataValidation(dvRecord); } + + /** + * Get the DVRecords objects that are associated to this sheet + * @return a list of DVRecord instances + */ + public List getDVRecords() { + List dvRecords = new ArrayList(); + List records = sheet.getRecords(); + + for(int index=0; index<records.size(); index++) { + if(records.get(index) instanceof DVRecord) { + dvRecords.add(records.get(index)); + } + } + return dvRecords; + } /** @@ -1273,11 +1283,9 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet // Since it's a formula cell, process the // formula string, and look to see if // it contains any references - FormulaParser fp = new FormulaParser(c.getCellFormula(), workbook); - fp.parse(); // Look for references, and update if needed - Ptg[] ptgs = fp.getRPNPtg(); + Ptg[] ptgs = FormulaParser.parse(c.getCellFormula(), workbook); boolean changed = false; for(int i=0; i<ptgs.length; i++) { if(ptgs[i] instanceof RefPtg) { @@ -1294,7 +1302,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet // re-create the formula string if(changed) { c.setCellFormula( - fp.toFormulaString(ptgs) + FormulaParser.toFormulaString(workbook, ptgs) ); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index eb1546ade9..801b298783 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -931,35 +931,17 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm Stack ptgs = new Stack(); if (settingRowAndColumn) { - final int exprsSize = 2 * 11 + 1; // Area3DPtg.SIZE + UnionPtg.SIZE + final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE ptgs.add(new MemFuncPtg(exprsSize)); } - if (startColumn >= 0) - { - Area3DPtg colArea = new Area3DPtg(); - colArea.setExternSheetIndex(externSheetIndex); - colArea.setFirstColumn((short)startColumn); - colArea.setLastColumn((short)endColumn); - colArea.setFirstRow(0); - colArea.setLastRow(MAX_ROW); - colArea.setFirstColRelative(false); - colArea.setLastColRelative(false); - colArea.setFirstRowRelative(false); - colArea.setLastRowRelative(false); + if (startColumn >= 0) { + Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, + false, false, false, false, externSheetIndex); ptgs.add(colArea); } - if (startRow >= 0) - { - Area3DPtg rowArea = new Area3DPtg(); - rowArea.setExternSheetIndex(externSheetIndex); - rowArea.setFirstColumn((short)0); - rowArea.setLastColumn(MAX_COLUMN); - rowArea.setFirstRow(startRow); - rowArea.setLastRow(endRow); - rowArea.setFirstColRelative(false); - rowArea.setLastColRelative(false); - rowArea.setFirstRowRelative(false); - rowArea.setLastRowRelative(false); + if (startRow >= 0) { + Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, + false, false, false, false, externSheetIndex); ptgs.add(rowArea); } if (settingRowAndColumn) diff --git a/src/java/org/apache/poi/hssf/util/AreaReference.java b/src/java/org/apache/poi/hssf/util/AreaReference.java index a10b9976aa..d6575642ee 100644 --- a/src/java/org/apache/poi/hssf/util/AreaReference.java +++ b/src/java/org/apache/poi/hssf/util/AreaReference.java @@ -17,293 +17,21 @@ package org.apache.poi.hssf.util; -import java.util.ArrayList; -import java.util.StringTokenizer; - -import org.apache.poi.hssf.record.formula.AreaI; - -public final class AreaReference { - - /** The character (!) that separates sheet names from cell references */ - private static final char SHEET_NAME_DELIMITER = '!'; - /** The character (:) that separates the two cell references in a multi-cell area reference */ - private static final char CELL_DELIMITER = ':'; - /** The character (') used to quote sheet names when they contain special characters */ - private static final char SPECIAL_NAME_DELIMITER = '\''; - - private final CellReference _firstCell; - private final CellReference _lastCell; - private final boolean _isSingleCell; +public final class AreaReference extends org.apache.poi.ss.util.AreaReference { /** * Create an area ref from a string representation. Sheet names containing special characters should be * delimited and escaped as per normal syntax rules for formulas.<br/> * The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles) */ public AreaReference(String reference) { - if(! isContiguous(reference)) { - throw new IllegalArgumentException( - "References passed to the AreaReference must be contiguous, " + - "use generateContiguous(ref) if you have non-contiguous references"); - } - - String[] parts = separateAreaRefs(reference); - - // Special handling for whole-column references - if(parts.length == 2 && parts[0].length() == 1 && - parts[1].length() == 1 && - parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && - parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') { - // Represented internally as x$1 to x$65536 - // which is the maximum range of rows - parts[0] = parts[0] + "$1"; - parts[1] = parts[1] + "$65536"; - } - - _firstCell = new CellReference(parts[0]); - - if(parts.length == 2) { - _lastCell = new CellReference(parts[1]); - _isSingleCell = false; - } else { - _lastCell = _firstCell; - _isSingleCell = true; - } + super(reference); } /** * Creates an area ref from a pair of Cell References. */ public AreaReference(CellReference topLeft, CellReference botRight) { - _firstCell = topLeft; - _lastCell = botRight; - _isSingleCell = false; - } - - /** - * Is the reference for a contiguous (i.e. - * unbroken) area, or is it made up of - * several different parts? - * (If it is, you will need to call - * .... - */ - public static boolean isContiguous(String reference) { - if(reference.indexOf(',') == -1) { - return true; - } - return false; - } - - /** - * Is the reference for a whole-column reference, - * such as C:C or D:G ? - */ - public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) { - // These are represented as something like - // C$1:C$65535 or D$1:F$0 - // i.e. absolute from 1st row to 0th one - if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() && - botRight.getRow() == 65535 && botRight.isRowAbsolute()) { - return true; - } - return false; - } - public boolean isWholeColumnReference() { - return isWholeColumnReference(_firstCell, _lastCell); - } - - /** - * Takes a non-contiguous area reference, and - * returns an array of contiguous area references. - */ - public static AreaReference[] generateContiguous(String reference) { - ArrayList refs = new ArrayList(); - StringTokenizer st = new StringTokenizer(reference, ","); - while(st.hasMoreTokens()) { - refs.add( - new AreaReference(st.nextToken()) - ); - } - return (AreaReference[])refs.toArray(new AreaReference[refs.size()]); - } - - /** - * @return <code>false</code> if this area reference involves more than one cell - */ - public boolean isSingleCell() { - return _isSingleCell; - } - - /** - * @return the first cell reference which defines this area. Usually this cell is in the upper - * left corner of the area (but this is not a requirement). - */ - public CellReference getFirstCell() { - return _firstCell; - } - - /** - * Note - if this area reference refers to a single cell, the return value of this method will - * be identical to that of <tt>getFirstCell()</tt> - * @return the second cell reference which defines this area. For multi-cell areas, this is - * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner - * of the area (but this is not a requirement). - */ - public CellReference getLastCell() { - return _lastCell; - } - /** - * Returns a reference to every cell covered by this area - */ - public CellReference[] getAllReferencedCells() { - // Special case for single cell reference - if(_isSingleCell) { - return new CellReference[] { _firstCell, }; - } - - // Interpolate between the two - int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow()); - int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow()); - int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol()); - int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol()); - String sheetName = _firstCell.getSheetName(); - - ArrayList refs = new ArrayList(); - for(int row=minRow; row<=maxRow; row++) { - for(int col=minCol; col<=maxCol; col++) { - CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute()); - refs.add(ref); - } - } - return (CellReference[])refs.toArray(new CellReference[refs.size()]); - } - - /** - * Example return values: - * <table border="0" cellpadding="1" cellspacing="0" summary="Example return values"> - * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> - * <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr> - * <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr> - * <tr><td>Sheet1!A$1:B4</td><td>Standard sheet name</td></tr> - * <tr><td>'O''Brien''s Sales'!B5:C6' </td><td>Sheet name with special characters</td></tr> - * </table> - * @return the text representation of this area reference as it would appear in a formula. - */ - public String formatAsString() { - // Special handling for whole-column references - if(isWholeColumnReference()) { - return - CellReference.convertNumToColString(_firstCell.getCol()) - + ":" + - CellReference.convertNumToColString(_lastCell.getCol()); - } - - StringBuffer sb = new StringBuffer(32); - sb.append(_firstCell.formatAsString()); - if(!_isSingleCell) { - sb.append(CELL_DELIMITER); - if(_lastCell.getSheetName() == null) { - sb.append(_lastCell.formatAsString()); - } else { - // don't want to include the sheet name twice - _lastCell.appendCellReference(sb); - } - } - return sb.toString(); - } - /** - * Formats a 2-D area as it would appear in a formula. See formatAsString() (no-arg) - */ - public static String formatAsString(AreaI area) { - CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative()); - CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative()); - - if(isWholeColumnReference(topLeft, botRight)) { - return (new AreaReference(topLeft, botRight)).formatAsString(); - } - return topLeft.formatAsString() + ":" + botRight.formatAsString(); - } - public String toString() { - StringBuffer sb = new StringBuffer(64); - sb.append(getClass().getName()).append(" ["); - sb.append(formatAsString()); - sb.append("]"); - return sb.toString(); - } - - /** - * Separates Area refs in two parts and returns them as separate elements in a String array, - * each qualified with the sheet name (if present) - * - * @return array with one or two elements. never <code>null</code> - */ - private static String[] separateAreaRefs(String reference) { - // TODO - refactor cell reference parsing logic to one place. - // Current known incarnations: - // FormulaParser.GetName() - // CellReference.separateRefParts() - // AreaReference.separateAreaRefs() (here) - // SheetNameFormatter.format() (inverse) - - - int len = reference.length(); - int delimiterPos = -1; - boolean insideDelimitedName = false; - for(int i=0; i<len; i++) { - switch(reference.charAt(i)) { - case CELL_DELIMITER: - if(!insideDelimitedName) { - if(delimiterPos >=0) { - throw new IllegalArgumentException("More than one cell delimiter '" - + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); - } - delimiterPos = i; - } - default: - continue; - case SPECIAL_NAME_DELIMITER: - // fall through - } - if(!insideDelimitedName) { - insideDelimitedName = true; - continue; - } - - if(i >= len-1) { - // reference ends with the delimited name. - // Assume names like: "Sheet1!'A1'" are never legal. - throw new IllegalArgumentException("Area reference '" + reference - + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); - } - if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { - // two consecutive quotes is the escape sequence for a single one - i++; // skip this and keep parsing the special name - } else { - // this is the end of the delimited name - insideDelimitedName = false; - } - } - if(delimiterPos < 0) { - return new String[] { reference, }; - } - - String partA = reference.substring(0, delimiterPos); - String partB = reference.substring(delimiterPos+1); - if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) { - // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? - // FormulaParser has code to handle that. - - throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER - + " in second cell reference of '" + reference + "'"); - } - - int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); - if(plingPos < 0) { - return new String [] { partA, partB, }; - } - - String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter - - return new String [] { partA, sheetName + partB, }; + super(topLeft, botRight); } -}
\ No newline at end of file +} diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java index 74e92b0a8d..aa71c68c54 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java @@ -1,26 +1,23 @@ -/* -* 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.ss.usermodel; -import java.lang.reflect.Constructor; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import java.util.Stack; import org.apache.poi.hssf.model.FormulaParser; @@ -28,6 +25,7 @@ import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.BoolPtg; import org.apache.poi.hssf.record.formula.ControlPtg; +import org.apache.poi.hssf.record.formula.ErrPtg; import org.apache.poi.hssf.record.formula.IntPtg; import org.apache.poi.hssf.record.formula.MemErrPtg; import org.apache.poi.hssf.record.formula.MissingArgPtg; @@ -41,77 +39,51 @@ import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.StringPtg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.record.formula.UnknownPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; -import org.apache.poi.hssf.record.formula.eval.Area3DEval; 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.FunctionEval; +import org.apache.poi.hssf.record.formula.eval.LazyAreaEval; +import org.apache.poi.hssf.record.formula.eval.LazyRefEval; import org.apache.poi.hssf.record.formula.eval.NameEval; import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.OperationEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; -import org.apache.poi.hssf.record.formula.eval.Ref3DEval; 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.usermodel.HSSFFormulaEvaluator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ public class FormulaEvaluator { - - // params to lookup the right constructor using reflection - private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class }; - private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class }; - - private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class }; - - private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class }; - - // Maps for mapping *Eval to *Ptg - private static final Map VALUE_EVALS_MAP = new HashMap(); - - /* - * Following is the mapping between the Ptg tokens returned - * by the FormulaParser and the *Eval classes that are used - * by the FormulaEvaluator - */ - static { - VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class); - VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class); - VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class); - VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class); - - } - - protected Sheet _sheet; protected Workbook _workbook; - + public FormulaEvaluator(Sheet sheet, Workbook workbook) { - this._sheet = sheet; - this._workbook = workbook; + _sheet = sheet; + _workbook = workbook; } - + /** * Does nothing * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell */ - public void setCurrentRow(Row row) {} + public void setCurrentRow(Row row) { + // do nothing + } + /** * If cell contains a formula, the formula is evaluated and returned, * else the CellValue simply copies the appropriate cell value from * the cell and also its cell type. This method should be preferred over * evaluateInCell() when the call should not modify the contents of the - * original cell. + * original cell. * @param cell */ public CellValue evaluate(Cell cell) { @@ -144,25 +116,25 @@ public class FormulaEvaluator { } return retval; } - - + + /** * If cell contains formula, it evaluates the formula, * and saves the result of the formula. The cell * remains as a formula cell. * Else if cell does not contain formula, this method leaves - * the cell unchanged. + * the cell unchanged. * Note that the type of the formula result is returned, * so you know what kind of value is also stored with - * the formula. + * the formula. * <pre> * int evaluatedCellType = evaluator.evaluateFormulaCell(cell); * </pre> * Be aware that your cell will hold both the formula, * and the result. If you want the cell replaced with - * the result of the formula, use {@link #evaluateInCell(HSSFCell)} + * the result of the formula, use {@link #evaluateInCell(Cell)} * @param cell The cell to evaluate - * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however) + * @return The type of the formula result (the cell's type remains as Cell.CELL_TYPE_FORMULA however) */ public int evaluateFormulaCell(Cell cell) { if (cell != null) { @@ -192,21 +164,21 @@ public class FormulaEvaluator { } return -1; } - + /** * If cell contains formula, it evaluates the formula, and * puts the formula result back into the cell, in place * of the old formula. * Else if cell does not contain formula, this method leaves - * the cell unchanged. - * Note that the same instance of HSSFCell is returned to + * the cell unchanged. + * Note that the same instance of Cell is returned to * allow chained calls like: * <pre> * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType(); * </pre> * Be aware that your cell value will be changed to hold the * result of the formula. If you simply want the formula - * value computed for you, use {@link #evaluateFormulaCell(HSSFCell)} + * value computed for you, use {@link #evaluateFormulaCell(Cell)} * @param cell */ public Cell evaluateInCell(Cell cell) { @@ -239,7 +211,7 @@ public class FormulaEvaluator { } return cell; } - + /** * Loops over all cells in all sheets of the supplied * workbook. @@ -248,32 +220,32 @@ public class FormulaEvaluator { * remain as formula cells. * For cells that do not contain formulas, no changes * are made. - * This is a helpful wrapper around looping over all + * This is a helpful wrapper around looping over all * cells, and calling evaluateFormulaCell on each one. */ - public static void evaluateAllFormulaCells(Workbook wb) { - for(int i=0; i<wb.getNumberOfSheets(); i++) { - Sheet sheet = wb.getSheetAt(i); - FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); - - for (Iterator rit = sheet.rowIterator(); rit.hasNext();) { - Row r = (Row)rit.next(); - - for (Iterator cit = r.cellIterator(); cit.hasNext();) { - Cell c = (Cell)cit.next(); - if (c.getCellType() == Cell.CELL_TYPE_FORMULA) - evaluator.evaluateFormulaCell(c); - } - } - } - } - - + public static void evaluateAllFormulaCells(Workbook wb) { + for(int i=0; i<wb.getNumberOfSheets(); i++) { + Sheet sheet = wb.getSheetAt(i); + FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); + + for (Iterator rit = sheet.rowIterator(); rit.hasNext();) { + Row r = (Row)rit.next(); + + for (Iterator cit = r.cellIterator(); cit.hasNext();) { + Cell c = (Cell)cit.next(); + if (c.getCellType() == Cell.CELL_TYPE_FORMULA) + evaluator.evaluateFormulaCell(c); + } + } + } + } + + /** * Returns a CellValue wrapper around the supplied ValueEval instance. * @param eval */ - protected static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) { + private static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) { CellValue retval = null; if (eval != null) { if (eval instanceof NumberEval) { @@ -297,7 +269,7 @@ public class FormulaEvaluator { else if (eval instanceof ErrorEval) { retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode()); -// retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()")); +// retval.setRichTextStringValue(new RichTextString("#An error occurred. check cell.getErrorCode()")); } else { retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); @@ -305,19 +277,19 @@ public class FormulaEvaluator { } return retval; } - + /** - * Dev. Note: Internal evaluate must be passed only a formula cell + * Dev. Note: Internal evaluate must be passed only a formula cell * else a runtime exception will be thrown somewhere inside the method. * (Hence this is a private method.) */ private static ValueEval internalEvaluate(Cell srcCell, Sheet sheet, Workbook workbook) { int srcRowNum = srcCell.getRowIndex(); short srcColNum = srcCell.getCellNum(); - - + + EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker(); - + if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) { return ErrorEval.CIRCULAR_REF_ERROR; } @@ -327,8 +299,9 @@ public class FormulaEvaluator { tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum); } } - private static ValueEval evaluateCell(Workbook workbook, Sheet sheet, + private static ValueEval evaluateCell(Workbook workbook, Sheet sheet, int srcRowNum, short srcColNum, String cellFormulaText) { + Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook); Stack stack = new Stack(); @@ -337,16 +310,16 @@ public class FormulaEvaluator { // since we don't know how to handle these yet :( Ptg ptg = ptgs[i]; if (ptg instanceof ControlPtg) { - // skip Parentheses, Attr, etc - continue; - } + // skip Parentheses, Attr, etc + continue; + } if (ptg instanceof MemErrPtg) { continue; } if (ptg instanceof MissingArgPtg) { continue; } - if (ptg instanceof NamePtg) { + if (ptg instanceof NamePtg) { // named ranges, macro functions NamePtg namePtg = (NamePtg) ptg; stack.push(new NameEval(namePtg.getIndex())); - continue; + continue; } if (ptg instanceof NameXPtg) { NameXPtg nameXPtg = (NameXPtg) ptg; @@ -354,7 +327,7 @@ public class FormulaEvaluator { continue; } if (ptg instanceof UnknownPtg) { continue; } - + Eval opResult; if (ptg instanceof OperationPtg) { OperationPtg optg = (OperationPtg) ptg; @@ -370,42 +343,11 @@ public class FormulaEvaluator { Eval p = (Eval) stack.pop(); ops[j] = p; } - Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet); - stack.push(opresult); - } - else if (ptg instanceof RefPtg) { - RefPtg refPtg = (RefPtg) ptg; - int colIx = refPtg.getColumn(); - int rowIx = refPtg.getRow(); - Row row = sheet.getRow(rowIx); - Cell cell = (row != null) ? row.getCell(colIx) : null; - stack.push(createRef2DEval(refPtg, cell, sheet, workbook)); - } - else if (ptg instanceof Ref3DPtg) { - Ref3DPtg refPtg = (Ref3DPtg) ptg; - int colIx = refPtg.getColumn(); - int rowIx = refPtg.getRow(); - Sheet xsheet = workbook.getSheetAt( - workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()) - ); - Row row = xsheet.getRow(rowIx); - Cell cell = (row != null) ? row.getCell(colIx) : null; - stack.push(createRef3DEval(refPtg, cell, xsheet, workbook)); - } - else if (ptg instanceof AreaPtg) { - AreaPtg ap = (AreaPtg) ptg; - AreaEval ae = evaluateAreaPtg(sheet, workbook, ap); - stack.push(ae); - } - else if (ptg instanceof Area3DPtg) { - Area3DPtg a3dp = (Area3DPtg) ptg; - AreaEval ae = evaluateArea3dPtg(workbook, a3dp); - stack.push(ae); - } - else { - Eval ptgEval = getEvalForPtg(ptg); - stack.push(ptgEval); + opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet); + } else { + opResult = getEvalForPtg(ptg, sheet, workbook); } + stack.push(opResult); } ValueEval value = ((ValueEval) stack.pop()); @@ -414,9 +356,9 @@ public class FormulaEvaluator { } value = dereferenceValue(value, srcRowNum, srcColNum); if (value instanceof BlankEval) { - // Note Excel behaviour here. A blank final final value is converted to zero. + // Note Excel behaviour here. A blank final final value is converted to zero. return NumberEval.ZERO; - // Formulas _never_ evaluate to blank. If a formula appears to have evaluated to + // Formulas _never_ evaluate to blank. If a formula appears to have evaluated to // blank, the actual value is empty string. This can be verified with ISBLANK(). } return value; @@ -425,8 +367,8 @@ public class FormulaEvaluator { /** * Dereferences a single value from any AreaEval or RefEval evaluation result. * If the supplied evaluationResult is just a plain value, it is returned as-is. - * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, - * <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>. + * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, + * <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>. */ private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) { if (evaluationResult instanceof RefEval) { @@ -460,107 +402,48 @@ public class FormulaEvaluator { } return operation.evaluate(ops, srcRowNum, srcColNum); } - - public static AreaEval evaluateAreaPtg(Sheet sheet, Workbook workbook, AreaPtg ap) { - int row0 = ap.getFirstRow(); - int col0 = ap.getFirstColumn(); - int row1 = ap.getLastRow(); - int col1 = ap.getLastColumn(); - - // If the last row is -1, then the - // reference is for the rest of the column - // (eg C:C) - // TODO: Handle whole column ranges properly - if(row1 == -1 && row0 >= 0) { - row1 = (short)sheet.getLastRowNum(); - } - ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1); - return new Area2DEval(ap, values); - } - - public static AreaEval evaluateArea3dPtg(Workbook workbook, Area3DPtg a3dp) { - int row0 = a3dp.getFirstRow(); - int col0 = a3dp.getFirstColumn(); - int row1 = a3dp.getLastRow(); - int col1 = a3dp.getLastColumn(); - Sheet xsheet = workbook.getSheetAt( - workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()) - ); - - // If the last row is -1, then the - // reference is for the rest of the column - // (eg C:C) - // TODO: Handle whole column ranges properly - if(row1 == -1 && row0 >= 0) { - row1 = (short)xsheet.getLastRowNum(); - } - - ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1); - return new Area3DEval(a3dp, values); - } - - private static ValueEval[] evalArea(Workbook workbook, Sheet sheet, - int row0, int col0, int row1, int col1) { - ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)]; - for (int x = row0; sheet != null && x < row1 + 1; x++) { - Row row = sheet.getRow(x); - for (int y = col0; y < col1 + 1; y++) { - ValueEval cellEval; - if(row == null) { - cellEval = BlankEval.INSTANCE; - } else { - cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook); - } - values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval; - } - } - return values; - } /** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be - * one of: Area3DPtg, AreaPtg, RefPtg, Ref3DPtg, IntPtg, NumberPtg, + * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be * passed here! - * - * @param ptg */ - protected static Eval getEvalForPtg(Ptg ptg) { - Eval retval = null; - - Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass()); - try { - if (ptg instanceof Area3DPtg) { - Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof AreaPtg) { - Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof RefPtg) { - Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof Ref3DPtg) { - Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else { - if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg - || ptg instanceof BoolPtg) { - Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY); - retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg }); - } - } + private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) { + if (ptg instanceof RefPtg) { + return new LazyRefEval(((RefPtg) ptg), sheet, workbook); } - catch (Exception e) { - throw new RuntimeException("Fatal Error: ", e); + if (ptg instanceof Ref3DPtg) { + Ref3DPtg refPtg = (Ref3DPtg) ptg; + Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())); + return new LazyRefEval(refPtg, xsheet, workbook); + } + if (ptg instanceof AreaPtg) { + return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook); + } + if (ptg instanceof Area3DPtg) { + Area3DPtg a3dp = (Area3DPtg) ptg; + Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())); + return new LazyAreaEval(a3dp, xsheet, workbook); } - return retval; + if (ptg instanceof IntPtg) { + return new NumberEval(((IntPtg)ptg).getValue()); + } + if (ptg instanceof NumberPtg) { + return new NumberEval(((NumberPtg)ptg).getValue()); + } + if (ptg instanceof StringPtg) { + return new StringEval(((StringPtg) ptg).getValue()); + } + if (ptg instanceof BoolPtg) { + return BoolEval.valueOf(((BoolPtg) ptg).getValue()); + } + if (ptg instanceof ErrPtg) { + return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); + } + throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")"); } - /** * Given a cell, find its type and from that create an appropriate ValueEval * impl instance and return that. Since the cell could be an external @@ -570,7 +453,7 @@ public class FormulaEvaluator { * @param sheet * @param workbook */ - protected static ValueEval getEvalForCell(Cell cell, Row row, Sheet sheet, Workbook workbook) { + public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) { if (cell == null) { return BlankEval.INSTANCE; @@ -593,73 +476,21 @@ public class FormulaEvaluator { } /** - * Creates a Ref2DEval for RefPtg. - * Non existent cells are treated as RefEvals containing BlankEval. - */ - private static Ref2DEval createRef2DEval(RefPtg ptg, Cell cell, - Sheet sheet, Workbook workbook) { - if (cell == null) { - return new Ref2DEval(ptg, BlankEval.INSTANCE); - } - - switch (cell.getCellType()) { - case Cell.CELL_TYPE_NUMERIC: - return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue())); - case Cell.CELL_TYPE_STRING: - return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString())); - case Cell.CELL_TYPE_FORMULA: - return new Ref2DEval(ptg, internalEvaluate(cell, sheet, workbook)); - case Cell.CELL_TYPE_BOOLEAN: - return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue())); - case Cell.CELL_TYPE_BLANK: - return new Ref2DEval(ptg, BlankEval.INSTANCE); - case Cell.CELL_TYPE_ERROR: - return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue())); - } - throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")"); - } - - /** - * create a Ref3DEval for Ref3DPtg. - */ - private static Ref3DEval createRef3DEval(Ref3DPtg ptg, Cell cell, - Sheet sheet, Workbook workbook) { - if (cell == null) { - return new Ref3DEval(ptg, BlankEval.INSTANCE); - } - switch (cell.getCellType()) { - case Cell.CELL_TYPE_NUMERIC: - return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue())); - case Cell.CELL_TYPE_STRING: - return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString())); - case Cell.CELL_TYPE_FORMULA: - return new Ref3DEval(ptg, internalEvaluate(cell, sheet, workbook)); - case Cell.CELL_TYPE_BOOLEAN: - return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue())); - case Cell.CELL_TYPE_BLANK: - return new Ref3DEval(ptg, BlankEval.INSTANCE); - case Cell.CELL_TYPE_ERROR: - return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue())); - } - throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")"); - } - - /** - * Mimics the 'data view' of a cell. This allows formula evaluator + * Mimics the 'data view' of a cell. This allows formula evaluator * to return a CellValue instead of precasting the value to String * or Number or boolean type. * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ public static class CellValue { - private CreationHelper creationHelper; + private CreationHelper creationHelper; private int cellType; private RichTextString richTextStringValue; private double numberValue; private boolean booleanValue; private byte errorValue; - + /** - * CellType should be one of the types defined in HSSFCell + * CellType should be one of the types defined in Cell * @param cellType */ public CellValue(int cellType, CreationHelper creationHelper) { @@ -705,8 +536,8 @@ public class FormulaEvaluator { * @deprecated */ public void setStringValue(String stringValue) { - this.richTextStringValue = - creationHelper.createRichTextString(stringValue); + this.richTextStringValue = + creationHelper.createRichTextString(stringValue); } /** * @return Returns the cellType. diff --git a/src/java/org/apache/poi/ss/util/AreaReference.java b/src/java/org/apache/poi/ss/util/AreaReference.java new file mode 100644 index 0000000000..b5e06b9af9 --- /dev/null +++ b/src/java/org/apache/poi/ss/util/AreaReference.java @@ -0,0 +1,296 @@ +/* ==================================================================== + 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.ss.util; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class AreaReference { + + /** The character (!) that separates sheet names from cell references */ + private static final char SHEET_NAME_DELIMITER = '!'; + /** The character (:) that separates the two cell references in a multi-cell area reference */ + private static final char CELL_DELIMITER = ':'; + /** The character (') used to quote sheet names when they contain special characters */ + private static final char SPECIAL_NAME_DELIMITER = '\''; + + private final CellReference _firstCell; + private final CellReference _lastCell; + private final boolean _isSingleCell; + + /** + * Create an area ref from a string representation. Sheet names containing special characters should be + * delimited and escaped as per normal syntax rules for formulas.<br/> + * The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles) + */ + public AreaReference(String reference) { + if(! isContiguous(reference)) { + throw new IllegalArgumentException( + "References passed to the AreaReference must be contiguous, " + + "use generateContiguous(ref) if you have non-contiguous references"); + } + + String[] parts = separateAreaRefs(reference); + + // Special handling for whole-column references + if(parts.length == 2 && parts[0].length() == 1 && + parts[1].length() == 1 && + parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && + parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') { + // Represented internally as x$1 to x$65536 + // which is the maximum range of rows + parts[0] = parts[0] + "$1"; + parts[1] = parts[1] + "$65536"; + } + + _firstCell = new CellReference(parts[0]); + + if(parts.length == 2) { + _lastCell = new CellReference(parts[1]); + _isSingleCell = false; + } else { + _lastCell = _firstCell; + _isSingleCell = true; + } + } + + /** + * Creates an area ref from a pair of Cell References. + */ + public AreaReference(CellReference topLeft, CellReference botRight) { + _firstCell = topLeft; + _lastCell = botRight; + _isSingleCell = false; + } + + /** + * Is the reference for a contiguous (i.e. + * unbroken) area, or is it made up of + * several different parts? + * (If it is, you will need to call + * .... + */ + public static boolean isContiguous(String reference) { + if(reference.indexOf(',') == -1) { + return true; + } + return false; + } + + /** + * Is the reference for a whole-column reference, + * such as C:C or D:G ? + */ + public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) { + // These are represented as something like + // C$1:C$65535 or D$1:F$0 + // i.e. absolute from 1st row to 0th one + if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() && + botRight.getRow() == 65535 && botRight.isRowAbsolute()) { + return true; + } + return false; + } + public boolean isWholeColumnReference() { + return isWholeColumnReference(_firstCell, _lastCell); + } + + /** + * Takes a non-contiguous area reference, and + * returns an array of contiguous area references. + */ + public static AreaReference[] generateContiguous(String reference) { + ArrayList refs = new ArrayList(); + StringTokenizer st = new StringTokenizer(reference, ","); + while(st.hasMoreTokens()) { + refs.add( + new AreaReference(st.nextToken()) + ); + } + return (AreaReference[])refs.toArray(new AreaReference[refs.size()]); + } + + /** + * @return <code>false</code> if this area reference involves more than one cell + */ + public boolean isSingleCell() { + return _isSingleCell; + } + + /** + * @return the first cell reference which defines this area. Usually this cell is in the upper + * left corner of the area (but this is not a requirement). + */ + public CellReference getFirstCell() { + return _firstCell; + } + + /** + * Note - if this area reference refers to a single cell, the return value of this method will + * be identical to that of <tt>getFirstCell()</tt> + * @return the second cell reference which defines this area. For multi-cell areas, this is + * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner + * of the area (but this is not a requirement). + */ + public CellReference getLastCell() { + return _lastCell; + } + /** + * Returns a reference to every cell covered by this area + */ + public CellReference[] getAllReferencedCells() { + // Special case for single cell reference + if(_isSingleCell) { + return new CellReference[] { _firstCell, }; + } + + // Interpolate between the two + int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow()); + int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow()); + int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol()); + int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol()); + String sheetName = _firstCell.getSheetName(); + + ArrayList refs = new ArrayList(); + for(int row=minRow; row<=maxRow; row++) { + for(int col=minCol; col<=maxCol; col++) { + CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute()); + refs.add(ref); + } + } + return (CellReference[])refs.toArray(new CellReference[refs.size()]); + } + + /** + * Example return values: + * <table border="0" cellpadding="1" cellspacing="0" summary="Example return values"> + * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr> + * <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr> + * <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr> + * <tr><td>Sheet1!A$1:B4</td><td>Standard sheet name</td></tr> + * <tr><td>'O''Brien''s Sales'!B5:C6' </td><td>Sheet name with special characters</td></tr> + * </table> + * @return the text representation of this area reference as it would appear in a formula. + */ + public String formatAsString() { + // Special handling for whole-column references + if(isWholeColumnReference()) { + return + CellReference.convertNumToColString(_firstCell.getCol()) + + ":" + + CellReference.convertNumToColString(_lastCell.getCol()); + } + + StringBuffer sb = new StringBuffer(32); + sb.append(_firstCell.formatAsString()); + if(!_isSingleCell) { + sb.append(CELL_DELIMITER); + if(_lastCell.getSheetName() == null) { + sb.append(_lastCell.formatAsString()); + } else { + // don't want to include the sheet name twice + _lastCell.appendCellReference(sb); + } + } + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(formatAsString()); + sb.append("]"); + return sb.toString(); + } + + /** + * Separates Area refs in two parts and returns them as separate elements in a String array, + * each qualified with the sheet name (if present) + * + * @return array with one or two elements. never <code>null</code> + */ + private static String[] separateAreaRefs(String reference) { + // TODO - refactor cell reference parsing logic to one place. + // Current known incarnations: + // FormulaParser.GetName() + // CellReference.separateRefParts() + // AreaReference.separateAreaRefs() (here) + // SheetNameFormatter.format() (inverse) + + + int len = reference.length(); + int delimiterPos = -1; + boolean insideDelimitedName = false; + for(int i=0; i<len; i++) { + switch(reference.charAt(i)) { + case CELL_DELIMITER: + if(!insideDelimitedName) { + if(delimiterPos >=0) { + throw new IllegalArgumentException("More than one cell delimiter '" + + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); + } + delimiterPos = i; + } + default: + continue; + case SPECIAL_NAME_DELIMITER: + // fall through + } + if(!insideDelimitedName) { + insideDelimitedName = true; + continue; + } + + if(i >= len-1) { + // reference ends with the delimited name. + // Assume names like: "Sheet1!'A1'" are never legal. + throw new IllegalArgumentException("Area reference '" + reference + + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); + } + if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { + // two consecutive quotes is the escape sequence for a single one + i++; // skip this and keep parsing the special name + } else { + // this is the end of the delimited name + insideDelimitedName = false; + } + } + if(delimiterPos < 0) { + return new String[] { reference, }; + } + + String partA = reference.substring(0, delimiterPos); + String partB = reference.substring(delimiterPos+1); + if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) { + // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? + // FormulaParser has code to handle that. + + throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER + + " in second cell reference of '" + reference + "'"); + } + + int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); + if(plingPos < 0) { + return new String [] { partA, partB, }; + } + + String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter + + return new String [] { partA, sheetName + partB, }; + } +} |