From 27f601e40e6bc2a5ac3eb759580106a569cbc0dc Mon Sep 17 00:00:00 2001
From: Yegor Kozlov
+ * A name must always be unique within its scope. POI prevents you from defining a name that is not unique
+ * within its scope. However you can use the same name in different scopes. Example:
+ *
+ *
+ * //by default names are workbook-global
+ * HSSFName name;
+ * name = workbook.createName();
+ * name.setNameName("sales_08");
+ *
+ * name = workbook.createName();
+ * name.setNameName("sales_08"); //will throw an exception: "The workbook already contains this name (case-insensitive)"
+ *
+ * //create sheet-level name
+ * name = workbook.createName();
+ * name.setSheetIndex(0); //the scope of the name is the first sheet
+ * name.setNameName("sales_08"); //ok
+ *
+ * name = workbook.createName();
+ * name.setSheetIndex(0);
+ * name.setNameName("sales_08"); //will throw an exception: "The sheet already contains this name (case-insensitive)"
+ *
+ *
+ * A name must always be unique within its scope. POI prevents you from defining a name that is not unique + * within its scope. However you can use the same name in different scopes. Example: + *
+ * * @param name named range name to set - * @throws IllegalArgumentException if the name is invalid or the workbook already contains this name (case-insensitive) + * @throws IllegalArgumentException if the name is invalid or the already exists within its scope (case-insensitive) */ void setNameName(String name); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index bb3aafe07a..53db069108 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -24,16 +24,21 @@ import java.util.Date; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.formula.FormulaRenderer; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.POIXMLException; +import org.apache.poi.util.POILogger; +import org.apache.poi.util.POILogFactory; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; /** * High level representation of a cell in a row of a spreadsheet. @@ -50,6 +55,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; * */ public final class XSSFCell implements Cell { + private static POILogger logger = POILogFactory.getLogger(XSSFCell.class); /** * The maximum number of columns in SpreadsheetML @@ -108,7 +114,7 @@ public final class XSSFCell implements Cell { protected SharedStringsTable getSharedStringSource() { return sharedStringSource; } - + /** * @return table of cell styles shared across this workbook */ @@ -122,8 +128,8 @@ public final class XSSFCell implements Cell { * @return the sheet this cell belongs to */ public XSSFSheet getSheet() { - return getRow().getSheet(); - } + return getRow().getSheet(); + } /** * Returns the row this cell belongs to @@ -131,8 +137,8 @@ public final class XSSFCell implements Cell { * @return the row this cell belongs to */ public XSSFRow getRow() { - return row; - } + return row; + } /** * Get the value of the cell as a boolean. @@ -321,7 +327,31 @@ public final class XSSFCell implements Cell { int cellType = getCellType(); if(cellType != CELL_TYPE_FORMULA) throw typeMismatch(CELL_TYPE_FORMULA, cellType, false); - return this.cell.getF().getStringValue(); + CTCellFormula f = cell.getF(); + if(f.getT() == STCellFormulaType.SHARED){ + return convertSharedFormula((int)f.getSi()); + } else { + return f.getStringValue(); + } + } + + /** + * Creates a non shared formula from the shared formula counterpart + * + * @return non shared formula created for the given shared formula and this cell + */ + private String convertSharedFormula(int idx){ + XSSFSheet sheet = getSheet(); + XSSFCell sfCell = sheet.getSharedFormulaCell(idx); + if(sfCell == null){ + throw new IllegalStateException("Shared Formula not found for group index " + idx); + } + String sharedFormula = sfCell.getCTCell().getF().getStringValue(); + XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook()); + Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb); + Ptg[] fmla = SharedFormulaRecord.convertSharedFormulas(ptgs, + getRowIndex() - sfCell.getRowIndex(), getColumnIndex() - sfCell.getColumnIndex()); + return FormulaRenderer.toFormulaString(fpb, fmla); } /** @@ -363,7 +393,7 @@ public final class XSSFCell implements Cell { * @return zero-based column index of a column in a sheet. */ public int getColumnIndex() { - return this.cellNum; + return this.cellNum; } /** @@ -372,8 +402,8 @@ public final class XSSFCell implements Cell { * @return zero-based row index of a row in the sheet that contains this cell */ public int getRowIndex() { - return row.getRowNum(); - } + return row.getRowNum(); + } /** * Returns an A1 style reference to the location of this cell @@ -381,8 +411,8 @@ public final class XSSFCell implements Cell { * @return A1 style reference to the location of this cell */ public String getReference() { - return cell.getR(); - } + return cell.getR(); + } /** * Return the cell's style. @@ -406,8 +436,8 @@ public final class XSSFCell implements Cell { if(style == null) { if(cell.isSetS()) cell.unsetS(); } else { - XSSFCellStyle xStyle = (XSSFCellStyle)style; - xStyle.verifyBelongsToStylesSource(stylesSource); + XSSFCellStyle xStyle = (XSSFCellStyle)style; + xStyle.verifyBelongsToStylesSource(stylesSource); long idx = stylesSource.putStyle(xStyle); cell.setS(idx); @@ -486,7 +516,7 @@ public final class XSSFCell implements Cell { * will change the cell to a numeric cell and set its value. */ public void setCellValue(Date value) { - boolean date1904 = getSheet().getWorkbook().isDate1904(); + boolean date1904 = getSheet().getWorkbook().isDate1904(); setCellValue(DateUtil.getExcelDate(value, date1904)); } @@ -507,7 +537,7 @@ public final class XSSFCell implements Cell { * will change the cell to a numeric cell and set its value. */ public void setCellValue(Calendar value) { - boolean date1904 = getSheet().getWorkbook().isDate1904(); + boolean date1904 = getSheet().getWorkbook().isDate1904(); setCellValue( DateUtil.getExcelDate(value, date1904 )); } @@ -765,8 +795,8 @@ public final class XSSFCell implements Cell { * @return hyperlink associated with this cell or+ * //by default names are workbook-global + * Name name; + * name = workbook.createName(); + * name.setNameName("sales_08"); + * + * name = workbook.createName(); + * name.setNameName("sales_08"); //will throw an exception: "The workbook already contains this name (case-insensitive)" + * + * //create sheet-level name + * name = workbook.createName(); + * name.setSheetIndex(0); //the scope of the name is the first sheet + * name.setNameName("sales_08"); //ok + * + * name = workbook.createName(); + * name.setSheetIndex(0); + * name.setNameName("sales_08"); //will throw an exception: "The sheet already contains this name (case-insensitive)" + * + *
null
if not found
*/
public XSSFHyperlink getHyperlink() {
- return getSheet().getHyperlink(row.getRowNum(), cellNum);
- }
+ return getSheet().getHyperlink(row.getRowNum(), cellNum);
+ }
/**
* Assign a hypelrink to this cell
@@ -774,14 +804,14 @@ public final class XSSFCell implements Cell {
* @param hyperlink the hypelrink to associate with this cell
*/
public void setHyperlink(Hyperlink hyperlink) {
- XSSFHyperlink link = (XSSFHyperlink)hyperlink;
-
- // Assign to us
- link.setCellReference( new CellReference(row.getRowNum(), cellNum).formatAsString() );
-
- // Add to the lists
- getSheet().setCellHyperlink(link);
- }
+ XSSFHyperlink link = (XSSFHyperlink)hyperlink;
+
+ // Assign to us
+ link.setCellReference( new CellReference(row.getRowNum(), cellNum).formatAsString() );
+
+ // Add to the lists
+ getSheet().setCellHyperlink(link);
+ }
/**
* Returns the xml bean containing information about the cell's location (reference), value,
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java
index d9761a0014..85e919bef4 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java
@@ -137,17 +137,45 @@ public final class XSSFName implements Name {
* Sets the name that will appear in the user interface for the defined name.
* Names must begin with a letter or underscore, not contain spaces and be unique across the workbook.
*
+ * + * A name must always be unique within its scope. POI prevents you from defining a name that is not unique + * within its scope. However you can use the same name in different scopes. Example: + *
+ * * @param name name of this defined name * @throws IllegalArgumentException if the name is invalid or the workbook already contains this name (case-insensitive) */ public void setNameName(String name) { validateName(name); + int sheetIndex = getSheetIndex(); + //Check to ensure no other names have the same case-insensitive name for (int i = 0; i < workbook.getNumberOfNames(); i++) { XSSFName nm = workbook.getNameAt(i); - if (nm != this && nm.getNameName().equalsIgnoreCase(name)) { - throw new IllegalArgumentException("The workbook already contains this name: " + name); + if (nm != this) { + if(name.equalsIgnoreCase(nm.getNameName()) && sheetIndex == nm.getSheetIndex()){ + String msg = "The "+(sheetIndex == -1 ? "workbook" : "sheet")+" already contains this name: " + name; + throw new IllegalArgumentException(msg); + } } } ctName.setName(name); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java index d402eed4fc..9de3ea46cb 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java @@ -63,7 +63,8 @@ public class XSSFRow implements Row, Comparable+ * //by default names are workbook-global + * XSSFName name; + * name = workbook.createName(); + * name.setNameName("sales_08"); + * + * name = workbook.createName(); + * name.setNameName("sales_08"); //will throw an exception: "The workbook already contains this name (case-insensitive)" + * + * //create sheet-level name + * name = workbook.createName(); + * name.setSheetIndex(0); //the scope of the name is the first sheet + * name.setNameName("sales_08"); //ok + * + * name = workbook.createName(); + * name.setSheetIndex(0); + * name.setNameName("sales_08"); //will throw an exception: "The sheet already contains this name (case-insensitive)" + * + *
null
if not found
+ */
+ XSSFCell getSharedFormulaCell(int sid){
+ return sharedFormulas.get(sid);
+ }
+
+ void onReadCell(XSSFCell cell){
+ //collect cells holding shared formulas
+ CTCell ct = cell.getCTCell();
+ CTCellFormula f = ct.getF();
+ if(f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null){
+ sharedFormulas.put((int)f.getSi(), cell);
+ }
+ }
@Override
protected void commit() throws IOException {
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
index 668e83a0e2..b9035db6b2 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java
@@ -19,83 +19,106 @@ package org.apache.poi.xssf.usermodel;
import junit.framework.TestCase;
-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.*;
+import org.apache.poi.xssf.XSSFTestDataSamples;
public final class TestXSSFFormulaEvaluation extends TestCase {
- public TestXSSFFormulaEvaluation(String name) {
- super(name);
-
- // Use system out logger
- System.setProperty(
- "org.apache.poi.util.POILogger",
- "org.apache.poi.util.SystemOutLogger"
- );
- }
+ public TestXSSFFormulaEvaluation(String name) {
+ super(name);
- public void testSimpleArithmatic() {
- XSSFWorkbook wb = new XSSFWorkbook();
- Sheet s = wb.createSheet();
- Row r = s.createRow(0);
-
- Cell c1 = r.createCell(0);
- c1.setCellFormula("1+5");
- assertEquals(0.0, c1.getNumericCellValue() );
-
- Cell c2 = r.createCell(1);
- c2.setCellFormula("10/2");
- assertEquals(0.0, c2.getNumericCellValue() );
+ // Use system out logger
+ System.setProperty(
+ "org.apache.poi.util.POILogger",
+ "org.apache.poi.util.SystemOutLogger"
+ );
+ }
- FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
-
- fe.evaluateFormulaCell(c1);
- fe.evaluateFormulaCell(c2);
-
- assertEquals(6.0, c1.getNumericCellValue(), 0.0001);
- assertEquals(5.0, c2.getNumericCellValue(), 0.0001);
- }
-
- public void testSumCount() {
- XSSFWorkbook wb = new XSSFWorkbook();
- Sheet s = wb.createSheet();
- Row r = s.createRow(0);
- r.createCell(0).setCellValue(2.5);
- r.createCell(1).setCellValue(1.1);
- r.createCell(2).setCellValue(3.2);
- r.createCell(4).setCellValue(10.7);
-
- r = s.createRow(1);
-
- Cell c1 = r.createCell(0);
- c1.setCellFormula("SUM(A1:B1)");
+ public void testSimpleArithmatic() {
+ XSSFWorkbook wb = new XSSFWorkbook();
+ Sheet s = wb.createSheet();
+ Row r = s.createRow(0);
+
+ Cell c1 = r.createCell(0);
+ c1.setCellFormula("1+5");
assertEquals(0.0, c1.getNumericCellValue() );
- Cell c2 = r.createCell(1);
- c2.setCellFormula("SUM(A1:E1)");
+ Cell c2 = r.createCell(1);
+ c2.setCellFormula("10/2");
assertEquals(0.0, c2.getNumericCellValue() );
- Cell c3 = r.createCell(2);
- c3.setCellFormula("COUNT(A1:A1)");
+ FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
+
+ fe.evaluateFormulaCell(c1);
+ fe.evaluateFormulaCell(c2);
+
+ assertEquals(6.0, c1.getNumericCellValue(), 0.0001);
+ assertEquals(5.0, c2.getNumericCellValue(), 0.0001);
+ }
+
+ public void testSumCount() {
+ XSSFWorkbook wb = new XSSFWorkbook();
+ Sheet s = wb.createSheet();
+ Row r = s.createRow(0);
+ r.createCell(0).setCellValue(2.5);
+ r.createCell(1).setCellValue(1.1);
+ r.createCell(2).setCellValue(3.2);
+ r.createCell(4).setCellValue(10.7);
+
+ r = s.createRow(1);
+
+ Cell c1 = r.createCell(0);
+ c1.setCellFormula("SUM(A1:B1)");
+ assertEquals(0.0, c1.getNumericCellValue() );
+
+ Cell c2 = r.createCell(1);
+ c2.setCellFormula("SUM(A1:E1)");
+ assertEquals(0.0, c2.getNumericCellValue() );
+
+ Cell c3 = r.createCell(2);
+ c3.setCellFormula("COUNT(A1:A1)");
assertEquals(0.0, c3.getNumericCellValue() );
- Cell c4 = r.createCell(3);
- c4.setCellFormula("COUNTA(A1:E1)");
+ Cell c4 = r.createCell(3);
+ c4.setCellFormula("COUNTA(A1:E1)");
assertEquals(0.0, c4.getNumericCellValue() );
- // Evaluate and test
- FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
-
- fe.evaluateFormulaCell(c1);
- fe.evaluateFormulaCell(c2);
- fe.evaluateFormulaCell(c3);
- fe.evaluateFormulaCell(c4);
-
- assertEquals(3.6, c1.getNumericCellValue(), 0.0001);
- assertEquals(17.5, c2.getNumericCellValue(), 0.0001);
- assertEquals(1, c3.getNumericCellValue(), 0.0001);
- assertEquals(4, c4.getNumericCellValue(), 0.0001);
- }
+ // Evaluate and test
+ FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
+
+ fe.evaluateFormulaCell(c1);
+ fe.evaluateFormulaCell(c2);
+ fe.evaluateFormulaCell(c3);
+ fe.evaluateFormulaCell(c4);
+
+ assertEquals(3.6, c1.getNumericCellValue(), 0.0001);
+ assertEquals(17.5, c2.getNumericCellValue(), 0.0001);
+ assertEquals(1, c3.getNumericCellValue(), 0.0001);
+ assertEquals(4, c4.getNumericCellValue(), 0.0001);
+ }
+
+ public void testSharedFormulas(){
+ XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("shared_formulas.xlsx");
+
+ XSSFSheet sheet = wb.getSheetAt(0);
+
+ FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
+ XSSFCell cell;
+
+ cell = sheet.getRow(1).getCell(0);
+ assertEquals("B2", cell.getCellFormula());
+ assertEquals("ProductionOrderConfirmation", evaluator.evaluate(cell).getStringValue());
+
+ cell = sheet.getRow(2).getCell(0);
+ assertEquals("B3", cell.getCellFormula());
+ assertEquals("RequiredAcceptanceDate", evaluator.evaluate(cell).getStringValue());
+
+ cell = sheet.getRow(3).getCell(0);
+ assertEquals("B4", cell.getCellFormula());
+ assertEquals("Header", evaluator.evaluate(cell).getStringValue());
+
+ cell = sheet.getRow(4).getCell(0);
+ assertEquals("B5", cell.getCellFormula());
+ assertEquals("UniqueDocumentNumberID", evaluator.evaluate(cell).getStringValue());
+ }
}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFName.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFName.java
index 5cf0990d4c..4d273ea26c 100755
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFName.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFName.java
@@ -109,4 +109,53 @@ public class TestXSSFName extends TestCase {
wb.removeName(0);
assertEquals(1, wb.getNumberOfNames());
}
+
+ public void testScope() {
+ XSSFWorkbook wb = new XSSFWorkbook();
+ wb.createSheet();
+ wb.createSheet();
+
+ XSSFName name;
+
+ name = wb.createName();
+ name.setNameName("aaa");
+ name = wb.createName();
+ try {
+ name.setNameName("aaa");
+ fail("Expected exception");
+ } catch(Exception e){
+ assertEquals("The workbook already contains this name: aaa", e.getMessage());
+ }
+
+ name = wb.createName();
+ name.setSheetIndex(0);
+ name.setNameName("aaa");
+ name = wb.createName();
+ name.setSheetIndex(0);
+ try {
+ name.setNameName("aaa");
+ fail("Expected exception");
+ } catch(Exception e){
+ assertEquals("The sheet already contains this name: aaa", e.getMessage());
+ }
+
+ name = wb.createName();
+ name.setSheetIndex(1);
+ name.setNameName("aaa");
+ name = wb.createName();
+ name.setSheetIndex(1);
+ try {
+ name.setNameName("aaa");
+ fail("Expected exception");
+ } catch(Exception e){
+ assertEquals("The sheet already contains this name: aaa", e.getMessage());
+ }
+
+ int cnt = 0;
+ for (int i = 0; i < wb.getNumberOfNames(); i++) {
+ if("aaa".equals(wb.getNameAt(i).getNameName())) cnt++;
+ }
+ assertEquals(3, cnt);
+ }
+
}
diff --git a/src/testcases/org/apache/poi/hssf/data/shared_formulas.xlsx b/src/testcases/org/apache/poi/hssf/data/shared_formulas.xlsx
new file mode 100755
index 0000000000000000000000000000000000000000..0a3cb70cb4442c6a1c7a8a93d0bf2e59ff41e588
GIT binary patch
literal 10137
zcmeHtWm{a!wskk|PH=aJ;0{58LvYu|rEy5m;2s=;hTv{N5`r}D5Zob12o@~C`&2xm!B9n`l0FwsbRM_jYukd^-nZd;@@i_W$qozib35G!0ZcIr04YkHuO2cq_gJ
zbA>^I)1J}+10-xQEAs3!bMiae&mC~75v=>Y=j#;P4caF^fi&Ll!{RKgp}J&a${-jc
zTHKalfls#~+5Pd!{DbhAp>r@%0~aUeJgc%DF*-i1((-tRh0;b(3OO;cMFRA~G?~`>
z-X%WUY0!W9kq84^h>_DV8EA*bMK0Q@-YPtp!*}yRtFFcvgn1Cat3{<5GioQps_E~2
z8V$lqRV;_V-5PVb$8o)UtP7*Ib&}4?T||Uc%Z2AE^1^a*fVjVi`BQXRx};MUtta5%
zhmbX9t2B?P4)5S>plIr3RxYnQAQ*j>Uy
zW)q1wn`CdxfK^dZi0q5k#Ye3TeMWmr*yZ0nUmJ&98ZY)`UOw_U=fGt&$#_MXo#TlK
z;yVtNhC|j(Kb>G9QFXn${Ww1g5vTn2QMr?`5P89PQ?a*lg8ecFCxPaYdtPTb_I?;}
zMUKNmr)S8&8-bl7y5=AR06>G}=f3g}tL$!L>1fIE$LAlfy|1SVUgX1T$DH$`bO$>!
zG?E~CM9tM^Rw|QpJB5?mzE0KEcq=g>??