added implementations for CLEAN(), CHAR() and ADDRESS(), see Bugzilla 50607
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1060788 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
db25367237
commit
48544b470b
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta1" date="2010-??-??">
|
<release version="3.8-beta1" date="2010-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">50607 - Added implementation for CLEAN(), CHAR() and ADDRESS()</action>
|
||||||
<action dev="poi-developers" type="add">50587 - Improved documentation on user-defined functions</action>
|
<action dev="poi-developers" type="add">50587 - Improved documentation on user-defined functions</action>
|
||||||
<action dev="poi-developers" type="add">Inside ExtractorFactory, support finding embedded OOXML documents and providing extractors for them</action>
|
<action dev="poi-developers" type="add">Inside ExtractorFactory, support finding embedded OOXML documents and providing extractors for them</action>
|
||||||
<action dev="poi-developers" type="add">Partial HDGF LZW compression support</action>
|
<action dev="poi-developers" type="add">Partial HDGF LZW compression support</action>
|
||||||
|
@ -130,6 +130,7 @@ public final class FunctionEval {
|
|||||||
|
|
||||||
retval[109] = NumericFunction.LOG;
|
retval[109] = NumericFunction.LOG;
|
||||||
|
|
||||||
|
retval[111] = TextFunction.CHAR;
|
||||||
retval[112] = TextFunction.LOWER;
|
retval[112] = TextFunction.LOWER;
|
||||||
retval[113] = TextFunction.UPPER;
|
retval[113] = TextFunction.UPPER;
|
||||||
|
|
||||||
@ -148,7 +149,7 @@ public final class FunctionEval {
|
|||||||
retval[130] = new T();
|
retval[130] = new T();
|
||||||
|
|
||||||
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
|
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
|
||||||
|
retval[162] = TextFunction.CLEAN; //Aniket Banerjee
|
||||||
retval[169] = new Counta();
|
retval[169] = new Counta();
|
||||||
|
|
||||||
retval[183] = AggregateFunction.PRODUCT;
|
retval[183] = AggregateFunction.PRODUCT;
|
||||||
@ -161,7 +162,7 @@ public final class FunctionEval {
|
|||||||
|
|
||||||
retval[212] = NumericFunction.ROUNDUP;
|
retval[212] = NumericFunction.ROUNDUP;
|
||||||
retval[213] = NumericFunction.ROUNDDOWN;
|
retval[213] = NumericFunction.ROUNDDOWN;
|
||||||
|
retval[219] = new Address(); //Aniket Banerjee
|
||||||
retval[220] = new Days360();
|
retval[220] = new Days360();
|
||||||
retval[221] = new Today();
|
retval[221] = new Today();
|
||||||
|
|
||||||
|
103
src/java/org/apache/poi/ss/formula/functions/Address.java
Normal file
103
src/java/org/apache/poi/ss/formula/functions/Address.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.SheetNameFormatter;
|
||||||
|
import org.apache.poi.ss.formula.eval.*;
|
||||||
|
import org.apache.poi.ss.util.CellReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a text reference as text, given specified row and column numbers.
|
||||||
|
*
|
||||||
|
* @author Aniket Banerjee (banerjee@google.com)
|
||||||
|
*/
|
||||||
|
public class Address implements Function {
|
||||||
|
public static final int REF_ABSOLUTE = 1;
|
||||||
|
public static final int REF_ROW_ABSOLUTE_COLUMN_RELATIVE = 2;
|
||||||
|
public static final int REF_ROW_RELATIVE_RELATIVE_ABSOLUTE = 3;
|
||||||
|
public static final int REF_RELATIVE = 4;
|
||||||
|
|
||||||
|
public ValueEval evaluate(ValueEval[] args, int srcRowIndex,
|
||||||
|
int srcColumnIndex) {
|
||||||
|
if(args.length < 2 || args.length > 5) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
boolean pAbsRow, pAbsCol;
|
||||||
|
|
||||||
|
int row = (int)NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
|
||||||
|
int col = (int)NumericFunction.singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex);
|
||||||
|
|
||||||
|
int refType;
|
||||||
|
if(args.length > 2){
|
||||||
|
refType = (int)NumericFunction.singleOperandEvaluate(args[2], srcRowIndex, srcColumnIndex);
|
||||||
|
} else {
|
||||||
|
refType = REF_ABSOLUTE;
|
||||||
|
}
|
||||||
|
switch (refType){
|
||||||
|
case REF_ABSOLUTE:
|
||||||
|
pAbsRow = true;
|
||||||
|
pAbsCol = true;
|
||||||
|
break;
|
||||||
|
case REF_ROW_ABSOLUTE_COLUMN_RELATIVE:
|
||||||
|
pAbsRow = true;
|
||||||
|
pAbsCol = false;
|
||||||
|
break;
|
||||||
|
case REF_ROW_RELATIVE_RELATIVE_ABSOLUTE:
|
||||||
|
pAbsRow = false;
|
||||||
|
pAbsCol = true;
|
||||||
|
break;
|
||||||
|
case REF_RELATIVE:
|
||||||
|
pAbsRow = false;
|
||||||
|
pAbsCol = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean a1;
|
||||||
|
if(args.length > 3){
|
||||||
|
ValueEval ve = OperandResolver.getSingleValue(args[3], srcRowIndex, srcColumnIndex);
|
||||||
|
// TODO R1C1 style is not yet supported
|
||||||
|
a1 = ve == MissingArgEval.instance ? true : OperandResolver.coerceValueToBoolean(ve, false);
|
||||||
|
} else {
|
||||||
|
a1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sheetName;
|
||||||
|
if(args.length == 5){
|
||||||
|
ValueEval ve = OperandResolver.getSingleValue(args[4], srcRowIndex, srcColumnIndex);
|
||||||
|
sheetName = ve == MissingArgEval.instance ? null : OperandResolver.coerceValueToString(ve);
|
||||||
|
} else {
|
||||||
|
sheetName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellReference ref = new CellReference(row - 1, col - 1, pAbsRow, pAbsCol);
|
||||||
|
StringBuffer sb = new StringBuffer(32);
|
||||||
|
if(sheetName != null) {
|
||||||
|
SheetNameFormatter.appendFormat(sb, sheetName);
|
||||||
|
sb.append('!');
|
||||||
|
}
|
||||||
|
sb.append(ref.formatAsString());
|
||||||
|
|
||||||
|
return new StringEval(sb.toString());
|
||||||
|
|
||||||
|
} catch (EvaluationException e){
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -83,6 +83,25 @@ public abstract class TextFunction implements Function {
|
|||||||
protected abstract ValueEval evaluate(String arg);
|
protected abstract ValueEval evaluate(String arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the character specified by a number.
|
||||||
|
*/
|
||||||
|
public static final Function CHAR = new Fixed1ArgFunction() {
|
||||||
|
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||||
|
int arg;
|
||||||
|
try {
|
||||||
|
arg = evaluateIntArg(arg0, srcRowIndex, srcColumnIndex);
|
||||||
|
if (arg < 0 || arg >= 256) {
|
||||||
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
return new StringEval(String.valueOf((char)arg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static final Function LEN = new SingleArgTextFunc() {
|
public static final Function LEN = new SingleArgTextFunc() {
|
||||||
protected ValueEval evaluate(String arg) {
|
protected ValueEval evaluate(String arg) {
|
||||||
return new NumberEval(arg.length());
|
return new NumberEval(arg.length());
|
||||||
@ -109,8 +128,43 @@ public abstract class TextFunction implements Function {
|
|||||||
return new StringEval(arg.trim());
|
return new StringEval(arg.trim());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An implementation of the CLEAN function:
|
||||||
|
* In Excel, the Clean function removes all non-printable characters from a string.
|
||||||
|
*
|
||||||
|
* Author: Aniket Banerjee(banerjee@google.com)
|
||||||
|
*/
|
||||||
|
public static final Function CLEAN = new SingleArgTextFunc() {
|
||||||
|
protected ValueEval evaluate(String arg) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (int i = 0; i < arg.length(); i++) {
|
||||||
|
char c = arg.charAt(i);
|
||||||
|
if (isPrintable(c)) {
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new StringEval(result.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Excel docs: The CLEAN function was designed to remove the first 32 nonprinting characters
|
||||||
|
* in the 7-bit ASCII code (values 0 through 31) from text. In the Unicode character set,
|
||||||
|
* there are additional nonprinting characters (values 127, 129, 141, 143, 144, and 157). By itself,
|
||||||
|
* the CLEAN function does not remove these additional nonprinting characters. To do this task,
|
||||||
|
* use the SUBSTITUTE function to replace the higher value Unicode characters with the 7-bit ASCII
|
||||||
|
* characters for which the TRIM and CLEAN functions were designed.
|
||||||
|
*
|
||||||
|
* @param c the character to test
|
||||||
|
* @return whether the character is printable
|
||||||
|
*/
|
||||||
|
private boolean isPrintable(char c){
|
||||||
|
int charCode = (int)c ;
|
||||||
|
return charCode >= 32;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
* An implementation of the MID function<br/>
|
* An implementation of the MID function<br/>
|
||||||
* MID returns a specific number of
|
* MID returns a specific number of
|
||||||
* characters from a text string, starting at the specified position.<p/>
|
* characters from a text string, starting at the specified position.<p/>
|
||||||
|
@ -60,6 +60,8 @@ public final class AllIndividualFunctionEvaluationTests {
|
|||||||
result.addTestSuite(TestTrunc.class);
|
result.addTestSuite(TestTrunc.class);
|
||||||
result.addTestSuite(TestValue.class);
|
result.addTestSuite(TestValue.class);
|
||||||
result.addTestSuite(TestXYNumericFunction.class);
|
result.addTestSuite(TestXYNumericFunction.class);
|
||||||
|
result.addTestSuite(TestAddress.class);
|
||||||
|
result.addTestSuite(TestClean.class);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.usermodel.CellValue;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.ss.util.CellReference;
|
||||||
|
|
||||||
|
public final class TestAddress extends TestCase {
|
||||||
|
|
||||||
|
public void testAddress() {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFCell cell = wb.createSheet().createRow(0).createCell(0);
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
|
||||||
|
String formulaText = "ADDRESS(1,2)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$B$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(22,44)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$AR$22");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(1,1)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$A$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(1,128)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$DX$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(1,512)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$SR$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(1,1000)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$ALL$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(1,10000)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$NTP$1");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(2,3)";
|
||||||
|
confirmResult(fe, cell, formulaText, "$C$2");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(2,3,2)";
|
||||||
|
confirmResult(fe, cell, formulaText, "C$2");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(2,3,2,,\"EXCEL SHEET\")";
|
||||||
|
confirmResult(fe, cell, formulaText, "'EXCEL SHEET'!C$2");
|
||||||
|
|
||||||
|
formulaText = "ADDRESS(2,3,3,TRUE,\"[Book1]Sheet1\")";
|
||||||
|
confirmResult(fe, cell, formulaText, "'[Book1]Sheet1'!$C2");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void confirmResult(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText,
|
||||||
|
String expectedResult) {
|
||||||
|
cell.setCellFormula(formulaText);
|
||||||
|
fe.notifyUpdateCell(cell);
|
||||||
|
CellValue result = fe.evaluate(cell);
|
||||||
|
assertEquals(result.getCellType(), HSSFCell.CELL_TYPE_STRING);
|
||||||
|
assertEquals(expectedResult, result.getStringValue());
|
||||||
|
}
|
||||||
|
}
|
@ -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.ss.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.usermodel.CellValue;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public final class TestClean extends TestCase {
|
||||||
|
|
||||||
|
public void testClean() {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFCell cell = wb.createSheet().createRow(0).createCell(0);
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
|
||||||
|
String[] asserts = {
|
||||||
|
"aniket\u0007\u0017\u0019", "aniket",
|
||||||
|
"\u0011aniket\u0007\u0017\u0010", "aniket",
|
||||||
|
"\u0011aniket\u0007\u0017\u007F", "aniket\u007F",
|
||||||
|
"\u2116aniket\u2211\uFB5E\u2039", "\u2116aniket\u2211\uFB5E\u2039",
|
||||||
|
};
|
||||||
|
|
||||||
|
for(int i = 0; i < asserts.length; i+= 2){
|
||||||
|
String formulaText = "CLEAN(\"" + asserts[i] + "\")";
|
||||||
|
confirmResult(fe, cell, formulaText, asserts[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
asserts = new String[] {
|
||||||
|
"CHAR(7)&\"text\"&CHAR(7)", "text",
|
||||||
|
"CHAR(7)&\"text\"&CHAR(17)", "text",
|
||||||
|
"CHAR(181)&\"text\"&CHAR(190)", "\u00B5text\u00BE",
|
||||||
|
"\"text\"&CHAR(160)&\"'\"", "text\u00A0'",
|
||||||
|
};
|
||||||
|
for(int i = 0; i < asserts.length; i+= 2){
|
||||||
|
String formulaText = "CLEAN(" + asserts[i] + ")";
|
||||||
|
confirmResult(fe, cell, formulaText, asserts[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void confirmResult(HSSFFormulaEvaluator fe, HSSFCell cell, String formulaText,
|
||||||
|
String expectedResult) {
|
||||||
|
cell.setCellFormula(formulaText);
|
||||||
|
fe.notifyUpdateCell(cell);
|
||||||
|
CellValue result = fe.evaluate(cell);
|
||||||
|
assertEquals(result.getCellType(), HSSFCell.CELL_TYPE_STRING);
|
||||||
|
assertEquals(expectedResult, result.getStringValue());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user