]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638001-638784,638786-639486,639488-639601,639603-639836 via svnmerge...
authorNick Burch <nick@apache.org>
Sat, 29 Mar 2008 17:00:47 +0000 (17:00 +0000)
committerNick Burch <nick@apache.org>
Sat, 29 Mar 2008 17:00:47 +0000 (17:00 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r638803 | nick | 2008-03-19 11:57:38 +0000 (Wed, 19 Mar 2008) | 1 line

  Added test to show that bug #41546 is already fixed. Also rename a test file to be more consistent
........
  r638804 | nick | 2008-03-19 12:01:32 +0000 (Wed, 19 Mar 2008) | 1 line

  Add test to show that bug #43251 is already fixed
........
  r638812 | nick | 2008-03-19 12:28:56 +0000 (Wed, 19 Mar 2008) | 1 line

  Patch from Dmitriy from bug #30311 - Support for conditional formatting records
........
  r638815 | nick | 2008-03-19 12:49:35 +0000 (Wed, 19 Mar 2008) | 1 line

  Fix bug #44627 - improve the thread safety of POILogFactory
........
  r639231 | nick | 2008-03-20 10:06:59 +0000 (Thu, 20 Mar 2008) | 1 line

  Test relating to bug #44636
........
  r639232 | nick | 2008-03-20 10:16:15 +0000 (Thu, 20 Mar 2008) | 1 line

  Simple patch from Josh from bug #44636 - fix for RefVPtg and edit-in-excel oddness
........
  r639242 | nick | 2008-03-20 11:02:39 +0000 (Thu, 20 Mar 2008) | 1 line

  Fix for readCompressedUnicode not moaning about length=0, from bug #44643
........
  r639254 | nick | 2008-03-20 11:43:14 +0000 (Thu, 20 Mar 2008) | 1 line

  Make junit happy
........
  r639836 | nick | 2008-03-21 21:04:47 +0000 (Fri, 21 Mar 2008) | 1 line

  Tweak how you get dataformat strings out of cell styles, to be more logical, and in keeping with how we'll want to do things for xssf too
........

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

37 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/record/CFHeaderRecord.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/CFRuleRecord.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/RecordFactory.java
src/java/org/apache/poi/hssf/record/RecordInputStream.java
src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/cf/CellRange.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/cf/FontFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/cf/PatternFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/RefVPtg.java
src/java/org/apache/poi/hssf/usermodel/HSSFBorderFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java
src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFFontFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFPatternFormatting.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/util/POILogFactory.java
src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/41546.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/43251.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/44593.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/44636.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/44643.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/Bug44593.xls [deleted file]
src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/poifs/storage/TestRawDataBlock.java
src/testcases/org/apache/poi/poifs/storage/TestRawDataBlockList.java

index 4bfd32b0e679b6616dc55d2cf0c82749b1f53315..b1504a7023deb2a4af84ce2a49abbb19903efda8 100644 (file)
@@ -36,6 +36,9 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.1-beta1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44636 - Fix formula parsing of RefVPtg, which was causing #VALUE to be shown on subsequent edits</action>
+           <action dev="POI-DEVELOPERS" type="fix">44627 - Improve the thread safety of POILogFactory</action>
+           <action dev="POI-DEVELOPERS" type="add">30311 - Initial support for Conditional Formatting</action>
            <action dev="POI-DEVELOPERS" type="fix">44609 - Handle leading spaces in formulas, such as '= 4'</action>
            <action dev="POI-DEVELOPERS" type="add">44608 - Support for PercentPtg in the formula evaluator</action>
            <action dev="POI-DEVELOPERS" type="fix">44606 - Support calculated string values for evaluated formulas</action>
index a6467d51647da7f9534bafe47eb4d2366d3f01f7..d29d57ff8d29c60ffd4fe37f0bcc528229407610 100644 (file)
@@ -33,6 +33,9 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.1-beta1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">44636 - Fix formula parsing of RefVPtg, which was causing #VALUE to be shown on subsequent edits</action>
+           <action dev="POI-DEVELOPERS" type="fix">44627 - Improve the thread safety of POILogFactory</action>
+           <action dev="POI-DEVELOPERS" type="add">30311 - Initial support for Conditional Formatting</action>
            <action dev="POI-DEVELOPERS" type="fix">44609 - Handle leading spaces in formulas, such as '= 4'</action>
            <action dev="POI-DEVELOPERS" type="add">44608 - Support for PercentPtg in the formula evaluator</action>
            <action dev="POI-DEVELOPERS" type="fix">44606 - Support calculated string values for evaluated formulas</action>
index f3f7deba07c929d25f39eed6e8b021b1be854331..451cde7579481469a7c846f7065813d0c828fe94 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
+import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.util.PaneInformation;
 
@@ -96,6 +97,7 @@ public class Sheet implements Model
     protected ObjectProtectRecord        objprotect        =     null;
     protected ScenarioProtectRecord      scenprotect       =     null;
     protected PasswordRecord             password          =     null;
+    protected List                       condFormatting    =     new ArrayList();;
 
     /** Add an UncalcedRecord if not true indicating formulas have not been calculated */ 
     protected boolean uncalced = false;
@@ -184,6 +186,17 @@ public class Sheet implements Model
                 retval.merged = ( MergeCellsRecord ) rec;
                 retval.numMergedRegions += retval.merged.getNumAreas();
             }
+            else if ( rec.getSid() == CFHeaderRecord.sid )
+            {
+               CFRecordsAggregate cfAgg = CFRecordsAggregate.createCFAggregate(recs, k);
+               retval.condFormatting.add(cfAgg);
+               rec = cfAgg;
+            }
+            else if ( rec.getSid() == CFRuleRecord.sid )
+            {
+               // Skip it since it is processed by CFRecordsAggregate
+               rec = null;
+            }
             else if (rec.getSid() == ColumnInfoRecord.sid)
             {
                 ColumnInfoRecord col = (ColumnInfoRecord)rec;
@@ -604,6 +617,66 @@ public class Sheet implements Model
     {
         return numMergedRegions;
     }
+    // Find correct position to add new CF record
+    private int findConditionalFormattingPosition()
+    {
+       // This is default. 
+       // If the algorithm does not find the right position,
+       // this one will be used (this is a position before EOF record)
+       int index = records.size()-2;
+       
+       for( int i=index; i>=0; i-- )
+       {
+               Record rec = (Record)records.get(i);
+               short sid = rec.getSid();
+               
+               // CFRecordsAggregate records already exist, just add to the end
+               if (rec instanceof CFRecordsAggregate)  { return i+1; }
+               
+               if( sid == (short)0x00ef )                              { return i+1; }// PHONETICPR
+               if( sid == (short)0x015f )                              { return i+1; }// LABELRANGES
+               if( sid == MergeCellsRecord.sid )               { return i+1; }
+               if( sid == (short)0x0099 )                              { return i+1; }// STANDARDWIDTH
+               if( sid == SelectionRecord.sid )                { return i+1; }
+               if( sid == PaneRecord.sid )                             { return i+1; }
+               if( sid == SCLRecord.sid )                              { return i+1; }
+               if( sid == WindowTwoRecord.sid )                { return i+1; }
+       }
+       
+       return index;
+    }
+
+    public int addConditionalFormatting(CFRecordsAggregate cfAggregate)
+    {
+       int index = findConditionalFormattingPosition();
+       records.add(index, cfAggregate);
+       condFormatting.add(cfAggregate);
+       return condFormatting.size()-1;
+    }
+
+    public void removeConditionalFormatting(int index)
+    {
+        if (index >= 0 && index <= condFormatting.size()-1 )
+        {
+               CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index);
+               records.remove(cfAggregate);
+               condFormatting.remove(index);
+        }
+    }
+    
+    public CFRecordsAggregate getCFRecordsAggregateAt(int index)
+    {
+        if (index >= 0 && index <= condFormatting.size()-1 )
+        {
+               return (CFRecordsAggregate) condFormatting.get(index);
+        }
+        return null;
+    }
+    
+    public int getNumConditionalFormattings()
+    {
+       return condFormatting.size();
+    }
 
     /**
      * Returns the number of low level binary records in this sheet.  This adjusts things for the so called
diff --git a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
new file mode 100644 (file)
index 0000000..00a8646
--- /dev/null
@@ -0,0 +1,223 @@
+/* ====================================================================
+   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.
+==================================================================== */
+
+/*
+ * ConditionalFormattingHeaderRecord.java
+ *
+ * Created on January 17, 2008, 3:05 AM
+ */
+package org.apache.poi.hssf.record;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.hssf.record.cf.CellRange;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Conditional Formatting Header record (CFHEADER)
+ * 
+ * @author Dmitriy Kumshayev
+ */
+public class CFHeaderRecord extends Record
+{
+       public static final short sid = 0x1B0;
+
+       private int field_1_numcf;
+       private int field_2_need_recalculation;
+       private CellRange field_3_enclosing_cell_range;
+       private List field_4_cell_ranges;
+
+       /** Creates new CFHeaderRecord */
+       public CFHeaderRecord()
+       {
+               field_4_cell_ranges = new ArrayList(5);
+       }
+
+       public CFHeaderRecord(RecordInputStream in)
+       {
+               super(in);
+       }
+
+       protected void fillFields(RecordInputStream in)
+       {
+               field_1_numcf = in.readShort();
+               field_2_need_recalculation = in.readShort();
+               field_3_enclosing_cell_range = new CellRange(in.readShort(),in.readShort(),in.readShort(),in.readShort());
+               int numCellRanges = in.readShort();
+               field_4_cell_ranges = new ArrayList(5);
+               for( int i=0; i<numCellRanges; i++)
+               {
+                       field_4_cell_ranges.add(new CellRange(in.readShort(),in.readShort(),in.readShort(),in.readShort()));
+               }
+       }
+       
+       public int getNumberOfConditionalFormats()
+       {
+               return field_1_numcf;
+       }
+       public void setNumberOfConditionalFormats(int n)
+       {
+               field_1_numcf=n;
+       }
+       
+       public boolean getNeedRecalculation()
+       {
+               return field_2_need_recalculation==1?true:false;
+       }
+
+       public void setNeedRecalculation(boolean b)
+       {
+               field_2_need_recalculation=b?1:0;
+       }
+       
+       public CellRange getEnclosingCellRange()
+       {
+               return field_3_enclosing_cell_range;
+       }
+
+       public void setEnclosingCellRange( CellRange cr)
+       {
+               field_3_enclosing_cell_range = cr.cloneCellRange();
+       }
+
+       /**
+        * Set cell ranges list to a single cell range and 
+        * modify the enclosing cell range accordingly.
+        * @param List cellRanges - list of CellRange objects
+        */
+       public void setCellRanges( List cellRanges )
+       {
+               field_4_cell_ranges.clear();
+               if(cellRanges!=null)
+               {
+                       field_3_enclosing_cell_range=null;
+                       for( int i=0; i<cellRanges.size(); i++)
+                       {
+                               field_4_cell_ranges.add(cellRanges.get(i));
+                               recalculateEnclosingRange((CellRange)cellRanges.get(i));
+                       }
+               }
+       }
+
+       private void recalculateEnclosingRange(CellRange cellRange)
+       {
+               field_3_enclosing_cell_range = cellRange.createEnclosingCellRange(field_3_enclosing_cell_range);
+       }
+       
+       public List getCellRanges()
+       {
+               return field_4_cell_ranges;
+       }
+
+       public String toString()
+       {
+               StringBuffer buffer = new StringBuffer();
+
+               buffer.append("[CFHEADER]\n");
+        buffer.append("    .id        = ").append(Integer.toHexString(sid)).append("\n");
+               buffer.append("    .numCF            = ").append(getNumberOfConditionalFormats()).append("\n");
+               buffer.append("    .needRecalc       = ").append(getNeedRecalculation()).append("\n");
+               buffer.append("    .enclosingCellRange= ").append(getEnclosingCellRange()).append("\n");
+               if( field_4_cell_ranges.size()>0)
+               {
+                       buffer.append("    .cfranges=[");
+                       for( int i=0; i<field_4_cell_ranges.size(); i++)
+                       {
+                               buffer.append(i==0?"":",").append(field_4_cell_ranges.get(i));
+                       }
+                       buffer.append("]\n");
+               }
+               buffer.append("[/CFHEADER]\n");
+               return buffer.toString();
+       }
+
+       /**
+        * @return byte array containing instance data
+        */
+
+       public int serialize(int offset, byte[] data)
+       {
+               int recordsize = getRecordSize();
+               
+               LittleEndian.putShort(data, 0 + offset, sid);
+               LittleEndian.putShort(data, 2 + offset, (short) (recordsize-4));
+               LittleEndian.putShort(data, 4 + offset, (short) field_1_numcf);
+               LittleEndian.putShort(data, 6 + offset, (short) field_2_need_recalculation);
+               LittleEndian.putShort(data, 8 + offset, (short) field_3_enclosing_cell_range.getFirstRow());
+               LittleEndian.putShort(data, 10 + offset, (short) field_3_enclosing_cell_range.getLastRow());
+               LittleEndian.putShort(data, 12 + offset, (short) field_3_enclosing_cell_range.getFirstColumn());
+               LittleEndian.putShort(data, 14 + offset, (short) field_3_enclosing_cell_range.getLastColumn());
+               LittleEndian.putShort(data, 16 + offset, (short) field_4_cell_ranges.size());
+               for( int i=0 ; i!=field_4_cell_ranges.size(); i++)
+               {
+                       LittleEndian.putShort(data, 18 + 0 + 8 * i + offset,
+                                       (short) ((CellRange) field_4_cell_ranges.get(i)).getFirstRow());
+                       LittleEndian.putShort(data, 18 + 2 + 8 * i + offset,
+                                       (short) ((CellRange) field_4_cell_ranges.get(i)).getLastRow());
+                       LittleEndian.putShort(data, 18 + 4 + 8 * i + offset,
+                                       (short) ((CellRange) field_4_cell_ranges.get(i)).getFirstColumn());
+                       LittleEndian.putShort(data, 18 + 6 + 8 * i + offset,
+                                       (short) ((CellRange) field_4_cell_ranges.get(i)).getLastColumn());
+               }
+               return getRecordSize();
+       }
+
+       public int getRecordSize()
+       {
+               return 18+8*field_4_cell_ranges.size();
+       }
+
+       /**
+        * called by constructor, should throw runtime exception in the event of a
+        * record passed with a differing ID.
+        *
+        * @param id alleged id for this record
+        */
+
+       protected void validateSid(short id)
+       {
+               if (id != sid)
+               {
+                       throw new RecordFormatException(
+                                       "NOT A ConditionalFormattingHeaderRecord RECORD");
+               }
+       }
+
+       public short getSid()
+       {
+               return sid;
+       }
+
+    public Object clone() 
+    {
+       CFHeaderRecord rec = new CFHeaderRecord();
+       rec.field_1_numcf = field_1_numcf;
+       rec.field_2_need_recalculation = field_2_need_recalculation;
+       rec.field_3_enclosing_cell_range = field_3_enclosing_cell_range;
+        rec.field_4_cell_ranges = new ArrayList(field_4_cell_ranges.size());
+        Iterator iterator = field_4_cell_ranges.iterator();
+        while (iterator.hasNext()) 
+        {
+           CellRange oldRange = (CellRange)iterator.next();
+           rec.field_4_cell_ranges.add(oldRange.cloneCellRange());
+        }
+        return rec;
+    }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/CFRuleRecord.java b/src/java/org/apache/poi/hssf/record/CFRuleRecord.java
new file mode 100644 (file)
index 0000000..0108443
--- /dev/null
@@ -0,0 +1,657 @@
+
+/* ====================================================================
+   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.
+==================================================================== */
+        
+
+/*
+ * ConditionalFormattingRuleRecord.java
+ *
+ * Created on January 23, 2008, 9:56 AM
+ */
+package org.apache.poi.hssf.record;
+
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.poi.hssf.record.cf.BorderFormatting;
+import org.apache.poi.hssf.record.cf.FontFormatting;
+import org.apache.poi.hssf.record.cf.PatternFormatting;
+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.LittleEndian;
+
+/**
+ * Conditional Formatting Rule Record.
+ * @author Dmitriy Kumshayev
+ */
+
+public class CFRuleRecord extends Record
+{
+    
+    public static final short sid = 0x01B1;
+    
+    private byte             field_1_condition_type;
+    public static final byte CONDITION_TYPE_NO_CONDITION_TYPE = 0;
+    public static final byte CONDITION_TYPE_CELL_VALUE_IS = 1;
+    public static final byte CONDITION_TYPE_FORMULA = 2;
+    
+    private byte             field_2_comparison_operator;
+    public static final byte COMPARISON_OPERATOR_NO_COMPARISON = 0;
+    public static final byte COMPARISON_OPERATOR_BETWEEN          = 1;
+    public static final byte COMPARISON_OPERATOR_NOT_BETWEEN   = 2;
+    public static final byte COMPARISON_OPERATOR_EQUAL         = 3;
+    public static final byte COMPARISON_OPERATOR_NOT_EQUAL     = 4;
+    public static final byte COMPARISON_OPERATOR_GT            = 5;
+    public static final byte COMPARISON_OPERATOR_LT            = 6;
+    public static final byte COMPARISON_OPERATOR_GE            = 7;
+    public static final byte COMPARISON_OPERATOR_LE            = 8;
+    
+    private short            field_3_formula1_len;
+    private short            field_4_formula2_len;
+    
+    private int              field_5_options;
+
+    private static final BitField modificationBits = BitFieldFactory.getInstance(0x83FFFFFF); // Bits: font,align,bord,patt,prot
+    private static final BitField alignHor             = BitFieldFactory.getInstance(0x00000001); // 0 = Horizontal alignment modified
+       private static final BitField alignVer          = BitFieldFactory.getInstance(0x00000002); // 0 = Vertical alignment modified
+       private static final BitField alignWrap         = BitFieldFactory.getInstance(0x00000004); // 0 = Text wrapped flag modified
+       private static final BitField alignRot          = BitFieldFactory.getInstance(0x00000008); // 0 = Text rotation modified
+       private static final BitField alignJustLast = BitFieldFactory.getInstance(0x00000010); // 0 = Justify last line flag modified
+       private static final BitField alignIndent       = BitFieldFactory.getInstance(0x00000020); // 0 = Indentation modified
+       private static final BitField alignShrink       = BitFieldFactory.getInstance(0x00000040); // 0 = Shrink to fit flag modified
+       private static final BitField notUsed1          = BitFieldFactory.getInstance(0x00000080); // Always 1
+       private static final BitField protLocked    = BitFieldFactory.getInstance(0x00000100); // 0 = Cell locked flag modified
+       private static final BitField protHidden        = BitFieldFactory.getInstance(0x00000200); // 0 = Cell hidden flag modified
+       private static final BitField bordLeft          = BitFieldFactory.getInstance(0x00000400); // 0 = Left border style and colour modified
+       private static final BitField bordRight         = BitFieldFactory.getInstance(0x00000800); // 0 = Right border style and colour modified
+       private static final BitField bordTop           = BitFieldFactory.getInstance(0x00001000); // 0 = Top border style and colour modified
+       private static final BitField bordBot           = BitFieldFactory.getInstance(0x00002000); // 0 = Bottom border style and colour modified
+       private static final BitField bordTlBr          = BitFieldFactory.getInstance(0x00004000); // 0 = Top-left to bottom-right border flag modified
+       private static final BitField bordBlTr          = BitFieldFactory.getInstance(0x00008000); // 0 = Bottom-left to top-right border flag modified
+       private static final BitField pattStyle         = BitFieldFactory.getInstance(0x00010000); // 0 = Pattern style modified
+       private static final BitField pattCol           = BitFieldFactory.getInstance(0x00020000); // 0 = Pattern colour modified
+       private static final BitField pattBgCol         = BitFieldFactory.getInstance(0x00040000); // 0 = Pattern background colour modified
+       private static final BitField notUsed2          = BitFieldFactory.getInstance(0x00380000); // Always 111
+       private static final BitField undocumented      = BitFieldFactory.getInstance(0x03C00000); // Undocumented bits
+    private static final BitField fmtBlockBits  = BitFieldFactory.getInstance(0x7C000000); // Bits: font,align,bord,patt,prot
+       private static final BitField font                      = BitFieldFactory.getInstance(0x04000000); // 1 = Record contains font formatting block
+       private static final BitField align             = BitFieldFactory.getInstance(0x08000000); // 1 = Record contains alignment formatting block
+       private static final BitField bord              = BitFieldFactory.getInstance(0x10000000); // 1 = Record contains border formatting block
+       private static final BitField patt              = BitFieldFactory.getInstance(0x20000000); // 1 = Record contains pattern formatting block
+       private static final BitField prot              = BitFieldFactory.getInstance(0x40000000); // 1 = Record contains protection formatting block
+       private static final BitField alignTextDir      = BitFieldFactory.getInstance(0x80000000); // 0 = Text direction modified
+    
+    
+    private short            field_6_not_used;
+    
+    private FontFormatting fontFormatting;
+
+    private byte                        field_8_align_text_break;
+    private byte                        field_9_align_text_rotation_angle;
+    private short                       field_10_align_indentation;
+    private short                       field_11_relative_indentation;
+    private short                       field_12_not_used;
+    
+    private BorderFormatting borderFormatting;
+
+    private PatternFormatting patternFormatting;
+    
+    private Stack            field_17_formula1;
+    private Stack            field_18_formula2;
+    
+    /** Creates new CFRuleRecord */
+    public CFRuleRecord()
+    {
+       field_1_condition_type=CONDITION_TYPE_NO_CONDITION_TYPE;
+       field_2_comparison_operator=COMPARISON_OPERATOR_NO_COMPARISON;
+       setExpression1Length((short)0);
+       setExpression2Length((short)0);
+       
+       // Set modification flags to 1: by default options are not modified
+       setOptions(modificationBits.setValue(field_5_options, -1));
+       // Set formatting block flags to 0 (no formatting blocks)
+       setOptions(fmtBlockBits.setValue(field_5_options, 0));
+       
+       field_6_not_used = 0;
+       fontFormatting=null;
+        field_8_align_text_break = 0;
+        field_9_align_text_rotation_angle = 0;
+        field_10_align_indentation = 0;
+        field_11_relative_indentation = 0;
+        field_12_not_used = 0;
+       borderFormatting=null;
+       patternFormatting=null;
+       field_17_formula1=null;
+       field_18_formula2=null;
+    }
+
+    /**
+     * Constructs a Formula record and sets its fields appropriately.
+     * 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 CFRuleRecord(RecordInputStream in)
+    {
+        super(in);
+    }
+
+    protected void fillFields(RecordInputStream in)
+    {
+        try {
+          field_1_condition_type               = in.readByte();
+          field_2_comparison_operator   = in.readByte();
+          field_3_formula1_len                 = in.readShort();
+          field_4_formula2_len                 = in.readShort();
+          field_5_options                              = in.readInt();
+          field_6_not_used              = in.readShort();
+
+          if(containsFontFormattingBlock())
+          {
+               fontFormatting = new FontFormatting(in);
+          }
+
+          if(containsBorderFormattingBlock())
+          {
+                 borderFormatting = new BorderFormatting(in);
+          }
+                  
+          if( containsPatternFormattingBlock())
+          {
+                 patternFormatting =  new PatternFormatting(in);
+          }
+          
+          if(field_3_formula1_len>0)
+          {
+              field_17_formula1 = Ptg.createParsedExpressionTokens(field_3_formula1_len, in);
+              
+              // Now convert any fields as required
+              field_17_formula1 = SharedFormulaRecord.convertSharedFormulas(
+                  field_17_formula1, 0, 0
+              );
+          }
+          if(field_4_formula2_len>0)
+          {
+              field_18_formula2 = Ptg.createParsedExpressionTokens(field_4_formula2_len, in);
+              
+              // Now convert any fields as required
+              field_18_formula2 = SharedFormulaRecord.convertSharedFormulas(
+                  field_18_formula2, 0, 0
+              );
+          }
+        } catch (java.lang.UnsupportedOperationException uoe)  {
+          throw new RecordFormatException(uoe);
+        }
+        
+    }
+    
+    public void setConditionType(byte conditionType)
+    {
+       field_1_condition_type = 
+               conditionType == CONDITION_TYPE_CELL_VALUE_IS ? 
+                       CONDITION_TYPE_CELL_VALUE_IS :
+                               CONDITION_TYPE_FORMULA;
+    }
+
+    public byte getConditionType()
+    {
+       return field_1_condition_type;
+    }
+
+    /**
+     * set the option flags
+     *
+     * @param options  bitmask
+     */
+
+    public void setOptions(int options)
+    {
+        field_5_options = options;
+    }
+
+    public boolean containsFontFormattingBlock()
+    {
+       return getOptionFlag(font);
+    }
+    public void setFontFormatting(FontFormatting fontFormatting)
+    {
+       this.fontFormatting = fontFormatting;
+       setOptionFlag(true,font);
+    }
+    public void setFontFormattingUnchanged()
+    {
+       setOptionFlag(false,font);
+    }
+    
+    public boolean containsAlignFormattingBlock()
+    {
+       return getOptionFlag(align);
+    }
+    public void setAlignFormattingUnchanged()
+    {
+       setOptionFlag(false,align);
+    }
+    
+    public boolean containsBorderFormattingBlock()
+    {
+       return getOptionFlag(bord);
+    }
+    public void setBorderFormatting(BorderFormatting borderFormatting)
+    {
+       this.borderFormatting = borderFormatting;
+       setOptionFlag(true,bord);
+    }
+    public void setBorderFormattingUnchanged()
+    {
+       setOptionFlag(false,bord);
+    }
+    
+    public boolean containsPatternFormattingBlock()
+    {
+       return getOptionFlag(patt);
+    }
+    public void setPatternFormatting(PatternFormatting patternFormatting)
+    {
+       this.patternFormatting = patternFormatting;
+       setOptionFlag(true,patt);
+    }
+    public void setPatternFormattingUnchanged()
+    {
+       setOptionFlag(false,patt);
+    }
+    
+    public boolean containsProtectionFormattingBlock()
+    {
+       return getOptionFlag(prot);
+    }
+    public void setProtectionFormattingUnchanged()
+    {
+       setOptionFlag(false,prot);
+    }
+    
+    public void setComparisonOperation(byte operation)
+    {
+       field_2_comparison_operator = operation;
+    }
+
+    public byte getComparisonOperation()
+    {
+       return field_2_comparison_operator;
+    }
+    
+    /**
+     * set the length (in number of tokens) of the expression 1
+     * @param len  length
+     */
+
+    private void setExpression1Length(short len)
+    {
+        field_3_formula1_len = len;
+    }
+
+    /**
+     * get the length (in number of tokens) of the expression 1
+     * @return  expression length
+     */
+
+    public short getExpression1Length()
+    {
+        return field_3_formula1_len;
+    }
+    
+    /**
+     * set the length (in number of tokens) of the expression 2
+     * @param len  length
+     */
+
+    private void setExpression2Length(short len)
+    {
+       field_4_formula2_len = len;
+    }
+
+    /**
+     * get the length (in number of tokens) of the expression 2
+     * @return  expression length
+     */
+
+    public short getExpression2Length()
+    {
+        return field_4_formula2_len;
+    }
+    /**
+     * get the option flags
+     *
+     * @return bit mask
+     */
+    public int getOptions()
+    {
+        return field_5_options;
+    }    
+
+    private boolean isModified(BitField field)
+       {
+       return !field.isSet(field_5_options);
+       }
+
+       private void setModified(boolean modified, BitField field)
+       {
+               field_5_options = field.setBoolean(field_5_options, !modified);
+       }
+       
+       public boolean isLeftBorderModified()
+       {
+               return isModified(bordLeft);
+       }
+
+       public void setLeftBorderModified(boolean modified)
+       {
+               setModified(modified,bordLeft);
+       }
+    
+       public boolean isRightBorderModified()
+       {
+               return isModified(bordRight);
+       }
+
+       public void setRightBorderModified(boolean modified)
+       {
+               setModified(modified,bordRight);
+       }
+    
+       public boolean isTopBorderModified()
+       {
+               return isModified(bordTop);
+       }
+
+       public void setTopBorderModified(boolean modified)
+       {
+               setModified(modified,bordTop);
+       }
+    
+       public boolean isBottomBorderModified()
+       {
+               return isModified(bordBot);
+       }
+
+       public void setBottomBorderModified(boolean modified)
+       {
+               setModified(modified,bordBot);
+       }
+    
+       public boolean isTopLeftBottomRightBorderModified()
+       {
+               return isModified(bordTlBr);
+       }
+
+       public void setTopLeftBottomRightBorderModified(boolean modified)
+       {
+               setModified(modified,bordTlBr);
+       }
+    
+       public boolean isBottomLeftTopRightBorderModified()
+       {
+               return isModified(bordBlTr);
+       }
+
+       public void setBottomLeftTopRightBorderModified(boolean modified)
+       {
+               setModified(modified,bordBlTr);
+       }
+    
+       public boolean isPatternStyleModified()
+       {
+               return isModified(pattStyle);
+       }
+
+       public void setPatternStyleModified(boolean modified)
+       {
+               setModified(modified,pattStyle);
+       }
+       
+       public boolean isPatternColorModified()
+       {
+               return isModified(pattCol);
+       }
+
+       public void setPatternColorModified(boolean modified)
+       {
+               setModified(modified,pattCol);
+       }
+       
+       public boolean isPatternBackgroundColorModified()
+       {
+               return isModified(pattBgCol);
+       }
+
+       public void setPatternBackgroundColorModified(boolean modified)
+       {
+               setModified(modified,pattBgCol);
+       }
+       
+    private boolean getOptionFlag(BitField field)
+       {
+       return field.isSet(field_5_options);
+       }
+
+       private void setOptionFlag(boolean flag, BitField field)
+       {
+               field_5_options = field.setBoolean(field_5_options, flag);
+       }
+       
+    /**
+     * get the stack of the 1st expression 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 getParsedExpression1()
+    {
+        return field_17_formula1;
+    }
+
+    /**
+     * get the stack of the 2nd expression 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 getParsedExpression2()
+    {
+        return field_18_formula2;
+    }
+    
+    public void setParsedExpression1(Stack ptgs) {
+      setExpression1Length(getTotalPtgSize(field_17_formula1 = ptgs));
+    }
+
+    public void setParsedExpression2(Stack ptgs) {
+        setExpression1Length(getTotalPtgSize(field_18_formula2 = ptgs));
+    }
+    
+    /**
+     * called by constructor, should throw runtime exception in the event of a
+     * record passed with a differing ID.
+     *
+     * @param id alleged id for this record
+     */
+
+    protected void validateSid(short id)
+    {
+        if (id != sid)
+        {
+            throw new RecordFormatException("NOT A CFRULE RECORD");
+        }
+    }
+
+    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.
+     *
+     * @param offset to begin writing at
+     * @param data byte array containing instance data
+     * @return number of bytes written
+     */
+
+    public int serialize(int offset, byte [] data)
+    {
+       int recordsize = getRecordSize();
+        LittleEndian.putShort(data, 0 + offset, sid);
+        LittleEndian.putShort(data, 2 + offset, (short)(recordsize-4));
+        data[4 + offset] = field_1_condition_type;
+        data[5 + offset] = field_2_comparison_operator;
+        LittleEndian.putShort(data, 6 + offset, field_3_formula1_len);
+        LittleEndian.putShort(data, 8 + offset, field_4_formula2_len);
+        LittleEndian.putInt(data,  10 + offset, field_5_options);
+        LittleEndian.putShort(data,14 + offset, field_6_not_used);
+        
+        offset += 16;
+        
+        if( containsFontFormattingBlock() )
+        {
+               byte[] fontFormattingRawRecord  = fontFormatting.getRawRecord();
+               System.arraycopy(fontFormattingRawRecord, 0, data, offset, fontFormattingRawRecord.length);
+               offset += fontFormattingRawRecord.length;
+        }
+        
+        if( containsBorderFormattingBlock())
+        {
+               offset += borderFormatting.serialize(offset, data);
+        }
+        
+        if( containsPatternFormattingBlock() )
+        {
+               offset += patternFormatting.serialize(offset, data);
+        }
+        
+        if (getExpression1Length()>0)
+        {
+            Ptg.serializePtgStack(this.field_17_formula1, data, offset);
+            offset += getExpression1Length();
+        }
+
+        if (getExpression2Length()>0)
+        {
+            Ptg.serializePtgStack(this.field_18_formula2, data, offset);
+            offset += getExpression2Length();
+        }
+        return recordsize;
+    }
+    
+    
+    
+
+    public int getRecordSize()
+    {
+        int retval =16+
+                               (containsFontFormattingBlock()?fontFormatting.getRawRecord().length:0)+
+                               (containsBorderFormattingBlock()?8:0)+
+                               (containsPatternFormattingBlock()?4:0)+
+                               getExpression1Length()+
+                               getExpression2Length()
+                               ;
+        return retval;
+    }
+
+    private short getTotalPtgSize(List list)
+    {
+       short  retval = 0;
+       if( list!=null)
+       {
+            for (int k = 0; k < list.size(); k++)
+            {
+                Ptg ptg = ( Ptg ) list.get(k);
+
+                retval += ptg.getSize();
+            }
+       }
+        return retval;
+    }
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("[CFRULE]\n");
+        if( containsFontFormattingBlock())
+        {
+            buffer.append(fontFormatting.toString());
+        }
+        if( containsBorderFormattingBlock())
+        {
+            buffer.append(borderFormatting.toString());
+        }
+        if( containsPatternFormattingBlock())
+        {
+            buffer.append(patternFormatting.toString());
+        }
+        buffer.append("[/CFRULE]\n");
+        return buffer.toString();
+    }
+    
+    public Object clone() {
+      CFRuleRecord rec = new CFRuleRecord();
+      rec.field_1_condition_type= field_1_condition_type;
+      rec.field_2_comparison_operator = field_2_comparison_operator;
+      rec.field_3_formula1_len = field_3_formula1_len;
+      rec.field_4_formula2_len = field_4_formula2_len;
+      rec.field_5_options = field_5_options;
+      rec.field_6_not_used = field_6_not_used;
+      if( containsFontFormattingBlock())
+      {
+         rec.fontFormatting = (FontFormatting)fontFormatting.clone();
+      }
+      if( containsBorderFormattingBlock())
+      {
+         rec.borderFormatting = (BorderFormatting)borderFormatting.clone();
+      }
+      if( containsPatternFormattingBlock())
+      {
+         rec.patternFormatting = (PatternFormatting)patternFormatting.clone();
+      }
+      if( field_3_formula1_len>0)
+      {
+          rec.field_17_formula1 = (Stack)field_17_formula1.clone();
+      }
+      if( field_4_formula2_len>0)
+      {
+          rec.field_18_formula2 = (Stack)field_18_formula2.clone();
+      }
+      
+      return rec;
+    }
+
+       public FontFormatting getFontFormatting()
+       {
+               return fontFormatting;
+       }
+
+}
index f247a8d4b9b3d24435455183546ff9612cd50837..1dada4450f5fd7aa23381cb0297d10e8c5fa1d10 100644 (file)
@@ -82,6 +82,8 @@ public class RecordFactory
                 SupBookRecord.class,
                 CRNCountRecord.class,
                 CRNRecord.class,
+                CFHeaderRecord.class,
+                CFRuleRecord.class,
             };
     }
     private static Map           recordsMap  = recordsToMap(records);
index 431558ccc8a2fd9cdf1cf831c4f090f3d9300256..ba900b2a28d336b0c30e0d655fcd24f15c6873da 100755 (executable)
@@ -266,6 +266,9 @@ public class RecordInputStream extends InputStream
   }
     
   public String readCompressedUnicode(int length) {
+       if(length == 0) {
+               return "";
+       }
     if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length " + length);
     }
@@ -273,7 +276,7 @@ public class RecordInputStream extends InputStream
     StringBuffer buf = new StringBuffer(length);
     for (int i=0;i<length;i++) {
       if ((remaining() == 0) && (isContinueNext())) {
-        nextRecord();
+          nextRecord();
           int compressByte = readByte();
           if(compressByte != 0) throw new IllegalArgumentException("compressByte in continue records must be 0 while reading compressed unicode");
       }
index b2fd9ae4f55ea1a3349daeb03b143c0b312f9135..a7715474ac4a26de2604d85c2e96cd6fd69522d0 100755 (executable)
@@ -180,19 +180,17 @@ public final class SharedFormulaRecord extends Record {
       return ((getFirstRow() <= formulaRow) && (getLastRow() >= formulaRow) &&
           (getFirstColumn() <= formulaColumn) && (getLastColumn() >= formulaColumn));
     }
-
-    /** 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());
+    
+    /**
+     * Creates a non shared formula from the shared formula 
+     * counter part
+     */
+    protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
         Stack newPtgStack = new Stack();
 
-        if (field_7_parsed_expr != null)
-          for (int k = 0; k < field_7_parsed_expr.size(); k++) {
-            Ptg ptg = (Ptg) field_7_parsed_expr.get(k);
+        if (ptgs != null)
+          for (int k = 0; k < ptgs.size(); k++) {
+            Ptg ptg = (Ptg) ptgs.get(k);
             if (ptg instanceof RefNPtg) {
               RefNPtg refNPtg = (RefNPtg)ptg;
               ptg = new ReferencePtg(fixupRelativeRow(formulaRow,refNPtg.getRow(),refNPtg.isRowRelative()),
@@ -243,7 +241,23 @@ public final class SharedFormulaRecord extends Record {
                                 areaNAPtg.isLastColRelative());
             } 
             newPtgStack.add(ptg);
-          }
+        }
+        return newPtgStack;
+    }
+
+    /** 
+     * 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);
         //Now its not shared!
         formula.setSharedFormula(false);
@@ -252,7 +266,7 @@ public final class SharedFormulaRecord extends Record {
       }
     }
     
-    private int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
+    private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
         if(relative) {
             // mask out upper bits to produce 'wrapping' at column 256 ("IV")
             return (column + currentcolumn) & 0x00FF;
@@ -260,7 +274,7 @@ public final class SharedFormulaRecord extends Record {
         return column;
     }
 
-    private int fixupRelativeRow(int currentrow, int row, boolean relative) {
+    private static int fixupRelativeRow(int currentrow, int row, boolean relative) {
         if(relative) {
             // mask out upper bits to produce 'wrapping' at row 65536
             return (row+currentrow) & 0x00FFFF;
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java
new file mode 100644 (file)
index 0000000..78d1ecb
--- /dev/null
@@ -0,0 +1,224 @@
+/* ====================================================================
+   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.aggregates;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.hssf.record.CFHeaderRecord;
+import org.apache.poi.hssf.record.CFRuleRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+/**
+ * CFRecordsAggregate - aggregates Conditional Formatting records CFHeaderRecord 
+ * and number of up to three CFRuleRecord records together to simplify
+ * access to them.  
+ * 
+ * @author Dmitriy Kumshayev
+ *
+ */
+public class CFRecordsAggregate extends Record
+{
+    public final static short sid = -2008;
+
+    private static POILogger  log = POILogFactory.getLogger(CFRecordsAggregate.class);
+    
+    private CFHeaderRecord header;
+
+    // List of CFRuleRecord objects
+       private List rules;
+       
+    public CFRecordsAggregate()
+    {
+        header = null;
+       rules  = new ArrayList(3);
+    }
+    
+    /**
+     * Create CFRecordsAggregate from a list of CF Records
+     * @param recs - list of {@link Record} objects
+     * @param offset - position of {@link CFHeaderRecord} object in the list of Record objects
+     * @return CFRecordsAggregate object
+     */
+    public static CFRecordsAggregate createCFAggregate(List recs, int offset)
+    {
+       CFRecordsAggregate      cfRecords  = new CFRecordsAggregate();
+        ArrayList              records = new ArrayList(4);
+        
+        int count = 0;
+        Record rec = ( Record ) recs.get(offset++);
+        
+        if (rec.getSid() == CFHeaderRecord.sid)
+        {
+            records.add(rec);
+            cfRecords.header = (CFHeaderRecord)rec;
+            
+            int nRules = cfRecords.header.getNumberOfConditionalFormats();
+            int rulesCount = 0;
+            while(  offset<recs.size() &&
+                       (rec = (Record)recs.get(offset++)).getSid() == CFRuleRecord.sid && 
+                       rec instanceof CFRuleRecord &&
+                       rulesCount++ < nRules
+                )
+            {
+                records.add(rec);
+                cfRecords.rules.add(rec);
+            }
+            
+                       if (nRules != cfRecords.rules.size())
+                       {
+                               if (log.check(POILogger.DEBUG))
+                               {
+                                       log.log(POILogger.DEBUG, "Expected  " + nRules + " Conditional Formats, "
+                                                       + "but found " + cfRecords.rules.size() + " rules");
+                               }
+                               cfRecords.header.setNumberOfConditionalFormats(nRules);
+                       }
+
+        }
+        return cfRecords;
+    }
+    
+    /**
+     * Create a deep clone of the record
+     * @return
+     */
+    public CFRecordsAggregate cloneCFAggregate()
+    {
+       
+      ArrayList records = new ArrayList(this.rules.size()+1);
+      
+      records.add(this.header.clone());
+      
+      for (int i=0; i<this.rules.size();i++) 
+      {
+        Record rec = (Record)((Record)this.rules.get(i)).clone();
+        records.add(rec);
+      }
+      return createCFAggregate(records, 0);
+    }
+    
+    /** You never fill an aggregate */
+       protected void fillFields(RecordInputStream in)
+       {
+       }
+       
+       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.
+     *
+     * @param offset to begin writing at
+     * @param data byte array containing instance data
+     * @return number of bytes written
+     */
+
+       public int serialize(int offset, byte[] data)
+       {
+        int pos = offset;
+               if( header != null && rules.size()>0 )
+               {
+                       header.setNumberOfConditionalFormats(rules.size());
+
+            pos += (( Record ) header).serialize(pos, data);
+
+               for(Iterator itr = rules.iterator(); itr.hasNext();)
+               {
+                   pos += (( Record ) itr.next()).serialize(pos, data);
+               }
+               }
+        return pos - offset;
+       }
+       
+       protected void validateSid(short id)
+       {
+               // do nothing here
+       }
+
+       /**
+        * @return the header
+        */
+       public CFHeaderRecord getHeader()
+       {
+               return header;
+       }
+
+       /**
+        * @return the rules
+        */
+       public List getRules()
+       {
+               return rules;
+       }
+
+       /**
+        *  @return sum of sizes of all aggregated records
+        */
+       public int getRecordSize() 
+       {
+        int size = 0;
+        if( header != null)
+        {
+               size += header.getRecordSize();
+        }
+        if( rules != null)
+        {
+            for(Iterator irecs = rules.iterator(); irecs.hasNext(); )
+            {
+                size += (( Record ) irecs.next()).getRecordSize();
+            }
+        }
+        return size;
+    }
+       
+       /**
+        * String representation of CFRecordsAggregate
+        */
+       public String toString()
+       {
+               StringBuffer buffer = new StringBuffer();
+
+               buffer.append("[CF]\n");
+               if( header != null )
+               {
+                       buffer.append(header.toString());
+               }
+               if( rules != null )
+               {
+                       for(int i=0; i<rules.size(); i++)
+                       {
+                               CFRuleRecord cfRule = (CFRuleRecord)rules.get(i);
+                               if(cfRule!=null)
+                               {
+                                       buffer.append(cfRule.toString());
+                               }
+                       }
+               }
+               buffer.append("[/CF]\n");
+               return buffer.toString();
+       }
+       
+}
diff --git a/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java b/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java
new file mode 100644 (file)
index 0000000..4343760
--- /dev/null
@@ -0,0 +1,560 @@
+
+/* ====================================================================
+   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.
+==================================================================== */
+        
+
+/*
+ * FontFormatting.java
+ *
+ * Created on January 22, 2008, 10:05 PM
+ */
+package org.apache.poi.hssf.record.cf;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Border Formatting Block of the Conditional Formatting Rule Record.
+ * 
+ * @author Dmitriy Kumshayev
+ */
+
+public class BorderFormatting 
+{
+       
+    /**
+     * No border
+     */
+
+    public final static short    BORDER_NONE                = 0x0;
+
+    /**
+     * Thin border
+     */
+
+    public final static short    BORDER_THIN                = 0x1;
+
+    /**
+     * Medium border
+     */
+
+    public final static short    BORDER_MEDIUM              = 0x2;
+
+    /**
+     * dash border
+     */
+
+    public final static short    BORDER_DASHED              = 0x3;
+
+    /**
+     * dot border
+     */
+
+    public final static short    BORDER_HAIR              = 0x4;
+
+    /**
+     * Thick border
+     */
+
+    public final static short    BORDER_THICK               = 0x5;
+
+    /**
+     * double-line border
+     */
+
+    public final static short    BORDER_DOUBLE              = 0x6;
+
+    /**
+     * hair-line border
+     */
+
+    public final static short    BORDER_DOTTED                = 0x7;
+
+    /**
+     * Medium dashed border
+     */
+
+    public final static short    BORDER_MEDIUM_DASHED       = 0x8;
+
+    /**
+     * dash-dot border
+     */
+
+    public final static short    BORDER_DASH_DOT            = 0x9;
+
+    /**
+     * medium dash-dot border
+     */
+
+    public final static short    BORDER_MEDIUM_DASH_DOT     = 0xA;
+
+    /**
+     * dash-dot-dot border
+     */
+
+    public final static short    BORDER_DASH_DOT_DOT        = 0xB;
+
+    /**
+     * medium dash-dot-dot border
+     */
+
+    public final static short    BORDER_MEDIUM_DASH_DOT_DOT = 0xC;
+
+    /**
+     * slanted dash-dot border
+     */
+
+    public final static short    BORDER_SLANTED_DASH_DOT    = 0xD;
+       
+    public BorderFormatting()
+    {
+        field_13_border_styles1        = (short)0;
+        field_14_border_styles2        = (short)0;
+    }
+    
+    /** Creates new FontFormatting */
+    public BorderFormatting(RecordInputStream in)
+       {
+        field_13_border_styles1        = in.readInt();
+        field_14_border_styles2        = in.readInt();
+       }
+    
+    // BORDER FORMATTING BLOCK
+    // For Border Line Style codes see HSSFCellStyle.BORDER_XXXXXX
+    private int                         field_13_border_styles1;
+    private static final BitField  bordLeftLineStyle  = BitFieldFactory.getInstance(0x0000000F);
+    private static final BitField  bordRightLineStyle = BitFieldFactory.getInstance(0x000000F0);
+    private static final BitField  bordTopLineStyle   = BitFieldFactory.getInstance(0x00000F00);
+    private static final BitField  bordBottomLineStyle= BitFieldFactory.getInstance(0x0000F000);
+    private static final BitField  bordLeftLineColor  = BitFieldFactory.getInstance(0x007F0000);
+    private static final BitField  bordRightLineColor = BitFieldFactory.getInstance(0x3F800000);
+    private static final BitField  bordTlBrLineOnOff  = BitFieldFactory.getInstance(0x40000000);
+    private static final BitField  bordBlTrtLineOnOff = BitFieldFactory.getInstance(0x80000000);
+
+    private int                         field_14_border_styles2;
+    private static final BitField  bordTopLineColor   = BitFieldFactory.getInstance(0x0000007F);
+    private static final BitField  bordBottomLineColor= BitFieldFactory.getInstance(0x00003f80);
+    private static final BitField  bordDiagLineColor  = BitFieldFactory.getInstance(0x001FC000);
+    private static final BitField  bordDiagLineStyle  = BitFieldFactory.getInstance(0x01E00000);
+
+    /**
+     * set the type of border to use for the left border of the cell
+     * @param border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public void setBorderLeft(short border)
+    {
+       field_13_border_styles1 = bordLeftLineStyle.setValue(field_13_border_styles1, border);
+    }
+
+    /**
+     * get the type of border to use for the left border of the cell
+     * @return border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public short getBorderLeft()
+    {
+       return (short)bordLeftLineStyle.getValue(field_13_border_styles1);
+    }
+
+    /**
+     * set the type of border to use for the right border of the cell
+     * @param border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public void setBorderRight(short border)
+    {
+       field_13_border_styles1 = bordRightLineStyle.setValue(field_13_border_styles1, border);
+    }
+
+    /**
+     * get the type of border to use for the right border of the cell
+     * @return border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public short getBorderRight()
+    {
+       return (short)bordRightLineStyle.getValue(field_13_border_styles1);
+    }
+
+    /**
+     * set the type of border to use for the top border of the cell
+     * @param border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public void setBorderTop(short border)
+    {
+       field_13_border_styles1 = bordTopLineStyle.setValue(field_13_border_styles1, border);
+    }
+
+    /**
+     * get the type of border to use for the top border of the cell
+     * @return border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public short getBorderTop()
+    {
+       return (short)bordTopLineStyle.getValue(field_13_border_styles1);
+    }
+
+    /**
+     * set the type of border to use for the bottom border of the cell
+     * @param border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public void setBorderBottom(short border)
+    {
+       field_13_border_styles1 = bordBottomLineStyle.setValue(field_13_border_styles1, border);
+    }
+
+    /**
+     * get the type of border to use for the bottom border of the cell
+     * @return border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+    public short getBorderBottom()
+    {
+       return (short)bordBottomLineStyle.getValue(field_13_border_styles1);
+    }
+    
+    /**
+     * set the type of border to use for the diagonal border of the cell
+     * @param border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+
+    public void setBorderDiagonal(short border)
+    {
+       field_14_border_styles2 = bordDiagLineStyle.setValue(field_14_border_styles2, border);
+    }
+
+    /**
+     * get the type of border to use for the diagonal border of the cell
+     * @return border type
+     * @see #BORDER_NONE
+     * @see #BORDER_THIN
+     * @see #BORDER_MEDIUM
+     * @see #BORDER_DASHED
+     * @see #BORDER_DOTTED
+     * @see #BORDER_THICK
+     * @see #BORDER_DOUBLE
+     * @see #BORDER_HAIR
+     * @see #BORDER_MEDIUM_DASHED
+     * @see #BORDER_DASH_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT
+     * @see #BORDER_DASH_DOT_DOT
+     * @see #BORDER_MEDIUM_DASH_DOT_DOT
+     * @see #BORDER_SLANTED_DASH_DOT
+     */
+    public short getBorderDiagonal()
+    {
+       return (short)bordDiagLineStyle.getValue(field_14_border_styles2);
+    }
+
+    /**
+     * set the color to use for the left border
+     * @param color The index of the color definition
+     */
+    public void setLeftBorderColor(short color)
+    {
+       field_13_border_styles1 = bordLeftLineColor.setValue(field_13_border_styles1, color);
+    }
+
+    /**
+     * get the color to use for the left border
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @param color The index of the color definition
+     */
+    public short getLeftBorderColor()
+    {
+       return (short)bordLeftLineColor.getValue(field_13_border_styles1);
+    }
+
+    /**
+     * set the color to use for the right border
+     * @param color The index of the color definition
+     */
+    public void setRightBorderColor(short color)
+    {
+       field_13_border_styles1 = bordRightLineColor.setValue(field_13_border_styles1, color);
+    }
+
+    /**
+     * get the color to use for the right border
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @param color The index of the color definition
+     */
+    public short getRightBorderColor()
+    {
+       return (short)bordRightLineColor.getValue(field_13_border_styles1);
+    }
+
+    /**
+     * set the color to use for the top border
+     * @param color The index of the color definition
+     */
+    public void setTopBorderColor(short color)
+    {
+       field_14_border_styles2 = bordTopLineColor.setValue(field_14_border_styles2, color);
+    }
+
+    /**
+     * get the color to use for the top border
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @param color The index of the color definition
+     */
+    public short getTopBorderColor()
+    {
+       return (short)bordTopLineColor.getValue(field_14_border_styles2);
+    }
+
+    /**
+     * set the color to use for the bottom border
+     * @param color The index of the color definition
+     */
+    public void setBottomBorderColor(short color)
+    {
+       field_14_border_styles2 = bordBottomLineColor.setValue(field_14_border_styles2, color);
+    }
+
+    /**
+     * get the color to use for the bottom border
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @param color The index of the color definition
+     */
+    public short getBottomBorderColor()
+    {
+       return (short)bordBottomLineColor.getValue(field_14_border_styles2);
+    }
+    
+    /**
+     * set the color to use for the diagonal borders
+     * @param color The index of the color definition
+     */
+    public void setDiagonalBorderColor(short color)
+    {
+       field_14_border_styles2 = bordDiagLineColor.setValue(field_14_border_styles2, color);
+    }
+
+    /**
+     * get the color to use for the diagonal border
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @param color The index of the color definition
+     */
+    public short getDiagonalBorderColor()
+    {
+       return (short)bordDiagLineColor.getValue(field_14_border_styles2);
+    }
+
+    /**
+     * Of/off bottom left to top right line
+     *   
+     * @param on - if true - on, otherwise off
+     */
+    public void setForwardDiagonalOn(boolean on)
+    {
+       field_13_border_styles1 = bordBlTrtLineOnOff.setBoolean(field_13_border_styles1, on);
+    }
+
+    /**
+     * Of/off top left to bottom right line
+     *   
+     * @param on - if true - on, otherwise off
+     */
+    public void setBackwardDiagonalOn(boolean on)
+    {
+       field_13_border_styles1 = bordTlBrLineOnOff.setBoolean(field_13_border_styles1, on);
+    }
+    
+    /**
+     * @return true if forward diagonal is on
+     */
+    public boolean isForwardDiagonalOn()
+    {
+       return bordBlTrtLineOnOff.isSet(field_13_border_styles1);
+    }
+
+    /**
+     * @return true if backward diagonal is on
+     */
+    public boolean isBackwardDiagonalOn()
+    {
+       return bordTlBrLineOnOff.isSet(field_13_border_styles1);
+    }
+    
+    
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("    [Border Formatting]\n");
+        buffer.append("          .lftln     = ").append(Integer.toHexString(getBorderLeft())).append("\n");
+        buffer.append("          .rgtln     = ").append(Integer.toHexString(getBorderRight())).append("\n");
+        buffer.append("          .topln     = ").append(Integer.toHexString(getBorderTop())).append("\n");
+        buffer.append("          .btmln     = ").append(Integer.toHexString(getBorderBottom())).append("\n");
+        buffer.append("          .leftborder= ").append(Integer.toHexString(getLeftBorderColor())).append("\n");
+        buffer.append("          .rghtborder= ").append(Integer.toHexString(getRightBorderColor())).append("\n");
+        buffer.append("          .topborder= ").append(Integer.toHexString(getTopBorderColor())).append("\n");
+        buffer.append("          .bottomborder= ").append(Integer.toHexString(getBottomBorderColor())).append("\n");
+        buffer.append("          .fwdiag= ").append(isForwardDiagonalOn()).append("\n");
+        buffer.append("          .bwdiag= ").append(isBackwardDiagonalOn()).append("\n");
+        buffer.append("    [/Border Formatting]\n");
+        return buffer.toString();
+    }
+    
+    public Object clone() 
+    {
+      BorderFormatting rec = new BorderFormatting();
+      rec.field_13_border_styles1 = field_13_border_styles1; 
+      rec.field_14_border_styles2 = field_14_border_styles2; 
+      return rec;
+    }
+    
+    public int serialize(int offset, byte [] data)
+    {
+       LittleEndian.putInt(data, offset, field_13_border_styles1);
+       offset += 4;
+       LittleEndian.putInt(data, offset, field_14_border_styles2);
+       offset += 4;
+       return 8;
+    }
+}
diff --git a/src/java/org/apache/poi/hssf/record/cf/CellRange.java b/src/java/org/apache/poi/hssf/record/cf/CellRange.java
new file mode 100644 (file)
index 0000000..88ec15b
--- /dev/null
@@ -0,0 +1,259 @@
+/* ====================================================================
+   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.cf;
+
+/**
+ * CellRange.java
+ * Created on January 22, 2008, 10:05 PM
+ * 
+ * @author Dmitriy Kumshayev
+ */
+
+public class CellRange
+{
+    private int               field_1_first_row;
+    private int               field_2_last_row;
+    private short             field_3_first_column;
+    private short             field_4_last_column;
+    
+    public CellRange(int firstRow, int lastRow, short firstColumn, short lastColumn)
+    {
+               this.field_1_first_row = firstRow;
+               this.field_2_last_row = lastRow;
+               this.field_3_first_column = firstColumn;
+               this.field_4_last_column = lastColumn;
+               validateRegion();
+       }
+    
+    private void validateRegion()
+    {
+       if( field_1_first_row < 0 || 
+               field_2_last_row < -1 ||
+               field_3_first_column < 0 ||
+               field_4_last_column < -1 ||
+               field_2_last_row>=0 && field_2_last_row<field_1_first_row  || 
+               field_4_last_column>=0 && field_4_last_column<field_3_first_column 
+       )
+       {
+               throw new IllegalArgumentException("Invalid cell region "+toString());
+       }
+    }
+    
+       public int getFirstRow()
+       {
+               return field_1_first_row;
+       }
+       private void setFirstRow(int firstRow)
+       {
+               this.field_1_first_row = firstRow;
+       }
+       public int getLastRow()
+       {
+               return field_2_last_row;
+       }
+       private void setLastRow(int lastRow)
+       {
+               this.field_2_last_row = lastRow;
+       }
+       public short getFirstColumn()
+       {
+               return field_3_first_column;
+       }
+       private void setFirstColumn(short firstColumn)
+       {
+               this.field_3_first_column = firstColumn;
+       }
+       public short getLastColumn()
+       {
+               return field_4_last_column;
+       }
+       private void setLastColumn(short lastColumn)
+       {
+               this.field_4_last_column = lastColumn;
+       }
+       
+       public static final int NO_INTERSECTION = 1;
+       public static final int OVERLAP = 2;
+       public static final int INSIDE = 3;
+       public static final int ENCLOSES = 4;
+       
+       /**
+        * Intersect this range with the specified range.
+        * 
+        * @param another - the specified range
+        * @return code which reflects how the specified range is related to this range.<br/>
+        * Possible return codes are:   
+        *              NO_INTERSECTION - the specified range is outside of this range;<br/> 
+        *              OVERLAP - both ranges partially overlap;<br/>
+        *              INSIDE - the specified range is inside of this one<br/>
+        *              ENCLOSES - the specified range encloses this range<br/>
+        */
+       public int intersect(CellRange another )
+       {
+               int   firstRow = another.getFirstRow();
+               int   lastRow  = another.getLastRow();
+               short firstCol = another.getFirstColumn();
+               short lastCol  = another.getLastColumn();
+               
+               if
+               ( 
+                               gt(this.getFirstRow(),lastRow) || 
+                               lt(this.getLastRow(),firstRow) ||
+                               gt(this.getFirstColumn(),lastCol) || 
+                               lt(this.getLastColumn(),firstCol) 
+               )
+               {
+                       return NO_INTERSECTION;
+               }
+               else if( this.contains(another) )
+               {
+                       return INSIDE;
+               }
+               else if( another.contains(this) )
+               {
+                       return ENCLOSES;
+               }
+               else
+               {
+                       return OVERLAP;
+               }
+                       
+       }
+               
+       /**
+        *  Check if the specified range is located inside of this cell range.
+        *  
+        * @param range
+        * @return true if this cell range contains the argument range inside if it's area
+        */
+   public boolean contains(CellRange range)
+   {
+               int firstRow = range.getFirstRow();
+               int lastRow = range.getLastRow();
+               short firstCol = range.getFirstColumn();
+               short lastCol = range.getLastColumn();
+               return le(this.getFirstRow(), firstRow) && ge(this.getLastRow(), lastRow)
+                               && le(this.getFirstColumn(), firstCol) && ge(this.getLastColumn(), lastCol);
+       }
+   
+       public boolean contains(int row, short column)
+       {
+               return le(this.getFirstRow(), row) && ge(this.getLastRow(), row)
+                               && le(this.getFirstColumn(), column) && ge(this.getLastColumn(), column);
+       }
+       
+   /**
+    * Check if the specified cell range has a shared border with the current range.
+    * 
+    * @return true if the ranges have a shared border.
+    */
+       public boolean hasSharedBorder(CellRange range)
+       {
+               int   firstRow = range.getFirstRow();
+               int   lastRow  = range.getLastRow();
+               short firstCol = range.getFirstColumn();
+               short lastCol  = range.getLastColumn();
+               return 
+                       (this.getFirstRow()>0 && this.getFirstRow() - 1 == lastRow || firstRow>0 &&this.getLastRow() == firstRow -1)&& 
+                       (this.getFirstColumn() == firstCol) && 
+                       (this.getLastColumn() == lastCol)                       ||
+                       (this.getFirstColumn()>0 && this.getFirstColumn() - 1 == lastCol || firstCol>0 && this.getLastColumn() == firstCol -1) && 
+                       (this.getFirstRow() == firstRow) && 
+                       (this.getLastRow() == lastRow)
+               ;
+       }
+       
+    /**
+     * Create an enclosing CellRange for the two cell ranges.
+     * 
+     * @return enclosing CellRange
+     */
+       public CellRange createEnclosingCellRange(CellRange range)
+       {
+               if( range == null)
+               {
+                       return cloneCellRange();
+               }
+               else
+               {
+                       CellRange cellRange = 
+                               new CellRange(
+                                       lt(range.getFirstRow(),getFirstRow())?range.getFirstRow():getFirstRow(),
+                                       gt(range.getLastRow(),getLastRow())?range.getLastRow():getLastRow(),
+                                       lt(range.getFirstColumn(),getFirstColumn())?range.getFirstColumn():getFirstColumn(),
+                                       gt(range.getLastColumn(),getLastColumn())?range.getLastColumn():getLastColumn()
+                               );
+                       return cellRange;
+               }
+       }
+       
+       public CellRange cloneCellRange()
+       {
+               return new CellRange(getFirstRow(),getLastRow(),getFirstColumn(),getLastColumn());
+       }
+       
+       /**
+        * Copy data from antother cell range to this cell range
+        * @param cr - another cell range
+        */
+       public void setCellRange(CellRange cr)
+       {
+               setFirstRow(cr.getFirstRow());
+               setLastRow(cr.getLastRow());
+               setFirstColumn(cr.getFirstColumn());
+               setLastColumn(cr.getLastColumn());
+       }
+
+       /**
+        * @return true if a < b
+        */
+       private static boolean lt(int a, int b)
+       {
+               return a == -1 ? false : (b == -1 ? true : a < b);
+       }
+       
+       /**
+        * @return true if a <= b
+        */
+       private static boolean le(int a, int b)
+       {
+               return a == b || lt(a,b);
+       }
+       
+       /**
+        * @return true if a > b
+        */
+       private static boolean gt(int a, int b)
+       {
+               return lt(b,a);
+       }
+
+       /**
+        * @return true if a >= b
+        */
+       private static boolean ge(int a, int b)
+       {
+               return !lt(a,b);
+       }
+       
+       public String toString()
+       {
+               return "("+this.getFirstRow()+","+this.getLastRow()+","+this.getFirstColumn()+","+this.getLastColumn()+")";
+       }
+    
+}
diff --git a/src/java/org/apache/poi/hssf/record/cf/FontFormatting.java b/src/java/org/apache/poi/hssf/record/cf/FontFormatting.java
new file mode 100644 (file)
index 0000000..1e6abee
--- /dev/null
@@ -0,0 +1,591 @@
+
+/* ====================================================================
+   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.cf;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Font Formatting Block of the Conditional Formatting Rule Record.
+ * 
+ * Created on January 22, 2008, 10:05 PM
+ * 
+ * @author Dmitriy Kumshayev
+ */
+
+public class FontFormatting 
+{
+    private byte [] record;
+    
+    private static final int OFFSET_FONT_NAME = 0;
+    private static final int OFFSET_FONT_HEIGHT = 64;
+    private static final int OFFSET_FONT_OPTIONS = 68;
+    private static final int OFFSET_FONT_WEIGHT = 72;
+    private static final int OFFSET_ESCAPEMENT_TYPE = 74;
+    private static final int OFFSET_UNDERLINE_TYPE = 76;
+    private static final int OFFSET_FONT_COLOR_INDEX = 80;
+    private static final int OFFSET_OPTION_FLAGS = 88;
+    private static final int OFFSET_ESCAPEMENT_TYPE_MODIFIED = 92;
+    private static final int OFFSET_UNDERLINE_TYPE_MODIFIED = 96;
+    private static final int OFFSET_NOT_USED1 = 100;
+    private static final int OFFSET_NOT_USED2 = 104;
+    private static final int OFFSET_FONT_FORMATING_END = 116;
+
+    
+    public final static int  FONT_CELL_HEIGHT_PRESERVED   = 0xFFFFFFFF;
+    
+    // FONT OPTIONS MASKS
+    private static final BitField posture              = BitFieldFactory.getInstance(0x00000002);
+    private static final BitField outline              = BitFieldFactory.getInstance(0x00000008);
+    private static final BitField shadow               = BitFieldFactory.getInstance(0x00000010);
+    private static final BitField condense             = BitFieldFactory.getInstance(0x00000020);
+    private static final BitField cancellation = BitFieldFactory.getInstance(0x00000080);
+    
+    // OPTION FLAGS MASKS
+    
+    private static final BitField styleModified                        = BitFieldFactory.getInstance(0x00000002);
+    private static final BitField outlineModified              = BitFieldFactory.getInstance(0x00000008);
+    private static final BitField shadowModified               = BitFieldFactory.getInstance(0x00000010);
+    private static final BitField condenseModified             = BitFieldFactory.getInstance(0x00000020);
+    private static final BitField cancellationModified = BitFieldFactory.getInstance(0x00000080);
+
+    /**
+     * Escapement type - None
+     */
+    public final static short     SS_NONE             = 0;
+    
+    /**
+     * Escapement type - Superscript
+     */
+    public final static short     SS_SUPER            = 1;
+    
+    /**
+     * Escapement type - Subscript
+     */
+    public final static short     SS_SUB              = 2;
+    
+    /**
+     *  Underline type - None
+     */ 
+    public final static byte      U_NONE              = 0;
+
+    /**
+     *  Underline type - Single
+     */ 
+    public final static byte      U_SINGLE            = 1;
+
+    /**
+     *  Underline type - Double
+     */ 
+    public final static byte      U_DOUBLE            = 2;
+
+    /**
+     *  Underline type - Single Accounting
+     */ 
+    public final static byte      U_SINGLE_ACCOUNTING = 0x21;
+    
+    /**
+     *  Underline type - Double Accounting
+     */ 
+    public final static byte      U_DOUBLE_ACCOUNTING = 0x22;
+    
+    /**
+     * Normal boldness (not bold)
+     */
+
+    protected final static short  FONT_WEIGHT_NORMAL   = 0x190;
+
+    /**
+     * Bold boldness (bold)
+     */
+
+    protected final static short  FONT_WEIGHT_BOLD     = 0x2bc;
+    
+    public FontFormatting()
+    {
+       this(new byte[118]);
+       
+       this.setFontHeight((short)-1);
+       this.setItalic(false);
+       this.setBold(false);
+       this.setOutline(false);
+       this.setShadow(false);
+       this.setCondense(false);
+       this.setStrikeout(false);
+       this.setEscapementType((short)0);
+       this.setUnderlineType((byte)0);
+       this.setFontColorIndex((short)-1);
+
+       this.setFontStyleModified(false);
+       this.setFontOutlineModified(false);
+       this.setFontShadowModified(false);
+       this.setFontCondenseModified(false);
+       this.setFontCancellationModified(false);
+       
+       this.setEscapementTypeModified(false);
+       this.setUnderlineTypeModified(false);
+       
+       LittleEndian.putShort(record, OFFSET_FONT_NAME, (short)0);
+       LittleEndian.putInt(record, OFFSET_NOT_USED1, 0x00000001);
+       LittleEndian.putInt(record, OFFSET_NOT_USED2, 0x00000001);
+       LittleEndian.putShort(record, OFFSET_FONT_FORMATING_END, (short)0x0001);
+    }
+    
+    /** Creates new FontFormatting */
+    private FontFormatting(byte[] record)
+    {
+       this.record = record;
+    }
+
+    /** Creates new FontFormatting */
+    public FontFormatting(RecordInputStream in)
+       {
+       record = new byte[118];
+               for (int i = 0; i != record.length; i++)
+               {
+                       record[i] = in.readByte();
+               }
+       }
+    
+    public byte[] getRawRecord()
+    {
+       return record;
+    }
+    
+    /**
+     * sets the height of the font in 1/20th point units
+     *  
+     *
+     * @param height  fontheight (in points/20); or -1 to preserve the cell font height
+     */
+    
+    public void setFontHeight(short height)
+    {
+       LittleEndian.putInt(record, OFFSET_FONT_HEIGHT, height);
+    }
+    
+    /**
+     * gets the height of the font in 1/20th point units
+     *
+     * @return fontheight (in points/20); or -1 if not modified
+     */
+    public short getFontHeight()
+    {
+       return (short) LittleEndian.getInt(record, OFFSET_FONT_HEIGHT);
+    }
+
+       private void setFontOption(boolean option, BitField field)
+       {
+               int options = LittleEndian.getInt(record,OFFSET_FONT_OPTIONS);
+       options = field.setBoolean(options, option);
+       LittleEndian.putInt(record,OFFSET_FONT_OPTIONS, options);
+       }
+       
+       private boolean getFontOption(BitField field)
+       {
+               int options = LittleEndian.getInt(record,OFFSET_FONT_OPTIONS);
+               return field.isSet(options);
+       }
+       
+    /**
+     * set the font to be italics or not
+     *
+     * @param italics - whether the font is italics or not
+     * @see #setAttributes(short)
+     */
+
+    public void setItalic(boolean italic)
+    {
+       setFontOption(italic, posture);
+    }
+    
+    /**
+     * get whether the font is to be italics or not
+     *
+     * @return italics - whether the font is italics or not
+     * @see #getAttributes()
+     */
+    
+    public boolean isItalic()
+    {
+               return getFontOption(posture);
+    }
+
+    public void setOutline(boolean on)
+    {
+       setFontOption(on, outline);
+    }
+
+    public boolean isOutlineOn()
+    {
+       return getFontOption(outline);
+    }
+
+    public void setShadow(boolean on)
+    {
+       setFontOption(on, shadow);
+    }
+
+    public boolean isShadowOn()
+    {
+       return getFontOption(shadow);
+    }
+
+    public void setCondense(boolean on)
+    {
+       setFontOption(on, condense);
+    }
+
+    public boolean isCondenseOn()
+    {
+       return getFontOption(condense);
+    }
+
+    /**
+     * set the font to be stricken out or not
+     *
+     * @param strike - whether the font is stricken out or not
+     */
+
+    public void setStrikeout(boolean strike)
+    {
+       setFontOption(strike, cancellation);
+    }
+
+    /**
+     * get whether the font is to be stricken out or not
+     *
+     * @return strike - whether the font is stricken out or not
+     * @see #getAttributes()
+     */
+
+    public boolean isStruckout()
+    {
+       return getFontOption(cancellation);
+    }
+    
+    /**
+     * set the font weight (100-1000dec or 0x64-0x3e8).  Default is
+     * 0x190 for normal and 0x2bc for bold
+     *
+     * @param bw - a number between 100-1000 for the fonts "boldness"
+     */
+
+    private void setFontWeight(short bw)
+    {
+       if( bw<100) { bw=100; }
+       if( bw>1000){ bw=1000; }
+       LittleEndian.putShort(record,OFFSET_FONT_WEIGHT, bw);
+    }
+    
+    /**
+     * set the font weight to bold (weight=700) or to normal(weight=400) boldness.
+     *
+     * @param bold - set font weight to bold if true; to normal otherwise  
+     */
+    public void setBold(boolean bold)
+    {
+       setFontWeight(bold?FONT_WEIGHT_BOLD:FONT_WEIGHT_NORMAL);
+    }
+    
+    /**
+     * get the font weight for this font (100-1000dec or 0x64-0x3e8).  Default is
+     * 0x190 for normal and 0x2bc for bold
+     *
+     * @return bw - a number between 100-1000 for the fonts "boldness"
+     */
+
+    public short getFontWeight()
+    {
+               return LittleEndian.getShort(record,OFFSET_FONT_WEIGHT);
+    }
+    
+    /**
+     * get whether the font weight is set to bold or not
+     *
+     * @return bold - whether the font is bold or not
+     */
+
+    public boolean isBold()
+    {
+       return getFontWeight()==FONT_WEIGHT_BOLD;
+    }
+    
+    /**
+     * get the type of super or subscript for the font
+     *
+     * @return super or subscript option
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_NONE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_SUPER
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_SUB
+     */
+    public short getEscapementType()
+    {
+               return LittleEndian.getShort(record,OFFSET_ESCAPEMENT_TYPE);
+    }
+
+    /**
+     * set the escapement type for the font
+     *
+     * @param escapementType  super or subscript option
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_NONE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_SUPER
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#SS_SUB
+     */
+    public void setEscapementType( short escapementType)
+    {
+       LittleEndian.putShort(record,OFFSET_ESCAPEMENT_TYPE, escapementType);
+    }
+
+    /**
+     * get the type of underlining for the font
+     *
+     * @return font underlining type
+     *
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_NONE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_SINGLE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_DOUBLE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_SINGLE_ACCOUNTING
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_DOUBLE_ACCOUNTING
+     */
+
+    public short getUnderlineType()
+    {
+               return LittleEndian.getShort(record,OFFSET_UNDERLINE_TYPE);
+    }
+    
+    /**
+     * set the type of underlining type for the font
+     *
+     * @param u  super or subscript option
+     *
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_NONE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_SINGLE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_DOUBLE
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_SINGLE_ACCOUNTING
+     * @see org.apache.poi.hssf.usermodel.HSSFFontFormatting#U_DOUBLE_ACCOUNTING
+     */
+    public void setUnderlineType( short underlineType)
+    {
+       LittleEndian.putShort(record,OFFSET_UNDERLINE_TYPE, underlineType);
+    }
+    
+    
+    public short getFontColorIndex()
+    {
+               return (short)LittleEndian.getInt(record,OFFSET_FONT_COLOR_INDEX);
+    }
+
+    public void setFontColorIndex(short fci )
+    {
+               LittleEndian.putInt(record,OFFSET_FONT_COLOR_INDEX,fci);
+    }
+
+    private boolean getOptionFlag(BitField field)
+       {
+               int optionFlags = LittleEndian.getInt(record,OFFSET_OPTION_FLAGS);
+       int value = field.getValue(optionFlags);
+       return value==0? true : false ;
+       }
+
+       private void setOptionFlag(boolean modified, BitField field)
+       {
+               int value = modified? 0 : 1;
+       int optionFlags = LittleEndian.getInt(record,OFFSET_OPTION_FLAGS);
+       optionFlags = field.setValue(optionFlags, value);
+       LittleEndian.putInt(record,OFFSET_OPTION_FLAGS, optionFlags);
+       }
+
+    
+    public boolean isFontStyleModified()
+    {
+       return getOptionFlag(styleModified);
+    }
+
+    public void setFontStyleModified(boolean modified)
+    {
+       setOptionFlag(modified, styleModified);
+    }
+
+    public boolean isFontOutlineModified()
+    {
+       return getOptionFlag(outlineModified);
+    }
+    
+    public void setFontOutlineModified(boolean modified)
+    {
+       setOptionFlag(modified, outlineModified);
+    }
+
+    public boolean isFontShadowModified()
+    {
+       return getOptionFlag(shadowModified);
+    }
+    
+    public void setFontShadowModified(boolean modified)
+    {
+       setOptionFlag(modified, shadowModified);
+    }
+    public boolean isFontCondenseModified()
+    {
+       return getOptionFlag(condenseModified);
+    }
+    
+    public void setFontCondenseModified(boolean modified)
+    {
+       setOptionFlag(modified, condenseModified);
+    }
+    
+    public void setFontCancellationModified(boolean modified)
+    {
+       setOptionFlag(modified, cancellationModified);
+    }
+    
+    public boolean isFontCancellationModified()
+    {
+       return getOptionFlag(cancellationModified);
+    }
+    
+    public void setEscapementTypeModified(boolean modified)
+    {
+       int value = modified? 0 : 1;
+       LittleEndian.putInt(record,OFFSET_ESCAPEMENT_TYPE_MODIFIED, value);
+    }
+    
+    public boolean isEscapementTypeModified()
+    {
+       int escapementModified = LittleEndian.getInt(record,OFFSET_ESCAPEMENT_TYPE_MODIFIED);
+       return escapementModified == 0;
+    }
+
+    public void setUnderlineTypeModified(boolean modified)
+    {
+       int value = modified? 0 : 1;
+       LittleEndian.putInt(record,OFFSET_UNDERLINE_TYPE_MODIFIED, value);
+    }
+
+    public boolean isUnderlineTypeModified()
+    {
+       int underlineModified = LittleEndian.getInt(record,OFFSET_UNDERLINE_TYPE_MODIFIED);
+       return underlineModified == 0;
+    }
+    
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("    [Font Formatting]\n");
+        
+        buffer.append("    .font height = ").append(getFontHeight()).append(" twips\n");
+        
+        if( isFontStyleModified() )
+        {
+            buffer.append("    .font posture = ").append(isItalic()?"Italic":"Normal").append("\n");
+        }
+        else
+        {
+            buffer.append("    .font posture = ]not modified]").append("\n");
+        }
+        
+        if( isFontOutlineModified() )
+        {
+            buffer.append("    .font outline = ").append(isOutlineOn()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .font outline is not modified\n");
+        }
+
+        if( isFontShadowModified() )
+        {
+            buffer.append("    .font shadow = ").append(isShadowOn()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .font shadow is not modified\n");
+        }
+        if( isFontCondenseModified() )
+        {
+            buffer.append("    .font condense = ").append(isCondenseOn()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .font condense is not modified\n");
+        }
+        
+        if( isFontCancellationModified() )
+        {
+            buffer.append("    .font strikeout = ").append(isStruckout()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .font strikeout is not modified\n");
+        }
+
+        if( isFontStyleModified() )
+        {
+            buffer.append("    .font weight = ").
+               append(getFontWeight()).
+               append(
+                                       getFontWeight() == FONT_WEIGHT_NORMAL ? "(Normal)"
+                                                       : getFontWeight() == FONT_WEIGHT_BOLD ? "(Bold)" : "0x"+Integer.toHexString(getFontWeight())).
+               append("\n");
+        }
+        else
+        {
+            buffer.append("    .font weight = ]not modified]").append("\n");
+        }
+        
+        if( isEscapementTypeModified() )
+        {
+            buffer.append("    .escapement type = ").append(getEscapementType()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .escapement type is not modified\n");
+        }
+
+        if( isUnderlineTypeModified() )
+        {
+            buffer.append("    .underline type = ").append(getUnderlineType()).append("\n");
+        }
+        else
+        {
+            buffer.append("    .underline type is not modified\n");
+        }
+        buffer.append("    .color index = ").append("0x"+Integer.toHexString(getFontColorIndex()).toUpperCase()).append("\n");
+        
+        
+        buffer.append("    [/Font Formatting]\n");
+        return buffer.toString();
+    }
+    
+    public Object clone() 
+    {
+      FontFormatting rec = new FontFormatting();
+      if( record != null)
+      {
+         byte[] clone = new byte[record.length];
+         System.arraycopy(record, 0, clone, 0, record.length);
+         rec.record = clone;
+      }
+      return rec;
+    }
+}
diff --git a/src/java/org/apache/poi/hssf/record/cf/PatternFormatting.java b/src/java/org/apache/poi/hssf/record/cf/PatternFormatting.java
new file mode 100644 (file)
index 0000000..3157bf4
--- /dev/null
@@ -0,0 +1,206 @@
+
+/* ====================================================================
+   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.
+==================================================================== */
+        
+
+/*
+ * FontFormatting.java
+ *
+ * Created on January 22, 2008, 10:05 PM
+ */
+package org.apache.poi.hssf.record.cf;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Pattern Formatting Block of the Conditional Formatting Rule Record.
+ * 
+ * @author Dmitriy Kumshayev
+ */
+
+public class PatternFormatting implements Cloneable
+{
+    /**  No background */
+    public final static short     NO_FILL             = 0  ;
+    /**  Solidly filled */
+    public final static short     SOLID_FOREGROUND    = 1  ;
+    /**  Small fine dots */
+    public final static short     FINE_DOTS           = 2  ;
+    /**  Wide dots */
+    public final static short     ALT_BARS            = 3  ;
+    /**  Sparse dots */
+    public final static short     SPARSE_DOTS         = 4  ;
+    /**  Thick horizontal bands */
+    public final static short     THICK_HORZ_BANDS    = 5  ;
+    /**  Thick vertical bands */
+    public final static short     THICK_VERT_BANDS    = 6  ;
+    /**  Thick backward facing diagonals */
+    public final static short     THICK_BACKWARD_DIAG = 7  ;
+    /**  Thick forward facing diagonals */
+    public final static short     THICK_FORWARD_DIAG  = 8  ;
+    /**  Large spots */
+    public final static short     BIG_SPOTS           = 9  ;
+    /**  Brick-like layout */
+    public final static short     BRICKS              = 10 ;
+    /**  Thin horizontal bands */
+    public final static short     THIN_HORZ_BANDS     = 11 ;
+    /**  Thin vertical bands */
+    public final static short     THIN_VERT_BANDS     = 12 ;
+    /**  Thin backward diagonal */
+    public final static short     THIN_BACKWARD_DIAG  = 13 ;
+    /**  Thin forward diagonal */
+    public final static short     THIN_FORWARD_DIAG   = 14 ;
+    /**  Squares */
+    public final static short     SQUARES             = 15 ;
+    /**  Diamonds */
+    public final static short     DIAMONDS            = 16 ;
+    /**  Less Dots */
+    public final static short     LESS_DOTS           = 17 ;
+    /**  Least Dots */
+    public final static short     LEAST_DOTS          = 18 ;
+       
+    public PatternFormatting()
+    {
+        field_15_pattern_style = (short)0;
+        field_16_pattern_color_indexes = (short)0;
+    }
+    
+    /** Creates new FontFormatting */
+    public PatternFormatting(RecordInputStream in)
+       {
+       field_15_pattern_style  = in.readShort();
+        field_16_pattern_color_indexes = in.readShort();
+       }
+    
+    // PATTERN FORMATING BLOCK
+    // For Pattern Styles see constants at HSSFCellStyle (from NO_FILL to LEAST_DOTS)
+    private short                       field_15_pattern_style;
+    private static final BitField  fillPatternStyle = BitFieldFactory.getInstance(0xFC00);
+    
+    private short                       field_16_pattern_color_indexes;
+    private static final BitField  patternColorIndex = BitFieldFactory.getInstance(0x007F);             
+    private static final BitField  patternBackgroundColorIndex = BitFieldFactory.getInstance(0x3F80);           
+
+    /**
+     * setting fill pattern
+     *
+     * @see #NO_FILL
+     * @see #SOLID_FOREGROUND
+     * @see #FINE_DOTS
+     * @see #ALT_BARS
+     * @see #SPARSE_DOTS
+     * @see #THICK_HORZ_BANDS
+     * @see #THICK_VERT_BANDS
+     * @see #THICK_BACKWARD_DIAG
+     * @see #THICK_FORWARD_DIAG
+     * @see #BIG_SPOTS
+     * @see #BRICKS
+     * @see #THIN_HORZ_BANDS
+     * @see #THIN_VERT_BANDS
+     * @see #THIN_BACKWARD_DIAG
+     * @see #THIN_FORWARD_DIAG
+     * @see #SQUARES
+     * @see #DIAMONDS
+     *
+     * @param fp  fill pattern 
+     */
+    public void setFillPattern(short fp)
+    {
+       field_15_pattern_style = fillPatternStyle.setShortValue(field_15_pattern_style, fp);
+    }
+
+    /**
+     * get the fill pattern 
+     * @return fill pattern
+     */
+
+    public short getFillPattern()
+    {
+        return fillPatternStyle.getShortValue(field_15_pattern_style);
+    }
+    
+    /**
+     * set the background fill color.
+     *
+     * @param bg  color
+     */
+
+    public void setFillBackgroundColor(short bg)
+    {          
+       field_16_pattern_color_indexes = patternBackgroundColorIndex.setShortValue(field_16_pattern_color_indexes,bg);
+    }
+
+    /**
+     * get the background fill color
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @return fill color
+     */
+    public short getFillBackgroundColor()
+    {
+       return patternBackgroundColorIndex.getShortValue(field_16_pattern_color_indexes);
+    }
+
+    /**
+     * set the foreground fill color
+     * @param bg  color
+     */
+    public void setFillForegroundColor(short fg)
+    {
+       field_16_pattern_color_indexes = patternColorIndex.setShortValue(field_16_pattern_color_indexes,fg);
+    }
+
+    /**
+     * get the foreground fill color
+     * @see org.apache.poi.hssf.usermodel.HSSFPalette#getColor(short)
+     * @return fill color
+     */
+    public short getFillForegroundColor()
+    {
+       return patternColorIndex.getShortValue(field_16_pattern_color_indexes);
+    }
+    
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("    [Pattern Formatting]\n");
+        buffer.append("          .fillpattern= ").append(Integer.toHexString(getFillPattern())).append("\n");
+        buffer.append("          .fgcoloridx= ").append(Integer.toHexString(getFillForegroundColor())).append("\n");
+        buffer.append("          .bgcoloridx= ").append(Integer.toHexString(getFillBackgroundColor())).append("\n");
+        buffer.append("    [/Pattern Formatting]\n");
+        return buffer.toString();
+    }
+    
+    public Object clone() 
+    {
+      PatternFormatting rec = new PatternFormatting();
+      rec.field_15_pattern_style = field_15_pattern_style;
+      rec.field_16_pattern_color_indexes = field_16_pattern_color_indexes; 
+      return rec;
+    }
+    
+    public int serialize(int offset, byte [] data)
+    {
+       LittleEndian.putShort(data, offset, field_15_pattern_style);
+       offset += 2;
+       LittleEndian.putShort(data, offset, field_16_pattern_color_indexes);
+       offset += 2;
+       return 4;
+    }
+}
index 8a6b2c03b4fa8352208c0c59117d8f0728bfce02..75c6348907018115fe3bb96fb1cda8e500144e93 100644 (file)
@@ -32,6 +32,7 @@ public final class RefVPtg extends ReferencePtg {
 
   public RefVPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {
     super(row, column, isRowRelative, isColumnRelative);
+    setClass(CLASS_VALUE);
   }
 
 
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFBorderFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFBorderFormatting.java
new file mode 100644 (file)
index 0000000..622f3a6
--- /dev/null
@@ -0,0 +1,128 @@
+/* ====================================================================
+   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.usermodel;
+
+import org.apache.poi.hssf.record.cf.BorderFormatting;
+
+/**
+ * High level representation for Border Formatting component
+ * of Conditional Formatting settings
+ * 
+ * @author Dmitriy Kumshayev
+ *
+ */
+public class HSSFBorderFormatting
+{
+    /**
+     * No border
+     */
+
+    public final static short BORDER_NONE =  BorderFormatting.BORDER_NONE;
+
+    /**
+     * Thin border
+     */
+
+    public final static short BORDER_THIN =  BorderFormatting.BORDER_THIN;
+
+    /**
+     * Medium border
+     */
+
+    public final static short BORDER_MEDIUM =  BorderFormatting.BORDER_MEDIUM;
+
+    /**
+     * dash border
+     */
+
+    public final static short BORDER_DASHED =  BorderFormatting.BORDER_DASHED;
+
+    /**
+     * dot border
+     */
+
+    public final static short BORDER_HAIR =  BorderFormatting.BORDER_HAIR;
+
+    /**
+     * Thick border
+     */
+
+    public final static short BORDER_THICK =  BorderFormatting.BORDER_THICK;
+
+    /**
+     * double-line border
+     */
+
+    public final static short BORDER_DOUBLE =  BorderFormatting.BORDER_DOUBLE;
+
+    /**
+     * hair-line border
+     */
+
+    public final static short BORDER_DOTTED =  BorderFormatting.BORDER_DOTTED;
+
+    /**
+     * Medium dashed border
+     */
+
+    public final static short BORDER_MEDIUM_DASHED =  BorderFormatting.BORDER_MEDIUM_DASHED;
+
+    /**
+     * dash-dot border
+     */
+
+    public final static short BORDER_DASH_DOT =  BorderFormatting.BORDER_DASH_DOT;
+
+    /**
+     * medium dash-dot border
+     */
+
+    public final static short BORDER_MEDIUM_DASH_DOT =  BorderFormatting.BORDER_MEDIUM_DASH_DOT;
+
+    /**
+     * dash-dot-dot border
+     */
+
+    public final static short BORDER_DASH_DOT_DOT =  BorderFormatting.BORDER_DASH_DOT_DOT;
+
+    /**
+     * medium dash-dot-dot border
+     */
+
+    public final static short BORDER_MEDIUM_DASH_DOT_DOT =  BorderFormatting.BORDER_MEDIUM_DASH_DOT_DOT;
+
+    /**
+     * slanted dash-dot border
+     */
+
+    public final static short BORDER_SLANTED_DASH_DOT =  BorderFormatting.BORDER_SLANTED_DASH_DOT;
+
+    
+       private BorderFormatting borderFormatting;
+       
+       public HSSFBorderFormatting()
+       {
+               borderFormatting = new BorderFormatting();
+       }
+
+       protected BorderFormatting getBorderFormattingBlock()
+       {
+               return borderFormatting;
+       }
+    
+}
index d0d29b5b79b3a28d604715ade9b002cf2c90b406..11d14782be40288280a53f8b91f38ecf96380189 100644 (file)
@@ -277,7 +277,7 @@ public class HSSFCellStyle implements CellStyle
     
     /**
      * Get the contents of the format string, by looking up
-     *  the DataFormat against the supplied workbook
+     *  the DataFormat against the bound workbook
      * @see org.apache.poi.hssf.usermodel.HSSFDataFormat
      */
     public String getDataFormatString() {
@@ -285,6 +285,16 @@ public class HSSFCellStyle implements CellStyle
        
         return format.getFormat(getDataFormat());
     }
+    /**
+     * Get the contents of the format string, by looking up
+     *  the DataFormat against the supplied workbook
+     * @see org.apache.poi.hssf.usermodel.HSSFDataFormat
+     */
+    public String getDataFormatString(org.apache.poi.ss.usermodel.Workbook workbook) {
+       HSSFDataFormat format = new HSSFDataFormat( ((HSSFWorkbook)workbook).getWorkbook() );
+       
+        return format.getFormat(getDataFormat());
+    }
 
     /**
      * set the font for this style
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
new file mode 100644 (file)
index 0000000..e5fbb1d
--- /dev/null
@@ -0,0 +1,201 @@
+/* ====================================================================
+   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.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.record.CFHeaderRecord;
+import org.apache.poi.hssf.record.CFRuleRecord;
+import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
+import org.apache.poi.hssf.record.cf.CellRange;
+import org.apache.poi.hssf.util.Region;
+
+public class HSSFConditionalFormatting
+{
+       HSSFSheet sheet;
+       CFRecordsAggregate cfAggregate;
+       
+       protected HSSFConditionalFormatting(HSSFSheet sheet)
+       {
+               this.sheet = sheet;
+               this.cfAggregate = new CFRecordsAggregate();
+       }
+       
+       protected HSSFConditionalFormatting(HSSFSheet sheet, CFRecordsAggregate cfAggregate)
+       {
+               this.sheet = sheet;
+               this.cfAggregate = cfAggregate;
+       }
+       
+
+       public void setFormattingRegions(Region[] regions)
+       {
+               if( regions != null)
+               {
+                       CFHeaderRecord header = cfAggregate.getHeader();
+                       header.setCellRanges(mergeCellRanges(toCellRangeList(regions)));
+               }
+       }
+
+       public Region[] getFormattingRegions()
+       {
+               CFHeaderRecord cfh = cfAggregate.getHeader();
+               
+               List cellRanges = cfh.getCellRanges();
+               
+               if (cellRanges != null)
+               {
+                       return toRegionArray(cellRanges);
+               }
+               return null;
+       }
+       
+       public void setConditionalFormat(int idx, HSSFConditionalFormattingRule cfRule)
+       {
+               cfAggregate.getRules().set(idx, cfRule);
+       }
+
+       public void addConditionalFormat(HSSFConditionalFormattingRule cfRule)
+       {
+               cfAggregate.getRules().add(cfRule);
+       }
+       
+       public HSSFConditionalFormattingRule getConditionalFormat(int idx)
+       {
+               CFRuleRecord ruleRecord = (CFRuleRecord)cfAggregate.getRules().get(idx);
+               return new HSSFConditionalFormattingRule(sheet.workbook, ruleRecord);
+       }
+       
+       /**
+        * Do all possible cell merges between cells of the list so that:<br>
+        *      <li>if a cell range is completely inside of another cell range, it gets removed from the list 
+        *      <li>if two cells have a shared border, merge them into one bigger cell range
+        * @param cellRangeList
+        * @return updated List of cell ranges
+        */
+       private static List mergeCellRanges(List cellRangeList)
+       {
+               boolean merged = false;
+               
+               do
+               {
+                       merged = false;
+                       
+                       if( cellRangeList.size()>1 )
+                       {
+                               for( int i=0; i<cellRangeList.size(); i++)
+                               {
+                                       CellRange range1 = (CellRange)cellRangeList.get(i);
+                                       for( int j=i+1; j<cellRangeList.size(); j++)
+                                       {
+                                               CellRange range2 = (CellRange)cellRangeList.get(j);
+                                               
+                                               switch(range1.intersect(range2))
+                                               {
+                                                       case CellRange.NO_INTERSECTION: 
+                                                       {
+                                                               if( range1.hasSharedBorder(range2))
+                                                               {
+                                                                       cellRangeList.set(i, range1.createEnclosingCellRange(range2));
+                                                                       cellRangeList.remove(j--);
+                                                                       merged = true;
+                                                               }
+                                                               else
+                                                               {
+                                                                       // No intersection and no shared border: do nothing 
+                                                               }
+                                                               break;
+                                                       }
+                                                       case CellRange.OVERLAP:
+                                                       {
+                                                               // TODO split and re-merge the intersected area
+                                                               break;
+                                                       }
+                                                       case CellRange.INSIDE:
+                                                       {
+                                                               // Remove range2, since it is completely inside of range1
+                                                               cellRangeList.remove(j--);
+                                                               merged = true;
+                                                               break;
+                                                       }
+                                                       case CellRange.ENCLOSES:
+                                                       {
+                                                               // range2 encloses range1, so replace it with the enclosing one
+                                                               cellRangeList.set(i, range2);
+                                                               cellRangeList.remove(j--);
+                                                               merged = true;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               while( merged );
+               
+               return cellRangeList;
+       }
+       
+       /**
+        * Convert a List of CellRange objects to an array of regions 
+        *  
+        * @param List of CellRange objects
+        * @return regions
+        */
+       private static Region[] toRegionArray(List cellRanges)
+       {
+               int size = cellRanges.size();
+               Region[] regions = new Region[size];
+               
+               for (int i = 0; i != size; i++)
+               {
+                       CellRange cr = (CellRange) cellRanges.get(i);
+                       regions[i] = new Region(cr.getFirstRow(), cr.getFirstColumn(), 
+                                       cr.getLastRow(), cr.getLastColumn());
+               }
+               return regions;
+       }
+       
+       /**
+        * Convert array of regions to a List of CellRange objects
+        *  
+        * @param regions
+        * @return List of CellRange objects
+        */
+       private static List toCellRangeList(Region[] regions)
+       {
+               List cellRangeList = new ArrayList();
+               for( int i=0; i<regions.length; i++)
+               {
+                       Region r = regions[i];
+                       CellRange cr = new CellRange(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r
+                                       .getColumnTo());
+                       cellRangeList.add(cr);
+               }
+               return cellRangeList;
+       }
+
+       public String toString()
+       {
+               if(cfAggregate!=null)
+               {
+                       return cfAggregate.toString();
+               }
+               return null;
+       }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java
new file mode 100644 (file)
index 0000000..3d003dc
--- /dev/null
@@ -0,0 +1,248 @@
+/* ====================================================================
+   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.usermodel;
+
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.CFRuleRecord;
+import org.apache.poi.hssf.record.formula.Ptg;
+
+/**
+ * 
+ * High level representation of Conditional Format  
+ * 
+ * @author Dmitriy Kumshayev
+ */
+
+public class HSSFConditionalFormattingRule
+{
+    public static final byte CELL_COMPARISON = CFRuleRecord.CONDITION_TYPE_CELL_VALUE_IS;
+    public static final byte FORMULA = CFRuleRecord.CONDITION_TYPE_FORMULA;
+
+    public static final byte COMPARISON_OPERATOR_NO_COMPARISON = CFRuleRecord.COMPARISON_OPERATOR_NO_COMPARISON;
+    public static final byte COMPARISON_OPERATOR_BETWEEN          = CFRuleRecord.COMPARISON_OPERATOR_BETWEEN;
+    public static final byte COMPARISON_OPERATOR_NOT_BETWEEN   = CFRuleRecord.COMPARISON_OPERATOR_NOT_BETWEEN;
+    public static final byte COMPARISON_OPERATOR_EQUAL         = CFRuleRecord.COMPARISON_OPERATOR_EQUAL;
+    public static final byte COMPARISON_OPERATOR_NOT_EQUAL     = CFRuleRecord.COMPARISON_OPERATOR_NOT_EQUAL;
+    public static final byte COMPARISON_OPERATOR_GT            = CFRuleRecord.COMPARISON_OPERATOR_GT;
+    public static final byte COMPARISON_OPERATOR_LT            = CFRuleRecord.COMPARISON_OPERATOR_LT;
+    public static final byte COMPARISON_OPERATOR_GE            = CFRuleRecord.COMPARISON_OPERATOR_GE;
+    public static final byte COMPARISON_OPERATOR_LE            = CFRuleRecord.COMPARISON_OPERATOR_LE;
+    
+    
+
+       private CFRuleRecord cfRuleRecord;
+       private HSSFWorkbook workbook;
+       
+       protected HSSFConditionalFormattingRule(HSSFWorkbook workbook)
+       {
+               this.workbook = workbook;
+               this.cfRuleRecord = new CFRuleRecord();
+       }
+
+       protected HSSFConditionalFormattingRule(HSSFWorkbook workbook, CFRuleRecord cfRuleRecord)
+       {
+               this.workbook = workbook;
+               this.cfRuleRecord = cfRuleRecord;
+       }
+
+       /** 
+        *  Keep Font Formatting unchanged for this Conditional Formatting Rule 
+        */
+       public void setFontFormattingUnchanged()
+       {
+               cfRuleRecord.setFontFormattingUnchanged();
+       }
+       /** 
+        *  Keep Border Formatting unchanged for this Conditional Formatting Rule 
+        */
+       public void setBorderFormattingUnchanged()
+       {
+               cfRuleRecord.setBorderFormattingUnchanged();
+       }
+       /** 
+        *  Keep Pattern Formatting unchanged for this Conditional Formatting Rule 
+        */
+       public void setPatternFormattingUnchanged()
+       {
+               cfRuleRecord.setPatternFormattingUnchanged();
+       }
+       
+       public void setFontFormatting(HSSFFontFormatting fontFormatting)
+       {
+               if( fontFormatting!=null )
+               {
+                       cfRuleRecord.setFontFormatting(fontFormatting.getFontFormattingBlock());
+               }
+               else
+               {
+                       setFontFormattingUnchanged();
+               }
+       }
+       public void setBorderFormatting(HSSFBorderFormatting borderFormatting)
+       {
+               if( borderFormatting != null )
+               {
+                       cfRuleRecord.setBorderFormatting(borderFormatting.getBorderFormattingBlock());
+               }
+               else
+               {
+                       setBorderFormattingUnchanged();
+               }
+       }
+       public void setPatternFormatting(HSSFPatternFormatting patternFormatting)
+       {
+               if( patternFormatting != null)
+               {
+                       cfRuleRecord.setPatternFormatting(patternFormatting.getPatternFormattingBlock());
+               }
+               else
+               {
+                       setPatternFormattingUnchanged();
+               }
+       }
+       
+       public void setCellComparisonCondition(byte comparisonOperation, String formula1, String formula2)
+       {
+               cfRuleRecord.setConditionType(CELL_COMPARISON);
+               cfRuleRecord.setComparisonOperation(comparisonOperation);
+               
+               // Formula 1
+               setFormula1(formula1);
+               
+               // Formula 2
+               setFormula1(formula2);
+       }
+       
+       public void setFormulaCondition(String formula)
+       {
+               cfRuleRecord.setConditionType(FORMULA);
+               // Formula 1
+               setFormula1(formula);
+       }
+       
+       public void setFormula1(String formula)
+       {
+               // Formula 1
+               if( formula != null)
+               {
+                   Stack parsedExpression = parseFormula(formula);
+                       if( parsedExpression != null )
+                       {
+                               cfRuleRecord.setParsedExpression1(parsedExpression);
+                       }
+                       else
+                       {
+                               cfRuleRecord.setParsedExpression1(null);
+                       }
+               }
+               else
+               {
+                       cfRuleRecord.setParsedExpression1(null);
+               }
+       }
+       
+       public void setFormula2(String formula)
+       {
+               // Formula 2
+               if( formula != null)
+               {
+                   Stack parsedExpression = parseFormula(formula);
+                       if( parsedExpression != null )
+                       {
+                               cfRuleRecord.setParsedExpression2(parsedExpression);
+                       }
+                       else
+                       {
+                               cfRuleRecord.setParsedExpression2(null);
+                       }
+               }
+               else
+               {
+                       cfRuleRecord.setParsedExpression2(null);
+               }
+       }
+       
+       public String getFormula1()
+       {
+        return toFormulaString(cfRuleRecord.getParsedExpression1());
+       }
+
+       public String getFormula2()
+       {
+               byte conditionType = cfRuleRecord.getConditionType();
+               switch(conditionType)
+               {
+                       case CELL_COMPARISON:
+                       {
+                               byte comparisonOperation = cfRuleRecord.getComparisonOperation();
+                               switch(comparisonOperation)
+                               {
+                                       case COMPARISON_OPERATOR_BETWEEN:
+                                       case COMPARISON_OPERATOR_NOT_BETWEEN:
+                                               return toFormulaString(cfRuleRecord.getParsedExpression2());
+                               }
+                       }
+               }
+               return null;
+       }
+
+       private String toFormulaString(List parsedExpression)
+       {
+               String formula = null;
+               if(parsedExpression!=null)
+               {
+               formula = FormulaParser.toFormulaString(workbook.getWorkbook(),parsedExpression);
+               }
+               return formula;
+       }
+       
+
+       private Stack parseFormula(String formula2)
+       {
+               FormulaParser parser = 
+                       new FormulaParser(formula2, workbook.getWorkbook());
+               parser.parse();
+
+               Stack parsedExpression = convertToTokenStack(parser.getRPNPtg());
+           parsedExpression = convertToTokenStack(parser.getRPNPtg());
+               return parsedExpression;
+       }
+
+       private static Stack convertToTokenStack(Ptg[] ptgs)
+       {
+               if( ptgs != null)
+               {
+                       Stack parsedExpression = new Stack();
+                       // fill the Ptg Stack with Ptgs of new formula
+                       for (int k = 0; k < ptgs.length; k++) 
+                       {
+                           parsedExpression.push(ptgs[ k ]);
+                       }
+                       return parsedExpression;
+               }
+               else
+               {
+                       return null;
+               }
+       }
+       
+       
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFontFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFontFormatting.java
new file mode 100644 (file)
index 0000000..ee1352f
--- /dev/null
@@ -0,0 +1,456 @@
+/* ====================================================================
+   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.usermodel;
+
+import org.apache.poi.hssf.record.cf.FontFormatting;
+/**
+ * High level representation for Font Formatting component
+ * of Conditional Formatting settings
+ * 
+ * @author Dmitriy Kumshayev
+ *
+ */
+public class HSSFFontFormatting
+{
+    /**
+     * Escapement type - None
+     */
+    public final static short     SS_NONE             = FontFormatting.SS_NONE;
+    
+    /**
+     * Escapement type - Superscript
+     */
+    public final static short     SS_SUPER            = FontFormatting.SS_SUPER;
+    
+    /**
+     * Escapement type - Subscript
+     */
+    public final static short     SS_SUB              = FontFormatting.SS_SUB;
+    
+    /**
+     *  Underline type - None
+     */ 
+    public final static byte      U_NONE              = FontFormatting.U_NONE;
+    /**
+     *  Underline type - Single
+     */ 
+    public final static byte      U_SINGLE            = FontFormatting.U_SINGLE;
+    /**
+     *  Underline type - Double
+     */ 
+    public final static byte      U_DOUBLE            = FontFormatting.U_DOUBLE;
+    /**
+     *  Underline type - Single Accounting
+     */ 
+    public final static byte      U_SINGLE_ACCOUNTING = FontFormatting.U_SINGLE_ACCOUNTING;
+    /**
+     *  Underline type - Double Accounting
+     */ 
+    public final static byte      U_DOUBLE_ACCOUNTING = FontFormatting.U_DOUBLE_ACCOUNTING;
+    
+       private FontFormatting fontFormatting;
+       
+       public HSSFFontFormatting()
+       {
+               fontFormatting = new FontFormatting();
+       }
+
+       protected FontFormatting getFontFormattingBlock()
+       {
+               return fontFormatting;
+       }
+
+    /**
+     * get the type of super or subscript for the font
+     *
+     * @return super or subscript option
+     * @see #SS_NONE
+     * @see #SS_SUPER
+     * @see #SS_SUB
+     */
+       public short getEscapementType()
+       {
+               return fontFormatting.getEscapementType();
+       }
+
+       /**
+        * @return font color index
+        */
+       public short getFontColorIndex()
+       {
+               return fontFormatting.getFontColorIndex();
+       }
+
+    /**
+     * gets the height of the font in 1/20th point units
+     *
+     * @return fontheight (in points/20); or -1 if not modified
+     */
+       public short getFontHeight()
+       {
+               return fontFormatting.getFontHeight();
+       }
+
+    /**
+     * get the font weight for this font (100-1000dec or 0x64-0x3e8).  Default is
+     * 0x190 for normal and 0x2bc for bold
+     *
+     * @return bw - a number between 100-1000 for the fonts "boldness"
+     */
+
+       public short getFontWeight()
+       {
+               return fontFormatting.getFontWeight();
+       }
+
+       /**
+        * @return
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#getRawRecord()
+        */
+       protected byte[] getRawRecord()
+       {
+               return fontFormatting.getRawRecord();
+       }
+
+    /**
+     * get the type of underlining for the font
+     *
+     * @return font underlining type
+     *
+     * @see #U_NONE
+     * @see #U_SINGLE
+     * @see #U_DOUBLE
+     * @see #U_SINGLE_ACCOUNTING
+     * @see #U_DOUBLE_ACCOUNTING
+     */
+       public short getUnderlineType()
+       {
+               return fontFormatting.getUnderlineType();
+       }
+
+    /**
+     * get whether the font weight is set to bold or not
+     *
+     * @return bold - whether the font is bold or not
+     */
+       public boolean isBold()
+       {
+               return fontFormatting.isBold();
+       }
+
+       /**
+        * @return  whether the font is condense or not
+        */
+       public boolean isCondenseOn()
+       {
+               return fontFormatting.isFontOutlineModified() && fontFormatting.isCondenseOn();
+       }
+
+       /**
+        * @return true if escapement type was modified from default   
+        */
+       public boolean isEscapementTypeModified()
+       {
+               return fontFormatting.isEscapementTypeModified();
+       }
+
+       /**
+        * @return true if font cancellation was modified from default   
+        */
+       public boolean isFontCancellationModified()
+       {
+               return fontFormatting.isFontCancellationModified();
+       }
+
+       /**
+        * @return true if font condense type was modified from default   
+        */
+       public boolean isFontCondenseModified()
+       {
+               return fontFormatting.isFontCondenseModified();
+       }
+
+       /**
+        * @return true if font outline type was modified from default   
+        */
+       public boolean isFontOutlineModified()
+       {
+               return fontFormatting.isFontOutlineModified();
+       }
+
+       /**
+        * @return true if font shadow type was modified from default   
+        */
+       public boolean isFontShadowModified()
+       {
+               return fontFormatting.isFontShadowModified();
+       }
+
+       /**
+        * @return true if font style was modified from default   
+        */
+       public boolean isFontStyleModified()
+       {
+               return fontFormatting.isFontStyleModified();
+       }
+
+       /**
+        * @return true if font style was set to <i>italic</i> 
+        */
+       public boolean isItalic()
+       {
+               return fontFormatting.isFontStyleModified() && fontFormatting.isItalic();
+       }
+
+       /**
+        * @return true if font outline is on
+        */
+       public boolean isOutlineOn()
+       {
+               return fontFormatting.isFontOutlineModified() && fontFormatting.isOutlineOn();
+       }
+
+       /**
+        * @return true if font shadow is on
+        */
+       public boolean isShadowOn()
+       {
+               return fontFormatting.isFontOutlineModified() && fontFormatting.isShadowOn();
+       }
+
+       /**
+        * @return true if font strikeout is on
+        */
+       public boolean isStruckout()
+       {
+               return fontFormatting.isFontCancellationModified() && fontFormatting.isStruckout();
+       }
+
+       /**
+        * @return true if font underline type was modified from default   
+        */
+       public boolean isUnderlineTypeModified()
+       {
+               return fontFormatting.isUnderlineTypeModified();
+       }
+
+    /**
+     * set font style options.
+     * 
+     * @param italic - if true, set posture style to italic, otherwise to normal 
+     * @param bold- if true, set font weight to bold, otherwise to normal
+     */
+       
+       public void setFontStyle(boolean italic, boolean bold)
+       {
+       boolean modified = italic || bold;
+       fontFormatting.setItalic(italic);
+       fontFormatting.setBold(bold);
+       fontFormatting.setFontStyleModified(modified);
+       }
+
+    /**
+     * set font style options to default values (non-italic, non-bold)
+     */
+       public void resetFontStyle()
+       {
+               setFontStyle(false,false);
+       }
+
+    /**
+     * set the escapement type for the font
+     *
+     * @param escapementType  super or subscript option
+     * @see #SS_NONE
+     * @see #SS_SUPER
+     * @see #SS_SUB
+     */
+       public void setCondense(boolean on)
+       {
+               fontFormatting.setCondense(on);
+               fontFormatting.setFontCondenseModified(on);
+       }
+
+    /**
+     * set the escapement type for the font
+     *
+     * @param escapementType  super or subscript option
+     * @see #SS_NONE
+     * @see #SS_SUPER
+     * @see #SS_SUB
+     */
+       public void setEscapementType(short escapementType)
+       {
+       switch(escapementType)
+       {
+               case HSSFFontFormatting.SS_SUB:
+               case HSSFFontFormatting.SS_SUPER:
+                       fontFormatting.setEscapementType(escapementType);
+                       fontFormatting.setEscapementTypeModified(true);
+               break;
+               case HSSFFontFormatting.SS_NONE:
+                       fontFormatting.setEscapementType(escapementType);
+                       fontFormatting.setEscapementTypeModified(false);
+               break;
+               default:
+       }
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setEscapementTypeModified(boolean)
+        */
+       public void setEscapementTypeModified(boolean modified)
+       {
+               fontFormatting.setEscapementTypeModified(modified);
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontCancellationModified(boolean)
+        */
+       public void setFontCancellationModified(boolean modified)
+       {
+               fontFormatting.setFontCancellationModified(modified);
+       }
+
+       /**
+        * @param fci
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontColorIndex(short)
+        */
+       public void setFontColorIndex(short fci)
+       {
+               fontFormatting.setFontColorIndex(fci);
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontCondenseModified(boolean)
+        */
+       public void setFontCondenseModified(boolean modified)
+       {
+               fontFormatting.setFontCondenseModified(modified);
+       }
+
+       /**
+        * @param height
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontHeight(short)
+        */
+       public void setFontHeight(short height)
+       {
+               fontFormatting.setFontHeight(height);
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontOutlineModified(boolean)
+        */
+       public void setFontOutlineModified(boolean modified)
+       {
+               fontFormatting.setFontOutlineModified(modified);
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontShadowModified(boolean)
+        */
+       public void setFontShadowModified(boolean modified)
+       {
+               fontFormatting.setFontShadowModified(modified);
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setFontStyleModified(boolean)
+        */
+       public void setFontStyleModified(boolean modified)
+       {
+               fontFormatting.setFontStyleModified(modified);
+       }
+
+       /**
+        * @param on
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setOutline(boolean)
+        */
+       public void setOutline(boolean on)
+       {
+               fontFormatting.setOutline(on);
+               fontFormatting.setFontOutlineModified(on);
+       }
+
+       /**
+        * @param on
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setShadow(boolean)
+        */
+       public void setShadow(boolean on)
+       {
+               fontFormatting.setShadow(on);
+               fontFormatting.setFontShadowModified(on);
+       }
+
+       /**
+        * @param strike
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setStrikeout(boolean)
+        */
+       public void setStrikeout(boolean strike)
+       {
+               fontFormatting.setStrikeout(strike);
+               fontFormatting.setFontCancellationModified(strike);
+       }
+
+    /**
+     * set the type of underlining type for the font
+     *
+     * @param u  super or subscript option
+     *
+     * @see #U_NONE
+     * @see #U_SINGLE
+     * @see #U_DOUBLE
+     * @see #U_SINGLE_ACCOUNTING
+     * @see #U_DOUBLE_ACCOUNTING
+     */
+       public void setUnderlineType(short underlineType)
+       {
+       switch(underlineType)
+       {
+               case HSSFFontFormatting.U_SINGLE:
+               case HSSFFontFormatting.U_DOUBLE:
+               case HSSFFontFormatting.U_SINGLE_ACCOUNTING:
+               case HSSFFontFormatting.U_DOUBLE_ACCOUNTING:
+                       fontFormatting.setUnderlineType(underlineType);
+               setUnderlineTypeModified(true);
+               break;
+                       
+               case HSSFFontFormatting.U_NONE:
+                       fontFormatting.setUnderlineType(underlineType);
+               setUnderlineTypeModified(false);
+               break;
+           default:
+       }
+       }
+
+       /**
+        * @param modified
+        * @see org.apache.poi.hssf.record.cf.FontFormatting#setUnderlineTypeModified(boolean)
+        */
+       public void setUnderlineTypeModified(boolean modified)
+       {
+               fontFormatting.setUnderlineTypeModified(modified);
+       }
+       
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatternFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatternFormatting.java
new file mode 100644 (file)
index 0000000..352a5b4
--- /dev/null
@@ -0,0 +1,134 @@
+/* ====================================================================
+   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.usermodel;
+
+import org.apache.poi.hssf.record.cf.PatternFormatting;
+
+/**
+ * High level representation for Conditional Formatting settings
+ * 
+ * @author Dmitriy Kumshayev
+ *
+ */
+public class HSSFPatternFormatting
+{
+    /**  No background */
+    public final static short NO_FILL                          =  PatternFormatting.NO_FILL;
+    /**  Solidly filled */
+    public final static short SOLID_FOREGROUND                 =  PatternFormatting.SOLID_FOREGROUND;
+    /**  Small fine dots */
+    public final static short FINE_DOTS                        =  PatternFormatting.FINE_DOTS;
+    /**  Wide dots */
+    public final static short ALT_BARS                                 =  PatternFormatting.ALT_BARS;
+    /**  Sparse dots */
+    public final static short SPARSE_DOTS                      =  PatternFormatting.SPARSE_DOTS;
+    /**  Thick horizontal bands */
+    public final static short THICK_HORZ_BANDS                 =  PatternFormatting.THICK_HORZ_BANDS;
+    /**  Thick vertical bands */
+    public final static short THICK_VERT_BANDS                 =  PatternFormatting.THICK_VERT_BANDS;
+    /**  Thick backward facing diagonals */
+    public final static short THICK_BACKWARD_DIAG      =  PatternFormatting.THICK_BACKWARD_DIAG;
+    /**  Thick forward facing diagonals */
+    public final static short THICK_FORWARD_DIAG       =  PatternFormatting.THICK_FORWARD_DIAG;
+    /**  Large spots */
+    public final static short BIG_SPOTS                        =  PatternFormatting.BIG_SPOTS;
+    /**  Brick-like layout */
+    public final static short BRICKS                           =  PatternFormatting.BRICKS;
+    /**  Thin horizontal bands */
+    public final static short THIN_HORZ_BANDS          =  PatternFormatting.THIN_HORZ_BANDS;
+    /**  Thin vertical bands */
+    public final static short THIN_VERT_BANDS          =  PatternFormatting.THIN_VERT_BANDS;
+    /**  Thin backward diagonal */
+    public final static short THIN_BACKWARD_DIAG       =  PatternFormatting.THIN_BACKWARD_DIAG;
+    /**  Thin forward diagonal */
+    public final static short THIN_FORWARD_DIAG        =  PatternFormatting.THIN_FORWARD_DIAG;
+    /**  Squares */
+    public final static short SQUARES                          =  PatternFormatting.SQUARES;
+    /**  Diamonds */
+    public final static short DIAMONDS                                 =  PatternFormatting.DIAMONDS;
+    /**  Less Dots */
+    public final static short LESS_DOTS                        =  PatternFormatting.LESS_DOTS;
+    /**  Least Dots */
+    public final static short LEAST_DOTS                       =  PatternFormatting.LEAST_DOTS;
+
+       private PatternFormatting patternFormatting;
+       
+       public HSSFPatternFormatting()
+       {
+               patternFormatting = new PatternFormatting();
+       }
+
+       protected PatternFormatting getPatternFormattingBlock()
+       {
+               return patternFormatting;
+       }
+
+       /**
+        * @return
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillBackgroundColor()
+        */
+       public short getFillBackgroundColor()
+       {
+               return patternFormatting.getFillBackgroundColor();
+       }
+
+       /**
+        * @return
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillForegroundColor()
+        */
+       public short getFillForegroundColor()
+       {
+               return patternFormatting.getFillForegroundColor();
+       }
+
+       /**
+        * @return
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#getFillPattern()
+        */
+       public short getFillPattern()
+       {
+               return patternFormatting.getFillPattern();
+       }
+
+       /**
+        * @param bg
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillBackgroundColor(short)
+        */
+       public void setFillBackgroundColor(short bg)
+       {
+               patternFormatting.setFillBackgroundColor(bg);
+       }
+
+       /**
+        * @param fg
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillForegroundColor(short)
+        */
+       public void setFillForegroundColor(short fg)
+       {
+               patternFormatting.setFillForegroundColor(fg);
+       }
+
+       /**
+        * @param fp
+        * @see org.apache.poi.hssf.record.cf.PatternFormatting#setFillPattern(short)
+        */
+       public void setFillPattern(short fp)
+       {
+               patternFormatting.setFillPattern(fp);
+       }
+}
index 047d7fcb3b4652ba0aa88252b890ee65ed0f8abf..d553f7b0a628200794289ab4e27a3232be677076 100644 (file)
@@ -53,6 +53,7 @@ import org.apache.poi.hssf.record.SCLRecord;
 import org.apache.poi.hssf.record.VCenterRecord;
 import org.apache.poi.hssf.record.WSBoolRecord;
 import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.ReferencePtg;
 import org.apache.poi.hssf.util.HSSFCellRangeAddress;
@@ -1844,4 +1845,161 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
         return null;
     }
 
+
+     /**
+      * A factory method allowing to create a conditional formatting rule 
+      * with a cell comparison operator and 
+      * formatting rules such as font format, border format and pattern format
+      * 
+      * @param comparisonOperation - one of the following values: <p>
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}</li>
+      * </p> 
+      * @param formula1 - formula for the valued, compared with the cell
+      * @param formula2 - second formula (only used with 
+      * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}) and
+      * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations)   
+      * @param fontFmt - font formatting rules
+      * @param bordFmt - border formatting rules
+      * @param patternFmt - pattern formatting rules
+      * @return
+      * 
+      */
+     public HSSFConditionalFormattingRule createConditionalFormattingRule(
+               byte comparisonOperation, 
+               String formula1, 
+               String formula2,
+               HSSFFontFormatting fontFmt,
+                       HSSFBorderFormatting bordFmt, 
+                       HSSFPatternFormatting patternFmt)
+     {
+       HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook);
+               cf.setFontFormatting(fontFmt);
+               cf.setBorderFormatting(bordFmt);
+               cf.setPatternFormatting(patternFmt);
+               cf.setCellComparisonCondition(comparisonOperation, formula1, formula2);
+       return cf;
+     }
+
+     /**
+      * A factory method allowing to create a conditional formatting rule with a formula 
+      * and formatting rules such as font format, border format and pattern format. <br>
+      * 
+      * The formatting rules are applied by Excel when the value of the formula not equal to 0. 
+      * 
+      * @param comparisonOperation - one of the following values: <p>
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}</li> 
+      *                <li>{@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}</li>
+      * </p> 
+      * @param formula1 - formula for the valued, compared with the cell
+      * @param formula2 - second formula (only used with 
+      * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}) and
+      * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations)   
+      * @param fontFmt - font formatting rules
+      * @param bordFmt - border formatting rules
+      * @param patternFmt - pattern formatting rules
+      * @return
+      * 
+      */
+     public HSSFConditionalFormattingRule createConditionalFormattingRule(
+               String formula, 
+               HSSFFontFormatting fontFmt,
+                       HSSFBorderFormatting bordFmt, 
+                       HSSFPatternFormatting patternFmt)
+     {
+       HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook);
+               cf.setFontFormatting(fontFmt);
+               cf.setBorderFormatting(bordFmt);
+               cf.setPatternFormatting(patternFmt);
+               cf.setFormulaCondition(formula);
+       return cf;
+     }
+     
+     /**
+      * Adds a copy of HSSFConditionalFormatting object to the sheet
+      * <p>This method could be used to copy HSSFConditionalFormatting object
+      * from one sheet to another. For example:
+      * <pre>
+      * HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(index);
+      * newSheet.addConditionalFormatting(cf);
+      * </pre>  
+      * 
+      * @param cf HSSFConditionalFormatting object
+      * @return index of the new Conditional Formatting object
+      */
+     public int addConditionalFormatting( HSSFConditionalFormatting cf )
+     {
+       HSSFConditionalFormatting cfClone = new HSSFConditionalFormatting(this,cf.cfAggregate.cloneCFAggregate());
+       cfClone.sheet=this;
+       return sheet.addConditionalFormatting(cfClone.cfAggregate);
+     }
+
+     /**
+      * Allows to add a new Conditional Formatting set to the sheet.
+      *  
+      * @param regions - list of rectangular regions to apply conditional formatting rules 
+      * @param cfRules - set of up to three conditional formatting rules
+      * 
+      * @return index of the newly created Conditional Formatting object
+      */
+     
+     public int addConditionalFormatting( Region [] regions, HSSFConditionalFormattingRule [] cfRules )
+     {
+       HSSFConditionalFormatting cf = new HSSFConditionalFormatting(this);
+       cf.setFormattingRegions(regions);
+       if( cfRules != null )
+       {
+               for( int i=0; i!= cfRules.length; i++ )
+               {
+               cf.addConditionalFormat(cfRules[i]);
+               }
+       }
+       return sheet.addConditionalFormatting(cf.cfAggregate);
+     }
+     
+       /**
+        * gets Conditional Formatting object at a particular index
+        * @param index of the Conditional Formatting object to fetch
+        * @return Conditional Formatting object
+        */
+     
+       public HSSFConditionalFormatting getConditionalFormattingAt(int index)
+       {
+               CFRecordsAggregate cf = sheet.getCFRecordsAggregateAt(index);
+               if( cf != null )
+               {
+                       return new HSSFConditionalFormatting(this,cf);
+               }
+               return null;
+       }
+
+       /**
+        * @return number of Conditional Formatting objects of the sheet
+        */
+       public int getNumConditionalFormattings()
+       {
+               return sheet.getNumConditionalFormattings();
+       }
+
+       /**
+        * removes a Conditional Formatting object by index
+        * @param index of a Conditional Formatting object to remove
+        */
+       public void removeConditionalFormatting(int index)
+       {
+               sheet.removeConditionalFormatting(index);
+       }
+
 }
index 74ea822a5f316c04a303ff9644a528050142501d..a9ce66f36b3273c4b2c2814bbc7176f5c7c0e15a 100644 (file)
@@ -33,14 +33,25 @@ import java.util.*;
 public class POILogFactory
 {
 
-    // map of POILogger instances, with classes as keys
-    private static Map          _loggers = new HashMap();;
-
+    /**
+     * Map of POILogger instances, with classes as keys
+     */
+    private static Map _loggers = new HashMap();;
 
     /**
-     * construct a POILogFactory.
+     * A common instance of NullLogger, as it does nothing
+     *  we only need the one
+     */
+    private static POILogger _nullLogger = new NullLogger();
+    /**
+     * The name of the class to use. Initialised the
+     *  first time we need it
      */
+    private static String _loggerClassName = null;
 
+    /**
+     * Construct a POILogFactory.
+     */
     private POILogFactory()
     {
     }
@@ -69,28 +80,48 @@ public class POILogFactory
     public static POILogger getLogger(final String cat)
     {
         POILogger logger = null;
+        
+        // If we haven't found out what logger to use yet,
+        //  then do so now
+        // Don't look it up until we're first asked, so
+        //  that our users can set the system property
+        //  between class loading and first use
+        if(_loggerClassName == null) {
+               try {
+                       _loggerClassName = System.getProperty("org.apache.poi.util.POILogger");
+               } catch(Exception e) {}
+               
+               // Use the default logger if none specified,
+               //  or none could be fetched
+               if(_loggerClassName == null) {
+                       _loggerClassName = _nullLogger.getClass().getName();
+               }
+        }
+        
+        // Short circuit for the null logger, which
+        //  ignores all categories
+        if(_loggerClassName.equals(_nullLogger.getClass().getName())) {
+               return _nullLogger;
+        }
 
-        if (_loggers.containsKey(cat))
-        {
+        
+        // Fetch the right logger for them, creating
+        //  it if that's required 
+        if (_loggers.containsKey(cat)) {
             logger = ( POILogger ) _loggers.get(cat);
-        }
-        else
-        {
-            try{
-              String loggerClassName = System.getProperty("org.apache.poi.util.POILogger");
-              Class loggerClass = Class.forName(loggerClassName);
+        } else {
+            try {
+              Class loggerClass = Class.forName(_loggerClassName);
               logger = ( POILogger ) loggerClass.newInstance();
+              logger.initialize(cat);
+            } catch(Exception e) {
+              // Give up and use the null logger
+              logger = _nullLogger;
             }
-            catch(Exception e){
-            
-              logger = new NullLogger();
-            }
-            
-            logger.initialize(cat);
             
+            // Save for next time
             _loggers.put(cat, logger);
         }
         return logger;
     }
-        
-}   // end public class POILogFactory
+}   // end public class POILogFactory
\ No newline at end of file
index 58ab5b47aefb6bcab2d4e061d89acf64a1228275..bb16fdfadd7bc5d9fc326a69ed8500ae84e01125 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
 
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Stack;
 
@@ -251,6 +252,35 @@ public class HSSFFormulaEvaluator {
         }
         return cell;
     }
+    
+    /**
+     * Loops over all cells in all sheets of the supplied
+     *  workbook.
+     * For cells that contain formulas, their formulas are
+     *  evaluated, and the results are saved. These cells
+     *  remain as formula cells.
+     * For cells that do not contain formulas, no changes
+     *  are made.
+     * This is a helpful wrapper around looping over all 
+     *  cells, and calling evaluateFormulaCell on each one.
+     */
+       public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
+               for(int i=0; i<wb.getNumberOfSheets(); i++) {
+                       HSSFSheet sheet = wb.getSheetAt(i);
+                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+
+                       for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
+                               HSSFRow r = (HSSFRow)rit.next();
+                               evaluator.setCurrentRow(r);
+
+                               for (Iterator cit = r.cellIterator(); cit.hasNext();) {
+                                       HSSFCell c = (HSSFCell)cit.next();
+                                       if (c.getCellType() == HSSFCell.CELL_TYPE_FORMULA)
+                                               evaluator.evaluateFormulaCell(c);
+                               }
+                       }
+               }
+       }
         
     
     /**
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java
new file mode 100644 (file)
index 0000000..db4be7f
--- /dev/null
@@ -0,0 +1,88 @@
+/* ====================================================================
+   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.usermodel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+public final class TestFormulaEvaluatorBugs extends TestCase {
+       private String dirName;
+       private String tmpDirName;
+
+       protected void setUp() throws Exception {
+               super.setUp();
+               dirName = System.getProperty("HSSF.testdata.path");
+               tmpDirName = System.getProperty("java.io.tmpdir");
+       }
+       
+       /**
+        * An odd problem with evaluateFormulaCell giving the
+        *  right values when file is opened, but changes
+        *  to the source data in some versions of excel 
+        *  doesn't cause them to be updated. However, other
+        *  versions of excel, and gnumeric, work just fine
+        * WARNING - tedious bug where you actually have to
+        *  open up excel
+        */
+       public void test44636() throws Exception {
+               // Open the existing file, tweak one value and
+               //  re-calculate
+               FileInputStream in = new FileInputStream(new File(dirName,"44636.xls"));
+               HSSFWorkbook wb = new HSSFWorkbook(in);
+               HSSFSheet sheet = wb.getSheetAt (0);
+               HSSFRow row = sheet.getRow (0);
+               
+               row.getCell((short)0).setCellValue(4.2);
+               row.getCell((short)2).setCellValue(25);
+               
+               HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
+               assertEquals(4.2*25, row.getCell((short)3).getNumericCellValue(), 0.0001);
+               
+               // Save
+               File existing = new File(tmpDirName,"44636-existing.xls");
+               FileOutputStream out = new FileOutputStream(existing);
+               wb.write(out);
+               out.close();
+               System.err.println("Existing file for bug #44636 written to " + existing.toString());
+               
+               
+               // Now, do a new file from scratch
+               wb = new HSSFWorkbook();
+               sheet = wb.createSheet();
+               
+               row = sheet.createRow(0);
+               row.createCell((short)0).setCellValue(1.2);
+               row.createCell((short)1).setCellValue(4.2);
+               
+               row = sheet.createRow(1);
+               row.createCell((short)0).setCellFormula("SUM(A1:B1)");
+               
+               HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
+               assertEquals(5.4, row.getCell((short)0).getNumericCellValue(), 0.0001);
+                               
+               // Save
+               File scratch = new File(tmpDirName,"44636-scratch.xls");
+               out = new FileOutputStream(scratch);
+               wb.write(out);
+               out.close();
+               System.err.println("New file for bug #44636 written to " + scratch.toString());
+       }
+}
diff --git a/src/testcases/org/apache/poi/hssf/data/41546.xls b/src/testcases/org/apache/poi/hssf/data/41546.xls
new file mode 100644 (file)
index 0000000..1332f3e
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/41546.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/43251.xls b/src/testcases/org/apache/poi/hssf/data/43251.xls
new file mode 100644 (file)
index 0000000..abc476a
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/43251.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44593.xls b/src/testcases/org/apache/poi/hssf/data/44593.xls
new file mode 100644 (file)
index 0000000..84d1311
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44593.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44636.xls b/src/testcases/org/apache/poi/hssf/data/44636.xls
new file mode 100644 (file)
index 0000000..3108a62
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44636.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44643.xls b/src/testcases/org/apache/poi/hssf/data/44643.xls
new file mode 100644 (file)
index 0000000..7ae5071
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44643.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/Bug44593.xls b/src/testcases/org/apache/poi/hssf/data/Bug44593.xls
deleted file mode 100644 (file)
index 84d1311..0000000
Binary files a/src/testcases/org/apache/poi/hssf/data/Bug44593.xls and /dev/null differ
diff --git a/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java b/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java
new file mode 100644 (file)
index 0000000..c3e5684
--- /dev/null
@@ -0,0 +1,145 @@
+/* ====================================================================
+   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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.cf.CellRange;
+
+/**
+ * Tests the serialization and deserialization of the TestCFHeaderRecord
+ * class works correctly.  
+ *
+ * @author Dmitriy Kumshayev 
+ */
+public class TestCFHeaderRecord
+        extends TestCase
+{
+
+    public TestCFHeaderRecord(String name)
+    {
+        super(name);
+    }
+
+    public void testCreateCFHeaderRecord () 
+    {
+        CFHeaderRecord record = new CFHeaderRecord();
+        List ranges = new ArrayList();
+        ranges.add(new CellRange(0,-1,(short)5,(short)5));
+        ranges.add(new CellRange(0,-1,(short)6,(short)6));
+        ranges.add(new CellRange(0,1,(short)0,(short)1));
+        ranges.add(new CellRange(0,1,(short)2,(short)3));
+        ranges.add(new CellRange(2,3,(short)0,(short)1));
+        ranges.add(new CellRange(2,3,(short)2,(short)3));
+        record.setCellRanges(ranges);
+        ranges = record.getCellRanges();
+        assertEquals(6,ranges.size());
+        CellRange enclosingCellRange = record.getEnclosingCellRange();
+        assertEquals(0, enclosingCellRange.getFirstRow());
+        assertEquals(-1, enclosingCellRange.getLastRow());
+        assertEquals(0, enclosingCellRange.getFirstColumn());
+        assertEquals(6, enclosingCellRange.getLastColumn());
+        record.setNeedRecalculation(true);
+        assertTrue(record.getNeedRecalculation());
+        record.setNeedRecalculation(false);
+        assertFalse(record.getNeedRecalculation());
+    }
+    
+    public void testSerialization() {
+       byte[] recordData = new byte[]
+       {
+                       (byte)0x03, (byte)0x00,
+                       (byte)0x01,     (byte)0x00,
+                       
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+                       
+                       (byte)0x04,     (byte)0x00,
+                       
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x01,     (byte)0x00,
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x01,     (byte)0x00,
+                       
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x01,     (byte)0x00,
+                       (byte)0x02,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+                       
+                       (byte)0x02,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+                       (byte)0x00,     (byte)0x00,
+                       (byte)0x01,     (byte)0x00,
+                       
+                       (byte)0x02,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+                       (byte)0x02,     (byte)0x00,
+                       (byte)0x03,     (byte)0x00,
+       };
+       
+       CFHeaderRecord record = new CFHeaderRecord(new TestcaseRecordInputStream(CFHeaderRecord.sid, (short)recordData.length, recordData));
+               
+       assertEquals("#CFRULES", 3, record.getNumberOfConditionalFormats());
+       assertTrue(record.getNeedRecalculation());
+        CellRange enclosingCellRange = record.getEnclosingCellRange();
+        assertEquals(0, enclosingCellRange.getFirstRow());
+        assertEquals(3, enclosingCellRange.getLastRow());
+        assertEquals(0, enclosingCellRange.getFirstColumn());
+        assertEquals(3, enclosingCellRange.getLastColumn());
+        List ranges = record.getCellRanges();
+        assertEquals(0, ((CellRange)ranges.get(0)).getFirstRow());
+        assertEquals(1, ((CellRange)ranges.get(0)).getLastRow());
+        assertEquals(0, ((CellRange)ranges.get(0)).getFirstColumn());
+        assertEquals(1, ((CellRange)ranges.get(0)).getLastColumn());
+        assertEquals(0, ((CellRange)ranges.get(1)).getFirstRow());
+        assertEquals(1, ((CellRange)ranges.get(1)).getLastRow());
+        assertEquals(2, ((CellRange)ranges.get(1)).getFirstColumn());
+        assertEquals(3, ((CellRange)ranges.get(1)).getLastColumn());
+        assertEquals(2, ((CellRange)ranges.get(2)).getFirstRow());
+        assertEquals(3, ((CellRange)ranges.get(2)).getLastRow());
+        assertEquals(0, ((CellRange)ranges.get(2)).getFirstColumn());
+        assertEquals(1, ((CellRange)ranges.get(2)).getLastColumn());
+        assertEquals(2, ((CellRange)ranges.get(3)).getFirstRow());
+        assertEquals(3, ((CellRange)ranges.get(3)).getLastRow());
+        assertEquals(2, ((CellRange)ranges.get(3)).getFirstColumn());
+        assertEquals(3, ((CellRange)ranges.get(3)).getLastColumn());
+        assertEquals(recordData.length+4, record.getRecordSize());
+       
+               byte[] output = record.serialize();
+               
+               assertEquals("Output size", recordData.length+4, output.length); //includes sid+recordlength
+               
+               for (int i = 0; i < recordData.length;i++) 
+               {
+                       assertEquals("CFHeaderRecord doesn't match", recordData[i], output[i+4]);
+               }
+    }
+    
+    
+    public static void main(String[] ignored_args)
+       {
+               System.out.println("Testing org.apache.poi.hssf.record.CFHeaderRecord");
+               junit.textui.TestRunner.run(TestCFHeaderRecord.class);
+       }
+    
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java b/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java
new file mode 100644 (file)
index 0000000..77731d7
--- /dev/null
@@ -0,0 +1,296 @@
+/* ====================================================================
+   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;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.cf.BorderFormatting;
+import org.apache.poi.hssf.record.cf.FontFormatting;
+import org.apache.poi.hssf.record.cf.PatternFormatting;
+import org.apache.poi.hssf.util.HSSFColor;
+
+/**
+ * Tests the serialization and deserialization of the TestCFRuleRecord
+ * class works correctly.  
+ *
+ * @author Dmitriy Kumshayev 
+ */
+public class TestCFRuleRecord
+        extends TestCase
+{
+
+    public TestCFRuleRecord(String name)
+    {
+        super(name);
+    }
+
+    public void testCreateCFRuleRecord () 
+    {
+       CFRuleRecord record = new CFRuleRecord();
+       testCFRuleRecord(record);
+       
+       // Serialize
+       byte [] serializedRecord = record.serialize();
+
+       // Strip header
+       byte [] recordData = new byte[serializedRecord.length-4];
+               System.arraycopy(serializedRecord, 4, recordData, 0, recordData.length);
+               
+               // Deserialize
+       record = new CFRuleRecord(new TestcaseRecordInputStream(CFRuleRecord.sid, (short)recordData.length, recordData));
+       
+       // Serialize again
+               byte[] output = record.serialize();
+       
+               // Compare
+               assertEquals("Output size", recordData.length+4, output.length); //includes sid+recordlength
+               
+               for (int i = 0; i < recordData.length;i++) 
+               {
+                       assertEquals("CFRuleRecord doesn't match", recordData[i], output[i+4]);
+               }
+    }
+
+       private void testCFRuleRecord(CFRuleRecord record)
+       {
+               FontFormatting fontFormatting = new FontFormatting();
+       testFontFormattingAccessors(fontFormatting);
+       assertFalse(record.containsFontFormattingBlock());
+       record.setFontFormatting(fontFormatting);
+       assertTrue(record.containsFontFormattingBlock());
+       
+       BorderFormatting borderFormatting = new BorderFormatting();
+       testBorderFormattingAccessors(borderFormatting);
+       assertFalse(record.containsBorderFormattingBlock());
+       record.setBorderFormatting(borderFormatting);
+       assertTrue(record.containsBorderFormattingBlock());
+       
+       assertFalse(record.isLeftBorderModified());
+       record.setLeftBorderModified(true);
+       assertTrue(record.isLeftBorderModified());
+       
+       assertFalse(record.isRightBorderModified());
+       record.setRightBorderModified(true);
+       assertTrue(record.isRightBorderModified());
+       
+       assertFalse(record.isTopBorderModified());
+       record.setTopBorderModified(true);
+       assertTrue(record.isTopBorderModified());
+       
+       assertFalse(record.isBottomBorderModified());
+       record.setBottomBorderModified(true);
+       assertTrue(record.isBottomBorderModified());
+       
+       assertFalse(record.isTopLeftBottomRightBorderModified());
+       record.setTopLeftBottomRightBorderModified(true);
+       assertTrue(record.isTopLeftBottomRightBorderModified());
+       
+       assertFalse(record.isBottomLeftTopRightBorderModified());
+       record.setBottomLeftTopRightBorderModified(true);
+       assertTrue(record.isBottomLeftTopRightBorderModified());
+       
+       
+       PatternFormatting patternFormatting = new PatternFormatting();
+       testPatternFormattingAccessors(patternFormatting);
+       assertFalse(record.containsPatternFormattingBlock());
+       record.setPatternFormatting(patternFormatting);
+       assertTrue(record.containsPatternFormattingBlock());
+       
+       assertFalse(record.isPatternBackgroundColorModified());
+       record.setPatternBackgroundColorModified(true);
+       assertTrue(record.isPatternBackgroundColorModified());
+       
+       assertFalse(record.isPatternColorModified());
+       record.setPatternColorModified(true);
+       assertTrue(record.isPatternColorModified());
+
+       assertFalse(record.isPatternStyleModified());
+       record.setPatternStyleModified(true);
+       assertTrue(record.isPatternStyleModified());
+       }
+
+       private void testPatternFormattingAccessors(PatternFormatting patternFormatting)
+       {
+               patternFormatting.setFillBackgroundColor(HSSFColor.GREEN.index);
+               assertEquals(HSSFColor.GREEN.index,patternFormatting.getFillBackgroundColor());
+
+               patternFormatting.setFillForegroundColor(HSSFColor.INDIGO.index);
+               assertEquals(HSSFColor.INDIGO.index,patternFormatting.getFillForegroundColor());
+               
+               patternFormatting.setFillPattern(PatternFormatting.DIAMONDS);
+               assertEquals(PatternFormatting.DIAMONDS,patternFormatting.getFillPattern());
+               
+       }
+       
+       private void testBorderFormattingAccessors(BorderFormatting borderFormatting)
+       {
+               borderFormatting.setBackwardDiagonalOn(false);
+               assertFalse(borderFormatting.isBackwardDiagonalOn());
+               borderFormatting.setBackwardDiagonalOn(true);
+               assertTrue(borderFormatting.isBackwardDiagonalOn());
+               
+               borderFormatting.setBorderBottom(BorderFormatting.BORDER_DOTTED);
+               assertEquals(BorderFormatting.BORDER_DOTTED, borderFormatting.getBorderBottom());
+               
+               borderFormatting.setBorderDiagonal(BorderFormatting.BORDER_MEDIUM);
+               assertEquals(BorderFormatting.BORDER_MEDIUM, borderFormatting.getBorderDiagonal());
+               
+               borderFormatting.setBorderLeft(BorderFormatting.BORDER_MEDIUM_DASH_DOT_DOT);
+               assertEquals(BorderFormatting.BORDER_MEDIUM_DASH_DOT_DOT, borderFormatting.getBorderLeft());
+
+               borderFormatting.setBorderRight(BorderFormatting.BORDER_MEDIUM_DASHED);
+               assertEquals(BorderFormatting.BORDER_MEDIUM_DASHED, borderFormatting.getBorderRight());
+
+               borderFormatting.setBorderTop(BorderFormatting.BORDER_HAIR);
+               assertEquals(BorderFormatting.BORDER_HAIR, borderFormatting.getBorderTop());
+
+               borderFormatting.setBottomBorderColor(HSSFColor.AQUA.index);
+               assertEquals(HSSFColor.AQUA.index, borderFormatting.getBottomBorderColor());
+
+               borderFormatting.setDiagonalBorderColor(HSSFColor.RED.index);
+               assertEquals(HSSFColor.RED.index, borderFormatting.getDiagonalBorderColor());
+
+               assertFalse(borderFormatting.isForwardDiagonalOn());
+               borderFormatting.setForwardDiagonalOn(true);
+               assertTrue(borderFormatting.isForwardDiagonalOn());
+
+               borderFormatting.setLeftBorderColor(HSSFColor.BLACK.index);
+               assertEquals(HSSFColor.BLACK.index, borderFormatting.getLeftBorderColor());
+
+               borderFormatting.setRightBorderColor(HSSFColor.BLUE.index);
+               assertEquals(HSSFColor.BLUE.index, borderFormatting.getRightBorderColor());
+
+               borderFormatting.setTopBorderColor(HSSFColor.GOLD.index);
+               assertEquals(HSSFColor.GOLD.index, borderFormatting.getTopBorderColor());
+       }
+    
+    
+       private void testFontFormattingAccessors(FontFormatting fontFormatting)
+       {
+               // Check for defaults
+       assertFalse(fontFormatting.isEscapementTypeModified());
+       assertFalse(fontFormatting.isFontCancellationModified());
+       assertFalse(fontFormatting.isFontCondenseModified());
+       assertFalse(fontFormatting.isFontOutlineModified());
+       assertFalse(fontFormatting.isFontShadowModified());
+       assertFalse(fontFormatting.isFontStyleModified());
+       assertFalse(fontFormatting.isUnderlineTypeModified());
+       
+       assertFalse(fontFormatting.isBold());
+       assertFalse(fontFormatting.isCondenseOn());
+       assertFalse(fontFormatting.isItalic());
+       assertFalse(fontFormatting.isOutlineOn());
+       assertFalse(fontFormatting.isShadowOn());
+       assertFalse(fontFormatting.isStruckout());
+
+       assertEquals(0, fontFormatting.getEscapementType());
+       assertEquals(-1, fontFormatting.getFontColorIndex());
+       assertEquals(-1, fontFormatting.getFontHeight());
+       assertEquals(400, fontFormatting.getFontWeight());
+       assertEquals(0, fontFormatting.getUnderlineType());
+       
+       fontFormatting.setBold(true);
+       assertTrue(fontFormatting.isBold());
+       fontFormatting.setBold(false);
+       assertFalse(fontFormatting.isBold());
+       
+       fontFormatting.setCondense(true);
+       assertTrue(fontFormatting.isCondenseOn());
+       fontFormatting.setCondense(false);
+       assertFalse(fontFormatting.isCondenseOn());
+       
+       fontFormatting.setEscapementType(FontFormatting.SS_SUB);
+       assertEquals(FontFormatting.SS_SUB, fontFormatting.getEscapementType());
+       fontFormatting.setEscapementType(FontFormatting.SS_SUPER);
+       assertEquals(FontFormatting.SS_SUPER, fontFormatting.getEscapementType());
+       fontFormatting.setEscapementType(FontFormatting.SS_NONE);
+       assertEquals(FontFormatting.SS_NONE, fontFormatting.getEscapementType());
+       
+       fontFormatting.setEscapementTypeModified(false);
+       assertFalse(fontFormatting.isEscapementTypeModified());
+       fontFormatting.setEscapementTypeModified(true);
+       assertTrue(fontFormatting.isEscapementTypeModified());
+
+       fontFormatting.setFontCancellationModified(false);
+       assertFalse(fontFormatting.isFontCancellationModified());
+       fontFormatting.setFontCancellationModified(true);
+       assertTrue(fontFormatting.isFontCancellationModified());
+       
+       fontFormatting.setFontColorIndex((short)10);
+       assertEquals(10,fontFormatting.getFontColorIndex());
+
+       fontFormatting.setFontCondenseModified(false);
+       assertFalse(fontFormatting.isFontCondenseModified());
+       fontFormatting.setFontCondenseModified(true);
+       assertTrue(fontFormatting.isFontCondenseModified());
+       
+       fontFormatting.setFontHeight((short)100);
+       assertEquals(100,fontFormatting.getFontHeight());
+       
+       fontFormatting.setFontOutlineModified(false);
+       assertFalse(fontFormatting.isFontOutlineModified());
+       fontFormatting.setFontOutlineModified(true);
+       assertTrue(fontFormatting.isFontOutlineModified());
+
+       fontFormatting.setFontShadowModified(false);
+       assertFalse(fontFormatting.isFontShadowModified());
+       fontFormatting.setFontShadowModified(true);
+       assertTrue(fontFormatting.isFontShadowModified());
+
+       fontFormatting.setFontStyleModified(false);
+       assertFalse(fontFormatting.isFontStyleModified());
+       fontFormatting.setFontStyleModified(true);
+       assertTrue(fontFormatting.isFontStyleModified());
+
+       fontFormatting.setItalic(false);
+       assertFalse(fontFormatting.isItalic());
+       fontFormatting.setItalic(true);
+       assertTrue(fontFormatting.isItalic());
+
+       fontFormatting.setOutline(false);
+       assertFalse(fontFormatting.isOutlineOn());
+       fontFormatting.setOutline(true);
+       assertTrue(fontFormatting.isOutlineOn());
+
+       fontFormatting.setShadow(false);
+       assertFalse(fontFormatting.isShadowOn());
+       fontFormatting.setShadow(true);
+       assertTrue(fontFormatting.isShadowOn());
+       
+       fontFormatting.setStrikeout(false);
+       assertFalse(fontFormatting.isStruckout());
+       fontFormatting.setStrikeout(true);
+       assertTrue(fontFormatting.isStruckout());
+       
+       fontFormatting.setUnderlineType(FontFormatting.U_DOUBLE_ACCOUNTING);
+       assertEquals(FontFormatting.U_DOUBLE_ACCOUNTING, fontFormatting.getUnderlineType());
+
+       fontFormatting.setUnderlineTypeModified(false);
+       assertFalse(fontFormatting.isUnderlineTypeModified());
+       fontFormatting.setUnderlineTypeModified(true);
+       assertTrue(fontFormatting.isUnderlineTypeModified());
+       }
+    
+    
+    public static void main(String[] ignored_args)
+       {
+               System.out.println("Testing org.apache.poi.hssf.record.CFRuleRecord");
+               junit.textui.TestRunner.run(TestCFRuleRecord.class);
+       }
+    
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java
new file mode 100644 (file)
index 0000000..332c992
--- /dev/null
@@ -0,0 +1,111 @@
+/* ====================================================================
+   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.aggregates;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.CFHeaderRecord;
+import org.apache.poi.hssf.record.CFRuleRecord;
+import org.apache.poi.hssf.record.RecordFactory;
+import org.apache.poi.hssf.record.cf.CellRange;
+
+/**
+ * Tests the serialization and deserialization of the CFRecordsAggregate
+ * class works correctly.  
+ *
+ * @author Dmitriy Kumshayev 
+ */
+public class TestCFRecordsAggregate
+        extends TestCase
+{
+
+    public TestCFRecordsAggregate(String name)
+    {
+        super(name);
+    }
+
+    public void testCFRecordsAggregate() 
+    {
+       CFRecordsAggregate record = new CFRecordsAggregate();
+       List recs = new ArrayList();
+       CFHeaderRecord header = new CFHeaderRecord();
+       CFRuleRecord rule1 = new CFRuleRecord();
+       CFRuleRecord rule2 = new CFRuleRecord();
+       CFRuleRecord rule3 = new CFRuleRecord();
+       header.setNumberOfConditionalFormats(3);
+       CellRange range1 = new CellRange(0,1,(short)0,(short)0);
+       CellRange range2 = new CellRange(0,1,(short)2,(short)2);
+       List cellRanges = new ArrayList();
+       cellRanges.add(range1);
+       cellRanges.add(range2);
+       header.setCellRanges(cellRanges);
+       recs.add(header);
+       recs.add(rule1);
+       recs.add(rule2);
+       recs.add(rule3);
+       record = CFRecordsAggregate.createCFAggregate(recs, 0);
+       
+       // Serialize
+       byte [] serializedRecord = record.serialize();
+       InputStream in = new ByteArrayInputStream(serializedRecord);
+       
+       //Parse
+       recs = RecordFactory.createRecords(in);
+       
+       // Verify
+       assertNotNull(recs);
+       assertEquals(4, recs.size());
+       
+       header = (CFHeaderRecord)recs.get(0);
+       rule1 = (CFRuleRecord)recs.get(1);
+       rule2 = (CFRuleRecord)recs.get(2);
+       rule3 = (CFRuleRecord)recs.get(3);
+       cellRanges = header.getCellRanges();
+       
+       assertEquals(2, cellRanges.size());
+       assertEquals(3, header.getNumberOfConditionalFormats());
+       
+       record = CFRecordsAggregate.createCFAggregate(recs, 0);
+       
+       record = record.cloneCFAggregate();
+       
+       assertNotNull(record.getHeader());
+       assertEquals(3,record.getRules().size());
+       
+       header = record.getHeader();
+       rule1 = (CFRuleRecord)record.getRules().get(0);
+       rule2 = (CFRuleRecord)record.getRules().get(1);
+       rule3 = (CFRuleRecord)record.getRules().get(2);
+       cellRanges = header.getCellRanges();
+       
+       assertEquals(2, cellRanges.size());
+       assertEquals(3, header.getNumberOfConditionalFormats());
+    }
+
+    public static void main(String[] ignored_args)
+       {
+               System.out.println("Testing org.apache.poi.hssf.record.aggregates.CFRecordsAggregate");
+               junit.textui.TestRunner.run(TestCFRecordsAggregate.class);
+       }
+    
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java b/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java
new file mode 100644 (file)
index 0000000..f9857fb
--- /dev/null
@@ -0,0 +1,139 @@
+/* ====================================================================
+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.cf;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests CellRange operations.
+ */
+public class TestCellRange extends TestCase
+{
+       private static final CellRange biggest     = new CellRange(0, -1,(short) 0,(short)-1);
+       private static final CellRange tenthColumn = new CellRange(0, -1,(short)10,(short)10);
+       private static final CellRange tenthRow    = new CellRange(10,10,(short) 0,(short)-1);
+       private static final CellRange box10x10    = new CellRange(0, 10,(short) 0,(short)10);
+       private static final CellRange box9x9      = new CellRange(0,  9,(short) 0,(short) 9);
+       private static final CellRange box10to20c  = new CellRange(0, 10,(short)10,(short)20);
+       private static final CellRange oneCell     = new CellRange(10,10,(short)10,(short)10);
+
+       boolean [][] contanis = new boolean[][]
+    {
+                       //           biggest, tenthColumn, tenthRow, box10x10, box9x9, box10to20c, oneCell
+  /*biggest    */ new boolean[]{true,       true ,    true ,    true ,  true ,      true ,  true},     
+  /*tenthColumn*/ new boolean[]{false,      true ,    false,    false,  false,      false,  true},     
+  /*tenthRow   */ new boolean[]{false,      false,    true ,    false,  false,      false,  true},     
+  /*box10x10   */ new boolean[]{false,      false,    false,    true ,  true ,      false,  true},     
+  /*box9x9     */ new boolean[]{false,      false,    false,    false,  true ,      false, false},     
+  /*box10to20c */ new boolean[]{false,      false,    false,    false,  false,      true ,  true},     
+  /*oneCell    */ new boolean[]{false,      false,    false,    false,  false,      false,  true},     
+     } ;
+       
+       
+       public void testContainsMethod()
+       {
+               CellRange [] ranges = new CellRange[]{biggest,tenthColumn,tenthRow,box10x10,box9x9,box10to20c,oneCell};
+               testContainsMethod(contanis,ranges);
+       }
+       
+       private void testContainsMethod(boolean[][]contains,CellRange[] ranges)
+       {
+               for(int i=0; i!=ranges.length;i++)
+               {
+                       for(int j=0; j!=ranges.length;j++)
+                       {
+                               assertEquals("("+i+","+j+"): ",contains[i][j],ranges[i].contains(ranges[j]));
+                       }
+               }
+       }
+       
+       private static final CellRange col1     = new CellRange(0, -1,(short) 1,(short)1);
+       private static final CellRange col2     = new CellRange(0, -1,(short) 2,(short)2);
+       private static final CellRange row1     = new CellRange(1,  1,(short) 0,(short)-1);
+       private static final CellRange row2     = new CellRange(2,  2,(short) 0,(short)-1);
+
+       private static final CellRange box0     = new CellRange( 0, 2,(short) 0,(short)2);
+       private static final CellRange box1     = new CellRange( 0, 1,(short) 0,(short)1);
+       private static final CellRange box2     = new CellRange( 0, 1,(short) 2,(short)3);
+       private static final CellRange box3     = new CellRange( 2, 3,(short) 0,(short)1);
+       private static final CellRange box4     = new CellRange( 2, 3,(short) 2,(short)3);
+       private static final CellRange box5     = new CellRange( 1, 3,(short) 1,(short)3);
+
+       public void testHasSharedBorderMethod()
+       {
+               assertFalse(col1.hasSharedBorder(col1));
+               assertFalse(col2.hasSharedBorder(col2));
+               assertTrue(col1.hasSharedBorder(col2));
+               assertTrue(col2.hasSharedBorder(col1));
+
+               assertFalse(row1.hasSharedBorder(row1));
+               assertFalse(row2.hasSharedBorder(row2));
+               assertTrue(row1.hasSharedBorder(row2));
+               assertTrue(row2.hasSharedBorder(row1));
+               
+               assertFalse(row1.hasSharedBorder(col1));
+               assertFalse(row1.hasSharedBorder(col2));
+               assertFalse(col1.hasSharedBorder(row1));
+               assertFalse(col2.hasSharedBorder(row1));
+               assertFalse(row2.hasSharedBorder(col1));
+               assertFalse(row2.hasSharedBorder(col2));
+               assertFalse(col1.hasSharedBorder(row2));
+               assertFalse(col2.hasSharedBorder(row2));
+               assertTrue(col2.hasSharedBorder(col1));
+               
+               assertFalse(box1.hasSharedBorder(box1));
+               assertTrue(box1.hasSharedBorder(box2));
+               assertTrue(box1.hasSharedBorder(box3));
+               assertFalse(box1.hasSharedBorder(box4));
+               
+               assertTrue(box2.hasSharedBorder(box1));
+               assertFalse(box2.hasSharedBorder(box2));
+               assertFalse(box2.hasSharedBorder(box3));
+               assertTrue(box2.hasSharedBorder(box4));
+               
+               assertTrue(box3.hasSharedBorder(box1));
+               assertFalse(box3.hasSharedBorder(box2));
+               assertFalse(box3.hasSharedBorder(box3));
+               assertTrue(box3.hasSharedBorder(box4));
+               
+               assertFalse(box4.hasSharedBorder(box1));
+               assertTrue(box4.hasSharedBorder(box2));
+               assertTrue(box4.hasSharedBorder(box3));
+               assertFalse(box4.hasSharedBorder(box4));
+       }
+       
+       public void testIntersectMethod()
+       {
+               assertEquals( CellRange.OVERLAP,box0.intersect(box5));
+               assertEquals( CellRange.OVERLAP,box5.intersect(box0));
+               assertEquals(CellRange.NO_INTERSECTION,box1.intersect(box4));
+               assertEquals(CellRange.NO_INTERSECTION,box4.intersect(box1));
+               assertEquals(CellRange.NO_INTERSECTION,box2.intersect(box3));
+               assertEquals(CellRange.NO_INTERSECTION,box3.intersect(box2));
+               assertEquals(CellRange.INSIDE,box0.intersect(box1));
+               assertEquals(CellRange.INSIDE,box0.intersect(box0));
+               assertEquals(CellRange.ENCLOSES,box1.intersect(box0));
+               assertEquals(CellRange.INSIDE,tenthColumn.intersect(oneCell));
+               assertEquals(CellRange.ENCLOSES,oneCell.intersect(tenthColumn));
+               assertEquals(CellRange.OVERLAP,tenthColumn.intersect(tenthRow));
+               assertEquals(CellRange.OVERLAP,tenthRow.intersect(tenthColumn));
+               assertEquals(CellRange.INSIDE,tenthColumn.intersect(tenthColumn));
+               assertEquals(CellRange.INSIDE,tenthRow.intersect(tenthRow));
+       }
+       
+}
index f06f591c425241aaa0dc70bb12251b53e73b8324..05ba29d09eab6aa01462ce1d21f817d11b77d534 100644 (file)
@@ -1034,6 +1034,28 @@ extends TestCase {
         assertTrue("No Exceptions while reading file", true);
     }
 
+    /**
+     * Bug 41546: Constructing HSSFWorkbook is failed,
+     *  Unknown Ptg in Formula: 0x1a (26)
+     */
+    public void test41546() throws Exception {
+        FileInputStream in = new FileInputStream(new File(cwd, "41546.xls"));
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        in.close();
+
+        assertTrue("No Exceptions while reading file", true);
+        assertEquals(1, wb.getNumberOfSheets());
+
+        //serialize and read again
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        wb.write(out);
+        out.close();
+
+        wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+        assertTrue("No Exceptions while reading file", true);
+        assertEquals(1, wb.getNumberOfSheets());
+    }
+
     /**
      * Bug 42564: Some files from Access were giving a RecordFormatException
      *  when reading the BOFRecord
@@ -1114,6 +1136,20 @@ extends TestCase {
         //assertEquals("=CHOOSE(2,A2,A3,A4)", c2.getCellFormula());
     }
     
+    /**
+     * Something up with the FileSharingRecord
+     */
+    public void test43251() throws Exception {
+        FileInputStream in = new FileInputStream(new File(cwd, "43251.xls"));
+        
+        // Used to blow up with an IllegalArgumentException
+        //  when creating a FileSharingRecord
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        in.close();
+        
+        assertEquals(1, wb.getNumberOfSheets());
+    }
+    
     /**
      * Crystal reports generates files with short 
      *  StyleRecords, which is against the spec
@@ -1157,7 +1193,7 @@ extends TestCase {
      *  probably due to dropdowns
      */
     public void test44593() throws Exception {
-        FileInputStream in = new FileInputStream(new File(cwd, "Bug44593.xls"));
+        FileInputStream in = new FileInputStream(new File(cwd, "44593.xls"));
         
         // Used to blow up with an IllegalArgumentException
         //  when creating a DVRecord
@@ -1168,6 +1204,20 @@ extends TestCase {
         
         assertEquals(2, wb.getNumberOfSheets());
     }
+    
+    /**
+     * Used to give problems due to trying to read a zero
+     *  length string, but that's now properly handled
+     */
+    public void test44643() throws Exception {
+        FileInputStream in = new FileInputStream(new File(cwd, "44643.xls"));
+        
+        // Used to blow up with an IllegalArgumentException
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        in.close();
+        
+        assertEquals(1, wb.getNumberOfSheets());
+    }
 }
 
 
index 4c84f04b71a884d710e863f547fd7b8031bc040b..73ddcad54312b376929c13a6f54916270066a748 100644 (file)
@@ -36,23 +36,23 @@ import junit.framework.*;
 public class TestRawDataBlock
     extends TestCase
 {
+       static {
+        // We always want to use our own
+        //  logger
+        System.setProperty(
+                       "org.apache.poi.util.POILogger",
+                       "org.apache.poi.util.DummyPOILogger"
+        );
+       }
 
     /**
      * Constructor TestRawDataBlock
      *
      * @param name
      */
-
     public TestRawDataBlock(String name)
     {
         super(name);
-        
-        // We always want to use our own
-        //  logger
-        System.setProperty(
-                       "org.apache.poi.util.POILogger",
-                       "org.apache.poi.util.DummyPOILogger"
-        );
     }
 
     /**
index d151029762089e1634a46b4ff9fadddc9643bc93..0f65c0e8a4421ac4f4830f1616d374ca8c674888 100644 (file)
@@ -36,23 +36,23 @@ import junit.framework.*;
 public class TestRawDataBlockList
     extends TestCase
 {
+       static {
+        // We always want to use our own
+        //  logger
+        System.setProperty(
+                       "org.apache.poi.util.POILogger",
+                       "org.apache.poi.util.DummyPOILogger"
+        );
+       }
 
     /**
      * Constructor TestRawDataBlockList
      *
      * @param name
      */
-
     public TestRawDataBlockList(String name)
     {
         super(name);
-        
-        // We always want to use our own
-        //  logger
-        System.setProperty(
-                       "org.apache.poi.util.POILogger",
-                       "org.apache.poi.util.DummyPOILogger"
-        );
     }
 
     /**
@@ -60,7 +60,6 @@ public class TestRawDataBlockList
      *
      * @exception IOException
      */
-
     public void testNormalConstructor()
         throws IOException
     {