]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Thu, 10 Jul 2008 20:37:09 +0000 (20:37 +0000)
committerNick Burch <nick@apache.org>
Thu, 10 Jul 2008 20:37:09 +0000 (20:37 +0000)
https://svn.apache.org:443/repos/asf/poi/trunk

........
  r674891 | nick | 2008-07-08 18:31:36 +0100 (Tue, 08 Jul 2008) | 1 line

  Test to check that formula stuff is mostly working with zip codes
........
  r674911 | josh | 2008-07-08 19:56:21 +0100 (Tue, 08 Jul 2008) | 1 line

  Fix bug #45338 - JDK 1.4/1.5 issue from r673997
........
  r674937 | josh | 2008-07-08 21:19:40 +0100 (Tue, 08 Jul 2008) | 1 line

  Hooked up new junit in suite (from bug 45338)
........
  r674953 | josh | 2008-07-08 22:00:13 +0100 (Tue, 08 Jul 2008) | 1 line

  Fix for bug 45354 - Proper distinguishing of cell references and named ranges within formulas
........
  r675079 | josh | 2008-07-09 02:45:33 +0100 (Wed, 09 Jul 2008) | 1 line

  Fix for bug 45348 - required tweaks to RVA formula logic
........
  r675086 | josh | 2008-07-09 03:15:52 +0100 (Wed, 09 Jul 2008) | 2 lines

  Minor junit fix after r674953 (bug 45354).  Hooked up loose tests in suite.
........
  r675218 | josh | 2008-07-09 15:58:06 +0100 (Wed, 09 Jul 2008) | 1 line

  Fix for bug 45367 - fixed boundary case when row zero is the last row removed from the sheet
........
  r675661 | nick | 2008-07-10 18:52:33 +0100 (Thu, 10 Jul 2008) | 1 line

  Apply some changes inspired by bug # 45373, which improve the performance of HSSFSheet.shiftRows
........
  r675671 | nick | 2008-07-10 19:41:25 +0100 (Thu, 10 Jul 2008) | 1 line

  Fix bug #45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra
........

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@675718 13f79535-47bb-0310-9956-ffa450edef68

26 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/util/CellReference.java
src/java/org/apache/poi/ss/util/CellReference.java
src/testcases/org/apache/poi/hssf/HSSFTests.java
src/testcases/org/apache/poi/hssf/data/45365.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/testRVA.xls
src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java
src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
src/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java
src/testcases/org/apache/poi/hssf/util/TestCellReference.java
src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java

index 46b55c3ceb2250e51492ae811be06ea3dfb14ddf..f0c5d67ac25b248d0ea3a3b9f5de70243d357329 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
+           <action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
+           <action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
+           <action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>
+           <action dev="POI-DEVELOPERS" type="fix">45354 - Fixed recognition of named ranges within formulas</action>
            <action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
            <action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
            <action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
index 31427e939eaf3f91abb713873ee0120772dc052e..693fc42e2e209534603f887c6bbf04ba72d27080 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
+           <action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
+           <action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
+           <action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>
+           <action dev="POI-DEVELOPERS" type="fix">45354 - Fixed recognition of named ranges within formulas</action>
            <action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
            <action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
            <action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
index 1c9b220356b736dc7d025ec1c6f76ec617110fb0..632eacf5cc5f295914e1f8914faa66af15ab3557 100644 (file)
@@ -20,10 +20,6 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintStream;
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 
 import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
 import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
@@ -37,7 +33,6 @@ import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.record.BOFRecord;
 import org.apache.poi.hssf.record.BlankRecord;
 import org.apache.poi.hssf.record.BoolErrRecord;
-import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.LabelRecord;
 import org.apache.poi.hssf.record.LabelSSTRecord;
@@ -47,7 +42,6 @@ import org.apache.poi.hssf.record.RKRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.SSTRecord;
 import org.apache.poi.hssf.record.StringRecord;
-import org.apache.poi.hssf.usermodel.HSSFDateUtil;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
@@ -180,7 +174,7 @@ public class XLS2CSVmra implements HSSFListener {
                        nextRow = frec.getRow();
                        nextColumn = frec.getColumn();
                        } else {
-                               thisStr = formatNumberDateCell(frec, frec.getValue());
+                               thisStr = formatListener.formatNumberDateCell(frec);
                        }
                } else {
                        thisStr = '"' + 
@@ -231,7 +225,7 @@ public class XLS2CSVmra implements HSSFListener {
             thisColumn = numrec.getColumn();
             
             // Format
-            thisStr = formatNumberDateCell(numrec, numrec.getValue());
+            thisStr = formatListener.formatNumberDateCell(numrec);
             break;
         case RKRecord.sid:
                RKRecord rkrec = (RKRecord) record;
@@ -290,44 +284,6 @@ public class XLS2CSVmra implements HSSFListener {
                }
        }
        
-       /**
-        * Formats a number or date cell, be that a real number, or the 
-        *  answer to a formula
-        */
-       private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
-        // Get the built in format, if there is one
-               int formatIndex = formatListener.getFormatIndex(cell);
-               String formatString = formatListener.getFormatString(cell);
-               
-               if(formatString == null) {
-            return Double.toString(value);
-        } else {
-               // Is it a date?
-               if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
-                               HSSFDateUtil.isValidExcelDate(value)) {
-                       // Java wants M not m for month
-                       formatString = formatString.replace('m','M');
-                       // Change \- into -, if it's there
-                       formatString = formatString.replaceAll("\\\\-","-");
-                       
-                       // Format as a date
-                       Date d = HSSFDateUtil.getJavaDate(value, false);
-                       DateFormat df = new SimpleDateFormat(formatString);
-                   return df.format(d);
-               } else {
-                       if(formatString == "General") {
-                               // Some sort of wierd default
-                               return Double.toString(value);
-                       }
-                       
-                       // Format as a number
-                   DecimalFormat df = new DecimalFormat(formatString);
-                   return df.format(value);
-               }
-        }
-       }
-
-       
        public static void main(String[] args) throws Exception {
                if(args.length < 1) {
                        System.err.println("Use:");
index b88143713db72bd04d4b10de833ebc9e4cf6d927..5a84f4564608079658a7d4fbbcf6dcdf72e1fc46 100644 (file)
 ==================================================================== */
 package org.apache.poi.hssf.eventusermodel;
 
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -24,8 +28,11 @@ import java.util.Map;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.ExtendedFormatRecord;
 import org.apache.poi.hssf.record.FormatRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.NumberRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
 
 /**
  * A proxy HSSFListener that keeps track of the document
@@ -69,6 +76,61 @@ public class FormatTrackingHSSFListener implements HSSFListener {
                }
        }
        
+       /**
+        * Formats the given numeric of date Cell's contents
+        *  as a String, in as close as we can to the way 
+        *  that Excel would do so.
+        * Uses the various format records to manage this.
+        * 
+        * TODO - move this to a central class in such a
+        *  way that hssf.usermodel can make use of it too
+        */
+       public String formatNumberDateCell(CellValueRecordInterface cell) {
+               double value;
+               if(cell instanceof NumberRecord) {
+                       value = ((NumberRecord)cell).getValue();
+               } else if(cell instanceof FormulaRecord) {
+                       value = ((FormulaRecord)cell).getValue();
+               } else {
+                       throw new IllegalArgumentException("Unsupported CellValue Record passed in " + cell);
+               }
+               
+        // Get the built in format, if there is one
+               int formatIndex = getFormatIndex(cell);
+               String formatString = getFormatString(cell);
+               
+               if(formatString == null) {
+            return Double.toString(value);
+        } else {
+               // Is it a date?
+               if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
+                               HSSFDateUtil.isValidExcelDate(value)) {
+                       // Java wants M not m for month
+                       formatString = formatString.replace('m','M');
+                       // Change \- into -, if it's there
+                       formatString = formatString.replaceAll("\\\\-","-");
+                       
+                       // Format as a date
+                       Date d = HSSFDateUtil.getJavaDate(value, false);
+                       DateFormat df = new SimpleDateFormat(formatString);
+                   return df.format(d);
+               } else {
+                       if(formatString == "General") {
+                               // Some sort of wierd default
+                               return Double.toString(value);
+                       }
+                       if(formatString == "0.00E+00") {
+                               // This seems to mean output as a normal double
+                               return Double.toString(value);
+                       }
+                       
+                       // Format as a number
+                   DecimalFormat df = new DecimalFormat(formatString);
+                   return df.format(value);
+               }
+        }
+       }
+       
        /**
         * Returns the format string, eg $##.##, for the
         *  given number format index.
index 2cc2a805311f2ce91cbee9dbdcdca23389889d49..afe7de239e02974d6d9cf7a1f13775629f0dc6cf 100644 (file)
@@ -29,6 +29,7 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.hssf.util.AreaReference;
 import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.util.CellReference.NameType;
 
 /**
  * This class parses a formula string into a List of tokens in RPN order.
@@ -293,9 +294,14 @@ public final class FormulaParser {
 
         // This can be either a cell ref or a named range
         // Try to spot which it is
-        if (isValidCellReference(name)) {
+        int nameType = CellReference.classifyCellReference(name);
+        if (nameType == NameType.CELL) {
             return new RefPtg(name);
         }
+        if (nameType != NameType.NAMED_RANGE) {
+            new FormulaParseException("Name '" + name
+                + "' does not look like a cell reference or named range");
+        }
 
         for(int i = 0; i < book.getNumberOfNames(); i++) {
             // named range name matching is case insensitive
@@ -303,11 +309,12 @@ public final class FormulaParser {
                 return new NamePtg(name, book);
             }
         }
-        throw new FormulaParseException("Found reference to named range \""
-                    + name + "\", but that named range wasn't defined!");
+        throw new FormulaParseException("Specified named range '"
+                    + name + "' does not exist in the current workbook.");
     }
 
     /**
+     * @param name an 'identifier' like string (i.e. contains alphanums, and dots)
      * @return <code>null</code> if name cannot be split at a dot
      */
     private AreaReference parseArea(String name) {
@@ -323,6 +330,8 @@ public final class FormulaParser {
                 return null;
             }
         }
+        // This expression is only valid as an area ref, if the LHS and RHS of the dot(s) are both
+        // cell refs.  Otherwise, this expression must be a named range name
         String partA = name.substring(0, dotPos);
         if (!isValidCellReference(partA)) {
             return null;
@@ -336,12 +345,14 @@ public final class FormulaParser {
         return new AreaReference(topLeft, bottomRight);
     }
 
+    /**
+     * @return <code>true</code> if the specified name is a valid cell reference
+     */
     private static boolean isValidCellReference(String str) {
-        // TODO - exact rules for recognising cell references may be too complicated for regex 
-        return CELL_REFERENCE_PATTERN.matcher(str).matches();
+        return CellReference.classifyCellReference(str) == NameType.CELL;
     }
-    
-    
+
+
     /**
      * Note - Excel function names are 'case aware but not case sensitive'.  This method may end
      * up creating a defined name record in the workbook if the specified name is not an internal
index 5358324a38e1ad44a9b89ce409fc8da7f8a59201..07d2bd2fd623c343034a29462b5634a09fd870e6 100644 (file)
@@ -71,11 +71,16 @@ final class OperandClassTransformer {
                                                + _formulaType + ") not supported yet");
                
                }
-               transformNode(rootNode, rootNodeOperandClass, false);
+               transformNode(rootNode, rootNodeOperandClass, false, false);
        }
 
+       /**
+        * @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a 
+        * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
+        * the function return value).
+        */
        private void transformNode(ParseNode node, byte desiredOperandClass,
-                       boolean callerForceArrayFlag) {
+                       boolean callerForceArrayFlag, boolean isDirectChildOfValueOperator) {
                Ptg token = node.getToken();
                ParseNode[] children = node.getChildren();
                if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
@@ -84,7 +89,7 @@ final class OperandClassTransformer {
                        // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
                        for (int i = 0; i < children.length; i++) {
                                ParseNode child = children[i];
-                               transformNode(child, desiredOperandClass, callerForceArrayFlag);
+                               transformNode(child, desiredOperandClass, callerForceArrayFlag, true);
                        }
                        return;
                }
@@ -101,22 +106,34 @@ final class OperandClassTransformer {
                        // nothing to do
                        return;
                }
-        if (callerForceArrayFlag) {
-               switch (desiredOperandClass) {
-                       case Ptg.CLASS_VALUE:
-                       case Ptg.CLASS_ARRAY:
-                               token.setClass(Ptg.CLASS_ARRAY); 
-                               break;
-                       case Ptg.CLASS_REF:
-                               token.setClass(Ptg.CLASS_REF); 
-                               break;
-                       default:
-                               throw new IllegalStateException("Unexpected operand class ("
-                                               + desiredOperandClass + ")");
-               }
-        } else {
-               token.setClass(desiredOperandClass);
-        }
+               if (isDirectChildOfValueOperator) {
+                       // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
+                       // All direct operands of value operators that are initially 'R' type will 
+                       // be converted to 'V' type.
+                       if (token.getPtgClass() == Ptg.CLASS_REF) {
+                               token.setClass(Ptg.CLASS_VALUE); 
+                       }
+               }
+               token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+       }
+
+       private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
+                       boolean callerForceArrayFlag) {
+               switch (desiredOperandClass) {
+                       case Ptg.CLASS_VALUE:
+                               if (!callerForceArrayFlag) {
+                                       return Ptg.CLASS_VALUE;
+                               }
+                               // else fall through
+                       case Ptg.CLASS_ARRAY:
+                               return Ptg.CLASS_ARRAY; 
+                       case Ptg.CLASS_REF:
+                               if (!callerForceArrayFlag) {
+                                       return currentOperandClass;
+                               }
+                               return Ptg.CLASS_REF; 
+               }
+               throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
        }
 
        private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
@@ -200,7 +217,7 @@ final class OperandClassTransformer {
                for (int i = 0; i < children.length; i++) {
                        ParseNode child = children[i];
                        byte paramOperandClass = afp.getParameterClass(i);
-                       transformNode(child, paramOperandClass, localForceArrayFlag);
+                       transformNode(child, paramOperandClass, localForceArrayFlag, false);
                }
        }
 }
index 8e47cbe7a0fc8fd034e56fbdd5c2ead788aa28d2..ace857da1e23f032951249f18776e461fe190b01 100755 (executable)
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record.formula;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.poi.hssf.util.CellReference;
+
 /**
  * Formats sheet names for use in formula expressions.
  * 
@@ -28,14 +29,12 @@ import java.util.regex.Pattern;
  */
 public final class SheetNameFormatter {
        
-       private static final String BIFF8_LAST_COLUMN = "IV";
-       private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
-       private static final String BIFF8_LAST_ROW = String.valueOf(0x10000);
-       private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
-
        private static final char DELIMITER = '\'';
        
-       private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z])+[0-9]+");
+       /**
+        * Matches a single cell ref with no absolute ('$') markers
+        */
+       private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z]+)([0-9]+)");
 
        private SheetNameFormatter() {
                // no instances of this class
@@ -105,27 +104,27 @@ public final class SheetNameFormatter {
                return false;
        }
        
-    /**
-     * @return <code>true</code> if the presence of the specified character in a sheet name would 
-     * require the sheet name to be delimited in formulas.  This includes every non-alphanumeric 
-     * character besides underscore '_'.
-     */
-    /* package */ static boolean isSpecialChar(char ch) {
-        // note - Character.isJavaIdentifierPart() would allow dollars '$'
-        if(Character.isLetterOrDigit(ch)) {
-            return false;
-        }
-        switch(ch) {
-            case '_': // underscore is ok
-                return false;
-            case '\n':
-            case '\r':
-            case '\t':
-                throw new RuntimeException("Illegal character (0x" 
-                        + Integer.toHexString(ch) + ") found in sheet name");
-        }
-        return true;
-    }
+       /**
+        * @return <code>true</code> if the presence of the specified character in a sheet name would 
+        * require the sheet name to be delimited in formulas.  This includes every non-alphanumeric 
+        * character besides underscore '_'.
+        */
+       /* package */ static boolean isSpecialChar(char ch) {
+               // note - Character.isJavaIdentifierPart() would allow dollars '$'
+               if(Character.isLetterOrDigit(ch)) {
+                       return false;
+               }
+               switch(ch) {
+                       case '_': // underscore is ok
+                               return false;
+                       case '\n':
+                       case '\r':
+                       case '\t':
+                               throw new RuntimeException("Illegal character (0x" 
+                                               + Integer.toHexString(ch) + ") found in sheet name");
+               }
+               return true;
+       }
        
 
        /**
@@ -149,64 +148,11 @@ public final class SheetNameFormatter {
         * <p/>
         * For better or worse this implementation attempts to replicate Excel's formula renderer.
         * Excel uses range checking on the apparent 'row' and 'column' components.  Note however that
-        * the maximum sheet size varies across versions:
-        * <p/>
-        * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
-        *                 summary="Notable cases.">
-        *   <tr><th>Version&nbsp;&nbsp;</th><th>File Format&nbsp;&nbsp;</th>
-        *      <th>Last Column&nbsp;&nbsp;</th><th>Last Row</th></tr>
-        *   <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr>
-        *   <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr>
-        * </table></blockquote>
-        * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for
-        * this method:
-        * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
-        *                 summary="Notable cases.">
-        *   <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
-        *      <th>Result&nbsp;</th></tr>
-        *   <tr><td>"A1", 1</td><td>true</td></tr>
-        *   <tr><td>"a111", 1</td><td>true</td></tr>
-        *   <tr><td>"A65536", 1</td><td>true</td></tr>
-        *   <tr><td>"A65537", 1</td><td>false</td></tr>
-        *   <tr><td>"iv1", 2</td><td>true</td></tr>
-        *   <tr><td>"IW1", 2</td><td>false</td></tr>
-        *   <tr><td>"AAA1", 3</td><td>false</td></tr>
-        *   <tr><td>"a111", 1</td><td>true</td></tr>
-        *   <tr><td>"Sheet1", 6</td><td>false</td></tr>
-        * </table></blockquote>
+        * the maximum sheet size varies across versions.
+        * @see org.apache.poi.hssf.util.CellReference
         */
-       /* package */ static boolean cellReferenceIsWithinRange(String rawSheetName, int numberOfLetters) {
-               
-               if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
-                       // "Sheet1" case etc
-                       return false; // that was easy
-               }
-               int nDigits = rawSheetName.length() - numberOfLetters;
-               if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
-                       return false; 
-               }
-               if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
-                       String colStr = rawSheetName.substring(0, BIFF8_LAST_COLUMN_TEXT_LEN).toUpperCase();
-                       if(colStr.compareTo(BIFF8_LAST_COLUMN) > 0) {
-                               return false;
-                       }
-               } else {
-                       // apparent column name has less chars than max
-                       // no need to check range
-               }
-               
-               if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
-                       String colStr = rawSheetName.substring(numberOfLetters);
-                       // ASCII comparison is valid if digit count is same
-                       if(colStr.compareTo(BIFF8_LAST_ROW) > 0) {
-                               return false;
-                       }
-               } else {
-                       // apparent row has less chars than max
-                       // no need to check range
-               }
-               
-               return true;
+       /* package */ static boolean cellReferenceIsWithinRange(String lettersPrefix, String numbersSuffix) {
+               return CellReference.cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
        }
 
        /**
@@ -239,7 +185,7 @@ public final class SheetNameFormatter {
                
                // rawSheetName == "Sheet1" gets this far.
                String lettersPrefix = matcher.group(1);
-               return cellReferenceIsWithinRange(rawSheetName, lettersPrefix.length());
+               String numbersSuffix = matcher.group(2);
+               return cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
        }
-
 }
index 3e95aaee4c4b93424759a8f1b78d3d008d8a52c0..0ddac1f6dbec5ec020c1d9d177ff40c3e6bab482 100644 (file)
@@ -187,6 +187,19 @@ public final class HSSFRow implements Comparable, Row {
             row.setFirstCol(findFirstCell(row.getFirstCol()));
         }
     }
+    
+    /**
+     * Removes all the cells from the row, and their
+     *  records too.
+     */
+    protected void removeAllCells() {
+       for(int i=0; i<cells.length; i++) {
+               if(cells[i] != null) {
+                       removeCell(cells[i], true);
+               }
+       }
+       cells=new HSSFCell[INITIAL_CAPACITY];
+    }
 
     /**
      * create a high level HSSFCell object from an existing low level record.  Should
index 23ce6120a9b3e0bfa62b01fd11c57c997d511a00..dc18e5599e50af0e6f21efa04d97451253de1fcd 100644 (file)
@@ -280,18 +280,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
     /**
      * used internally to refresh the "last row" when the last row is removed.
      */
-
-    private int findLastRow(int lastrow)
-    {
+    private int findLastRow(int lastrow) {
+        if (lastrow < 1) {
+            return -1;
+        }
         int rownum = lastrow - 1;
         HSSFRow r = getRow(rownum);
 
-        while (r == null && rownum > 0)
-        {
+        while (r == null && rownum > 0) {
             r = getRow(--rownum);
         }
-        if (r == null)
-          return -1;
+        if (r == null) {
+            return -1;
+        }
         return rownum;
     }
 
@@ -1224,6 +1225,28 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
      * @param resetOriginalRowHeight whether to set the original row's height to the default
      */
     public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight)
+    {
+       shiftRows(startRow, endRow, n, copyRowHeight, resetOriginalRowHeight, true);
+    }
+    
+    /**
+     * Shifts rows between startRow and endRow n number of rows.
+     * If you use a negative number, it will shift rows up.
+     * Code ensures that rows don't wrap around
+     *
+     * <p>
+     * Additionally shifts merged regions that are completely defined in these
+     * rows (ie. merged 2 cells on a row to be shifted).
+     * <p>
+     * TODO Might want to add bounds checking here
+     * @param startRow the row to start shifting
+     * @param endRow the row to end shifting
+     * @param n the number of rows to shift
+     * @param copyRowHeight whether to copy the row height during the shift
+     * @param resetOriginalRowHeight whether to set the original row's height to the default
+     * @param moveComments whether to move comments at the same time as the cells they are attached to
+     */
+    public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments)
     {
         int s, e, inc;
         if ( n < 0 )
@@ -1250,44 +1273,55 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
                 row2Replace = createRow( rowNum + n );
 
             HSSFCell cell;
-
-
-
-
-        // Removes the cells before over writting them.
-            for ( short col = row2Replace.getFirstCellNum(); col <= row2Replace.getLastCellNum(); col++ )
-            {
-                cell = row2Replace.getCell( col );
-                if ( cell != null )
-                    row2Replace.removeCell( cell );
+            
+            // Remove all the old cells from the row we'll
+            //  be writing too, before we start overwriting 
+            //  any cells. This avoids issues with cells 
+            //  changing type, and records not being correctly
+            //  overwritten
+            row2Replace.removeAllCells();
+
+            // If this row doesn't exist, nothing needs to
+            //  be done for the now empty destination row
+            if (row == null) continue; // Nothing to do for this row
+
+            // Fetch the first and last columns of the
+            //  row now, so we still have them to hand
+            //  once we start removing cells
+               short firstCol = row.getFirstCellNum();
+               short lastCol = row.getLastCellNum();
+
+            // Fix up row heights if required
+            if (copyRowHeight) {
+                row2Replace.setHeight(row.getHeight());
+            }
+            if (resetOriginalRowHeight) {
+                row.setHeight((short)0xff);
             }
-        if (row == null) continue; // Nothing to do for this row
-        else {
-        if (copyRowHeight) {
-            row2Replace.setHeight(row.getHeight());
-        }
-
-        if (resetOriginalRowHeight) {
-            row.setHeight((short)0xff);
-        }
-        }
-            for ( short col = row.getFirstCellNum(); col <= row.getLastCellNum(); col++ )
-            {
-                cell = row.getCell( col );
-                if ( cell != null )
-                {
-                    row.removeCell( cell );
-                    CellValueRecordInterface cellRecord = cell.getCellValueRecord();
-                    cellRecord.setRow( rowNum + n );
-                    row2Replace.createCellFromRecord( cellRecord );
-                    sheet.addValueRecord( rowNum + n, cellRecord );
-                }
 
-                // move comments if exist (can exist even if cell is null)
-                HSSFComment comment = getCellComment(rowNum, col);
-                if (comment != null) {
-                   comment.setRow(rowNum + n);
-                }
+            // Copy each cell from the source row to
+            //  the destination row
+            for(Iterator cells = row.cellIterator(); cells.hasNext(); ) {
+               cell = (HSSFCell)cells.next();
+                row.removeCell( cell );
+                CellValueRecordInterface cellRecord = cell.getCellValueRecord();
+                cellRecord.setRow( rowNum + n );
+                row2Replace.createCellFromRecord( cellRecord );
+                sheet.addValueRecord( rowNum + n, cellRecord );
+            }
+            // Now zap all the cells in the source row
+            row.removeAllCells();
+            
+            // Move comments from the source row to the
+            //  destination row. Note that comments can
+            //  exist for cells which are null
+            if(moveComments) {
+                   for( short col = firstCol; col <= lastCol; col++ ) {
+                       HSSFComment comment = getCellComment(rowNum, col);
+                       if (comment != null) {
+                          comment.setRow(rowNum + n);
+                       }
+                   }
             }
         }
         if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 );
index b80ccb790551b0e22ea9dfefccf6f811a164b126..09d66e1f60fb8ef779df2770c29d1bfe8873686a 100644 (file)
@@ -376,28 +376,28 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
              log.log(POILogger.DEBUG, "convertLabelRecords exit");
      }
 
-       /**
-        * Retrieves the current policy on what to do when
-        *  getting missing or blank cells from a row.
-        * The default is to return blank and null cells.
-        *  {@link MissingCellPolicy}
-        */
-       public MissingCellPolicy getMissingCellPolicy() {
-               return missingCellPolicy;
-       }
+    /**
+     * Retrieves the current policy on what to do when
+     *  getting missing or blank cells from a row.
+     * The default is to return blank and null cells.
+     *  {@link MissingCellPolicy}
+     */
+    public MissingCellPolicy getMissingCellPolicy() {
+        return missingCellPolicy;
+    }
 
-       /**
-        * Sets the policy on what to do when
-        *  getting missing or blank cells from a row.
-        * This will then apply to all calls to 
-        *  {@link Row.getCell()}. See
-        *  {@link MissingCellPolicy}
-        */
-       public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
-               this.missingCellPolicy = missingCellPolicy;
-       }
+    /**
+     * Sets the policy on what to do when
+     *  getting missing or blank cells from a row.
+     * This will then apply to all calls to 
+     *  {@link HSSFRow.getCell()}. See
+     *  {@link MissingCellPolicy}
+     */
+    public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
+        this.missingCellPolicy = missingCellPolicy;
+    }
 
-       /**
+    /**
      * sets the order of appearance for a given sheet.
      *
      * @param sheetname the name of the sheet to reorder
@@ -1041,11 +1041,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
                              String name, boolean italic, boolean strikeout,
                              short typeOffset, byte underline)
     {
-       for (short i=0; i<=getNumberOfFonts(); i++) {
-               // Remember - there is no 4!
-               if(i == 4) continue;
-               
-               HSSFFont hssfFont = getFontAt(i);
+        for (short i=0; i<=getNumberOfFonts(); i++) {
+            // Remember - there is no 4!
+            if(i == 4) continue;
+            
+            HSSFFont hssfFont = getFontAt(i);
             if (hssfFont.getBoldweight() == boldWeight
                     && hssfFont.getColor() == color
                     && hssfFont.getFontHeight() == fontHeight
@@ -1077,19 +1077,17 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * @param idx  index number
      * @return HSSFFont at the index
      */
+    public HSSFFont getFontAt(short idx) {
+        if(fonts == null) fonts = new Hashtable();
+
+        // So we don't confuse users, give them back
+        //  the same object every time, but create
+        //  them lazily
+        Short sIdx = new Short(idx);
+        if(fonts.containsKey(sIdx)) {
+            return (HSSFFont)fonts.get(sIdx);
+        }
 
-    public HSSFFont getFontAt(short idx)
-    {
-       if(fonts == null) fonts = new Hashtable();
-       
-       // So we don't confuse users, give them back
-       //  the same object every time, but create
-       //  them lazily
-       Short sIdx = Short.valueOf(idx);
-       if(fonts.containsKey(sIdx)) {
-               return (HSSFFont)fonts.get(sIdx);
-       }
-       
         FontRecord font = workbook.getFontRecordAt(idx);
         HSSFFont retval = new HSSFFont(idx, font);
         fonts.put(sIdx, retval);
index c63e7bc999b74a494c55d3508c75b49709a07266..0ddb0764ee33c2fcff8c4c7c7fa17872486a01ea 100644 (file)
@@ -25,6 +25,15 @@ package org.apache.poi.hssf.util;
  * @author  Dennis Doubleday (patch to seperateRowColumns())
  */
 public final class CellReference extends org.apache.poi.ss.util.CellReference {
+       /**
+        * Used to classify identifiers found in formulas as cell references or not.
+        */
+       public static final class NameType {
+               public static final int CELL = 1;
+               public static final int NAMED_RANGE = 2;
+               public static final int BAD_CELL_OR_NAMED_RANGE = -1;
+       }
+
     /**
      * Create an cell ref from a string representation.  Sheet names containing special characters should be
      * delimited and escaped as per normal syntax rules for formulas.
@@ -33,8 +42,15 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
        super(cellRef);
     }
 
+    public CellReference(int pRow, int pCol) {
+       super(pRow, pCol, true, true);
+    }
+    public CellReference(int pRow, short pCol) {
+       super(pRow, (int)pCol, true, true);
+    }
+    
     public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
-        this(null, pRow, pCol, pAbsRow, pAbsCol);
+        super(null, pRow, pCol, pAbsRow, pAbsCol);
     }
     public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
        super(pSheetName, pRow, pCol, pAbsRow, pAbsCol);
index 4f90311f3b650ed276f27595d771076850f7f7b4..b46cd88ccba32365d2e94d2a0059e6514f5eaae0 100644 (file)
 
 package org.apache.poi.ss.util;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import org.apache.poi.hssf.record.formula.SheetNameFormatter;
 
 /**
- * Common convertion functions between Excel style A1, C27 style
- *  cell references, and POI usermodel style row=0, column=0
- *  style references.
- * Applys for both HSSF and XSSF.
+ *
  * @author  Avik Sengupta
  * @author  Dennis Doubleday (patch to seperateRowColumns())
  */
 public class CellReference {
+       /**
+        * Used to classify identifiers found in formulas as cell references or not.
+        */
+       public static final class NameType {
+               public static final int CELL = 1;
+               public static final int NAMED_RANGE = 2;
+               public static final int BAD_CELL_OR_NAMED_RANGE = -1;
+       }
+
     /** The character ($) that signifies a row or column value is absolute instead of relative */ 
     private static final char ABSOLUTE_REFERENCE_MARKER = '$';
     /** The character (!) that separates sheet names from cell references */ 
@@ -35,6 +44,20 @@ public class CellReference {
     /** The character (') used to quote sheet names when they contain special characters */
     private static final char SPECIAL_NAME_DELIMITER = '\'';
     
+    /**
+     * Matches a run of letters followed by a run of digits.  The run of letters is group 1 and the
+     * run of digits is group 2.  Each group may optionally be prefixed with a single '$'.
+     */
+       private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
+       /**
+        * Named range names must start with a letter or underscore.  Subsequent characters may include
+        * digits or dot.  (They can even end in dot).
+        */
+       private static final Pattern NAMED_RANGE_NAME_PATTERN = Pattern.compile("[_A-Za-z][_.A-Za-z0-9]*");
+       private static final String BIFF8_LAST_COLUMN = "IV";
+       private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
+       private static final String BIFF8_LAST_ROW = String.valueOf(0x10000);
+       private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
 
     private final int _rowIndex;
     private final int _colIndex;
@@ -70,13 +93,13 @@ public class CellReference {
         _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
     }
 
-    /**
-     * Creates a cell reference for the given row and cell.
-     * Assumes these references are relative
-     */
-    public CellReference(int row, int col) {
-       this(row, col, false, false);
+    public CellReference(int pRow, int pCol) {
+       this(pRow, pCol, false, false);
     }
+    public CellReference(int pRow, short pCol) {
+       this(pRow, (int)pCol, false, false);
+    }
+
     public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
         this(null, pRow, pCol, pAbsRow, pAbsCol);
     }
@@ -97,7 +120,7 @@ public class CellReference {
     }
 
     public int getRow(){return _rowIndex;}
-    public int getCol(){return _colIndex;}
+    public short getCol(){return (short) _colIndex;}
     public boolean isRowAbsolute(){return _isRowAbs;}
     public boolean isColAbsolute(){return _isColAbs;}
     /**
@@ -111,27 +134,148 @@ public class CellReference {
     /**
      * takes in a column reference portion of a CellRef and converts it from
      * ALPHA-26 number format to 0-based base 10.
-     * ALPHA-26 goes A to Z, then AA to AZ, BA to BZ, ..., ZA to ZZ, 
-     *  AAA to AAZ, ABA to ABZ, ..., AZA to AZZ, BAA to BAZ etc
      */
     private int convertColStringToNum(String ref) {
-        int lastIx = ref.length()-1;
-        int retval=0;
-        int pos = 0;
+               int lastIx = ref.length()-1;
+               int retval=0;
+               int pos = 0;
+               
+               for (int k = lastIx; k > -1; k--) {
+                       char thechar = ref.charAt(k);
+                       // Character.getNumericValue() returns the values
+                       //  10-35 for the letter A-Z
+                       int shift = (int)Math.pow(26, pos);
+                       retval += (Character.getNumericValue(thechar)-9) * shift;
+                       pos++;
+               }
+               return retval-1;
+    }
 
-        for (int k = lastIx; k > -1; k--) {
-            char thechar = ref.charAt(k);
-            // Character.getNumericValue() returns the values
-            //  10-35 for the letter A-Z
-            int shift = (int)Math.pow(26, pos);
-            retval += (Character.getNumericValue(thechar)-9) * shift;
-            pos++;
-        }
-        return retval-1;
+    /**
+     * Classifies an identifier as either a simple (2D) cell reference or a named range name
+     * @return one of the values from <tt>NameType</tt> 
+     */
+    public static int classifyCellReference(String str) {
+       int len = str.length();
+       if (len < 1) {
+               throw new IllegalArgumentException("Empty string not allowed");
+       }
+       char firstChar = str.charAt(0);
+       switch (firstChar) {
+               case ABSOLUTE_REFERENCE_MARKER:
+               case '.':
+               case '_':
+                       break;
+               default:
+                       if (!Character.isLetter(firstChar)) {
+                       throw new IllegalArgumentException("Invalid first char (" + firstChar 
+                                       + ") of cell reference or named range.  Letter expected");
+               }
+       }
+       if (!Character.isDigit(str.charAt(len-1))) {
+               // no digits at end of str
+               return validateNamedRangeName(str);
+       }
+       Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
+       if (!cellRefPatternMatcher.matches()) {
+               return validateNamedRangeName(str);
+       }
+       String lettersGroup = cellRefPatternMatcher.group(1);
+       String digitsGroup = cellRefPatternMatcher.group(2);
+       if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
+               // valid cell reference
+               return NameType.CELL;
+       }
+       // If str looks like a cell reference, but is out of (row/col) range, it is a valid
+       // named range name
+       // This behaviour is a little weird.  For example, "IW123" is a valid named range name
+       // because the column "IW" is beyond the maximum "IV".  Note - this behaviour is version
+       // dependent.  In Excel 2007, "IW123" is not a valid named range name.
+       if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
+               // Of course, named range names cannot have '$'
+               return NameType.BAD_CELL_OR_NAMED_RANGE;
+       }
+       return NameType.NAMED_RANGE;
     }
 
+    private static int validateNamedRangeName(String str) {
+               if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) {
+                       return NameType.BAD_CELL_OR_NAMED_RANGE;
+               }
+               return NameType.NAMED_RANGE;
+               
+       }
+    
+    
+       /**
+        * Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can be 
+        * interpreted as a cell reference.  Names of that form can be also used for sheets and/or
+        * named ranges, and in those circumstances, the question of whether the potential cell 
+        * reference is valid (in range) becomes important.
+        * <p/>
+        * Note - that the maximum sheet size varies across Excel versions:
+        * <p/>
+        * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
+        *                 summary="Notable cases.">
+        *   <tr><th>Version&nbsp;&nbsp;</th><th>File Format&nbsp;&nbsp;</th>
+        *      <th>Last Column&nbsp;&nbsp;</th><th>Last Row</th></tr>
+        *   <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr>
+        *   <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr>
+        * </table></blockquote>
+        * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for
+        * this method:
+        * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
+        *                 summary="Notable cases.">
+        *   <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
+        *      <th>Result&nbsp;</th></tr>
+        *   <tr><td>"A", "1"</td><td>true</td></tr>
+        *   <tr><td>"a", "111"</td><td>true</td></tr>
+        *   <tr><td>"A", "65536"</td><td>true</td></tr>
+        *   <tr><td>"A", "65537"</td><td>false</td></tr>
+        *   <tr><td>"iv", "1"</td><td>true</td></tr>
+        *   <tr><td>"IW", "1"</td><td>false</td></tr>
+        *   <tr><td>"AAA", "1"</td><td>false</td></tr>
+        *   <tr><td>"a", "111"</td><td>true</td></tr>
+        *   <tr><td>"Sheet", "1"</td><td>false</td></tr>
+        * </table></blockquote>
+        * 
+        * @param colStr a string of only letter characters
+        * @param rowStr a string of only digit characters
+        * @return <code>true</code> if the row and col parameters are within range of a BIFF8 spreadsheet.
+        */
+       public static boolean cellReferenceIsWithinRange(String colStr, String rowStr) {
+               int numberOfLetters = colStr.length();
+               if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
+                       // "Sheet1" case etc
+                       return false; // that was easy
+               }
+               int nDigits = rowStr.length();
+               if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
+                       return false; 
+               }
+               if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
+                       if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
+                               return false;
+                       }
+               } else {
+                       // apparent column name has less chars than max
+                       // no need to check range
+               }
+               
+               if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
+                       // ASCII comparison is valid if digit count is same
+                       if(rowStr.compareTo(BIFF8_LAST_ROW) > 0) {
+                               return false;
+                       }
+               } else {
+                       // apparent row has less chars than max
+                       // no need to check range
+               }
+               
+               return true;
+       }
 
-    /**
+       /**
      * Separates the row from the columns and returns an array of three Strings.  The first element
      * is the sheet name. Only the first element may be null.  The second element in is the column 
      * name still in ALPHA-26 number format.  The third element is the row.
@@ -212,24 +356,24 @@ public class CellReference {
      * eg column #3 -> D
      */
     protected static String convertNumToColString(int col) {
-       // Excel counts column A as the 1st column, we
-       //  treat it as the 0th one
-        int excelColNum = col + 1;
-        
-        String colRef = "";
-        int colRemain = excelColNum;
-        
-        while(colRemain > 0) {
-               int thisPart = colRemain % 26;
-               if(thisPart == 0) { thisPart = 26; }
-               colRemain = (colRemain - thisPart) / 26;
-               
-               // The letter A is at 65
-               char colChar = (char)(thisPart+64);
-               colRef = colChar + colRef; 
-        }
-
-        return colRef;
+               // Excel counts column A as the 1st column, we
+               //  treat it as the 0th one
+               int excelColNum = col + 1;
+               
+               String colRef = "";
+               int colRemain = excelColNum;
+               
+               while(colRemain > 0) {
+                       int thisPart = colRemain % 26;
+                       if(thisPart == 0) { thisPart = 26; }
+                       colRemain = (colRemain - thisPart) / 26;
+                       
+                       // The letter A is at 65
+                       char colChar = (char)(thisPart+64);
+                       colRef = colChar + colRef;
+               }
+               
+               return colRef;
     }
 
     /**
@@ -260,21 +404,21 @@ public class CellReference {
         return sb.toString();
     }
     
-    /**
-     * Returns the three parts of the cell reference, the
-     *  Sheet name (or null if none supplied), the 1 based
-     *  row number, and the A based column letter.
-     * This will not include any markers for absolute
-     *  references, so use {@link #formatAsString()}
-     *  to properly turn references into strings. 
-     */
-    public String[] getCellRefParts() {
-       return new String[] {
-               _sheetName,
-               Integer.toString(_rowIndex+1),
-               convertNumToColString(_colIndex)
-       };
-    }
+       /**
+        * Returns the three parts of the cell reference, the
+        *  Sheet name (or null if none supplied), the 1 based
+        *  row number, and the A based column letter.
+        * This will not include any markers for absolute
+        *  references, so use {@link #formatAsString()}
+        *  to properly turn references into strings. 
+        */
+       public String[] getCellRefParts() {
+               return new String[] {
+                               _sheetName,
+                               Integer.toString(_rowIndex+1),
+                               convertNumToColString(_colIndex)
+               };
+       }
 
     /**
      * Appends cell reference with '$' markers for absolute values as required.
index 0a4fa66af7d58ce8922988cd5e7aa40556a32c95..ff15491d90400611c2b39ba49f1e0e91aed9075a 100644 (file)
@@ -22,6 +22,7 @@ import junit.framework.TestSuite;
 
 import org.apache.poi.hssf.eventmodel.TestEventRecordFactory;
 import org.apache.poi.hssf.eventmodel.TestModelFactory;
+import org.apache.poi.hssf.eventusermodel.AllEventUserModelTests;
 import org.apache.poi.hssf.model.AllModelTests;
 import org.apache.poi.hssf.record.AllRecordTests;
 import org.apache.poi.hssf.usermodel.AllUserModelTests;
@@ -48,6 +49,7 @@ public final class HSSFTests {
         TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
         // $JUnit-BEGIN$
 
+        suite.addTest(AllEventUserModelTests.suite());
         suite.addTest(AllModelTests.suite());
         suite.addTest(AllUserModelTests.suite());
         suite.addTest(AllRecordTests.suite());
diff --git a/src/testcases/org/apache/poi/hssf/data/45365.xls b/src/testcases/org/apache/poi/hssf/data/45365.xls
new file mode 100644 (file)
index 0000000..fbf1d97
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45365.xls differ
index f23821117e96c2c5e9649a8c5227748bb519ae5a..17aa9fd7108c2f6cf471f36780a549725365e6c4 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/testRVA.xls and b/src/testcases/org/apache/poi/hssf/data/testRVA.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java b/src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java
new file mode 100644 (file)
index 0000000..41e3b86
--- /dev/null
@@ -0,0 +1,38 @@
+/* ====================================================================
+   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.eventusermodel;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all tests for <tt>org.apache.poi.hssf.eventusermodel</tt>.
+ * 
+ * @author Josh Micich
+ */
+public class AllEventUserModelTests {
+       
+       public static Test suite() {
+               TestSuite result = new TestSuite(AllEventUserModelTests.class.getName());
+               result.addTestSuite(TestEventWorkbookBuilder.class);
+               result.addTestSuite(TestFormatTrackingHSSFListener.class);
+               result.addTestSuite(TestHSSFEventFactory.class);
+               result.addTestSuite(TestMissingRecordAwareHSSFListener.class);
+               return result;
+       }
+}
index adf084331a4f2f5c072a850587ef4833fcc888a5..215732deb5bd6b252bb3ee76df562a041d861253 100644 (file)
@@ -134,7 +134,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
                fr = (FormulaRecord)mockListen._frecs.get(5);
                assertEquals(6, fr.getRow());
                assertEquals(0, fr.getColumn());
-               assertEquals("SUM('Sh3'!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                
                // Now, load via Usermodel and re-check
@@ -142,7 +142,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
                POIFSFileSystem fs = new POIFSFileSystem(is);
                HSSFWorkbook wb = new HSSFWorkbook(fs);
                assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
-               assertEquals("SUM('Sh3'!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
+               assertEquals("SUM(Sh3!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
        }
        
        private static final class MockHSSFListener implements HSSFListener {
index e52a3bc961af2b3a85adfb951496240b59f91c62..60f5d2ca72d7c227cbff61d48d21d7587526ffca 100644 (file)
@@ -24,6 +24,9 @@ import java.util.List;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.NumberRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 /**
@@ -31,16 +34,17 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  */
 public final class TestFormatTrackingHSSFListener extends TestCase {
        private FormatTrackingHSSFListener listener;
-       
-       public void setUp() {
+       private MockHSSFListener mockListen;
+
+       private void processFile(String filename) throws Exception {
                HSSFRequest req = new HSSFRequest();
-               MockHSSFListener mockListen = new MockHSSFListener();
+               mockListen = new MockHSSFListener();
                listener = new FormatTrackingHSSFListener(mockListen);
                req.addListenerForAllRecords(listener);
                
                HSSFEventFactory factory = new HSSFEventFactory();
                try {
-                       InputStream is = HSSFTestDataSamples.openSampleFileStream("MissingBits.xls");
+                       InputStream is = HSSFTestDataSamples.openSampleFileStream(filename);
                        POIFSFileSystem fs = new POIFSFileSystem(is);
                        factory.processWorkbookEvents(req, fs);
                } catch (IOException e) {
@@ -49,11 +53,42 @@ public final class TestFormatTrackingHSSFListener extends TestCase {
        } 
        
        public void testFormats() throws Exception {
+               processFile("MissingBits.xls");
+               
                assertEquals("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)", listener.getFormatString(41));
                assertEquals("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)", listener.getFormatString(42));
                assertEquals("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)", listener.getFormatString(43));
        }
        
+       /**
+        * Ensure that all number and formula records can be
+        *  turned into strings without problems
+        */
+       public void testTurnToString() throws Exception {
+               processFile("45365.xls");
+               
+               for(int i=0; i<mockListen._records.size(); i++) {
+                       Record r = (Record)mockListen._records.get(i);
+                       CellValueRecordInterface cvr = null;
+                       
+                       if(r instanceof NumberRecord) {
+                               cvr = (CellValueRecordInterface)r;
+                       }
+                       if(r instanceof FormulaRecord) {
+                               cvr = (CellValueRecordInterface)r;
+                       }
+                       
+                       if(cvr != null) {
+                               // Should always give us a string 
+                               String s = listener.formatNumberDateCell(cvr);
+                               assertNotNull(s);
+                               assertTrue(s.length() > 0);
+                       }
+               }
+               
+               // TODO - test some specific format strings
+       }
+       
        private static final class MockHSSFListener implements HSSFListener {
                public MockHSSFListener() {}
                private final List _records = new ArrayList();
index 115a7e081b35dc05f5122b1b580081f5da98881a..a21850887ca7280187f0b02f8b621d5383ffef4c 100644 (file)
@@ -46,6 +46,7 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
 import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
 import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
 import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@@ -791,4 +792,37 @@ public final class TestFormulaParser extends TestCase {
                assertEquals("ERROR.TYPE", funcPtg.getName());
        }
        
+       public void testNamedRangeThatLooksLikeCell() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFName name = wb.createName();
+               name.setReference("Sheet1!B1");
+               name.setNameName("pfy1");
+
+               Ptg[] ptgs;
+               try {
+                       ptgs = FormulaParser.parse("count(pfy1)", wb);
+               } catch (IllegalArgumentException e) {
+                       if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
+                               throw new AssertionFailedError("Identified bug 45354");
+                       }
+                       throw e;
+               }
+               assertEquals(2, ptgs.length);
+               assertEquals(NamePtg.class, ptgs[0].getClass());
+
+               HSSFCell cell = sheet.createRow(0).createCell((short)0);
+               cell.setCellFormula("count(pfy1)");
+               assertEquals("COUNT(pfy1)", cell.getCellFormula());
+               try {
+                       cell.setCellFormula("count(pf1)");
+                       throw new AssertionFailedError("Expected formula parse execption");
+               } catch (FormulaParseException e) {
+                       if (!e.getMessage().equals("Specified named range 'pf1' does not exist in the current workbook.")) {
+                               throw e;
+                       }
+               }
+               cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
+               
+       }
 }
index 90a5d8b13fd69fad082263fab7b0594d4fce730c..47cfb574c1dd29ee093f89db321696420d081cc6 100644 (file)
@@ -57,6 +57,31 @@ public final class TestOperandClassTransformer extends TestCase {
                confirmFuncClass(ptgs, 2, "INDEX", Ptg.CLASS_VALUE);
        }
 
+       /**
+        * Even though count expects args of type R, because A1 is a direct operand of a
+        * value operator it must get type V
+        */
+       public void testDirectOperandOfValueOperator() {
+               String formula = "COUNT(A1*1)";
+               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               if (ptgs[0].getPtgClass() == Ptg.CLASS_REF) {
+                       throw new AssertionFailedError("Identified bug 45348");
+               }
+
+               confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
+               confirmTokenClass(ptgs, 3, Ptg.CLASS_VALUE);
+       }
+       
+       /**
+        * A cell ref passed to a function expecting type V should be converted to type V
+        */
+       public void testRtoV() {
+
+               String formula = "lookup(A1, A3:A52, B3:B52)";
+               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
+       }
+       
        public void testComplexIRR_bug45041() {
                String formula = "(1+IRR(SUMIF(A:A,ROW(INDIRECT(MIN(A:A)&\":\"&MAX(A:A))),B:B),0))^365-1";
                Ptg[] ptgs = FormulaParser.parse(formula, null);
@@ -89,8 +114,11 @@ public final class TestOperandClassTransformer extends TestCase {
 
        private void confirmTokenClass(Ptg[] ptgs, int i, byte operandClass) {
                Ptg ptg = ptgs[i];
+               if (ptg.isBaseToken()) {
+                       throw new AssertionFailedError("ptg[" + i + "] is a base token");
+               }
                if (operandClass != ptg.getPtgClass()) {
-                       throw new AssertionFailedError("Wrong operand class for function ptg ("
+                       throw new AssertionFailedError("Wrong operand class for ptg ("
                                        + ptg.toString() + "). Expected " + getOperandClassName(operandClass)
                                        + " but got " + getOperandClassName(ptg.getPtgClass()));
                }
index 768c429580fd47fba23bcf66cd4b813f075a97fe..369c09583a57777c427985c781c9dac1e5ce8a2b 100644 (file)
@@ -14,7 +14,6 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record.formula;
 
@@ -72,11 +71,14 @@ public final class TestSheetNameFormatter extends TestCase {
                confirmCellNameMatch("aa1", true);
                confirmCellNameMatch("A1A", false);
                confirmCellNameMatch("A1A1", false);
+               confirmCellNameMatch("Sh3", false);
                confirmCellNameMatch("SALES20080101", false); // out of range
        }
        
        private static void confirmCellRange(String text, int numberOfPrefixLetters, boolean expected) {
-               assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(text, numberOfPrefixLetters));
+               String prefix = text.substring(0, numberOfPrefixLetters);
+               String suffix = text.substring(numberOfPrefixLetters);
+               assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(prefix, suffix));
        }
        
        /**
@@ -93,7 +95,7 @@ public final class TestSheetNameFormatter extends TestCase {
                confirmCellRange("AAA1", 3, false);
                confirmCellRange("a111", 1, true);
                confirmCellRange("Sheet1", 6, false);
-        confirmCellRange("iV65536", 2, true);  // max cell in Excel 97-2003
-        confirmCellRange("IW65537", 2, false);
+               confirmCellRange("iV65536", 2, true);  // max cell in Excel 97-2003
+               confirmCellRange("IW65537", 2, false);
        }
 }
index cf48573682e0367e02fe436f9f293a4ce2b06420..523d42ec9efa268377a2105940126cfc0d066709 100644 (file)
@@ -31,8 +31,12 @@ import org.apache.poi.ss.util.Region;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.NameRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
 import org.apache.poi.util.TempFile;
 
@@ -1136,4 +1140,80 @@ public final class TestBugs extends TestCase {
                        )
        );
     }
+    
+    /**
+     * From the mailing list - ensure we can handle a formula
+     *  containing a zip code, eg ="70164"
+     * @throws Exception
+     */
+    public void testZipCodeFormulas() throws Exception {
+       HSSFWorkbook wb = new HSSFWorkbook();
+       HSSFSheet s = wb.createSheet();
+       s.createRow(0);
+       HSSFCell c1 = s.getRow(0).createCell((short)0);
+       HSSFCell c2 = s.getRow(0).createCell((short)1);
+
+       // As number and string
+       c1.setCellFormula("70164");
+       c2.setCellFormula("\"70164\"");
+       
+       // Check the formulas
+       assertEquals("70164.0", c1.getCellFormula());
+       assertEquals("\"70164\"", c2.getCellFormula());
+       
+       // And check the values - blank
+       assertEquals(0.0, c1.getNumericCellValue(), 0.00001);
+       assertEquals("", c1.getRichStringCellValue().getString());
+       assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
+       assertEquals("", c2.getRichStringCellValue().getString());
+       
+       // Now evaluate
+       HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb);
+       eval.setCurrentRow(s.getRow(0));
+       eval.evaluateFormulaCell(c1);
+       eval.evaluateFormulaCell(c2);
+       
+       // Check
+       assertEquals(70164.0, c1.getNumericCellValue(), 0.00001);
+       assertEquals("", c1.getRichStringCellValue().getString());
+       assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
+       
+       // TODO - why isn't this working?
+//     assertEquals("70164", c2.getRichStringCellValue().getString());
+  
+       
+       // Write and read
+       HSSFWorkbook nwb = writeOutAndReadBack(wb);
+       HSSFSheet ns = nwb.getSheetAt(0);
+       HSSFCell nc1 = ns.getRow(0).getCell((short)0);
+       HSSFCell nc2 = ns.getRow(0).getCell((short)1);
+       
+       // Re-check
+       assertEquals(70164.0, nc1.getNumericCellValue(), 0.00001);
+       assertEquals("", nc1.getRichStringCellValue().getString());
+       assertEquals(0.0, nc2.getNumericCellValue(), 0.00001);
+       assertEquals("70164", nc2.getRichStringCellValue().getString());
+       
+       // Now check record level stuff too
+       ns.getSheet().setLoc(0);
+       int fn = 0;
+       CellValueRecordInterface cvr;
+       while((cvr = ns.getSheet().getNextValueRecord()) != null) {
+               if(cvr instanceof FormulaRecordAggregate) {
+                       FormulaRecordAggregate fr = (FormulaRecordAggregate)cvr;
+                       
+                       if(fn == 0) {
+                               assertEquals(70164.0, fr.getFormulaRecord().getValue(), 0.0001);
+                               assertNull(fr.getStringRecord());
+                       } else {
+                               assertEquals(0.0, fr.getFormulaRecord().getValue(), 0.0001);
+                               assertNotNull(fr.getStringRecord());
+                               assertEquals("70164", fr.getStringRecord().getString());
+                       }
+                       
+                       fn++;
+               }
+       }
+       assertEquals(2, fn);
+    }
 }
index 3b154b2562541df18d51aacfdef69676fd36c990..90971b3c03235b2d44c4efb950b3e4df71529511 100644 (file)
@@ -169,6 +169,20 @@ public final class TestHSSFSheet extends TestCase {
                sheet.removeRow(row);
        }
 
+       public void testRemoveZeroRow() {
+               HSSFWorkbook workbook = new HSSFWorkbook();
+               HSSFSheet sheet = workbook.createSheet("Sheet1");
+               HSSFRow row = sheet.createRow(0);
+               try {
+                       sheet.removeRow(row);
+               } catch (IllegalArgumentException e) {
+                       if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
+                               throw new AssertionFailedError("Identified bug 45367");
+                       }
+                       throw e;
+               }
+       }
+
        public void testCloneSheet() {
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet sheet = workbook.createSheet("Test Clone");
index 1f7aa5f2591922828baff1a0eb76d2054fb287b9..ec88e5c439e446670ede789f67c04cc9f1841d53 100755 (executable)
@@ -14,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.hssf.util;
 
 import junit.framework.Test;
@@ -28,12 +28,13 @@ import junit.framework.TestSuite;
 public class AllHSSFUtilTests {
        
        public static Test suite() {
-               TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.util");
-        result.addTestSuite(TestAreaReference.class);
-        result.addTestSuite(TestCellReference.class);
-        result.addTestSuite(TestRangeAddress.class);
-        result.addTestSuite(TestRKUtil.class);
-        result.addTestSuite(TestSheetReferences.class);
+               TestSuite result = new TestSuite(AllHSSFUtilTests.class.getName());
+               result.addTestSuite(TestAreaReference.class);
+               result.addTestSuite(TestCellReference.class);
+               result.addTestSuite(TestHSSFColor.class);
+               result.addTestSuite(TestRangeAddress.class);
+               result.addTestSuite(TestRKUtil.class);
+               result.addTestSuite(TestSheetReferences.class);
                return result;
        }
 }
index 648fb9a8e32e44ec5c58b36356c54a26ebf041f1..8ec8f994674576a37ee780245c3da12322059220 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.poi.hssf.util;
 
 import junit.framework.TestCase;
 
+import org.apache.poi.hssf.util.CellReference.NameType;
+
 
 public final class TestCellReference extends TestCase {
     
@@ -75,7 +77,6 @@ public final class TestCellReference extends TestCase {
         confirmCell(cf, "Amazing!", 0, 0, false, false, "'Amazing!'!A1");
     }
 
-    
     /* package */ static void confirmCell(CellReference cf, String expSheetName, int expRow, 
             int expCol, boolean expIsRowAbs, boolean expIsColAbs, String expText) {
         
@@ -87,8 +88,22 @@ public final class TestCellReference extends TestCase {
         assertEquals("text is wrong", expText, cf.formatAsString());
     }
 
-    public static void main(String [] args) {
-        System.out.println("Testing org.apache.poi.hssf.util.TestCellReference");
-        junit.textui.TestRunner.run(TestCellReference.class);
+    public void testClassifyCellReference() {
+        confirmNameType("a1", NameType.CELL);
+        confirmNameType("pfy1", NameType.NAMED_RANGE);
+        confirmNameType("pf1", NameType.NAMED_RANGE); // (col) out of cell range
+        confirmNameType("fp1", NameType.CELL);
+        confirmNameType("pf$1", NameType.BAD_CELL_OR_NAMED_RANGE);
+        confirmNameType("_A1", NameType.NAMED_RANGE);
+        confirmNameType("A_1", NameType.NAMED_RANGE);
+        confirmNameType("A1_", NameType.NAMED_RANGE);
+        confirmNameType(".A1", NameType.BAD_CELL_OR_NAMED_RANGE);
+        confirmNameType("A.1", NameType.NAMED_RANGE);
+        confirmNameType("A1.", NameType.NAMED_RANGE);
+    }
+    
+    private void confirmNameType(String ref, int expectedResult) {
+        int actualResult = CellReference.classifyCellReference(ref);
+        assertEquals(expectedResult, actualResult);
     }
 }
index 42571eb3231badf770160230f3084ad64fd4de5e..d93f5d7b08f925557c876dd0cb92ef9fdeed7eec 100644 (file)
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.hssf.util;
 
 import java.util.Hashtable;
 
 import junit.framework.TestCase;
-
+/**
+ * @author Nick Burch
+ */
 public final class TestHSSFColor extends TestCase {
        public void testBasics() {
                assertNotNull(HSSFColor.YELLOW.class);