]> source.dussan.org Git - poi.git/commitdiff
Support for Mid, Replace and Substitute excel functions (bug #s 44095, 44097, 44099)
authorNick Burch <nick@apache.org>
Fri, 21 Dec 2007 12:24:54 +0000 (12:24 +0000)
committerNick Burch <nick@apache.org>
Fri, 21 Dec 2007 12:24:54 +0000 (12:24 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@606172 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mid.java
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Replace.java
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Substitute.java
src/scratchpad/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls

index 21f1187d19dea4360d90c2f913373dd8ab1be2e8..db17e5f5a133ef4b0c89420d047551fa89f20bc0 100644 (file)
@@ -36,6 +36,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.0.2-FINAL" date="2007-??-??">
+            <action dev="POI-DEVELOPERS" type="add">44095, 44097, 44099 - [PATCH] Support for Mid, Replace and Substitute excel functions</action>
             <action dev="POI-DEVELOPERS" type="add">44055 - [PATCH] Support for getting the from field from HSMF messages</action>
             <action dev="POI-DEVELOPERS" type="add">43551 - [PATCH] Support for 1904 date windowing in HSSF (previously only supported 1900 date windowing)</action>
             <action dev="POI-DEVELOPERS" type="add">41064 - [PATCH] Support for String continue records</action>
index b40122763e3874e5f781f9764c225f04e83d7930..bff8b99c7863f2c9aec30b8dd7798c03210971b4 100644 (file)
@@ -33,6 +33,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.0.2-FINAL" date="2007-??-??">
+            <action dev="POI-DEVELOPERS" type="add">44095, 44097, 44099 - [PATCH] Support for Mid, Replace and Substitute excel functions</action>
             <action dev="POI-DEVELOPERS" type="add">44055 - [PATCH] Support for getting the from field from HSMF messages</action>
             <action dev="POI-DEVELOPERS" type="add">43551 - [PATCH] Support for 1904 date windowing in HSSF (previously only supported 1900 date windowing)</action>
             <action dev="POI-DEVELOPERS" type="add">41064 - [PATCH] Support for String continue records</action>
index 30593142e864544b5db13bc202ac1bf03c5cbcec..d6c4399ae33ac9b0dfba899edf0aff61c6995ae1 100644 (file)
  */
 package org.apache.poi.hssf.record.formula.functions;
 
-public class Mid extends NotImplementedFunction {
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+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.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * An implementation of the MID function:
+ * Returns a specific number of characters from a text string, 
+ * starting at the position you specify, based on the number 
+ * of characters you specify.
+ * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
+ */
+public class Mid extends TextFunction {
+       /**
+        * Returns a specific number of characters from a text string, 
+        * starting at the position you specify, based on the number 
+        * of characters you specify.
+        * 
+        * @see org.apache.poi.hssf.record.formula.eval.Eval
+        */
+    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {          
+       Eval retval = null;
+        String str = null;
+        int startNum = 0;
+        int numChars = 0;
+        
+        switch (operands.length) {
+               default:
+                   retval = ErrorEval.VALUE_INVALID;
+               case 3:
+                       // first operand is text string containing characters to extract
+                   // second operand is position of first character to extract
+                   // third operand is the number of characters to return
+                   ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
+                   ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
+                   ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
+                   if (firstveval instanceof StringValueEval
+                       && secondveval instanceof NumericValueEval
+                       && thirdveval instanceof NumericValueEval) {
+                       
+                       StringValueEval strEval = (StringValueEval) firstveval;
+                       str = strEval.getStringValue();
+                       
+                       NumericValueEval startNumEval = (NumericValueEval) secondveval;
+                       // NOTE: it is safe to cast to int here
+                       // because in Excel =MID("test", 1, 1.7) returns t 
+                       // so 1.7 must be truncated to 1
+                       // and =MID("test", 1.9, 2) returns te 
+                       // so 1.9 must be truncated to 1
+                       startNum = (int) startNumEval.getNumberValue();
+                       
+                       NumericValueEval numCharsEval = (NumericValueEval) thirdveval;
+                       numChars = (int) numCharsEval.getNumberValue();
+                       
+                   } else {
+                       retval = ErrorEval.VALUE_INVALID;
+                   }
+           }
+               
+        if (retval == null) {
+                       if (startNum < 1 || numChars < 0) {
+                               retval = ErrorEval.VALUE_INVALID;
+                       } else if (startNum > str.length() || numChars == 0) {
+                               retval = BlankEval.INSTANCE;
+                       } else if (startNum + numChars > str.length()) {
+                               retval = new StringEval(str.substring(startNum - 1));
+                       } else {
+                               retval = new StringEval(str.substring(startNum - 1, numChars));
+                       } 
+        } 
+               return retval;
+    }
 
 }
index 3ba7a2b2ce0bc58d0506134c2e31e5e67181c122..95413f08238bb1d4f088b6e82e1dbe493445aad9 100644 (file)
  */
 package org.apache.poi.hssf.record.formula.functions;
 
-public class Replace extends NotImplementedFunction {
+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.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * An implementation of the REPLACE function:
+ * Replaces part of a text string based on the number of characters 
+ * you specify, with another text string.
+ * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
+ */
+public class Replace extends TextFunction {
+
+       /**
+        * Replaces part of a text string based on the number of characters 
+        * you specify, with another text string.
+        * 
+        * @see org.apache.poi.hssf.record.formula.eval.Eval
+        */
+    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {          
+       Eval retval = null;
+        String oldStr = null;
+        String newStr = null;
+        int startNum = 0;
+        int numChars = 0;
+        
+        switch (operands.length) {
+               default:
+                   retval = ErrorEval.VALUE_INVALID;
+               case 4:         
+                       // first operand is text string containing characters to replace
+                   // second operand is position of first character to replace
+                   // third operand is the number of characters in the old string
+                   // you want to replace with new string
+                   // fourth operand is the new string
+                   ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
+                   ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
+                   ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
+                   ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol);
+                   if (firstveval instanceof StringValueEval
+                       && secondveval instanceof NumericValueEval
+                       && thirdveval instanceof NumericValueEval
+                       && fourthveval instanceof StringValueEval) {
+                       
+                       StringValueEval oldStrEval = (StringValueEval) firstveval;
+                       oldStr = oldStrEval.getStringValue();
+                       
+                       NumericValueEval startNumEval = (NumericValueEval) secondveval;
+                       // NOTE: it is safe to cast to int here
+                       // because in Excel =REPLACE("task", 2.7, 3, "est") 
+                       // returns test 
+                       // so 2.7 must be truncated to 2
+                       // and =REPLACE("task", 1, 1.9, "") returns ask 
+                       // so 1.9 must be truncated to 1
+                       startNum = (int) startNumEval.getNumberValue();
+                       
+                       NumericValueEval numCharsEval = (NumericValueEval) thirdveval;
+                       numChars = (int) numCharsEval.getNumberValue();
+                                    
+                       StringValueEval newStrEval = (StringValueEval) fourthveval;
+                       newStr = newStrEval.getStringValue();
+                   } else {
+                       retval = ErrorEval.VALUE_INVALID;
+                   }
+           }
+               
+        if (retval == null) {
+                       if (startNum < 1 || numChars < 0) {
+                               retval = ErrorEval.VALUE_INVALID;
+                       } else {
+                               StringBuffer strBuff = new StringBuffer(oldStr);
+                               // remove any characters that should be replaced
+                               if (startNum <= oldStr.length() && numChars != 0) {
+                                       strBuff.delete(startNum - 1, startNum - 1 + numChars);
+                               } 
+                               // now insert (or append) newStr
+                               if (startNum > strBuff.length()) {
+                                       strBuff.append(newStr);
+                               } else {
+                                       strBuff.insert(startNum - 1, newStr);
+                               }
+                               retval = new StringEval(strBuff.toString());
+                       }
+        } 
+               return retval;
+    }
 
 }
index 8a975c569e65604fd39b4ccd4be8f7a9bab6f3c8..9d2e9ce3618a1a2d570a8c90f670ec268e9e1620 100644 (file)
  */
 package org.apache.poi.hssf.record.formula.functions;
 
-public class Substitute extends NotImplementedFunction {
+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.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
+/**
+ * An implementation of the SUBSTITUTE function:
+ * Substitutes text in a text string with new text, some number of times.
+ * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
+ */
+public class Substitute extends TextFunction {
+       private static final int REPLACE_ALL = -1;
+       
+       /**
+        *Substitutes text in a text string with new text, some number of times.
+        * 
+        * @see org.apache.poi.hssf.record.formula.eval.Eval
+        */
+    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {          
+       Eval retval = null;
+        String oldStr = null;
+        String searchStr = null;
+        String newStr = null;
+        int numToReplace = REPLACE_ALL;
+        
+        switch (operands.length) {
+               default:
+                   retval = ErrorEval.VALUE_INVALID;
+               case 4:
+                       ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol);
+                       if (fourthveval instanceof NumericValueEval) {
+                               NumericValueEval numToReplaceEval = (NumericValueEval) fourthveval;
+                               // NOTE: it is safe to cast to int here
+                       // because in Excel =SUBSTITUTE("teststr","t","T",1.9) 
+                       // returns Teststr 
+                       // so 1.9 must be truncated to 1
+                               numToReplace = (int) numToReplaceEval.getNumberValue();
+                       } else {
+                               retval = ErrorEval.VALUE_INVALID;
+                       }
+               case 3: 
+                       // first operand is text string containing characters to replace
+                   // second operand is text to find
+                   // third operand is replacement text
+                   ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
+                   ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
+                   ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
+                   if (firstveval instanceof StringValueEval
+                       && secondveval instanceof StringValueEval
+                       && thirdveval instanceof StringValueEval) {
+                       
+                       StringValueEval oldStrEval = (StringValueEval) firstveval;
+                       oldStr = oldStrEval.getStringValue();
+                       
+                       StringValueEval searchStrEval = (StringValueEval) secondveval;
+                       searchStr = searchStrEval.getStringValue();
+                       
+                       StringValueEval newStrEval = (StringValueEval) thirdveval;
+                       newStr = newStrEval.getStringValue();
+                   } else {
+                       retval = ErrorEval.VALUE_INVALID;
+                   }
+           }
+               
+        if (retval == null) {
+                       if (numToReplace != REPLACE_ALL && numToReplace < 1) {
+                               retval = ErrorEval.VALUE_INVALID;
+                       } else if (searchStr.length() == 0) {
+                               retval = new StringEval(oldStr);
+                       } else {
+                               StringBuffer strBuff = new StringBuffer();
+                               int startIndex = 0;
+                               int nextMatch = -1;
+                               for (int leftToReplace = numToReplace; 
+                                       (leftToReplace > 0 || numToReplace == REPLACE_ALL) 
+                                               && (nextMatch = oldStr.indexOf(searchStr, startIndex)) != -1;
+                                       leftToReplace--) {
+                                       // store everything from end of last match to start of this match
+                                       strBuff.append(oldStr.substring(startIndex, nextMatch));
+                                       strBuff.append(newStr);
+                                       startIndex = nextMatch + searchStr.length();
+                               }
+                               // store everything from end of last match to end of string
+                               if (startIndex < oldStr.length()) {
+                                       strBuff.append(oldStr.substring(startIndex));
+                               }
+                               retval = new StringEval(strBuff.toString());
+                       }
+        } 
+               return retval;
+    }
+    
 }
index c6366ea1c4314965ccf43d14699a2abf31e419bc..cf4b6fa5011723ad05fd24152a3a3e4a451d2c98 100644 (file)
Binary files a/src/scratchpad/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/scratchpad/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ