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:
Yegor Kozlov 2011-01-19 12:30:18 +00:00
parent db25367237
commit 48544b470b
7 changed files with 305 additions and 3 deletions

View File

@ -34,6 +34,7 @@
<changes>
<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">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>

View File

@ -130,6 +130,7 @@ public final class FunctionEval {
retval[109] = NumericFunction.LOG;
retval[111] = TextFunction.CHAR;
retval[112] = TextFunction.LOWER;
retval[113] = TextFunction.UPPER;
@ -148,7 +149,7 @@ public final class FunctionEval {
retval[130] = new T();
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
retval[162] = TextFunction.CLEAN; //Aniket Banerjee
retval[169] = new Counta();
retval[183] = AggregateFunction.PRODUCT;
@ -161,7 +162,7 @@ public final class FunctionEval {
retval[212] = NumericFunction.ROUNDUP;
retval[213] = NumericFunction.ROUNDDOWN;
retval[219] = new Address(); //Aniket Banerjee
retval[220] = new Days360();
retval[221] = new Today();

View 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();
}
}
}

View File

@ -83,6 +83,25 @@ public abstract class TextFunction implements Function {
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() {
protected ValueEval evaluate(String arg) {
return new NumberEval(arg.length());
@ -109,8 +128,43 @@ public abstract class TextFunction implements Function {
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/>
* MID returns a specific number of
* characters from a text string, starting at the specified position.<p/>

View File

@ -60,6 +60,8 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestTrunc.class);
result.addTestSuite(TestValue.class);
result.addTestSuite(TestXYNumericFunction.class);
result.addTestSuite(TestAddress.class);
result.addTestSuite(TestClean.class);
return result;
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}