]> 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, 29 Aug 2008 15:27:07 +0000 (15:27 +0000)
committerNick Burch <nick@apache.org>
Fri, 29 Aug 2008 15:27:07 +0000 (15:27 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r689973 | josh | 2008-08-28 21:39:41 +0100 (Thu, 28 Aug 2008) | 1 line

  Consolidated TableRecord inside FormulaRecordAggregate.  Simplifications to FormulaRecord
........
  r690091 | josh | 2008-08-29 04:25:23 +0100 (Fri, 29 Aug 2008) | 1 line

  Changed FormulaRecord.getParsedExpression to return Ptg array
........
  r690094 | josh | 2008-08-29 04:52:51 +0100 (Fri, 29 Aug 2008) | 1 line

  Removing calls to AreaEval.getValues()
........
  r690112 | josh | 2008-08-29 06:29:56 +0100 (Fri, 29 Aug 2008) | 1 line

  Removing calls to AreaEval.getValues() from count and lookup functions
........
  r690259 | yegor | 2008-08-29 14:58:56 +0100 (Fri, 29 Aug 2008) | 1 line

  utility to dump POIFS into filesystem
........
  r690262 | yegor | 2008-08-29 15:01:04 +0100 (Fri, 29 Aug 2008) | 1 line

  initial support for embedded movies and controls.
........

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

80 files changed:
src/java/org/apache/poi/hssf/dev/FormulaViewer.java
src/java/org/apache/poi/hssf/model/RecordStream.java
src/java/org/apache/poi/hssf/record/FormulaRecord.java
src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java
src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java
src/java/org/apache/poi/hssf/record/formula/eval/BlankEval.java
src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java
src/java/org/apache/poi/hssf/record/formula/functions/And.java
src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Count.java
src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Counta.java
src/java/org/apache/poi/hssf/record/formula/functions/Countif.java
src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java
src/java/org/apache/poi/hssf/record/formula/functions/Lookup.java
src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java
src/java/org/apache/poi/hssf/record/formula/functions/Match.java
src/java/org/apache/poi/hssf/record/formula/functions/Not.java
src/java/org/apache/poi/hssf/record/formula/functions/Or.java
src/java/org/apache/poi/hssf/record/formula/functions/Sumproduct.java
src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/poifs/dev/POIFSDump.java [new file with mode: 0755]
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
src/java/org/apache/poi/util/HexDump.java
src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java
src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java
src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/model/Table.java
src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java
src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java
src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java
src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
src/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestRowCol.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java
src/testcases/org/apache/poi/hssf/usermodel/FormulaExtractor.java
src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java
src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java
src/testcases/org/apache/poi/util/TestHexDump.java

index 91d342099bb7bf9817c920483df5ea31286ee5c9..36e527ae5bd4445b12a963c33e53ac58cd68c8fa 100644 (file)
    limitations under the License.
 ==================================================================== */
 
-
-/*
- * FormulaViewer.java - finds formulas in a BIFF8 file and attempts to parse them and
- * display info about them.
- *
- * Created on November 18, 2001, 7:58 AM
- */
 package org.apache.poi.hssf.dev;
 
 import java.io.FileInputStream;
-
-//import java.io.*;
 import java.util.List;
 
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.hssf.record.*;
-import org.apache.poi.hssf.record.formula.*;
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordFactory;
+import org.apache.poi.hssf.record.formula.ExpPtg;
+import org.apache.poi.hssf.record.formula.FuncPtg;
+import org.apache.poi.hssf.record.formula.OperationPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.model.*;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
 /**
  * FormulaViewer - finds formulas in a BIFF8 file and attempts to read them/display
@@ -87,20 +83,21 @@ public class FormulaViewer
     
     private void listFormula(FormulaRecord record) {
         String sep="~";
-        List tokens= record.getParsedExpression();
-        int numptgs = record.getNumberOfExpressionTokens();
-        Ptg token = null;
-        String name,numArg;
-        if (tokens != null) {
-            token = (Ptg) tokens.get(numptgs-1);
+        Ptg[] tokens= record.getParsedExpression();
+        Ptg token;
+        int numptgs = tokens.length;
+        String numArg;
+            token = tokens[numptgs-1];
             if (token instanceof FuncPtg) {
                 numArg = String.valueOf(numptgs-1);
-            } else { numArg = String.valueOf(-1);}
+            } else { 
+               numArg = String.valueOf(-1);
+            }
             
             StringBuffer buf = new StringBuffer();
             
             if (token instanceof ExpPtg) return;
-            buf.append(name=((OperationPtg) token).toFormulaString((HSSFWorkbook)null));
+            buf.append(((OperationPtg) token).toFormulaString((HSSFWorkbook)null));
             buf.append(sep);
             switch (token.getPtgClass()) {
                 case Ptg.CLASS_REF :
@@ -116,7 +113,7 @@ public class FormulaViewer
             
             buf.append(sep);
             if (numptgs>1) {
-                token = (Ptg) tokens.get(numptgs-2);
+                token = tokens[numptgs-2];
                 switch (token.getPtgClass()) {
                     case Ptg.CLASS_REF :
                         buf.append("REF");
@@ -134,9 +131,6 @@ public class FormulaViewer
             buf.append(sep);
             buf.append(numArg);
             System.out.println(buf.toString());
-        } else  {
-            System.out.println("#NAME");
-        }
     }
 
     /**
@@ -155,20 +149,18 @@ public class FormulaViewer
         System.out.println("value = " + record.getValue());
         System.out.print("xf = " + record.getXFIndex());
         System.out.print(", number of ptgs = "
-                           + record.getNumberOfExpressionTokens());
+                           + record.getParsedExpression().length);
         System.out.println(", options = " + record.getOptions());
         System.out.println("RPN List = "+formulaString(record));
         System.out.println("Formula text = "+ composeFormula(record));
     }
 
     private String formulaString(FormulaRecord record) {
-        StringBuffer formula = new StringBuffer("=");
-        int          numptgs = record.getNumberOfExpressionTokens();
-        List         tokens    = record.getParsedExpression();
-        Ptg token;
+
         StringBuffer buf = new StringBuffer();
-           for (int i=0;i<numptgs;i++) {
-           token = (Ptg) tokens.get(i);
+               Ptg[] tokens = record.getParsedExpression();
+               for (int i = 0; i < tokens.length; i++) {
+                       Ptg token = tokens[i];
             buf.append( token.toFormulaString((HSSFWorkbook)null));
             switch (token.getPtgClass()) {
                 case Ptg.CLASS_REF :
@@ -187,9 +179,9 @@ public class FormulaViewer
     }
     
     
-    private String composeFormula(FormulaRecord record)
+    private static String composeFormula(FormulaRecord record)
     {
-       return  org.apache.poi.hssf.model.FormulaParser.toFormulaString((HSSFWorkbook)null,record.getParsedExpression());
+       return  FormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
     }
 
     /**
index 1a0687395432c56432ec997e3303e679a86eef92..8869a9cf03cbe38469263a65373b5cf10e946e42 100755 (executable)
@@ -30,19 +30,28 @@ public final class RecordStream {
        private final List _list;
        private int _nextIndex;
        private int _countRead;
+       private final int _endIx;
 
-       public RecordStream(List inputList, int startIndex) {
+       /**
+        * Creates a RecordStream bounded by startIndex and endIndex
+        */
+       public RecordStream(List inputList, int startIndex, int endIx) {
                _list = inputList;
                _nextIndex = startIndex;
+               _endIx = endIx;
                _countRead = 0;
        }
 
+       public RecordStream(List records, int startIx) {
+               this(records, startIx, records.size());
+       }
+
        public boolean hasNext() {
-               return _nextIndex < _list.size();
+               return _nextIndex < _endIx;
        }
 
        public Record getNext() {
-               if(_nextIndex >= _list.size()) {
+               if(!hasNext()) {
                        throw new RuntimeException("Attempt to read past end of record stream");
                }
                _countRead ++;
@@ -53,14 +62,17 @@ public final class RecordStream {
         * @return the {@link Class} of the next Record. <code>null</code> if this stream is exhausted.
         */
        public Class peekNextClass() {
-               if(_nextIndex >= _list.size()) {
+               if(!hasNext()) {
                        return null;
                }
                return _list.get(_nextIndex).getClass();
        }
 
+       /**
+        * @return -1 if at end of records
+        */
        public int peekNextSid() {
-               if(_nextIndex >= _list.size()) {
+               if(!hasNext()) {
                        return -1;
                }
                return ((Record)_list.get(_nextIndex)).getSid();
index 9f6bb45866e3e53e6198bc1f961e81944f26642c..4cf7d95a3e97534c176c19443617900ebd2da069 100644 (file)
 
 package org.apache.poi.hssf.record;
 
+import java.util.Arrays;
 import java.util.List;
-import java.util.Stack;
 
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -33,69 +34,60 @@ import org.apache.poi.util.LittleEndian;
  * @version 2.0-pre
  */
 public final class FormulaRecord extends Record implements CellValueRecordInterface {
-    
+
     public static final short sid = 0x0006;   // docs say 406...because of a bug Microsoft support site article #Q184647)
+    private static int FIXED_SIZE = 22;
 
     private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
     private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
-    private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);    
+    private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
 
-    private int             field_1_row;
+    private int               field_1_row;
     private short             field_2_column;
     private short             field_3_xf;
     private double            field_4_value;
     private short             field_5_options;
     private int               field_6_zero;
-    private short             field_7_expression_len;
-    private Stack             field_8_parsed_expr;
-    
+    private Ptg[]             field_8_parsed_expr;
+
     /**
      * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
      */
-    private byte[]                     value_data;
-    private byte[]            all_data; //if formula support is not enabled then
-                                        //we'll just store/reserialize
+    private byte[]            value_data;
 
     /** Creates new FormulaRecord */
 
-    public FormulaRecord()
-    {
-        field_8_parsed_expr = new Stack();
+    public FormulaRecord() {
+        field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
     }
 
     /**
      * Constructs a Formula record and sets its fields appropriately.
-     * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an 
+     * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an
      * "explanation of this bug in the documentation) or an exception
      *  will be throw upon validation
      *
      * @param in the RecordInputstream to read the record from
      */
 
-    public FormulaRecord(RecordInputStream in)
-    {
+    public FormulaRecord(RecordInputStream in) {
         super(in);
     }
 
-    protected void fillFields(RecordInputStream in)
-    {
-        try {
+    protected void fillFields(RecordInputStream in) {
           field_1_row            = in.readUShort();
           field_2_column         = in.readShort();
           field_3_xf             = in.readShort();
           field_4_value          = in.readDouble();
           field_5_options        = in.readShort();
-                       
+
         if (Double.isNaN(field_4_value)) {
             value_data = in.getNANData();
         }
-        
+
           field_6_zero           = in.readInt();
-          field_7_expression_len = in.readShort();
-          field_8_parsed_expr    = Ptg.createParsedExpressionTokens(field_7_expression_len, in);
-        } catch (java.lang.UnsupportedOperationException uoe)  {
-          throw new RecordFormatException(uoe);
-        }
+          int field_7_expression_len = in.readShort(); // this length does not include any extra array data
+          field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in);
         if (in.remaining() == 10) {
             // TODO - this seems to occur when IntersectionPtg is present
             // 10 extra bytes are just 0x01 and 0x00
@@ -103,19 +95,15 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
         }
     }
 
-    //public void setRow(short row)
-    public void setRow(int row)
-    {
+    public void setRow(int row) {
         field_1_row = row;
     }
 
-    public void setColumn(short column)
-    {
+    public void setColumn(short column) {
         field_2_column = column;
     }
 
-    public void setXFIndex(short xf)
-    {
+    public void setXFIndex(short xf) {
         field_3_xf = xf;
     }
 
@@ -124,9 +112,7 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
      *
      * @param value  calculated value
      */
-
-    public void setValue(double value)
-    {
+    public void setValue(double value) {
         field_4_value = value;
     }
 
@@ -135,35 +121,19 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
      *
      * @param options  bitmask
      */
-
-    public void setOptions(short options)
-    {
+    public void setOptions(short options) {
         field_5_options = options;
     }
 
-    /**
-     * set the length (in number of tokens) of the expression
-     * @param len  length
-     */
-
-    public void setExpressionLength(short len)
-    {
-        field_7_expression_len = len;
-    }
-
-    //public short getRow()
-    public int getRow()
-    {
+    public int getRow() {
         return field_1_row;
     }
 
-    public short getColumn()
-    {
+    public short getColumn() {
         return field_2_column;
     }
 
-    public short getXFIndex()
-    {
+    public short getXFIndex() {
         return field_3_xf;
     }
 
@@ -172,8 +142,7 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
      *
      * @return calculated value
      */
-    public double getValue()
-    {
+    public double getValue() {
         return field_4_value;
     }
 
@@ -182,108 +151,43 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
      *
      * @return bitmask
      */
-    public short getOptions()
-    {
+    public short getOptions() {
         return field_5_options;
-    }    
-    
+    }
+
     public boolean isSharedFormula() {
         return sharedFormula.isSet(field_5_options);
     }
     public void setSharedFormula(boolean flag) {
-       field_5_options =
-               sharedFormula.setShortBoolean(field_5_options, flag);
+        field_5_options =
+            sharedFormula.setShortBoolean(field_5_options, flag);
     }
-    
+
     public boolean isAlwaysCalc() {
-       return alwaysCalc.isSet(field_5_options);
+        return alwaysCalc.isSet(field_5_options);
     }
     public void setAlwaysCalc(boolean flag) {
-       field_5_options =
-               alwaysCalc.setShortBoolean(field_5_options, flag);
+        field_5_options =
+            alwaysCalc.setShortBoolean(field_5_options, flag);
     }
-    
+
     public boolean isCalcOnLoad() {
-       return calcOnLoad.isSet(field_5_options);
+        return calcOnLoad.isSet(field_5_options);
     }
     public void setCalcOnLoad(boolean flag) {
-       field_5_options =
-               calcOnLoad.setShortBoolean(field_5_options, flag);
-    }
-    
-    /**
-     * get the length (in number of tokens) of the expression
-     * @return  expression length
-     */
-
-    public short getExpressionLength()
-    {
-        return field_7_expression_len;
+        field_5_options =
+            calcOnLoad.setShortBoolean(field_5_options, flag);
     }
 
     /**
-     * push a token onto the stack
-     *
-     * @param ptg  the token
-     */
-
-    public void pushExpressionToken(Ptg ptg)
-    {
-        field_8_parsed_expr.push(ptg);
-    }
-
-    /**
-     * pop a token off of the stack
-     *
-     * @return Ptg - the token
+     * @return the formula tokens. never <code>null</code>
      */
-
-    public Ptg popExpressionToken()
-    {
-        return ( Ptg ) field_8_parsed_expr.pop();
+    public Ptg[] getParsedExpression() {
+        return (Ptg[]) field_8_parsed_expr.clone();
     }
 
-    /**
-     * peek at the token on the top of stack
-     *
-     * @return Ptg - the token
-     */
-
-    public Ptg peekExpressionToken()
-    {
-        return ( Ptg ) field_8_parsed_expr.peek();
-    }
-
-    /**
-     * get the size of the stack
-     * @return size of the stack
-     */
-
-    public int getNumberOfExpressionTokens()
-    {
-        if (this.field_8_parsed_expr == null) {
-            return 0;
-        } else {
-            return field_8_parsed_expr.size();
-        }
-    }
-
-    /**
-     * get the stack as a list
-     *
-     * @return list of tokens (casts stack to a list and returns it!)
-     * this method can return null is we are unable to create Ptgs from 
-     *     existing excel file
-     * callers should check for null!
-     */
-
-    public List getParsedExpression()
-    {
-        return field_8_parsed_expr;
-    }
-    
-    public void setParsedExpression(Stack ptgs) {
-      field_8_parsed_expr = ptgs;
+    public void setParsedExpression(Ptg[] ptgs) {
+        field_8_parsed_expr = ptgs;
     }
 
     /**
@@ -292,156 +196,86 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
      *
      * @param id alleged id for this record
      */
-
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
+    protected void validateSid(short id) {
+        if (id != sid) {
             throw new RecordFormatException("NOT A FORMULA RECORD");
         }
     }
 
-    public short getSid()
-    {
+    public short getSid() {
         return sid;
     }
 
-    /**
-     * called by the class that is responsible for writing this sucker.
-     * Subclasses should implement this so that their data is passed back in a
-     * byte array.
-     *
-     * @return byte array containing instance data
-     */
+    private int getDataSize() {
+        return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr);
+    }
+    public int serialize(int offset, byte [] data) {
 
-    public int serialize(int offset, byte [] data)
-    {
-        if (this.field_8_parsed_expr != null) {
-        int ptgSize = getTotalPtgSize();
+        int dataSize = getDataSize();
 
         LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, ( short ) (22 + ptgSize));
-        //LittleEndian.putShort(data, 4 + offset, getRow());
-        LittleEndian.putShort(data, 4 + offset, ( short ) getRow());
+        LittleEndian.putUShort(data, 2 + offset, dataSize);
+        LittleEndian.putUShort(data, 4 + offset, getRow());
         LittleEndian.putShort(data, 6 + offset, getColumn());
         LittleEndian.putShort(data, 8 + offset, getXFIndex());
-        
+
         //only reserialize if the value is still NaN and we have old nan data
-        if (Double.isNaN(this.getValue()) && value_data != null) {             
-                       System.arraycopy(value_data,0,data,10 + offset,value_data.length);
+        if (Double.isNaN(getValue()) && value_data != null) {
+            System.arraycopy(value_data,0,data,10 + offset,value_data.length);
         } else {
-                       LittleEndian.putDouble(data, 10 + offset, field_4_value);
+            LittleEndian.putDouble(data, 10 + offset, field_4_value);
         }
-               
+
         LittleEndian.putShort(data, 18 + offset, getOptions());
-        
+
         //when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
         //Microsoft Excel Developer's Kit Page 318
         LittleEndian.putInt(data, 20 + offset, 0);
-        LittleEndian.putShort(data, 24 + offset, getExpressionLength());
-        Ptg.serializePtgStack(field_8_parsed_expr, data, 26+offset);
-        } else {
-            System.arraycopy(all_data,0,data,offset,all_data.length);
-        }
-        return getRecordSize();
+        int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr);
+        LittleEndian.putUShort(data, 24 + offset, formulaTokensSize);
+        Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset);
+        return 4 + dataSize;
     }
-    
-    
-    
-
-    public int getRecordSize()
-    {
-        int retval =0;
-        
-        if (this.field_8_parsed_expr != null) {
-            retval = getTotalPtgSize() + 26;
-        } else {
-            retval =all_data.length;
-        }
-        return retval;
 
-        // return getTotalPtgSize() + 28;
-    }
-
-    private int getTotalPtgSize()
-    {
-        List list   = getParsedExpression();
-        int  retval = 0;
-
-        for (int k = 0; k < list.size(); k++)
-        {
-            Ptg ptg = ( Ptg ) list.get(k);
-
-            retval += ptg.getSize();
-        }
-        return retval;
+    public int getRecordSize() {
+        return 4 + getDataSize();
     }
 
-    public boolean isInValueSection()
-    {
+    public boolean isInValueSection() {
         return true;
     }
 
-    public boolean isValue()
-    {
+    public boolean isValue() {
         return true;
     }
-    
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer();
-            buffer.append("[FORMULA]\n");
-            buffer.append("    .row       = ")
-                .append(Integer.toHexString(getRow())).append("\n");
-            buffer.append("    .column    = ")
-                .append(Integer.toHexString(getColumn()))
-                .append("\n");
-            buffer.append("    .xf              = ")
-                .append(Integer.toHexString(getXFIndex())).append("\n");
-            if (Double.isNaN(this.getValue()) && value_data != null)
-              buffer.append("    .value (NaN)     = ")
-                  .append(org.apache.poi.util.HexDump.dump(value_data,0,0))
-                  .append("\n");
-            else
-              buffer.append("    .value           = ").append(getValue())
-                  .append("\n");
-            buffer.append("    .options         = ").append(getOptions())
-                .append("\n");
-            buffer.append("      .alwaysCalc         = ").append(alwaysCalc.isSet(getOptions()))
-                .append("\n");
-            buffer.append("      .calcOnLoad         = ").append(calcOnLoad.isSet(getOptions()))
-                .append("\n");
-            buffer.append("      .sharedFormula         = ").append(sharedFormula.isSet(getOptions()))
-                .append("\n");
-            buffer.append("    .zero            = ").append(field_6_zero)
-                .append("\n");
-            buffer.append("    .expressionlength= ").append(getExpressionLength())
-                .append("\n");
-
-            if (field_8_parsed_expr != null) {
-                buffer.append("    .numptgsinarray  = ").append(field_8_parsed_expr.size())
-                    .append("\n");
-            
-
-                for (int k = 0; k < field_8_parsed_expr.size(); k++ ) {
-                   buffer.append("     Ptg(")
-                        .append(k)
-                        .append(")=")
-                        .append(field_8_parsed_expr.get(k).toString())
-                        .append("\n")
-                        .append(((Ptg)field_8_parsed_expr.get(k)).toDebugString())
-                        .append("\n");
-                }
-            }else {
-                buffer.append("Formula full data \n")
-                    .append(org.apache.poi.util.HexDump.dump(this.all_data,0,0));
-            }
-            
-            
-            buffer.append("[/FORMULA]\n");
-        return buffer.toString();
+
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer();
+        sb.append("[FORMULA]\n");
+        sb.append("    .row       = ").append(HexDump.shortToHex(getRow())).append("\n");
+        sb.append("    .column    = ").append(HexDump.shortToHex(getColumn())).append("\n");
+        sb.append("    .xf        = ").append(HexDump.shortToHex(getXFIndex())).append("\n");
+        sb.append("    .value     = ");
+        if (Double.isNaN(this.getValue()) && value_data != null) {
+            sb.append("(NaN)").append(HexDump.dump(value_data,0,0)).append("\n");
+        } else {
+            sb.append(getValue()).append("\n");
+        }
+        sb.append("    .options   = ").append(HexDump.shortToHex(getOptions())).append("\n");
+        sb.append("    .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n");
+        sb.append("    .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n");
+        sb.append("    .shared    = ").append(sharedFormula.isSet(getOptions())).append("\n");
+        sb.append("    .zero      = ").append(HexDump.intToHex(field_6_zero)).append("\n");
+
+        for (int k = 0; k < field_8_parsed_expr.length; k++ ) {
+            sb.append("     Ptg[").append(k).append("]=");
+            sb.append(field_8_parsed_expr[k].toString()).append("\n");
+        }
+        sb.append("[/FORMULA]\n");
+        return sb.toString();
     }
-    
+
     public Object clone() {
       FormulaRecord rec = new FormulaRecord();
       rec.field_1_row = field_1_row;
@@ -450,18 +284,14 @@ public final class FormulaRecord extends Record implements CellValueRecordInterf
       rec.field_4_value = field_4_value;
       rec.field_5_options = field_5_options;
       rec.field_6_zero = field_6_zero;
-      rec.field_7_expression_len = field_7_expression_len;
-      rec.field_8_parsed_expr = new Stack();
-      int size = 0;
-      if (field_8_parsed_expr != null)
-        size = field_8_parsed_expr.size();
-      for (int i=0; i< size; i++) {
-        Ptg ptg = ((Ptg)field_8_parsed_expr.get(i)).copy();        
-        rec.field_8_parsed_expr.add(i, ptg);
+      int nTokens = field_8_parsed_expr.length;
+      Ptg[] ptgs = new Ptg[nTokens];
+      for (int i=0; i< nTokens; i++) {
+        ptgs[i] = field_8_parsed_expr[i].copy();
       }
+      rec.field_8_parsed_expr = ptgs;
       rec.value_data = value_data;
-      rec.all_data = all_data;
       return rec;
     }
-
 }
+
index a8aeed0dae8d5f802e6bf0e95ab2d586e4c82901..3f3a047e647da81a25ffa3ef6d8db7bbb1df37cf 100755 (executable)
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
+
 package org.apache.poi.hssf.record;
 
+import java.util.List;
 import java.util.Stack;
 
-import org.apache.poi.hssf.record.formula.*;
+import org.apache.poi.hssf.record.formula.AreaNPtg;
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RefNPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
 
 /**
  * Title:        SharedFormulaRecord
  * Description:  Primarily used as an excel optimization so that multiple similar formulas
- *                               are not written out too many times.  We should recognize this record and
+ *               are not written out too many times.  We should recognize this record and
  *               serialize as is since this is used when reading templates.
  * <p>
  * Note: the documentation says that the SID is BC where biffviewer reports 4BC.  The hex dump shows
@@ -33,15 +38,15 @@ import org.apache.poi.hssf.record.formula.*;
  * @author Danny Mui at apache dot org
  */
 public final class SharedFormulaRecord extends Record {
-    public final static short   sid = 0x4BC;
-    
+    public final static short   sid = 0x04BC;
+
     private int               field_1_first_row;
     private int               field_2_last_row;
     private short             field_3_first_column;
     private short             field_4_last_column;
     private int               field_5_reserved;
     private short             field_6_expression_len;
-    private Stack             field_7_parsed_expr;    
+    private Stack             field_7_parsed_expr;
 
     public SharedFormulaRecord()
     {
@@ -55,15 +60,15 @@ public final class SharedFormulaRecord extends Record {
     {
           super(in);
     }
-    
+
     protected void validateSid(short id)
     {
         if (id != this.sid)
         {
             throw new RecordFormatException("Not a valid SharedFormula");
-        }        
-    }    
-    
+        }
+    }
+
     public int getFirstRow() {
       return field_1_first_row;
     }
@@ -139,7 +144,7 @@ public final class SharedFormulaRecord extends Record {
                 .append(field_7_parsed_expr.get(k).toString())
                 .append("\n");
         }
-        
+
         buffer.append("[/SHARED FORMULA RECORD]\n");
         return buffer.toString();
     }
@@ -163,7 +168,7 @@ public final class SharedFormulaRecord extends Record {
     private Stack getParsedExpressionTokens(RecordInputStream in)
     {
         Stack stack = new Stack();
-        
+
         while (in.remaining() != 0) {
             Ptg ptg = Ptg.createPtg(in);
             stack.push(ptg);
@@ -180,15 +185,15 @@ public final class SharedFormulaRecord extends Record {
       return ((getFirstRow() <= formulaRow) && (getLastRow() >= formulaRow) &&
           (getFirstColumn() <= formulaColumn) && (getLastColumn() >= formulaColumn));
     }
-    
+
     /**
-     * Creates a non shared formula from the shared formula 
+     * Creates a non shared formula from the shared formula
      * counter part
      */
     protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
         if(false) {
             /*
-             * TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records. 
+             * TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
              * If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records.
              * Disabling this code breaks one existing junit.
              * Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly.
@@ -225,31 +230,30 @@ public final class SharedFormulaRecord extends Record {
             if (!ptg.isBaseToken()) {
                 ptg.setClass(originalOperandClass);
             }
-            
+
             newPtgStack.add(ptg);
         }
         return newPtgStack;
     }
 
-    /** 
-     * Creates a non shared formula from the shared formula 
+    /**
+     * Creates a non shared formula from the shared formula
      * counter part
      */
     public void convertSharedFormulaRecord(FormulaRecord formula) {
       //Sanity checks
-      final int formulaRow = formula.getRow();
-      final int formulaColumn = formula.getColumn();
-      if (isFormulaInShared(formula)) {
-        formula.setExpressionLength(getExpressionLength());
-        
-        Stack newPtgStack = 
-               convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
-        formula.setParsedExpression(newPtgStack);
+        if (!isFormulaInShared(formula)) {
+            throw new RuntimeException("Shared Formula Conversion: Coding Error");
+        }
+        final int formulaRow = formula.getRow();
+        final int formulaColumn = formula.getColumn();
+
+        List ptgList =  convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
+        Ptg[] ptgs = new Ptg[ptgList.size()];
+        ptgList.toArray(ptgs);
+        formula.setParsedExpression(ptgs);
         //Now its not shared!
         formula.setSharedFormula(false);
-      } else {
-        throw new RuntimeException("Shared Formula Conversion: Coding Error");
-      }
     }
 
     private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
index 3359ca55a41c68917cd0864a03ef8efcc186ecab..393f1c0c053009961891e0019a12660e706be451 100644 (file)
 
 package org.apache.poi.hssf.record.aggregates;
 
+import org.apache.poi.hssf.model.RecordStream;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.SharedFormulaRecord;
 import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.TableRecord;
 
 /**
  * The formula record aggregate is used to join together the formula record and it's
@@ -29,61 +32,67 @@ import org.apache.poi.hssf.record.StringRecord;
  */
 public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
 
-    private FormulaRecord _formulaRecord;
+    private final FormulaRecord _formulaRecord;
+    /** caches the calculated result of the formula */
     private StringRecord _stringRecord;
+    private TableRecord _tableRecord;
     
-    public FormulaRecordAggregate( FormulaRecord formulaRecord, StringRecord stringRecord )
-    {
+    public FormulaRecordAggregate(FormulaRecord formulaRecord) {
         _formulaRecord = formulaRecord;
-        _stringRecord = stringRecord;
+        _stringRecord = null;
     }
-
-    public void setStringRecord( StringRecord stringRecord ) {
-        _stringRecord = stringRecord;
+    public FormulaRecordAggregate(FormulaRecord formulaRecord, RecordStream rs) {
+        _formulaRecord = formulaRecord;
+        Class nextClass = rs.peekNextClass();
+        if (nextClass == SharedFormulaRecord.class) {
+            // For (text) shared formulas, the SharedFormulaRecord comes before the StringRecord.
+            // In any case it is OK to skip SharedFormulaRecords because they were collected 
+            // before constructing the ValueRecordsAggregate.
+            rs.getNext(); // skip the shared formula record
+            nextClass = rs.peekNextClass();
+        }
+        if (nextClass == StringRecord.class) {
+            _stringRecord = (StringRecord) rs.getNext();
+        } else if (nextClass == TableRecord.class) {
+            _tableRecord = (TableRecord) rs.getNext();
+        }
     }
 
-    public void setFormulaRecord( FormulaRecord formulaRecord )
-    {
-        _formulaRecord = formulaRecord;
+    public void setStringRecord(StringRecord stringRecord) {
+        _stringRecord = stringRecord;
+        _tableRecord = null; // probably can't have both present at the same time
+        // TODO - establish rules governing when each of these sub records may exist
     }
     
-    public FormulaRecord getFormulaRecord()
-    {
+    public FormulaRecord getFormulaRecord() {
         return _formulaRecord;
     }
 
-    public StringRecord getStringRecord()
-    {
+    public StringRecord getStringRecord() {
         return _stringRecord;
     }
     
-    public short getXFIndex()
-    {
+    public short getXFIndex() {
         return _formulaRecord.getXFIndex();
     }
 
-    public void setXFIndex(short xf)
-    {
-        _formulaRecord.setXFIndex( xf );
+    public void setXFIndex(short xf) {
+        _formulaRecord.setXFIndex(xf);
     }
 
-    public void setColumn(short col)
-    {
-        _formulaRecord.setColumn( col );
+    public void setColumn(short col) {
+        _formulaRecord.setColumn(col);
     }
 
-    public void setRow(int row)
-    {
-        _formulaRecord.setRow( row );
+    public void setRow(int row) {
+        _formulaRecord.setRow(row);
     }
 
-    public short getColumn()
-    {
+    public short getColumn() {
         return _formulaRecord.getColumn();
     }
 
-    public int getRow()
-    {
+    public int getRow() {
         return _formulaRecord.getRow();
     }
 
@@ -94,8 +103,11 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
     public void visitContainedRecords(RecordVisitor rv) {
          rv.visitRecord(_formulaRecord);
          if (_stringRecord != null) {
-              rv.visitRecord(_stringRecord);
+             rv.visitRecord(_stringRecord);
          }
+         if (_tableRecord != null) {
+             rv.visitRecord(_tableRecord);
+        }
     }
    
     public String getStringValue() {
index d839ecfab623ea5f1d42fa40eade43c483213733..fcbc89f63b7558d3cf3261ed635daee60740f0c5 100644 (file)
@@ -82,7 +82,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
             if (!rec.isValue()) {
                 throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")");
             }
-            i += _valuesAgg.construct(recs, i, endIx, sfh);
+            i += _valuesAgg.construct(recs, i, endIx, sfh)-1;
         }
         "".length();
     }
index 0db1201432e647e3485845c84da08d4b0626db5c..886bb617d574a0a941895ec57fa49d955d547934 100644 (file)
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.poi.hssf.model.RecordStream;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.DBCellRecord;
 import org.apache.poi.hssf.record.FormulaRecord;
@@ -111,12 +112,12 @@ public final class ValueRecordsAggregate {
 
     public void removeAllCellsValuesForRow(int rowIndex) {
         if (rowIndex >= records.length) {
-            throw new IllegalArgumentException("Specified rowIndex " + rowIndex 
+            throw new IllegalArgumentException("Specified rowIndex " + rowIndex
                     + " is outside the allowable range (0.." +records.length + ")");
         }
         records[rowIndex] = null;
     }
-    
+
 
     public int getPhysicalNumberOfCells()
     {
@@ -142,62 +143,48 @@ public final class ValueRecordsAggregate {
     }
 
     /**
-     * Processes a sequential group of cell value records.  Stops at endIx or the first 
+     * Processes a sequential group of cell value records.  Stops at endIx or the first
      * non-value record encountered.
      * @param sfh used to resolve any shared formulas for the current sheet
      * @return the number of records consumed
      */
     public int construct(List records, int offset, int endIx, SharedFormulaHolder sfh) {
-        int k = 0;
+        RecordStream rs = new RecordStream(records, offset, endIx);
 
-        FormulaRecordAggregate lastFormulaAggregate = null;
-        
         // Now do the main processing sweep
-        for (k = offset; k < endIx; k++) {
-            Record rec = ( Record ) records.get(k);
-
-            if (rec instanceof StringRecord) {
-                if (lastFormulaAggregate == null) {
-                    throw new RuntimeException("StringRecord found without preceding FormulaRecord");
-                }
-                if (lastFormulaAggregate.getStringRecord() != null) {
-                    throw new RuntimeException("Multiple StringRecords found after FormulaRecord");
-                }
-                lastFormulaAggregate.setStringRecord((StringRecord)rec);
-                lastFormulaAggregate = null;
-                continue;
-            }
-            
-            if (rec instanceof TableRecord) {
-                // TODO - don't loose this record
-                // DATATABLE probably belongs in formula record aggregate
-                if (lastFormulaAggregate == null) {
-                    throw new RuntimeException("No preceding formula record found");
-                }
-                lastFormulaAggregate = null;
-                continue; 
+        while (rs.hasNext()) {
+            Class recClass = rs.peekNextClass();
+            if (recClass == StringRecord.class) {
+                throw new RuntimeException("Loose StringRecord found without preceding FormulaRecord");
             }
-            
-            if (rec instanceof SharedFormulaRecord) {
-                // Already handled, not to worry
-                continue;
+
+            if (recClass == TableRecord.class) {
+                throw new RuntimeException("Loose TableRecord found without preceding FormulaRecord");
             }
 
-            if (rec instanceof UnknownRecord) {
+            if (recClass == UnknownRecord.class) {
                 break;
             }
-            if (rec instanceof RowRecord) {
-                break; 
+            if (recClass == RowRecord.class) {
+                break;
             }
-            if (rec instanceof DBCellRecord) {
+            if (recClass == DBCellRecord.class) {
                 // end of 'Row Block'.  This record is ignored by POI
                 break;
             }
-            if (rec instanceof MergeCellsRecord) {
+
+            Record rec = rs.getNext();
+
+            if (recClass == SharedFormulaRecord.class) {
+                // Already handled, not to worry
+                continue;
+            }
+            if (recClass == MergeCellsRecord.class) {
                 // doesn't really belong here
                 // can safely be ignored, because it has been processed in a higher method
                 continue;
             }
+
             if (!rec.isValue()) {
                 throw new RuntimeException("bad record type");
             }
@@ -206,14 +193,13 @@ public final class ValueRecordsAggregate {
                 if (formula.isSharedFormula()) {
                     sfh.convertSharedFormulaRecord(formula);
                 }
-                
-                lastFormulaAggregate = new FormulaRecordAggregate((FormulaRecord)rec, null);
-                insertCell( lastFormulaAggregate );
+
+                insertCell(new FormulaRecordAggregate((FormulaRecord)rec, rs));
                 continue;
             }
             insertCell(( CellValueRecordInterface ) rec);
         }
-        return k - offset - 1;
+        return rs.getCountRead();
     }
 
     /** Tallies a count of the size of the cell records
@@ -235,7 +221,7 @@ public final class ValueRecordsAggregate {
 
     /** Returns true if the row has cells attached to it */
     public boolean rowHasCells(int row) {
-        if (row > records.length-1) //previously this said row > records.length which means if 
+        if (row > records.length-1) //previously this said row > records.length which means if
             return false;  // if records.length == 60 and I pass "60" here I get array out of bounds
       CellValueRecordInterface[] rowCells=records[row]; //because a 60 length array has the last index = 59
       if(rowCells==null) return false;
@@ -260,7 +246,7 @@ public final class ValueRecordsAggregate {
         }
         return pos - offset;
     }
-    
+
     public int visitCellsForRow(int rowIndex, RecordVisitor rv) {
         int result = 0;
         CellValueRecordInterface[] cellRecs = records[rowIndex];
@@ -292,7 +278,7 @@ public final class ValueRecordsAggregate {
 
     public CellValueRecordInterface[] getValueRecords() {
         List temp = new ArrayList();
-        
+
         for (int i = 0; i < records.length; i++) {
             CellValueRecordInterface[] rowCells = records[i];
             if (rowCells == null) {
@@ -305,7 +291,7 @@ public final class ValueRecordsAggregate {
                 }
             }
         }
-        
+
         CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()];
         temp.toArray(result);
         return result;
@@ -314,7 +300,7 @@ public final class ValueRecordsAggregate {
     {
     return new MyIterator();
     }
-  
+
   private final class MyIterator implements Iterator {
     short nextColumn=-1;
     int nextRow,lastRow;
@@ -325,7 +311,7 @@ public final class ValueRecordsAggregate {
       this.lastRow=records.length-1;
       findNext();
     }
-    
+
     public MyIterator(int firstRow,int lastRow)
     {
       this.nextRow=firstRow;
index b38bcd27de628058a0924d3a2437706f057e9bf1..5ea1061ba7745f0b858f16994133a4d326bde1f2 100644 (file)
@@ -39,6 +39,11 @@ public final class ArrayPtg extends Ptg {
        public static final byte sid  = 0x20;
 
        private static final int RESERVED_FIELD_LEN = 7;
+       /** 
+        * The size of the plain tArray token written within the standard formula tokens
+        * (not including the data which comes after all formula tokens)
+        */
+       public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN;
        // TODO - fix up field visibility and subclasses
        private byte[] field_1_reserved;
        
@@ -123,7 +128,7 @@ public final class ArrayPtg extends Ptg {
        public int writeTokenValueBytes(byte[] data, int offset) {
 
                LittleEndian.putByte(data,  offset + 0, token_1_columns-1);
-               LittleEndian.putShort(data, offset + 1, (short)(token_2_rows-1));
+               LittleEndian.putUShort(data, offset + 1, token_2_rows-1);
                ConstantValueParser.encode(data, offset + 3, token_3_arrayValues);
                return 3 + ConstantValueParser.getEncodedSize(token_3_arrayValues);
        }
@@ -137,11 +142,11 @@ public final class ArrayPtg extends Ptg {
        }
 
        /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
-       public int getSize()
-       {
-               int size = 1+7+1+2;
-               size += ConstantValueParser.getEncodedSize(token_3_arrayValues);
-               return size;
+       public int getSize() {
+               return PLAIN_TOKEN_SIZE 
+                       // data written after the all tokens:
+                       + 1 + 2 // column, row
+                       + ConstantValueParser.getEncodedSize(token_3_arrayValues);
        }
 
        public String toFormulaString(Workbook book)
index 50f3450d281647f9b0ea3475fc4d11ab63d66320..43c5e86ebe290b4ac730710ecec5b3ac160b16a6 100644 (file)
@@ -221,13 +221,6 @@ public abstract class Ptg implements Cloneable {
                }
                throw new RuntimeException("Unexpected base token id (" + id + ")");
        }
-       /**
-        * 
-        * 
-        */
-       public static int getEncodedSize(Stack ptgs) {
-               return getEncodedSize(toPtgArray(ptgs));
-       }
        /**
         * @return a distinct copy of this <tt>Ptg</tt> if the class is mutable, or the same instance
         * if the class is immutable.
@@ -265,6 +258,11 @@ public abstract class Ptg implements Cloneable {
                } 
                return result;
        }
+       /**
+        * This method will return the same result as {@link #getEncodedSizeWithoutArrayData(Ptg[])} 
+        * if there are no array tokens present.
+        * @return the full size taken to encode the specified <tt>Ptg</tt>s 
+        */
        // TODO - several duplicates of this code should be refactored here
        public static int getEncodedSize(Ptg[] ptgs) {
                int result = 0;
@@ -273,6 +271,22 @@ public abstract class Ptg implements Cloneable {
                }
                return result;
        }
+       /**
+        * Used to calculate value that should be encoded at the start of the encoded Ptg token array;
+        * @return the size of the encoded Ptg tokens not including any trailing array data.
+        */
+       public static int getEncodedSizeWithoutArrayData(Ptg[] ptgs) {
+               int result = 0;
+               for (int i = 0; i < ptgs.length; i++) {
+                       Ptg ptg = ptgs[i];
+                       if (ptg instanceof ArrayPtg) {
+                               result += ArrayPtg.PLAIN_TOKEN_SIZE;
+                       } else {
+                               result += ptg.getSize();
+                       }
+               }
+               return result;
+       }
        /**
         * Writes the ptgs to the data buffer, starting at the specified offset.  
         *
index 82cc8a9b40d8957de83346b27e5d4bf57fcef317..182b9b618b458587d6adc666930f02476a9a4e4b 100644 (file)
@@ -1,28 +1,25 @@
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
+ *
  */
 public interface AreaEval extends ValueEval {
 
@@ -30,76 +27,81 @@ public interface AreaEval extends ValueEval {
      * returns the 0-based index of the first row in
      * this area.
      */
-    public int getFirstRow();
+    int getFirstRow();
 
     /**
      * returns the 0-based index of the last row in
      * this area.
      */
-    public int getLastRow();
+    int getLastRow();
 
     /**
      * returns the 0-based index of the first col in
      * this area.
      */
-    public int getFirstColumn();
+    int getFirstColumn();
 
     /**
      * returns the 0-based index of the last col in
      * this area.
      */
-    public int getLastColumn();
-    
+    int getLastColumn();
+
     /**
      * returns true if the Area's start and end row indexes
      * are same. This result of this method should agree
      * with getFirstRow() == getLastRow().
      */
-    public boolean isRow();
-    
+    boolean isRow();
+
     /**
      * returns true if the Area's start and end col indexes
      * are same. This result of this method should agree
      * with getFirstColumn() == getLastColumn().
      */
-    public boolean isColumn();
+    boolean isColumn();
 
     /**
      * The array of values  in this area. Although the area
      * maybe 1D (ie. isRow() or isColumn() returns true) or 2D
      * the returned array is 1D.
      */
-    public ValueEval[] getValues();
+    ValueEval[] getValues();
 
     /**
-     * returns the ValueEval from the values array at the specified 
-     * row and col index. The specified indexes should be absolute indexes
-     * in the sheet and not relative indexes within the area. Also,
-     * if contains(row, col) evaluates to true, a null value will
-     * bre returned.
-     * @param row
-     * @param col
+     * @return the ValueEval from within this area at the specified row and col index. Never 
+     * <code>null</code> (possibly {@link BlankEval}).  The specified indexes should be absolute 
+     * indexes in the sheet and not relative indexes within the area.  
      */
-    public ValueEval getValueAt(int row, int col);
-    
+    ValueEval getValueAt(int row, int col);
+
     /**
-     * returns true if the cell at row and col specified 
-     * as absolute indexes in the sheet is contained in 
+     * returns true if the cell at row and col specified
+     * as absolute indexes in the sheet is contained in
      * this area.
      * @param row
      * @param col
      */
-    public boolean contains(int row, int col);
-    
+    boolean contains(int row, int col);
+
     /**
      * returns true if the specified col is in range
      * @param col
      */
-    public boolean containsColumn(short col);
-    
+    boolean containsColumn(short col);
+
     /**
      * returns true if the specified row is in range
      * @param row
      */
-    public boolean containsRow(int row);
+    boolean containsRow(int row);
+
+    int getWidth();
+    int getHeight();
+    /**
+     * @return the ValueEval from within this area at the specified relativeRowIndex and 
+     * relativeColumnIndex. Never <code>null</code> (possibly {@link BlankEval}). The
+     * specified indexes should relative to the top left corner of this area.  
+     */
+    ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
 }
index 9436ae0aa9d5e447384da90e7206367bcd9de208..1686e75f33a4a3f27d501162cd5726a892f66de3 100644 (file)
@@ -1,19 +1,19 @@
-/*
* 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.
- */
+/* ====================================================================
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at
+
      http://www.apache.org/licenses/LICENSE-2.0
+
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.eval;
 
@@ -94,9 +94,7 @@ abstract class AreaEvalBase implements AreaEval {
                        throw new IllegalArgumentException("Specified column index (" + col 
                                        + ") is outside the allowed range (" + _firstColumn + ".." + col + ")");
                }
-
-               int index = rowOffsetIx * _nColumns + colOffsetIx;
-               return _values[index];
+               return getRelativeValue(rowOffsetIx, colOffsetIx);
        }
 
        public final boolean contains(int row, int col) {
@@ -119,4 +117,20 @@ abstract class AreaEvalBase implements AreaEval {
        public final boolean isRow() {
                return _firstRow == _lastRow;
        }
+       public int getHeight() {
+               return _lastRow-_firstRow+1;
+       }
+
+       public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+               int index = relativeRowIndex * _nColumns + relativeColumnIndex;
+               ValueEval result = _values[index];
+               if (result == null) {
+                       return BlankEval.INSTANCE;
+               }
+               return result;
+       }
+
+       public int getWidth() {
+               return _lastColumn-_firstColumn+1;
+       }
 }
index df671821fecb6242dcc01ffff6b0e5d9fb75022f..d1f28df008e54a7d237fc4109e61992af6b52629 100644 (file)
@@ -1,30 +1,27 @@
-/*
-* 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.
-*/
-/*
- * Created on May 9, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; This class is a
  *         marker class. It is a special value for empty cells.
  */
-public class BlankEval implements ValueEval {
+public final class BlankEval implements ValueEval {
 
     public static BlankEval INSTANCE = new BlankEval();
 
index be1cda5f8e3ab07d9f5e9d94b71a2447f5eb3c0f..627b26998950850e3fd38eeb3e3b460a8300a793 100755 (executable)
@@ -27,7 +27,7 @@ public final class OperandResolver {
        private OperandResolver() {
                // no instances of this class
        }
-       
+
        /**
         * Retrieves a single value from a variety of different argument types according to standard
         * Excel rules.  Does not perform any type conversion.
@@ -113,17 +113,17 @@ public final class OperandResolver {
                }
                if (result instanceof ErrorEval) {
                        throw new EvaluationException((ErrorEval) result);
-                       
+
                }
                return result;
        }
-       
+
        /**
         * @return possibly  <tt>ErrorEval</tt>, and <code>null</code> 
         */
        private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae, 
                        int srcCellRow, short srcCellCol) throws EvaluationException {
-       
+
                if(false) {
                        // this is too simplistic
                        if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
@@ -131,25 +131,25 @@ public final class OperandResolver {
                        }
                /*
                Circular references are not dealt with directly here, but it is worth noting some issues.
-               
+
                ANY one of the return statements in this method could return a cell that is identical
                to the one immediately being evaluated.  The evaluating cell is identified by srcCellRow,
                srcCellRow AND sheet.  The sheet is not available in any nearby calling method, so that's
                one reason why circular references are not easy to detect here. (The sheet of the returned
                cell can be obtained from ae if it is an Area3DEval.)
-               
+
                Another reason there's little value in attempting to detect circular references here is
                that only direct circular references could be detected.  If the cycle involved two or more
                cells this method could not detect it.  
-               
+
                Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector
                (and HSSFFormulaEvaluator). 
                 */
                }
-               
+
                if (ae.isColumn()) {
                        if(ae.isRow()) {
-                               return ae.getValues()[0];
+                               return ae.getRelativeValue(0, 0);
                        }
                        if(!ae.containsRow(srcCellRow)) {
                                throw EvaluationException.invalidValue();
@@ -199,20 +199,20 @@ public final class OperandResolver {
         */
        public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
 
-           if (ev instanceof NumericValueEval) {
-               // this also handles booleans
-               return ((NumericValueEval)ev).getNumberValue();
-           }
-           if (ev instanceof StringEval) {
-               Double dd = parseDouble(((StringEval) ev).getStringValue());
-               if (dd == null) {
-                       throw EvaluationException.invalidValue();
-               }
-               return dd.doubleValue();
+               if (ev instanceof NumericValueEval) {
+                       // this also handles booleans
+                       return ((NumericValueEval)ev).getNumberValue();
+               }
+               if (ev instanceof StringEval) {
+                       Double dd = parseDouble(((StringEval) ev).getStringValue());
+                       if (dd == null) {
+                               throw EvaluationException.invalidValue();
+                       }
+                       return dd.doubleValue();
                }
                throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")");
        }
-       
+
        /**
         * Converts a string to a double using standard rules that Excel would use.<br/>
         * Tolerates currency prefixes, commas, leading and trailing spaces.<p/>
@@ -245,7 +245,7 @@ public final class OperandResolver {
                        return null;
                }
                // TODO - support notation like '1E3' (==1000)
-               
+
                double val;
                try {
                        val = Double.parseDouble(text);
@@ -254,7 +254,7 @@ public final class OperandResolver {
                }
                return new Double(isPositive ? +val : -val);
        }
-       
+
        /**
         * @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
         * @return the converted string value. never <code>null</code>
@@ -274,4 +274,51 @@ public final class OperandResolver {
                }
                throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
        }
+
+       /**
+        * @return <code>null</code> to represent blank values
+        * @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted
+        */
+       public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException {
+
+               if (ve == null || ve instanceof BlankEval) {
+                       // TODO - remove 've == null' condition once AreaEval is fixed
+                       return null;
+               }
+               if (ve instanceof BoolEval) {
+                       return Boolean.valueOf(((BoolEval) ve).getBooleanValue());
+               }
+
+               if (ve instanceof BlankEval) {
+                       return null;
+               }
+
+               if (ve instanceof StringEval) {
+                       if (stringsAreBlanks) {
+                               return null;
+                       }
+                       String str = ((StringEval) ve).getStringValue();
+                       if (str.equalsIgnoreCase("true")) {
+                               return Boolean.TRUE;
+                       }
+                       if (str.equalsIgnoreCase("false")) {
+                               return Boolean.FALSE;
+                       }
+                       // else - string cannot be converted to boolean
+                       throw new EvaluationException(ErrorEval.VALUE_INVALID);
+               }
+
+               if (ve instanceof NumericValueEval) {
+                       NumericValueEval ne = (NumericValueEval) ve;
+                       double d = ne.getNumberValue();
+                       if (Double.isNaN(d)) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+                       return Boolean.valueOf(d != 0);
+               }
+               if (ve instanceof ErrorEval) {
+                       throw new EvaluationException((ErrorEval) ve);
+               }
+               throw new RuntimeException("Unexpected eval (" + ve.getClass().getName() + ")");
+       }
 }
index 455772f73707ba1d70565a1c565fcc232d89d09b..8ef99f890407a756a36bac1d2f928c1c06696101 100644 (file)
@@ -1,85 +1,32 @@
-/*
-* 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.
-*/
-/*
- * Created on May 9, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
+/* ====================================================================
+   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
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BoolEval;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
+   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.
+==================================================================== */
 
-public class And extends BooleanFunction {
+package org.apache.poi.hssf.record.formula.functions;
+
+/**
+ * 
+ */
+public final class And extends BooleanFunction {
 
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        boolean b = true;
-        boolean atleastOneNonBlank = false;
-        
-        /*
-         * Note: do not abort the loop if b is false, since we could be
-         * dealing with errorevals later. 
-         */
-        outer:
-        for (int i=0, iSize=operands.length; i<iSize; i++) {
-            if (operands[i] instanceof AreaEval) {
-                AreaEval ae = (AreaEval) operands[i];
-                ValueEval[] values = ae.getValues();
-                for (int j=0, jSize=values.length; j<jSize; j++) {
-                    ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
-                    if (tempVe instanceof BoolEval) {
-                        b = b && ((BoolEval) tempVe).getBooleanValue();
-                        atleastOneNonBlank = true;
-                    }
-                    else if (tempVe instanceof ErrorEval) {
-                        retval = tempVe;
-                        break outer;
-                    }
-                }
-            }
-            else {
-                ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
-                if (tempVe instanceof BoolEval) {
-                    b = b && ((BoolEval) tempVe).getBooleanValue();
-                    atleastOneNonBlank = true;
-                }
-                else if (tempVe instanceof StringEval) {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-                else if (tempVe instanceof ErrorEval) {
-                    retval = tempVe;
-                    break outer;
-                }
-            }
-        }
-        
-        if (!atleastOneNonBlank) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        
-        if (retval == null) { // if no error
-            retval = b ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-        
-        return retval;
-    }
+       protected boolean getInitialResultValue() {
+               return true;
+       }
 
+       protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
+               return cumulativeResult && currentValue;
+       }
 }
index b3e56cfdc668a7a411173cb8274bb5ff4be82ab1..9ec924e27a2b7f81843ccb2b7f231d2c5f252be6 100644 (file)
-/*
-* 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.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
 import org.apache.poi.hssf.record.formula.eval.BoolEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.record.formula.eval.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
-
 /**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  * Here are the general rules concerning Boolean functions:
  * <ol>
- * <li> Blanks are not either true or false
- * <li> Strings are not either true or false (even strings "true" 
- * or "TRUE" or "0" etc.)
- * <li> Numbers: 0 is false. Any other number is TRUE.
- * <li> References are evaluated and above rules apply.
- * <li> Areas: Individual cells in area are evaluated and checked to 
- * see if they are blanks, strings etc.
+ * <li> Blanks are ignored (not either true or false) </li>
+ * <li> Strings are ignored if part of an area ref or cell ref, otherwise they must be 'true' or 'false'</li> 
+ * <li> Numbers: 0 is false. Any other number is TRUE </li>
+ * <li> Areas: *all* cells in area are evaluated according to the above rules</li>
  * </ol>
+ * 
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  */
 public abstract class BooleanFunction implements Function {
 
-    protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol, boolean stringsAreBlanks) {
-        ValueEval retval;
-        
-        if (eval instanceof RefEval) {
-            RefEval re = (RefEval) eval;
-            ValueEval ve = re.getInnerValueEval();
-            retval = internalResolve(ve, true);
-        }
-        else {
-            retval = internalResolve(eval, stringsAreBlanks);
-        }
-        
-        return retval;
-    }
-    
-    private ValueEval internalResolve(Eval ve, boolean stringsAreBlanks) {
-        ValueEval retval = null;
-        
-        // blankeval is returned as is
-        if (ve instanceof BlankEval) {
-            retval = BlankEval.INSTANCE;
-        }
-        
-        // stringeval
-        else if (ve instanceof StringEval) {
-            retval = stringsAreBlanks ? (ValueEval) BlankEval.INSTANCE : (StringEval) ve;
-        }
-        
-        // bools are bools :)
-        else if (ve instanceof BoolEval) {
-            retval = (BoolEval) ve;
-        }
-        
-        // convert numbers to bool
-        else if (ve instanceof NumericValueEval) {
-            NumericValueEval ne = (NumericValueEval) ve;
-            double d = ne.getNumberValue();
-            retval = Double.isNaN(d) 
-                    ? (ValueEval) ErrorEval.VALUE_INVALID
-                    : (d != 0) 
-                        ? BoolEval.TRUE
-                        : BoolEval.FALSE;
-        }
-        
-        // since refevals
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        
-        return retval;
-        
-    }
+       public final Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+               if (args.length < 1) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+               boolean boolResult;
+               try {
+                       boolResult = calculate(args);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               return BoolEval.valueOf(boolResult);
+       }
+
+       private boolean calculate(Eval[] args) throws EvaluationException {
+
+               boolean result = getInitialResultValue();
+               boolean atleastOneNonBlank = false;
+               
+               /*
+                * Note: no short-circuit boolean loop exit because any ErrorEvals will override the result
+                */
+               for (int i=0, iSize=args.length; i<iSize; i++) {
+                       Eval arg = args[i];
+                       if (arg instanceof AreaEval) {
+                               AreaEval ae = (AreaEval) arg;
+                               int height = ae.getHeight();
+                               int width = ae.getWidth();
+                               for (int rrIx=0; rrIx<height; rrIx++) {
+                                       for (int rcIx=0; rcIx<width; rcIx++) {
+                                               ValueEval ve = ae.getRelativeValue(rrIx, rcIx);
+                                               Boolean tempVe = OperandResolver.coerceValueToBoolean(ve, true);
+                                               if (tempVe != null) {
+                                                       result = partialEvaluate(result, tempVe.booleanValue());
+                                                       atleastOneNonBlank = true;
+                                               }
+                                       }
+                               }
+                               continue;
+                       }
+                       Boolean tempVe;
+                       if (arg instanceof RefEval) {
+                               ValueEval ve = ((RefEval) arg).getInnerValueEval();
+                               tempVe = OperandResolver.coerceValueToBoolean(ve, true);
+                       } else if (arg instanceof ValueEval) {
+                               ValueEval ve = (ValueEval) arg;
+                               tempVe = OperandResolver.coerceValueToBoolean(ve, false);
+                       } else {
+                               throw new RuntimeException("Unexpected eval (" + arg.getClass().getName() + ")");
+                       }
+                       
+                       
+                       if (tempVe != null) {
+                               result = partialEvaluate(result, tempVe.booleanValue());
+                               atleastOneNonBlank = true;
+                       }
+               }
+               
+               if (!atleastOneNonBlank) {
+                       throw new EvaluationException(ErrorEval.VALUE_INVALID);
+               }
+               return result;
+       }
+       
+       
+       protected abstract boolean getInitialResultValue();
+       protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue);
 }
index eb55fc4a421b4cd8c2773b7202d6475ce0f3297c..fd5944e8584ad81e7abdc804427ec03b9b4b1582 100644 (file)
@@ -1,32 +1,26 @@
-/*
-* 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.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
 
 /**
  * Counts the number of cells that contain numeric data within
@@ -39,7 +33,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  * TODO: Check this properly matches excel on edge cases
  *  like formula cells, error cells etc
  */
-public class Count implements Function {
+public final class Count implements Function {
 
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
                int nArgs = args.length;
@@ -56,63 +50,23 @@ public class Count implements Function {
                int temp = 0;
                
                for(int i=0; i<nArgs; i++) {
-                       temp += countArg(args[i]);
+                       temp += CountUtils.countArg(args[i], predicate);
                        
                }
                return new NumberEval(temp);
        }
 
-       private static int countArg(Eval eval) {
-        if (eval instanceof AreaEval) {
-            AreaEval ae = (AreaEval) eval;
-            return countAreaEval(ae);
-        }
-        if (eval instanceof RefEval) {
-            RefEval refEval = (RefEval)eval;
-                       return countValue(refEval.getInnerValueEval());
-        }
-        if (eval instanceof NumberEval) {
-            return 1;
-        }
-               
-               throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
-       }
+       private static final I_MatchPredicate predicate = new I_MatchPredicate() {
 
-       private static int countAreaEval(AreaEval ae) {
-               
-               int temp = 0;
-               ValueEval[] values = ae.getValues();
-               for (int i = 0; i < values.length; i++) {
-                       ValueEval val = values[i];
-                       if(val == null) {
-                               // seems to occur.  Really we would have expected BlankEval
-                               continue;
-                       }
-                       temp += countValue(val);
-                       
-               }
-               return temp;
-       }
+               public boolean matches(Eval valueEval) {
 
-       private static int countValue(ValueEval valueEval) {
-               
-               if(valueEval == BlankEval.INSTANCE) {
-                       return 0;
-               }
-               
-               if(valueEval instanceof BlankEval) {
-                       // wouldn't need this if BlankEval was final
-                       return 0;
-               }
+                       if(valueEval instanceof NumberEval) {
+                               // only numbers are counted
+                               return true;
+                       }
 
-               if(valueEval instanceof ErrorEval) {
-                       // note - error values not counted
-                       return 0;
+                       // error values and string values not counted
+                       return false;
                }
-               
-               if(valueEval instanceof NumberEval)
-                       return 1;
-
-               return 0;
-       }
+       };
 }
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java
new file mode 100644 (file)
index 0000000..f8d8883
--- /dev/null
@@ -0,0 +1,78 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.RefEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * Common logic for COUNT, COUNTA and COUNTIF
+ *
+ * @author Josh Micich 
+ */
+final class CountUtils {
+
+       private CountUtils() {
+               // no instances of this class
+       }
+       
+       /**
+        * Common interface for the matching criteria.
+        */
+       public interface I_MatchPredicate {
+               boolean matches(Eval x);
+       }
+       /**
+        * @return the number of evaluated cells in the range that match the specified criteria
+        */
+       public static int countMatchingCellsInArea(AreaEval areaEval, I_MatchPredicate criteriaPredicate) {
+               int result = 0;
+
+               int height = areaEval.getHeight();
+               int width = areaEval.getWidth();
+               for (int rrIx=0; rrIx<height; rrIx++) {
+                       for (int rcIx=0; rcIx<width; rcIx++) {
+                               ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
+                       if(criteriaPredicate.matches(ve)) {
+                               result++;
+                       }
+                       }
+               }
+               return result;
+       }
+       /**
+        * @return 1 if the evaluated cell matches the specified criteria
+        */
+       public static int countMatchingCell(RefEval refEval, I_MatchPredicate criteriaPredicate) {
+               if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
+                       return 1;
+               }
+               return 0;
+       }
+       public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) {
+               if (eval instanceof AreaEval) {
+                       return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate);
+               }
+               if (eval instanceof RefEval) {
+                       return CountUtils.countMatchingCell((RefEval) eval, criteriaPredicate);
+               }
+               return criteriaPredicate.matches(eval) ? 1 : 0;
+       }
+}
index 9061e77e5da9cc5b13044bbb3986f666817846dc..01fc0616d36650c0279f8c5c3fa58629dd248915 100644 (file)
@@ -1,31 +1,27 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
 
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.functions;
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
 import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
 
 /**
  * Counts the number of cells that contain data within the list of arguments. 
@@ -51,70 +47,26 @@ public final class Counta implements Function {
                }
                
                int temp = 0;
-               // Note - observed behavior of Excel:
-               // Error values like #VALUE!, #REF!, #DIV/0!, #NAME? etc don't cause this COUNTA to return an error
-               // in fact, they seem to get counted
                
                for(int i=0; i<nArgs; i++) {
-                       temp += countArg(args[i]);
+                       temp += CountUtils.countArg(args[i], predicate);
                        
                }
                return new NumberEval(temp);
        }
 
-       private static int countArg(Eval eval) {
-        if (eval instanceof AreaEval) {
-            AreaEval ae = (AreaEval) eval;
-            return countAreaEval(ae);
-        }
-        if (eval instanceof RefEval) {
-            RefEval refEval = (RefEval)eval;
-                       return countValue(refEval.getInnerValueEval());
-        }
-        if (eval instanceof NumberEval) {
-            return 1;
-        }
-        if (eval instanceof StringEval) {
-            return 1;
-        }
-        
-               
-               throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
-       }
+       private static final I_MatchPredicate predicate = new I_MatchPredicate() {
 
-       private static int countAreaEval(AreaEval ae) {
-               
-               int temp = 0;
-               ValueEval[] values = ae.getValues();
-               for (int i = 0; i < values.length; i++) {
-                       ValueEval val = values[i];
-                       if(val == null) {
-                               // seems to occur.  Really we would have expected BlankEval
-                               continue;
-                       }
-                       temp += countValue(val);
-                       
-               }
-               return temp;
-       }
+               public boolean matches(Eval valueEval) {
+                       // Note - observed behavior of Excel:
+                       // Error values like #VALUE!, #REF!, #DIV/0!, #NAME? etc don't cause this COUNTA to return an error
+                       // in fact, they seem to get counted
 
-       private static int countValue(ValueEval valueEval) {
-               
-               if(valueEval == BlankEval.INSTANCE) {
-                       return 0;
-               }
-               
-               if(valueEval instanceof BlankEval) {
-                       // wouldn't need this if BlankEval was final
-                       return 0;
-               }
-
-               if(valueEval instanceof ErrorEval) {
-                       // note - error values are counted
-                       return 1;
+                       if(valueEval == BlankEval.INSTANCE) {
+                               return false;
+                       }
+                       // Note - everything but BlankEval counts
+                       return true;
                }
-               // also empty strings and zeros are counted too
-
-               return 1;
-       }
+       };
 }
index 902a991b378c47aedf712209e10f21876b5c9084..00eb86e94389ef92cf7e22f2e9d518a7e300116f 100644 (file)
@@ -1,19 +1,19 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.functions;
 
@@ -28,7 +28,7 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
 import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.record.formula.eval.RefEval;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
 
 /**
  * Implementation for the function COUNTIF<p/>
@@ -144,12 +144,6 @@ public final class Countif implements Function {
                }
        }
 
-       /**
-        * Common interface for the matching criteria.
-        */
-       /* package */ interface I_MatchPredicate {
-               boolean matches(Eval x);
-       }
 
        private static final class NumberMatcher implements I_MatchPredicate {
 
@@ -360,21 +354,12 @@ public final class Countif implements Function {
         * @return the number of evaluated cells in the range that match the specified criteria
         */
        private Eval countMatchingCellsInArea(Eval rangeArg, I_MatchPredicate criteriaPredicate) {
-               int result = 0;
+               
+               int result;
                if (rangeArg instanceof RefEval) {
-                       RefEval refEval = (RefEval) rangeArg;
-                       if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
-                               result++;
-                       }
+                       result = CountUtils.countMatchingCell((RefEval) rangeArg, criteriaPredicate);
                } else if (rangeArg instanceof AreaEval) {
-
-                       AreaEval range = (AreaEval) rangeArg;
-                       ValueEval[] values = range.getValues();
-                       for (int i = 0; i < values.length; i++) {
-                               if(criteriaPredicate.matches(values[i])) {
-                                       result++;
-                               }
-                       }
+                       result = CountUtils.countMatchingCellsInArea((AreaEval) rangeArg, criteriaPredicate);
                } else {
                        throw new IllegalArgumentException("Bad range arg type (" + rangeArg.getClass().getName() + ")");
                }
index d493cd53321f43c0da40d5c94fc2492e61a1074c..8604eadc3741a12c20e65023c866ef8f89261500 100644 (file)
@@ -42,40 +42,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
  */
 public final class Hlookup implements Function {
        
-       private static final class RowVector implements ValueVector {
-
-               private final AreaEval _tableArray;
-               private final int _size;
-               private final int _rowAbsoluteIndex;
-               private final int _firstColumnAbsoluteIndex;
-
-               public RowVector(AreaEval tableArray, int rowIndex) {
-                       _rowAbsoluteIndex = tableArray.getFirstRow() + rowIndex;
-                       if(!tableArray.containsRow(_rowAbsoluteIndex)) {
-                               int lastRowIx =  tableArray.getLastRow() -  tableArray.getFirstRow();
-                               throw new IllegalArgumentException("Specified row index (" + rowIndex 
-                                               + ") is outside the allowed range (0.." + lastRowIx + ")");
-                       }
-                       _tableArray = tableArray;
-                       _size = tableArray.getLastColumn() - tableArray.getFirstColumn() + 1;
-                       if(_size < 1) {
-                               throw new RuntimeException("bad table array size zero");
-                       }
-                       _firstColumnAbsoluteIndex = tableArray.getFirstColumn();
-               }
-
-               public ValueEval getItem(int index) {
-                       if(index>_size) {
-                               throw new ArrayIndexOutOfBoundsException("Specified index (" + index 
-                                               + ") is outside the allowed range (0.." + (_size-1) + ")");
-                       }
-                       return _tableArray.getValueAt(_rowAbsoluteIndex, (short) (_firstColumnAbsoluteIndex + index));
-               }
-               public int getSize() {
-                       return _size;
-               }
-       }
-
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
                Eval arg3 = null;
                switch(args.length) {
@@ -93,7 +59,7 @@ public final class Hlookup implements Function {
                        ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
                        AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
                        boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
-                       int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, new RowVector(tableArray, 0), isRangeLookup);
+                       int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
                        ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
                        int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
                        ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
@@ -113,11 +79,9 @@ public final class Hlookup implements Function {
                if(colIndex < 0) {
                        throw EvaluationException.invalidValue();
                }
-               int nCols = tableArray.getLastColumn() - tableArray.getFirstRow() + 1;
-               
-               if(colIndex >= nCols) {
+               if(colIndex >= tableArray.getWidth()) {
                        throw EvaluationException.invalidRef();
                }
-               return new RowVector(tableArray, colIndex);
+               return LookupUtils.createRowVector(tableArray, colIndex);
        }
 }
index be1d0d0f940f47a32d55a69cd43231d0fcb1a297..305d8fb0d7dbb645305c404d1d62732095821add 100644 (file)
@@ -40,19 +40,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
  * @author Josh Micich
  */
 public final class Lookup implements Function {
-       private static final class SimpleValueVector implements ValueVector {
-               private final ValueEval[] _values;
-
-               public SimpleValueVector(ValueEval[] values) {
-                       _values = values;
-               }
-               public ValueEval getItem(int index) {
-                       return _values[index];
-               }
-               public int getSize() {
-                       return _values.length;
-               }
-       }
 
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
                switch(args.length) {
@@ -86,11 +73,11 @@ public final class Lookup implements Function {
        }
 
        private static ValueVector createVector(AreaEval ae) {
-               
-               if(!ae.isRow() && !ae.isColumn()) {
-                       // extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
-                       throw new RuntimeException("non-vector lookup or result areas not supported yet");
+               ValueVector result = LookupUtils.createVector(ae);
+               if (result != null) {
+                       return result;
                }
-               return new SimpleValueVector(ae.getValues());
+               // extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
+               throw new RuntimeException("non-vector lookup or result areas not supported yet");
        }
 }
index d6a8489623ea8b73f11039a92f254ddc7084736c..e8c083dc5a65ece2c1332bd93d50cf37fdc75570 100644 (file)
@@ -34,11 +34,11 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
  * Common functionality used by VLOOKUP, HLOOKUP, LOOKUP and MATCH
- * 
+ *
  * @author Josh Micich
  */
 final class LookupUtils {
-       
+
        /**
         * Represents a single row or column within an <tt>AreaEval</tt>.
         */
@@ -46,14 +46,95 @@ final class LookupUtils {
                ValueEval getItem(int index);
                int getSize();
        }
+
+
+       private static final class RowVector implements ValueVector {
+
+               private final AreaEval _tableArray;
+               private final int _size;
+               private final int _rowIndex;
+
+               public RowVector(AreaEval tableArray, int rowIndex) {
+                       _rowIndex = rowIndex;
+                       int _rowAbsoluteIndex = tableArray.getFirstRow() + rowIndex;
+                       if(!tableArray.containsRow(_rowAbsoluteIndex)) {
+                               int lastRowIx =  tableArray.getLastRow() -  tableArray.getFirstRow();
+                               throw new IllegalArgumentException("Specified row index (" + rowIndex
+                                               + ") is outside the allowed range (0.." + lastRowIx + ")");
+                       }
+                       _tableArray = tableArray;
+                       _size = tableArray.getWidth();
+               }
+
+               public ValueEval getItem(int index) {
+                       if(index > _size) {
+                               throw new ArrayIndexOutOfBoundsException("Specified index (" + index
+                                               + ") is outside the allowed range (0.." + (_size-1) + ")");
+                       }
+                       return _tableArray.getRelativeValue(_rowIndex, index);
+               }
+               public int getSize() {
+                       return _size;
+               }
+       }
+
+       private static final class ColumnVector implements ValueVector {
+
+               private final AreaEval _tableArray;
+               private final int _size;
+               private final int _columnIndex;
+
+               public ColumnVector(AreaEval tableArray, int columnIndex) {
+                       _columnIndex = columnIndex;
+                       int _columnAbsoluteIndex = tableArray.getFirstColumn() + columnIndex;
+                       if(!tableArray.containsColumn((short)_columnAbsoluteIndex)) {
+                               int lastColIx =  tableArray.getLastColumn() -  tableArray.getFirstColumn();
+                               throw new IllegalArgumentException("Specified column index (" + columnIndex
+                                               + ") is outside the allowed range (0.." + lastColIx + ")");
+                       }
+                       _tableArray = tableArray;
+                       _size = _tableArray.getHeight();
+               }
+
+               public ValueEval getItem(int index) {
+                       if(index > _size) {
+                               throw new ArrayIndexOutOfBoundsException("Specified index (" + index
+                                               + ") is outside the allowed range (0.." + (_size-1) + ")");
+                       }
+                       return _tableArray.getRelativeValue(index, _columnIndex);
+               }
+               public int getSize() {
+                       return _size;
+               }
+       }
+
+       public static ValueVector createRowVector(AreaEval tableArray, int relativeRowIndex) {
+               return new RowVector(tableArray, relativeRowIndex);
+       }
+       public static ValueVector createColumnVector(AreaEval tableArray, int relativeColumnIndex) {
+               return new ColumnVector(tableArray, relativeColumnIndex);
+       }
+       /**
+        * @return <code>null</code> if the supplied area is neither a single row nor a single colum
+        */
+       public static ValueVector createVector(AreaEval ae) {
+               if (ae.isColumn()) {
+                       return createColumnVector(ae, 0);
+               }
+               if (ae.isRow()) {
+                       return createRowVector(ae, 0);
+               }
+               return null;
+       }
+
        /**
         * Enumeration to support <b>4</b> valued comparison results.<p/>
-        * Excel lookup functions have complex behaviour in the case where the lookup array has mixed 
+        * Excel lookup functions have complex behaviour in the case where the lookup array has mixed
         * types, and/or is unordered.  Contrary to suggestions in some Excel documentation, there
         * does not appear to be a universal ordering across types.  The binary search algorithm used
         * changes behaviour when the evaluated 'mid' value has a different type to the lookup value.<p/>
-        * 
-        * A simple int might have done the same job, but there is risk in confusion with the well 
+        *
+        * A simple int might have done the same job, but there is risk in confusion with the well
         * known <tt>Comparable.compareTo()</tt> and <tt>Comparator.compare()</tt> which both use
         * a ubiquitous 3 value result encoding.
         */
@@ -80,7 +161,7 @@ final class LookupUtils {
                public static final CompareResult LESS_THAN = new CompareResult(false, -1);
                public static final CompareResult EQUAL = new CompareResult(false, 0);
                public static final CompareResult GREATER_THAN = new CompareResult(false, +1);
-               
+
                public static final CompareResult valueOf(int simpleCompareResult) {
                        if(simpleCompareResult < 0) {
                                return LESS_THAN;
@@ -90,7 +171,7 @@ final class LookupUtils {
                        }
                        return EQUAL;
                }
-               
+
                public boolean isTypeMismatch() {
                        return _isTypeMismatch;
                }
@@ -128,17 +209,17 @@ final class LookupUtils {
                        return "??error??";
                }
        }
-       
+
        public interface LookupValueComparer {
                /**
-                * @return one of 4 instances or <tt>CompareResult</tt>: <tt>LESS_THAN</tt>, <tt>EQUAL</tt>, 
+                * @return one of 4 instances or <tt>CompareResult</tt>: <tt>LESS_THAN</tt>, <tt>EQUAL</tt>,
                 * <tt>GREATER_THAN</tt> or <tt>TYPE_MISMATCH</tt>
                 */
                CompareResult compareTo(ValueEval other);
        }
-       
+
        private static abstract class LookupValueComparerBase implements LookupValueComparer {
-               
+
                private final Class _targetClass;
                protected LookupValueComparerBase(ValueEval targetValue) {
                        if(targetValue == null) {
@@ -154,7 +235,7 @@ final class LookupUtils {
                                return CompareResult.TYPE_MISMATCH;
                        }
                        if (_targetClass == StringEval.class) {
-                               
+
                        }
                        return compareSameType(other);
                }
@@ -169,7 +250,7 @@ final class LookupUtils {
                /** used only for debug purposes */
                protected abstract String getValueAsString();
        }
-       
+
        private static final class StringLookupComparer extends LookupValueComparerBase {
                private String _value;
 
@@ -223,9 +304,9 @@ final class LookupUtils {
                        return String.valueOf(_value);
                }
        }
-       
+
        /**
-        * Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b> 
+        * Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b>
         * or <b>row_index_num</b> respectively).<br>
         * Sample behaviour:
         *    <table border="0" cellpadding="1" cellspacing="2" summary="Sample behaviour">
@@ -242,17 +323,17 @@ final class LookupUtils {
         *      <tr><td>""</td><td>&nbsp;</td><td>#REF!</td></tr>
         *      <tr><td>&lt;blank&gt;</td><td>&nbsp;</td><td>#VALUE!</td></tr>
         *    </table><br/>
-        *    
-        *  * Note - out of range errors (both too high and too low) are handled by the caller. 
+        *
+        *  * Note - out of range errors (both too high and too low) are handled by the caller.
         * @return column or row index as a zero-based value
-        * 
+        *
         */
        public static int resolveRowOrColIndexArg(ValueEval veRowColIndexArg) throws EvaluationException {
                if(veRowColIndexArg == null) {
                        throw new IllegalArgumentException("argument must not be null");
                }
                if(veRowColIndexArg instanceof BlankEval) {
-                       throw EvaluationException.invalidValue(); 
+                       throw EvaluationException.invalidValue();
                }
                if(veRowColIndexArg instanceof StringEval) {
                        StringEval se = (StringEval) veRowColIndexArg;
@@ -260,7 +341,7 @@ final class LookupUtils {
                        Double dVal = OperandResolver.parseDouble(strVal);
                        if(dVal == null) {
                                // String does not resolve to a number. Raise #VALUE! error.
-                               throw EvaluationException.invalidRef(); 
+                               throw EvaluationException.invalidRef();
                                // This includes text booleans "TRUE" and "FALSE".  They are not valid.
                        }
                        // else - numeric value parses OK
@@ -268,9 +349,9 @@ final class LookupUtils {
                // actual BoolEval values get interpreted as FALSE->0 and TRUE->1
                return OperandResolver.coerceValueToInt(veRowColIndexArg) - 1;
        }
-       
-       
-       
+
+
+
        /**
         * The second argument (table_array) should be an area ref, but can actually be a cell ref, in
         * which case it is interpreted as a 1x1 area ref.  Other scalar values cause #VALUE! error.
@@ -279,13 +360,13 @@ final class LookupUtils {
                if (eval instanceof AreaEval) {
                        return (AreaEval) eval;
                }
-               
+
                if(eval instanceof RefEval) {
                        RefEval refEval = (RefEval) eval;
                        // Make this cell ref look like a 1x1 area ref.
-                       
+
                        // It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval.
-                       // This code only requires the value array item. 
+                       // This code only requires the value array item.
                        // anything would be ok for rowIx and colIx, but may as well get it right.
                        int rowIx = refEval.getRow();
                        int colIx = refEval.getColumn();
@@ -295,10 +376,10 @@ final class LookupUtils {
                }
                throw EvaluationException.invalidValue();
        }
-       
+
 
        /**
-        * Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions. 
+        * Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions.
         * @param rangeLookupArg
         * @param srcCellRow
         * @param srcCellCol
@@ -318,7 +399,7 @@ final class LookupUtils {
                        return false;
                }
                if(valEval instanceof BoolEval) {
-                       // Happy day flow 
+                       // Happy day flow
                        BoolEval boolEval = (BoolEval) valEval;
                        return boolEval.getBooleanValue();
                }
@@ -327,7 +408,7 @@ final class LookupUtils {
                        String stringValue = ((StringEval) valEval).getStringValue();
                        if(stringValue.length() < 1) {
                                // More trickiness:
-                               // Empty string is not the same as BlankEval.  It causes #VALUE! error 
+                               // Empty string is not the same as BlankEval.  It causes #VALUE! error
                                throw EvaluationException.invalidValue();
                        }
                        // TODO move parseBoolean to OperandResolver
@@ -337,10 +418,10 @@ final class LookupUtils {
                                return b.booleanValue();
                        }
                        // Even more trickiness:
-                       // Note - even if the StringEval represents a number value (for example "1"), 
-                       // Excel does not resolve it to a boolean.  
+                       // Note - even if the StringEval represents a number value (for example "1"),
+                       // Excel does not resolve it to a boolean.
                        throw EvaluationException.invalidValue();
-                       // This is in contrast to the code below,, where NumberEvals values (for 
+                       // This is in contrast to the code below,, where NumberEvals values (for
                        // example 0.01) *do* resolve to equivalent boolean values.
                }
                if (valEval instanceof NumericValueEval) {
@@ -350,7 +431,7 @@ final class LookupUtils {
                }
                throw new RuntimeException("Unexpected eval type (" + valEval.getClass().getName() + ")");
        }
-       
+
        public static int lookupIndexOfValue(ValueEval lookupValue, ValueVector vector, boolean isRangeLookup) throws EvaluationException {
                LookupValueComparer lookupComparer = createLookupComparer(lookupValue);
                int result;
@@ -364,13 +445,13 @@ final class LookupUtils {
                }
                return result;
        }
-       
-       
+
+
        /**
         * Finds first (lowest index) exact occurrence of specified value.
         * @param lookupValue the value to be found in column or row vector
-        * @param vector the values to be searched. For VLOOKUP this is the first column of the 
-        *      tableArray. For HLOOKUP this is the first row of the tableArray. 
+        * @param vector the values to be searched. For VLOOKUP this is the first column of the
+        *      tableArray. For HLOOKUP this is the first row of the tableArray.
         * @return zero based index into the vector, -1 if value cannot be found
         */
        private static int lookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) {
@@ -385,10 +466,10 @@ final class LookupUtils {
                return -1;
        }
 
-       
+
        /**
         * Encapsulates some standard binary search functionality so the unusual Excel behaviour can
-        * be clearly distinguished. 
+        * be clearly distinguished.
         */
        private static final class BinarySearchIndexes {
 
@@ -427,7 +508,7 @@ final class LookupUtils {
        }
        /**
         * Excel has funny behaviour when the some elements in the search vector are the wrong type.
-        * 
+        *
         */
        private static int performBinarySearch(ValueVector vector, LookupValueComparer lookupComparer) {
                // both low and high indexes point to values assumed too low and too high.
@@ -435,7 +516,7 @@ final class LookupUtils {
 
                while(true) {
                        int midIx = bsi.getMidIx();
-                       
+
                        if(midIx < 0) {
                                return bsi.getLowIx();
                        }
@@ -455,17 +536,17 @@ final class LookupUtils {
                }
        }
        /**
-        * Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the 
+        * Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the
         * first compatible value.
         * @param midIx 'mid' index (value which has the wrong type)
-        * @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid 
+        * @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid
         * index.  Zero or greater signifies that an exact match for the lookup value was found
         */
        private static int handleMidValueTypeMismatch(LookupValueComparer lookupComparer, ValueVector vector,
                        BinarySearchIndexes bsi, int midIx) {
                int newMid = midIx;
                int highIx = bsi.getHighIx();
-               
+
                while(true) {
                        newMid++;
                        if(newMid == highIx) {
@@ -511,9 +592,9 @@ final class LookupUtils {
        }
 
        public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException {
-               
+
                if (lookupValue instanceof BlankEval) {
-                       // blank eval can never be found in a lookup array 
+                       // blank eval can never be found in a lookup array
                        throw new EvaluationException(ErrorEval.NA);
                }
                if (lookupValue instanceof StringEval) {
index a2a12cdba8cf656144a2c2d72b36ffc47364d040..a464ec873aeb90ba949c08c1dcc377b92fdb3b2f 100644 (file)
@@ -29,18 +29,19 @@ import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 import org.apache.poi.hssf.record.formula.functions.LookupUtils.CompareResult;
 import org.apache.poi.hssf.record.formula.functions.LookupUtils.LookupValueComparer;
+import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
 
 /**
  * Implementation for the MATCH() Excel function.<p/>
- * 
+ *
  * <b>Syntax:</b><br/>
  * <b>MATCH</b>(<b>lookup_value</b>, <b>lookup_array</b>, match_type)<p/>
- * 
- * Returns a 1-based index specifying at what position in the <b>lookup_array</b> the specified 
+ *
+ * Returns a 1-based index specifying at what position in the <b>lookup_array</b> the specified
  * <b>lookup_value</b> is found.<p/>
- * 
+ *
  * Specific matching behaviour can be modified with the optional <b>match_type</b> parameter.
- * 
+ *
  *    <table border="0" cellpadding="1" cellspacing="0" summary="match_type parameter description">
  *      <tr><th>Value</th><th>Matching Behaviour</th></tr>
  *      <tr><td>1</td><td>(default) find the largest value that is less than or equal to lookup_value.
@@ -50,26 +51,26 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.LookupValueCompa
  *      <tr><td>-1</td><td>find the smallest value that is greater than or equal to lookup_value.
  *        The lookup_array must be in descending <i>order</i>*.</td></tr>
  *    </table>
- * 
+ *
  * * Note regarding <i>order</i> - For the <b>match_type</b> cases that require the lookup_array to
  *  be ordered, MATCH() can produce incorrect results if this requirement is not met.  Observed
  *  behaviour in Excel is to return the lowest index value for which every item after that index
  *  breaks the match rule.<br>
  *  The (ascending) sort order expected by MATCH() is:<br/>
  *  numbers (low to high), strings (A to Z), boolean (FALSE to TRUE)<br/>
- *  MATCH() ignores all elements in the lookup_array with a different type to the lookup_value. 
+ *  MATCH() ignores all elements in the lookup_array with a different type to the lookup_value.
  *  Type conversion of the lookup_array elements is never performed.
- *  
- *  
+ *
+ *
  * @author Josh Micich
  */
 public final class Match implements Function {
-       
+
 
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
-               
+
                double match_type = 1; // default
-               
+
                switch(args.length) {
                        case 3:
                                try {
@@ -85,15 +86,15 @@ public final class Match implements Function {
                        default:
                                return ErrorEval.VALUE_INVALID;
                }
-               
+
                boolean matchExact = match_type == 0;
                // Note - Excel does not strictly require -1 and +1
                boolean findLargestLessThanOrEqual = match_type > 0;
-               
-               
+
+
                try {
                        ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
-                       ValueEval[] lookupRange = evaluateLookupRange(args[1]);
+                       ValueVector lookupRange = evaluateLookupRange(args[1]);
                        int index = findIndexOfValue(lookupValue, lookupRange, matchExact, findLargestLessThanOrEqual);
                        return new NumberEval(index + 1); // +1 to convert to 1-based
                } catch (EvaluationException e) {
@@ -101,19 +102,40 @@ public final class Match implements Function {
                }
        }
 
-       private static ValueEval[] evaluateLookupRange(Eval eval) throws EvaluationException {
+       private static final class SingleValueVector implements ValueVector {
+
+               private final ValueEval _value;
+
+               public SingleValueVector(ValueEval value) {
+                       _value = value;
+               }
+
+               public ValueEval getItem(int index) {
+                       if (index != 0) {
+                               throw new RuntimeException("Invalid index ("
+                                               + index + ") only zero is allowed");
+                       }
+                       return _value;
+               }
+
+               public int getSize() {
+                       return 1;
+               }
+       }
+
+       private static ValueVector evaluateLookupRange(Eval eval) throws EvaluationException {
                if (eval instanceof RefEval) {
                        RefEval re = (RefEval) eval;
-                       return new ValueEval[] { re.getInnerValueEval(), };
+                       return new SingleValueVector(re.getInnerValueEval());
                }
                if (eval instanceof AreaEval) {
-                       AreaEval ae = (AreaEval) eval;
-                       if(!ae.isColumn() && !ae.isRow()) {
+                       ValueVector result = LookupUtils.createVector((AreaEval)eval);
+                       if (result == null) {
                                throw new EvaluationException(ErrorEval.NA);
                        }
-                       return ae.getValues();
+                       return result;
                }
-               
+
                // Error handling for lookup_range arg is also unusual
                if(eval instanceof NumericValueEval) {
                        throw new EvaluationException(ErrorEval.NA);
@@ -133,7 +155,7 @@ public final class Match implements Function {
 
 
 
-       private static double evaluateMatchTypeArg(Eval arg, int srcCellRow, short srcCellCol) 
+       private static double evaluateMatchTypeArg(Eval arg, int srcCellRow, short srcCellCol)
                        throws EvaluationException {
                Eval match_type = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
 
@@ -156,28 +178,29 @@ public final class Match implements Function {
                }
                throw new RuntimeException("Unexpected match_type type (" + match_type.getClass().getName() + ")");
        }
-       
+
        /**
         * @return zero based index
         */
-       private static int findIndexOfValue(ValueEval lookupValue, ValueEval[] lookupRange,
+       private static int findIndexOfValue(ValueEval lookupValue, ValueVector lookupRange,
                        boolean matchExact, boolean findLargestLessThanOrEqual) throws EvaluationException {
 
                LookupValueComparer lookupComparer = createLookupComparer(lookupValue, matchExact);
-               
+
+               int size = lookupRange.getSize();
                if(matchExact) {
-                       for (int i = 0; i < lookupRange.length; i++) {
-                               if(lookupComparer.compareTo(lookupRange[i]).isEqual()) {
+                       for (int i = 0; i < size; i++) {
+                               if(lookupComparer.compareTo(lookupRange.getItem(i)).isEqual()) {
                                        return i;
                                }
                        }
                        throw new EvaluationException(ErrorEval.NA);
                }
-               
+
                if(findLargestLessThanOrEqual) {
                        // Note - backward iteration
-                       for (int i = lookupRange.length - 1; i>=0;  i--) {
-                               CompareResult cmp = lookupComparer.compareTo(lookupRange[i]);
+                       for (int i = size - 1; i>=0;  i--) {
+                               CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
                                if(cmp.isTypeMismatch()) {
                                        continue;
                                }
@@ -187,11 +210,11 @@ public final class Match implements Function {
                        }
                        throw new EvaluationException(ErrorEval.NA);
                }
-               
+
                // else - find smallest greater than or equal to
                // TODO - is binary search used for (match_type==+1) ?
-               for (int i = 0; i<lookupRange.length; i++) {
-                       CompareResult cmp = lookupComparer.compareTo(lookupRange[i]);
+               for (int i = 0; i<size; i++) {
+                       CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
                        if(cmp.isEqual()) {
                                return i;
                        }
@@ -212,7 +235,7 @@ public final class Match implements Function {
                        if(isLookupValueWild(stringValue)) {
                                throw new RuntimeException("Wildcard lookup values '" + stringValue + "' not supported yet");
                        }
-                       
+
                }
                return LookupUtils.createLookupComparer(lookupValue);
        }
index 7ce39d6c71498924cb4b1d166c9a41a172a83ee8..b383e090df354c823377b83ec5d892b198af8ec7 100644 (file)
@@ -1,33 +1,27 @@
-/*
-* 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.
-*/
-/*
- * Created on May 9, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.BoolEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 
@@ -37,87 +31,21 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  * (treated as a boolean). If the specified arg is a number,
  * then it is true <=> 'number is non-zero'
  */
-public class Not extends BooleanFunction {
-    
-  
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        boolean b = true;
-        ValueEval tempVe = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 1:
-            if (operands[0] instanceof AreaEval) {
-                AreaEval ae = (AreaEval) operands[0];
-                if (ae.isRow() && ae.containsColumn(srcCol)) {
-                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
-                    tempVe = singleOperandEvaluate(ve);
-                } else if (ae.isColumn() && ae.containsRow(srcRow)) {
-                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
-                    tempVe = singleOperandEvaluate(ve);
-                } else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }            
-            }
-            else {
-                tempVe = singleOperandEvaluate(operands[0]);
-                if (tempVe instanceof StringEval) {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-                else if (tempVe instanceof ErrorEval) {
-                    retval = tempVe;
-                }
-            }
-        }
-        
-        if (retval == null) { // if no error
-            if (tempVe instanceof BoolEval) {
-                b = b && ((BoolEval) tempVe).getBooleanValue();
-            }
-            else if (tempVe instanceof StringEval) {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-            else if (tempVe instanceof ErrorEval) {
-                retval = tempVe;
-            }
-            retval = b ? BoolEval.FALSE : BoolEval.TRUE;
-        }
-        
-        return retval;
-    }
-    
-    
-    protected ValueEval singleOperandEvaluate(Eval ve) {
-        ValueEval retval = ErrorEval.VALUE_INVALID;
-        if (ve instanceof RefEval) {
-            RefEval re = (RefEval) ve;
-            retval = singleOperandEvaluate(re.getInnerValueEval());
-        }
-        else if (ve instanceof BoolEval) {
-            retval = (BoolEval) ve;
-        }
-        else if (ve instanceof NumberEval) {
-            NumberEval ne = (NumberEval) ve;
-            retval = ne.getNumberValue() != 0 ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-        else if (ve instanceof StringEval) {
-            StringEval se = (StringEval) ve;
-            String str = se.getStringValue();
-            retval = str.equalsIgnoreCase("true")
-                    ? BoolEval.TRUE
-                    : str.equalsIgnoreCase("false")
-                            ? BoolEval.FALSE
-                            : (ValueEval) ErrorEval.VALUE_INVALID;
-        }
-        else if (ve instanceof BlankEval) {
-            retval = BoolEval.FALSE;
-        }
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        return retval;
-    }
+public final class Not implements Function {
+
+       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               if (args.length != 1) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+               boolean boolArgVal;
+               try {
+                       ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
+                       Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
+                       boolArgVal = b == null ? false : b.booleanValue();
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               
+               return BoolEval.valueOf(!boolArgVal);
+       }
 }
index 22d7003d4d18fabb6c69dd3b207e0e134594e3c3..2d36037f5d49715c9cdaadff59b689b2a357fe57 100644 (file)
@@ -1,81 +1,32 @@
-/*
-* 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.
-*/
-/*
- * Created on May 9, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
+/* ====================================================================
+   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
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BoolEval;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
+   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.
+==================================================================== */
 
-public class Or extends BooleanFunction {
+package org.apache.poi.hssf.record.formula.functions;
+
+/**
+ * 
+ */
+public final class Or extends BooleanFunction {
 
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        boolean b = false;
-        boolean atleastOneNonBlank = false;
-        
-        /*
-         * Note: do not abort the loop if b is true, since we could be
-         * dealing with errorevals later. 
-         */
-        outer:
-        for (int i=0, iSize=operands.length; i<iSize; i++) {
-            if (operands[i] instanceof AreaEval) {
-                AreaEval ae = (AreaEval) operands[i];
-                ValueEval[] values = ae.getValues();
-                for (int j=0, jSize=values.length; j<jSize; j++) {
-                    ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
-                    if (tempVe instanceof BoolEval) {
-                        b = b || ((BoolEval) tempVe).getBooleanValue();
-                        atleastOneNonBlank = true;
-                    }
-                    else if (tempVe instanceof ErrorEval) {
-                        retval = tempVe;
-                        break outer;
-                    }
-                }
-            }
-            else {
-                ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
-                if (tempVe instanceof BoolEval) {
-                    b = b || ((BoolEval) tempVe).getBooleanValue();
-                    atleastOneNonBlank = true;
-                }
-                else if (tempVe instanceof ErrorEval) {
-                    retval = tempVe;
-                    break outer;
-                }
-            }
-        }
-        
-        if (!atleastOneNonBlank) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        
-        if (retval == null) { // if no error
-            retval = b ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-        
-        return retval;
-    }
+       protected boolean getInitialResultValue() {
+               return false;
+       }
 
+       protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
+               return cumulativeResult || currentValue;
+       }
 }
index 9f6eafa4dcf958c85e2c31c923ace1d4cfc23e25..1ed56e4718fb8966d0cc51f771220269d875c299 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval;
 import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
 import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
 import org.apache.poi.hssf.record.formula.eval.RefEval;
@@ -53,16 +54,6 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  */
 public final class Sumproduct implements Function {
 
-       private static final class EvalEx extends Exception {
-               private final ErrorEval _error;
-
-               public EvalEx(ErrorEval error) {
-                       _error = error;
-               }
-               public ErrorEval getError() {
-                       return _error;
-               }
-       }
 
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
                
@@ -86,14 +77,14 @@ public final class Sumproduct implements Function {
                                }
                                return evaluateAreaSumProduct(args);
                        }
-               } catch (EvalEx e) {
-                       return e.getError();
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
                }
                throw new RuntimeException("Invalid arg type for SUMPRODUCT: (" 
                                + firstArg.getClass().getName() + ")");
        }
 
-       private Eval evaluateSingleProduct(Eval[] evalArgs) throws EvalEx {
+       private static Eval evaluateSingleProduct(Eval[] evalArgs) throws EvaluationException {
                int maxN = evalArgs.length;
 
                double term = 1D;
@@ -104,7 +95,7 @@ public final class Sumproduct implements Function {
                return new NumberEval(term);
        }
 
-       private double getScalarValue(Eval arg) throws EvalEx {
+       private static double getScalarValue(Eval arg) throws EvaluationException {
                
                Eval eval;
                if (arg instanceof RefEval) {
@@ -121,9 +112,9 @@ public final class Sumproduct implements Function {
                        AreaEval ae = (AreaEval) eval;
                        // an area ref can work as a scalar value if it is 1x1
                        if(!ae.isColumn() || !ae.isRow()) {
-                               throw new EvalEx(ErrorEval.VALUE_INVALID);
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
                        }
-                       eval = ae.getValues()[0];
+                       eval = ae.getRelativeValue(0, 0);
                }
 
                if (!(eval instanceof ValueEval)) {
@@ -134,7 +125,7 @@ public final class Sumproduct implements Function {
                return getProductTerm((ValueEval) eval, true);
        }
 
-       private Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvalEx {
+       private static Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvaluationException {
                int maxN = evalArgs.length;
                AreaEval[] args = new AreaEval[maxN];
                try {
@@ -147,23 +138,27 @@ public final class Sumproduct implements Function {
                
                AreaEval firstArg = args[0];
                
-               int height = firstArg.getLastRow() - firstArg.getFirstRow() + 1;
-               int width = firstArg.getLastColumn() - firstArg.getFirstColumn() + 1; // TODO - junit
-               
+               int height = firstArg.getHeight();
+               int width = firstArg.getWidth(); // TODO - junit
                
-
-               double[][][] elements = new double[maxN][][];
-               
-               for (int n = 0; n < maxN; n++) {
-                       elements[n] = evaluateArea(args[n], height, width);
+               // first check dimensions
+               if (!areasAllSameSize(args, height, width)) {
+                       // normally this results in #VALUE!, 
+                       // but errors in individual cells take precedence
+                       for (int i = 1; i < args.length; i++) {
+                               throwFirstError(args[i]);
+                       }
+                       return ErrorEval.VALUE_INVALID;
                }
+
                double acc = 0;
                
-               for(int r=0; r<height; r++) {
-                       for(int c=0; c<width; c++) {
+               for (int rrIx=0; rrIx<height; rrIx++) {
+                       for (int rcIx=0; rcIx<width; rcIx++) {
                                double term = 1D;
                                for(int n=0; n<maxN; n++) {
-                                       term *= elements[n][r][c];
+                                       double val = getProductTerm(args[n].getRelativeValue(rrIx, rcIx), false);
+                                       term *= val;
                                }
                                acc += term;
                        }
@@ -172,60 +167,61 @@ public final class Sumproduct implements Function {
                return new NumberEval(acc);
        }
 
-       /**
-        * @return a 2-D array of the specified height and width corresponding to the evaluated cell 
-        *  values of the specified areaEval 
-        * @throws EvalEx if any ErrorEval value was encountered while evaluating the area
-        */
-       private static double[][] evaluateArea(AreaEval areaEval, int height, int width) throws EvalEx {
-               int fr =areaEval.getFirstRow();
-               int fc =areaEval.getFirstColumn();
-               
-               // check that height and width match
-               if(areaEval.getLastRow() - fr + 1 != height) {
-                       throw new EvalEx(ErrorEval.VALUE_INVALID);
-               }
-               if(areaEval.getLastColumn() - fc + 1 != width) {
-                       throw new EvalEx(ErrorEval.VALUE_INVALID);
-               }
-               ValueEval[] values = areaEval.getValues();
-               double[][] result = new double[height][width];
-               for(int r=0; r<height; r++) {
-                       for(int c=0; c<width; c++) {
-                               ValueEval ve = values[r*width + c];
-                               result[r][c] = getProductTerm(ve, false);
+       private static void throwFirstError(AreaEval areaEval) throws EvaluationException {
+               int height = areaEval.getHeight();
+               int width = areaEval.getWidth();
+               for (int rrIx=0; rrIx<height; rrIx++) {
+                       for (int rcIx=0; rcIx<width; rcIx++) {
+                               ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
+                               if (ve instanceof ErrorEval) {
+                                       throw new EvaluationException((ErrorEval) ve);
+                               }
+                       }
+               }
+       }
+
+       private static boolean areasAllSameSize(AreaEval[] args, int height, int width) {
+               for (int i = 0; i < args.length; i++) {
+                       AreaEval areaEval = args[i];
+                       // check that height and width match
+                       if(areaEval.getHeight() != height) {
+                               return false;
+                       }
+                       if(areaEval.getWidth() != width) {
+                               return false;
                        }
                }
-               return result;
+               return true;
        }
 
+
        /**
         * Determines a <code>double</code> value for the specified <code>ValueEval</code>. 
         * @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs.
-        * @throws EvalEx if <code>ve</code> represents an error value.
+        * @throws EvaluationException if <code>ve</code> represents an error value.
         * <p/>
         * Note - string values and empty cells are interpreted differently depending on 
         * <code>isScalarProduct</code>.  For scalar products, if any term is blank or a string, the
         * error (#VALUE!) is raised.  For area (sum)products, if any term is blank or a string, the
         * result is zero.
         */
-       private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvalEx {
+       private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvaluationException {
 
                if(ve instanceof BlankEval || ve == null) {
                        // TODO - shouldn't BlankEval.INSTANCE be used always instead of null?
                        // null seems to occur when the blank cell is part of an area ref (but not reliably)
                        if(isScalarProduct) {
-                               throw new EvalEx(ErrorEval.VALUE_INVALID);
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
                        }
                        return 0;
                }
                
                if(ve instanceof ErrorEval) {
-                       throw new EvalEx((ErrorEval)ve);
+                       throw new EvaluationException((ErrorEval)ve);
                }
                if(ve instanceof StringEval) {
                        if(isScalarProduct) {
-                               throw new EvalEx(ErrorEval.VALUE_INVALID);
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
                        }
                        // Note for area SUMPRODUCTs, string values are interpreted as zero
                        // even if they would parse as valid numeric values
index bd158b897c818e03a33c0a6227c5962a5f04c732..54f7d465e5de77d75946c61fec8dae63d2a93610 100644 (file)
@@ -42,40 +42,6 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
  */
 public final class Vlookup implements Function {
        
-       private static final class ColumnVector implements ValueVector {
-
-               private final AreaEval _tableArray;
-               private final int _size;
-               private final int _columnAbsoluteIndex;
-               private final int _firstRowAbsoluteIndex;
-
-               public ColumnVector(AreaEval tableArray, int columnIndex) {
-                       _columnAbsoluteIndex = tableArray.getFirstColumn() + columnIndex;
-                       if(!tableArray.containsColumn((short)_columnAbsoluteIndex)) {
-                               int lastColIx =  tableArray.getLastColumn() -  tableArray.getFirstColumn();
-                               throw new IllegalArgumentException("Specified column index (" + columnIndex 
-                                               + ") is outside the allowed range (0.." + lastColIx + ")");
-                       }
-                       _tableArray = tableArray;
-                       _size = tableArray.getLastRow() - tableArray.getFirstRow() + 1;
-                       if(_size < 1) {
-                               throw new RuntimeException("bad table array size zero");
-                       }
-                       _firstRowAbsoluteIndex = tableArray.getFirstRow();
-               }
-
-               public ValueEval getItem(int index) {
-                       if(index>_size) {
-                               throw new ArrayIndexOutOfBoundsException("Specified index (" + index 
-                                               + ") is outside the allowed range (0.." + (_size-1) + ")");
-                       }
-                       return _tableArray.getValueAt(_firstRowAbsoluteIndex + index, (short)_columnAbsoluteIndex);
-               }
-               public int getSize() {
-                       return _size;
-               }
-       }
-
        public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
                Eval arg3 = null;
                switch(args.length) {
@@ -93,7 +59,7 @@ public final class Vlookup implements Function {
                        ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
                        AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
                        boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
-                       int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, new ColumnVector(tableArray, 0), isRangeLookup);
+                       int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
                        ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
                        int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
                        ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
@@ -113,11 +79,9 @@ public final class Vlookup implements Function {
                if(colIndex < 0) {
                        throw EvaluationException.invalidValue();
                }
-               int nCols = tableArray.getLastColumn() - tableArray.getFirstColumn() + 1;
-               
-               if(colIndex >= nCols) {
+               if(colIndex >= tableArray.getWidth()) {
                        throw EvaluationException.invalidRef();
                }
-               return new ColumnVector(tableArray, colIndex);
+               return LookupUtils.createColumnVector(tableArray, colIndex);
        }
 }
index 7f8f7aa3b90d1d762fa285ed66a494159fbda638..e5ece999438eb042007ac7de3e6dc72129d7c5fe 100644 (file)
@@ -296,7 +296,7 @@ public class HSSFCell implements Cell {
 
                 if (cellType != this.cellType)
                 {
-                    frec = new FormulaRecordAggregate(new FormulaRecord(),null);
+                    frec = new FormulaRecordAggregate(new FormulaRecord());
                 }
                 else
                 {
@@ -592,41 +592,27 @@ public class HSSFCell implements Cell {
         int row=record.getRow();
         short col=record.getColumn();
         short styleIndex=record.getXFIndex();
-        //Workbook.currentBook=book;
-        if (formula==null) {
-            setCellType(CELL_TYPE_BLANK,false,row,col,styleIndex);
-        } else {
-            setCellType(CELL_TYPE_FORMULA,false,row,col,styleIndex);
-            FormulaRecordAggregate rec = (FormulaRecordAggregate) record;
-            FormulaRecord frec = rec.getFormulaRecord();
-            frec.setOptions(( short ) 2);
-            frec.setValue(0);
-            
-            //only set to default if there is no extended format index already set
-            if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
-            Ptg[] ptgs = FormulaParser.parse(formula, book);
-            int   size = 0;
-
-            // clear the Ptg Stack
-            for (int i=0, iSize=frec.getNumberOfExpressionTokens(); i<iSize; i++) {
-                frec.popExpressionToken();
-            }
 
-            // fill the Ptg Stack with Ptgs of new formula
-            for (int k = 0; k < ptgs.length; k++) {
-                size += ptgs[ k ].getSize();
-                frec.pushExpressionToken(ptgs[ k ]);
-            }
-            rec.getFormulaRecord().setExpressionLength(( short ) size);
-            //Workbook.currentBook = null;
+        if (formula==null) {
+            setCellType(CELL_TYPE_BLANK, false, row, col, styleIndex);
+            return;
         }
+        setCellType(CELL_TYPE_FORMULA, false, row, col, styleIndex);
+        FormulaRecordAggregate rec = (FormulaRecordAggregate) record;
+        FormulaRecord frec = rec.getFormulaRecord();
+        frec.setOptions((short) 2);
+        frec.setValue(0);
+        
+        //only set to default if there is no extended format index already set
+        if (rec.getXFIndex() == (short)0) {
+                       rec.setXFIndex((short) 0x0f);
+               }
+        Ptg[] ptgs = FormulaParser.parse(formula, book);
+        frec.setParsedExpression(ptgs);
     }
 
     public String getCellFormula() {
-        //Workbook.currentBook=book;
-        String retval = FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
-        //Workbook.currentBook=null;
-        return retval;
+        return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
     }
 
 
diff --git a/src/java/org/apache/poi/poifs/dev/POIFSDump.java b/src/java/org/apache/poi/poifs/dev/POIFSDump.java
new file mode 100755 (executable)
index 0000000..5028bab
--- /dev/null
@@ -0,0 +1,74 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.poifs.dev;\r
+\r
+import org.apache.poi.poifs.filesystem.*;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.FileOutputStream;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ *\r
+ * Dump internal structure of a OLE2 file into file system\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class POIFSDump {\r
+\r
+    public static void main(String[] args) throws Exception {\r
+        for (int i = 0; i < args.length; i++) {\r
+            System.out.println("Dumping " + args[i]);\r
+            FileInputStream is = new FileInputStream(args[i]);\r
+            POIFSFileSystem fs = new POIFSFileSystem(is);\r
+            is.close();\r
+\r
+            DirectoryEntry root = fs.getRoot();\r
+            File file = new File(root.getName());\r
+            file.mkdir();\r
+\r
+            dump(root, file);\r
+        }\r
+   }\r
+\r
+\r
+    public static void dump(DirectoryEntry root, File parent) throws IOException {\r
+        for(Iterator it = root.getEntries(); it.hasNext();){\r
+            Entry entry = (Entry)it.next();\r
+            if(entry instanceof DocumentNode){\r
+                DocumentNode node = (DocumentNode)entry;\r
+                DocumentInputStream is = new DocumentInputStream(node);\r
+                byte[] bytes = new byte[node.getSize()];\r
+                is.read(bytes);\r
+                is.close();\r
+\r
+                FileOutputStream out = new FileOutputStream(new File(parent, node.getName().trim()));\r
+                out.write(bytes);\r
+                out.close();\r
+            } else if (entry instanceof DirectoryEntry){\r
+                DirectoryEntry dir = (DirectoryEntry)entry;\r
+                File file = new File(parent, entry.getName());\r
+                file.mkdir();\r
+                dump(dir, file);\r
+            } else {\r
+                System.err.println("Skipping unsupported POIFS entry: " + entry);\r
+            }\r
+        }\r
+    }\r
+}\r
index 9cd7f918e4ed69138286d734516804ec5a2766c9..74e92b0a8dafb74fa5ad8a67b929a0347720b3b6 100644 (file)
@@ -102,7 +102,7 @@ public class FormulaEvaluator {
     
     /**
      * Does nothing
-     * @deprecated - not needed, since the current row can be derived from the cell
+     * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
      */
     public void setCurrentRow(Row row) {}
 
@@ -437,7 +437,7 @@ public class FormulaEvaluator {
             AreaEval ae = (AreaEval) evaluationResult;
             if (ae.isRow()) {
                 if(ae.isColumn()) {
-                    return ae.getValues()[0];
+                    return ae.getRelativeValue(0, 0);
                 }
                 return ae.getValueAt(ae.getFirstRow(), srcColNum);
             }
index aaea9d57a2a9a6a8a9706889e4bb6862fe798111..ed6f128822d006eb96d2fd949b70becedd7d6c70 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
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.util;
 
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
 import java.text.DecimalFormat;
 
 /**
@@ -29,27 +34,16 @@ import java.text.DecimalFormat;
  * @author Marc Johnson
  * @author Glen Stampoultzis  (glens at apache.org)
  */
-
-public class HexDump
-{
-    public static final String        EOL         =
-        System.getProperty("line.separator");
-//    private static final StringBuffer _lbuffer    = new StringBuffer(8);
-//    private static final StringBuffer _cbuffer    = new StringBuffer(2);
-    private static final char         _hexcodes[] =
-    {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
-        'E', 'F'
-    };
-    private static final int          _shifts[]   =
+public class HexDump {
+    public static final String EOL = System.getProperty("line.separator");
+    private static final char _hexcodes[] = "0123456789ABCDEF".toCharArray();
+    private static final int _shifts[]   =
     {
         60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0
     };
 
-
-    // all static methods, so no need for a public constructor
-    private HexDump()
-    {
+    private HexDump() {
+        // all static methods, so no need for a public constructor
     }
 
     /**
@@ -69,7 +63,7 @@ public class HexDump
      * @exception IllegalArgumentException if the output stream is
      *            null
      */
-    public synchronized static void dump(final byte [] data, final long offset,
+    public static void dump(final byte [] data, final long offset,
                             final OutputStream stream, final int index, final int length)
             throws IOException, ArrayIndexOutOfBoundsException,
                     IllegalArgumentException
@@ -413,6 +407,50 @@ public class HexDump
         byte[] data = buf.toByteArray();
         dump(data, 0, out, start, data.length);
     }
+    /**
+     * @return char array of uppercase hex chars, zero padded and prefixed with '0x'
+     */
+    private static char[] toHexChars(long pValue, int nBytes) {
+        int charPos = 2 + nBytes*2;
+        // The return type is char array because most callers will probably append the value to a
+        // StringBuffer, or write it to a Stream / Writer so there is no need to create a String;
+        char[] result = new char[charPos];
+        
+        long value = pValue;
+        do {
+            result[--charPos] = _hexcodes[(int) (value & 0x0F)];
+            value >>>= 4;
+        } while (charPos > 1);
+    
+        // Prefix added to avoid ambiguity
+        result[0] = '0';
+        result[1] = 'x';
+        return result;
+    }
+    /**
+     * @return char array of 4 (zero padded) uppercase hex chars and prefixed with '0x'
+     */
+    public static char[] longToHex(long value) {
+        return toHexChars(value, 8);
+    }
+    /**
+     * @return char array of 4 (zero padded) uppercase hex chars and prefixed with '0x'
+     */
+    public static char[] intToHex(int value) {
+        return toHexChars(value, 4);
+    }
+    /**
+     * @return char array of 2 (zero padded) uppercase hex chars and prefixed with '0x'
+     */
+    public static char[] shortToHex(int value) {
+        return toHexChars(value, 2);
+    }
+    /**
+     * @return char array of 1 (zero padded) uppercase hex chars and prefixed with '0x'
+     */
+    public static char[] byteToHex(int value) {
+        return toHexChars(value, 1);
+    }
 
     public static void main(String[] args) throws Exception {
         File file = new File(args[0]);
index fe18664d075aa7a997e19e8e747352724b09a368..b7f22c137b85abade91294ef3aec3ef330b365a1 100644 (file)
@@ -17,6 +17,7 @@
 package org.apache.poi.hslf.blip;
 
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogger;
 import org.apache.poi.hslf.model.Picture;
 import org.apache.poi.hslf.model.Shape;
 import org.apache.poi.hslf.exceptions.HSLFException;
@@ -76,7 +77,7 @@ public class WMF extends Metafile {
         Header header = new Header();
         header.wmfsize = data.length - aldus.getSize();
         header.bounds = new java.awt.Rectangle((short)aldus.left, (short)aldus.top, (short)aldus.right-(short)aldus.left, (short)aldus.bottom-(short)aldus.top);
-        //coefficiaent to translate from WMF dpi to 96pdi
+        //coefficient to translate from WMF dpi to 96pdi
         int coeff = 96*Shape.EMU_PER_POINT/aldus.inch;
         header.size = new java.awt.Dimension(header.bounds.width*coeff, header.bounds.height*coeff);
         header.zipsize = compressed.length;
@@ -119,7 +120,7 @@ public class WMF extends Metafile {
      *  <li>short  Checksum;       Checksum value for previous 10 shorts
      * </ul>
      */
-    public static class AldusHeader{
+    public class AldusHeader{
         public static final int APMHEADER_KEY = 0x9AC6CDD7;
 
         public int handle;
@@ -143,8 +144,9 @@ public class WMF extends Metafile {
             reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;
 
             checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            if (checksum != getChecksum())
-                throw new HSLFException("WMF checksum does not match the header data");
+            if (checksum != getChecksum()){
+                logger.log(POILogger.WARN, "WMF checksum does not match the header data");
+            }
         }
 
         /**
index 603361e4d82f27234353cd056bc226c4d5206b88..d02c4928ebad0b496d5c42d904b8911746ae2205 100644 (file)
@@ -125,7 +125,7 @@ public class PPTXMLDump {
                 dump(data, pos, size, padding);
             } else {
                 //dump first 100 bytes of the atom data
-                dump(out, data, pos, Math.min(size, 100), padding, true);
+                dump(out, data, pos, size, padding, true);
             }
                        padding--;
             write(out, "</"+recname + ">" + CR, padding);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java
new file mode 100755 (executable)
index 0000000..4b1889a
--- /dev/null
@@ -0,0 +1,152 @@
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Represents an ActiveX control in a PowerPoint document.\r
+ *\r
+ * TODO: finish\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ActiveXShape extends Picture {\r
+    public static final int DEFAULT_ACTIVEX_THUMBNAIL = -1;\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+    * @param pictureIdx the index of the picture\r
+     */\r
+    public ActiveXShape(int movieIdx, int pictureIdx){\r
+        super(pictureIdx, null);\r
+        setActiveXIndex(movieIdx);\r
+    }\r
+\r
+    /**\r
+      * Create a <code>Picture</code> object\r
+      *\r
+      * @param escherRecord the <code>EscherSpContainer</code> record which holds information about\r
+      *        this picture in the <code>Slide</code>\r
+      * @param parent the parent shape of this picture\r
+      */\r
+     protected ActiveXShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Create a new Placeholder and initialize internal structures\r
+     *\r
+     * @return the created <code>EscherContainerRecord</code> which holds shape data\r
+     */\r
+    protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {\r
+        _escherContainer = super.createSpContainer(idx, isChild);\r
+\r
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);\r
+        spRecord.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE | EscherSpRecord.FLAG_OLESHAPE);\r
+\r
+        setShapeType(ShapeTypes.HostControl);\r
+        setEscherProperty(EscherProperties.BLIP__PICTUREID, idx);\r
+        setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001);\r
+        setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008);\r
+        setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);\r
+        setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, -1);\r
+\r
+        EscherClientDataRecord cldata = new EscherClientDataRecord();\r
+        cldata.setOptions((short)0xF);\r
+        _escherContainer.getChildRecords().add(cldata);\r
+\r
+        OEShapeAtom oe = new OEShapeAtom();\r
+\r
+        //convert hslf into ddf\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        try {\r
+            oe.writeOut(out);\r
+        } catch(Exception e){\r
+            throw new HSLFException(e);\r
+        }\r
+        cldata.setRemainingData(out.toByteArray());\r
+\r
+        return _escherContainer;\r
+    }\r
+\r
+    /**\r
+     * Assign a control to this shape\r
+     *\r
+     * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}\r
+     * @param idx  the index of the movie\r
+     */\r
+    public void setActiveXIndex(int idx){\r
+        EscherContainerRecord spContainer = getSpContainer();\r
+        for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) {\r
+            EscherRecord obj = (EscherRecord) it.next();\r
+            if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) {\r
+                EscherClientDataRecord clientRecord = (EscherClientDataRecord)obj;\r
+                byte[] recdata = clientRecord.getRemainingData();\r
+                LittleEndian.putInt(recdata, 8, idx);\r
+            }\r
+        }\r
+    }\r
+\r
+    public int getControlIndex(){\r
+        int idx = -1;\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        if(oe != null) idx = oe.getOptions();\r
+        return idx;\r
+    }\r
+\r
+    /**\r
+     * Set a property of this ActiveX control\r
+     * @param key\r
+     * @param value\r
+     */\r
+    public void setProperty(String key, String value){\r
+\r
+    }\r
+\r
+    /**\r
+     * Document-level container that specifies information about an ActiveX control\r
+     *\r
+     * @return container that specifies information about an ActiveX control\r
+     */\r
+    public ExControl getExControl(){\r
+        int idx = getControlIndex();\r
+        ExControl ctrl = null;\r
+        Document doc = getSheet().getSlideShow().getDocumentRecord();\r
+        ExObjList lst = (ExObjList)doc.findFirstOfType(RecordTypes.ExObjList.typeID);\r
+        if(lst != null){\r
+            Record[] ch = lst.getChildRecords();\r
+            for (int i = 0; i < ch.length; i++) {\r
+                if(ch[i] instanceof ExControl){\r
+                    ExControl c = (ExControl)ch[i];\r
+                    if(c.getExOleObjAtom().getObjID() == idx){\r
+                        ctrl = c;\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return ctrl;\r
+    }\r
+\r
+    protected void afterInsert(Sheet sheet){\r
+        ExControl ctrl = getExControl();\r
+        ctrl.getExControlAtom().setSlideId(sheet._getSheetNumber());\r
+\r
+        try {\r
+            String name = ctrl.getProgId() + "-" + getControlIndex();\r
+            byte[] data = (name + '\u0000').getBytes("UTF-16LE");\r
+            EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.GROUPSHAPE__SHAPENAME, false, data);\r
+            EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+            opt.addEscherProperty(prop);\r
+        } catch (UnsupportedEncodingException e){\r
+            throw new HSLFException(e);\r
+        }\r
+\r
+    }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java
new file mode 100755 (executable)
index 0000000..4cf7037
--- /dev/null
@@ -0,0 +1,159 @@
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Iterator;\r
+\r
+/**\r
+ * Represents a movie in a PowerPoint document.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class MovieShape extends Picture {\r
+    public static final int DEFAULT_MOVIE_THUMBNAIL = -1;\r
+\r
+    public static final int MOVIE_MPEG = 1;\r
+    public static final int MOVIE_AVI  = 2;\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+    * @param pictureIdx the index of the picture\r
+     */\r
+    public MovieShape(int movieIdx, int pictureIdx){\r
+        super(pictureIdx, null);\r
+        setMovieIndex(movieIdx);\r
+        setAutoPlay(true);\r
+    }\r
+\r
+    /**\r
+     * Create a new <code>Picture</code>\r
+     *\r
+     * @param idx the index of the picture\r
+     * @param parent the parent shape\r
+     */\r
+    public MovieShape(int movieIdx, int idx, Shape parent) {\r
+        super(idx, parent);\r
+        setMovieIndex(movieIdx);\r
+    }\r
+\r
+    /**\r
+      * Create a <code>Picture</code> object\r
+      *\r
+      * @param escherRecord the <code>EscherSpContainer</code> record which holds information about\r
+      *        this picture in the <code>Slide</code>\r
+      * @param parent the parent shape of this picture\r
+      */\r
+     protected MovieShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+    }\r
+\r
+    /**\r
+     * Create a new Placeholder and initialize internal structures\r
+     *\r
+     * @return the created <code>EscherContainerRecord</code> which holds shape data\r
+     */\r
+    protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {\r
+        _escherContainer = super.createSpContainer(idx, isChild);\r
+\r
+        setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x1000100);\r
+        setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x10001);\r
+\r
+        EscherClientDataRecord cldata = new EscherClientDataRecord();\r
+        cldata.setOptions((short)0xF);\r
+        _escherContainer.getChildRecords().add(cldata);\r
+\r
+        OEShapeAtom oe = new OEShapeAtom();\r
+        InteractiveInfo info = new InteractiveInfo();\r
+        InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();\r
+        infoAtom.setAction(InteractiveInfoAtom.ACTION_MEDIA);\r
+        infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NULL);\r
+\r
+        AnimationInfo an = new AnimationInfo();\r
+        AnimationInfoAtom anAtom = an.getAnimationInfoAtom();\r
+        anAtom.setFlag(AnimationInfoAtom.Automatic, true);\r
+\r
+        //convert hslf into ddf\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        try {\r
+            oe.writeOut(out);\r
+            an.writeOut(out);\r
+            info.writeOut(out);\r
+        } catch(Exception e){\r
+            throw new HSLFException(e);\r
+        }\r
+        cldata.setRemainingData(out.toByteArray());\r
+\r
+        return _escherContainer;\r
+    }\r
+\r
+    /**\r
+     * Assign a movie to this shape\r
+     *\r
+     * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)}\r
+     * @param idx  the index of the movie\r
+     */\r
+    public void setMovieIndex(int idx){\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        oe.setOptions(idx);\r
+\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null) {\r
+            AnimationInfoAtom ai = an.getAnimationInfoAtom();\r
+            ai.setDimColor(0x07000000);\r
+            ai.setFlag(AnimationInfoAtom.Automatic, true);\r
+            ai.setFlag(AnimationInfoAtom.Play, true);\r
+            ai.setFlag(AnimationInfoAtom.Synchronous, true);\r
+            ai.setOrderID(idx + 1);\r
+        }\r
+    }\r
+\r
+    public void setAutoPlay(boolean flag){\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null){\r
+            an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag);\r
+            updateClientData();\r
+        }\r
+    }\r
+\r
+    public boolean  isAutoPlay(){\r
+        AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);\r
+        if(an != null){\r
+            return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic);\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     *  Returns UNC or local path to a video file\r
+     *\r
+     * @return UNC or local path to a video file\r
+     */\r
+    public String getPath(){\r
+        OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);\r
+        int idx = oe.getOptions();\r
+\r
+        SlideShow ppt = getSheet().getSlideShow();\r
+        ExObjList lst = (ExObjList)ppt.getDocumentRecord().findFirstOfType(RecordTypes.ExObjList.typeID);\r
+        if(lst == null) return null;\r
+\r
+        Record[]  r = lst.getChildRecords();\r
+        for (int i = 0; i < r.length; i++) {\r
+            if(r[i] instanceof ExMCIMovie){\r
+                ExMCIMovie mci = (ExMCIMovie)r[i];\r
+                ExVideoContainer exVideo = mci.getExVideo();\r
+                int objectId = exVideo.getExMediaAtom().getObjectId();\r
+                if(objectId == idx){\r
+                    return exVideo.getPathAtom().getText();\r
+                }\r
+            }\r
+\r
+        }\r
+        return null;\r
+    }\r
+}\r
index 4bef9033dbe2f255a47360e7643d08385954d61e..8b4abd5e590e5680058bf0e288192bd4a9ee1f88 100644 (file)
@@ -19,8 +19,10 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.POILogFactory;
+import org.apache.poi.hslf.record.*;
 
 import java.util.List;
+import java.util.Iterator;
 
 /**
  * Create a <code>Shape</code> object depending on its type
@@ -45,14 +47,14 @@ public class ShapeFactory {
 
     public static ShapeGroup createShapeGroup(EscherContainerRecord spContainer, Shape parent){
         ShapeGroup group = null;
-        UnknownEscherRecord opt = (UnknownEscherRecord)Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
+        EscherRecord opt = Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122);
         if(opt != null){
             try {
                 EscherPropertyFactory f = new EscherPropertyFactory();
-                List props = f.createProperties( opt.getData(), 0, opt.getInstance() );
+                List props = f.createProperties( opt.serialize(), 8, opt.getInstance() );
                 EscherSimpleProperty p = (EscherSimpleProperty)props.get(0);
                 if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){
-                    group = new ShapeGroup(spContainer, parent);
+                    group = new Table(spContainer, parent);
                 } else {
                     group = new ShapeGroup(spContainer, parent);
                 }
@@ -68,7 +70,7 @@ public class ShapeFactory {
      }
 
     public static Shape createSimpeShape(EscherContainerRecord spContainer, Shape parent){
-        Shape shape;
+        Shape shape = null;
         EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
 
         int type = spRecord.getOptions() >> 4;
@@ -76,14 +78,26 @@ public class ShapeFactory {
             case ShapeTypes.TextBox:
                 shape = new TextBox(spContainer, parent);
                 break;
-            case ShapeTypes.HostControl: 
+            case ShapeTypes.HostControl:
             case ShapeTypes.PictureFrame: {
-                EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
-                EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID);
-                if(prop != null)
-                    shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object 
-                else
-                    shape = new Picture(spContainer, parent);
+                InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID);
+                OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID);
+                if(info != null && info.getInteractiveInfoAtom() != null){
+                    switch(info.getInteractiveInfoAtom().getAction()){
+                        case InteractiveInfoAtom.ACTION_OLE:
+                            shape = new OLEShape(spContainer, parent);
+                            break;
+                        case InteractiveInfoAtom.ACTION_MEDIA:
+                            shape = new MovieShape(spContainer, parent);
+                            break;
+                        default:
+                            break;
+                    }
+                } else if (oes != null){
+                    shape = new OLEShape(spContainer, parent);
+                }
+                
+                if(shape == null) shape = new Picture(spContainer, parent);
                 break;
             }
             case ShapeTypes.Line:
@@ -108,4 +122,22 @@ public class ShapeFactory {
         return shape;
 
     }
+
+    protected static Record getClientDataRecord(EscherContainerRecord spContainer, int recordType) {
+        Record oep = null;
+        for (Iterator it = spContainer.getChildRecords().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].getRecordType() == recordType) {
+                        return records[j];
+                    }
+                }
+            }
+        }
+        return oep;
+    }
+
 }
index d336cb3f3ffbef268dd210a2f75419dc395d5d58..71d10948c5bc52f236758cc5b6b3d73b30a5071a 100644 (file)
@@ -21,11 +21,13 @@ import org.apache.poi.ddf.*;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.exceptions.HSLFException;
 
 import java.awt.*;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.util.Iterator;
+import java.io.ByteArrayOutputStream;
 
 /**
  *  An abstract simple (non-group) shape.
@@ -35,6 +37,12 @@ import java.util.Iterator;
  */
 public class SimpleShape extends Shape {
 
+    /**
+     * Records stored in EscherClientDataRecord
+     */
+    protected Record[] _clientRecords;
+    protected EscherClientDataRecord _clientData;
+
     /**
      * Create a SimpleShape object and initialize it from the supplied Record container.
      *
@@ -293,21 +301,46 @@ public class SimpleShape extends Shape {
      * @param recordType type of the record to search
      */
     protected Record getClientDataRecord(int recordType) {
-        Record oep = null;
-        EscherContainerRecord spContainer = getSpContainer();
-        for (Iterator it = spContainer.getChildRecords().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].getRecordType() == recordType) {
-                        return records[j];
-                    }
-                }
+
+        Record[] records = getClientRecords();
+        if(records != null) for (int i = 0; i < records.length; i++) {
+            if(records[i].getRecordType() == recordType){
+                return records[i];
+            }
+        }
+        return null;
+    }
+
+    protected Record[] getClientRecords() {
+        if(_clientData == null){
+            EscherRecord r = Shape.getEscherChild(getSpContainer(), EscherClientDataRecord.RECORD_ID);
+            //ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID
+            //convert in to EscherClientDataRecord on the fly
+            if(!(r instanceof EscherClientDataRecord)){
+                byte[] data = r.serialize();
+                r = new EscherClientDataRecord();
+                r.fillFields(data, 0, new DefaultEscherRecordFactory());
             }
+            _clientData = (EscherClientDataRecord)r;
         }
-        return oep;
+        if(_clientData != null && _clientRecords == null){
+            byte[] data = _clientData.getRemainingData();
+            _clientRecords = Record.findChildRecords(data, 0, data.length);
+        }
+        return _clientRecords;
     }
 
+    protected void updateClientData() {
+        if(_clientData != null && _clientRecords != null){
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            try {
+                for (int i = 0; i < _clientRecords.length; i++) {
+                    _clientRecords[i].writeOut(out);
+                }
+            } catch(Exception e){
+                throw new HSLFException(e);
+            }
+            _clientData.setRemainingData(out.toByteArray());
+        }
+    }
 }
index 43dab58af129ef7f5bafe6354778ede79df27796..113a2d8f2b05b460bbfb2e0eaf8c149bb066fd46 100755 (executable)
@@ -89,7 +89,7 @@ public class Table extends ShapeGroup {
      * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape\r
      * @param parent       the parent of the shape\r
      */\r
-    protected Table(EscherContainerRecord escherRecord, Shape parent) {\r
+    public Table(EscherContainerRecord escherRecord, Shape parent) {\r
         super(escherRecord, parent);\r
     }\r
 \r
index 6eff54329078d622c775cca914a1131eafe1c873..9a86120e7c7484b1b622280ad5538b955fa6bc47 100755 (executable)
@@ -23,9 +23,11 @@ import org.apache.poi.util.POILogFactory;
 \r
 import java.text.AttributedString;\r
 import java.text.AttributedCharacterIterator;\r
+import java.text.BreakIterator;\r
 import java.awt.font.TextAttribute;\r
 import java.awt.font.LineBreakMeasurer;\r
 import java.awt.font.TextLayout;\r
+import java.awt.font.FontRenderContext;\r
 import java.awt.*;\r
 import java.awt.geom.Rectangle2D;\r
 import java.awt.geom.Point2D;\r
@@ -89,11 +91,69 @@ public class TextPainter {
     }\r
 \r
     public void paint(Graphics2D graphics){\r
+        Rectangle2D anchor = _shape.getLogicalAnchor2D();\r
+        TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext());\r
+        if(elem == null) return;\r
+\r
+        float textHeight = 0;\r
+        for (int i = 0; i < elem.length; i++) {\r
+            textHeight += elem[i].ascent + elem[i].descent;\r
+        }\r
+\r
+        int valign = _shape.getVerticalAlignment();\r
+        double y0 = anchor.getY();\r
+        switch (valign){\r
+            case TextShape.AnchorTopBaseline:\r
+            case TextShape.AnchorTop:\r
+                y0 += _shape.getMarginTop();\r
+                break;\r
+            case TextShape.AnchorBottom:\r
+                y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();\r
+                break;\r
+            default:\r
+            case TextShape.AnchorMiddle:\r
+                float delta =  (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();\r
+                y0 += _shape.getMarginTop()  + delta/2;\r
+                break;\r
+        }\r
+\r
+        //finally draw the text fragments\r
+        for (int i = 0; i < elem.length; i++) {\r
+            y0 += elem[i].ascent;\r
+\r
+            Point2D.Double pen = new Point2D.Double();\r
+            pen.y = y0;\r
+            switch (elem[i]._align) {\r
+                default:\r
+                case TextShape.AlignLeft:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft();\r
+                    break;\r
+                case TextShape.AlignCenter:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
+                    break;\r
+                case TextShape.AlignRight:\r
+                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
+                            (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
+                    break;\r
+            }\r
+            if(elem[i]._bullet != null){\r
+                graphics.drawString(elem[i]._bullet.getIterator(), (float)(pen.x + elem[i]._bulletOffset), (float)pen.y);\r
+            }\r
+            AttributedCharacterIterator chIt = elem[i]._text.getIterator();\r
+            if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
+                graphics.drawString(chIt, (float)(pen.x + elem[i]._textOffset), (float)pen.y);\r
+            }\r
+            y0 += elem[i].descent;\r
+        }\r
+    }\r
+\r
+    public TextElement[] getTextElements(float textWidth, FontRenderContext frc){\r
         TextRun run = _shape.getTextRun();\r
-        if (run == null) return;\r
+        if (run == null) return null;\r
 \r
         String text = run.getText();\r
-        if (text == null || text.equals("")) return;\r
+        if (text == null || text.equals("")) return null;\r
 \r
         AttributedString at = getAttributedString(run);\r
 \r
@@ -101,11 +161,8 @@ public class TextPainter {
         int paragraphStart = it.getBeginIndex();\r
         int paragraphEnd = it.getEndIndex();\r
 \r
-        Rectangle2D anchor = _shape.getLogicalAnchor2D();\r
-\r
-        float textHeight = 0;\r
         ArrayList lines = new ArrayList();\r
-        LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());\r
+        LineBreakMeasurer measurer = new LineBreakMeasurer(it, frc);\r
         measurer.setPosition(paragraphStart);\r
         while (measurer.getPosition() < paragraphEnd) {\r
             int startIndex = measurer.getPosition();\r
@@ -120,7 +177,7 @@ public class TextPainter {
                 break;\r
             }\r
 \r
-            float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();\r
+            float wrappingWidth = textWidth - _shape.getMarginLeft() - _shape.getMarginRight();\r
             int bulletOffset = rt.getBulletOffset();\r
             int textOffset = rt.getTextOffset();\r
             int indent = rt.getIndentLevel();\r
@@ -138,7 +195,7 @@ public class TextPainter {
                 if(text_val != 0) textOffset = text_val;\r
             }\r
 \r
-            wrappingWidth -= textOffset;\r
+            if(bulletOffset > 0 || prStart || startIndex == 0) wrappingWidth -= textOffset;\r
 \r
             if (_shape.getWordWrap() == TextShape.WrapNone) {\r
                 wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;\r
@@ -147,7 +204,7 @@ public class TextPainter {
             TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1,\r
                     nextBreak == -1 ? paragraphEnd : nextBreak, true);\r
             if (textLayout == null) {\r
-                textLayout = measurer.nextLayout((float)anchor.getWidth(),\r
+                textLayout = measurer.nextLayout(textWidth,\r
                     nextBreak == -1 ? paragraphEnd : nextBreak, false);\r
             }\r
             if(textLayout == null){\r
@@ -173,6 +230,8 @@ public class TextPainter {
             el.advance = textLayout.getAdvance();\r
             el._textOffset = textOffset;\r
             el._text = new AttributedString(it, startIndex, endIndex);\r
+            el.textStartIndex = startIndex;\r
+            el.textEndIndex = endIndex;\r
 \r
             if (prStart){\r
                 int sp = rt.getSpaceBefore();\r
@@ -203,8 +262,6 @@ public class TextPainter {
             }\r
             el.descent = descent;\r
 \r
-            textHeight += el.ascent + el.descent;\r
-\r
             if(rt.isBullet() && (prStart || startIndex == 0)){\r
                 it.setIndex(startIndex);\r
 \r
@@ -236,56 +293,11 @@ public class TextPainter {
             lines.add(el);\r
         }\r
 \r
-        int valign = _shape.getVerticalAlignment();\r
-        double y0 = anchor.getY();\r
-        switch (valign){\r
-            case TextShape.AnchorTopBaseline:\r
-            case TextShape.AnchorTop:\r
-                y0 += _shape.getMarginTop();\r
-                break;\r
-            case TextShape.AnchorBottom:\r
-                y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom();\r
-                break;\r
-            default:\r
-            case TextShape.AnchorMiddle:\r
-                float delta =  (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom();\r
-                y0 += _shape.getMarginTop()  + delta/2;\r
-                break;\r
-        }\r
-\r
         //finally draw the text fragments\r
-        for (int i = 0; i < lines.size(); i++) {\r
-            TextElement elem = (TextElement)lines.get(i);\r
-            y0 += elem.ascent;\r
-\r
-            Point2D.Double pen = new Point2D.Double();\r
-            pen.y = y0;\r
-            switch (elem._align) {\r
-                default:\r
-                case TextShape.AlignLeft:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft();\r
-                    break;\r
-                case TextShape.AlignCenter:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
-                    break;\r
-                case TextShape.AlignRight:\r
-                    pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
-                    break;\r
-            }\r
-            if(elem._bullet != null){\r
-                graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
-            }\r
-            AttributedCharacterIterator chIt = elem._text.getIterator();\r
-            if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
-                graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y);\r
-            }\r
-            y0 += elem.descent;\r
-        }\r
+        TextElement[] elems = new TextElement[lines.size()];\r
+        return (TextElement[])lines.toArray(elems);\r
     }\r
 \r
-\r
     public static class TextElement {\r
         public AttributedString _text;\r
         public int _textOffset;\r
@@ -294,5 +306,6 @@ public class TextPainter {
         public int _align;\r
         public float ascent, descent;\r
         public float advance;\r
+        public int textStartIndex, textEndIndex;\r
     }\r
 }\r
index 27dbe1b2d573f0c50bf5fb7bd3f5bf2d8deeb1be..cf2db50e86b2a6e0846f759693640845b85398a7 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.poi.hslf.model;
 
 import java.util.LinkedList;
 import java.util.Vector;
+import java.util.List;
 
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.*;
@@ -45,7 +46,8 @@ public class TextRun
        protected TextBytesAtom  _byteAtom;
        protected TextCharsAtom  _charAtom;
        protected StyleTextPropAtom _styleAtom;
-       protected boolean _isUnicode;
+    protected TextRulerAtom _ruler;
+    protected boolean _isUnicode;
        protected RichTextRun[] _rtRuns;
        private SlideShow slideShow;
     private Sheet sheet;
@@ -103,155 +105,159 @@ public class TextRun
                        pStyles = _styleAtom.getParagraphStyles();
                        cStyles = _styleAtom.getCharacterStyles();
                }
-
-               // Handle case of no current style, with a default
-               if(pStyles.size() == 0 || cStyles.size() == 0) { 
-                       _rtRuns = new RichTextRun[1];
-                       _rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
-               } else {
-                       // Build up Rich Text Runs, one for each 
-                       //  character/paragraph style pair
-                       Vector rtrs = new Vector();
-
-                       int pos = 0;
-                       
-                       int curP = 0;
-                       int curC = 0;
-                       int pLenRemain = -1;
-                       int cLenRemain = -1;
-                       
-                       // Build one for each run with the same style
-                       while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
-                               // Get the Props to use
-                               TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
-                               TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
-                               
-                               int pLen = pProps.getCharactersCovered();
-                               int cLen = cProps.getCharactersCovered();
-                               
-                               // Handle new pass
-                               boolean freshSet = false;
-                               if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
-                               if(pLenRemain == -1) { pLenRemain = pLen; }
-                               if(cLenRemain == -1) { cLenRemain = cLen; }
-                               
-                               // So we know how to build the eventual run
-                               int runLen = -1;
-                               boolean pShared = false;
-                               boolean cShared = false;
-                               
-                               // Same size, new styles - neither shared
-                               if(pLen == cLen && freshSet) {
-                                       runLen = cLen;
-                                       pShared = false;
-                                       cShared = false;
-                                       curP++;
-                                       curC++;
-                                       pLenRemain = -1;
-                                       cLenRemain = -1;
-                               } else {
-                                       // Some sharing
-                                       
-                                       // See if we are already in a shared block
-                                       if(pLenRemain < pLen) {
-                                               // Existing shared p block
-                                               pShared = true;
-                                               
-                                               // Do we end with the c block, or either side of it?
-                                               if(pLenRemain == cLenRemain) {
-                                                       // We end at the same time
-                                                       cShared = false;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       curC++;
-                                                       pLenRemain = -1;
-                                                       cLenRemain = -1;
-                                               } else if(pLenRemain < cLenRemain) {
-                                                       // We end before the c block
-                                                       cShared = true;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               } else {
-                                                       // We end after the c block
-                                                       cShared = false;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               }
-                                       } else if(cLenRemain < cLen) {
-                                               // Existing shared c block
-                                               cShared = true;
-                                               
-                                               // Do we end with the p block, or either side of it?
-                                               if(pLenRemain == cLenRemain) {
-                                                       // We end at the same time
-                                                       pShared = false;
-                                                       runLen = cLenRemain;
-                                                       curP++;
-                                                       curC++;
-                                                       pLenRemain = -1;
-                                                       cLenRemain = -1;
-                                               } else if(cLenRemain < pLenRemain) {
-                                                       // We end before the p block
-                                                       pShared = true;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               } else {
-                                                       // We end after the p block
-                                                       pShared = false;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               }
-                                       } else {
-                                               // Start of a shared block
-                                               if(pLenRemain < cLenRemain) {
-                                                       // Shared c block
-                                                       pShared = false;
-                                                       cShared = true;
-                                                       runLen = pLenRemain;
-                                                       curP++;
-                                                       cLenRemain -= pLenRemain;
-                                                       pLenRemain = -1;
-                                               } else {
-                                                       // Shared p block
-                                                       pShared = true;
-                                                       cShared = false;
-                                                       runLen = cLenRemain;
-                                                       curC++;
-                                                       pLenRemain -= cLenRemain;
-                                                       cLenRemain = -1;
-                                               }
-                                       }
-                               }
-                               
-                               // Wind on
-                               int prevPos = pos;
-                               pos += runLen;
-                               // Adjust for end-of-run extra 1 length
-                               if(pos > runRawText.length()) {
-                                       runLen--; 
-                               }
-                               
-                               // Save
-                               RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
-                               rtrs.add(rtr);
-                       }
-                       
-                       // Build the array
-                       _rtRuns = new RichTextRun[rtrs.size()];
-                       rtrs.copyInto(_rtRuns);
-               }
+        buildRichTextRuns(pStyles, cStyles, runRawText);
        }
        
-       
-       // Update methods follow
+       public void buildRichTextRuns(LinkedList pStyles, LinkedList cStyles, String runRawText){
+
+        // Handle case of no current style, with a default
+        if(pStyles.size() == 0 || cStyles.size() == 0) {
+            _rtRuns = new RichTextRun[1];
+            _rtRuns[0] = new RichTextRun(this, 0, runRawText.length());
+        } else {
+            // Build up Rich Text Runs, one for each
+            //  character/paragraph style pair
+            Vector rtrs = new Vector();
+
+            int pos = 0;
+
+            int curP = 0;
+            int curC = 0;
+            int pLenRemain = -1;
+            int cLenRemain = -1;
+
+            // Build one for each run with the same style
+            while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) {
+                // Get the Props to use
+                TextPropCollection pProps = (TextPropCollection)pStyles.get(curP);
+                TextPropCollection cProps = (TextPropCollection)cStyles.get(curC);
+
+                int pLen = pProps.getCharactersCovered();
+                int cLen = cProps.getCharactersCovered();
+
+                // Handle new pass
+                boolean freshSet = false;
+                if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; }
+                if(pLenRemain == -1) { pLenRemain = pLen; }
+                if(cLenRemain == -1) { cLenRemain = cLen; }
+
+                // So we know how to build the eventual run
+                int runLen = -1;
+                boolean pShared = false;
+                boolean cShared = false;
+
+                // Same size, new styles - neither shared
+                if(pLen == cLen && freshSet) {
+                    runLen = cLen;
+                    pShared = false;
+                    cShared = false;
+                    curP++;
+                    curC++;
+                    pLenRemain = -1;
+                    cLenRemain = -1;
+                } else {
+                    // Some sharing
+
+                    // See if we are already in a shared block
+                    if(pLenRemain < pLen) {
+                        // Existing shared p block
+                        pShared = true;
+
+                        // Do we end with the c block, or either side of it?
+                        if(pLenRemain == cLenRemain) {
+                            // We end at the same time
+                            cShared = false;
+                            runLen = pLenRemain;
+                            curP++;
+                            curC++;
+                            pLenRemain = -1;
+                            cLenRemain = -1;
+                        } else if(pLenRemain < cLenRemain) {
+                            // We end before the c block
+                            cShared = true;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        } else {
+                            // We end after the c block
+                            cShared = false;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        }
+                    } else if(cLenRemain < cLen) {
+                        // Existing shared c block
+                        cShared = true;
+
+                        // Do we end with the p block, or either side of it?
+                        if(pLenRemain == cLenRemain) {
+                            // We end at the same time
+                            pShared = false;
+                            runLen = cLenRemain;
+                            curP++;
+                            curC++;
+                            pLenRemain = -1;
+                            cLenRemain = -1;
+                        } else if(cLenRemain < pLenRemain) {
+                            // We end before the p block
+                            pShared = true;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        } else {
+                            // We end after the p block
+                            pShared = false;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        }
+                    } else {
+                        // Start of a shared block
+                        if(pLenRemain < cLenRemain) {
+                            // Shared c block
+                            pShared = false;
+                            cShared = true;
+                            runLen = pLenRemain;
+                            curP++;
+                            cLenRemain -= pLenRemain;
+                            pLenRemain = -1;
+                        } else {
+                            // Shared p block
+                            pShared = true;
+                            cShared = false;
+                            runLen = cLenRemain;
+                            curC++;
+                            pLenRemain -= cLenRemain;
+                            cLenRemain = -1;
+                        }
+                    }
+                }
+
+                // Wind on
+                int prevPos = pos;
+                pos += runLen;
+                // Adjust for end-of-run extra 1 length
+                if(pos > runRawText.length()) {
+                    runLen--;
+                }
+
+                // Save
+                RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared);
+                rtrs.add(rtr);
+            }
+
+            // Build the array
+            _rtRuns = new RichTextRun[rtrs.size()];
+            rtrs.copyInto(_rtRuns);
+        }
+
+    }
+
+    // Update methods follow
        
        /**
         * Adds the supplied text onto the end of the TextRun, 
@@ -379,7 +385,7 @@ public class TextRun
         * @param run
         * @param s
         */
-       public synchronized void changeTextInRichTextRun(RichTextRun run, String s) {
+       public void changeTextInRichTextRun(RichTextRun run, String s) {
                // Figure out which run it is
                int runID = -1;
                for(int i=0; i<_rtRuns.length; i++) {
@@ -457,7 +463,7 @@ public class TextRun
         *  as the the first character has. 
         * If you care about styling, do setText on a RichTextRun instead 
         */
-       public synchronized void setRawText(String s) {
+       public void setRawText(String s) {
                // Save the new text to the atoms
                storeText(s);
                RichTextRun fst = _rtRuns[0];
@@ -491,7 +497,7 @@ public class TextRun
      * Changes the text.
      * Converts '\r' into '\n'
      */
-    public synchronized void setText(String s) {
+    public void setText(String s) {
         String text = normalize(s);
         setRawText(text);
     }
@@ -500,7 +506,7 @@ public class TextRun
         * Ensure a StyleTextPropAtom is present for this run, 
         *  by adding if required. Normally for internal TextRun use.
         */
-       public synchronized void ensureStyleAtomPresent() {
+       public void ensureStyleAtomPresent() {
                if(_styleAtom != null) {
                        // All there
                        return;
@@ -669,13 +675,28 @@ public class TextRun
     }
 
     public TextRulerAtom getTextRuler(){
-        for (int i = 0; i < _records.length; i++) {
-            if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i];
+        if(_ruler == null){
+            if(_records != null) for (int i = 0; i < _records.length; i++) {
+                if(_records[i] instanceof TextRulerAtom) {
+                    _ruler = (TextRulerAtom)_records[i];
+                    break;
+                }
+            }
+
         }
-        return null;
+        return _ruler;
 
     }
 
+    public TextRulerAtom createTextRuler(){
+        _ruler = getTextRuler();
+        if(_ruler == null){
+            _ruler = TextRulerAtom.getParagraphInstance();
+            _headerAtom.getParentRecord().appendChildRecord(_ruler);
+        }
+        return _ruler;
+    }
+
     /**
      * Returns a new string with line breaks converted into internal ppt representation
      */
@@ -692,4 +713,5 @@ public class TextRun
     public Record[] getRecords(){
         return _records;
     }
+
 }
index de78b62656289d4a76a94008b06b08abfabe542c..751753b22e538e426dd25d9c42fc43459dd2c480 100755 (executable)
@@ -133,6 +133,7 @@ public abstract class TextShape extends SimpleShape {
             _txtbox.appendChildRecord(sta);
 
             _txtrun = new TextRun(tha,tca,sta);
+            _txtrun._records = new Record[]{tha, tca, sta};
             _txtrun.setText("");
 
             _escherContainer.addChildRecord(_txtbox.getEscherRecord());
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java
new file mode 100755 (executable)
index 0000000..37dae8f
--- /dev/null
@@ -0,0 +1,97 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  You may obtain a copy of the License at\r
+*\r
+*     http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about animation information for a shape.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class AnimationInfo extends RecordContainer {\r
+       private byte[] _header;\r
+\r
+       // Links to our more interesting children\r
+       private AnimationInfoAtom animationAtom;\r
+\r
+       /**\r
+        * Set things up, and find our more interesting children\r
+        */\r
+       protected AnimationInfo(byte[] source, int start, int len) {\r
+               // Grab the header\r
+               _header = new byte[8];\r
+               System.arraycopy(source,start,_header,0,8);\r
+\r
+               // Find our children\r
+               _children = Record.findChildRecords(source,start+8,len-8);\r
+               findInterestingChildren();\r
+       }\r
+\r
+       /**\r
+        * Go through our child records, picking out the ones that are\r
+        *  interesting, and saving those for use by the easy helper\r
+        *  methods.\r
+        */     \r
+       private void findInterestingChildren() {\r
+\r
+               // First child should be the ExMediaAtom\r
+               if(_children[0] instanceof AnimationInfoAtom) {\r
+                       animationAtom = (AnimationInfoAtom)_children[0];\r
+               } else {\r
+                       logger.log(POILogger.ERROR, "First child record wasn't a AnimationInfoAtom, was of type " + _children[0].getRecordType());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Create a new AnimationInfo, with blank fields\r
+        */\r
+       public AnimationInfo() {\r
+        // Setup our header block\r
+               _header = new byte[8];\r
+               _header[0] = 0x0f; // We are a container record\r
+               LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+               \r
+        _children = new Record[1];\r
+               _children[0] = animationAtom = new AnimationInfoAtom();\r
+       }\r
+\r
+       /**\r
+        * We are of type 4103\r
+        */\r
+       public long getRecordType() { return RecordTypes.AnimationInfo.typeID; }\r
+\r
+       /**\r
+        * Write the contents of the record back, so it can be written\r
+        *  to disk\r
+        */\r
+       public void writeOut(OutputStream out) throws IOException {\r
+               writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+       }\r
+\r
+    /**\r
+     * Returns the AnimationInfo\r
+     */\r
+    public AnimationInfoAtom getAnimationInfoAtom() {\r
+        return animationAtom;\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java
new file mode 100755 (executable)
index 0000000..66982ea
--- /dev/null
@@ -0,0 +1,278 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * An atom record that specifies the animation information for a shape.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class AnimationInfoAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * whether the animation plays in the reverse direction\r
+     */\r
+    public static final int Reverse = 1;\r
+    /**\r
+     * whether the animation starts automatically\r
+     */\r
+    public static final int Automatic = 4;\r
+    /**\r
+     * whether the animation has an associated sound\r
+     */\r
+    public static final int Sound = 16;\r
+    /**\r
+     * whether all playing sounds are stopped when this animation begins\r
+     */\r
+    public static final int StopSound = 64;\r
+    /**\r
+     * whether an associated sound, media or action verb is activated when the shape is clicked.\r
+     */\r
+    public static final int Play = 256;\r
+    /**\r
+     * specifies that the animation, while playing, stops other slide show actions.\r
+     */\r
+    public static final int Synchronous = 1024;\r
+    /**\r
+     * whether the shape is hidden while the animation is not playing\r
+     */\r
+    public static final int Hide = 4096;\r
+    /**\r
+     * whether the background of the shape is animated\r
+     */\r
+    public static final int AnimateBg = 16384;\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    protected AnimationInfoAtom() {\r
+        _recdata = new byte[28];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 0, (short)0x01);\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected AnimationInfoAtom(byte[] source, int start, int len) {\r
+        // Get the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.AnimationInfoAtom.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * A rgb structure that specifies a color for the dim effect after the animation is complete.\r
+     *\r
+     * @return  color for the dim effect after the animation is complete\r
+     */\r
+    public int getDimColor(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * A rgb structure that specifies a color for the dim effect after the animation is complete.\r
+     *\r
+     * @param rgb  color for the dim effect after the animation is complete\r
+     */\r
+    public void setDimColor(int rgb){\r
+         LittleEndian.putInt(_recdata, 0, rgb);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * @return A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public int getMask(){\r
+        return LittleEndian.getInt(_recdata, 4);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying video\r
+     *\r
+     * @param mask A bit mask specifying options for displaying video\r
+     */\r
+    public void setMask(int mask){\r
+        LittleEndian.putInt(_recdata, 4, mask);\r
+    }\r
+\r
+    /**\r
+     * @param bit the bit to check\r
+     * @return whether the specified flag is set\r
+     */\r
+    public boolean getFlag(int bit){\r
+        return (getMask() & bit) != 0;\r
+    }\r
+\r
+    /**\r
+     * @param  bit the bit to set\r
+     * @param  value whether the specified bit is set\r
+     */\r
+    public void setFlag(int bit, boolean value){\r
+        int mask = getMask();\r
+        if(value) mask |= bit;\r
+        else mask &= ~bit;\r
+        setMask(mask);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies a reference to a sound\r
+     * in the SoundCollectionContainer record to locate the embedded audio\r
+     *\r
+     * @return  reference to a sound\r
+     */\r
+    public int getSoundIdRef(){\r
+        return LittleEndian.getInt(_recdata, 8);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies a reference to a sound\r
+     * in the SoundCollectionContainer record to locate the embedded audio\r
+     *\r
+     * @param id reference to a sound\r
+     */\r
+    public void setSoundIdRef(int id){\r
+         LittleEndian.putInt(_recdata, 8, id);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.\r
+     * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.\r
+     */\r
+    public int getDelayTime(){\r
+        return LittleEndian.getInt(_recdata, 12);\r
+    }\r
+    /**\r
+     * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play.\r
+     * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored.\r
+     */\r
+    public void setDelayTime(int id){\r
+         LittleEndian.putInt(_recdata, 12, id);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the order of the animation in the slide.\r
+     * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of\r
+     * the corresponding placeholder shape on the main master slide or title master slide.\r
+     * The value -1 SHOULD NOT <105> be used.\r
+     */\r
+    public int getOrderID(){\r
+        return LittleEndian.getInt(_recdata, 16);\r
+    }\r
+\r
+    /**\r
+     * A signed integer that specifies the order of the animation in the slide.\r
+     * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of\r
+     * the corresponding placeholder shape on the main master slide or title master slide.\r
+     * The value -1 SHOULD NOT <105> be used.\r
+     */\r
+    public void setOrderID(int id){\r
+         LittleEndian.putInt(_recdata, 16, id);\r
+    }\r
+\r
+    /**\r
+     * An unsigned integer that specifies the number of slides that this animation continues playing.\r
+     * This field is utilized only in conjunction with media.\r
+     * The value 0xFFFFFFFF specifies that the animation plays for one slide.\r
+     */\r
+    public int getSlideCount(){\r
+        return LittleEndian.getInt(_recdata, 18);\r
+    }\r
+\r
+    /**\r
+     * An unsigned integer that specifies the number of slides that this animation continues playing.\r
+     * This field is utilized only in conjunction with media.\r
+     * The value 0xFFFFFFFF specifies that the animation plays for one slide.\r
+     */\r
+    public void setSlideCount(int id){\r
+         LittleEndian.putInt(_recdata, 18, id);\r
+    }\r
+\r
+    public String toString(){\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append("AnimationInfoAtom\n");\r
+        buf.append("\tDimColor: " + getDimColor() + "\n");\r
+        int mask = getMask();\r
+        buf.append("\tMask: " + mask + ", 0x"+Integer.toHexString(mask)+"\n");\r
+        buf.append("\t  Reverse: " + getFlag(Reverse)+"\n");\r
+        buf.append("\t  Automatic: " + getFlag(Automatic)+"\n");\r
+        buf.append("\t  Sound: " + getFlag(Sound)+"\n");\r
+        buf.append("\t  StopSound: " + getFlag(StopSound)+"\n");\r
+        buf.append("\t  Play: " + getFlag(Play)+"\n");\r
+        buf.append("\t  Synchronous: " + getFlag(Synchronous)+"\n");\r
+        buf.append("\t  Hide: " + getFlag(Hide)+"\n");\r
+        buf.append("\t  AnimateBg: " + getFlag(AnimateBg)+"\n");\r
+        buf.append("\tSoundIdRef: " + getSoundIdRef() + "\n");\r
+        buf.append("\tDelayTime: " + getDelayTime() + "\n");\r
+        buf.append("\tOrderID: " + getOrderID() + "\n");\r
+        buf.append("\tSlideCount: " + getSlideCount() + "\n");\r
+        return buf.toString();\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java
new file mode 100755 (executable)
index 0000000..81ad98d
--- /dev/null
@@ -0,0 +1,53 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  You may obtain a copy of the License at\r
+*\r
+*     http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about a movie stored externally.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExAviMovie extends ExMCIMovie {\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     */\r
+    protected ExAviMovie(byte[] source, int start, int len) {\r
+        super(source, start, len);\r
+    }\r
+\r
+    /**\r
+     * Create a new ExAviMovie, with blank fields\r
+     */\r
+    public ExAviMovie() {\r
+        super();\r
+\r
+    }\r
+    /**\r
+     * We are of type 4102\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.ExAviMovie.typeID;\r
+    }\r
+\r
+}\r
index e6e7da893c71ed0591578ae52918bca70f0741da..603cbad764800e6de61c6520850d7bf8def93c57 100755 (executable)
@@ -24,7 +24,7 @@ import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;\r
 \r
 /**\r
- * Container for OLE Control object. It contains:\r
+ * A container record that specifies information about an ActiveX control. It contains:\r
  * <p>\r
  * 1. ExControlAtom (4091)\r
  * 2. ExOleObjAtom (4035)\r
@@ -40,9 +40,6 @@ import org.apache.poi.util.POILogger;
  */\r
 public class ExControl extends ExEmbed {\r
 \r
-    // Links to our more interesting children\r
-    private ExControlAtom ctrlAtom;\r
-\r
     /**\r
      * Set things up, and find our more interesting children\r
      *\r
@@ -60,7 +57,7 @@ public class ExControl extends ExEmbed {
     public ExControl() {\r
         super();\r
 \r
-        _children[0] = ctrlAtom = new ExControlAtom();\r
+        _children[0] = embedAtom = new ExControlAtom();\r
     }\r
 \r
     /**\r
@@ -70,7 +67,7 @@ public class ExControl extends ExEmbed {
      */\r
     public ExControlAtom getExControlAtom()\r
     {\r
-        return ctrlAtom;\r
+        return (ExControlAtom)_children[0];\r
     }\r
 \r
     /**\r
@@ -82,14 +79,4 @@ public class ExControl extends ExEmbed {
     public long getRecordType() {\r
         return RecordTypes.ExControl.typeID;\r
     }\r
-\r
-    protected RecordAtom getEmbedAtom(Record[] children){\r
-        RecordAtom atom = null;\r
-        if(_children[0] instanceof ExControlAtom) {\r
-            atom = (ExControlAtom)_children[0];\r
-        } else {\r
-            logger.log(POILogger.ERROR, "First child record wasn't a ExControlAtom, was of type " + _children[0].getRecordType());\r
-        }\r
-        return atom;\r
-    }\r
 }\r
index ae99d0a235e7b32aa426106a305fa318694d5e4c..0204803ea5ae59d2a4508181772fa4c5998d7e48 100755 (executable)
@@ -23,8 +23,7 @@ import java.io.OutputStream;
 import org.apache.poi.util.LittleEndian;\r
 \r
 /**\r
- * Contains a long integer, slideID, which stores the unique slide identifier of the slide\r
- * where this control resides.\r
+ * An atom record that specifies an ActiveX control.\r
  *\r
  * @author Yegor Kozlov\r
  */\r
@@ -67,10 +66,32 @@ public class ExControlAtom extends RecordAtom {
         _id = LittleEndian.getInt(source, start + 8);\r
     }\r
 \r
+    /**\r
+     * An integer that specifies which presentation slide is associated with the ActiveX control.\r
+     * <p>\r
+     * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.\r
+     * The value 0x00000000 specifies a null reference.\r
+     * </p>\r
+     *\r
+     * @return an integer that specifies which presentation slide is associated with the ActiveX control\r
+     */\r
     public int getSlideId() {\r
         return _id;\r
     }\r
 \r
+    /**\r
+     * Sets which presentation slide is associated with the ActiveX control.\r
+     *\r
+     * @param id an integer that specifies which presentation slide is associated with the ActiveX control\r
+     * <p>\r
+     * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record.\r
+     * The value 0x00000000 specifies a null reference.\r
+     * </p>\r
+     */\r
+    public void setSlideId(int id) {\r
+        _id = id;\r
+    }\r
+\r
     /**\r
      * Gets the record type.\r
      * @return the record type.\r
index 6d61f2ef71097bb5f554eae75cd20898fbbb3e0f..09e95abe4c22285b8996181382ad8667ff0ba5f5 100644 (file)
@@ -36,7 +36,7 @@ public class ExEmbed extends RecordContainer {
     private byte[] _header;
 
     // Links to our more interesting children
-    private RecordAtom embedAtom;
+    protected RecordAtom embedAtom;
     private ExOleObjAtom oleObjAtom;
     private CString menuName;
     private CString progId;
@@ -72,10 +72,11 @@ public class ExEmbed extends RecordContainer {
 
         // Setup our child records
         CString cs1 = new CString();
+        cs1.setOptions(0x1 << 4);
         CString cs2 = new CString();
+        cs2.setOptions(0x2 << 4);
         CString cs3 = new CString();
-//        cs1.setOptions(0x00);
-//        cs2.setOptions(0x10);
+        cs3.setOptions(0x3 << 4);
         _children[0] = new ExEmbedAtom();
         _children[1] = new ExOleObjAtom();
         _children[2] = cs1;
@@ -91,7 +92,11 @@ public class ExEmbed extends RecordContainer {
     private void findInterestingChildren() {
 
         // First child should be the ExHyperlinkAtom
-        embedAtom = getEmbedAtom(_children);
+        if(_children[0] instanceof ExEmbedAtom) {
+            embedAtom = (ExEmbedAtom)_children[0];
+        } else {
+            logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
+        }
 
         // Second child should be the ExOleObjAtom
         if (_children[1] instanceof ExOleObjAtom) {
@@ -102,25 +107,17 @@ public class ExEmbed extends RecordContainer {
 
         for (int i = 2; i < _children.length; i++) {
             if (_children[i] instanceof CString){
-                if (menuName == null) menuName = (CString)_children[i];
-                else if (progId == null) progId = (CString)_children[i];
-                else if (clipboardName == null) clipboardName = (CString)_children[i];
-            } else {
-                logger.log(POILogger.ERROR, "Record after atoms wasn't a CString, was of type " + _children[i].getRecordType());
+                CString cs = (CString)_children[i];
+                int opts = cs.getOptions() >> 4;
+                switch(opts){
+                    case 0x1: menuName = cs; break;
+                    case 0x2: progId = cs; break;
+                    case 0x3: clipboardName = cs; break;
+                }
             }
         }
     }
 
-    protected RecordAtom getEmbedAtom(Record[] children){
-        RecordAtom atom = null;
-        if(_children[0] instanceof ExEmbedAtom) {
-            atom = (ExEmbedAtom)_children[0];
-        } else {
-            logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType());
-        }
-        return atom;
-    }
-
     /**
      * Gets the {@link ExEmbedAtom}.
      *
@@ -151,6 +148,11 @@ public class ExEmbed extends RecordContainer {
         return menuName == null ? null : menuName.getText();
     }
 
+    public void setMenuName(String s)
+    {
+        if(menuName != null) menuName.setText(s);
+    }
+
     /**
      * Gets the OLE Programmatic Identifier.
      * 
@@ -161,6 +163,10 @@ public class ExEmbed extends RecordContainer {
         return progId == null ? null : progId.getText();
     }
 
+    public void setProgId(String s)
+    {
+        if(progId != null) progId.setText(s);
+    }
     /**
      * Gets the name that appears in the paste special dialog.
      *
@@ -171,6 +177,10 @@ public class ExEmbed extends RecordContainer {
         return clipboardName == null ? null : clipboardName.getText();
     }
 
+    public void setClipboardName(String s)
+    {
+        if(clipboardName != null) clipboardName.setText(s);
+    }
     /**
      * Returns the type (held as a little endian in bytes 3 and 4)
      * that this class handles.
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java
new file mode 100755 (executable)
index 0000000..c226092
--- /dev/null
@@ -0,0 +1,100 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  You may obtain a copy of the License at\r
+*\r
+*     http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about a movie stored externally.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExMCIMovie extends RecordContainer {\r
+    private byte[] _header;\r
+\r
+    //An ExVideoContainer record that specifies information about the MCI movie\r
+    private ExVideoContainer exVideo;\r
+\r
+    /**\r
+     * Set things up, and find our more interesting children\r
+     */\r
+    protected ExMCIMovie(byte[] source, int start, int len) {\r
+        // Grab the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source, start, _header, 0, 8);\r
+\r
+        // Find our children\r
+        _children = Record.findChildRecords(source, start + 8, len - 8);\r
+        findInterestingChildren();\r
+    }\r
+\r
+    /**\r
+     * Create a new ExMCIMovie, with blank fields\r
+     */\r
+    public ExMCIMovie() {\r
+        _header = new byte[8];\r
+        // Setup our header block\r
+        _header[0] = 0x0f; // We are a container record\r
+        LittleEndian.putShort(_header, 2, (short) getRecordType());\r
+\r
+        exVideo = new ExVideoContainer();\r
+        _children = new Record[]{exVideo};\r
+\r
+    }\r
+\r
+    /**\r
+     * Go through our child records, picking out the ones that are\r
+     * interesting, and saving those for use by the easy helper\r
+     * methods.\r
+     */\r
+    private void findInterestingChildren() {\r
+\r
+        // First child should be the ExVideoContainer\r
+        if (_children[0] instanceof ExVideoContainer) {\r
+            exVideo = (ExVideoContainer) _children[0];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "First child record wasn't a ExVideoContainer, was of type " + _children[0].getRecordType());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * We are of type 4103\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.ExMCIMovie.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        writeOut(_header[0], _header[1], getRecordType(), _children, out);\r
+    }\r
+\r
+    /**\r
+     * Returns the ExVideoContainer that specifies information about the MCI movie\r
+     */\r
+    public ExVideoContainer getExVideo() {\r
+        return exVideo; }\r
+\r
+\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java
new file mode 100755 (executable)
index 0000000..e80768f
--- /dev/null
@@ -0,0 +1,172 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * An atom record that specifies information about external audio or video data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExMediaAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * A bit that specifies whether the audio or video data is repeated continuously during playback.\r
+     */\r
+    public static final int fLoop = 1;\r
+    /**\r
+     * A bit that specifies whether the audio or video data is rewound after playing.\r
+     */\r
+    public static final int fRewind = 2;\r
+    /**\r
+     * A bit that specifies whether the audio data is recorded narration for the slide show. It MUST be FALSE if this ExMediaAtom record is contained by an ExVideoContainer record.\r
+     */\r
+    public static final int fNarration = 4;\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    protected ExMediaAtom() {\r
+        _recdata = new byte[8];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected ExMediaAtom(byte[] source, int start, int len) {\r
+        // Get the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() { return RecordTypes.ExMediaAtom.typeID; }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies an ID for an external object.\r
+     *\r
+     * @return  A 4-byte unsigned integer that specifies an ID for an external object.\r
+     */\r
+    public int getObjectId(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * A 4-byte unsigned integer that specifies an ID for an external object.\r
+     *\r
+     * @param id  A 4-byte unsigned integer that specifies an ID for an external object.\r
+     */\r
+    public void setObjectId(int id){\r
+         LittleEndian.putInt(_recdata, 0, id);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying headers and footers\r
+     *\r
+     * @return A bit mask specifying options for displaying headers and footers\r
+     */\r
+    public int getMask(){\r
+        return LittleEndian.getInt(_recdata, 4);\r
+    }\r
+\r
+    /**\r
+     *  A bit mask specifying options for displaying video\r
+     *\r
+     * @param mask A bit mask specifying options for displaying video\r
+     */\r
+    public void setMask(int mask){\r
+        LittleEndian.putInt(_recdata, 4, mask);\r
+    }\r
+\r
+    /**\r
+     * @param bit the bit to check\r
+     * @return whether the specified flag is set\r
+     */\r
+    public boolean getFlag(int bit){\r
+        return (getMask() & bit) != 0;\r
+    }\r
+\r
+    /**\r
+     * @param  bit the bit to set\r
+     * @param  value whether the specified bit is set\r
+     */\r
+    public void setFlag(int bit, boolean value){\r
+        int mask = getMask();\r
+        if(value) mask |= bit;\r
+        else mask &= ~bit;\r
+        setMask(mask);\r
+    }\r
+\r
+    public String toString(){\r
+        StringBuffer buf = new StringBuffer();\r
+        buf.append("ExMediaAtom\n");\r
+        buf.append("\tObjectId: " + getObjectId() + "\n");\r
+        buf.append("\tMask    : " + getMask() + "\n");\r
+        buf.append("\t  fLoop        : " + getFlag(fLoop) + "\n");\r
+        buf.append("\t  fRewind   : " + getFlag(fRewind) + "\n");\r
+        buf.append("\t  fNarration    : " + getFlag(fNarration) + "\n");\r
+        return buf.toString();\r
+    }\r
+\r
+}\r
index ae96275cd538c8528247d9b735f59e0d8e44e15a..9f412d249e89370abb18d53a5778c904342b3685 100644 (file)
@@ -65,11 +65,35 @@ import org.apache.poi.util.LittleEndian;
  */
 public class ExOleObjAtom extends RecordAtom {
 
+    /**
+     * The object) is displayed as an embedded object inside of a container,
+     */
     public static final int DRAW_ASPECT_VISIBLE = 1;
+    /**
+     *   The object is displayed as a thumbnail image.
+     */
+    public static final int DRAW_ASPECT_THUMBNAIL = 2;
+    /**
+     *   The object is displayed as an icon.
+     */
     public static final int DRAW_ASPECT_ICON = 4;
+    /**
+     *   The object is displayed on the screen as though it were printed to a printer.
+     */
+    public static final int DRAW_ASPECT_DOCPRINT = 8;
 
+    /**
+     * An embedded OLE object; the object is serialized and saved within the file.
+     */
     public static final int TYPE_EMBEDDED = 0;
+    /**
+     * A linked OLE object; the object is saved outside of the file.
+     */
     public static final int TYPE_LINKED = 1;
+    /**
+     * The OLE object is an ActiveX control.
+     */
+    public static final int TYPE_CONTROL = 2;
 
     public static final int SUBTYPE_DEFAULT = 0;
     public static final int SUBTYPE_CLIPART_GALLERY = 1;
@@ -101,14 +125,13 @@ public class ExOleObjAtom extends RecordAtom {
     /**
      * Constructs a brand new link related atom record.
      */
-    protected ExOleObjAtom() {
+    public ExOleObjAtom() {
         _header = new byte[8];
-        _data = new byte[18];
+        _data = new byte[24];
 
+        LittleEndian.putShort(_header, 0, (short)1); //MUST be 0x1
         LittleEndian.putShort(_header, 2, (short)getRecordType());
         LittleEndian.putInt(_header, 4, _data.length);
-
-        // I hope it is fine for the other values to be zero.
     }
 
     /**
@@ -144,6 +167,16 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 0);
     }
 
+    /**
+     * Sets whether the object can be completely seen, or if only the
+     * icon is visible.
+     *
+     * @param aspect the draw aspect, one of the {@code DRAW_ASPECT_*} constants.
+     */
+     public void setDrawAspect(int aspect) {
+        LittleEndian.putInt(_data, 0, aspect);
+    }
+
     /**
      * Gets whether the object is embedded or linked.
      *
@@ -153,6 +186,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 4);
     }
 
+    /**
+     * Sets whether the object is embedded or linked.
+     *
+     * @param type the type, one of the {@code TYPE_EMBEDDED_*} constants.
+     */
+    public void setType(int type) {
+        LittleEndian.putInt(_data, 4, type);
+    }
+
     /**
      * Gets the unique identifier for the OLE object.
      *
@@ -162,6 +204,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 8);
     }
 
+    /**
+     * Sets the unique identifier for the OLE object.
+     *
+     * @param id the object ID.
+     */
+    public void setObjID(int id) {
+        LittleEndian.putInt(_data, 8, id);
+    }
+
     /**
      * Gets the type of OLE object.
      * 
@@ -171,6 +222,15 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 12);
     }
 
+    /**
+     * Sets the type of OLE object.
+     *
+     * @param type the sub-type, one of the {@code SUBTYPE_*} constants.
+     */
+    public void setSubType(int type) {
+        LittleEndian.putInt(_data, 12, type);
+    }
+
     /**
      * Gets the reference to the persistent object
      *
@@ -181,6 +241,16 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 16);
     }
 
+    /**
+     * Sets the reference to the persistent object
+     *
+     * @param ref the reference to the persistent object, corresponds with an
+     *         {@code ExOleObjStg} storage container.
+     */
+    public void setObjStgDataRef(int ref) {
+        LittleEndian.putInt(_data, 16, ref);
+    }
+
     /**
      * Gets whether the object's image is blank.
      *
@@ -191,6 +261,24 @@ public class ExOleObjAtom extends RecordAtom {
         return LittleEndian.getInt(_data, 20) != 0;
     }
     
+    /**
+     * Gets misc options (the last four bytes in the atom).
+     *
+     * @return {@code true} if the object's image is blank.
+     */
+    public int getOptions() {
+        // Even though this is a mere boolean, KOffice's code says it's an int.
+        return LittleEndian.getInt(_data, 20);
+    }
+
+    /**
+     * Sets misc options (the last four bytes in the atom).
+     */
+    public void setOptions(int opts) {
+        // Even though this is a mere boolean, KOffice's code says it's an int.
+        LittleEndian.putInt(_data, 20, opts);
+    }
+
     /**
      * Returns the type (held as a little endian in bytes 3 and 4)
      * that this class handles.
@@ -210,4 +298,16 @@ public class ExOleObjAtom extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    public String toString(){
+        StringBuffer buf = new StringBuffer();
+        buf.append("ExOleObjAtom\n");
+        buf.append("  drawAspect: " + getDrawAspect() + "\n");
+        buf.append("  type: " + getType() + "\n");
+        buf.append("  objID: " + getObjID() + "\n");
+        buf.append("  subType: " + getSubType() + "\n");
+        buf.append("  objStgDataRef: " + getObjStgDataRef() + "\n");
+        buf.append("  options: " + getOptions() + "\n");
+        return buf.toString();
+    }
 }
index c7408b0fe4e5ffbe903c27659d9abedd37341c7d..36fbebf4c48b320303e012b46d5af20f40ab9b37 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi.hslf.record;
 import java.io.*;
 import java.util.zip.InflaterInputStream;
 import java.util.zip.DeflaterOutputStream;
+import java.util.Hashtable;
 
 import org.apache.poi.util.LittleEndian;
 
@@ -28,7 +29,7 @@ import org.apache.poi.util.LittleEndian;
  *
  * @author Daniel Noll
  */
-public class ExOleObjStg extends RecordAtom implements PersistRecord {
+public class ExOleObjStg extends RecordAtom implements PositionDependentRecord, PersistRecord {
 
     private int _persistId; // Found from PersistPtrHolder
 
@@ -45,10 +46,11 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
     /**
      * Constructs a new empty storage container.
      */
-    protected ExOleObjStg() {
+    public ExOleObjStg() {
         _header = new byte[8];
         _data = new byte[0];
 
+        LittleEndian.putShort(_header, 0, (short)0x10);
         LittleEndian.putShort(_header, 2, (short)getRecordType());
         LittleEndian.putInt(_header, 4, _data.length);
     }
@@ -90,6 +92,10 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
         return new InflaterInputStream(compressedStream);
     }
 
+    public byte[] getRawData() {
+        return _data;
+    }
+
     /**
      * Sets the embedded data.
      *
@@ -144,4 +150,23 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord {
     public void setPersistId(int id) {
         _persistId = id;
     }
+
+    /** Our location on the disk, as of the last write out */
+    protected int myLastOnDiskOffset;
+
+    /** Fetch our location on the disk, as of the last write out */
+    public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
+
+    /**
+     * Update the Record's idea of where on disk it lives, after a write out.
+     * Use with care...
+     */
+    public void setLastOnDiskOffset(int offset) {
+        myLastOnDiskOffset = offset;
+    }
+
+    public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) {
+        return;
+    }
+
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java
new file mode 100755 (executable)
index 0000000..36f9fda
--- /dev/null
@@ -0,0 +1,108 @@
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements.  See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License.  You may obtain a copy of the License at\r
+*\r
+*     http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * A container record that specifies information about external video data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class ExVideoContainer extends RecordContainer {\r
+       private byte[] _header;\r
+\r
+       // Links to our more interesting children\r
+       private ExMediaAtom mediaAtom;\r
+    //the UNC or local path to a video file.\r
+    private CString pathAtom;\r
+\r
+       /**\r
+        * Set things up, and find our more interesting children\r
+        */\r
+       protected ExVideoContainer(byte[] source, int start, int len) {\r
+               // Grab the header\r
+               _header = new byte[8];\r
+               System.arraycopy(source,start,_header,0,8);\r
+\r
+               // Find our children\r
+               _children = Record.findChildRecords(source,start+8,len-8);\r
+               findInterestingChildren();\r
+       }\r
+\r
+       /**\r
+        * Go through our child records, picking out the ones that are\r
+        *  interesting, and saving those for use by the easy helper\r
+        *  methods.\r
+        */     \r
+       private void findInterestingChildren() {\r
+\r
+               // First child should be the ExMediaAtom\r
+               if(_children[0] instanceof ExMediaAtom) {\r
+                       mediaAtom = (ExMediaAtom)_children[0];\r
+               } else {\r
+                       logger.log(POILogger.ERROR, "First child record wasn't a ExMediaAtom, was of type " + _children[0].getRecordType());\r
+               }\r
+        if(_children[1] instanceof CString) {\r
+            pathAtom = (CString)_children[1];\r
+        } else {\r
+            logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType());\r
+        }\r
+       }\r
+\r
+       /**\r
+        * Create a new ExVideoContainer, with blank fields\r
+        */\r
+       public ExVideoContainer() {\r
+        // Setup our header block\r
+               _header = new byte[8];\r
+               _header[0] = 0x0f; // We are a container record\r
+               LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+               \r
+        _children = new Record[2];\r
+               _children[0] = mediaAtom = new ExMediaAtom();\r
+               _children[1] = pathAtom = new CString();\r
+       }\r
+\r
+       /**\r
+        * We are of type 4103\r
+        */\r
+       public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; }\r
+\r
+       /**\r
+        * Write the contents of the record back, so it can be written\r
+        *  to disk\r
+        */\r
+       public void writeOut(OutputStream out) throws IOException {\r
+               writeOut(_header[0],_header[1],getRecordType(),_children,out);\r
+       }\r
+\r
+    /**\r
+     * Returns the ExMediaAtom of this link\r
+     */\r
+    public ExMediaAtom getExMediaAtom() { return mediaAtom; }\r
+\r
+    /**\r
+     * Returns the Path Atom (CString) of this link\r
+     */\r
+    public CString getPathAtom() { return pathAtom; }\r
+\r
+}\r
index 8943f882d8b448c3eec0b04e84d091ed67e612fe..0ee8cd883f5f607b937912f0ecc58a1cfe45a9b4 100644 (file)
@@ -39,27 +39,39 @@ public class InteractiveInfoAtom extends RecordAtom
     /**
      * Action Table
      */
-    public static final int ACTION_NONE = 0;
-    public static final int ACTION_MACRO = 1;
-    public static final int ACTION_RUNPROGRAM = 2;
-    public static final int ACTION_JUMP = 3;
-    public static final int ACTION_HYPERLINK = 4;
-    public static final int ACTION_OLE = 5;
-    public static final int ACTION_MEDIA = 6;
-    public static final int ACTION_CUSTOMSHOW = 7;
+    public static final byte ACTION_NONE = 0;
+    public static final byte ACTION_MACRO = 1;
+    public static final byte ACTION_RUNPROGRAM = 2;
+    public static final byte ACTION_JUMP = 3;
+    public static final byte ACTION_HYPERLINK = 4;
+    public static final byte ACTION_OLE = 5;
+    public static final byte ACTION_MEDIA = 6;
+    public static final byte ACTION_CUSTOMSHOW = 7;
 
     /**
      *  Jump Table
      */
-    public static final int JUMP_NONE = 0;
-    public static final int JUMP_NEXTSLIDE = 1;
-    public static final int JUMP_PREVIOUSSLIDE = 2;
-    public static final int JUMP_FIRSTSLIDE = 3;
-    public static final int JUMP_LASTSLIDE = 4;
-    public static final int JUMP_LASTSLIDEVIEWED = 5;
-    public static final int JUMP_ENDSHOW = 6;
-
+    public static final byte JUMP_NONE = 0;
+    public static final byte JUMP_NEXTSLIDE = 1;
+    public static final byte JUMP_PREVIOUSSLIDE = 2;
+    public static final byte JUMP_FIRSTSLIDE = 3;
+    public static final byte JUMP_LASTSLIDE = 4;
+    public static final byte JUMP_LASTSLIDEVIEWED = 5;
+    public static final byte JUMP_ENDSHOW = 6;
 
+    /**
+     * Types of hyperlinks
+     */
+    public static final byte LINK_NextSlide = 0x00;
+    public static final byte LINK_PreviousSlide = 0x01;
+    public static final byte LINK_FirstSlide = 0x02;
+    public static final byte LINK_LastSlide = 0x03;
+    public static final byte LINK_CustomShow = 0x06;
+    public static final byte LINK_SlideNumber = 0x07;
+    public static final byte LINK_Url = 0x08;
+    public static final byte LINK_OtherPresentation = 0x09;
+    public static final byte LINK_OtherFile = 0x0A;
+    public static final byte LINK_NULL = (byte)0xFF;
 
     /**
      * Record header.
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java
new file mode 100755 (executable)
index 0000000..4a319c2
--- /dev/null
@@ -0,0 +1,110 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.hslf.util.SystemTimeUtils;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * Atom that contains information that describes shape client data.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class OEShapeAtom extends RecordAtom\r
+{\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * record data\r
+     */\r
+    private byte[] _recdata;\r
+\r
+    /**\r
+     * Constructs a brand new link related atom record.\r
+     */\r
+    public OEShapeAtom() {\r
+        _recdata = new byte[4];\r
+\r
+        _header = new byte[8];\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _recdata.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the link related atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected OEShapeAtom(byte[] source, int start, int len) {\r
+        // Get the header\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Grab the record data\r
+        _recdata = new byte[len-8];\r
+        System.arraycopy(source,start+8,_recdata,0,len-8);\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() { return RecordTypes.OEShapeAtom.typeID; }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_recdata);\r
+    }\r
+\r
+    /**\r
+     * shape flags.\r
+     *\r
+     * @return  shape flags.\r
+     */\r
+    public int getOptions(){\r
+        return LittleEndian.getInt(_recdata, 0);\r
+    }\r
+\r
+    /**\r
+     * shape flags.\r
+     *\r
+     * @param id  shape flags.\r
+     */\r
+    public void setOptions(int id){\r
+         LittleEndian.putInt(_recdata, 0, id);\r
+    }\r
+}\r
index 9c867d5ab0fb07bf24297dd25d4c64c029bf5e0c..758d21bdac661127e6faa9ca54e681bc53951cc0 100644 (file)
@@ -77,7 +77,7 @@ public class RecordTypes {
     public static final Type BookmarkSeedAtom = new Type(2025,null);
     public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class);
     public static final Type ExObjRefAtom = new Type(3009,null);
-    public static final Type OEShapeAtom = new Type(3009,null);
+    public static final Type OEShapeAtom = new Type(3009,OEShapeAtom.class);
     public static final Type OEPlaceholderAtom = new Type(3011,OEPlaceholderAtom.class);
     public static final Type GPopublicintAtom = new Type(3024,null);
     public static final Type GRatioAtom = new Type(3031,null);
@@ -128,11 +128,11 @@ public class RecordTypes {
     public static final Type DateTimeMCAtom = new Type(4087,null);
     public static final Type GenericDateMCAtom = new Type(4088,null);
     public static final Type FooterMCAtom = new Type(4090,null);
-    public static final Type ExControlAtom = new Type(4091,null);
-    public static final Type ExMediaAtom = new Type(4100,null);
-    public static final Type ExVideo = new Type(4101,null);
-    public static final Type ExAviMovie = new Type(4102,null);
-    public static final Type ExMCIMovie = new Type(4103,null);
+    public static final Type ExControlAtom = new Type(4091,ExControlAtom.class);
+    public static final Type ExMediaAtom = new Type(4100,ExMediaAtom.class);
+    public static final Type ExVideoContainer = new Type(4101,ExVideoContainer.class);
+    public static final Type ExAviMovie = new Type(4102,ExAviMovie.class);
+    public static final Type ExMCIMovie = new Type(4103,ExMCIMovie.class);
     public static final Type ExMIDIAudio = new Type(4109,null);
     public static final Type ExCDAudio = new Type(4110,null);
     public static final Type ExWAVAudioEmbedded = new Type(4111,null);
@@ -140,7 +140,8 @@ public class RecordTypes {
     public static final Type ExOleObjStg = new Type(4113,ExOleObjStg.class);
     public static final Type ExCDAudioAtom = new Type(4114,null);
     public static final Type ExWAVAudioEmbeddedAtom = new Type(4115,null);
-    public static final Type AnimationInfoAtom = new Type(4116,null);
+    public static final Type AnimationInfo = new Type(4116,AnimationInfo.class);
+    public static final Type AnimationInfoAtom = new Type(4081,AnimationInfoAtom.class);
     public static final Type RTFDateTimeMCAtom = new Type(4117,null);
     public static final Type ProgTags = new Type(5000,DummyPositionSensitiveRecordWithChildren.class);
     public static final Type ProgStringTag = new Type(5001,null);
index 969e9036afffda7aa03f11be0c8b77617e9b9bd8..7b2304074f02dab8a323dc98ff8f6289876432ec 100644 (file)
@@ -367,8 +367,14 @@ public class StyleTextPropAtom extends RecordAtom
 
                rawContents     = baos.toByteArray();
        }
-       
-       /**
+
+    public void setRawContents(byte[] bytes) {
+        rawContents = bytes;
+        reserved = new byte[0];
+        initialised = false;
+    }
+
+    /**
         * Create a new Paragraph TextPropCollection, and add it to the list
         * @param charactersCovered The number of characters this TextPropCollection will cover
         * @return the new TextPropCollection, which will then be in the list
index 71ded85406139db760a6a49b0a310e324bb33b74..0be988d51f5e395c55fe8c684525e7f2e93d05a8 100755 (executable)
@@ -53,7 +53,7 @@ public class TextRulerAtom extends RecordAtom {
     /**\r
      * Constructs a new empty ruler atom.\r
      */\r
-    protected TextRulerAtom() {\r
+    public TextRulerAtom() {\r
         _header = new byte[8];\r
         _data = new byte[0];\r
 \r
@@ -191,4 +191,19 @@ public class TextRulerAtom extends RecordAtom {
     public int[] getBulletOffsets(){\r
         return bulletOffsets;\r
     }\r
+\r
+    public static TextRulerAtom getParagraphInstance(){\r
+        byte[] data = new byte[] {\r
+            0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,\r
+            0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01\r
+        };\r
+        TextRulerAtom ruler = new TextRulerAtom(data, 0, data.length);\r
+        return ruler;\r
+    }\r
+\r
+    public void setParagraphIndent(short tetxOffset, short bulletOffset){\r
+        LittleEndian.putShort(_data, 4, tetxOffset);\r
+        LittleEndian.putShort(_data, 6, bulletOffset);\r
+        LittleEndian.putShort(_data, 8, bulletOffset);\r
+    }\r
 }\r
index 6bc203b658f97a512c9726554544320386cef698..a4238b473806b1c4dc6ca6f6d9f33664c6073f22 100644 (file)
@@ -687,6 +687,7 @@ public class RichTextRun {
      */
     public void setBulletFont(int idx) {
         setParaTextPropVal("bullet.font", idx);
+        setFlag(false, ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
     }
 
     /**
index a546d0444c1d26adb3027647a1dc4ca13a6099c6..f480a30aa5632a053abb9e8e7a2fb0ce2e6a2917 100644 (file)
@@ -24,11 +24,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.List;
+import java.util.*;
 
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherContainerRecord;
@@ -38,21 +34,9 @@ import org.apache.poi.hslf.HSLFSlideShow;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.*;
-import org.apache.poi.hslf.record.Document;
-import org.apache.poi.hslf.record.DocumentAtom;
-import org.apache.poi.hslf.record.FontCollection;
-import org.apache.poi.hslf.record.FontEntityAtom;
-import org.apache.poi.hslf.record.HeadersFootersContainer;
-import org.apache.poi.hslf.record.ParentAwareRecord;
-import org.apache.poi.hslf.record.PersistPtrHolder;
-import org.apache.poi.hslf.record.PositionDependentRecord;
-import org.apache.poi.hslf.record.PositionDependentRecordContainer;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.RecordContainer;
-import org.apache.poi.hslf.record.RecordTypes;
-import org.apache.poi.hslf.record.SlideListWithText;
-import org.apache.poi.hslf.record.SlidePersistAtom;
-import org.apache.poi.hslf.record.UserEditAtom;
+import org.apache.poi.hslf.model.Notes;
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
 import org.apache.poi.util.ArrayUtil;
 import org.apache.poi.util.POILogFactory;
@@ -590,9 +574,8 @@ public final class SlideShow {
         * Create a blank <code>Slide</code>.
         *
         * @return  the created <code>Slide</code>
-        * @throws IOException
         */
-       public Slide createSlide() throws IOException {
+       public Slide createSlide() {
                SlideListWithText slist = null;
 
                // We need to add the records to the SLWT that deals
@@ -660,9 +643,13 @@ public final class SlideShow {
                for (int i = 0; i < _records.length; i++) {
                        Record record = _records[i];
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
-                       record.writeOut(out);
+                       try {
+                record.writeOut(out);
+            } catch (IOException e){
+                throw new HSLFException(e);
+            }
 
-                       // Grab interesting records as they come past
+              // Grab interesting records as they come past
                        if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){
                                ptr = (PersistPtrHolder)_records[i];
                        }
@@ -888,4 +875,72 @@ public final class SlideShow {
         }
     }
 
+    /**
+     * Add a movie in this presentation
+     *
+     * @param path the path or url to the movie
+     * @return 0-based index of the movie
+     */
+    public int addMovie(String path, int type) {
+        ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
+        if(lst == null){
+            lst = new ExObjList();
+            _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
+        }
+
+        ExObjListAtom objAtom = lst.getExObjListAtom();
+        //increment the object ID seed
+        int objectId = (int)objAtom.getObjectIDSeed() + 1;
+        objAtom.setObjectIDSeed(objectId);
+        ExMCIMovie mci;
+        switch (type){
+            case MovieShape.MOVIE_MPEG:
+                mci = new ExMCIMovie();
+                break;
+            case MovieShape.MOVIE_AVI:
+                mci = new ExAviMovie();
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported Movie: " + type);
+        }
+
+        lst.appendChildRecord(mci);
+        ExVideoContainer exVideo = mci.getExVideo();
+        exVideo.getExMediaAtom().setObjectId(objectId);
+        exVideo.getExMediaAtom().setMask(0xE80000);
+        exVideo.getPathAtom().setText(path);
+        return objectId;
+    }
+
+    /**
+     * Add a control in this presentation
+     *
+     * @param name   name of the control, e.g. "Shockwave Flash Object"
+     * @param progId OLE Programmatic Identifier, e.g. "ShockwaveFlash.ShockwaveFlash.9"
+     * @return 0-based index of the control
+     */
+    public int addControl(String name, String progId) {
+        ExObjList lst = _documentRecord.getExObjList();
+        if (lst == null) {
+            lst = new ExObjList();
+            _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
+        }
+        ExObjListAtom objAtom = lst.getExObjListAtom();
+        //increment the object ID seed
+        int objectId = (int) objAtom.getObjectIDSeed() + 1;
+        objAtom.setObjectIDSeed(objectId);
+        ExControl ctrl = new ExControl();
+        ExOleObjAtom oleObj = ctrl.getExOleObjAtom();
+        oleObj.setObjID(objectId);
+        oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);
+        oleObj.setType(ExOleObjAtom.TYPE_CONTROL);
+        oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);
+
+        ctrl.setProgId(progId);
+        ctrl.setMenuName(name);
+        ctrl.setClipboardName(name);
+        lst.addChildAfter(ctrl, objAtom);
+
+        return objectId;
+    }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java
new file mode 100755 (executable)
index 0000000..9e0bfaf
--- /dev/null
@@ -0,0 +1,66 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+/**\r
+ * Test <code>MovieShape</code> object.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestMovieShape extends TestCase {\r
+\r
+    protected String cwd = System.getProperty("HSLF.testdata.path");\r
+\r
+    public void testCreate() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        String path = cwd + "/test-movie.mpg";\r
+        int movieIdx = ppt.addMovie(path, MovieShape.MOVIE_MPEG);\r
+        int thumbnailIdx = ppt.addPicture(new File(cwd, "tomcat.png"), Picture.PNG);\r
+\r
+        MovieShape shape = new MovieShape(movieIdx, thumbnailIdx);\r
+        shape.setAnchor(new Rectangle2D.Float(300,225,120,90));\r
+        slide.addShape(shape);\r
+\r
+        assertEquals(path, shape.getPath());\r
+        assertTrue(shape.isAutoPlay());\r
+        shape.setAutoPlay(false);\r
+        assertFalse(shape.isAutoPlay());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+\r
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));\r
+        slide = ppt.getSlides()[0];\r
+        shape = (MovieShape)slide.getShapes()[0];\r
+        assertEquals(path, shape.getPath());\r
+        assertFalse(shape.isAutoPlay());\r
+\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
new file mode 100755 (executable)
index 0000000..fff6482
--- /dev/null
@@ -0,0 +1,63 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+/**\r
+ * Test <code>Table</code> object.\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTable extends TestCase {\r
+\r
+    /**\r
+     * Test that ShapeFactory works properly and returns <code>Table</code>\r
+     */\r
+    public void testShapeFactory() throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        Table tbl = new Table(2, 5);\r
+        slide.addShape(tbl);\r
+\r
+        assertTrue(slide.getShapes()[0] instanceof Table);\r
+        Table tbl2 = (Table)slide.getShapes()[0];\r
+        assertEquals(tbl.getNumberOfColumns(), tbl2.getNumberOfColumns());\r
+        assertEquals(tbl.getNumberOfRows(), tbl2.getNumberOfRows());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
+\r
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));\r
+        slide = ppt.getSlides()[0];\r
+        assertTrue(slide.getShapes()[0] instanceof Table);\r
+        Table tbl3 = (Table)slide.getShapes()[0];\r
+        assertEquals(tbl.getNumberOfColumns(), tbl3.getNumberOfColumns());\r
+        assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());\r
+    }\r
+\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java
new file mode 100755 (executable)
index 0000000..3b8cf75
--- /dev/null
@@ -0,0 +1,91 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+import org.apache.poi.util.HexDump;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestAnimationInfoAtom extends TestCase {\r
+    // From a real file\r
+    /*\r
+     <AnimationInfoAtom info="1" type="4081" size="28" offset="4015" header="01 00 F1 0F 1C 00 00 00 ">\r
+       00 00 00 07 04 05 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00\r
+       00 00 00\r
+     </AnimationInfoAtom>\r
+     */\r
+    private byte[] data = new byte[] {\r
+         0x01, 0x00, (byte)0xF1, 0x0F, 0x1C, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x07, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\r
+    };\r
+\r
+    public void testRead() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);\r
+        assertEquals(RecordTypes.AnimationInfoAtom.typeID, record.getRecordType());\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Automatic));\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Play));\r
+        assertTrue(record.getFlag(AnimationInfoAtom.Synchronous));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Reverse));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Sound));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.StopSound));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.Hide));\r
+        assertFalse(record.getFlag(AnimationInfoAtom.AnimateBg));\r
+        assertEquals(0x07000000, record.getDimColor());\r
+        assertEquals(0, record.getSoundIdRef());\r
+        assertEquals(0, record.getDelayTime());\r
+        assertEquals(2, record.getOrderID());\r
+        assertEquals(0, record.getSlideCount());\r
+    }\r
+\r
+    public void testWrite() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length);\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        AnimationInfoAtom record = new AnimationInfoAtom();\r
+        record.setDimColor(0x07000000);\r
+        record.setOrderID(2);\r
+        record.setFlag(AnimationInfoAtom.Automatic, true);\r
+        record.setFlag(AnimationInfoAtom.Play, true);\r
+        record.setFlag(AnimationInfoAtom.Synchronous, true);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java
new file mode 100755 (executable)
index 0000000..82255d9
--- /dev/null
@@ -0,0 +1,127 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link org.apache.poi.hslf.record.ExControl} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExControl extends TestCase {\r
+\r
+       // From a real file (embedded SWF control)\r
+    /*\r
+     <ExControl info="15" type="4078" size="218" offset="76" header="0F 00 EE 0F DA 00 00 00 ">\r
+       <ExControlAtom info="0" type="4091" size="4" offset="84" header="00 00 FB 0F 04 00 00 00 ">\r
+         00 01 00 00\r
+       </ExControlAtom>\r
+       <ExOleObjAtom info="1" type="4035" size="24" offset="96" header="01 00 C3 0F 18 00 00 00 ">\r
+         01 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 96 13 00\r
+       </ExOleObjAtom>\r
+       <CString info="16" type="4026" size="44" offset="128" header="10 00 BA 0F 2C 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61\r
+         00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00\r
+       </CString>\r
+       <CString info="32" type="4026" size="62" offset="180" header="20 00 BA 0F 3E 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00 6C 00 61 00 73\r
+         00 68 00 2E 00 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00\r
+         6C 00 61 00 73 00 68 00 2E 00 39 00\r
+       </CString>\r
+       <CString info="48" type="4026" size="44" offset="250" header="30 00 BA 0F 2C 00 00 00 ">\r
+         53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61\r
+         00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00\r
+       </CString>\r
+     </ExControl>\r
+     */\r
+    private byte[] data = new byte[] {\r
+            0x0F, 0x00, (byte)0xEE, 0x0F, (byte)0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xFB, 0x0F, 0x04, 0x00, 0x00, 0x00,\r
+            0x00, 0x01, 0x00, 0x00, 0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,\r
+            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00,\r
+            0x10, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00,\r
+            0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68,\r
+            0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, (byte)0xBA,\r
+            0x0F, 0x3E, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00,\r
+            0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x53, 0x00, 0x68,\r
+            0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00,\r
+            0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x39, 0x00, 0x30, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00,\r
+            0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20,\r
+            0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00,\r
+            0x65, 0x00, 0x63, 0x00, 0x74, 0x00\r
+    };\r
+\r
+    public void testRead() throws Exception {\r
+               ExControl record = new ExControl(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExControl.typeID, record.getRecordType());\r
+\r
+        assertNotNull(record.getExControlAtom());\r
+        assertEquals(256, record.getExControlAtom().getSlideId());\r
+\r
+        ExOleObjAtom oleObj = record.getExOleObjAtom();\r
+        assertNotNull(oleObj);\r
+        assertEquals(oleObj.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        assertEquals(oleObj.getType(), ExOleObjAtom.TYPE_CONTROL);\r
+        assertEquals(oleObj.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);\r
+\r
+        assertEquals("Shockwave Flash Object", record.getMenuName());\r
+        assertEquals("ShockwaveFlash.ShockwaveFlash.9", record.getProgId());\r
+        assertEquals("Shockwave Flash Object", record.getClipboardName());\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExControl record = new ExControl(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExControl record = new ExControl();\r
+        ExControlAtom ctrl = record.getExControlAtom();\r
+        ctrl.setSlideId(256);\r
+\r
+        ExOleObjAtom oleObj = record.getExOleObjAtom();\r
+        oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        oleObj.setType(ExOleObjAtom.TYPE_CONTROL);\r
+        oleObj.setObjID(1);\r
+        oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        oleObj.setObjStgDataRef(2);\r
+        oleObj.setOptions(1283584);\r
+\r
+        record.setMenuName("Shockwave Flash Object");\r
+        record.setProgId("ShockwaveFlash.ShockwaveFlash.9");\r
+        record.setClipboardName("Shockwave Flash Object");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertEquals(data.length, b.length);\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java
new file mode 100755 (executable)
index 0000000..6cda6a1
--- /dev/null
@@ -0,0 +1,94 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link org.apache.poi.hslf.record.HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExMediaAtom extends TestCase {\r
+       // From a real file\r
+    private byte[] data = new byte[] {\r
+            0x00, 0x00, (byte)0x04, 0x10, 0x08, 0x00, 0x00, 00,\r
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\r
+\r
+    public void testRead() throws Exception {\r
+               ExMediaAtom record = new ExMediaAtom(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExMediaAtom.typeID, record.getRecordType());\r
+\r
+        assertEquals(1, record.getObjectId());\r
+        assertFalse(record.getFlag(ExMediaAtom.fLoop));\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+        assertFalse(record.getFlag(ExMediaAtom.fRewind));\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExMediaAtom record = new ExMediaAtom(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExMediaAtom ref = new ExMediaAtom(data, 0, data.length);\r
+        System.out.println(ref.getMask());\r
+\r
+        ExMediaAtom record = new ExMediaAtom();\r
+        record.setObjectId(1);\r
+        record.setFlag(HeadersFootersAtom.fHasDate, false);\r
+        record.setFlag(HeadersFootersAtom.fHasTodayDate, false);\r
+        record.setFlag(HeadersFootersAtom.fHasFooter, false);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testFlags() throws Exception {\r
+        ExMediaAtom record = new ExMediaAtom();\r
+\r
+        //in a new record all the bits are 0\r
+        for(int i = 0; i < 3; i++) assertFalse(record.getFlag(1 << i));\r
+\r
+        record.setFlag(ExMediaAtom.fLoop, true);\r
+        assertTrue(record.getFlag(ExMediaAtom.fLoop));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, true);\r
+        assertTrue(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, false);\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+        record.setFlag(ExMediaAtom.fNarration, false);\r
+        assertFalse(record.getFlag(ExMediaAtom.fNarration));\r
+\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java
new file mode 100755 (executable)
index 0000000..ba539c2
--- /dev/null
@@ -0,0 +1,78 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link ExOleObjAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExOleObjAtom extends TestCase {\r
+       // From a real file (embedded SWF control)\r
+    private byte[] data = new byte[] {\r
+            0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00,\r
+            0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00  };\r
+\r
+    public void testRead() throws Exception {\r
+               ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExOleObjAtom.typeID, record.getRecordType());\r
+        System.out.println(record);\r
+\r
+        assertEquals(record.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        assertEquals(record.getType(), ExOleObjAtom.TYPE_CONTROL);\r
+        assertEquals(record.getObjID(), 1);\r
+        assertEquals(record.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        assertEquals(record.getObjStgDataRef(), 2);\r
+        assertEquals(record.getOptions(), 1283584); //ther meaning is unknown\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExOleObjAtom record = new ExOleObjAtom();\r
+        record.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE);\r
+        record.setType(ExOleObjAtom.TYPE_CONTROL);\r
+        record.setObjID(1);\r
+        record.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT);\r
+        record.setObjStgDataRef(2);\r
+        record.setOptions(1283584);\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java
new file mode 100755 (executable)
index 0000000..4ce08c7
--- /dev/null
@@ -0,0 +1,144 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+import java.util.Arrays;\r
+\r
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;\r
+import org.apache.poi.poifs.filesystem.DirectoryNode;\r
+import org.apache.poi.poifs.filesystem.DocumentEntry;\r
+\r
+/**\r
+ * Tests that {@link ExOleObjStg} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExOleObjStg extends TestCase {\r
+\r
+    // From a real file (embedded SWF control)\r
+    /*\r
+    <ExOleObjStg info="16" type="4113" size="347" offset="4322" header="10 00 11 10 5B 01 00 00 ">\r
+      00 0E 00 00 78 9C BB 70 5E F0 C1 C2 8D 52 0F 19 D0 80 1D 03 33 C3 BF FF 9C\r
+      0C 6C 48 62 8C 40 CC 04 E3 08 30 30 B0 40 C5 FE FD FF FF 1F 24 C4 0C C4 FF\r
+      47 C1 90 02 41 0C F9 40 58 C2 A0 C0 E0 CA 90 07 A4 8B 18 2A D1 93 02 5E 20\r
+      C6 C0 0A 8F 73 50 5A C8 BB 5D 73 29 77 DD 79 C1 69 3B 5C 5C 83 43 50 D5 06\r
+      BC 48 2F 2B 66 38 C9 C8 0E 64 3B 30 42 C4 9C 81 B6 83 EC 4D 05 93 C5 24 D9\r
+      0D 02 42 0C 4C 8C C8 FE 21 56 9F 02 23 C9 56 E1 04 E4 D8 4F 4D 40 89 FD A0\r
+      BC FB 17 4B BA F8 07 C5 A3 60 78 03 7A E6 FF 09 67 59 1B 41 F9 9F 95 61 34\r
+      FF 53 13 50 62 3F 4C 1F AC 1C 18 CD F7 23 0B C0 DA 74 A0 B6 1B A8 3D 37 1A\r
+      F7 23 0B A4 87 A6 85 0A 00 1B 64 6F 38 21 98 03 DA C2 E7 60 90 01 92 69 0C\r
+      39 0C 65 0C 05 40 32 11 58 2F A4 02 6B 07 3D 60 19 5D 0E 14 27 4E 05 1F 90\r
+      0C 67 C8 04 96 ED 29 C0 72 BE 1C C8 E3 06 E3 FF FF 39 18 B8 80 2C 0F A0 5C\r
+      3A 43 06 58 2D A8 A7 E1 C3 10 02 97 87 B8 02 E6 1A 60 77 83 21 18 A8 12 64\r
+      8A 23 D0 B6 1C B8 59 C8 AA 90 F5 F0 62 94 75 DC C0 DE 0A 37 5C 1D 33 54 35\r
+      88 97 08 35 91 83 81 07 EC 27 10 BF 18 E8 9B E1 0F 00 BD 65 3D D4\r
+    </ExOleObjStg>\r
+     */\r
+    private byte[] data = new byte[] {\r
+            0x10, 0x00, 0x11, 0x10, 0x5B, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x78, (byte)0x9C, (byte)0xBB, 0x70,\r
+            0x5E, (byte)0xF0, (byte)0xC1, (byte)0xC2, (byte)0x8D, 0x52, 0x0F, 0x19, (byte)0xD0, (byte)0x80, 0x1D, 0x03,\r
+            0x33, (byte)0xC3, (byte)0xBF, (byte)0xFF, (byte)0x9C, 0x0C, 0x6C, 0x48, 0x62, (byte)0x8C, 0x40, (byte)0xCC,\r
+            0x04, (byte)0xE3, 0x08, 0x30, 0x30, (byte)0xB0, 0x40, (byte)0xC5, (byte)0xFE, (byte)0xFD, (byte)0xFF, (byte)0xFF,\r
+            0x1F, 0x24, (byte)0xC4, (byte)0x0C, (byte)0xC4, (byte)0xFF, 0x47, (byte)0xC1, (byte)0x90, 0x02, 0x41, 0x0C,\r
+            (byte)0xF9, 0x40, 0x58, (byte)0xC2, (byte)0xA0, (byte)0xC0, (byte)0xE0, (byte)0xCA, (byte)0x90, 0x07, (byte)0xA4,\r
+            (byte)0x8B, 0x18, 0x2A, (byte)0xD1, (byte)0x93, 0x02, 0x5E, 0x20, (byte)0xC6, (byte)0xC0, 0x0A, (byte)0x8F,\r
+            0x73, 0x50, 0x5A, (byte)0xC8, (byte)0xBB, 0x5D, 0x73, 0x29, 0x77, (byte)0xDD, 0x79, (byte)0xC1, 0x69, 0x3B,\r
+            0x5C, 0x5C, (byte)0x83, 0x43, 0x50, (byte)0xD5, 0x06, (byte)0xBC, 0x48, 0x2F, 0x2B, 0x66, 0x38, (byte)0xC9,\r
+            (byte)0xC8, 0x0E, 0x64, 0x3B, 0x30, 0x42, (byte)0xC4, (byte)0x9C, (byte)0x81, (byte)0xB6, (byte)0x83, (byte)0xEC,\r
+            0x4D, 0x05, (byte)0x93, (byte)0xC5, 0x24, (byte)0xD9, 0x0D, 0x02, 0x42, 0x0C, 0x4C, (byte)0x8C, (byte)0xC8,\r
+            (byte)0xFE, 0x21, 0x56, (byte)0x9F, 0x02, 0x23, (byte)0xC9, 0x56, (byte)0xE1, 0x04, (byte)0xE4, (byte)0xD8,\r
+            0x4F, 0x4D, 0x40, (byte)0x89, (byte)0xFD, (byte)0xA0, (byte)0xBC, (byte)0xFB, 0x17, 0x4B, (byte)0xBA, (byte)0xF8,\r
+            0x07, (byte)0xC5, (byte)0xA3, 0x60, 0x78, 0x03, 0x7A, (byte)0xE6, (byte)0xFF, 0x09, 0x67, 0x59, 0x1B, 0x41,\r
+            (byte)0xF9, (byte)0x9F, (byte)0x95, 0x61, 0x34, (byte)0xFF, 0x53, 0x13, 0x50, 0x62, 0x3F, 0x4C, 0x1F, (byte)0xAC,\r
+            0x1C, 0x18, (byte)0xCD, (byte)0xF7, 0x23, 0x0B, (byte)0xC0, (byte)0xDA, 0x74, (byte)0xA0, (byte)0xB6, 0x1B,\r
+            (byte)0xA8, 0x3D, 0x37, 0x1A, (byte)0xF7, 0x23, 0x0B, (byte)0xA4, (byte)0x87, (byte)0xA6, (byte)0x85, 0x0A,\r
+            0x00, 0x1B, 0x64, 0x6F, 0x38, 0x21, (byte)0x98, 0x03, (byte)0xDA, (byte)0xC2, (byte)0xE7, 0x60, (byte)0x90,\r
+            0x01, (byte)0x92, 0x69, 0x0C, 0x39, 0x0C, 0x65, 0x0C, 0x05, 0x40, 0x32, 0x11, 0x58, 0x2F, (byte)0xA4, 0x02,\r
+            0x6B, 0x07, 0x3D, 0x60, 0x19, 0x5D, 0x0E, 0x14, 0x27, 0x4E, 0x05, 0x1F, (byte)0x90, 0x0C, 0x67, (byte)0xC8,\r
+            0x04, (byte)0x96, (byte)0xED, 0x29, (byte)0xC0, 0x72, (byte)0xBE, 0x1C, (byte)0xC8, (byte)0xE3, 0x06, (byte)0xE3,\r
+            (byte)0xFF, (byte)0xFF, 0x39, 0x18, (byte)0xB8, (byte)0x80, 0x2C, 0x0F, (byte)0xA0, 0x5C, 0x3A, 0x43, 0x06, 0x58,\r
+            0x2D, (byte)0xA8, (byte)0xA7, (byte)0xE1, (byte)0xC3, 0x10, 0x02, (byte)0x97, (byte)0x87, (byte)0xB8, 0x02,\r
+            (byte)0xE6, 0x1A, 0x60, 0x77, (byte)0x83, 0x21, 0x18, (byte)0xA8, 0x12, 0x64, (byte)0x8A, 0x23, (byte)0xD0,\r
+            (byte)0xB6, 0x1C, (byte)0xB8, 0x59, (byte)0xC8, (byte)0xAA, (byte)0x90, (byte)0xF5, (byte)0xF0, 0x62, (byte)0x94,\r
+            0x75, (byte)0xDC, (byte)0xC0, (byte)0xDE, 0x0A, 0x37, 0x5C, 0x1D, 0x33, 0x54, 0x35, (byte)0x88, (byte)0x97, 0x08,\r
+            0x35, (byte)0x91, (byte)0x83, (byte)0x81, 0x07, (byte)0xEC, 0x27, 0x10, (byte)0xBF, 0x18, (byte)0xE8, (byte)0x9B,\r
+            (byte)0xE1, 0x0F, 0x00, (byte)0xBD, 0x65, 0x3D, (byte)0xD4\r
+                };\r
+\r
+    public void testRead() throws Exception {\r
+        ExOleObjStg record = new ExOleObjStg(data, 0, data.length);\r
+        assertEquals(RecordTypes.ExOleObjStg.typeID, record.getRecordType());\r
+\r
+        int len = record.getDataLength();\r
+        byte[] oledata = readAll(record.getData());\r
+        assertEquals(len, oledata.length);\r
+\r
+        POIFSFileSystem fs = new POIFSFileSystem(record.getData());\r
+        assertTrue("Constructed POIFS from ExOleObjStg data", true);\r
+        DocumentEntry doc = (DocumentEntry)fs.getRoot().getEntry("Contents");\r
+        assertNotNull(doc);\r
+        assertTrue("Fetched the Contents stream containing OLE properties", true);\r
+    }\r
+\r
+    public void testWrite() throws Exception {\r
+        ExOleObjStg record = new ExOleObjStg(data, 0, data.length);\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExOleObjStg src = new ExOleObjStg(data, 0, data.length);\r
+        byte[] oledata = readAll(src.getData());\r
+\r
+        ExOleObjStg tgt = new ExOleObjStg();\r
+        tgt.setData(oledata);\r
+\r
+\r
+        assertEquals(src.getDataLength(), tgt.getDataLength());\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        tgt.writeOut(out);\r
+        byte[] b = out.toByteArray();\r
+\r
+        assertEquals(data.length, b.length);\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+\r
+    private byte[] readAll(InputStream is) throws IOException {\r
+        int pos;\r
+        byte[] chunk = new byte[1024];\r
+        ByteArrayOutputStream out = new  ByteArrayOutputStream();\r
+        while((pos = is.read(chunk)) > 0){\r
+            out.write(chunk, 0, pos);\r
+        }\r
+        return out.toByteArray();\r
+\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java
new file mode 100755 (executable)
index 0000000..224aedf
--- /dev/null
@@ -0,0 +1,90 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+        \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests that {@link HeadersFootersAtom} works properly\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestExVideoContainer extends TestCase {\r
+\r
+    // From a real file\r
+    private byte[] data = new byte[]{\r
+            0x0F, 0x00, 0x05, 0x10, (byte) 0x9E, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, 0x04, 0x10, 0x08, 0x00, 0x00, 0x00,\r
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+            0x00, 0x00, (byte)0xBA, 0x0F, (byte)0x86, 0x00, 0x00, 0x00,\r
+            0x44, 0x00, 0x3A, 0x00, 0x5C, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6A, 0x00, 0x65, 0x00,\r
+            0x63, 0x00, 0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x75, 0x00,\r
+            0x6C, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x47, 0x00, 0x5C, 0x00, 0x6D, 0x00, 0x63, 0x00,\r
+            0x6F, 0x00, 0x6D, 0x00, 0x5F, 0x00, 0x76, 0x00, 0x5F, 0x00, 0x31, 0x00, 0x5F, 0x00, 0x30, 0x00,\r
+            0x5F, 0x00, 0x34, 0x00, 0x5C, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x5C, 0x00,\r
+            0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61, 0x00, 0x5C, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,\r
+            0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00,\r
+            0x73, 0x00, 0x5C, 0x00, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x2E, 0x00,\r
+            0x6D, 0x00, 0x70, 0x00, 0x67, 0x00};\r
+\r
+\r
+\r
+\r
+    public void testRead() throws Exception {\r
+               ExVideoContainer record = new ExVideoContainer(data, 0, data.length);\r
+               assertEquals(RecordTypes.ExVideoContainer.typeID, record.getRecordType());\r
+\r
+        ExMediaAtom exMedia = record.getExMediaAtom();\r
+        assertEquals(1, exMedia.getObjectId());\r
+        assertNotNull(exMedia);\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fLoop));\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fNarration));\r
+        assertFalse(exMedia.getFlag(ExMediaAtom.fRewind));\r
+\r
+        CString path = record.getPathAtom();\r
+        assertNotNull(exMedia);\r
+        assertEquals("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg", path.getText());\r
+    }\r
+\r
+       public void testWrite() throws Exception {\r
+               ExVideoContainer record = new ExVideoContainer(data, 0, data.length);\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               record.writeOut(baos);\r
+               byte[] b = baos.toByteArray();\r
+\r
+               assertTrue(Arrays.equals(data, b));\r
+       }\r
+\r
+    public void testNewRecord() throws Exception {\r
+        ExVideoContainer record = new ExVideoContainer();\r
+        record.getExMediaAtom().setObjectId(1);\r
+        record.getPathAtom().setText("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg");\r
+\r
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+        record.writeOut(baos);\r
+        byte[] b = baos.toByteArray();\r
+\r
+        assertTrue(Arrays.equals(data, b));\r
+    }\r
+}
\ No newline at end of file
index 0b610cb6452cbeca0026f4a1b2321275dbc9bdc6..3579ace5905d7dcf372d242150dce1b70f561580 100755 (executable)
@@ -48,6 +48,10 @@ public class TestTextRulerAtom extends TestCase {
         0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05\r
        };\r
 \r
+    private byte[] data_2 = new byte[] {\r
+        0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,\r
+        0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01\r
+    };\r
 \r
     public void testReadRuler() throws Exception {\r
                TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
@@ -73,4 +77,16 @@ public class TestTextRulerAtom extends TestCase {
         byte[] result = out.toByteArray();\r
         assertTrue(Arrays.equals(result, data_1));\r
        }\r
+\r
+    public void testRead2() throws Exception {\r
+               TextRulerAtom ruler = TextRulerAtom.getParagraphInstance();\r
+        ruler.setParagraphIndent((short)249, (short)321);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ruler.writeOut(out);\r
+\r
+        byte[] result = out.toByteArray();\r
+        assertTrue(Arrays.equals(result, data_2));\r
+\r
+       }\r
+\r
 }\r
index aa08040fcd2f61d67e37fab37d75a4b97e6dea7d..e1193179fe82d74f371a555c4c4e860e50e6e71f 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ
index 215732deb5bd6b252bb3ee76df562a041d861253..c8beb7b78b8bdd6e7f3f3c4469eb5884510ef566 100644 (file)
@@ -29,6 +29,7 @@ import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.Ref3DPtg;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.SheetReferences;
@@ -56,13 +57,13 @@ public final class TestEventWorkbookBuilder extends TestCase {
                }
        } 
        
-       public void testBasics() throws Exception {
+       public void testBasics() {
                assertNotNull(listener.getSSTRecord());
                assertNotNull(listener.getBoundSheetRecords());
                assertNotNull(listener.getExternSheetRecords());
        }
        
-       public void testGetStubWorkbooks() throws Exception {
+       public void testGetStubWorkbooks() {
                assertNotNull(listener.getStubWorkbook());
                assertNotNull(listener.getStubHSSFWorkbook());
                
@@ -70,7 +71,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
                assertNotNull(listener.getStubHSSFWorkbook().getSheetReferences());
        }
        
-       public void testContents() throws Exception {
+       public void testContents() {
                assertEquals(2, listener.getSSTRecord().getNumStrings());
                assertEquals(3, listener.getBoundSheetRecords().length);
                assertEquals(1, listener.getExternSheetRecords().length);
@@ -83,11 +84,12 @@ public final class TestEventWorkbookBuilder extends TestCase {
                assertEquals("S2", ref.getSheetName(2));
        }
        
-       public void testFormulas() throws Exception {
-               FormulaRecord fr;
+       public void testFormulas() {
+               
+               FormulaRecord[] fRecs = mockListen.getFormulaRecords();
                
                // Check our formula records
-               assertEquals(6, mockListen._frecs.size());
+               assertEquals(6, fRecs.length);
                
                Workbook stubWB = listener.getStubWorkbook();
                assertNotNull(stubWB);
@@ -100,47 +102,45 @@ public final class TestEventWorkbookBuilder extends TestCase {
                assertEquals("Sh3", stubWB.getSheetName(2));
                
                // Check we can get the formula without breaking
-               for(int i=0; i<mockListen._frecs.size(); i++) {
-                       fr = (FormulaRecord)mockListen._frecs.get(i);
-                       FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression());
+               for(int i=0; i<fRecs.length; i++) {
+                       FormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
                }
                
                // Peer into just one formula, and check that
                //  all the ptgs give back the right things
-               List ptgs = ((FormulaRecord)mockListen._frecs.get(0)).getParsedExpression();
-               assertEquals(1, ptgs.size());
-               assertTrue(ptgs.get(0) instanceof Ref3DPtg);
+               Ptg[] ptgs = fRecs[0].getParsedExpression();
+               assertEquals(1, ptgs.length);
+               assertTrue(ptgs[0] instanceof Ref3DPtg);
                
-               Ref3DPtg ptg = (Ref3DPtg)ptgs.get(0);
+               Ref3DPtg ptg = (Ref3DPtg)ptgs[0];
                assertEquals("Sheet1!A1", ptg.toFormulaString(stubHSSF));
                
                
                // Now check we get the right formula back for
                //  a few sample ones
+               FormulaRecord fr;
                
                // Sheet 1 A2 is on same sheet
-               fr = (FormulaRecord)mockListen._frecs.get(0);
+               fr = fRecs[0];
                assertEquals(1, fr.getRow());
                assertEquals(0, fr.getColumn());
                assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                // Sheet 1 A5 is to another sheet
-               fr = (FormulaRecord)mockListen._frecs.get(3);
+               fr = fRecs[3];
                assertEquals(4, fr.getRow());
                assertEquals(0, fr.getColumn());
                assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                // Sheet 1 A7 is to another sheet, range
-               fr = (FormulaRecord)mockListen._frecs.get(5);
+               fr = fRecs[5];
                assertEquals(6, fr.getRow());
                assertEquals(0, fr.getColumn());
                assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                
                // Now, load via Usermodel and re-check
-               InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
-               POIFSFileSystem fs = new POIFSFileSystem(is);
-               HSSFWorkbook wb = new HSSFWorkbook(fs);
+               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("3dFormulas.xls");
                assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
                assertEquals("SUM(Sh3!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
        }
@@ -156,5 +156,10 @@ public final class TestEventWorkbookBuilder extends TestCase {
                                _frecs.add(record);
                        }
                }
+               public FormulaRecord[] getFormulaRecords() {
+                       FormulaRecord[] result = new FormulaRecord[_frecs.size()];
+                       _frecs.toArray(result);
+                       return result;
+               }
        }
 }
\ No newline at end of file
index 3fd2b3cf3ead3443c96d5320628b267061316c42..0d96d737cf08cd0f02c5f02a4ec246718177c8ce 100644 (file)
 
 package org.apache.poi.hssf.record;
 
-
 import java.io.ByteArrayInputStream;
-import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.formula.AttrPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.IntPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.RefPtg;
 
 /**
@@ -39,14 +38,12 @@ public final class TestFormulaRecord extends TestCase {
        public void testCreateFormulaRecord () {
                FormulaRecord record = new FormulaRecord();
                record.setColumn((short)0);
-               //record.setRow((short)1);
                record.setRow(1);
                record.setXFIndex((short)4);
                
-               assertEquals(record.getColumn(),(short)0);
-               //assertEquals(record.getRow(),(short)1);
-               assertEquals((short)record.getRow(),(short)1);
-               assertEquals(record.getXFIndex(),(short)4);
+               assertEquals(record.getColumn(),0);
+               assertEquals(record.getRow(), 1);
+               assertEquals(record.getXFIndex(),4);
        }
        
        /**
@@ -103,7 +100,7 @@ public final class TestFormulaRecord extends TestCase {
                assertEquals("Offset 22", 1, output[26]);
        }
        
-       public void testWithConcat()  throws Exception {
+       public void testWithConcat() {
                // =CHOOSE(2,A2,A3,A4)
                byte[] data = {
                                6, 0, 68, 0,
@@ -126,23 +123,19 @@ public final class TestFormulaRecord extends TestCase {
                
                FormulaRecord fr = new FormulaRecord(inp);
                
-               List ptgs = fr.getParsedExpression();
-               assertEquals(9, ptgs.size());
-               assertEquals(IntPtg.class,         ptgs.get(0).getClass());
-               assertEquals(AttrPtg.class,       ptgs.get(1).getClass());
-               assertEquals(RefPtg.class, ptgs.get(2).getClass());
-               assertEquals(AttrPtg.class,       ptgs.get(3).getClass());
-               assertEquals(RefPtg.class, ptgs.get(4).getClass());
-               assertEquals(AttrPtg.class,       ptgs.get(5).getClass());
-               assertEquals(RefPtg.class, ptgs.get(6).getClass());
-               assertEquals(AttrPtg.class,       ptgs.get(7).getClass());
-               assertEquals(FuncVarPtg.class,   ptgs.get(8).getClass());
+               Ptg[] ptgs = fr.getParsedExpression();
+               assertEquals(9, ptgs.length);
+               assertEquals(IntPtg.class,         ptgs[0].getClass());
+               assertEquals(AttrPtg.class,       ptgs[1].getClass());
+               assertEquals(RefPtg.class, ptgs[2].getClass());
+               assertEquals(AttrPtg.class,       ptgs[3].getClass());
+               assertEquals(RefPtg.class, ptgs[4].getClass());
+               assertEquals(AttrPtg.class,       ptgs[5].getClass());
+               assertEquals(RefPtg.class, ptgs[6].getClass());
+               assertEquals(AttrPtg.class,       ptgs[7].getClass());
+               assertEquals(FuncVarPtg.class,   ptgs[8].getClass());
                
-               FuncVarPtg choose = (FuncVarPtg)ptgs.get(8);
+               FuncVarPtg choose = (FuncVarPtg)ptgs[8];
                assertEquals("CHOOSE", choose.getName());
        }
-       
-       public static void main(String [] ignored_args) {
-               junit.textui.TestRunner.run(TestFormulaRecord.class);
-       }
 }
index 88b5477783c9a6e5bc470cc2f754306de63b508c..b7e43ec4d4004c878aad5c4643add1f291e6d31a 100644 (file)
@@ -35,7 +35,8 @@ public final class TestFormulaRecordAggregate extends junit.framework.TestCase {
         FormulaRecord f = new FormulaRecord();
         StringRecord s = new StringRecord();
         s.setString("abc");
-        FormulaRecordAggregate fagg = new FormulaRecordAggregate(f,s);
+        FormulaRecordAggregate fagg = new FormulaRecordAggregate(f);
+        fagg.setStringRecord(s);
         assertEquals("abc", fagg.getStringValue());
     }
 }
index 9b4a44ea142c4e912a2da731cb9d14ae50d29e35..51435d314c9e1198d2e7e3fecae61d7f0923eb89 100755 (executable)
@@ -33,31 +33,35 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  * @author Josh Micich
  */
 final class EvalFactory {
-       private static final NumberEval ZERO = new NumberEval(0);
 
        private EvalFactory() {
                // no instances of this class
        }
 
        /**
-        * Creates a dummy AreaEval (filled with zeros)
-        * <p/>
-        * nCols and nRows could have been derived
+        * Creates a dummy AreaEval 
+        * @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
         */
-       public static AreaEval createAreaEval(String areaRefStr, int nCols, int nRows) {
-               int nValues = nCols * nRows;
-               ValueEval[] values = new ValueEval[nValues];
-               for (int i = 0; i < nValues; i++) {
-                       values[i] = ZERO;
+       public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
+               AreaPtg areaPtg = new AreaPtg(areaRefStr);
+               int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1;
+               int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1;
+               int nExpected = nRows * nCols;
+               if (values.length != nExpected) {
+                       throw new RuntimeException("Expected " + nExpected + " values but got " + values.length);
                }
-               
-               return new Area2DEval(new AreaPtg(areaRefStr), values);
+               for (int i = 0; i < nExpected; i++) {
+                       if (values[i] == null) {
+                               values[i] = NumberEval.ZERO;
+                       }
+               }
+               return new Area2DEval(areaPtg, values);
        }
 
        /**
         * Creates a single RefEval (with value zero)
         */
        public static RefEval createRefEval(String refStr) {
-               return new Ref2DEval(new RefPtg(refStr), ZERO);
+               return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO);
        }
 }
index de1714ce212e3b5ce08fb7e6ea0ac2757da6e89f..eca00872a3ddf772a4f08ae425c575ea8cd4a95d 100755 (executable)
@@ -14,7 +14,6 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record.formula.functions;
 
@@ -33,7 +32,7 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
 import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.functions.Countif.I_MatchPredicate;
+import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
@@ -43,53 +42,49 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 
 /**
  * Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
- * 
+ *
  * @author Josh Micich
  */
 public final class TestCountFuncs extends TestCase {
 
-       public TestCountFuncs(String testName) {
-               super(testName);
-       }
-       
        public void testCountA() {
-               
+
                Eval[] args;
-               
+
                args = new Eval[] {
-                       new NumberEval(0),       
+                       new NumberEval(0),
                };
                confirmCountA(1, args);
-               
+
                args = new Eval[] {
-                       new NumberEval(0),      
+                       new NumberEval(0),
                        new NumberEval(0),
                        new StringEval(""),
                };
                confirmCountA(3, args);
-               
+
                args = new Eval[] {
-                       EvalFactory.createAreaEval("D2:F5", 3, 4),      
+                       EvalFactory.createAreaEval("D2:F5", new ValueEval[12]),
                };
                confirmCountA(12, args);
-               
+
                args = new Eval[] {
-                       EvalFactory.createAreaEval("D1:F5", 3, 5),      // 15
-                       EvalFactory.createRefEval("A1"),        
-                       EvalFactory.createAreaEval("A1:G6", 7, 6),      // 42
+                       EvalFactory.createAreaEval("D1:F5", new ValueEval[15]),
+                       EvalFactory.createRefEval("A1"),
+                       EvalFactory.createAreaEval("A1:G6", new ValueEval[42]),
                        new NumberEval(0),
                };
                confirmCountA(59, args);
        }
 
        public void testCountIf() {
-               
+
                AreaEval range;
                ValueEval[] values;
-               
+
                // when criteria is a boolean value
                values = new ValueEval[] {
-                               new NumberEval(0),      
+                               new NumberEval(0),
                                new StringEval("TRUE"), // note - does not match boolean TRUE
                                BoolEval.TRUE,
                                BoolEval.FALSE,
@@ -98,22 +93,22 @@ public final class TestCountFuncs extends TestCase {
                };
                range = createAreaEval("A1:B3", values);
                confirmCountIf(2, range, BoolEval.TRUE);
-               
+
                // when criteria is numeric
                values = new ValueEval[] {
-                               new NumberEval(0),      
-                               new StringEval("2"),    
-                               new StringEval("2.001"),        
-                               new NumberEval(2),      
-                               new NumberEval(2),      
+                               new NumberEval(0),
+                               new StringEval("2"),
+                               new StringEval("2.001"),
+                               new NumberEval(2),
+                               new NumberEval(2),
                                BoolEval.TRUE,
                };
                range = createAreaEval("A1:B3", values);
                confirmCountIf(3, range, new NumberEval(2));
                // note - same results when criteria is a string that parses as the number with the same value
                confirmCountIf(3, range, new StringEval("2.00"));
-               
-               if (false) { // not supported yet: 
+
+               if (false) { // not supported yet:
                        // when criteria is an expression (starting with a comparison operator)
                        confirmCountIf(4, range, new StringEval(">1"));
                }
@@ -123,7 +118,7 @@ public final class TestCountFuncs extends TestCase {
         */
        public void testCountIfWithCriteriaReference() {
 
-               ValueEval[] values = { 
+               ValueEval[] values = {
                                new NumberEval(22),
                                new NumberEval(25),
                                new NumberEval(21),
@@ -132,14 +127,14 @@ public final class TestCountFuncs extends TestCase {
                                new NumberEval(25),
                };
                Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
-               
+
                Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25));
                Eval[] args=  { arg0, criteriaArg, };
-               
+
                double actual = NumericFunctionInvoker.invoke(new Countif(), args);
                assertEquals(4, actual, 0D);
        }
-       
+
 
        private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
                return new Area2DEval(new AreaPtg(areaRefStr), values);
@@ -150,15 +145,15 @@ public final class TestCountFuncs extends TestCase {
                assertEquals(expected, result, 0);
        }
        private static void confirmCountIf(int expected, AreaEval range, Eval criteria) {
-               
+
                Eval[] args = { range, criteria, };
                double result = NumericFunctionInvoker.invoke(new Countif(), args);
                assertEquals(expected, result, 0);
        }
-       
+
        public void testCountIfEmptyStringCriteria() {
                I_MatchPredicate mp;
-               
+
                // pred '=' matches blank cell but not empty string
                mp = Countif.createCriteriaPredicate(new StringEval("="));
                confirmPredicate(false, mp, "");
@@ -168,21 +163,21 @@ public final class TestCountFuncs extends TestCase {
                mp = Countif.createCriteriaPredicate(new StringEval(""));
                confirmPredicate(true, mp, "");
                confirmPredicate(true, mp, null);
-               
-               // pred '<>' matches empty string but not blank cell 
+
+               // pred '<>' matches empty string but not blank cell
                mp = Countif.createCriteriaPredicate(new StringEval("<>"));
                confirmPredicate(false, mp, null);
                confirmPredicate(true, mp, "");
        }
-       
+
        public void testCountifComparisons() {
                I_MatchPredicate mp;
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval(">5"));
                confirmPredicate(false, mp, 4);
                confirmPredicate(false, mp, 5);
                confirmPredicate(true, mp, 6);
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("<=5"));
                confirmPredicate(true, mp, 4);
                confirmPredicate(true, mp, 5);
@@ -194,7 +189,7 @@ public final class TestCountFuncs extends TestCase {
 
                mp = Countif.createCriteriaPredicate(new StringEval("=abc"));
                confirmPredicate(true, mp, "abc");
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("=42"));
                confirmPredicate(false, mp, 41);
                confirmPredicate(true, mp, 42);
@@ -211,28 +206,28 @@ public final class TestCountFuncs extends TestCase {
                confirmPredicate(true, mp, "500");
                confirmPredicate(true, mp, "4t4");
        }
-       
+
        public void testWildCards() {
                I_MatchPredicate mp;
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("a*b"));
                confirmPredicate(false, mp, "abc");
                confirmPredicate(true, mp, "ab");
                confirmPredicate(true, mp, "axxb");
                confirmPredicate(false, mp, "xab");
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("a?b"));
                confirmPredicate(false, mp, "abc");
                confirmPredicate(false, mp, "ab");
                confirmPredicate(false, mp, "axxb");
                confirmPredicate(false, mp, "xab");
                confirmPredicate(true, mp, "axb");
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("a~?"));
                confirmPredicate(false, mp, "a~a");
                confirmPredicate(false, mp, "a~?");
                confirmPredicate(true, mp, "a?");
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("~*a"));
                confirmPredicate(false, mp, "~aa");
                confirmPredicate(false, mp, "~*a");
@@ -245,40 +240,40 @@ public final class TestCountFuncs extends TestCase {
        }
        public void testNotQuiteWildCards() {
                I_MatchPredicate mp;
-       
-               // make sure special reg-ex chars are treated like normal chars 
+
+               // make sure special reg-ex chars are treated like normal chars
                mp = Countif.createCriteriaPredicate(new StringEval("a.b"));
                confirmPredicate(false, mp, "aab");
                confirmPredicate(true, mp, "a.b");
 
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval("a~b"));
                confirmPredicate(false, mp, "ab");
                confirmPredicate(false, mp, "axb");
                confirmPredicate(false, mp, "a~~b");
                confirmPredicate(true, mp, "a~b");
-               
+
                mp = Countif.createCriteriaPredicate(new StringEval(">a*b"));
                confirmPredicate(false, mp, "a(b");
                confirmPredicate(true, mp, "aab");
                confirmPredicate(false, mp, "a*a");
                confirmPredicate(true, mp, "a*c");
        }
-       
+
        private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) {
                assertEquals(expectedResult, matchPredicate.matches(new NumberEval(value)));
        }
        private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, String value) {
-               Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value); 
+               Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value);
                assertEquals(expectedResult, matchPredicate.matches(ev));
        }
-       
+
        public void testCountifFromSpreadsheet() {
                final String FILE_NAME = "countifExamples.xls";
                final int START_ROW_IX = 1;
                final int COL_IX_ACTUAL = 2;
                final int COL_IX_EXPECTED = 3;
-               
+
                int failureCount = 0;
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME);
                HSSFSheet sheet = wb.getSheetAt(0);
@@ -299,7 +294,7 @@ public final class TestCountFuncs extends TestCase {
                                failureCount++;
                        }
                }
-               
+
                if (failureCount > 0) {
                        throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details");
                }
index 4002c30d0f036b5c5fa4b2d76c12df1f5096e451..f51436fe95b8ec411b085056f58955bfce035583 100755 (executable)
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record.formula.functions;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
  * Tests for ROW(), ROWS(), COLUMN(), COLUMNS()
@@ -29,10 +29,6 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
  */
 public final class TestRowCol extends TestCase {
 
-       public TestRowCol(String testName) {
-               super(testName);
-       }
-       
        public void testCol() {
                Function target = new Column();
                {
@@ -41,7 +37,7 @@ public final class TestRowCol extends TestCase {
                        assertEquals(3, actual, 0D);
                }
                {
-                       Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
+                       Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
                        double actual = NumericFunctionInvoker.invoke(target, args);
                        assertEquals(5, actual, 0D);
                }
@@ -55,7 +51,7 @@ public final class TestRowCol extends TestCase {
                        assertEquals(5, actual, 0D);
                }
                {
-                       Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
+                       Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
                        double actual = NumericFunctionInvoker.invoke(target, args);
                        assertEquals(2, actual, 0D);
                }
@@ -86,7 +82,7 @@ public final class TestRowCol extends TestCase {
        }
        
        private static void confirmRowsFunc(String areaRefStr, int nCols, int nRows) {
-               Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
+               Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
 
                double actual = NumericFunctionInvoker.invoke(new Rows(), args);
                assertEquals(nRows, actual, 0D);
@@ -94,7 +90,7 @@ public final class TestRowCol extends TestCase {
        
 
        private static void confirmColumnsFunc(String areaRefStr, int nCols, int nRows) {
-               Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
+               Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
 
                double actual = NumericFunctionInvoker.invoke(new Columns(), args);
                assertEquals(nCols, actual, 0D);
index ea06c5e1b56794e5fe309b4d28fb218e33be5b3a..2f9a6314be54222bd69195d188dcebc3449424bc 100755 (executable)
@@ -49,7 +49,7 @@ public final class TestSumproduct extends TestCase {
        }
 
        public void testScalarSimple() {
-               
+
                RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3));
                Eval[] args = {
                        refEval, 
@@ -59,19 +59,19 @@ public final class TestSumproduct extends TestCase {
                confirmDouble(6D, result);
        }
 
-
        public void testAreaSimple() {
-               
-               AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
-               AreaEval aeB = EvalFactory.createAreaEval("B1:B3", 1, 3);
-               ValueEval[] aValues = aeA.getValues();
-               ValueEval[] bValues = aeB.getValues();
-               aValues[0] = new NumberEval(2);
-               aValues[1] = new NumberEval(4);
-               aValues[2] = new NumberEval(5);
-               bValues[0] = new NumberEval(3);
-               bValues[1] = new NumberEval(6);
-               bValues[2] = new NumberEval(7);
+               ValueEval[] aValues = {
+                       new NumberEval(2),
+                       new NumberEval(4),
+                       new NumberEval(5),
+               };
+               ValueEval[] bValues = {
+                       new NumberEval(3),
+                       new NumberEval(6),
+                       new NumberEval(7),
+               };
+               AreaEval aeA = EvalFactory.createAreaEval("A1:A3", aValues);
+               AreaEval aeB = EvalFactory.createAreaEval("B1:B3", bValues);
                
                Eval[] args = { aeA, aeB, };
                Eval result = invokeSumproduct(args);
@@ -82,10 +82,9 @@ public final class TestSumproduct extends TestCase {
         * For scalar products, the terms may be 1x1 area refs
         */
        public void testOneByOneArea() {
-               
-               AreaEval ae = EvalFactory.createAreaEval("A1:A1", 1, 1);
-               ae.getValues()[0] = new NumberEval(7);
-               
+
+               AreaEval ae = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(7), });
+
                Eval[] args = {
                                ae, 
                                new NumberEval(2),
@@ -94,27 +93,29 @@ public final class TestSumproduct extends TestCase {
                confirmDouble(14D, result);
        }
 
-       
        public void testMismatchAreaDimensions() {
                
-               AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
-               AreaEval aeB = EvalFactory.createAreaEval("B1:D1", 3, 1);
-               
+               AreaEval aeA = EvalFactory.createAreaEval("A1:A3", new ValueEval[3]);
+               AreaEval aeB = EvalFactory.createAreaEval("B1:D1", new ValueEval[3]);
+
                Eval[] args;
                args = new Eval[] { aeA, aeB, };
                assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
-               
+
                args = new Eval[] { aeA, new NumberEval(5), };
                assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
        }
        
        public void testAreaWithErrorCell() {
-               AreaEval aeA = EvalFactory.createAreaEval("A1:A2", 1, 2);
-               AreaEval aeB = EvalFactory.createAreaEval("B1:B2", 1, 2);
+               ValueEval[] aValues = {
+                       ErrorEval.REF_INVALID,
+                       null,
+               };
+               AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues);
+               AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]);
                aeB.getValues()[1] = ErrorEval.REF_INVALID;
-               
+
                Eval[] args = { aeA, aeB, };
                assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args));
        }
-       
 }
index d657647eae1a8ed3cedea2c4cf798102d70e8f20..891b36ad3de221ee2741eaf4f05e0330a6780e92 100644 (file)
@@ -17,8 +17,6 @@
 
 package org.apache.poi.hssf.usermodel;
 
-import java.util.List;
-
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.Ptg;
@@ -40,10 +38,6 @@ public final class FormulaExtractor {
                        throw new IllegalArgumentException("Not a formula cell");
                }
                FormulaRecordAggregate fra = (FormulaRecordAggregate) vr;
-               List tokens = fra.getFormulaRecord().getParsedExpression();
-               Ptg[] result = new Ptg[tokens.size()];
-               tokens.toArray(result);
-               return result;
+               return fra.getFormulaRecord().getParsedExpression();
        }
-
 }
index 9cdf07cd66f91856d7557cdfe094cc73ee75fe44..c128c773c55ad73a189b3bb1dc3c1d01c4b85eb2 100644 (file)
 package org.apache.poi.hssf.usermodel;
 
 import java.util.Iterator;
-import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 import org.apache.poi.hssf.util.CellReference;
 
@@ -65,15 +65,14 @@ public final class TestBug42464 extends TestCase {
                        }
                        FormulaRecordAggregate record = (FormulaRecordAggregate) cell.getCellValueRecord();
                        FormulaRecord r = record.getFormulaRecord();
-                       List ptgs = r.getParsedExpression();
+                       Ptg[] ptgs = r.getParsedExpression();
                        
                        String cellRef = new CellReference(row.getRowNum(), cell.getCellNum(), false, false).formatAsString();
                        if(false && cellRef.equals("BP24")) { // TODO - replace System.out.println()s with asserts
                                System.out.print(cellRef);
-                               System.out.println(" - has " + r.getNumberOfExpressionTokens() 
-                                       + " ptgs over " + r.getExpressionLength()  + " tokens:");
-                               for(int i=0; i<ptgs.size(); i++) {
-                                       String c = ptgs.get(i).getClass().toString();
+                               System.out.println(" - has " + ptgs.length + " ptgs:");
+                               for(int i=0; i<ptgs.length; i++) {
+                                       String c = ptgs[i].getClass().toString();
                                        System.out.println("\t" + c.substring(c.lastIndexOf('.')+1) );
                                }
                                System.out.println("-> " + cell.getCellFormula());
index 7dbb9c3e16cf8cc7900a5a98f489eed7f3ca06d3..6ebcf96bb66b194fcc5bab96b16a0f8cd13921bf 100644 (file)
@@ -20,7 +20,6 @@ package org.apache.poi.hssf.usermodel;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.Iterator;
-import java.util.List;
 
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
@@ -29,6 +28,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.AreaPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 
 /**
  * 
@@ -183,14 +183,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                HSSFCell cellSUM = rowSUM.getCell(0);
 
                FormulaRecordAggregate frec = (FormulaRecordAggregate) cellSUM.getCellValueRecord();
-               List ops = frec.getFormulaRecord().getParsedExpression();
-               assertEquals(2, ops.size());
-               assertEquals(AreaPtg.class, ops.get(0).getClass());
-               assertEquals(FuncVarPtg.class, ops.get(1).getClass());
+               Ptg[] ops = frec.getFormulaRecord().getParsedExpression();
+               assertEquals(2, ops.length);
+               assertEquals(AreaPtg.class, ops[0].getClass());
+               assertEquals(FuncVarPtg.class, ops[1].getClass());
 
                // Actually stored as C1 to C65536
                // (last row is -1 === 65535)
-               AreaPtg ptg = (AreaPtg) ops.get(0);
+               AreaPtg ptg = (AreaPtg) ops[0];
                assertEquals(2, ptg.getFirstColumn());
                assertEquals(2, ptg.getLastColumn());
                assertEquals(0, ptg.getFirstRow());
index 47d006068a98f6436701204ec5f3f23ce6fc58df..a95ebbc2382651b4e9aaca0d20ccc6125ea98d2c 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
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.util;
 
-import junit.framework.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
-import java.io.*;
+import junit.framework.TestCase;
 
 /**
  * @author Glen Stampoultzis (glens at apache.org)
  * @author Marc Johnson (mjohnson at apache dot org)
  */
+public final class TestHexDump extends TestCase {
 
-public class TestHexDump
-    extends TestCase
-{
-
-    /**
-     * Creates new TestHexDump
-     *
-     * @param name
-     */
-
-    public TestHexDump(String name)
-    {
-        super(name);
-    }
-
-    private char toHex(final int n)
-    {
-        char[] hexChars =
-        {
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
-            'D', 'E', 'F'
-        };
 
-        return hexChars[ n % 16 ];
+    private static char toHex(int n) {
+        return Character.toUpperCase(Character.forDigit(n & 0x0F, 16));
     }
 
-    /**
-     * test dump method
-     *
-     * @exception IOException
-     */
-
-    public void testDump()
-        throws IOException
-    {
+    public void testDump() throws IOException {
         byte[] testArray = new byte[ 256 ];
 
         for (int j = 0; j < 256; j++)
@@ -245,8 +216,7 @@ public class TestHexDump
         // verify proper behavior with negative index
         try
         {
-            HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(),
-                         -1);
+            HexDump.dump(testArray, 0x10000000, new ByteArrayOutputStream(), -1);
             fail("should have caught ArrayIndexOutOfBoundsException on negative index");
         }
         catch (ArrayIndexOutOfBoundsException ignored_exception)
@@ -287,37 +257,33 @@ public class TestHexDump
 
     }
 
-    public void testToHex()
-            throws Exception
-    {
-        assertEquals( "000A", HexDump.toHex((short)0xA));
-        assertEquals( "0A", HexDump.toHex((byte)0xA));
-        assertEquals( "0000000A", HexDump.toHex(0xA));
+    public void testToHex() {
+        assertEquals("000A", HexDump.toHex((short)0xA));
+        assertEquals("0A", HexDump.toHex((byte)0xA));
+        assertEquals("0000000A", HexDump.toHex(0xA));
 
-        assertEquals( "FFFF", HexDump.toHex((short)0xFFFF));
+        assertEquals("FFFF", HexDump.toHex((short)0xFFFF));
+        
+        confirmStr("0xFE", HexDump.byteToHex(-2));
+        confirmStr("0x25", HexDump.byteToHex(37));
+        confirmStr("0xFFFE", HexDump.shortToHex(-2));
+        confirmStr("0x0005", HexDump.shortToHex(5));
+        confirmStr("0xFFFFFF9C", HexDump.intToHex(-100));
+        confirmStr("0x00001001", HexDump.intToHex(4097));
+        confirmStr("0xFFFFFFFFFFFF0006", HexDump.longToHex(-65530));
+        confirmStr("0x0000000000003FCD", HexDump.longToHex(16333));
+    }
 
+    private static void confirmStr(String expected, char[] actualChars) {
+        assertEquals(expected, new String(actualChars));
     }
 
-    private char toAscii(final int c)
-    {
+    private static char toAscii(int c) {
         char rval = '.';
 
-        if ((c >= 32) && (c <= 126))
-        {
+        if (c >= 32 && c <= 126) {
             rval = ( char ) c;
         }
         return rval;
     }
-
-    /**
-     * main method to run the unit tests
-     *
-     * @param ignored_args
-     */
-
-    public static void main(String [] ignored_args)
-    {
-        System.out.println("Testing util.HexDump functionality");
-        junit.textui.TestRunner.run(TestHexDump.class);
-    }
 }