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

........
  r658322 | nick | 2008-05-20 17:37:15 +0100 (Tue, 20 May 2008) | 1 line

  Fix bug #44977 - Support for AM/PM in excel date formats
........
  r658336 | nick | 2008-05-20 17:51:49 +0100 (Tue, 20 May 2008) | 1 line

  Test which seems to show that bug #44996 is invalid, but not completely sure
........
  r658349 | nick | 2008-05-20 17:57:20 +0100 (Tue, 20 May 2008) | 1 line

  Patch from bug #45001 - Partial fix for HWPF Range.insertBefore() and Range.delete() with unicode characters
........
  r658350 | nick | 2008-05-20 18:12:08 +0100 (Tue, 20 May 2008) | 1 line

  Put abstract write(OutputStream) method on POIDocument
........
  r658352 | nick | 2008-05-20 18:17:16 +0100 (Tue, 20 May 2008) | 1 line

  Patch from bug #45003 - Support embeded HDGF visio documents
........
  r658833 | josh | 2008-05-21 20:57:40 +0100 (Wed, 21 May 2008) | 1 line

  improved toString and refactored toFormulaString on Area(3D)Ptg
........
  r658984 | josh | 2008-05-22 04:00:29 +0100 (Thu, 22 May 2008) | 1 line

  Fixed compiler errors.  Other improvements for type safety and immutability.
........
  r658986 | josh | 2008-05-22 04:26:25 +0100 (Thu, 22 May 2008) | 1 line

  Follow on from bug 44675 - regenerated functionMetadata.txt from new ooo excelfileformat.odt
........
  r659067 | nick | 2008-05-22 10:51:44 +0100 (Thu, 22 May 2008) | 1 line

  Example for finding hslf sounds from Yegor
........
  r659403 | josh | 2008-05-23 04:56:31 +0100 (Fri, 23 May 2008) | 1 line

  Fix for 45066 - sheet encoding size mismatch problems
........
  r659429 | josh | 2008-05-23 06:28:54 +0100 (Fri, 23 May 2008) | 1 line

  Fix for bug 45046 - allowed DEFINEDNAME records without EXTERNALBOOK records
........
  r659452 | josh | 2008-05-23 07:43:51 +0100 (Fri, 23 May 2008) | 1 line

  Bug 45041 - improved FormulaParser parse error messages
........
  r659455 | josh | 2008-05-23 07:54:46 +0100 (Fri, 23 May 2008) | 1 line

  Bug 45025 - improved FormulaParser parse error messages (r659452 had wrong bug number)
........
  r659462 | josh | 2008-05-23 08:42:14 +0100 (Fri, 23 May 2008) | 1 line

  Marked out test failure which was fixed by patch for bug 39903
........
  r659478 | josh | 2008-05-23 09:55:48 +0100 (Fri, 23 May 2008) | 1 line

  Fix for bug 35925 - Missing HSSFColor.TAN from HashTables returned by getIndexHash() and getTripletHash()
........

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

36 files changed:
build.xml
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/examples/src/org/apache/poi/hslf/usermodel/examples/SoundFinder.java [new file with mode: 0644]
src/java/org/apache/poi/POIDocument.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/LinkTable.java
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/DBCellRecord.java
src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/util/AreaReference.java
src/java/org/apache/poi/hssf/util/HSSFColor.java
src/java/org/apache/poi/ss/usermodel/DateUtil.java
src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt
src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt
src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java
src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/model/TestSheet.java
src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java
src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java

index c58f06ae2eed2854910bea1ac2d94229332fbe0f..121cbbd2d19f3bb281d40903948cc752a8b365e1 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -245,6 +245,7 @@ under the License.
     <path id="examples.classpath">
         <path refid="main.classpath"/>
         <pathelement location="${main.output.dir}"/>
+        <pathelement location="${scratchpad.output.dir}"/>
     </path>
 
 
index b84dec3344dd10361b54ede2a166a38fa1894dd6..fc755ea79ab8c54f14d04944226c407576d69a35 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-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">45025 - improved FormulaParser parse error messages</action>
+           <action dev="POI-DEVELOPERS" type="add">45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable</action>
+           <action dev="POI-DEVELOPERS" type="add">45066 - fixed sheet encoding size mismatch problems</action>
+           <action dev="POI-DEVELOPERS" type="add">45003 - Support embeded HDGF visio documents</action>
+           <action dev="POI-DEVELOPERS" type="fix">45001 - Partial fix for HWPF Range.insertBefore() and Range.delete() with unicode characters</action>
+           <action dev="POI-DEVELOPERS" type="fix">44977 - Support for AM/PM in excel date formats</action>
            <action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
            <action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
            <action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
index 0b7f0140d4e4622982ab416c96bb7b19577de2d4..ba2c99ec68d6f459f073d32f5ab3e0735f615f7c 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-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">45025 - improved FormulaParser parse error messages</action>
+           <action dev="POI-DEVELOPERS" type="add">45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable</action>
+           <action dev="POI-DEVELOPERS" type="add">45066 - fixed sheet encoding size mismatch problems</action>
+           <action dev="POI-DEVELOPERS" type="add">45003 - Support embeded HDGF visio documents</action>
+           <action dev="POI-DEVELOPERS" type="fix">45001 - Partial fix for HWPF Range.insertBefore() and Range.delete() with unicode characters</action>
+           <action dev="POI-DEVELOPERS" type="fix">44977 - Support for AM/PM in excel date formats</action>
            <action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
            <action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
            <action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
diff --git a/src/examples/src/org/apache/poi/hslf/usermodel/examples/SoundFinder.java b/src/examples/src/org/apache/poi/hslf/usermodel/examples/SoundFinder.java
new file mode 100644 (file)
index 0000000..800a495
--- /dev/null
@@ -0,0 +1,80 @@
+/* ====================================================================
+   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.hslf.usermodel.examples;
+import org.apache.poi.ddf.*;
+import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.record.InteractiveInfo;
+import org.apache.poi.hslf.record.InteractiveInfoAtom;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.usermodel.*;
+import java.io.FileInputStream;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * For each slide iterate over shapes and found associated sound data.
+ *
+ * @author Yegor Kozlov
+ */
+public class SoundFinder {
+    public static void main(String[] args) throws Exception {
+        SlideShow ppt = new SlideShow(new FileInputStream(args[0]));
+        SoundData[] sounds = ppt.getSoundData();
+
+        Slide[] slide = ppt.getSlides();
+        for (int i = 0; i < slide.length; i++) {
+            Shape[] shape = slide[i].getShapes();
+            for (int j = 0; j < shape.length; j++) {
+                int soundRef = getSoundReference(shape[j]);
+                if(soundRef != -1) {
+                    System.out.println("Slide["+i+"], shape["+j+"], soundRef: "+soundRef);
+                    System.out.println("  " + sounds[soundRef].getSoundName());
+                    System.out.println("  " + sounds[soundRef].getSoundType());
+                }
+            }
+        }
+    }
+
+    /**
+     * Check if a given shape is associated with a sound.
+     * @return 0-based reference to a sound in the sound collection
+     * or -1 if the shape is not associated with a sound
+     */
+    protected static int getSoundReference(Shape shape){
+        int soundRef = -1;
+        //dive into the shape container and search for InteractiveInfoAtom
+        EscherContainerRecord spContainer = shape.getSpContainer();
+        List spchild = spContainer.getChildRecords();
+        for (Iterator it = spchild.iterator(); it.hasNext();) {
+            EscherRecord obj = (EscherRecord) it.next();
+            if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {
+                byte[] data = obj.serialize();
+                Record[] records = Record.findChildRecords(data, 8,
+data.length - 8);
+                for (int j = 0; j < records.length; j++) {
+                    if (records[j] instanceof InteractiveInfo) {
+                        InteractiveInfoAtom info = ((InteractiveInfo)records[j]).getInteractiveInfoAtom();
+                        if (info.getAction() == InteractiveInfoAtom.ACTION_MEDIA) {
+                            soundRef = info.getSoundRef();
+                        }
+                    }
+                }
+            }
+        }
+        return soundRef;
+    }
+}
index 01e50231ce0df556b07fb471e7bd508406190449..4d7e50c0fb0c292a0ce281c3a3a6733bfff5482f 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.Iterator;
 import java.util.List;
 
@@ -191,6 +192,11 @@ public abstract class POIDocument {
                        System.err.println("Couldn't write property set with name " + name + " as not supported by HPSF yet");
                }
        }
+       
+       /**
+        * Writes the document out to the specified output stream
+        */
+       public abstract void write(OutputStream out) throws IOException;
 
        /**
         * Copies nodes from one POIFS to the other minus the excepts
index 39a79bb76d1551e9f09d504905cf31ab0eed5dd2..085a140ad9af226ee44d7b6694447b6ffb2505ac 100644 (file)
@@ -55,7 +55,7 @@ public final class FormulaParser {
      */
     static final class FormulaParseException extends RuntimeException {
         // This class was given package scope until it would become clear that it is useful to
-        // general client code. 
+        // general client code.
         public FormulaParseException(String msg) {
             super(msg);
         }
@@ -127,42 +127,34 @@ public final class FormulaParser {
             // Just return if so and reset 'look' to something to keep
             // SkipWhitespace from spinning
             look = (char)0;
-        }    
+        }
         pointer++;
         //System.out.println("Got char: "+ look);
     }
 
     /** Report What Was Expected */
     private RuntimeException expected(String s) {
-        String msg = "Parse error near char " + (pointer-1) + "'" + look + "'" 
+        String msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
             + " in specified formula '" + formulaString + "'. Expected "
             + s;
         return new FormulaParseException(msg);
     }
 
-
-
     /** Recognize an Alpha Character */
     private boolean IsAlpha(char c) {
         return Character.isLetter(c) || c == '$' || c=='_';
     }
 
-
-
     /** Recognize a Decimal Digit */
     private boolean IsDigit(char c) {
-        //System.out.println("Checking digit for"+c);
         return Character.isDigit(c);
     }
 
-
-
     /** Recognize an Alphanumeric */
     private boolean  IsAlNum(char c) {
         return  (IsAlpha(c) || IsDigit(c));
     }
 
-
     /** Recognize White Space */
     private boolean IsWhite( char c) {
         return  (c ==' ' || c== TAB);
@@ -178,7 +170,7 @@ public final class FormulaParser {
     /**
      *  Consumes the next input character if it is equal to the one specified otherwise throws an
      *  unchecked exception. This method does <b>not</b> consume whitespace (before or after the
-     *  matched character). 
+     *  matched character).
      */
     private void Match(char x) {
         if (look != x) {
@@ -218,7 +210,6 @@ public final class FormulaParser {
         return Token.toString();
     }
 
-
     /** Get a Number */
     private String GetNum() {
         StringBuffer value = new StringBuffer();
@@ -281,18 +272,18 @@ public final class FormulaParser {
         // This can be either a cell ref or a named range
         // Try to spot which it is
         boolean cellRef = CELL_REFERENCE_PATTERN.matcher(name).matches();
+
         if (cellRef) {
             return new ReferencePtg(name);
         }
 
         for(int i = 0; i < book.getNumberOfNames(); i++) {
             // named range name matching is case insensitive
-               if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
+            if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
                 return new NamePtg(name, book);
             }
         }
-        throw new FormulaParseException("Found reference to named range \"" 
+        throw new FormulaParseException("Found reference to named range \""
                     + name + "\", but that named range wasn't defined!");
     }
 
@@ -307,19 +298,19 @@ public final class FormulaParser {
     /**
      * 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
-     * Excel function, and has not been encountered before. 
-     * 
-     * @param name case preserved function name (as it was entered/appeared in the formula). 
+     * Excel function, and has not been encountered before.
+     *
+     * @param name case preserved function name (as it was entered/appeared in the formula).
      */
     private Ptg function(String name) {
         int numArgs =0 ;
-        // Note regarding parameter - 
+        // Note regarding parameter -
         if(!AbstractFunctionPtg.isInternalFunctionName(name)) {
             // external functions get a Name token which points to a defined name record
             NamePtg nameToken = new NamePtg(name, this.book);
-            
+
             // in the token tree, the name is more or less the first argument
-            numArgs++;  
+            numArgs++;
             tokens.add(nameToken);
         }
         //average 2 args per function
@@ -477,26 +468,25 @@ public final class FormulaParser {
     private static boolean isArgumentDelimiter(char ch) {
         return ch ==  ',' || ch == ')';
     }
-    
+
     /** get arguments to a function */
     private int Arguments(List argumentPointers) {
         SkipWhite();
         if(look == ')') {
             return 0;
         }
-        
+
         boolean missedPrevArg = true;
-        
         int numArgs = 0;
-        while(true) {
+        while (true) {
             SkipWhite();
-            if(isArgumentDelimiter(look)) {
-                if(missedPrevArg) {
+            if (isArgumentDelimiter(look)) {
+                if (missedPrevArg) {
                     tokens.add(new MissingArgPtg());
                     addArgumentPointer(argumentPointers);
                     numArgs++;
                 }
-                if(look == ')') {
+                if (look == ')') {
                     break;
                 }
                 Match(',');
@@ -507,6 +497,10 @@ public final class FormulaParser {
             addArgumentPointer(argumentPointers);
             numArgs++;
             missedPrevArg = false;
+            SkipWhite();
+            if (!isArgumentDelimiter(look)) {
+                throw expected("',' or ')'");
+            }
         }
         return numArgs;
     }
@@ -524,7 +518,7 @@ public final class FormulaParser {
             tokens.add(new PowerPtg());
         }
     }
-    
+
     private void percentFactor() {
         tokens.add(parseSimpleFactor());
         while(true) {
@@ -536,8 +530,8 @@ public final class FormulaParser {
             tokens.add(new PercentPtg());
         }
     }
-    
-    
+
+
     /**
      * factors (without ^ or % )
      */
@@ -561,9 +555,6 @@ public final class FormulaParser {
                 return new ParenthesisPtg();
             case '"':
                 return parseStringLiteral();
-            case ',':
-            case ')':
-                return new MissingArgPtg(); // TODO - not quite the right place to recognise a missing arg
         }
         if (IsAlpha(look) || look == '\''){
             return parseIdent();
@@ -707,10 +698,9 @@ public final class FormulaParser {
     }
 
 
-    private StringPtg parseStringLiteral()
-    {
+    private StringPtg parseStringLiteral() {
         Match('"');
-        
+
         StringBuffer token = new StringBuffer();
         while (true) {
             if (look == '"') {
@@ -745,7 +735,7 @@ public final class FormulaParser {
             return; // finished with Term
         }
     }
-    
+
     private void comparisonExpression() {
         concatExpression();
         while (true) {
@@ -787,7 +777,7 @@ public final class FormulaParser {
         }
         return new LessThanPtg();
     }
-    
+
 
     private void concatExpression() {
         additiveExpression();
@@ -801,7 +791,7 @@ public final class FormulaParser {
             tokens.add(new ConcatPtg());
         }
     }
-    
+
 
     /** Parse and Translate an Expression */
     private void additiveExpression() {
@@ -838,8 +828,9 @@ end;
      **/
 
 
-    /** API call to execute the parsing of the formula
-     *
+    /**
+     *  API call to execute the parsing of the formula
+     * @deprecated use Ptg[] FormulaParser.parse(String, HSSFWorkbook) directly
      */
     public void parse() {
         pointer=0;
@@ -847,8 +838,8 @@ end;
         comparisonExpression();
 
         if(pointer <= formulaLength) {
-            String msg = "Unused input [" + formulaString.substring(pointer-1) 
-                + "] after attempting to parse the formula [" + formulaString + "]"; 
+            String msg = "Unused input [" + formulaString.substring(pointer-1)
+                + "] after attempting to parse the formula [" + formulaString + "]";
             throw new FormulaParseException(msg);
         }
     }
@@ -863,11 +854,12 @@ end;
      * a result of the parsing
      */
     public Ptg[] getRPNPtg() {
-     return getRPNPtg(FORMULA_TYPE_CELL);
+        return getRPNPtg(FORMULA_TYPE_CELL);
     }
 
     public Ptg[] getRPNPtg(int formulaType) {
         Node node = createTree();
+        // RVA is for 'operand class': 'reference', 'value', 'array'
         setRootLevelRVA(node, formulaType);
         setParameterRVA(node,formulaType);
         return (Ptg[]) tokens.toArray(new Ptg[0]);
@@ -948,7 +940,7 @@ end;
         }
      }
     /**
-     * Convience method which takes in a list then passes it to the
+     * Convenience method which takes in a list then passes it to the
      *  other toFormulaString signature.
      * @param book   workbook for 3D and named references
      * @param lptgs  list of Ptg, can be null or empty
@@ -963,7 +955,7 @@ end;
         return retval;
     }
     /**
-     * Convience method which takes in a list then passes it to the
+     * Convenience method which takes in a list then passes it to the
      *  other toFormulaString signature. Works on the current
      *  workbook for 3D and named references
      * @param lptgs  list of Ptg, can be null or empty
@@ -1011,7 +1003,7 @@ end;
                     continue;
                     // but if it ever did, care must be taken:
                     // tAttrSpace comes *before* the operand it applies to, which may be consistent
-                    // with how the formula text appears but is against the RPN ordering assumed here 
+                    // with how the formula text appears but is against the RPN ordering assumed here
                 }
                 if (attrPtg.isSemiVolatile()) {
                     // similar to tAttrSpace - RPN is violated
@@ -1038,7 +1030,7 @@ end;
             stack.push(o.toFormulaString(operands));
         }
         if(stack.isEmpty()) {
-            // inspection of the code above reveals that every stack.pop() is followed by a 
+            // inspection of the code above reveals that every stack.pop() is followed by a
             // stack.push(). So this is either an internal error or impossible.
             throw new IllegalStateException("Stack underflow");
         }
index 88c94a61e76f14c9b16a3cbdb04f66174515f6f1..a19971b7d59bc72cad9d56ea81562f18abb673b6 100755 (executable)
@@ -41,7 +41,7 @@ import org.apache.poi.hssf.record.SupBookRecord;
  *
  *  In BIFF8 the Link Table consists of
  *  <ul>
- *  <li>one or more EXTERNALBOOK Blocks<p/>
+ *  <li>zero or more EXTERNALBOOK Blocks<p/>
  *     each consisting of
  *     <ul>
  *     <li>exactly one EXTERNALBOOK (0x01AE) record</li>
@@ -55,7 +55,7 @@ import org.apache.poi.hssf.record.SupBookRecord;
  *     </li>
  *     </ul>
  *  </li>
- *  <li>exactly one EXTERNSHEET (0x0017) record</li>
+ *  <li>zero or one EXTERNSHEET (0x0017) record</li>
  *  <li>zero or more DEFINEDNAME (0x0018) records</li>
  *  </ul>
  *
@@ -63,6 +63,7 @@ import org.apache.poi.hssf.record.SupBookRecord;
  * @author Josh Micich
  */
 final class LinkTable {
+       // TODO make this class into a record aggregate
 
        private static final class CRNBlock {
 
@@ -79,8 +80,8 @@ final class LinkTable {
                        _crns = crns;
                }
                public CRNRecord[] getCrns() {
-            return (CRNRecord[]) _crns.clone();
-        }
+                       return (CRNRecord[]) _crns.clone();
+               }
        }
 
        private static final class ExternalBookBlock {
@@ -136,16 +137,19 @@ final class LinkTable {
                while(rs.peekNextClass() == SupBookRecord.class) {
                   temp.add(new ExternalBookBlock(rs));
                }
-               if(temp.size() < 1) {
-                       throw new RuntimeException("Need at least one EXTERNALBOOK blocks");
-               }
+               
                _externalBookBlocks = new ExternalBookBlock[temp.size()];
                temp.toArray(_externalBookBlocks);
                temp.clear();
-
-               // If link table is present, there is always 1 of ExternSheetRecord
-               Record next = rs.getNext();
-               _externSheetRecord = (ExternSheetRecord)next;
+               
+               if (_externalBookBlocks.length > 0) {
+                       // If any ExternalBookBlock present, there is always 1 of ExternSheetRecord
+                       Record next = rs.getNext();
+                       _externSheetRecord = (ExternSheetRecord) next;
+               } else {
+                       _externSheetRecord = null;
+               }
+               
                _definedNames = new ArrayList();
                // collect zero or more DEFINEDNAMEs id=0x18
                while(rs.peekNextClass() == NameRecord.class) {
@@ -222,7 +226,7 @@ final class LinkTable {
        public void addName(NameRecord name) {
                _definedNames.add(name);
 
-          // TODO - this is messy
+               // TODO - this is messy
                // Not the most efficient way but the other way was causing too many bugs
                int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
                if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
@@ -242,8 +246,8 @@ final class LinkTable {
 
        public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
                if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) {
-            return -1;
-        }
+                       return -1;
+               }
                return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
        }
 
@@ -265,7 +269,7 @@ final class LinkTable {
                        ExternSheetSubRecord esr = _externSheetRecord.getREFRecordAt(i);
 
                        if (esr.getIndexToFirstSupBook() ==  sheetNumber
-                               && esr.getIndexToLastSupBook() == sheetNumber){
+                                       && esr.getIndexToLastSupBook() == sheetNumber){
                                return i;
                        }
                }
index 45090e85cf161bcd22ed837434e0a7a74b1710e4..871ecc100f859c14ce0dbf7407fafde49bac66e9 100644 (file)
@@ -96,8 +96,8 @@ public final class Sheet implements Model {
     protected List                       condFormatting    =     new ArrayList();
 
     /** Add an UncalcedRecord if not true indicating formulas have not been calculated */
-    protected boolean uncalced = false;
-       
+    protected boolean _isUncalced = false;
+    
     public static final byte PANE_LOWER_RIGHT = (byte)0;
     public static final byte PANE_UPPER_RIGHT = (byte)1;
     public static final byte PANE_LOWER_LEFT = (byte)2;
@@ -162,7 +162,7 @@ public final class Sheet implements Model {
                 }
             }
             else if (rec.getSid() == UncalcedRecord.sid) {
-                retval.uncalced = true;
+                retval._isUncalced = true;
             }
             else if (rec.getSid() == DimensionsRecord.sid)
             {
@@ -329,16 +329,8 @@ public final class Sheet implements Model {
             }
         }
         retval.records = records;
-//        if (retval.rows == null)
-//        {
-//            retval.rows = new RowRecordsAggregate();
-//        }
         retval.checkCells();
         retval.checkRows();
-//        if (retval.cells == null)
-//        {
-//            retval.cells = new ValueRecordsAggregate();
-//        }
         if (log.check( POILogger.DEBUG ))
             log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
         return retval;
@@ -816,17 +808,17 @@ public final class Sheet implements Model {
             // Once the rows have been found in the list of records, start
             //  writing out the blocked row information. This includes the DBCell references
             if (record instanceof RowRecordsAggregate) {
-              pos += ((RowRecordsAggregate)record).serialize(pos, data, cells);   // rec.length;
+              pos += ((RowRecordsAggregate)record).serialize(pos, data, cells);
             } else if (record instanceof ValueRecordsAggregate) {
               //Do nothing here. The records were serialized during the RowRecordAggregate block serialization
             } else {
-              pos += record.serialize(pos, data );   // rec.length;
+              pos += record.serialize(pos, data );
             }
 
             // If the BOF record was just serialized then add the IndexRecord
             if (record.getSid() == BOFRecord.sid) {
               // Add an optional UncalcedRecord
-              if (uncalced) {
+              if (_isUncalced) {
                   UncalcedRecord rec = new UncalcedRecord();
                   pos += rec.serialize(pos, data);
               }
@@ -837,31 +829,10 @@ public final class Sheet implements Model {
                 pos += serializeIndexRecord(k, pos, data);
               }
             }
-
-            //// uncomment to test record sizes ////
-//            System.out.println( record.getClass().getName() );
-//            byte[] data2 = new byte[record.getRecordSize()];
-//            record.serialize(0, data2 );   // rec.length;
-//            if (LittleEndian.getUShort(data2, 2) != record.getRecordSize() - 4
-//                    && record instanceof RowRecordsAggregate == false
-//                    && record instanceof ValueRecordsAggregate == false
-//                    && record instanceof EscherAggregate == false)
-//            {
-//                throw new RuntimeException("Blah!!!  Size off by " + ( LittleEndian.getUShort(data2, 2) - record.getRecordSize() - 4) + " records.");
-//            }
-
-//asd:            int len = record.serialize(pos + offset, data );
-
-            /////  DEBUG BEGIN /////
-//asd:            if (len != record.getRecordSize())
-//asd:                throw new IllegalStateException("Record size does not match serialized bytes.  Serialized size = " + len + " but getRecordSize() returns " + record.getRecordSize() + ". Record object is " + record.getClass());
-            /////  DEBUG END /////
-
-//asd:            pos += len;   // rec.length;
-
         }
-        if (log.check( POILogger.DEBUG ))
+        if (log.check( POILogger.DEBUG )) {
             log.log(POILogger.DEBUG, "Sheet.serialize returning ");
+        }
         return pos-offset;
     }
 
@@ -875,10 +846,17 @@ public final class Sheet implements Model {
       for (int j = BOFRecordIndex+1; j < records.size(); j++)
       {
         Record tmpRec = (( Record ) records.get(j));
-        if (tmpRec instanceof RowRecordsAggregate)
-          break;
+        if (tmpRec instanceof UncalcedRecord) {
+            continue;
+        }
+        if (tmpRec instanceof RowRecordsAggregate) {
+            break;
+        }
         sheetRecSize+= tmpRec.getRecordSize();
       }
+      if (_isUncalced) {
+          sheetRecSize += UncalcedRecord.getStaticRecordSize();
+      }
       //Add the references to the DBCells in the IndexRecord (one for each block)
       int blockCount = rows.getRowBlockCount();
       //Calculate the size of this IndexRecord
@@ -2017,31 +1995,33 @@ public final class Sheet implements Model {
     {
         int retval = 0;
 
-        for ( int k = 0; k < records.size(); k++ )
-        {
-            retval += ( (Record) records.get( k ) ).getRecordSize();
+        for ( int k = 0; k < records.size(); k++) {
+            Record record = (Record) records.get(k);
+            if (record instanceof UncalcedRecord) {
+                // skip the UncalcedRecord if present, it's only encoded if the isUncalced flag is set
+                continue;
+            }
+            retval += record.getRecordSize();
         }
-        //Add space for the IndexRecord
         if (rows != null) {
-            final int blocks = rows.getRowBlockCount();
-            retval += IndexRecord.getRecordSizeForBlockCount(blocks);
-
-            //Add space for the DBCell records
-            //Once DBCell per block.
-            //8 bytes per DBCell (non variable section)
-            //2 bytes per row reference
-            retval += (8 * blocks);
-            for (Iterator itr = rows.getIterator(); itr.hasNext();) {
-                RowRecord row = (RowRecord)itr.next();
-                if (cells != null && cells.rowHasCells(row.getRowNumber()))
-                    retval += 2;
+            // Add space for the IndexRecord and DBCell records
+            final int nBlocks = rows.getRowBlockCount();
+            int nRows = 0;
+            if (cells != null) {
+                for (Iterator itr = rows.getIterator(); itr.hasNext();) {
+                    RowRecord row = (RowRecord)itr.next();
+                    if (cells.rowHasCells(row.getRowNumber())) {
+                        nRows++;
+                    }
+                }
             }
+            retval += IndexRecord.getRecordSizeForBlockCount(nBlocks);
+            retval += DBCellRecord.calculateSizeOfRecords(nBlocks, nRows);
         }
         // Add space for UncalcedRecord
-        if (uncalced) {
+        if (_isUncalced) {
             retval += UncalcedRecord.getStaticRecordSize();
         }
-
         return retval;
     }
 
@@ -2518,13 +2498,13 @@ public final class Sheet implements Model {
      * @return whether an uncalced record must be inserted or not at generation
      */
     public boolean getUncalced() {
-        return uncalced;
+        return _isUncalced;
     }
     /**
      * @param uncalced whether an uncalced record must be inserted or not at generation
      */
     public void setUncalced(boolean uncalced) {
-        this.uncalced = uncalced;
+        this._isUncalced = uncalced;
     }
 
     /**
index 08f2263182c873c14ea7a876524d8ad02186b7a9..d87fc2c632dae81716f3008be3370f70a3e50d9f 100644 (file)
@@ -191,12 +191,11 @@ public class Workbook implements Model
                 case ExternSheetRecord.sid :
                     throw new RuntimeException("Extern sheet is part of LinkTable");
                 case NameRecord.sid :
-                    throw new RuntimeException("DEFINEDNAME is part of LinkTable");
                 case SupBookRecord.sid :
+                    // LinkTable can start with either of these
                     if (log.check( POILogger.DEBUG ))
                         log.log(DEBUG, "found SupBook record at " + k);
                     retval.linkTable = new LinkTable(recs, k, retval.records);
-                    //                    retval.records.supbookpos = k;
                     k+=retval.linkTable.getRecordCount() - 1;
                     continue;
                 case FormatRecord.sid :
index caeb333a52fa8db454ff0a2677b1b274a1842716..1da6b82c73c41e8f91764ad9740ade55b07d82d6 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,6 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record;
 
@@ -29,10 +27,7 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height
  * @version 2.0-pre
  */
-
-public class DBCellRecord
-    extends Record
-{
+public final class DBCellRecord extends Record {
     public final static int BLOCK_SIZE = 32;
     public final static short sid = 0xd7;
     private int               field_1_row_offset;
@@ -46,7 +41,6 @@ public class DBCellRecord
      * Constructs a DBCellRecord and sets its fields appropriately
      * @param in the RecordInputstream to read the record from
      */
-
     public DBCellRecord(RecordInputStream in)
     {
         super(in);
@@ -78,7 +72,6 @@ public class DBCellRecord
      *
      * @param offset    offset to the start of the first cell in the next DBCell block
      */
-
     public void setRowOffset(int offset)
     {
         field_1_row_offset = offset;
@@ -108,7 +101,6 @@ public class DBCellRecord
      *
      * @return rowoffset to the start of the first cell in the next DBCell block
      */
-
     public int getRowOffset()
     {
         return field_1_row_offset;
@@ -120,7 +112,6 @@ public class DBCellRecord
      * @param index of the cell offset to retrieve
      * @return celloffset from the celloffset array
      */
-
     public short getCellOffsetAt(int index)
     {
         return field_2_cell_offsets[ index ];
@@ -131,7 +122,6 @@ public class DBCellRecord
      *
      * @return number of cell offsets
      */
-
     public int getNumCellOffsets()
     {
         return field_2_cell_offsets.length;
@@ -175,9 +165,15 @@ public class DBCellRecord
         return 8 + (getNumCellOffsets() * 2);
     }
     
-    /** Returns the size of a DBCellRecord when it needs to reference a certain number of rows*/
-    public static int getRecordSizeForRows(int rows) {
-      return 8 + (rows * 2);
+    /**
+     *  @returns the size of the group of <tt>DBCellRecord</tt>s needed to encode
+     *  the specified number of blocks and rows
+     */
+    public static int calculateSizeOfRecords(int nBlocks, int nRows) {
+        // One DBCell per block.
+        // 8 bytes per DBCell (non variable section)
+        // 2 bytes per row reference
+        return nBlocks * 8 + nRows * 2;
     }
 
     public short getSid()
index 458bd1e5948d0105b7bc8fa8d04c2c8e6c6ca05e..668a251516fd278f30968fed344486ae04c94844 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.poi.util.LittleEndian;
 
 
 /**
- * Title:        Area 3D Ptg - 3D referecnce (Sheet + Area)<P>
+ * Title:        Area 3D Ptg - 3D reference (Sheet + Area)<P>
  * Description:  Defined a area in Extern Sheet. <P>
  * REFERENCE:  <P>
  * @author Libin Roman (Vista Portal LDT. Developer)
@@ -35,7 +35,6 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height (jheight at chariot dot net dot au)
  * @version 1.0-pre
  */
-
 public class Area3DPtg extends Ptg implements AreaI
 {
        public final static byte sid = 0x3b;
@@ -84,23 +83,15 @@ public class Area3DPtg extends Ptg implements AreaI
                  setExternSheetIndex(externalSheetIndex);
        }
 
-       public String toString()
-       {
-               StringBuffer buffer = new StringBuffer();
-
-               buffer.append( "AreaPtg\n" );
-               buffer.append( "Index to Extern Sheet = " + getExternSheetIndex() ).append( "\n" );
-               buffer.append( "firstRow = " + getFirstRow() ).append( "\n" );
-               buffer.append( "lastRow  = " + getLastRow() ).append( "\n" );
-               buffer.append( "firstCol = " + getFirstColumn() ).append( "\n" );
-               buffer.append( "lastCol  = " + getLastColumn() ).append( "\n" );
-               buffer.append( "firstColRel= "
-                               + isFirstRowRelative() ).append( "\n" );
-               buffer.append( "lastColRowRel = "
-                               + isLastRowRelative() ).append( "\n" );
-               buffer.append( "firstColRel   = " + isFirstColRelative() ).append( "\n" );
-               buffer.append( "lastColRel      = " + isLastColRelative() ).append( "\n" );
-               return buffer.toString();
+       public String toString() {
+               StringBuffer sb = new StringBuffer();
+               sb.append(getClass().getName());
+               sb.append(" [");
+               sb.append("sheetIx=").append(getExternSheetIndex());
+               sb.append(" ! ");
+               sb.append(AreaReference.formatAsString(this));
+               sb.append("]");
+               return sb.toString();
        }
 
        public void writeBytes( byte[] array, int offset )
@@ -284,7 +275,7 @@ public class Area3DPtg extends Ptg implements AreaI
                }
                
                // Now the normal area bit
-               retval.append( AreaPtg.toFormulaString(this, book) );
+               retval.append(AreaReference.formatAsString(this));
                
                // All done
                return retval.toString();
@@ -326,6 +317,7 @@ public class Area3DPtg extends Ptg implements AreaI
 
        public int hashCode()
        {
+               // TODO - hashCode seems to be unused
                int result;
                result = (int) field_1_index_extern_sheet;
                result = 29 * result + (int) field_2_first_row;
index c4e7534c7d8c10d2d283ca3639b32d99ac8be861..38cae39f442fc68b4e0d7cb9ae2a20d254ac83f5 100644 (file)
@@ -114,23 +114,13 @@ public class AreaPtg extends Ptg implements AreaI {
       return "AreaPtg";
     }    
 
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer();
-
-        buffer.append(getAreaPtgName());
-        buffer.append("\n");
-        buffer.append("firstRow = " + getFirstRow()).append("\n");
-        buffer.append("lastRow  = " + getLastRow()).append("\n");
-        buffer.append("firstCol = " + getFirstColumn()).append("\n");
-        buffer.append("lastCol  = " + getLastColumn()).append("\n");
-        buffer.append("firstColRowRel= "
-                      + isFirstRowRelative()).append("\n");
-        buffer.append("lastColRowRel = "
-                      + isLastRowRelative()).append("\n");
-        buffer.append("firstColRel   = " + isFirstColRelative()).append("\n");
-        buffer.append("lastColRel    = " + isLastColRelative()).append("\n");
-        return buffer.toString();
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(getClass().getName());
+        sb.append(" [");
+        sb.append(AreaReference.formatAsString(this));
+        sb.append("]");
+        return sb.toString();
     }
 
     public void writeBytes(byte [] array, int offset) {
@@ -307,19 +297,8 @@ public class AreaPtg extends Ptg implements AreaI {
         field_4_last_column = column;
     }
     
-    public String toFormulaString(Workbook book)
-    {
-       return toFormulaString(this, book);
-    }
-    protected static String toFormulaString(AreaI area, Workbook book) {
-       CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative());
-       CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative());
-       
-       if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
-               return (new AreaReference(topLeft, botRight)).formatAsString();
-       } else {
-               return topLeft.formatAsString() + ":" + botRight.formatAsString(); 
-       }
+    public String toFormulaString(Workbook book) {
+        return AreaReference.formatAsString(this);
     }
 
     public byte getDefaultOperandClass() {
index 08ddb9656fc2071247ef55fd9083d5e3c452d59a..32fce2f0992413bd146a402914d8be91d09b4250 100644 (file)
    limitations under the License.
 ==================================================================== */
 
-
-/*
- * HSSFWorkbook.java
- *
- * Created on September 30, 2001, 3:37 PM
- */
 package org.apache.poi.hssf.usermodel;
 
 import java.io.ByteArrayInputStream;
@@ -81,7 +75,6 @@ import org.apache.poi.util.POILogger;
  * @author  Shawn Laubach (slaubach at apache dot org)
  * @version 2.0-pre
  */
-
 public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook
 {
     private static final int DEBUG = POILogger.DEBUG;
@@ -105,7 +98,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * this holds the HSSFSheet objects attached to this workbook
      */
 
-    protected ArrayList sheets;
+    protected List _sheets;
 
     /**
      * this holds the HSSFName objects attached to this workbook
@@ -159,7 +152,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     {
         super(null, null);
         workbook = book;
-        sheets = new ArrayList( INITIAL_CAPACITY );
+        _sheets = new ArrayList( INITIAL_CAPACITY );
         names = new ArrayList( INITIAL_CAPACITY );
     }
 
@@ -250,7 +243,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
            this.directory = null;
         }
 
-        sheets = new ArrayList(INITIAL_CAPACITY);
+        _sheets = new ArrayList(INITIAL_CAPACITY);
         names  = new ArrayList(INITIAL_CAPACITY);
 
         // Grab the data from the workbook stream, however
@@ -280,7 +273,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
             HSSFSheet hsheet = new HSSFSheet(this, sheet);
 
-            sheets.add(hsheet);
+            _sheets.add(hsheet);
 
             // workbook.setSheetName(sheets.size() -1, "Sheet"+sheets.size());
         }
@@ -378,12 +371,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      */
 
     public void setSheetOrder(String sheetname, int pos ) {
-        sheets.add(pos,sheets.remove(getSheetIndex(sheetname)));
+        _sheets.add(pos,_sheets.remove(getSheetIndex(sheetname)));
         workbook.setSheetOrder(sheetname, pos);
     }
 
     private void validateSheetIndex(int index) {
-        int lastSheetIx = sheets.size() - 1;
+        int lastSheetIx = _sheets.size() - 1;
         if (index < 0 || index > lastSheetIx) {
             throw new IllegalArgumentException("Sheet index (" 
                     + index +") is out of range (0.." +    lastSheetIx + ")");
@@ -397,7 +390,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public void setSelectedTab(int index) {
         
         validateSheetIndex(index);
-        int nSheets = sheets.size();
+        int nSheets = _sheets.size();
         for (int i=0; i<nSheets; i++) {
                getSheetAt(i).setSelected(i == index);
         }
@@ -415,7 +408,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         for (int i = 0; i < indexes.length; i++) {
             validateSheetIndex(indexes[i]);
         }
-        int nSheets = sheets.size();
+        int nSheets = _sheets.size();
         for (int i=0; i<nSheets; i++) {
             boolean bSelect = false;
             for (int j = 0; j < indexes.length; j++) {
@@ -437,7 +430,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public void setActiveSheet(int index) {
         
         validateSheetIndex(index);
-        int nSheets = sheets.size();
+        int nSheets = _sheets.size();
         for (int i=0; i<nSheets; i++) {
              getSheetAt(i).setActive(i == index);
         }
@@ -509,19 +502,15 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * set the sheet name.
      * Will throw IllegalArgumentException if the name is greater than 31 chars
      * or contains /\?*[]
-     * @param sheet number (0 based)
+     * @param sheetIx number (0 based)
      */
-    public void setSheetName(int sheet, String name)
+    public void setSheetName(int sheetIx, String name)
     {
-        if (workbook.doesContainsSheetName( name, sheet ))
+        if (workbook.doesContainsSheetName( name, sheetIx )) {
             throw new IllegalArgumentException( "The workbook already contains a sheet with this name" );
-
-        if (sheet > (sheets.size() - 1))
-        {
-            throw new RuntimeException("Sheet out of bounds");
         }
-
-        workbook.setSheetName( sheet, name);
+        validateSheetIndex(sheetIx);
+        workbook.setSheetName(sheetIx, name);
     }
 
 
@@ -533,15 +522,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * or contains /\?*[]
      * @param sheet number (0 based)
      */
-    public void setSheetName( int sheet, String name, short encoding )
+    public void setSheetName(int sheetIx, String name, short encoding)
     {
-        if (workbook.doesContainsSheetName( name, sheet ))
+        if (workbook.doesContainsSheetName( name, sheetIx )) {
             throw new IllegalArgumentException( "The workbook already contains a sheet with this name" );
-
-        if (sheet > (sheets.size() - 1))
-        {
-            throw new RuntimeException("Sheet out of bounds");
         }
+        validateSheetIndex(sheetIx);
 
         switch ( encoding ) {
         case ENCODING_COMPRESSED_UNICODE:
@@ -553,51 +539,39 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
             throw new RuntimeException( "Unsupported encoding" );
         }
 
-        workbook.setSheetName( sheet, name, encoding );
+        workbook.setSheetName( sheetIx, name, encoding );
     }
 
     /**
      * get the sheet name
-     * @param sheet Number
+     * @param sheetIx Number
      * @return Sheet name
      */
-
-    public String getSheetName(int sheet)
+    public String getSheetName(int sheetIx)
     {
-        if (sheet > (sheets.size() - 1))
-        {
-            throw new RuntimeException("Sheet out of bounds");
-        }
-        return workbook.getSheetName(sheet);
+        validateSheetIndex(sheetIx);
+        return workbook.getSheetName(sheetIx);
     }
 
     /**
      * check whether a sheet is hidden
-     * @param sheet Number
+     * @param sheetIx Number
      * @return True if sheet is hidden
      */
-
-    public boolean isSheetHidden(int sheet) {
-        if (sheet > (sheets.size() - 1))
-        {
-            throw new RuntimeException("Sheet out of bounds");
-        }
-        return workbook.isSheetHidden(sheet);
+    public boolean isSheetHidden(int sheetIx) {
+        validateSheetIndex(sheetIx);
+        return workbook.isSheetHidden(sheetIx);
     }
 
     /**
      * Hide or unhide a sheet
      *
-     * @param sheetnum The sheet number
+     * @param sheetIx The sheet index
      * @param hidden True to mark the sheet as hidden, false otherwise
      */
-
-    public void setSheetHidden(int sheet, boolean hidden) {
-        if (sheet > (sheets.size() - 1))
-        {
-            throw new RuntimeException("Sheet out of bounds");
-        }
-        workbook.setSheetHidden(sheet,hidden);
+    public void setSheetHidden(int sheetIx, boolean hidden) {
+        validateSheetIndex(sheetIx);
+        workbook.setSheetHidden(sheetIx, hidden);
     }
 
     /*
@@ -619,12 +593,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     /** Returns the index of the given sheet
      * @param sheet the sheet to look up
-     * @return index of the sheet (0 based)
+     * @return index of the sheet (0 based). <tt>-1</tt> if not found
      */
     public int getSheetIndex(org.apache.poi.ss.usermodel.Sheet sheet)
     {
-        for(int i=0; i<sheets.size(); i++) {
-            if(sheets.get(i) == sheet) {
+        for(int i=0; i<_sheets.size(); i++) {
+            if(_sheets.get(i) == sheet) {
                 return i;
             }
         }
@@ -653,9 +627,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     {
         HSSFSheet sheet = new HSSFSheet(this);
 
-        sheets.add(sheet);
-        workbook.setSheetName(sheets.size() - 1, "Sheet" + (sheets.size() - 1));
-        boolean isOnlySheet = sheets.size() == 1;
+        _sheets.add(sheet);
+        workbook.setSheetName(_sheets.size() - 1, "Sheet" + (_sheets.size() - 1));
+        boolean isOnlySheet = _sheets.size() == 1;
         sheet.setSelected(isOnlySheet);
         sheet.setActive(isOnlySheet);
         return sheet;
@@ -669,13 +643,13 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     public HSSFSheet cloneSheet(int sheetNum) {
         validateSheetIndex(sheetNum);
-        HSSFSheet srcSheet = (HSSFSheet) sheets.get(sheetNum);
+        HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum);
         String srcName = workbook.getSheetName(sheetNum);
         HSSFSheet clonedSheet = srcSheet.cloneSheet(this);
         clonedSheet.setSelected(false);
         clonedSheet.setActive(false);
 
-        sheets.add(clonedSheet);
+        _sheets.add(clonedSheet);
         int i = 1;
         while (true) {
             // Try and find the next sheet name that is unique
@@ -689,7 +663,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
             //If the sheet name is unique, then set it otherwise move on to the next number.
             if (workbook.getSheetIndex(name) == -1) {
-              workbook.setSheetName(sheets.size()-1, name);
+              workbook.setSheetName(_sheets.size()-1, name);
               break;
             }
         }
@@ -710,14 +684,14 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     public HSSFSheet createSheet(String sheetname)
     {
-        if (workbook.doesContainsSheetName( sheetname, sheets.size() ))
+        if (workbook.doesContainsSheetName( sheetname, _sheets.size() ))
             throw new IllegalArgumentException( "The workbook already contains a sheet of this name" );
 
         HSSFSheet sheet = new HSSFSheet(this);
 
-        sheets.add(sheet);
-        workbook.setSheetName(sheets.size() - 1, sheetname);
-        boolean isOnlySheet = sheets.size() == 1;
+        _sheets.add(sheet);
+        workbook.setSheetName(_sheets.size() - 1, sheetname);
+        boolean isOnlySheet = _sheets.size() == 1;
         sheet.setSelected(isOnlySheet);
         sheet.setActive(isOnlySheet);
         return sheet;
@@ -730,13 +704,19 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     public int getNumberOfSheets()
     {
-        return sheets.size();
+        return _sheets.size();
     }
 
     public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
        return workbook.getSheetIndexFromExternSheetIndex(externSheetNumber);
        }
 
+    private HSSFSheet[] getSheets() {
+        HSSFSheet[] result = new HSSFSheet[_sheets.size()];
+        _sheets.toArray(result);
+        return result;
+    }
+
        /**
      * Get the HSSFSheet object at the given index.
      * @param index of the sheet number (0-based physical & logical)
@@ -745,7 +725,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     public HSSFSheet getSheetAt(int index)
     {
-        return (HSSFSheet) sheets.get(index);
+        return (HSSFSheet) _sheets.get(index);
     }
 
     /**
@@ -758,13 +738,13 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     {
         HSSFSheet retval = null;
 
-        for (int k = 0; k < sheets.size(); k++)
+        for (int k = 0; k < _sheets.size(); k++)
         {
             String sheetname = workbook.getSheetName(k);
 
             if (sheetname.equalsIgnoreCase(name))
             {
-                retval = (HSSFSheet) sheets.get(k);
+                retval = (HSSFSheet) _sheets.get(k);
             }
         }
         return retval;
@@ -793,11 +773,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         boolean wasActive = getSheetAt(index).isActive();
         boolean wasSelected = getSheetAt(index).isSelected();
 
-        sheets.remove(index);
+        _sheets.remove(index);
         workbook.removeSheet(index);
 
         // set the remaining active/selected sheet
-        int nSheets = sheets.size();
+        int nSheets = _sheets.size();
         if (nSheets < 1) {
             // nothing more to do if there are no sheets left
             return;
@@ -1173,48 +1153,47 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     public byte[] getBytes()
     {
-        if (log.check( POILogger.DEBUG ))
+        if (log.check( POILogger.DEBUG )) {
             log.log(DEBUG, "HSSFWorkbook.getBytes()");
+        }
+        
+        HSSFSheet[] sheets = getSheets();
+        int nSheets = sheets.length;
 
         // before getting the workbook size we must tell the sheets that
         // serialization is about to occur.
-        for (int k = 0; k < sheets.size(); k++)
-            ((HSSFSheet) sheets.get(k)).getSheet().preSerialize();
-
-        int wbsize = workbook.getSize();
+        for (int i = 0; i < nSheets; i++) {
+            sheets[i].getSheet().preSerialize();
+        }
 
-        // log.debug("REMOVEME: old sizing method "+workbook.serialize().length);
-        // ArrayList sheetbytes = new ArrayList(sheets.size());
-        int totalsize = wbsize;
+        int totalsize = workbook.getSize();
 
-        for (int k = 0; k < sheets.size(); k++)
-        {
+        // pre-calculate all the sheet sizes and set BOF indexes
+        int[] estimatedSheetSizes = new int[nSheets];
+        for (int k = 0; k < nSheets; k++) {
             workbook.setSheetBof(k, totalsize);
-            totalsize += ((HSSFSheet) sheets.get(k)).getSheet().getSize();
+            int sheetSize = sheets[k].getSheet().getSize();
+            estimatedSheetSizes[k] = sheetSize;
+            totalsize += sheetSize;
         }
 
 
-/*        if (totalsize < 4096)
-        {
-            totalsize = 4096;
-        }*/
         byte[] retval = new byte[totalsize];
         int pos = workbook.serialize(0, retval);
 
-        // System.arraycopy(wb, 0, retval, 0, wb.length);
-        for (int k = 0; k < sheets.size(); k++)
-        {
-
-            // byte[] sb = (byte[])sheetbytes.get(k);
-            // System.arraycopy(sb, 0, retval, pos, sb.length);
-            int len = ((HSSFSheet) sheets.get(k)).getSheet().serialize(pos,
-                                retval);
-            pos += len;   // sb.length;
+        for (int k = 0; k < nSheets; k++) {
+            int serializedSize = sheets[k].getSheet().serialize(pos, retval);
+            if (serializedSize != estimatedSheetSizes[k]) {
+                // Wrong offset values have been passed in the call to setSheetBof() above.
+                // For books with more than one sheet, this discrepancy would cause excel 
+                // to report errors and loose data while reading the workbook
+                throw new IllegalStateException("Actual serialized sheet size (" + serializedSize 
+                        + ") differs from pre-calculated size (" + estimatedSheetSizes[k] 
+                        + ") for sheet (" + k + ")");
+                // TODO - add similar sanity check to ensure that Sheet.serializeIndexRecord() does not write mis-aligned offsets either
+            }
+            pos += serializedSize;
         }
-/*        for (int k = pos; k < totalsize; k++)
-        {
-            retval[k] = 0;
-        }*/
         return retval;
     }
 
index 3f6b321a6e5fe8711cc13d3dfda7a314847584b5..a10b9976aa50e19fcb037b56bcaaf055754743fa 100644 (file)
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.hssf.util;
 
 import java.util.ArrayList;
 import java.util.StringTokenizer;
 
+import org.apache.poi.hssf.record.formula.AreaI;
+
 public final class AreaReference {
 
     /** The character (!) that separates sheet names from cell references */ 
@@ -50,13 +51,13 @@ public final class AreaReference {
         
         // Special handling for whole-column references
         if(parts.length == 2 && parts[0].length() == 1 &&
-                       parts[1].length() == 1 && 
-                       parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
-                       parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
-               // Represented internally as x$1 to x$65536
-               //  which is the maximum range of rows
-               parts[0] = parts[0] + "$1";
-               parts[1] = parts[1] + "$65536";
+                parts[1].length() == 1 && 
+                parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
+                parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
+            // Represented internally as x$1 to x$65536
+            //  which is the maximum range of rows
+            parts[0] = parts[0] + "$1";
+            parts[1] = parts[1] + "$65536";
         }
         
         _firstCell = new CellReference(parts[0]);
@@ -74,9 +75,9 @@ public final class AreaReference {
      * Creates an area ref from a pair of Cell References.
      */
     public AreaReference(CellReference topLeft, CellReference botRight) {
-       _firstCell = topLeft;
-       _lastCell = botRight;
-       _isSingleCell = false;
+        _firstCell = topLeft;
+        _lastCell = botRight;
+        _isSingleCell = false;
     }
 
     /**
@@ -98,17 +99,17 @@ public final class AreaReference {
      *  such as C:C or D:G ?
      */
     public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) {
-       // These are represented as something like
-       //   C$1:C$65535 or D$1:F$0
-       // i.e. absolute from 1st row to 0th one
-       if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() &&
-               botRight.getRow() == 65535 && botRight.isRowAbsolute()) {
-               return true;
-       }
-       return false;
+        // These are represented as something like
+        //   C$1:C$65535 or D$1:F$0
+        // i.e. absolute from 1st row to 0th one
+        if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() &&
+            botRight.getRow() == 65535 && botRight.isRowAbsolute()) {
+            return true;
+        }
+        return false;
     }
     public boolean isWholeColumnReference() {
-       return isWholeColumnReference(_firstCell, _lastCell);
+        return isWholeColumnReference(_firstCell, _lastCell);
     }
 
     /**
@@ -155,26 +156,26 @@ public final class AreaReference {
      * Returns a reference to every cell covered by this area
      */
     public CellReference[] getAllReferencedCells() {
-       // Special case for single cell reference
-       if(_isSingleCell) {
-               return  new CellReference[] { _firstCell, };
-       }
+        // Special case for single cell reference
+        if(_isSingleCell) {
+            return  new CellReference[] { _firstCell, };
+        }
  
-       // Interpolate between the two
+        // Interpolate between the two
         int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow());
-       int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow());
-       int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol());
-       int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol());
+        int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow());
+        int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol());
+        int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol());
         String sheetName = _firstCell.getSheetName();
-       
-       ArrayList refs = new ArrayList();
-       for(int row=minRow; row<=maxRow; row++) {
-               for(int col=minCol; col<=maxCol; col++) {
-                       CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute());
-                       refs.add(ref);
-               }
-       }
-       return (CellReference[])refs.toArray(new CellReference[refs.size()]);
+        
+        ArrayList refs = new ArrayList();
+        for(int row=minRow; row<=maxRow; row++) {
+            for(int col=minCol; col<=maxCol; col++) {
+                CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute());
+                refs.add(ref);
+            }
+        }
+        return (CellReference[])refs.toArray(new CellReference[refs.size()]);
     }
 
     /**
@@ -189,14 +190,14 @@ public final class AreaReference {
      * @return the text representation of this area reference as it would appear in a formula.
      */
     public String formatAsString() {
-       // Special handling for whole-column references
-       if(isWholeColumnReference()) {
-               return
-                       CellReference.convertNumToColString(_firstCell.getCol())
-                       + ":" +
-                       CellReference.convertNumToColString(_lastCell.getCol());
-       }
-       
+        // Special handling for whole-column references
+        if(isWholeColumnReference()) {
+            return
+                CellReference.convertNumToColString(_firstCell.getCol())
+                + ":" +
+                CellReference.convertNumToColString(_lastCell.getCol());
+        }
+        
         StringBuffer sb = new StringBuffer(32);
         sb.append(_firstCell.formatAsString());
         if(!_isSingleCell) {
@@ -210,6 +211,18 @@ public final class AreaReference {
         }
         return sb.toString();
     }
+    /**
+     * Formats a 2-D area as it would appear in a formula.  See formatAsString() (no-arg)
+     */
+    public static String formatAsString(AreaI area) {
+        CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative());
+        CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative());
+        
+        if(isWholeColumnReference(topLeft, botRight)) {
+            return (new AreaReference(topLeft, botRight)).formatAsString();
+        }
+        return topLeft.formatAsString() + ":" + botRight.formatAsString(); 
+    }
     public String toString() {
         StringBuffer sb = new StringBuffer(64);
         sb.append(getClass().getName()).append(" [");
index 9251ccc74e3c340c8f9487fa4c197f136b6c12ef..2c51b3d20854554e779f2b4f915a4d680bb83432 100644 (file)
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.hssf.util;
 
+import java.lang.reflect.Field;
 import java.util.Hashtable;
 
 import org.apache.poi.ss.usermodel.Color;
 
 /**
  * Intends to provide support for the very evil index to triplet issue and
- * will likely replace the color contants interface for HSSF 2.0.
+ * will likely replace the color constants interface for HSSF 2.0.
  * This class contains static inner class members for representing colors.
  * Each color has an index (for the standard palette in Excel (tm) ),
  * native (RGB) triplet and string triplet.  The string triplet is as the
@@ -35,14 +35,10 @@ import org.apache.poi.ss.usermodel.Color;
  * @author  Andrew C. Oliver (acoliver at apache dot org)
  * @author  Brian Sanders (bsanders at risklabs dot com) - full default color palette
  */
-
-public class HSSFColor implements Color
-{
-    private final static int PALETTE_SIZE = 56;
-    private final static int DISTINCT_COLOR_COUNT = 46;
+public class HSSFColor implements Color {
+    // TODO make subclass instances immutable
 
     /** Creates a new instance of HSSFColor */
-
     public HSSFColor()
     {
     }
@@ -54,87 +50,86 @@ public class HSSFColor implements Color
      * it takes to create it once per request but you will not hold onto it
      * if you have none of those requests.
      *
-     * @return a hashtable containing all colors mapped to their excel-style 
-     * pallette index
+     * @return a hashtable containing all colors keyed by <tt>Integer</tt> excel-style palette indexes
      */
     public final static Hashtable getIndexHash() {
 
-        Hashtable hash = new Hashtable(PALETTE_SIZE);
-
-        hash.put(new Integer(HSSFColor.BLACK.index), new HSSFColor.BLACK());
-        hash.put(new Integer(HSSFColor.BROWN.index), new HSSFColor.BROWN());
-        hash.put(new Integer(HSSFColor.OLIVE_GREEN.index),
-                 new HSSFColor.OLIVE_GREEN());
-        hash.put(new Integer(HSSFColor.DARK_GREEN.index), new HSSFColor.DARK_GREEN());
-        hash.put(new Integer(HSSFColor.DARK_TEAL.index), new HSSFColor.DARK_TEAL());
-        hash.put(new Integer(HSSFColor.DARK_BLUE.index), new HSSFColor.DARK_BLUE());
-        hash.put(new Integer(HSSFColor.DARK_BLUE.index2), new HSSFColor.DARK_BLUE());
-        hash.put(new Integer(HSSFColor.INDIGO.index), new HSSFColor.INDIGO());
-        hash.put(new Integer(HSSFColor.GREY_80_PERCENT.index),
-                 new HSSFColor.GREY_80_PERCENT());
-        hash.put(new Integer(HSSFColor.ORANGE.index), new HSSFColor.ORANGE());
-        hash.put(new Integer(HSSFColor.DARK_YELLOW.index),
-                 new HSSFColor.DARK_YELLOW());
-        hash.put(new Integer(HSSFColor.GREEN.index), new HSSFColor.GREEN());
-        hash.put(new Integer(HSSFColor.TEAL.index), new HSSFColor.TEAL());
-        hash.put(new Integer(HSSFColor.TEAL.index2), new HSSFColor.TEAL());
-        hash.put(new Integer(HSSFColor.BLUE.index), new HSSFColor.BLUE());
-        hash.put(new Integer(HSSFColor.BLUE.index2), new HSSFColor.BLUE());
-        hash.put(new Integer(HSSFColor.BLUE_GREY.index), new HSSFColor.BLUE_GREY());
-        hash.put(new Integer(HSSFColor.GREY_50_PERCENT.index),
-                 new HSSFColor.GREY_50_PERCENT());
-        hash.put(new Integer(HSSFColor.RED.index), new HSSFColor.RED());
-        hash.put(new Integer(HSSFColor.LIGHT_ORANGE.index),
-                 new HSSFColor.LIGHT_ORANGE());
-        hash.put(new Integer(HSSFColor.LIME.index), new HSSFColor.LIME());
-        hash.put(new Integer(HSSFColor.SEA_GREEN.index), new HSSFColor.SEA_GREEN());
-        hash.put(new Integer(HSSFColor.AQUA.index), new HSSFColor.AQUA());
-        hash.put(new Integer(HSSFColor.LIGHT_BLUE.index), new HSSFColor.LIGHT_BLUE());
-        hash.put(new Integer(HSSFColor.VIOLET.index), new HSSFColor.VIOLET());
-        hash.put(new Integer(HSSFColor.VIOLET.index2), new HSSFColor.VIOLET());
-        hash.put(new Integer(HSSFColor.GREY_40_PERCENT.index),
-                 new HSSFColor.GREY_40_PERCENT());
-        hash.put(new Integer(HSSFColor.PINK.index), new HSSFColor.PINK());
-        hash.put(new Integer(HSSFColor.PINK.index2), new HSSFColor.PINK());
-        hash.put(new Integer(HSSFColor.GOLD.index), new HSSFColor.GOLD());
-        hash.put(new Integer(HSSFColor.YELLOW.index), new HSSFColor.YELLOW());
-        hash.put(new Integer(HSSFColor.YELLOW.index2), new HSSFColor.YELLOW());
-        hash.put(new Integer(HSSFColor.BRIGHT_GREEN.index),
-                 new HSSFColor.BRIGHT_GREEN());
-        hash.put(new Integer(HSSFColor.BRIGHT_GREEN.index2),
-                 new HSSFColor.BRIGHT_GREEN());
-        hash.put(new Integer(HSSFColor.TURQUOISE.index), new HSSFColor.TURQUOISE());
-        hash.put(new Integer(HSSFColor.TURQUOISE.index2), new HSSFColor.TURQUOISE());
-        hash.put(new Integer(HSSFColor.DARK_RED.index), new HSSFColor.DARK_RED());
-        hash.put(new Integer(HSSFColor.DARK_RED.index2), new HSSFColor.DARK_RED());
-        hash.put(new Integer(HSSFColor.SKY_BLUE.index), new HSSFColor.SKY_BLUE());
-        hash.put(new Integer(HSSFColor.PLUM.index), new HSSFColor.PLUM());
-        hash.put(new Integer(HSSFColor.PLUM.index2), new HSSFColor.PLUM());
-        hash.put(new Integer(HSSFColor.GREY_25_PERCENT.index),
-                 new HSSFColor.GREY_25_PERCENT());
-        hash.put(new Integer(HSSFColor.ROSE.index), new HSSFColor.ROSE());
-        hash.put(new Integer(HSSFColor.LIGHT_YELLOW.index),
-                 new HSSFColor.LIGHT_YELLOW());
-        hash.put(new Integer(HSSFColor.LIGHT_GREEN.index),
-                 new HSSFColor.LIGHT_GREEN());
-        hash.put(new Integer(HSSFColor.LIGHT_TURQUOISE.index),
-                 new HSSFColor.LIGHT_TURQUOISE());
-        hash.put(new Integer(HSSFColor.LIGHT_TURQUOISE.index2),
-                 new HSSFColor.LIGHT_TURQUOISE());
-        hash.put(new Integer(HSSFColor.PALE_BLUE.index), new HSSFColor.PALE_BLUE());
-        hash.put(new Integer(HSSFColor.LAVENDER.index), new HSSFColor.LAVENDER());
-        hash.put(new Integer(HSSFColor.WHITE.index), new HSSFColor.WHITE());
-        hash.put(new Integer(HSSFColor.CORNFLOWER_BLUE.index),
-                 new HSSFColor.CORNFLOWER_BLUE());
-        hash.put(new Integer(HSSFColor.LEMON_CHIFFON.index),
-                 new HSSFColor.LEMON_CHIFFON());
-        hash.put(new Integer(HSSFColor.MAROON.index), new HSSFColor.MAROON());
-        hash.put(new Integer(HSSFColor.ORCHID.index), new HSSFColor.ORCHID());
-        hash.put(new Integer(HSSFColor.CORAL.index), new HSSFColor.CORAL());
-        hash.put(new Integer(HSSFColor.ROYAL_BLUE.index), new HSSFColor.ROYAL_BLUE());
-        hash.put(new Integer(HSSFColor.LIGHT_CORNFLOWER_BLUE.index),
-                 new HSSFColor.LIGHT_CORNFLOWER_BLUE());
-       return hash;
+        return createColorsByIndexMap();
+    }
+
+    private static Hashtable createColorsByIndexMap() {
+        HSSFColor[] colors = getAllColors();
+        Hashtable result = new Hashtable(colors.length * 3 / 2);
+
+        for (int i = 0; i < colors.length; i++) {
+            HSSFColor color = colors[i];
+
+            Integer index1 = new Integer(color.getIndex());
+            if (result.containsKey(index1)) {
+                HSSFColor prevColor = (HSSFColor)result.get(index1);
+                throw new RuntimeException("Dup color index (" + index1
+                        + ") for colors (" + prevColor.getClass().getName()
+                        + "),(" + color.getClass().getName() + ")");
+            }
+            result.put(index1, color);
+        }
+
+        for (int i = 0; i < colors.length; i++) {
+            HSSFColor color = colors[i];
+            Integer index2 = getIndex2(color);
+            if (index2 == null) {
+                // most colors don't have a second index
+                continue;
+            }
+            if (result.containsKey(index2)) {
+                if (false) { // Many of the second indexes clash
+                    HSSFColor prevColor = (HSSFColor)result.get(index2);
+                    throw new RuntimeException("Dup color index (" + index2
+                            + ") for colors (" + prevColor.getClass().getName()
+                            + "),(" + color.getClass().getName() + ")");
+                }
+            }
+            result.put(index2, color);
+        }
+        return result;
+    }
+
+    private static Integer getIndex2(HSSFColor color) {
+
+        Field f;
+        try {
+            f = color.getClass().getDeclaredField("index2");
+        } catch (NoSuchFieldException e) {
+            // can happen because not all colors have a second index
+            return null;
+        }
+
+        Short s;
+        try {
+            s = (Short) f.get(color);
+        } catch (IllegalArgumentException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return new Integer(s.intValue());
+    }
+
+    private static HSSFColor[] getAllColors() {
+
+        return new HSSFColor[] {
+                new BLACK(), new BROWN(), new OLIVE_GREEN(), new DARK_GREEN(),
+                new DARK_TEAL(), new DARK_BLUE(), new INDIGO(), new GREY_80_PERCENT(),
+                new ORANGE(), new DARK_YELLOW(), new GREEN(), new TEAL(), new BLUE(),
+                new BLUE_GREY(), new GREY_50_PERCENT(), new RED(), new LIGHT_ORANGE(), new LIME(),
+                new SEA_GREEN(), new AQUA(), new LIGHT_BLUE(), new VIOLET(), new GREY_40_PERCENT(),
+                new PINK(), new GOLD(), new YELLOW(), new BRIGHT_GREEN(), new TURQUOISE(),
+                new DARK_RED(), new SKY_BLUE(), new PLUM(), new GREY_25_PERCENT(), new ROSE(),
+                new LIGHT_YELLOW(), new LIGHT_GREEN(), new LIGHT_TURQUOISE(), new PALE_BLUE(),
+                new LAVENDER(), new WHITE(), new CORNFLOWER_BLUE(), new LEMON_CHIFFON(),
+                new MAROON(), new ORCHID(), new CORAL(), new ROYAL_BLUE(),
+                new LIGHT_CORNFLOWER_BLUE(), new TAN(),
+        };
     }
 
     /**
@@ -144,73 +139,28 @@ public class HSSFColor implements Color
      * it takes to create it once per request but you will not hold onto it
      * if you have none of those requests.
      *
-     * @return a hashtable containing all colors mapped to their gnumeric-like
-     * triplet string
+     * @return a hashtable containing all colors keyed by String gnumeric-like triplets
      */
-
     public final static Hashtable getTripletHash()
     {
-        Hashtable hash = new Hashtable(DISTINCT_COLOR_COUNT);
-
-        hash.put(HSSFColor.BLACK.hexString, new HSSFColor.BLACK());
-        hash.put(HSSFColor.BROWN.hexString, new HSSFColor.BROWN());
-        hash.put(HSSFColor.OLIVE_GREEN.hexString,
-                 new HSSFColor.OLIVE_GREEN());
-        hash.put(HSSFColor.DARK_GREEN.hexString, new HSSFColor.DARK_GREEN());
-        hash.put(HSSFColor.DARK_TEAL.hexString, new HSSFColor.DARK_TEAL());
-        hash.put(HSSFColor.DARK_BLUE.hexString, new HSSFColor.DARK_BLUE());
-        hash.put(HSSFColor.INDIGO.hexString, new HSSFColor.INDIGO());
-        hash.put(HSSFColor.GREY_80_PERCENT.hexString,
-                 new HSSFColor.GREY_80_PERCENT());
-        hash.put(HSSFColor.ORANGE.hexString, new HSSFColor.ORANGE());
-        hash.put(HSSFColor.DARK_YELLOW.hexString,
-                 new HSSFColor.DARK_YELLOW());
-        hash.put(HSSFColor.GREEN.hexString, new HSSFColor.GREEN());
-        hash.put(HSSFColor.TEAL.hexString, new HSSFColor.TEAL());
-        hash.put(HSSFColor.BLUE.hexString, new HSSFColor.BLUE());
-        hash.put(HSSFColor.BLUE_GREY.hexString, new HSSFColor.BLUE_GREY());
-        hash.put(HSSFColor.GREY_50_PERCENT.hexString,
-                 new HSSFColor.GREY_50_PERCENT());
-        hash.put(HSSFColor.RED.hexString, new HSSFColor.RED());
-        hash.put(HSSFColor.LIGHT_ORANGE.hexString,
-                 new HSSFColor.LIGHT_ORANGE());
-        hash.put(HSSFColor.LIME.hexString, new HSSFColor.LIME());
-        hash.put(HSSFColor.SEA_GREEN.hexString, new HSSFColor.SEA_GREEN());
-        hash.put(HSSFColor.AQUA.hexString, new HSSFColor.AQUA());
-        hash.put(HSSFColor.LIGHT_BLUE.hexString, new HSSFColor.LIGHT_BLUE());
-        hash.put(HSSFColor.VIOLET.hexString, new HSSFColor.VIOLET());
-        hash.put(HSSFColor.GREY_40_PERCENT.hexString,
-                 new HSSFColor.GREY_40_PERCENT());
-        hash.put(HSSFColor.PINK.hexString, new HSSFColor.PINK());
-        hash.put(HSSFColor.GOLD.hexString, new HSSFColor.GOLD());
-        hash.put(HSSFColor.YELLOW.hexString, new HSSFColor.YELLOW());
-        hash.put(HSSFColor.BRIGHT_GREEN.hexString,
-                 new HSSFColor.BRIGHT_GREEN());
-        hash.put(HSSFColor.TURQUOISE.hexString, new HSSFColor.TURQUOISE());
-        hash.put(HSSFColor.DARK_RED.hexString, new HSSFColor.DARK_RED());
-        hash.put(HSSFColor.SKY_BLUE.hexString, new HSSFColor.SKY_BLUE());
-        hash.put(HSSFColor.PLUM.hexString, new HSSFColor.PLUM());
-        hash.put(HSSFColor.GREY_25_PERCENT.hexString,
-                 new HSSFColor.GREY_25_PERCENT());
-        hash.put(HSSFColor.ROSE.hexString, new HSSFColor.ROSE());
-        hash.put(HSSFColor.LIGHT_YELLOW.hexString,
-                 new HSSFColor.LIGHT_YELLOW());
-        hash.put(HSSFColor.LIGHT_GREEN.hexString,
-                 new HSSFColor.LIGHT_GREEN());
-        hash.put(HSSFColor.LIGHT_TURQUOISE.hexString,
-                 new HSSFColor.LIGHT_TURQUOISE());
-        hash.put(HSSFColor.PALE_BLUE.hexString, new HSSFColor.PALE_BLUE());
-        hash.put(HSSFColor.LAVENDER.hexString, new HSSFColor.LAVENDER());
-        hash.put(HSSFColor.WHITE.hexString, new HSSFColor.WHITE());
-        hash.put(HSSFColor.CORNFLOWER_BLUE.hexString, new HSSFColor.CORNFLOWER_BLUE());
-        hash.put(HSSFColor.LEMON_CHIFFON.hexString, new HSSFColor.LEMON_CHIFFON());
-        hash.put(HSSFColor.MAROON.hexString, new HSSFColor.MAROON());
-        hash.put(HSSFColor.ORCHID.hexString, new HSSFColor.ORCHID());
-        hash.put(HSSFColor.CORAL.hexString, new HSSFColor.CORAL());
-        hash.put(HSSFColor.ROYAL_BLUE.hexString, new HSSFColor.ROYAL_BLUE());
-        hash.put(HSSFColor.LIGHT_CORNFLOWER_BLUE.hexString,
-                 new HSSFColor.LIGHT_CORNFLOWER_BLUE());
-        return hash;
+        return createColorsByHexStringMap();
+    }
+
+    private static Hashtable createColorsByHexStringMap() {
+        HSSFColor[] colors = getAllColors();
+        Hashtable result = new Hashtable(colors.length * 3 / 2);
+
+        for (int i = 0; i < colors.length; i++) {
+            HSSFColor color = colors[i];
+
+            String hexString = color.getHexString();
+            if (result.containsKey(hexString)) {
+                throw new RuntimeException("Dup color hexString (" + hexString
+                        + ") for color (" + color.getClass().getName() + ")");
+            }
+            result.put(hexString, color);
+        }
+        return result;
     }
 
     /**
@@ -1492,7 +1442,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class CORNFLOWER_BLUE
      */
@@ -1521,8 +1471,8 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
-    
+
+
     /**
      * Class LEMON_CHIFFON
      */
@@ -1551,7 +1501,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class MAROON
      */
@@ -1580,7 +1530,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class ORCHID
      */
@@ -1609,7 +1559,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class CORAL
      */
@@ -1638,7 +1588,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class ROYAL_BLUE
      */
@@ -1667,7 +1617,7 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Class LIGHT_CORNFLOWER_BLUE
      */
@@ -1696,19 +1646,19 @@ public class HSSFColor implements Color
             return hexString;
         }
     }
-    
+
     /**
      * Special Default/Normal/Automatic color.
      * <p><i>Note:</i> This class is NOT in the default HashTables returned by HSSFColor.
      * The index is a special case which is interpreted in the various setXXXColor calls.
-     * 
+     *
      * @author Jason
      *
      */
     public final static class AUTOMATIC extends HSSFColor
     {
-       private static HSSFColor instance = new AUTOMATIC();
-       
+        private static HSSFColor instance = new AUTOMATIC();
+
         public final static short   index     = 0x40;
 
         public short getIndex()
@@ -1725,7 +1675,7 @@ public class HSSFColor implements Color
         {
             return BLACK.hexString;
         }
-        
+
         public static HSSFColor getInstance() {
           return instance;
         }
index 9335de35dad2578ac330e0b9ad4e8c28b11c35d7..0a9bdcfe8814fac7d660b7f0278d11ed27670e06 100644 (file)
@@ -226,7 +226,9 @@ public class DateUtil
        
        // Otherwise, check it's only made up, in any case, of:
        //  y m d h s - / , . :
-       if(fs.matches("^[yYmMdDhHsS\\-/,. :]+$")) {
+       // optionally followed by AM/PM
+       // optionally followed by AM/PM
+       if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP]*$")) {
                return true;
        }
        
index 475131e1c0e4d347cd092cfde7691414384848b1..8a85f42841a8405a4e591145d67cc359a6750462 100644 (file)
@@ -14,7 +14,7 @@
 # limitations under the License.\r
 \r
 # Created by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor)\r
-# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A)\r
+# from source file 'excelfileformat.odt' (size=356107, md5=0x8f789cb6e75594caf068f8e193004ef4)\r
 #\r
 #Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )\r
 \r
@@ -37,7 +37,7 @@
 15     SIN     1       1       V       V               \r
 16     COS     1       1       V       V               \r
 17     TAN     1       1       V       V               \r
-18     ARCTAN  1       1       V       V               \r
+18     ATAN    1       1       V       V               \r
 19     PI      0       0       V       -               \r
 20     SQRT    1       1       V       V               \r
 21     EXP     1       1       V       V               \r
 169    COUNTA  0       30      V       R               \r
 183    PRODUCT 0       30      V       R               \r
 184    FACT    1       1       V       V               \r
-191    DPRODUCT        3       3       V       R R R           \r
-192    ISNONTEXT       1       1       V       V               \r
+189    DPRODUCT        3       3       V       R R R           \r
+190    ISNONTEXT       1       1       V       V               \r
 193    STDEVP  1       30      V       R               \r
 194    VARP    1       30      V       R               \r
 195    DSTDEVP 3       3       V       R R R           \r
 244    INFO    1       1       V       V               \r
 # New Built-In Sheet Functions in BIFF4\r
 14     FIXED   2       3       V       V V V           x\r
+204    USDOLLAR        1       2       V       V V             x\r
+215    DBCS    1       1       V       V               x\r
 216    RANK    2       3       V       V R V           \r
 247    DB      4       5       V       V V V V V               \r
 252    FREQUENCY       2       2       A       R R             \r
index 6902027de974b8925260c978be952f9cc2a79cce..8a85f42841a8405a4e591145d67cc359a6750462 100644 (file)
 # limitations under the License.\r
 \r
 # Created by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor)\r
-# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A)\r
+# from source file 'excelfileformat.odt' (size=356107, md5=0x8f789cb6e75594caf068f8e193004ef4)\r
 #\r
 #Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )\r
-#\r
-# + some manual edits !\r
 \r
 # Built-In Sheet Functions in BIFF2\r
 0      COUNT   0       30      V       R               \r
 244    INFO    1       1       V       V               \r
 # New Built-In Sheet Functions in BIFF4\r
 14     FIXED   2       3       V       V V V           x\r
-204    USDOLLAR        1       1       V       V               x\r
+204    USDOLLAR        1       2       V       V V             x\r
 215    DBCS    1       1       V       V               x\r
 216    RANK    2       3       V       V R V           \r
 247    DB      4       5       V       V V V V V               \r
index af66163072e673f36f1a925c53d7624cff95f1ee..764b8e3f5ff60f655f8b76d6380dc51db4b2fab3 100644 (file)
@@ -18,6 +18,7 @@ package org.apache.poi.hdgf;
 
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 
 import org.apache.poi.POIDocument;
 import org.apache.poi.hdgf.chunks.ChunkFactory;
@@ -27,6 +28,7 @@ import org.apache.poi.hdgf.streams.PointerContainingStream;
 import org.apache.poi.hdgf.streams.Stream;
 import org.apache.poi.hdgf.streams.StringsStream;
 import org.apache.poi.hdgf.streams.TrailerStream;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentEntry;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.LittleEndian;
@@ -53,14 +55,17 @@ public class HDGFDiagram extends POIDocument {
        private PointerFactory ptrFactory;
        
        public HDGFDiagram(POIFSFileSystem fs) throws IOException {
-               super(fs);
+               this(fs.getRoot(), fs);
+       }
+       public HDGFDiagram(DirectoryNode dir, POIFSFileSystem fs) throws IOException {
+               super(dir, fs);
                
                DocumentEntry docProps =
-                       (DocumentEntry)filesystem.getRoot().getEntry("VisioDocument");
+                       (DocumentEntry)dir.getEntry("VisioDocument");
 
                // Grab the document stream
                _docstream = new byte[docProps.getSize()];
-               filesystem.createDocumentInputStream("VisioDocument").read(_docstream);
+               dir.createDocumentInputStream("VisioDocument").read(_docstream);
                
                // Read in the common POI streams
                readProperties();
@@ -149,6 +154,10 @@ public class HDGFDiagram extends POIDocument {
                }
        }
        
+       public void write(OutputStream out) {
+               throw new IllegalStateException("Writing is not yet implemented, see http://poi.apache.org/hdgf/");
+       }
+       
        /**
         * For testing only
         */
index 58c69ff4b934708f3db6ad9b8b3de779f541aec4..812ab1a4a766feb548571b252cb27513f58bf5c7 100644 (file)
@@ -21,17 +21,18 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class holds all the FSPA (File Shape Address) structures.
  * 
  * @author Squeeself
  */
-public class FSPATable 
+public final class FSPATable 
 {
-    protected ArrayList shapes = new ArrayList();
-    protected HashMap cps = new HashMap();
-    protected List _text;
+    private final List _shapes = new ArrayList();
+    private final Map _shapeIndexesByPropertyStart = new HashMap();
+    private final List _text;
     
     public FSPATable(byte[] tableStream, int fcPlcspa, int lcbPlcspa, List tpt)
     {
@@ -46,32 +47,35 @@ public class FSPATable
             GenericPropertyNode property = plex.getProperty(i);
             FSPA fspa = new FSPA(property.getBytes(), 0);
             
-            shapes.add(fspa);
-            cps.put(Integer.valueOf(property.getStart()), Integer.valueOf(i));
+            _shapes.add(fspa);
+            _shapeIndexesByPropertyStart.put(new Integer(property.getStart()), new Integer(i));
         }
     }
     
     public FSPA getFspaFromCp(int cp)
     {
-        Integer idx = (Integer)cps.get(Integer.valueOf(cp));
-        if (idx == null)
+        Integer idx = (Integer)_shapeIndexesByPropertyStart.get(new Integer(cp));
+        if (idx == null) {
             return null;
-        return (FSPA)shapes.get(idx.intValue());
+        }
+        return (FSPA)_shapes.get(idx.intValue());
     }
     
-    public List getShapes()
+    public FSPA[] getShapes()
     {
-        return shapes;
+        FSPA[] result = new FSPA[_shapes.size()];
+        _shapes.toArray(result);
+        return result;
     }
     
     public String toString()
     {
         StringBuffer buf = new StringBuffer();
-        buf.append("[FPSA PLC size=").append(shapes.size()).append("]\n");
-        for (Iterator it = cps.keySet().iterator(); it.hasNext(); )
+        buf.append("[FPSA PLC size=").append(_shapes.size()).append("]\n");
+        for (Iterator it = _shapeIndexesByPropertyStart.keySet().iterator(); it.hasNext(); )
         {
             Integer i = (Integer) it.next();
-            FSPA fspa = (FSPA) shapes.get(((Integer)cps.get(i)).intValue());
+            FSPA fspa = (FSPA) _shapes.get(((Integer)_shapeIndexesByPropertyStart.get(i)).intValue());
             buf.append("  [FC: ").append(i.toString()).append("] ");
             buf.append(fspa.toString());
             buf.append("\n");
index 48e6d78b3a9852b7a654ccc8432daa6f8d44eb00..887e13d829792a68dde64249b1cb1315625393f1 100644 (file)
@@ -294,6 +294,16 @@ public class FileInformationBlock extends FIBAbstractType
       _longHandler.setLong(FIBLongHandler.CBMAC, cbMac);
     }
 
+       public int getCcpText()
+       {
+         return _longHandler.getLong(FIBLongHandler.CCPTEXT);
+       }
+
+       public void setCcpText(int ccpText)
+       {
+         _longHandler.setLong(FIBLongHandler.CCPTEXT, ccpText);
+       }
+
     public void clearOffsetsSizes()
     {
       _fieldHandler.clearFields();
index 67c634d9f687b76f52d534e10e7920d7a97d0bd9..bc33954dff63e548328e28a20f1ac4915e5f55ca 100644 (file)
@@ -90,12 +90,20 @@ public class TextPiece extends PropertyNode implements Comparable
 
    public void adjustForDelete(int start, int length)
    {
+
+          if (usesUnicode()) {
+
+                  start /= 2;
+                  length /= 2;
+          }
+
           int myStart = getStart();
           int myEnd = getEnd();
           int end = start + length;
 
           /* do we have to delete from this text piece? */
           if (start <= myEnd && end >= myStart) {
+
                   /* find where the deleted area overlaps with this text piece */
                   int overlapStart = Math.max(myStart, start);
                   int overlapEnd = Math.min(myEnd, end);
index f2d9a615f812d8ce3c31e61c670aef0d75cec083..85592a92a21b8b6e868a502ce234b89cd1c21009 100644 (file)
@@ -226,6 +226,25 @@ public class Range
     }
   }
 
+  /**
+   * Does any <code>TextPiece</code> in this Range use unicode?
+   *
+   *   @return true if it does and false if it doesn't
+   */
+  public boolean usesUnicode() {
+
+       initText();
+
+       for (int i = _textStart; i < _textEnd; i++)
+       {
+         TextPiece piece = (TextPiece)_text.get(i);
+         if (piece.usesUnicode())
+                 return true;
+       }
+
+       return false;
+  }
+
   /**
    * Gets the text that this Range contains.
    *
@@ -306,13 +325,19 @@ public class Range
     // Since this is the first item in our list, it is safe to assume that
     // _start >= tp.getStart()
     int insertIndex = _start - tp.getStart();
+       if (tp.usesUnicode())
+               insertIndex /= 2;
     sb.insert(insertIndex, text);
+
     int adjustedLength = _doc.getTextTable().adjustForInsert(_textStart, text.length());
     _doc.getCharacterTable().adjustForInsert(_charStart, adjustedLength);
     _doc.getParagraphTable().adjustForInsert(_parStart, adjustedLength);
     _doc.getSectionTable().adjustForInsert(_sectionStart, adjustedLength);
     adjustForInsert(text.length());
 
+       // update the FIB.CCPText field
+       adjustFIB(text.length());
+
     return getCharacterRun(0);
   }
 
@@ -489,6 +514,7 @@ public class Range
 
   public void delete()
   {
+
     initAll();
 
     int numSections = _sections.size();
@@ -519,6 +545,12 @@ public class Range
        TextPiece piece = (TextPiece)_text.get(x);
        piece.adjustForDelete(_start, _end - _start);
     }
+
+       // update the FIB.CCPText field
+       if (usesUnicode())
+               adjustFIB(-((_end - _start) / 2));
+       else
+               adjustFIB(-(_end - _start));
   }
 
   /**
@@ -827,6 +859,19 @@ public class Range
     _sectionRangeFound = false;
   }
 
+  /**
+   *   Adjust the value of <code>FIB.CCPText</code> after an insert or a delete...
+   *
+   *   @param  adjustment      The (signed) value that should be added to <code>FIB.CCPText</code>
+   */
+  protected void adjustFIB(int adjustment) {
+
+       // update the FIB.CCPText field (this should happen once per adjustment, so we don't want it in
+       // adjustForInsert() or it would get updated multiple times if the range has a parent)
+       // without this, OpenOffice.org (v. 2.2.x) does not see all the text in the document
+       _doc.getFileInformationBlock().setCcpText(_doc.getFileInformationBlock().getCcpText() + adjustment);
+  }
+
   /**
    * adjust this range after an insert happens.
    * @param length the length to adjust for
@@ -834,6 +879,7 @@ public class Range
   private void adjustForInsert(int length)
   {
     _end += length;
+
     reset();
     Range parent = (Range)_parent.get();
     if (parent != null)
@@ -842,4 +888,14 @@ public class Range
     }
   }
 
+
+       public int getStartOffset() {
+
+               return _start;
+       }
+
+       public int getEndOffset() {
+
+               return _end;
+       }
 }
diff --git a/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls
new file mode 100644 (file)
index 0000000..96ef3ee
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls differ
index 1895705a564bb9bc961a66b5f85c56d362a88849..f2821140f3c325ae6cfdaac482463a9539635b12 100644 (file)
@@ -55,120 +55,99 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
 /**
  * Test the low level formula parser functionality. High level tests are to
- *  be done via usermodel/HSSFCell.setFormulaValue() .
- * Some tests are also done in scratchpad, if they need
- *  HSSFFormulaEvaluator, which is there
+ * be done via usermodel/HSSFCell.setFormulaValue().
  */
 public final class TestFormulaParser extends TestCase {
 
-    /**
-     * @return parsed token array already confirmed not <code>null</code>
-     */
-    private static Ptg[] parseFormula(String s) {
-       // TODO - replace multiple copies of this code with calls to this method
-        FormulaParser fp = new FormulaParser(s, null);
-        fp.parse();
-        Ptg[] result = fp.getRPNPtg();
-        assertNotNull("Ptg array should not be null", result);
-        return result;
-    }
-
-    public void testSimpleFormula() {
-        FormulaParser fp = new FormulaParser("2+2",null);
-        fp.parse();
-        Ptg[] ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
-    }
-    public void testFormulaWithSpace1() {
-        FormulaParser fp = new FormulaParser(" 2 + 2 ",null);
-        fp.parse();
-        Ptg[] ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
-        assertTrue("",(ptgs[0] instanceof IntPtg));
-        assertTrue("",(ptgs[1] instanceof IntPtg));
-        assertTrue("",(ptgs[2] instanceof AddPtg));
-    }
-
-    public void testFormulaWithSpace2() {
-        Ptg[] ptgs;
-        FormulaParser fp;
-        fp = new FormulaParser("2+ sum( 3 , 4) ",null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
-    }
-
-     public void testFormulaWithSpaceNRef() {
-        Ptg[] ptgs;
-        FormulaParser fp;
-        fp = new FormulaParser("sum( A2:A3 )",null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
-    }
-
-    public void testFormulaWithString() {
-        Ptg[] ptgs;
-        FormulaParser fp;
-        fp = new FormulaParser("\"hello\" & \"world\" ",null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3);
-    }
-
-    public void testTRUE() throws Exception {
-        FormulaParser fp = new FormulaParser("TRUE", null);
-        fp.parse();
-        Ptg[] asts = fp.getRPNPtg();
-        assertEquals(1, asts.length);
-        BoolPtg flag  = (BoolPtg) asts[0];
-        assertEquals(true, flag.getValue());
-    }
-
-    public void testYN() throws Exception {
-        final String yn = "IF(TRUE,\"Y\",\"N\")";
-        FormulaParser fp = new FormulaParser(yn, null);
-        fp.parse();
-        Ptg[] asts = fp.getRPNPtg();
-        assertEquals(7, asts.length);
-
-        BoolPtg flag  = (BoolPtg) asts[0];
-               AttrPtg funif = (AttrPtg) asts[1];
-        StringPtg y = (StringPtg) asts[2];
-               AttrPtg goto1 = (AttrPtg) asts[3];
-        StringPtg n = (StringPtg) asts[4];
-
-
-        assertEquals(true, flag.getValue());
-        assertEquals("Y", y.getValue());
-        assertEquals("N", n.getValue());
-        assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
-        assertTrue("Goto ptg exists", goto1.isGoto());
-    }
-
-       public void testSimpleIf() throws Exception {
-               final String simpleif = "IF(1=1,0,1)";
-               FormulaParser fp = new FormulaParser(simpleif, null);
+       /**
+        * @return parsed token array already confirmed not <code>null</code>
+        */
+       private static Ptg[] parseFormula(String s) {
+               FormulaParser fp = new FormulaParser(s, null);
                fp.parse();
-               Ptg[] asts = fp.getRPNPtg();
-               assertEquals(9, asts.length);
-               
-               IntPtg op1 = (IntPtg) asts[0];
-               IntPtg op2 = (IntPtg) asts[1];
-               EqualPtg eq = (EqualPtg) asts[2];               
-               AttrPtg ifPtg = (AttrPtg) asts[3];
-               IntPtg res1 = (IntPtg) asts[4];
-                               
-               AttrPtg ptgGoto= (AttrPtg) asts[5];             
-               assertEquals("Goto 1 Length", (short)10, ptgGoto.getData());
-               
-               IntPtg res2 = (IntPtg) asts[6];         
-               AttrPtg ptgGoto2 = (AttrPtg) asts[7];           
-               assertEquals("Goto 2 Length", (short)3, ptgGoto2.getData());
-               
-               assertEquals("If FALSE offset", (short)7, ifPtg.getData());
+               Ptg[] result = fp.getRPNPtg();
+               assertNotNull("Ptg array should not be null", result);
+               return result;
+       }
+
+       public void testSimpleFormula() {
+               Ptg[] ptgs = parseFormula("2+2");
+               assertEquals(3, ptgs.length);
+       }
+
+       public void testFormulaWithSpace1() {
+               Ptg[] ptgs = parseFormula(" 2 + 2 ");
+               assertEquals(3, ptgs.length);
+               assertTrue("",(ptgs[0] instanceof IntPtg));
+               assertTrue("",(ptgs[1] instanceof IntPtg));
+               assertTrue("",(ptgs[2] instanceof AddPtg));
+       }
+
+       public void testFormulaWithSpace2() {
+               Ptg[] ptgs = parseFormula("2+ sum( 3 , 4) ");
+               assertEquals(5, ptgs.length);
+       }
+
+       public void testFormulaWithSpaceNRef() {
+               Ptg[] ptgs = parseFormula("sum( A2:A3 )");
+               assertEquals(2, ptgs.length);
+       }
+
+       public void testFormulaWithString() {
+               Ptg[] ptgs = parseFormula("\"hello\" & \"world\" ");
+               assertEquals(3, ptgs.length);
+       }
+
+       public void testTRUE() {
+               Ptg[] ptgs = parseFormula("TRUE");
+               assertEquals(1, ptgs.length);
+               BoolPtg flag  = (BoolPtg) ptgs[0];
+               assertEquals(true, flag.getValue());
+       }
+
+       public void testYN() {
+               Ptg[] ptgs = parseFormula("IF(TRUE,\"Y\",\"N\")");
+               assertEquals(7, ptgs.length);
+
+               BoolPtg flag  = (BoolPtg) ptgs[0];
+               AttrPtg funif = (AttrPtg) ptgs[1];
+               StringPtg y = (StringPtg) ptgs[2];
+               AttrPtg goto1 = (AttrPtg) ptgs[3];
+               StringPtg n = (StringPtg) ptgs[4];
+
+
+               assertEquals(true, flag.getValue());
+               assertEquals("Y", y.getValue());
+               assertEquals("N", n.getValue());
+               assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
+               assertTrue("Goto ptg exists", goto1.isGoto());
+       }
+
+       public void testSimpleIf() {
+               String formula = "IF(1=1,0,1)";
+
+               Class[] expectedClasses = {
+                       IntPtg.class,
+                       IntPtg.class,
+                       EqualPtg.class,
+                       AttrPtg.class,
+                       IntPtg.class,
+                       AttrPtg.class,
+                       IntPtg.class,
+                       AttrPtg.class,
+                       FuncVarPtg.class,
+               };
+               confirmTokenClasses(formula, expectedClasses);
                
-               FuncVarPtg funcPtg = (FuncVarPtg)asts[8];
+               Ptg[] ptgs = parseFormula(formula);
+
+               AttrPtg ifPtg = (AttrPtg) ptgs[3];
+               AttrPtg ptgGoto= (AttrPtg) ptgs[5];
+               assertEquals("Goto 1 Length", 10, ptgGoto.getData());
+
+               AttrPtg ptgGoto2 = (AttrPtg) ptgs[7];
+               assertEquals("Goto 2 Length", 3, ptgGoto2.getData());
+               assertEquals("If FALSE offset", 7, ifPtg.getData());
        }
 
        /**
@@ -176,753 +155,714 @@ public final class TestFormulaParser extends TestCase {
         *
         */
        public void testNestedFunctionIf() {
-               String function = "IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))";
+               Ptg[] ptgs = parseFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
+               assertEquals(11, ptgs.length);
 
-               FormulaParser fp = new FormulaParser(function, null);
-               fp.parse();
-               Ptg[] asts = fp.getRPNPtg();
-               assertEquals("11 Ptgs expected", 11, asts.length);
-
-               assertTrue("IF Attr set correctly", (asts[3] instanceof AttrPtg));
-               AttrPtg ifFunc = (AttrPtg)asts[3];
+               assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
+               AttrPtg ifFunc = (AttrPtg)ptgs[3];
                assertTrue("It is not an if", ifFunc.isOptimizedIf());
-               
-               assertTrue("Average Function set correctly", (asts[5] instanceof FuncVarPtg));
+
+               assertTrue("Average Function set correctly", (ptgs[5] instanceof FuncVarPtg));
        }
-       
-       public void testIfSingleCondition(){
-               String function = "IF(1=1,10)";
 
-               FormulaParser fp = new FormulaParser(function, null);
-               fp.parse();
-               Ptg[] asts = fp.getRPNPtg();
-               assertEquals("7 Ptgs expected", 7, asts.length);
+       public void testIfSingleCondition(){
+               Ptg[] ptgs = parseFormula("IF(1=1,10)");
+               assertEquals(7, ptgs.length);
 
-               assertTrue("IF Attr set correctly", (asts[3] instanceof AttrPtg));
-               AttrPtg ifFunc = (AttrPtg)asts[3];
+               assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
+               AttrPtg ifFunc = (AttrPtg)ptgs[3];
                assertTrue("It is not an if", ifFunc.isOptimizedIf());
-               
-               assertTrue("Single Value is not an IntPtg", (asts[4] instanceof IntPtg));
-               IntPtg intPtg = (IntPtg)asts[4];
+
+               assertTrue("Single Value is not an IntPtg", (ptgs[4] instanceof IntPtg));
+               IntPtg intPtg = (IntPtg)ptgs[4];
                assertEquals("Result", (short)10, intPtg.getValue());
-               
-               assertTrue("Ptg is not a Variable Function", (asts[6] instanceof FuncVarPtg));
-               FuncVarPtg funcPtg = (FuncVarPtg)asts[6];
+
+               assertTrue("Ptg is not a Variable Function", (ptgs[6] instanceof FuncVarPtg));
+               FuncVarPtg funcPtg = (FuncVarPtg)ptgs[6];
                assertEquals("Arguments", 2, funcPtg.getNumberOfOperands());
        }
 
        public void testSumIf() {
-               String function ="SUMIF(A1:A5,\">4000\",B1:B5)";
-               FormulaParser fp = new FormulaParser(function, null);
-               fp.parse();
-               Ptg[] asts = fp.getRPNPtg();
-               assertEquals("4 Ptgs expected", 4, asts.length);
+               Ptg[] ptgs = parseFormula("SUMIF(A1:A5,\">4000\",B1:B5)");
+               assertEquals(4, ptgs.length);
        }
-       
+
        /**
         * Bug Reported by xt-jens.riis@nokia.com (Jens Riis)
         * Refers to Bug <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=17582">#17582</a>
         *
         */
-       public void testNonAlphaFormula(){
+       public void testNonAlphaFormula() {
                String currencyCell = "F3";
-               String function="\"TOTAL[\"&"+currencyCell+"&\"]\"";
+               Ptg[] ptgs = parseFormula("\"TOTAL[\"&"+currencyCell+"&\"]\"");
+               assertEquals(5, ptgs.length);
+               assertTrue ("Ptg[0] is a string", (ptgs[0] instanceof StringPtg));
+               StringPtg firstString = (StringPtg)ptgs[0];
 
-               Ptg[] asts = parseFormula(function);
-               assertEquals("5 ptgs expected", 5, asts.length);
-               assertTrue ("Ptg[0] is a string", (asts[0] instanceof StringPtg));
-               StringPtg firstString = (StringPtg)asts[0];             
-               
                assertEquals("TOTAL[", firstString.getValue());
                //the PTG order isn't 100% correct but it still works - dmui
        }
 
        public void testSimpleLogical() {
-      Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
-      assertEquals("Ptg array length", 9, ptgs.length);
-      assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
+        Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
+        assertEquals(9, ptgs.length);
+        assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
        }
-       
+
        public void testParenIf() {
                Ptg[] ptgs = parseFormula("IF((A1+A2)<=3,\"yes\",\"no\")");
-               assertEquals("Ptg array length", 12, ptgs.length);
+               assertEquals(12, ptgs.length);
                assertEquals("6th Ptg is less than equal",LessEqualPtg.class,ptgs[5].getClass());
                assertEquals("11th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[10].getClass());
        }
-       
+
        public void testEmbeddedIf() {
                Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))");
-               assertEquals("Ptg array length", 17, ptgs.length);
-               
+               assertEquals(17, ptgs.length);
+
                assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass());
                assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass());
                assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
        }
-       
-    public void testMacroFunction() {
-       HSSFWorkbook w = new HSSFWorkbook();
-        FormulaParser fp = new FormulaParser("FOO()", w);
-        fp.parse();
-        Ptg[] ptg = fp.getRPNPtg();
-
-        // the name gets encoded as the first arg
-        NamePtg tname = (NamePtg) ptg[0];
-        assertEquals("FOO", tname.toFormulaString(w));
-
-        AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
-        assertTrue(tfunc.isExternalFunction());
-    }
-
-    public void testEmbeddedSlash() {
-        FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",null);
-        fp.parse();
-        Ptg[] ptg = fp.getRPNPtg();
-        assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
-        assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
-    }
-
-    public void testConcatenate() {
-               FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")", null);
-               fp.parse();
-               Ptg[] ptg = fp.getRPNPtg();
-               assertTrue("first ptg is string", ptg[0] instanceof StringPtg);
-               assertTrue("second ptg is string", ptg[1] instanceof StringPtg);
-       }
-
-    public void testWorksheetReferences()
-    {
-       HSSFWorkbook wb = new HSSFWorkbook();
-       
-       wb.createSheet("NoQuotesNeeded");
-       wb.createSheet("Quotes Needed Here &#$@");
-       
-       HSSFSheet sheet = wb.createSheet("Test");
-       HSSFRow row = sheet.createRow(0);
-       HSSFCell cell;
-       
-       cell = row.createCell((short)0);
-       cell.setCellFormula("NoQuotesNeeded!A1");
-       
-       cell = row.createCell((short)1);
-       cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
-    }
-
-    public void testUnaryMinus()
-    {
-               FormulaParser fp = new FormulaParser("-A1", null);
-               fp.parse();
-               Ptg[] ptg = fp.getRPNPtg();
-               assertTrue("got 2 ptgs", ptg.length == 2);
-               assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
-               assertTrue("second ptg is Minus",ptg[1] instanceof UnaryMinusPtg);
-     }
-
-    public void testUnaryPlus()
-    {
-               FormulaParser fp = new FormulaParser("+A1", null);
+
+       public void testMacroFunction() {
+               HSSFWorkbook w = new HSSFWorkbook();
+               FormulaParser fp = new FormulaParser("FOO()", w);
                fp.parse();
                Ptg[] ptg = fp.getRPNPtg();
-               assertTrue("got 2 ptgs", ptg.length == 2);
-               assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
-               assertTrue("second ptg is Plus",ptg[1] instanceof UnaryPlusPtg);
-     }
 
-       public void testLeadingSpaceInString()
-       {
+               // the name gets encoded as the first arg
+               NamePtg tname = (NamePtg) ptg[0];
+               assertEquals("FOO", tname.toFormulaString(w));
+
+               AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
+               assertTrue(tfunc.isExternalFunction());
+       }
+
+       public void testEmbeddedSlash() {
+               Ptg[] ptgs = parseFormula("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")");
+               assertTrue("first ptg is string", ptgs[0] instanceof StringPtg);
+               assertTrue("second ptg is string", ptgs[1] instanceof StringPtg);
+       }
+
+       public void testConcatenate() {
+               Ptg[] ptgs = parseFormula("CONCATENATE(\"first\",\"second\")");
+               assertTrue("first ptg is string", ptgs[0] instanceof StringPtg);
+               assertTrue("second ptg is string", ptgs[1] instanceof StringPtg);
+       }
+
+       public void testWorksheetReferences() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+
+               wb.createSheet("NoQuotesNeeded");
+               wb.createSheet("Quotes Needed Here &#$@");
+
+               HSSFSheet sheet = wb.createSheet("Test");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell;
+
+               cell = row.createCell((short)0);
+               cell.setCellFormula("NoQuotesNeeded!A1");
+
+               cell = row.createCell((short)1);
+               cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
+       }
+
+       public void testUnaryMinus() {
+               Ptg[] ptgs = parseFormula("-A1");
+               assertEquals(2, ptgs.length);
+               assertTrue("first ptg is reference",ptgs[0] instanceof ReferencePtg);
+               assertTrue("second ptg is Minus",ptgs[1] instanceof UnaryMinusPtg);
+       }
+
+       public void testUnaryPlus() {
+               Ptg[] ptgs = parseFormula("+A1");
+               assertEquals(2, ptgs.length);
+               assertTrue("first ptg is reference",ptgs[0] instanceof ReferencePtg);
+               assertTrue("second ptg is Plus",ptgs[1] instanceof UnaryPlusPtg);
+       }
+
+       public void testLeadingSpaceInString() {
                String value = "  hi  ";
-               FormulaParser fp = new FormulaParser("\"" + value + "\"", null);
-               fp.parse();
-               Ptg[] ptg = fp.getRPNPtg();
+               Ptg[] ptgs = parseFormula("\"" + value + "\"");
 
-               assertTrue("got 1 ptg", ptg.length == 1);
-               assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg);
-               assertTrue("ptg0 contains exact value", ((StringPtg)ptg[0]).getValue().equals(value));
+               assertEquals(1, ptgs.length);
+               assertTrue("ptg0 is a StringPtg", ptgs[0] instanceof StringPtg);
+               assertTrue("ptg0 contains exact value", ((StringPtg)ptgs[0]).getValue().equals(value));
        }
-       
-       public void testLookupAndMatchFunctionArgs()
-       {
-               FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null);
-               fp.parse();
-               Ptg[] ptg = fp.getRPNPtg();
 
-               assertTrue("got 4 ptg", ptg.length == 4);
-               assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
-               
-               fp = new FormulaParser("match(A1, A3:A52)", null);
-               fp.parse();
-               ptg = fp.getRPNPtg();
+       public void testLookupAndMatchFunctionArgs() {
+               Ptg[] ptgs = parseFormula("lookup(A1, A3:A52, B3:B52)");
+
+               assertEquals(4, ptgs.length);
+               assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
 
-               assertTrue("got 3 ptg", ptg.length == 3);
-               assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
+               ptgs = parseFormula("match(A1, A3:A52)");
+
+               assertEquals(3, ptgs.length);
+               assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
        }
-       
+
        /** bug 33160*/
        public void testLargeInt() {
-               FormulaParser fp = new FormulaParser("40", null);
-               fp.parse();
-               Ptg[] ptg=fp.getRPNPtg();
-               assertTrue("ptg is Int, is "+ptg[0].getClass(),ptg[0] instanceof IntPtg);
-               
-               fp = new FormulaParser("40000", null);
-               fp.parse();
-               ptg=fp.getRPNPtg();
-               assertTrue("ptg should be  IntPtg, is "+ptg[0].getClass(), ptg[0] instanceof IntPtg);
+               Ptg[] ptgs = parseFormula("40");
+               assertTrue("ptg is Int, is "+ptgs[0].getClass(),ptgs[0] instanceof IntPtg);
+
+               ptgs = parseFormula("40000");
+               assertTrue("ptg should be  IntPtg, is "+ptgs[0].getClass(), ptgs[0] instanceof IntPtg);
        }
 
        /** bug 33160, testcase by Amol Deshmukh*/
        public void testSimpleLongFormula() {
-               FormulaParser fp = new FormulaParser("40000/2", null);
-               fp.parse();
-               Ptg[] ptgs = fp.getRPNPtg();
-               assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
+               Ptg[] ptgs = parseFormula("40000/2");
+               assertEquals(3, ptgs.length);
                assertTrue("IntPtg", (ptgs[0] instanceof IntPtg));
                assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
                assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
        }
-       
+
        /** bug 35027, underscore in sheet name */
        public void testUnderscore() {
                HSSFWorkbook wb = new HSSFWorkbook();
-       
-       wb.createSheet("Cash_Flow");
-       
-       HSSFSheet sheet = wb.createSheet("Test");
-       HSSFRow row = sheet.createRow(0);
-       HSSFCell cell;
-       
-       cell = row.createCell((short)0);
-       cell.setCellFormula("Cash_Flow!A1");
-               
+
+               wb.createSheet("Cash_Flow");
+
+               HSSFSheet sheet = wb.createSheet("Test");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell;
+
+               cell = row.createCell((short)0);
+               cell.setCellFormula("Cash_Flow!A1");
+       }
+
+       // bug 38396 : Formula with exponential numbers not parsed correctly.
+       public void testExponentialParsing() {
+               Ptg[] ptgs;
+               ptgs = parseFormula("1.3E21/2");
+               assertEquals(3, ptgs.length);
+               assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+               assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+               assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
+
+               ptgs = parseFormula("1322E21/2");
+               assertEquals(3, ptgs.length);
+               assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+               assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+               assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
+
+               ptgs = parseFormula("1.3E1/2");
+               assertEquals(3, ptgs.length);
+               assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+               assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+               assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
        }
 
-    // bug 38396 : Formula with exponential numbers not parsed correctly.
-    public void testExponentialParsing() {
-        FormulaParser fp = new FormulaParser("1.3E21/2", null);
-        fp.parse();
-        Ptg[] ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-        fp = new FormulaParser("1322E21/2", null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-        fp = new FormulaParser("1.3E1/2", null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-    }
-    public void testExponentialInSheet() throws Exception {
-        HSSFWorkbook wb = new HSSFWorkbook();
-
-        wb.createSheet("Cash_Flow");
-
-        HSSFSheet sheet = wb.createSheet("Test");
-        HSSFRow row = sheet.createRow(0);
-        HSSFCell cell = row.createCell((short)0);
-        String formula = null;
+       public void testExponentialInSheet() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+
+               wb.createSheet("Cash_Flow");
+
+               HSSFSheet sheet = wb.createSheet("Test");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell = row.createCell((short)0);
+               String formula = null;
+
+               cell.setCellFormula("1.3E21/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1.3E21/3", formula);
+
+               cell.setCellFormula("-1.3E21/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.3E21/3", formula);
+
+               cell.setCellFormula("1322E21/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1.322E24/3", formula);
+
+               cell.setCellFormula("-1322E21/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.322E24/3", formula);
+
+               cell.setCellFormula("1.3E1/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "13.0/3", formula);
+
+               cell.setCellFormula("-1.3E1/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-13.0/3", formula);
+
+               cell.setCellFormula("1.3E-4/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1.3E-4/3", formula);
 
-        cell.setCellFormula("1.3E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.3E21/3", formula);
-
-        cell.setCellFormula("-1.3E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.3E21/3", formula);
+               cell.setCellFormula("-1.3E-4/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.3E-4/3", formula);
 
-        cell.setCellFormula("1322E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.322E24/3", formula);
+               cell.setCellFormula("13E-15/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1.3E-14/3", formula);
 
-        cell.setCellFormula("-1322E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.322E24/3", formula);
+               cell.setCellFormula("-13E-15/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.3E-14/3", formula);
 
-        cell.setCellFormula("1.3E1/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "13.0/3", formula);
+               cell.setCellFormula("1.3E3/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1300.0/3", formula);
 
-        cell.setCellFormula("-1.3E1/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-13.0/3", formula);
+               cell.setCellFormula("-1.3E3/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1300.0/3", formula);
 
-        cell.setCellFormula("1.3E-4/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.3E-4/3", formula);
+               cell.setCellFormula("1300000000000000/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "1.3E15/3", formula);
 
-        cell.setCellFormula("-1.3E-4/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.3E-4/3", formula);
-
-        cell.setCellFormula("13E-15/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.3E-14/3", formula);
-
-        cell.setCellFormula("-13E-15/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.3E-14/3", formula);
-
-        cell.setCellFormula("1.3E3/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1300.0/3", formula);
-
-        cell.setCellFormula("-1.3E3/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1300.0/3", formula);
-
-        cell.setCellFormula("1300000000000000/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.3E15/3", formula);
-
-        cell.setCellFormula("-1300000000000000/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.3E15/3", formula);
-
-        cell.setCellFormula("-10E-1/3.1E2*4E3/3E4");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.0/310.0*4000.0/30000.0", formula);
-    }
-
-     public static void main(String [] args) {
-        System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser");
-        junit.textui.TestRunner.run(TestFormulaParser.class);
-    }
-
-    public void testNumbers() {
-        HSSFWorkbook wb = new HSSFWorkbook();
-
-        wb.createSheet("Cash_Flow");
-
-        HSSFSheet sheet = wb.createSheet("Test");
-        HSSFRow row = sheet.createRow(0);
-        HSSFCell cell = row.createCell((short)0);
-        String formula = null;
-
-        // starts from decimal point
-
-        cell.setCellFormula(".1");
-        formula = cell.getCellFormula();
-        assertEquals("0.1", formula);
-
-        cell.setCellFormula("+.1");
-        formula = cell.getCellFormula();
-        assertEquals("+0.1", formula);
-
-        cell.setCellFormula("-.1");
-        formula = cell.getCellFormula();
-        assertEquals("-0.1", formula);
-
-        // has exponent
-
-        cell.setCellFormula("10E1");
-        formula = cell.getCellFormula();
-        assertEquals("100.0", formula);
-
-        cell.setCellFormula("10E+1");
-        formula = cell.getCellFormula();
-        assertEquals("100.0", formula);
-
-        cell.setCellFormula("10E-1");
-        formula = cell.getCellFormula();
-        assertEquals("1.0", formula);
-    }
-
-    public void testRanges() {
-        HSSFWorkbook wb = new HSSFWorkbook();
-
-        wb.createSheet("Cash_Flow");
-
-        HSSFSheet sheet = wb.createSheet("Test");
-        HSSFRow row = sheet.createRow(0);
-        HSSFCell cell = row.createCell((short)0);
-        String formula = null;
-
-        cell.setCellFormula("A1.A2");
-        formula = cell.getCellFormula();
-        assertEquals("A1:A2", formula);
-
-        cell.setCellFormula("A1..A2");
-        formula = cell.getCellFormula();
-        assertEquals("A1:A2", formula);
-
-        cell.setCellFormula("A1...A2");
-        formula = cell.getCellFormula();
-        assertEquals("A1:A2", formula);
-    }
-
-    /**
-     * Test for bug observable at svn revision 618865 (5-Feb-2008)<br/>
-     * a formula consisting of a single no-arg function got rendered without the function braces
-     */
-    public void testToFormulaStringZeroArgFunction() {
-       HSSFWorkbook book = new HSSFWorkbook();
-
-        Ptg[] ptgs = {
-                new FuncPtg(10),
-        };
-        assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
-    }
-
-    public void testPercent() {
-        Ptg[] ptgs;
-        ptgs = parseFormula("5%");
-        assertEquals(2, ptgs.length);
-        assertEquals(ptgs[0].getClass(), IntPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
-        // spaces OK
-        ptgs = parseFormula(" 250 % ");
-        assertEquals(2, ptgs.length);
-        assertEquals(ptgs[0].getClass(), IntPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
-
-        // double percent OK
-        ptgs = parseFormula("12345.678%%");
-        assertEquals(3, ptgs.length);
-        assertEquals(ptgs[0].getClass(), NumberPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-        assertEquals(ptgs[2].getClass(), PercentPtg.class);
-
-        // percent of a bracketed expression
-        ptgs = parseFormula("(A1+35)%*B1%");
-        assertEquals(8, ptgs.length);
-        assertEquals(ptgs[4].getClass(), PercentPtg.class);
-        assertEquals(ptgs[6].getClass(), PercentPtg.class);
-
-        // percent of a text quantity
-        ptgs = parseFormula("\"8.75\"%");
-        assertEquals(2, ptgs.length);
-        assertEquals(ptgs[0].getClass(), StringPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
-        // percent to the power of
-        ptgs = parseFormula("50%^3");
-        assertEquals(4, ptgs.length);
-        assertEquals(ptgs[0].getClass(), IntPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-        assertEquals(ptgs[2].getClass(), IntPtg.class);
-        assertEquals(ptgs[3].getClass(), PowerPtg.class);
-
-        //
-        // things that parse OK but would *evaluate* to an error
-
-        ptgs = parseFormula("\"abc\"%");
-        assertEquals(2, ptgs.length);
-        assertEquals(ptgs[0].getClass(), StringPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
-        ptgs = parseFormula("#N/A%");
-        assertEquals(2, ptgs.length);
-        assertEquals(ptgs[0].getClass(), ErrPtg.class);
-        assertEquals(ptgs[1].getClass(), PercentPtg.class);
-    }
-
-    /**
-     * Tests combinations of various operators in the absence of brackets
-     */
-    public void testPrecedenceAndAssociativity() {
-
-        Class[] expClss;
-
-        // TRUE=TRUE=2=2  evaluates to FALSE
-        expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
-                IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class,  };
-        confirmTokenClasses("TRUE=TRUE=2=2", expClss);
-
-
-        //  2^3^2    evaluates to 64 not 512
-        expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
-                IntPtg.class, PowerPtg.class, };
-        confirmTokenClasses("2^3^2", expClss);
-
-        // "abc" & 2 + 3 & "def"   evaluates to "abc5def"
-        expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
-                AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
-        confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
-
-
-        //  (1 / 2) - (3 * 4)
-        expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
-                IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
-        confirmTokenClasses("1/2-3*4", expClss);
-
-        // 2 * (2^2)
-        expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
-        // NOT: (2 *2) ^ 2 -> int int multiply int power
-        confirmTokenClasses("2*2^2", expClss);
-
-        //  2^200% -> 2 not 1.6E58
-        expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
-        confirmTokenClasses("2^200%", expClss);
-    }
-
-    private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
-        Ptg[] ptgs = parseFormula(formula);
-        assertEquals(expectedClasses.length, ptgs.length);
-        for (int i = 0; i < expectedClasses.length; i++) {
-            if(expectedClasses[i] != ptgs[i].getClass()) {
-                fail("difference at token[" + i + "]: expected ("
-                    + expectedClasses[i].getName() + ") but got ("
-                    + ptgs[i].getClass().getName() + ")");
-            }
-        }
-    }
-
-    public void testPower() {
-        confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
-    }
-
-    private static Ptg parseSingleToken(String formula, Class ptgClass) {
-        Ptg[] ptgs = parseFormula(formula);
-        assertEquals(1, ptgs.length);
-        Ptg result = ptgs[0];
-        assertEquals(ptgClass, result.getClass());
-        return result;
-    }
-
-    public void testParseNumber() {
-        IntPtg ip;
-
-        // bug 33160
-        ip = (IntPtg) parseSingleToken("40", IntPtg.class);
-        assertEquals(40, ip.getValue());
-        ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
-        assertEquals(40000, ip.getValue());
-
-        // check the upper edge of the IntPtg range:
-        ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
-        assertEquals(65535, ip.getValue());
-        NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
-        assertEquals(65536, np.getValue(), 0);
-
-        np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
-        assertEquals(65534.6, np.getValue(), 0);
-    }
-
-    public void testMissingArgs() {
-
-        Class[] expClss;
-
-        expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
-                FuncVarPtg.class, };
-        confirmTokenClasses("if(A1, ,C1)", expClss);
-
-        expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
-                FuncVarPtg.class, };
-        confirmTokenClasses("counta( , A1:B2, )", expClss);
-    }
-
-    public void testParseErrorLiterals() {
-
-        confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
-        confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
-        confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
-        confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
-        confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
-        confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
-        confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
-    }
-
-    private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
-        assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
-    }
-
-    /**
-     * To aid readability the parameters have been encoded with single quotes instead of double
-     * quotes.  This method converts single quotes to double quotes before performing the parse
-     * and result check.
-     */
-    private static void confirmStringParse(String singleQuotedValue) {
-        // formula: internal quotes become double double, surround with double quotes
-        String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
-        String expectedValue = singleQuotedValue.replace('\'', '"');
-
-        StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
-        assertEquals(expectedValue, sp.getValue());
-    }
-    public void testParseStringLiterals_bug28754() {
-
-        StringPtg sp;
-        try {
-            sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
-        } catch (RuntimeException e) {
-            if(e.getMessage().startsWith("Cannot Parse")) {
-                throw new AssertionFailedError("Identified bug 28754a");
-            }
-            throw e;
-        }
-        assertEquals("test\"ing", sp.getValue());
-
-        HSSFWorkbook wb = new HSSFWorkbook();
-        HSSFSheet sheet = wb.createSheet();
-        wb.setSheetName(0, "Sheet1");
-
-        HSSFRow row = sheet.createRow(0);
-        HSSFCell cell = row.createCell((short)0);
-        cell.setCellFormula("right(\"test\"\"ing\", 3)");
-        String actualCellFormula = cell.getCellFormula();
-        if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
-            throw new AssertionFailedError("Identified bug 28754b");
-        }
-        assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
-    }
-
-    public void testParseStringLiterals() {
-        confirmStringParse("goto considered harmful");
-
-        confirmStringParse("goto 'considered' harmful");
-
-        confirmStringParse("");
-        confirmStringParse("'");
-        confirmStringParse("''");
-        confirmStringParse("' '");
-        confirmStringParse(" ' ");
-    }
-
-    public void testParseSumIfSum() {
-        String formulaString;
-        Ptg[] ptgs;
-        ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
-        formulaString = FormulaParser.toFormulaString(null, ptgs);
-        assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
-
-        ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
-        formulaString = FormulaParser.toFormulaString(null, ptgs);
-        assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
-    }
-    public void testParserErrors() {
-        parseExpectedException("1 2");
-        parseExpectedException(" 12 . 345  ");
-        parseExpectedException("1 .23  ");
-
-        parseExpectedException("sum(#NAME)");
-        parseExpectedException("1 + #N / A * 2");
-        parseExpectedException("#value?");
-        parseExpectedException("#DIV/ 0+2");
-
-
-        parseExpectedException("IF(TRUE)");
-        parseExpectedException("countif(A1:B5, C1, D1)");
-    }
-
-    private static void parseExpectedException(String formula) {
-        try {
-            parseFormula(formula);
-            throw new AssertionFailedError("expected parse exception");
-        } catch (FormulaParseException e) {
-            // expected during successful test
-            assertNotNull(e.getMessage());
-        } catch (RuntimeException e) {
-            e.printStackTrace();
-            fail("Wrong exception:" + e.getMessage());
-        }
-    }
-
-    public void testSetFormulaWithRowBeyond32768_Bug44539() {
-
-        HSSFWorkbook wb = new HSSFWorkbook();
-        HSSFSheet sheet = wb.createSheet();
-        wb.setSheetName(0, "Sheet1");
-
-        HSSFRow row = sheet.createRow(0);
-        HSSFCell cell = row.createCell((short)0);
-        cell.setCellFormula("SUM(A32769:A32770)");
-        if("SUM(A-32767:A-32766)".equals(cell.getCellFormula())) {
-            fail("Identified bug 44539");
-        }
-        assertEquals("SUM(A32769:A32770)", cell.getCellFormula());
-    }
-
-    public void testSpaceAtStartOfFormula() {
-        // Simulating cell formula of "= 4" (note space)
-        // The same Ptg array can be observed if an excel file is saved with that exact formula
-
-        AttrPtg spacePtg = AttrPtg.createSpace(AttrPtg.SpaceType.SPACE_BEFORE, 1);
-        Ptg[] ptgs = { spacePtg, new IntPtg(4), };
-        String formulaString;
-        try {
-            formulaString = FormulaParser.toFormulaString(null, ptgs);
-        } catch (IllegalStateException e) {
-            if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
-                throw new AssertionFailedError("Identified bug 44609");
-            }
-            // else some unexpected error
-            throw e;
-        }
-        // FormulaParser strips spaces anyway
-        assertEquals("4", formulaString);
-
-        ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
-        formulaString = FormulaParser.toFormulaString(null, ptgs);
-        assertEquals("3+4", formulaString);
-    }
-
-    /**
-     * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
-     */
-    public void testTooFewOperandArgs() {
-        // Simulating badly encoded cell formula of "=/1"
-        // Not sure if Excel could ever produce this
-        Ptg[] ptgs = {
-                // Excel would probably have put tMissArg here
-                new IntPtg(1),
-                new DividePtg(),
-        };
-        try {
-            FormulaParser.toFormulaString(null, ptgs);
-            fail("Expected exception was not thrown");
-        } catch (IllegalStateException e) {
-            // expected during successful test
-            assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
-        }
-    }
-    /**
-     * Make sure that POI uses the right Func Ptg when encoding formulas.  Functions with variable
-     * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.<p/>
-     * 
-     * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions.  In many cases
-     * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases 
-     * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. 
-     */
-    public void testFuncPtgSelection() {
-        HSSFWorkbook book = new HSSFWorkbook();
-        Ptg[] ptgs;
-        ptgs = FormulaParser.parse("countif(A1:A2, 1)", book);
-        assertEquals(3, ptgs.length);
-        if(FuncVarPtg.class == ptgs[2].getClass()) {
-            throw new AssertionFailedError("Identified bug 44675");
-        }
-        assertEquals(FuncPtg.class, ptgs[2].getClass());
-        ptgs = FormulaParser.parse("sin(1)", book);
-        assertEquals(2, ptgs.length);
-        assertEquals(FuncPtg.class, ptgs[1].getClass());
-    }
-    
-    public void testWrongNumberOfFunctionArgs() {
-        confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0.");
-        confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4.");
-        confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6.");
-        confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2.");
-    }
-
-    private static void confirmArgCountMsg(String formula, String expectedMessage) {
-        HSSFWorkbook book = new HSSFWorkbook();
-        try {
-            FormulaParser.parse(formula, book);
-            throw new AssertionFailedError("Didn't get parse exception as expected");
-        } catch (FormulaParseException e) {
-            assertEquals(expectedMessage, e.getMessage());
-        }
-    }
+               cell.setCellFormula("-1300000000000000/3");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.3E15/3", formula);
+
+               cell.setCellFormula("-10E-1/3.1E2*4E3/3E4");
+               formula = cell.getCellFormula();
+               assertEquals("Exponential formula string", "-1.0/310.0*4000.0/30000.0", formula);
+       }
+
+       public void testNumbers() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+
+               wb.createSheet("Cash_Flow");
+
+               HSSFSheet sheet = wb.createSheet("Test");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell = row.createCell((short)0);
+               String formula = null;
+
+               // starts from decimal point
+
+               cell.setCellFormula(".1");
+               formula = cell.getCellFormula();
+               assertEquals("0.1", formula);
+
+               cell.setCellFormula("+.1");
+               formula = cell.getCellFormula();
+               assertEquals("+0.1", formula);
+
+               cell.setCellFormula("-.1");
+               formula = cell.getCellFormula();
+               assertEquals("-0.1", formula);
+
+               // has exponent
+
+               cell.setCellFormula("10E1");
+               formula = cell.getCellFormula();
+               assertEquals("100.0", formula);
+
+               cell.setCellFormula("10E+1");
+               formula = cell.getCellFormula();
+               assertEquals("100.0", formula);
+
+               cell.setCellFormula("10E-1");
+               formula = cell.getCellFormula();
+               assertEquals("1.0", formula);
+       }
+
+       public void testRanges() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+
+               wb.createSheet("Cash_Flow");
+
+               HSSFSheet sheet = wb.createSheet("Test");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell = row.createCell((short)0);
+               String formula = null;
+
+               cell.setCellFormula("A1.A2");
+               formula = cell.getCellFormula();
+               assertEquals("A1:A2", formula);
+
+               cell.setCellFormula("A1..A2");
+               formula = cell.getCellFormula();
+               assertEquals("A1:A2", formula);
+
+               cell.setCellFormula("A1...A2");
+               formula = cell.getCellFormula();
+               assertEquals("A1:A2", formula);
+       }
+
+       /**
+        * Test for bug observable at svn revision 618865 (5-Feb-2008)<br/>
+        * a formula consisting of a single no-arg function got rendered without the function braces
+        */
+       public void testToFormulaStringZeroArgFunction() {
+               HSSFWorkbook book = new HSSFWorkbook();
+
+               Ptg[] ptgs = {
+                               new FuncPtg(10),
+               };
+               assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
+       }
+
+       public void testPercent() {
+               Ptg[] ptgs;
+               ptgs = parseFormula("5%");
+               assertEquals(2, ptgs.length);
+               assertEquals(ptgs[0].getClass(), IntPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+               // spaces OK
+               ptgs = parseFormula(" 250 % ");
+               assertEquals(2, ptgs.length);
+               assertEquals(ptgs[0].getClass(), IntPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+
+               // double percent OK
+               ptgs = parseFormula("12345.678%%");
+               assertEquals(3, ptgs.length);
+               assertEquals(ptgs[0].getClass(), NumberPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+               assertEquals(ptgs[2].getClass(), PercentPtg.class);
+
+               // percent of a bracketed expression
+               ptgs = parseFormula("(A1+35)%*B1%");
+               assertEquals(8, ptgs.length);
+               assertEquals(ptgs[4].getClass(), PercentPtg.class);
+               assertEquals(ptgs[6].getClass(), PercentPtg.class);
+
+               // percent of a text quantity
+               ptgs = parseFormula("\"8.75\"%");
+               assertEquals(2, ptgs.length);
+               assertEquals(ptgs[0].getClass(), StringPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+               // percent to the power of
+               ptgs = parseFormula("50%^3");
+               assertEquals(4, ptgs.length);
+               assertEquals(ptgs[0].getClass(), IntPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+               assertEquals(ptgs[2].getClass(), IntPtg.class);
+               assertEquals(ptgs[3].getClass(), PowerPtg.class);
+
+               //
+               // things that parse OK but would *evaluate* to an error
+
+               ptgs = parseFormula("\"abc\"%");
+               assertEquals(2, ptgs.length);
+               assertEquals(ptgs[0].getClass(), StringPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+               ptgs = parseFormula("#N/A%");
+               assertEquals(2, ptgs.length);
+               assertEquals(ptgs[0].getClass(), ErrPtg.class);
+               assertEquals(ptgs[1].getClass(), PercentPtg.class);
+       }
+
+       /**
+        * Tests combinations of various operators in the absence of brackets
+        */
+       public void testPrecedenceAndAssociativity() {
+
+               Class[] expClss;
+
+               // TRUE=TRUE=2=2  evaluates to FALSE
+               expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
+                               IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class,  };
+               confirmTokenClasses("TRUE=TRUE=2=2", expClss);
+
+
+               //  2^3^2       evaluates to 64 not 512
+               expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
+                               IntPtg.class, PowerPtg.class, };
+               confirmTokenClasses("2^3^2", expClss);
+
+               // "abc" & 2 + 3 & "def"   evaluates to "abc5def"
+               expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
+                               AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
+               confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
+
+
+               //  (1 / 2) - (3 * 4)
+               expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
+                               IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
+               confirmTokenClasses("1/2-3*4", expClss);
+
+               // 2 * (2^2)
+               expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
+               // NOT: (2 *2) ^ 2 -> int int multiply int power
+               confirmTokenClasses("2*2^2", expClss);
+
+               //  2^200% -> 2 not 1.6E58
+               expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
+               confirmTokenClasses("2^200%", expClss);
+       }
+
+       private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
+               Ptg[] ptgs = parseFormula(formula);
+               assertEquals(expectedClasses.length, ptgs.length);
+               for (int i = 0; i < expectedClasses.length; i++) {
+                       if(expectedClasses[i] != ptgs[i].getClass()) {
+                               fail("difference at token[" + i + "]: expected ("
+                                       + expectedClasses[i].getName() + ") but got ("
+                                       + ptgs[i].getClass().getName() + ")");
+                       }
+               }
+       }
+
+       public void testPower() {
+               confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
+       }
+
+       private static Ptg parseSingleToken(String formula, Class ptgClass) {
+               Ptg[] ptgs = parseFormula(formula);
+               assertEquals(1, ptgs.length);
+               Ptg result = ptgs[0];
+               assertEquals(ptgClass, result.getClass());
+               return result;
+       }
+
+       public void testParseNumber() {
+               IntPtg ip;
+
+               // bug 33160
+               ip = (IntPtg) parseSingleToken("40", IntPtg.class);
+               assertEquals(40, ip.getValue());
+               ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
+               assertEquals(40000, ip.getValue());
+
+               // check the upper edge of the IntPtg range:
+               ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
+               assertEquals(65535, ip.getValue());
+               NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
+               assertEquals(65536, np.getValue(), 0);
+
+               np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
+               assertEquals(65534.6, np.getValue(), 0);
+       }
+
+       public void testMissingArgs() {
+
+               Class[] expClss;
+
+               expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
+                               FuncVarPtg.class, };
+               confirmTokenClasses("if(A1, ,C1)", expClss);
+
+               expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
+                               FuncVarPtg.class, };
+               confirmTokenClasses("counta( , A1:B2, )", expClss);
+       }
+
+       public void testParseErrorLiterals() {
+
+               confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
+               confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
+               confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
+               confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
+               confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
+               confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
+               confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
+       }
+
+       private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
+               assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
+       }
+
+       /**
+        * To aid readability the parameters have been encoded with single quotes instead of double
+        * quotes.  This method converts single quotes to double quotes before performing the parse
+        * and result check.
+        */
+       private static void confirmStringParse(String singleQuotedValue) {
+               // formula: internal quotes become double double, surround with double quotes
+               String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
+               String expectedValue = singleQuotedValue.replace('\'', '"');
+
+               StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
+               assertEquals(expectedValue, sp.getValue());
+       }
+       public void testParseStringLiterals_bug28754() {
+
+               StringPtg sp;
+               try {
+                       sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
+               } catch (RuntimeException e) {
+                       if(e.getMessage().startsWith("Cannot Parse")) {
+                               throw new AssertionFailedError("Identified bug 28754a");
+                       }
+                       throw e;
+               }
+               assertEquals("test\"ing", sp.getValue());
+
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet();
+               wb.setSheetName(0, "Sheet1");
+
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell = row.createCell((short)0);
+               cell.setCellFormula("right(\"test\"\"ing\", 3)");
+               String actualCellFormula = cell.getCellFormula();
+               if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
+                       throw new AssertionFailedError("Identified bug 28754b");
+               }
+               assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
+       }
+
+       public void testParseStringLiterals() {
+               confirmStringParse("goto considered harmful");
+
+               confirmStringParse("goto 'considered' harmful");
+
+               confirmStringParse("");
+               confirmStringParse("'");
+               confirmStringParse("''");
+               confirmStringParse("' '");
+               confirmStringParse(" ' ");
+       }
+
+       public void testParseSumIfSum() {
+               String formulaString;
+               Ptg[] ptgs;
+               ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
+               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
+
+               ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
+               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
+       }
+       public void testParserErrors() {
+               parseExpectedException("1 2");
+               parseExpectedException(" 12 . 345  ");
+               parseExpectedException("1 .23  ");
+
+               parseExpectedException("sum(#NAME)");
+               parseExpectedException("1 + #N / A * 2");
+               parseExpectedException("#value?");
+               parseExpectedException("#DIV/ 0+2");
+
+
+               parseExpectedException("IF(TRUE)");
+               parseExpectedException("countif(A1:B5, C1, D1)");
+       }
+
+       private static void parseExpectedException(String formula) {
+               try {
+                       parseFormula(formula);
+                       throw new AssertionFailedError("expected parse exception");
+               } catch (FormulaParseException e) {
+                       // expected during successful test
+                       assertNotNull(e.getMessage());
+               } catch (RuntimeException e) {
+                       e.printStackTrace();
+                       fail("Wrong exception:" + e.getMessage());
+               }
+       }
+
+       public void testSetFormulaWithRowBeyond32768_Bug44539() {
+
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet();
+               wb.setSheetName(0, "Sheet1");
+
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell = row.createCell((short)0);
+               cell.setCellFormula("SUM(A32769:A32770)");
+               if("SUM(A-32767:A-32766)".equals(cell.getCellFormula())) {
+                       fail("Identified bug 44539");
+               }
+               assertEquals("SUM(A32769:A32770)", cell.getCellFormula());
+       }
+
+       public void testSpaceAtStartOfFormula() {
+               // Simulating cell formula of "= 4" (note space)
+               // The same Ptg array can be observed if an excel file is saved with that exact formula
+
+               AttrPtg spacePtg = AttrPtg.createSpace(AttrPtg.SpaceType.SPACE_BEFORE, 1);
+               Ptg[] ptgs = { spacePtg, new IntPtg(4), };
+               String formulaString;
+               try {
+                       formulaString = FormulaParser.toFormulaString(null, ptgs);
+               } catch (IllegalStateException e) {
+                       if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
+                               throw new AssertionFailedError("Identified bug 44609");
+                       }
+                       // else some unexpected error
+                       throw e;
+               }
+               // FormulaParser strips spaces anyway
+               assertEquals("4", formulaString);
+
+               ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
+               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               assertEquals("3+4", formulaString);
+       }
+
+       /**
+        * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
+        */
+       public void testTooFewOperandArgs() {
+               // Simulating badly encoded cell formula of "=/1"
+               // Not sure if Excel could ever produce this
+               Ptg[] ptgs = {
+                               // Excel would probably have put tMissArg here
+                               new IntPtg(1),
+                               new DividePtg(),
+               };
+               try {
+                       FormulaParser.toFormulaString(null, ptgs);
+                       fail("Expected exception was not thrown");
+               } catch (IllegalStateException e) {
+                       // expected during successful test
+                       assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
+               }
+       }
+       /**
+        * Make sure that POI uses the right Func Ptg when encoding formulas.  Functions with variable
+        * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.<p/>
+        * 
+        * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions.  In many cases
+        * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases 
+        * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. 
+        */
+       public void testFuncPtgSelection() {
+
+               Ptg[] ptgs;
+               ptgs = parseFormula("countif(A1:A2, 1)");
+               assertEquals(3, ptgs.length);
+               if(FuncVarPtg.class == ptgs[2].getClass()) {
+                       throw new AssertionFailedError("Identified bug 44675");
+               }
+               assertEquals(FuncPtg.class, ptgs[2].getClass());
+               ptgs = parseFormula("sin(1)");
+               assertEquals(2, ptgs.length);
+               assertEquals(FuncPtg.class, ptgs[1].getClass());
+       }
+
+       public void testWrongNumberOfFunctionArgs() {
+               confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0.");
+               confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4.");
+               confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6.");
+               confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2.");
+       }
+
+       private static void confirmArgCountMsg(String formula, String expectedMessage) {
+               HSSFWorkbook book = new HSSFWorkbook();
+               try {
+                       FormulaParser.parse(formula, book);
+                       throw new AssertionFailedError("Didn't get parse exception as expected");
+               } catch (FormulaParseException e) {
+                       assertEquals(expectedMessage, e.getMessage());
+               }
+       }
+
+       public void testParseErrorExpecteMsg() {
+
+               try {
+                       parseFormula("round(3.14;2)");
+                       throw new AssertionFailedError("Didn't get parse exception as expected");
+               } catch (FormulaParseException e) {
+                       assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
+               }
+       }
 }
index 9281eb80d7c0a8767f2d7c32b23ce57187ea1fbb..3bdcae57cb7b65f23a49ad7faf4ff8b1c17b3ad9 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hssf.model;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 import org.apache.poi.hssf.record.*;
 import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
@@ -351,5 +352,25 @@ public final class TestSheet extends TestCase {
         xfindex = sheet.getXFIndexForColAt((short) 10);
         assertEquals(DEFAULT_IDX, xfindex);
     }
+
+    /**
+     * Prior to bug 45066, POI would get the estimated sheet size wrong 
+     * when an <tt>UncalcedRecord</tt> was present.<p/>
+     */
+    public void testUncalcSize_bug45066() {
+
+        List records = new ArrayList();
+        records.add(new BOFRecord());
+        records.add(new UncalcedRecord());
+        records.add(new EOFRecord());
+        Sheet sheet = Sheet.createSheet( records, 0, 0 );
+
+        int estimatedSize = sheet.getSize();
+        int serializedSize = sheet.serialize(0, new byte[estimatedSize]);
+        if (serializedSize != estimatedSize) {
+            throw new AssertionFailedError("Identified bug 45066 b");
+        }
+        assertEquals(50, serializedSize);
+    }
 }
 
index 47137df4f6440d3f6299e7639e3920fa28d76161..7702fce3d4db5487ca056bcd9e4950c269028794 100644 (file)
@@ -26,9 +26,12 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -37,7 +40,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
-import java.util.zip.CRC32;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 
@@ -149,7 +151,6 @@ public final class ExcelFileFormatDocFunctionExtractor {
        
        private static final class FunctionDataCollector {
 
-
                private final Map _allFunctionsByIndex;
                private final Map _allFunctionsByName;
                private final Set _groupFunctionIndexes;
@@ -184,25 +185,29 @@ public final class ExcelFileFormatDocFunctionExtractor {
                        _allFunctionsByName.put(funcName, fd);
                }
 
+               /**
+                * Some extra validation here.
+                * Any function which changes definition will have a footnote in the source document
+                */
                private void checkRedefinedFunction(boolean hasNote, String funcName, Integer funcIxKey) {
                        FunctionData fdPrev;
+                       // check by index
                        fdPrev = (FunctionData) _allFunctionsByIndex.get(funcIxKey);
                        if(fdPrev != null) {
-                               if(fdPrev.hasFootnote() && hasNote) {
-                                       // func def can change if both have a foot-note
-                                       _allFunctionsByName.remove(fdPrev.getName());
-                               } else {
-                                       throw new RuntimeException("changing function definition without foot-note");
+                               if(!fdPrev.hasFootnote() || !hasNote) {
+                                       throw new RuntimeException("changing function [" 
+                                                       + funcIxKey + "] definition without foot-note");
                                }
+                               _allFunctionsByName.remove(fdPrev.getName());
                        }
+                       // check by name
                        fdPrev = (FunctionData) _allFunctionsByName.get(funcName);
                        if(fdPrev != null) {
-                               if(fdPrev.hasFootnote() && hasNote) {
-                                       // func def can change if both have a foot-note
-                                       _allFunctionsByIndex.remove(new Integer(fdPrev.getIndex()));
-                               } else {
-                                       throw new RuntimeException("changing function definition without foot-note");
+                               if(!fdPrev.hasFootnote() || !hasNote) {
+                                       throw new RuntimeException("changing function '" 
+                                                       + funcName + "' definition without foot-note");
                                }
+                               _allFunctionsByIndex.remove(new Integer(fdPrev.getIndex()));
                        }
                }
 
@@ -237,9 +242,13 @@ public final class ExcelFileFormatDocFunctionExtractor {
                private static final String[] TABLE_CELL_RELPATH_NAMES = {
                        "table:table-row", "table:table-cell", "text:p",        
                };
-               private static final String[] NOTE_REF_RELPATH_NAMES = {
+               // after May 2008 there was one more style applied to the footnotes
+               private static final String[] NOTE_REF_RELPATH_NAMES_OLD = { 
                        "table:table-row", "table:table-cell", "text:p", "text:span", "text:note-ref",
                };
+               private static final String[] NOTE_REF_RELPATH_NAMES = {
+                       "table:table-row", "table:table-cell", "text:p", "text:span", "text:span", "text:note-ref",
+               };
 
 
                private final Stack _elemNameStack;
@@ -368,6 +377,8 @@ public final class ExcelFileFormatDocFunctionExtractor {
                        } else if(matchesRelPath(TABLE_CELL_RELPATH_NAMES)) {
                                _textNodeBuffer.setLength(0);
                                _cellHasNote = false;
+                       } else if(matchesRelPath(NOTE_REF_RELPATH_NAMES_OLD)) {
+                               _cellHasNote = true;
                        } else if(matchesRelPath(NOTE_REF_RELPATH_NAMES)) {
                                _cellHasNote = true;
                        }
@@ -456,6 +467,9 @@ public final class ExcelFileFormatDocFunctionExtractor {
        }
 
        private static void processFile(File effDocFile, File outFile) {
+               if(!effDocFile.exists()) {
+                       throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist");
+               }
                OutputStream os;
                try {
                        os = new FileOutputStream(outFile);
@@ -475,7 +489,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
                ps.println("# Created by (" + genClass.getName() + ")");
                // identify the source file
                ps.print("# from source file '" + SOURCE_DOC_FILE_NAME + "'");
-               ps.println(" (size=" + effDocFile.length() + ", crc=" + getFileCRC(effDocFile) + ")");
+               ps.println(" (size=" + effDocFile.length() + ", md5=" + getFileMD5(effDocFile) + ")");
                ps.println("#");
                ps.println("#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )");
                ps.println("");
@@ -490,6 +504,14 @@ public final class ExcelFileFormatDocFunctionExtractor {
                        throw new RuntimeException(e);
                }
                ps.close();
+               
+               String canonicalOutputFileName;
+               try {
+                       canonicalOutputFileName = outFile.getCanonicalPath();
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+               System.out.println("Successfully output to '" + canonicalOutputFileName + "'");
        }
 
        private static void outputLicenseHeader(PrintStream ps) {
@@ -519,8 +541,14 @@ public final class ExcelFileFormatDocFunctionExtractor {
        /**
         * Helps identify the source file
         */
-       private static String getFileCRC(File f) {
-               CRC32 crc = new CRC32();
+       private static String getFileMD5(File f) {
+               MessageDigest m;
+               try {
+                       m = MessageDigest.getInstance("MD5");
+               } catch (NoSuchAlgorithmException e) {
+                       throw new RuntimeException(e);
+               }
+
                byte[]buf = new byte[2048];
                try {
                        InputStream is = new FileInputStream(f);
@@ -529,21 +557,17 @@ public final class ExcelFileFormatDocFunctionExtractor {
                                if(bytesRead<1) { 
                                        break;
                                }
-                               crc.update(buf, 0, bytesRead);
+                               m.update(buf, 0, bytesRead);
                        }
                        is.close();
                } catch (IOException e) {
                        throw new RuntimeException(e);
                }
-               return "0x" + Long.toHexString(crc.getValue()).toUpperCase();
+               
+               return "0x" + new BigInteger(1, m.digest()).toString(16);
        }
 
-       private static File getSourceFile() {
-               if (false) {
-                       File dir = new File("c:/temp");
-                       File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME);
-                       return effDocFile;
-               }
+       private static File downloadSourceFile() {
                URL url;
                try {
                        url = new URL("http://sc.openoffice.org/" + SOURCE_DOC_FILE_NAME);
@@ -557,7 +581,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
                        URLConnection conn = url.openConnection();
                        InputStream is = conn.getInputStream();
                        System.out.println("downloading " + url.toExternalForm());
-                       result = File.createTempFile("excelfileformat", "odt");
+                       result = File.createTempFile("excelfileformat", ".odt");
                        OutputStream os = new FileOutputStream(result);
                        while(true) {
                                int bytesRead = is.read(buf);
@@ -577,12 +601,17 @@ public final class ExcelFileFormatDocFunctionExtractor {
 
        public static void main(String[] args) {
 
-               File effDocFile = getSourceFile();
-               if(!effDocFile.exists()) {
-                       throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist");
-               }
-
                File outFile = new File("functionMetadata-asGenerated.txt");
-               processFile(effDocFile, outFile);
+
+               if (false) { // set true to use local file
+                       File dir = new File("c:/temp");
+                       File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME);
+                       processFile(effDocFile, outFile);
+                       return;
+               }
+               
+               File tempEFFDocFile = downloadSourceFile();
+               processFile(tempEFFDocFile, outFile);
+               tempEFFDocFile.delete();
        }
 }
index 1e26fa706ed1f90cb79111b85e42a388969bb31a..7030c5a508c0ca189a08780b4bd68e8a7e348847 100644 (file)
@@ -21,7 +21,6 @@ import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.model.FormulaParser;
-import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
 import org.apache.poi.hssf.record.formula.FuncPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
@@ -29,7 +28,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 /**
  * Tests parsing of some built-in functions that were not properly
- * registered in POI as bug #44675, #44733 (March/April 2008).
+ * registered in POI as of bug #44675, #44733 (March/April 2008).
  * 
  * @author Josh Micich
  */
@@ -76,7 +75,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
        }
        
        public void testUsdollar() {
-               confirmFunc("USDOLLAR(1)", 2, false, 204);
+               confirmFunc("USDOLLAR(1)", 2, true, 204);
        }
 
        public void testDBCS() {
index 6766f2fc073b9ebe7f4146125fd3a7720b4b979f..0a62d64cdbfba944ab39c668c5551c1419e34bfb 100644 (file)
 
 package org.apache.poi.hssf.record.formula.function;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
 
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
 /**
  * Tests reading from a sample spreadsheet some built-in functions that were not properly
- * registered in POI as bug #44675, #44733 (March/April 2008).
+ * registered in POI as of bug #44675, #44733 (March/April 2008).
  * 
  * @author Josh Micich
  */
index 0e18e21e52c93cf4be21916a9058f7fc101c5c3a..363e58c142788aad28784887703e87e7f483af2a 100755 (executable)
@@ -28,7 +28,7 @@ import junit.framework.TestSuite;
 public class AllUserModelTests {
        
        public static Test suite() {
-               TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.usermodel");
+               TestSuite result = new TestSuite(AllUserModelTests.class.getName());
                
                result.addTestSuite(TestBugs.class);
                result.addTestSuite(TestCellStyle.class);
@@ -58,6 +58,7 @@ public class AllUserModelTests {
                result.addTestSuite(TestHSSFSheetSetOrder.class);
                result.addTestSuite(TestHSSFTextbox.class);
                result.addTestSuite(TestHSSFWorkbook.class);
+               result.addTestSuite(TestLinkTable.class);
                result.addTestSuite(TestNamedRange.class);
                result.addTestSuite(TestOLE2Embeding.class);
                result.addTestSuite(TestPOIFSProperties.class);
index a2e8bd3baab451b0c7e3f3eb226f995ab98b6434..7f4375847780648e714b0a41c86921bcf8edb68d 100644 (file)
@@ -58,6 +58,30 @@ public final class TestHSSFCell extends TestCase {
         }
     }
     
+    public void testSetValues() throws Exception {
+        HSSFWorkbook book = new HSSFWorkbook();
+        HSSFSheet sheet = book.createSheet("test");
+        HSSFRow row = sheet.createRow(0);
+
+        HSSFCell cell = row.createCell((short)0);
+        
+        cell.setCellValue(1.2);
+        assertEquals(1.2, cell.getNumericCellValue(), 0.0001);
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cell.getCellType());
+        
+        cell.setCellValue(false);
+        assertEquals(false, cell.getBooleanCellValue());
+        assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, cell.getCellType());
+        
+        cell.setCellValue(new HSSFRichTextString("Foo"));
+        assertEquals("Foo", cell.getRichStringCellValue().getString());
+        assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCellType());
+        
+        cell.setCellValue(new HSSFRichTextString("345"));
+        assertEquals("345", cell.getRichStringCellValue().getString());
+        assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCellType());
+    }
+    
     /**
      * test that Boolean and Error types (BoolErrRecord) are supported properly.
      */
index cd0901a2912a8ca29499f361c3d3b58a333c7350..76c098da2df3e4a1342483b63e6e7983fc393437 100644 (file)
@@ -266,6 +266,8 @@ public class TestHSSFDateUtil extends TestCase {
         formats = new String[] {
                 "yyyy-mm-dd hh:mm:ss", "yyyy/mm/dd HH:MM:SS", 
                 "mm/dd HH:MM", "yy/mmm/dd SS",
+                "mm/dd HH:MM AM", "mm/dd HH:MM am",
+                "mm/dd HH:MM PM", "mm/dd HH:MM pm" 
         };
         for(int i=0; i<formats.length; i++) {
             assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
index 7599f2a16621d7be45d2ed17f64618319b9cd60f..3b154b2562541df18d51aacfdef69676fd36c990 100644 (file)
@@ -36,7 +36,6 @@ import org.apache.poi.hssf.record.VCenterRecord;
 import org.apache.poi.hssf.record.WSBoolRecord;
 import org.apache.poi.hssf.record.WindowTwoRecord;
 import org.apache.poi.ss.util.Region;
-import org.apache.poi.util.TempFile;
 
 /**
  * Tests HSSFSheet.  This test case is very incomplete at the moment.
@@ -54,10 +53,7 @@ public final class TestHSSFSheet extends TestCase {
        /**
         * Test the gridset field gets set as expected.
         */
-
-       public void testBackupRecord()
-                       throws Exception
-       {
+       public void testBackupRecord() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet s = wb.createSheet();
                Sheet sheet = s.getSheet();
@@ -70,10 +66,7 @@ public final class TestHSSFSheet extends TestCase {
        /**
         * Test vertically centered output.
         */
-
-       public void testVerticallyCenter()
-                       throws Exception
-       {
+       public void testVerticallyCenter() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet s = wb.createSheet();
                Sheet sheet = s.getSheet();
@@ -90,10 +83,7 @@ public final class TestHSSFSheet extends TestCase {
        /**
         * Test horizontally centered output.
         */
-
-       public void testHorizontallyCenter()
-                       throws Exception
-       {
+       public void testHorizontallyCenter() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet s = wb.createSheet();
                Sheet sheet = s.getSheet();
@@ -103,16 +93,13 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(false, record.getHCenter());
                s.setHorizontallyCenter(true);
                assertEquals(true, record.getHCenter());
-
        }
 
 
        /**
         * Test WSBboolRecord fields get set in the user model.
         */
-
-       public void testWSBool()
-       {
+       public void testWSBool() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet s = wb.createSheet();
                Sheet sheet = s.getSheet();
@@ -158,9 +145,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(true, s.getRowSumsRight());
        }
 
-       public void testReadBooleans()
-                       throws Exception
-       {
+       public void testReadBooleans() {
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet sheet = workbook.createSheet("Test boolean");
                HSSFRow row = sheet.createRow((short) 2);
@@ -168,23 +153,16 @@ public final class TestHSSFSheet extends TestCase {
                cell.setCellValue(true);
                cell = row.createCell((short) 11);
                cell.setCellValue(true);
-               File tempFile = TempFile.createTempFile("bool", "test.xls");
-               FileOutputStream stream = new FileOutputStream(tempFile);
-               workbook.write(stream);
-               stream.close();
+               
+               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
 
-               FileInputStream readStream = new FileInputStream(tempFile);
-               workbook = new HSSFWorkbook(readStream);
                sheet = workbook.getSheetAt(0);
                row = sheet.getRow(2);
-               stream.close();
-               tempFile.delete();
                assertNotNull(row);
                assertEquals(2, row.getPhysicalNumberOfCells());
        }
 
-       public void testRemoveRow()
-       {
+       public void testRemoveRow() {
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet sheet = workbook.createSheet("Test boolean");
                HSSFRow row = sheet.createRow((short) 2);
@@ -197,8 +175,8 @@ public final class TestHSSFSheet extends TestCase {
                HSSFRow row = sheet.createRow(0);
                HSSFCell cell = row.createCell((short) 0);
                HSSFCell cell2 = row.createCell((short) 1);
-               cell.setCellValue(new HSSFRichTextString("clone_test")); 
-               cell2.setCellFormula("sin(1)"); 
+               cell.setCellValue(new HSSFRichTextString("clone_test"));
+               cell2.setCellFormula("sin(1)");
 
                HSSFSheet clonedSheet = workbook.cloneSheet(0);
                HSSFRow clonedRow = clonedSheet.getRow(0);
@@ -234,7 +212,7 @@ public final class TestHSSFSheet extends TestCase {
 
                assertNotNull(workbook.getSheet("Test Clone"));
                assertNotNull(workbook.getSheet("Test Clone(1)"));
-               assertNotNull(workbook.getSheet("Test Clone(2)"));  
+               assertNotNull(workbook.getSheet("Test Clone(2)"));
        }
 
        /**
@@ -275,7 +253,7 @@ public final class TestHSSFSheet extends TestCase {
        /**
         * Setting landscape and portrait stuff on existing sheets
         */
-       public void testPrintSetupLandscapeExisting() throws Exception {
+       public void testPrintSetupLandscapeExisting() {
                HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls");
 
                assertEquals(3, workbook.getNumberOfSheets());
@@ -306,9 +284,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(1, sheetLS.getPrintSetup().getCopies());
 
                // Save and re-load, and check still there
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               workbook.write(baos);
-               workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray()));
+               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
 
                assertTrue(sheetL.getPrintSetup().getLandscape());
                assertFalse(sheetPM.getPrintSetup().getLandscape());
@@ -318,7 +294,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(1, sheetLS.getPrintSetup().getCopies());
        }
 
-       public void testGroupRows() throws Exception {
+       public void testGroupRows() {
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet s = workbook.createSheet();
                HSSFRow r1 = s.createRow(0);
@@ -342,11 +318,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(0, r5.getOutlineLevel());
 
                // Save and re-open
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               workbook.write(baos);
-               workbook = new HSSFWorkbook(
-                               new ByteArrayInputStream(baos.toByteArray())
-               );
+               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
 
                s = workbook.getSheetAt(0);
                r1 = s.getRow(0);
@@ -362,7 +334,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(0, r5.getOutlineLevel());
        }
 
-       public void testGroupRowsExisting() throws Exception {
+       public void testGroupRowsExisting() {
                HSSFWorkbook workbook = openSample("NoGutsRecords.xls");
 
                HSSFSheet s = workbook.getSheetAt(0);
@@ -391,11 +363,11 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(0, r6.getOutlineLevel());
 
                // Save and re-open
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               workbook.write(baos);
-               workbook = new HSSFWorkbook(
-                               new ByteArrayInputStream(baos.toByteArray())
-               );
+               try {
+                       workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+               } catch (OutOfMemoryError e) {
+                       throw new AssertionFailedError("Identified bug 39903");
+               }
 
                s = workbook.getSheetAt(0);
                r1 = s.getRow(0);
@@ -413,7 +385,7 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals(0, r6.getOutlineLevel());
        }
 
-       public void testGetDrawings() throws Exception {
+       public void testGetDrawings() {
                HSSFWorkbook wb1c = openSample("WithChart.xls");
                HSSFWorkbook wb2c = openSample("WithTwoCharts.xls");
 
@@ -440,7 +412,7 @@ public final class TestHSSFSheet extends TestCase {
                HSSFSheet hssfSheet = workbook.createSheet();
                Sheet sheet = hssfSheet.getSheet();
                ProtectRecord protect = sheet.getProtect();
-   
+
                assertFalse(protect.getProtect());
 
                // This will tell us that cloneSheet, and by extension,
@@ -454,12 +426,12 @@ public final class TestHSSFSheet extends TestCase {
 
        public void testProtectSheet() {
                short expected = (short)0xfef1;
-       HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet s = wb.createSheet();
                s.protectSheet("abcdefghij");
                Sheet sheet = s.getSheet();
-       ProtectRecord protect = sheet.getProtect();
-       PasswordRecord pass = sheet.getPassword();
+               ProtectRecord protect = sheet.getProtect();
+               PasswordRecord pass = sheet.getPassword();
                assertTrue("protection should be on",protect.getProtect());
                assertTrue("object protection should be on",sheet.isProtected()[1]);
                assertTrue("scenario protection should be on",sheet.isProtected()[2]);
@@ -467,9 +439,7 @@ public final class TestHSSFSheet extends TestCase {
        }
 
 
-       public void testZoom()
-                       throws Exception
-       {
+       public void testZoom() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet();
                assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid));
@@ -482,7 +452,6 @@ public final class TestHSSFSheet extends TestCase {
                int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid);
                int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid);
                assertTrue(sclLoc == window2Loc + 1);
-
        }
 
 
@@ -493,7 +462,7 @@ public final class TestHSSFSheet extends TestCase {
        public void testRemoveMerged() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet();
-               Region region = new Region(0, (short)0, 1, (short)1);   
+               Region region = new Region(0, (short)0, 1, (short)1);
                sheet.addMergedRegion(region);
                region = new Region(1, (short)0, 2, (short)1);
                sheet.addMergedRegion(region);
@@ -522,7 +491,6 @@ public final class TestHSSFSheet extends TestCase {
                assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
                region = sheet.getMergedRegionAt(0);
                assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo());
-
        }
 
        public void testShiftMerged() {
@@ -536,33 +504,25 @@ public final class TestHSSFSheet extends TestCase {
                cell = row.createCell((short)1);
                cell.setCellValue(new HSSFRichTextString("second row, second cell"));
 
-               Region region = new Region(1, (short)0, 1, (short)1);   
+               Region region = new Region(1, (short)0, 1, (short)1);
                sheet.addMergedRegion(region);
 
                sheet.shiftRows(1, 1, 1);
 
                region = sheet.getMergedRegionAt(0);
                assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom());
-
        }
 
        /**
         * Tests the display of gridlines, formulas, and rowcolheadings.
         * @author Shawn Laubach (slaubach at apache dot org)
         */
-       public void testDisplayOptions() throws Exception {
+       public void testDisplayOptions() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet();
 
-               File tempFile = TempFile.createTempFile("display", "test.xls");
-               FileOutputStream stream = new FileOutputStream(tempFile);
-               wb.write(stream);
-               stream.close();
-
-               FileInputStream readStream = new FileInputStream(tempFile);
-               wb = new HSSFWorkbook(readStream);
+               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
                sheet = wb.getSheetAt(0);
-               readStream.close();
 
                assertEquals(sheet.isDisplayGridlines(), true);
                assertEquals(sheet.isDisplayRowColHeadings(), true);
@@ -572,16 +532,8 @@ public final class TestHSSFSheet extends TestCase {
                sheet.setDisplayRowColHeadings(false);
                sheet.setDisplayFormulas(true);
 
-               tempFile = TempFile.createTempFile("display", "test.xls");
-               stream = new FileOutputStream(tempFile);
-               wb.write(stream);
-               stream.close();
-
-               readStream = new FileInputStream(tempFile);
-               wb = new HSSFWorkbook(readStream);
+               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
                sheet = wb.getSheetAt(0);
-               readStream.close();
-
 
                assertEquals(sheet.isDisplayGridlines(), false);
                assertEquals(sheet.isDisplayRowColHeadings(), false);
@@ -593,7 +545,7 @@ public final class TestHSSFSheet extends TestCase {
         * Make sure the excel file loads work
         *
         */
-       public void testPageBreakFiles() throws Exception{
+       public void testPageBreakFiles() {
                HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
 
                HSSFSheet sheet = wb.getSheetAt(0);
@@ -611,27 +563,19 @@ public final class TestHSSFSheet extends TestCase {
                assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
                assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
 
-               File tempFile = TempFile.createTempFile("display", "testPagebreaks.xls");
-               FileOutputStream stream = new FileOutputStream(tempFile);
-               wb.write(stream);
-               stream.close();
-
-               wb = new HSSFWorkbook(new FileInputStream(tempFile));
+               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
                sheet = wb.getSheetAt(0);
 
                assertTrue("No row page break", sheet.isRowBroken(22));
                assertTrue("No column page break", sheet.isColumnBroken((short)4));
 
-
                assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
                assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
-
-
        }
 
-       public void testDBCSName () throws Exception {
+       public void testDBCSName () {
                HSSFWorkbook wb = openSample("DBCSSheetName.xls");
-               HSSFSheet s= wb.getSheetAt(1);
+               wb.getSheetAt(1);
                assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" );
                assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e");
        }
@@ -684,17 +628,15 @@ public final class TestHSSFSheet extends TestCase {
        /**
         *
         */
-       public void testAddEmptyRow() throws Exception {
+       public void testAddEmptyRow() {
                //try to add 5 empty rows to a new sheet
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet sheet = workbook.createSheet();
-               for (int i = 0; i < 5; i++) sheet.createRow(i);
-
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               workbook.write(out);
-               out.close();
+               for (int i = 0; i < 5; i++) {
+                       sheet.createRow(i);
+               }
 
-               workbook = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
 
                //try adding empty rows in an existing worksheet
                workbook = openSample("Simple.xls");
@@ -702,14 +644,10 @@ public final class TestHSSFSheet extends TestCase {
                sheet = workbook.getSheetAt(0);
                for (int i = 3; i < 10; i++) sheet.createRow(i);
 
-               out = new ByteArrayOutputStream();
-               workbook.write(out);
-               out.close();
-
-               workbook = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
        }
 
-       public void testAutoSizeColumn() throws Exception {
+       public void testAutoSizeColumn() {
                HSSFWorkbook wb = openSample("43902.xls");
                String sheetName = "my sheet";
                HSSFSheet sheet = wb.getSheet(sheetName);
@@ -719,7 +657,7 @@ public final class TestHSSFSheet extends TestCase {
                //  machines based on the fonts available.
                // So, we use ranges, which are pretty large, but
                //  thankfully don't overlap!
-               int minWithRow1And2 = 6400; 
+               int minWithRow1And2 = 6400;
                int maxWithRow1And2 = 7800;
                int minWithRow1Only = 2750;
                int maxWithRow1Only = 3300;
@@ -733,13 +671,10 @@ public final class TestHSSFSheet extends TestCase {
                //create a region over the 2nd row and auto size the first column
                sheet.addMergedRegion(new Region(1,(short)0,1,(short)1));
                sheet.autoSizeColumn((short)0);
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               wb.write(out);
-               out.close();
+               HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
 
-               // check that the autoSized column width has ignored the 2nd row 
+               // check that the autoSized column width has ignored the 2nd row
                // because it is included in a merged region (Excel like behavior)
-               HSSFWorkbook wb2 = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
                HSSFSheet sheet2 = wb2.getSheet(sheetName);
                assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only);
                assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only);
@@ -747,10 +682,7 @@ public final class TestHSSFSheet extends TestCase {
                // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width
                sheet2.removeMergedRegion(1);
                sheet2.autoSizeColumn((short)0);
-               out = new ByteArrayOutputStream();
-               wb2.write(out);
-               out.close();
-               HSSFWorkbook wb3 = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+               HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2);
                HSSFSheet sheet3 = wb3.getSheet(sheetName);
                assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2);
                assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2);
@@ -827,7 +759,7 @@ public final class TestHSSFSheet extends TestCase {
                assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation());
        }
 
-       public void testColumnWidth() throws Exception {
+       public void testColumnWidth() {
                //check we can correctly read column widths from a reference workbook
                HSSFWorkbook wb = openSample("colwidth.xls");
 
@@ -867,11 +799,8 @@ public final class TestHSSFSheet extends TestCase {
                }
 
                //serialize and read again
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               wb.write(out);
-               out.close();
+               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
 
-               wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
                sh = wb.getSheetAt(0);
                assertEquals(10, sh.getDefaultColumnWidth());
                //columns A-C have default width
index cb5b3d3554d5af813f4b73aed75c09fdf84cba3d..b9873fa4b634edcc8d328f7753438373ddd17b45 100644 (file)
@@ -20,12 +20,16 @@ package org.apache.poi.hssf.usermodel;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.List;
 
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.record.NameRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordInputStream;
 import org.apache.poi.util.TempFile;
 /**
  *
@@ -376,4 +380,49 @@ public final class TestHSSFWorkbook extends TestCase {
         assertEquals("active", expectedActive, sheet.isActive());
         assertEquals("selected", expectedSelected, sheet.isSelected());
     }
-}
+    
+    /**
+     * If Sheet.getSize() returns a different result to Sheet.serialize(), this will cause the BOF
+     * records to be written with invalid offset indexes.  Excel does not like this, and such 
+     * errors are particularly hard to track down.  This test ensures that HSSFWorkbook throws
+     * a specific exception as soon as the situation is detected. See bugzilla 45066
+     */
+    public void testSheetSerializeSizeMismatch_bug45066() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        Sheet sheet = wb.createSheet("Sheet1").getSheet();
+        List sheetRecords = sheet.getRecords();
+        // one way (of many) to cause the discrepancy is with a badly behaved record:
+        sheetRecords.add(new BadlyBehavedRecord());
+        // There is also much logic inside Sheet that (if buggy) might also cause the discrepancy
+        try {
+            wb.getBytes();
+            throw new AssertionFailedError("Identified bug 45066 a");
+        } catch (IllegalStateException e) {
+            // Expected badly behaved sheet record to cause exception
+            assertTrue(e.getMessage().startsWith("Actual serialized sheet size"));
+        }
+    }
+    /**
+     * result returned by getRecordSize() differs from result returned by serialize()
+     */
+    private static final class BadlyBehavedRecord extends Record {
+        public BadlyBehavedRecord() {
+            // 
+        }
+        protected void fillFields(RecordInputStream in) {
+            throw new RuntimeException("Should not be called");
+        }
+        public short getSid() {
+            return 0x777;
+        }
+        public int serialize(int offset, byte[] data) {
+            return 4;
+        }
+        protected void validateSid(short id) {
+            throw new RuntimeException("Should not be called");
+        }
+        public int getRecordSize() {
+            return 8;
+        }
+    }
+ }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java b/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java
new file mode 100644 (file)
index 0000000..7d1082e
--- /dev/null
@@ -0,0 +1,44 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+/**
+ * Tests for LinkTable
+ * 
+ * @author Josh Micich
+ */
+public final class TestLinkTable extends TestCase {
+
+       /**
+        * The example file attached to bugzilla 45046 is a clear example of Name records being present
+        * without an External Book (SupBook) record.  Excel has no trouble reading this file.<br/>
+        * TODO get OOO documentation updated to reflect this (that EXTERNALBOOK is optional). 
+        * 
+        * It's not clear what exact steps need to be taken in Excel to create such a workbook 
+        */
+       public void testLinkTableWithoutExternalBookRecord_bug45046() {
+               HSSFWorkbook wb;
+
+               try {
+                       wb = HSSFTestDataSamples.openSampleWorkbook("ex45046-21984.xls");
+               } catch (RuntimeException e) {
+                       if ("DEFINEDNAME is part of LinkTable".equals(e.getMessage())) {
+                               throw new AssertionFailedError("Identified bug 45046 b");
+                       }
+                       throw e;
+               }
+               // some other sanity checks
+               assertEquals(3, wb.getNumberOfSheets());
+               String formula = wb.getSheetAt(0).getRow(4).getCell(13).getCellFormula();
+               
+               if ("ipcSummenproduktIntern($P5,N$6,$A$9,N$5)".equals(formula)) {
+                       // The reported symptom of this bugzilla is an earlier bug (already fixed) 
+                       throw new AssertionFailedError("Identified bug 41726"); 
+                       // This is observable in version 3.0
+               }
+               
+               assertEquals("ipcSummenproduktIntern($C5,N$2,$A$9,N$1)", formula);
+       }
+}
index fc2a24b7826431ad0741eea1d77142708b8e1ee7..62a26e90b5293728aba73bd37ca2de43c7f0d33c 100644 (file)
@@ -45,8 +45,8 @@ public final class TestSheetHiding extends TestCase {
         */
        public void testTextSheets() throws Exception {
                // Both should have two sheets
-               assertEquals(2, wbH.sheets.size());
-               assertEquals(2, wbU.sheets.size());
+               assertEquals(2, wbH.getNumberOfSheets());
+               assertEquals(2, wbU.getNumberOfSheets());
 
                // All sheets should have one row
                assertEquals(0, wbH.getSheetAt(0).getLastRowNum());