Further support for whole-column references, including formula strings and the evaluator. Also has some new tests for it
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@628065 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
720b6bdf3d
commit
818880bf7f
@ -36,7 +36,7 @@
|
||||
|
||||
<!-- Don't forget to update status.xml too! -->
|
||||
<release version="3.1-beta1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44410 - Partial support for whole-column ranges, such as C:C, in the formula evaluator</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44410 - Support for whole-column ranges, such as C:C, in formula strings and the formula evaluator</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44421 - Update Match function to properly support Area references</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44417 - Improved handling of references for the need to quote the sheet name for some formulas, but not when fetching a sheet by name</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44413 - Fix for circular references in INDEX, OFFSET, VLOOKUP formulas, where a cell is actually allowed to reference itself</action>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<!-- Don't forget to update changes.xml too! -->
|
||||
<changes>
|
||||
<release version="3.1-beta1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">44410 - Partial support for whole-column ranges, such as C:C, in the formula evaluator</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44410 - Support for whole-column ranges, such as C:C, in formula strings and the formula evaluator</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44421 - Update Match function to properly support Area references</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44417 - Improved handling of references for the need to quote the sheet name for some formulas, but not when fetching a sheet by name</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">44413 - Fix for circular references in INDEX, OFFSET, VLOOKUP formulas, where a cell is actually allowed to reference itself</action>
|
||||
|
@ -17,15 +17,13 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.hssf.util.AreaReference;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.hssf.util.SheetReferences;
|
||||
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
import org.apache.poi.hssf.util.AreaReference;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
|
||||
/**
|
||||
@ -38,7 +36,7 @@ import org.apache.poi.util.BitFieldFactory;
|
||||
* @version 1.0-pre
|
||||
*/
|
||||
|
||||
public class Area3DPtg extends Ptg
|
||||
public class Area3DPtg extends Ptg implements AreaI
|
||||
{
|
||||
public final static byte sid = 0x3b;
|
||||
private final static int SIZE = 11; // 10 + 1 for Ptg
|
||||
@ -263,15 +261,18 @@ public class Area3DPtg extends Ptg
|
||||
*/
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
// First do the sheet name
|
||||
StringBuffer retval = new StringBuffer();
|
||||
String sheetName = Ref3DPtg.getSheetName(book, field_1_index_extern_sheet);
|
||||
if(sheetName != null) {
|
||||
SheetNameFormatter.appendFormat(retval, sheetName);
|
||||
retval.append( '!' );
|
||||
}
|
||||
retval.append( ( new CellReference( getFirstRow(), getFirstColumn(), !isFirstRowRelative(), !isFirstColRelative() ) ).formatAsString() );
|
||||
retval.append( ':' );
|
||||
retval.append( ( new CellReference( getLastRow(), getLastColumn(), !isLastRowRelative(), !isLastColRelative() ) ).formatAsString() );
|
||||
|
||||
// Now the normal area bit
|
||||
retval.append( AreaPtg.toFormulaString(this, book) );
|
||||
|
||||
// All done
|
||||
return retval.toString();
|
||||
}
|
||||
|
||||
|
60
src/java/org/apache/poi/hssf/record/formula/AreaI.java
Normal file
60
src/java/org/apache/poi/hssf/record/formula/AreaI.java
Normal file
@ -0,0 +1,60 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
/**
|
||||
* Common interface for AreaPtg and Area3DPtg, and their
|
||||
* child classes.
|
||||
*/
|
||||
public interface AreaI {
|
||||
/**
|
||||
* @return the first row in the area
|
||||
*/
|
||||
public short getFirstRow();
|
||||
|
||||
/**
|
||||
* @return last row in the range (x2 in x1,y1-x2,y2)
|
||||
*/
|
||||
public short getLastRow();
|
||||
|
||||
/**
|
||||
* @return the first column number in the area.
|
||||
*/
|
||||
public short getFirstColumn();
|
||||
|
||||
/**
|
||||
* @return lastcolumn in the area
|
||||
*/
|
||||
public short 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();
|
||||
}
|
@ -34,7 +34,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*/
|
||||
|
||||
public class AreaPtg
|
||||
extends Ptg
|
||||
extends Ptg implements AreaI
|
||||
{
|
||||
public final static short sid = 0x25;
|
||||
private final static int SIZE = 9;
|
||||
@ -284,11 +284,17 @@ public class AreaPtg
|
||||
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
// TODO:
|
||||
// For a reference like C:C, which is stored as
|
||||
// C1:C0 (last row is -1), return as C:C
|
||||
return (new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative())).formatAsString() + ":" +
|
||||
(new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative())).formatAsString();
|
||||
return toFormulaString(this, book);
|
||||
}
|
||||
protected static String toFormulaString(AreaI area, Workbook book) {
|
||||
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(AreaReference.isWholeColumnReference(topLeft, botRight)) {
|
||||
return (new AreaReference(topLeft, botRight)).formatAsString();
|
||||
} else {
|
||||
return topLeft.formatAsString() + ":" + botRight.formatAsString();
|
||||
}
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {
|
||||
|
@ -47,6 +47,17 @@ public final class AreaReference {
|
||||
}
|
||||
|
||||
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$0
|
||||
parts[0] = parts[0] + "$1";
|
||||
parts[1] = parts[1] + "$0";
|
||||
}
|
||||
|
||||
_firstCell = new CellReference(parts[0]);
|
||||
|
||||
if(parts.length == 2) {
|
||||
@ -58,6 +69,15 @@ public final class AreaReference {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -72,6 +92,24 @@ public final class AreaReference {
|
||||
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$0 or D$1:F$0
|
||||
// i.e. absolute from 1st row to 0th one
|
||||
if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() &&
|
||||
botRight.getRow() == -1 && 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.
|
||||
@ -150,6 +188,14 @@ public final class AreaReference {
|
||||
* @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) {
|
||||
|
@ -188,9 +188,11 @@ public final class CellReference {
|
||||
}
|
||||
|
||||
/**
|
||||
* takes in a 0-based base-10 column and returns a ALPHA-26 representation
|
||||
* Takes in a 0-based base-10 column and returns a ALPHA-26
|
||||
* representation.
|
||||
* eg column #3 -> D
|
||||
*/
|
||||
private static String convertNumToColString(int col) {
|
||||
protected static String convertNumToColString(int col) {
|
||||
String retval = null;
|
||||
int mod = col % 26;
|
||||
int div = col / 26;
|
||||
|
@ -23,11 +23,9 @@ import java.io.FileInputStream;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.functions.Sumproduct;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
|
||||
/**
|
||||
* Bug 44410: SUM(C:C) is valid in excel, and means a sum
|
||||
@ -52,6 +50,8 @@ public class TestBug44410 extends TestCase {
|
||||
HSSFRow rowIDX = (HSSFRow)sheet.getRow(3);
|
||||
// =sum(C:C) -> 6
|
||||
HSSFRow rowSUM = (HSSFRow)sheet.getRow(4);
|
||||
// =sum(C:D) -> 66
|
||||
HSSFRow rowSUM2D = (HSSFRow)sheet.getRow(5);
|
||||
|
||||
// Test the sum
|
||||
HSSFCell cellSUM = rowSUM.getCell((short)0);
|
||||
@ -59,8 +59,9 @@ public class TestBug44410 extends TestCase {
|
||||
FormulaRecordAggregate frec =
|
||||
(FormulaRecordAggregate)cellSUM.getCellValueRecord();
|
||||
List ops = frec.getFormulaRecord().getParsedExpression();
|
||||
assertEquals(2, ops.size());
|
||||
assertEquals(AreaPtg.class, ops.get(0).getClass());
|
||||
assertEquals(AttrPtg.class, ops.get(1).getClass());
|
||||
assertEquals(FuncVarPtg.class, ops.get(1).getClass());
|
||||
|
||||
// Actually stored as C1 to C0 (last row is -1)
|
||||
AreaPtg ptg = (AreaPtg)ops.get(0);
|
||||
@ -68,12 +69,12 @@ public class TestBug44410 extends TestCase {
|
||||
assertEquals(2, ptg.getLastColumn());
|
||||
assertEquals(0, ptg.getFirstRow());
|
||||
assertEquals(-1, ptg.getLastRow());
|
||||
assertEquals("C$1:C$0", ptg.toFormulaString(wb.getWorkbook()));
|
||||
assertEquals("C:C", ptg.toFormulaString(wb.getWorkbook()));
|
||||
|
||||
// So will show up wrong here, as we don't
|
||||
// have the sheet to hand when turning the Ptgs
|
||||
// into a string
|
||||
assertEquals("SUM(C$1:C$0)", cellSUM.getCellFormula());
|
||||
// Will show as C:C, but won't know how many
|
||||
// rows it covers as we don't have the sheet
|
||||
// to hand when turning the Ptgs into a string
|
||||
assertEquals("SUM(C:C)", cellSUM.getCellFormula());
|
||||
eva.setCurrentRow(rowSUM);
|
||||
|
||||
// But the evaluator knows the sheet, so it
|
||||
@ -82,12 +83,17 @@ public class TestBug44410 extends TestCase {
|
||||
|
||||
|
||||
// Test the index
|
||||
// Again, the formula string will be wrong, as we
|
||||
// don't have the sheet to hand, but the
|
||||
// evaluator will be correct
|
||||
// Again, the formula string will be right but
|
||||
// lacking row count, evaluated will be right
|
||||
HSSFCell cellIDX = rowIDX.getCell((short)0);
|
||||
assertEquals("INDEX(C$1:C$0,2,1)", cellIDX.getCellFormula());
|
||||
assertEquals("INDEX(C:C,2,1)", cellIDX.getCellFormula());
|
||||
eva.setCurrentRow(rowIDX);
|
||||
assertEquals(2, eva.evaluate(cellIDX).getNumberValue(), 0);
|
||||
|
||||
// Across two colums
|
||||
HSSFCell cellSUM2D = rowSUM2D.getCell((short)0);
|
||||
assertEquals("SUM(C:D)", cellSUM2D.getCellFormula());
|
||||
eva.setCurrentRow(rowSUM2D);
|
||||
assertEquals(66, eva.evaluate(cellSUM2D).getNumberValue(), 0);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user