You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2008/04/05 15:07:25 UTC

svn commit: r645088 [1/3] - in /poi/branches/ooxml: ./ src/documentation/content/xdocs/ src/java/org/apache/poi/hssf/model/ src/java/org/apache/poi/hssf/record/ src/java/org/apache/poi/hssf/record/aggregates/ src/java/org/apache/poi/hssf/record/cf/ src...

Author: nick
Date: Sat Apr  5 06:07:22 2008
New Revision: 645088

URL: http://svn.apache.org/viewvc?rev=645088&view=rev
Log:
Merged revisions 642878-642946 via svnmerge from 
https://svn.apache.org/repos/asf/poi/trunk

........
  r642878 | josh | 2008-03-31 06:10:35 +0100 (Mon, 31 Mar 2008) | 1 line
  
  More work on Conditional Formatting (bug 30311) junit and fixes from Dmitriy. Some other clean-up.
........
  r642880 | josh | 2008-03-31 06:19:00 +0100 (Mon, 31 Mar 2008) | 1 line
  
  removed incorrect test case methods
........
  r642891 | josh | 2008-03-31 06:56:11 +0100 (Mon, 31 Mar 2008) | 1 line
  
  silenced noisy tests
........
  r642904 | josh | 2008-03-31 07:55:04 +0100 (Mon, 31 Mar 2008) | 1 line
  
  changes/status for #44675, #44695, #44691
........
  r642946 | yegor | 2008-03-31 10:58:27 +0100 (Mon, 31 Mar 2008) | 1 line
  
  Implement Sheet.removeShape(Shape shape) in HSLF
........

Added:
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/aggregates/AllRecordAggregateTests.java
      - copied unchanged from r642946, poi/trunk/src/testcases/org/apache/poi/hssf/record/aggregates/AllRecordAggregateTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConfditionalFormatting.java
      - copied unchanged from r642946, poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFConfditionalFormatting.java
Modified:
    poi/branches/ooxml/   (props changed)
    poi/branches/ooxml/src/documentation/content/xdocs/changes.xml
    poi/branches/ooxml/src/documentation/content/xdocs/status.xml
    poi/branches/ooxml/src/java/org/apache/poi/hssf/model/FormulaParser.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/CellRange.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFBorderFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
    poi/branches/ooxml/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
    poi/branches/ooxml/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/cf/TestCellRange.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java

Propchange: poi/branches/ooxml/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Sat Apr  5 06:07:22 2008
@@ -1 +1 @@
-/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576
+/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576,642878-642946

Modified: poi/branches/ooxml/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/documentation/content/xdocs/changes.xml?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/changes.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/changes.xml Sat Apr  5 06:07:22 2008
@@ -26,6 +26,7 @@
         <!-- in strict alphabetical order -->
         <person id="AO" name="Andrew C. Oliver" email="acoliver2@users.sourceforge.net"/>
         <person id="GJS" name="Glen Stampoultzis" email="user@poi.apache.org"/>
+        <person id="JM" name="Josh Micich" email="josh@apache.org"/>
         <person id="MJ" name="Marc Johnson" email="mjohnson@apache.org"/>
         <person id="NKB" name="Nicola Ken Barozzi" email="barozzi@nicolaken.com"/>
         <person id="NB" name="Nick Burch" email="nick@torchbox.com"/>
@@ -36,6 +37,9 @@
 
 		<!-- Don't forget to update status.xml too! -->
         <release version="3.0.3-beta1" date="2008-04-??">
+           <action dev="POI-DEVELOPERS" type="add">Implement Sheet.removeShape(Shape shape) in HSLF</action>
+           <action dev="POI-DEVELOPERS" type="add">Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691</action>
+           <action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
            <action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action>
            <action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action>
            <action dev="POI-DEVELOPERS" type="add">44652 / 44603 - Improved handling of Pictures in Word Documents</action>

Modified: poi/branches/ooxml/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/documentation/content/xdocs/status.xml?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/status.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/status.xml Sat Apr  5 06:07:22 2008
@@ -22,6 +22,7 @@
         <!-- in strict alphabetical order -->
         <person id="AO" name="Andrew C. Oliver" email="acoliver2@users.sourceforge.net"/>
         <person id="GJS" name="Glen Stampoultzis" email="user@poi.apache.org"/>
+        <person id="JM" name="Josh Micich" email="josh@apache.org"/>
         <person id="MJ" name="Marc Johnson" email="mjohnson@apache.org"/>
         <person id="NKB" name="Nicola Ken Barozzi" email="barozzi@nicolaken.com"/>
         <person id="NB" name="Nick Burch" email="nick@torchbox.com"/>
@@ -33,6 +34,9 @@
 	<!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.0.3-beta1" date="2008-04-??">
+           <action dev="POI-DEVELOPERS" type="add">Implement Sheet.removeShape(Shape shape) in HSLF</action>
+           <action dev="POI-DEVELOPERS" type="add">Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691</action>
+           <action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
            <action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action>
            <action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action>
            <action dev="POI-DEVELOPERS" type="add">44652 / 44603 - Improved handling of Pictures in Word Documents</action>

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/model/FormulaParser.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/model/FormulaParser.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/model/FormulaParser.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/model/FormulaParser.java Sat Apr  5 06:07:22 2008
@@ -28,8 +28,6 @@
 import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 
-
-
 /**
  * This class parses a formula string into a List of tokens in RPN order.
  * Inspired by
@@ -48,11 +46,11 @@
  *  @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
  */
 public final class FormulaParser {
-    
+
     /**
      * Specific exception thrown when a supplied formula does not parse properly.<br/>
      * Primarily used by test cases when testing for specific parsing exceptions.</p>
-     *    
+     *
      */
     static final class FormulaParseException extends RuntimeException {
         // This class was given package scope until it would become clear that it is useful to

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java Sat Apr  5 06:07:22 2008
@@ -15,18 +15,10 @@
    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.hssf.util.Region;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -34,19 +26,27 @@
  * 
  * @author Dmitriy Kumshayev
  */
-public class CFHeaderRecord extends Record
+public final class CFHeaderRecord extends Record
 {
 	public static final short sid = 0x1B0;
 
+	private static final CellRange[] EMPTY_CELL_RANGE_ARRAY = { };
+
 	private int field_1_numcf;
 	private int field_2_need_recalculation;
 	private CellRange field_3_enclosing_cell_range;
-	private List field_4_cell_ranges;
+	private CellRange[] field_4_cell_ranges;
 
 	/** Creates new CFHeaderRecord */
 	public CFHeaderRecord()
 	{
-		field_4_cell_ranges = new ArrayList(5);
+		field_4_cell_ranges = EMPTY_CELL_RANGE_ARRAY;
+	}
+	public CFHeaderRecord(Region[] regions)
+	{
+		CellRange[] unmergedRanges = CellRange.convertRegionsToCellRanges(regions);
+		CellRange[] mergeCellRanges = CellRange.mergeCellRanges(unmergedRanges);
+		setCellRanges(mergeCellRanges);
 	}
 
 	public CFHeaderRecord(RecordInputStream in)
@@ -60,11 +60,12 @@
 		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);
+		CellRange[] crs = new CellRange[numCellRanges];
 		for( int i=0; i<numCellRanges; i++)
 		{
-			field_4_cell_ranges.add(new CellRange(in.readShort(),in.readShort(),in.readShort(),in.readShort()));
+			crs[i] = new CellRange(in.readShort(),in.readShort(),in.readShort(),in.readShort());
 		}
+		field_4_cell_ranges = crs;
 	}
 	
 	public int getNumberOfConditionalFormats()
@@ -101,28 +102,24 @@
 	 * modify the enclosing cell range accordingly.
 	 * @param List cellRanges - list of CellRange objects
 	 */
-	public void setCellRanges( List cellRanges )
+	public void setCellRanges(CellRange[] cellRanges)
 	{
-		field_4_cell_ranges.clear();
-		if(cellRanges!=null)
+		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));
-			}
+			throw new IllegalArgumentException("cellRanges must not be null");
 		}
-	}
-
-	private void recalculateEnclosingRange(CellRange cellRange)
-	{
-		field_3_enclosing_cell_range = cellRange.createEnclosingCellRange(field_3_enclosing_cell_range);
+		field_4_cell_ranges = (CellRange[]) cellRanges.clone();
+		CellRange enclosingRange = null;
+		for (int i = 0; i < cellRanges.length; i++)
+		{
+			enclosingRange = cellRanges[i].createEnclosingCellRange(enclosingRange);
+		}
+		field_3_enclosing_cell_range=enclosingRange;
 	}
 	
-	public List getCellRanges()
+	public CellRange[] getCellRanges()
 	{
-		return field_4_cell_ranges;
+		return (CellRange[]) field_4_cell_ranges.clone();
 	}
 
 	public String toString()
@@ -130,16 +127,16 @@
 		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("	.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.length>0)
 		{
-			buffer.append("    .cfranges=[");
-			for( int i=0; i<field_4_cell_ranges.size(); i++)
+			buffer.append("	.cfranges=[");
+			for( int i=0; i<field_4_cell_ranges.length; i++)
 			{
-				buffer.append(i==0?"":",").append(field_4_cell_ranges.get(i));
+				buffer.append(i==0?"":",").append(field_4_cell_ranges[i].toString());
 			}
 			buffer.append("]\n");
 		}
@@ -163,24 +160,21 @@
 		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, 16 + offset, (short) field_4_cell_ranges.length);
+		for( int i=0 ; i!=field_4_cell_ranges.length; 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());
+			CellRange cr = field_4_cell_ranges[i];
+			LittleEndian.putShort(data, 18 + 0 + 8 * i + offset, (short) cr.getFirstRow());
+			LittleEndian.putShort(data, 18 + 2 + 8 * i + offset, (short) cr.getLastRow());
+			LittleEndian.putShort(data, 18 + 4 + 8 * i + offset, (short) cr.getFirstColumn());
+			LittleEndian.putShort(data, 18 + 6 + 8 * i + offset, (short) cr.getLastColumn());
 		}
 		return getRecordSize();
 	}
 
 	public int getRecordSize()
 	{
-		return 18+8*field_4_cell_ranges.size();
+		return 18+8*field_4_cell_ranges.length;
 	}
 
 	/**
@@ -204,20 +198,17 @@
 		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;
-    }
-
+	public Object clone() 
+	{
+		CFHeaderRecord result = new CFHeaderRecord();
+		result.field_1_numcf = field_1_numcf;
+		result.field_2_need_recalculation = field_2_need_recalculation;
+		result.field_3_enclosing_cell_range = field_3_enclosing_cell_range;
+		CellRange[] crs = new CellRange[field_4_cell_ranges.length];
+		for (int i = 0; i < crs.length; i++) {
+			crs[i] = field_4_cell_ranges[i].cloneCellRange();
+		}
+		result.field_4_cell_ranges = crs;
+		return result;
+	}
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java Sat Apr  5 06:07:22 2008
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -17,16 +16,12 @@
 ==================================================================== */
         
 
-/*
- * 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.model.FormulaParser;
+import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.cf.BorderFormatting;
 import org.apache.poi.hssf.record.cf.FontFormatting;
 import org.apache.poi.hssf.record.cf.PatternFormatting;
@@ -39,316 +34,292 @@
  * Conditional Formatting Rule Record.
  * @author Dmitriy Kumshayev
  */
-
-public class CFRuleRecord extends Record
+public final 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)
+	public static final short sid = 0x01B1;
+
+	public static final class ComparisonOperator {
+		public static final byte NO_COMPARISON = 0;
+		public static final byte BETWEEN       = 1;
+		public static final byte NOT_BETWEEN   = 2;
+		public static final byte EQUAL         = 3;
+		public static final byte NOT_EQUAL     = 4;
+		public static final byte GT            = 5;
+		public static final byte LT            = 6;
+		public static final byte GE            = 7;
+		public static final byte LE            = 8;
+	}
+
+	private byte  field_1_condition_type;
+	public static final byte CONDITION_TYPE_CELL_VALUE_IS = 1;
+	public static final byte CONDITION_TYPE_FORMULA = 2;
+
+	private byte  field_2_comparison_operator;
+
+	private short field_3_formula1_len;
+	private short field_4_formula2_len;
+
+	private int   field_5_options;
+
+	private static final BitField modificationBits = bf(0x83FFFFFF); // Bits: font,align,bord,patt,prot
+	private static final BitField alignHor      = bf(0x00000001); // 0 = Horizontal alignment modified
+	private static final BitField alignVer      = bf(0x00000002); // 0 = Vertical alignment modified
+	private static final BitField alignWrap     = bf(0x00000004); // 0 = Text wrapped flag modified
+	private static final BitField alignRot      = bf(0x00000008); // 0 = Text rotation modified
+	private static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified
+	private static final BitField alignIndent   = bf(0x00000020); // 0 = Indentation modified
+	private static final BitField alignShrin    = bf(0x00000040); // 0 = Shrink to fit flag modified
+	private static final BitField notUsed1      = bf(0x00000080); // Always 1
+	private static final BitField protLocked    = bf(0x00000100); // 0 = Cell locked flag modified
+	private static final BitField protHidden    = bf(0x00000200); // 0 = Cell hidden flag modified
+	private static final BitField bordLeft      = bf(0x00000400); // 0 = Left border style and colour modified
+	private static final BitField bordRight     = bf(0x00000800); // 0 = Right border style and colour modified
+	private static final BitField bordTop       = bf(0x00001000); // 0 = Top border style and colour modified
+	private static final BitField bordBot       = bf(0x00002000); // 0 = Bottom border style and colour modified
+	private static final BitField bordTlBr      = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified
+	private static final BitField bordBlTr      = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified
+	private static final BitField pattStyle     = bf(0x00010000); // 0 = Pattern style modified
+	private static final BitField pattCol       = bf(0x00020000); // 0 = Pattern colour modified
+	private static final BitField pattBgCol     = bf(0x00040000); // 0 = Pattern background colour modified
+	private static final BitField notUsed2      = bf(0x00380000); // Always 111
+	private static final BitField undocumented  = bf(0x03C00000); // Undocumented bits
+	private static final BitField fmtBlockBits  = bf(0x7C000000); // Bits: font,align,bord,patt,prot
+	private static final BitField font          = bf(0x04000000); // 1 = Record contains font formatting block
+	private static final BitField align         = bf(0x08000000); // 1 = Record contains alignment formatting block
+	private static final BitField bord          = bf(0x10000000); // 1 = Record contains border formatting block
+	private static final BitField patt          = bf(0x20000000); // 1 = Record contains pattern formatting block
+	private static final BitField prot          = bf(0x40000000); // 1 = Record contains protection formatting block
+	private static final BitField alignTextDir  = bf(0x80000000); // 0 = Text direction modified
+
+
+	private static BitField bf(int i) {
+		return BitFieldFactory.getInstance(i);
+	}
+
+	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 Ptg[] field_17_formula1;
+	private Ptg[] field_18_formula2;
+	
+	/** Creates new CFRuleRecord */
+	private CFRuleRecord(byte conditionType, byte comparisonOperation)
+	{
+		field_1_condition_type=conditionType;
+		field_2_comparison_operator=comparisonOperation;
+		field_3_formula1_len = (short)0;
+		field_4_formula2_len = (short)0;
+
+		// Set modification flags to 1: by default options are not modified
+		field_5_options = modificationBits.setValue(field_5_options, -1);
+		// Set formatting block flags to 0 (no formatting blocks)
+		field_5_options = fmtBlockBits.setValue(field_5_options, 0);
+		field_5_options = undocumented.clear(field_5_options);
+
+		field_6_not_used = (short)0x8002; // Excel seems to write this value, but it doesn't seem to care what it reads
+		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;
+	}
+	
+	private CFRuleRecord(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) {
+		this(conditionType, comparisonOperation); 
+		field_1_condition_type = CONDITION_TYPE_CELL_VALUE_IS;
+		field_2_comparison_operator = comparisonOperation;
+		setParsedExpression1(formula1);
+		setParsedExpression2(formula2);
+	}
+
+	/**
+	 * Creates a new comparison operation rule
+	 */
+	public static CFRuleRecord create(Workbook workbook, String formulaText) {
+		Ptg[] formula1 = parseFormula(formulaText, workbook);
+		return new CFRuleRecord(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON,
+				formula1, null);
+	}
+	/**
+	 * Creates a new comparison operation rule
+	 */
+	public static CFRuleRecord create(Workbook workbook, byte comparisonOperation,
+			String formulaText1, String formulaText2) {
+		Ptg[] formula1 = parseFormula(formulaText1, workbook);
+		Ptg[] formula2 = parseFormula(formulaText2, workbook);
+		return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2);
+		
+	}
+
+	/**
+	 * 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) {
+				Stack ptgs = Ptg.createParsedExpressionTokens(field_3_formula1_len, in);
+				// Now convert any fields as required
+				ptgs = SharedFormulaRecord.convertSharedFormulas(ptgs, 0, 0);
+				field_17_formula1 = toArray(ptgs);
+			}
+			if (field_4_formula2_len > 0) {
+				Stack ptgs = Ptg.createParsedExpressionTokens(field_4_formula2_len, in);
+
+				// Now convert any fields as required
+				ptgs = SharedFormulaRecord.convertSharedFormulas(ptgs, 0, 0);
+				field_18_formula2 = toArray(ptgs);
+			}
+		} catch (java.lang.UnsupportedOperationException uoe) {
+			throw new RecordFormatException(uoe);
+		}
+
+	}
+
+	public byte getConditionType()
+	{
+		return field_1_condition_type;
+	}
+
+	public boolean containsFontFormattingBlock()
+	{
+		return getOptionFlag(font);
+	}
+	public void setFontFormatting(FontFormatting fontFormatting)
+	{
+		this.fontFormatting = fontFormatting;
+		setOptionFlag(fontFormatting != null, 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(borderFormatting != null, bord);
+	}
+	
+	public boolean containsPatternFormattingBlock()
+	{
+		return getOptionFlag(patt);
+	}
+	public void setPatternFormatting(PatternFormatting patternFormatting)
+	{
+		this.patternFormatting = patternFormatting;
+		setOptionFlag(patternFormatting!=null, patt);
+	}
+
+	
+	public boolean containsProtectionFormattingBlock()
 	{
-    	return !field.isSet(field_5_options);
+		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;
+	}
+	
+
+	/**
+	 * get the length (in number of tokens) of the expression 1
+	 * @return  expression length
+	 */
+	private short getExpression1Length()
+	{
+		return field_3_formula1_len;
+	}
+	
+
+	/**
+	 * get the length (in number of tokens) of the expression 2
+	 * @return  expression length
+	 */
+	private 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)
@@ -365,7 +336,7 @@
 	{
 		setModified(modified,bordLeft);
 	}
-    
+	
 	public boolean isRightBorderModified()
 	{
 		return isModified(bordRight);
@@ -375,7 +346,7 @@
 	{
 		setModified(modified,bordRight);
 	}
-    
+	
 	public boolean isTopBorderModified()
 	{
 		return isModified(bordTop);
@@ -385,7 +356,7 @@
 	{
 		setModified(modified,bordTop);
 	}
-    
+	
 	public boolean isBottomBorderModified()
 	{
 		return isModified(bordBot);
@@ -395,7 +366,7 @@
 	{
 		setModified(modified,bordBot);
 	}
-    
+	
 	public boolean isTopLeftBottomRightBorderModified()
 	{
 		return isModified(bordTlBr);
@@ -405,7 +376,7 @@
 	{
 		setModified(modified,bordTlBr);
 	}
-    
+	
 	public boolean isBottomLeftTopRightBorderModified()
 	{
 		return isModified(bordBlTr);
@@ -415,7 +386,7 @@
 	{
 		setModified(modified,bordBlTr);
 	}
-    
+	
 	public boolean isPatternStyleModified()
 	{
 		return isModified(pattStyle);
@@ -446,9 +417,9 @@
 		setModified(modified,pattBgCol);
 	}
 	
-    private boolean getOptionFlag(BitField field)
+	private boolean getOptionFlag(BitField field)
 	{
-    	return field.isSet(field_5_options);
+		return field.isSet(field_5_options);
 	}
 
 	private void setOptionFlag(boolean flag, BitField field)
@@ -456,202 +427,226 @@
 		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;
-    }
+	/**
+	 * 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 Ptg[] 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 Ptg[] getParsedExpression2()
+	{
+		return field_18_formula2;
+	}
+	
+	private void setParsedExpression1(Ptg[] ptgs) {
+		short len = getTotalPtgSize(field_17_formula1 = ptgs);
+		field_3_formula1_len = len;
+	}
+
+	private void setParsedExpression2(Ptg[] ptgs) {
+		short len = getTotalPtgSize(field_18_formula2 = ptgs);
+		field_4_formula2_len = len;
+	}
+	
+	/**
+	 * 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 pOffset, byte [] data)
+	{
+		
+		int offset = pOffset;
+		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(convertToTokenStack(field_17_formula1), data, offset);
+			offset += getExpression1Length();
+		}
+
+		if (getExpression2Length()>0)
+		{
+			Ptg.serializePtgStack(convertToTokenStack(field_18_formula2), data, offset);
+			offset += getExpression2Length();
+		}
+		if(offset - pOffset != recordsize) {
+			throw new IllegalStateException("write mismatch (" + (offset - pOffset) + "!=" + recordsize + ")");
+		}
+		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(Ptg[] ptgs)
+	{
+		if( ptgs == null) {
+			return 0;
+		}
+		short  retval = 0;
+		for (int i = 0; i < ptgs.length; i++)
+		{
+			retval += ptgs[i].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(field_1_condition_type, 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 = (Ptg[]) field_17_formula1.clone();
+		}
+		if (field_4_formula2_len > 0) {
+			rec.field_18_formula2 = (Ptg[]) field_18_formula2.clone();
+		}
+
+		return rec;
+	}
 
 	public FontFormatting getFontFormatting()
 	{
 		return fontFormatting;
 	}
 
+	
+	/**
+	 * @return <code>null</code> if <tt>formula</tt> was null.
+	 */
+	private static Ptg[] parseFormula(String formula, Workbook workbook)
+	{
+		if(formula == null) {
+			return null;
+		}
+		return FormulaParser.parse(formula, workbook);
+	}
+
+	// TODO - treat formulas as token arrays instead of Stacks throughout the rest of POI
+	private static Stack convertToTokenStack(Ptg[] ptgs)
+	{
+		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;
+	}
+	private static Ptg[] toArray(Stack ptgs) {
+		Ptg[] result = new Ptg[ptgs.size()];
+		ptgs.toArray(result);
+		return result;
+	}
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java Sat Apr  5 06:07:22 2008
@@ -37,7 +37,7 @@
 	private static final int OPT_PICTURE_LINK          = 0x0004;
 	private static final int OPT_STD_DOCUMENT_NAME     = 0x0008;
 	private static final int OPT_OLE_LINK              = 0x0010;
-//	private static final int OPT_CLIP_FORMAT_MASK	   = 0x7FE0;
+//	private static final int OPT_CLIP_FORMAT_MASK      = 0x7FE0;
 	private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;
 
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java Sat Apr  5 06:07:22 2008
@@ -24,6 +24,7 @@
 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.hssf.util.Region;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -37,19 +38,38 @@
  */
 public final class CFRecordsAggregate extends Record
 {
+	/** Excel allows up to 3 conditional formating rules */
+	private static final int MAX_CONDTIONAL_FORMAT_RULES = 3;
+
 	public final static short sid = -2008; // not a real BIFF record
 
-	private static POILogger  log = POILogFactory.getLogger(CFRecordsAggregate.class);
+	private static POILogger log = POILogFactory.getLogger(CFRecordsAggregate.class);
 
-	private CFHeaderRecord header;
+	private final CFHeaderRecord header;
 
-	// List of CFRuleRecord objects
+	/** List of CFRuleRecord objects */
 	private final List rules;
 
-	public CFRecordsAggregate()
-	{
-		header = null;
-		rules  = new ArrayList(3);
+	private CFRecordsAggregate(CFHeaderRecord pHeader, CFRuleRecord[] pRules) {
+		if(pHeader == null) {
+			throw new IllegalArgumentException("header must not be null");
+		}
+		if(pRules == null) {
+			throw new IllegalArgumentException("rules must not be null");
+		}
+		if(pRules.length > MAX_CONDTIONAL_FORMAT_RULES) {
+			throw new IllegalArgumentException("No more than " 
+					+ MAX_CONDTIONAL_FORMAT_RULES + " rules may be specified");
+		}
+		header = pHeader;
+		rules = new ArrayList(3);
+		for (int i = 0; i < pRules.length; i++) {
+			rules.add(pRules[i]);
+		}
+	}
+
+	public CFRecordsAggregate(Region[] regions, CFRuleRecord[] rules) {
+		this(new CFHeaderRecord(regions), rules);
 	}
 
 	/**
@@ -60,42 +80,46 @@
 	 */
 	public static CFRecordsAggregate createCFAggregate(List recs, int pOffset)
 	{
+		Record rec = ( Record ) recs.get(pOffset);
+		if (rec.getSid() != CFHeaderRecord.sid) {
+			throw new IllegalStateException("next record sid was " + rec.getSid() 
+					+ " instead of " + CFHeaderRecord.sid + " as expected");
+		}
 
-		int offset = pOffset;
-		CFRecordsAggregate 	cfRecords  = new CFRecordsAggregate();
-		ArrayList 		records = new ArrayList(4);
-
-		Record rec = ( Record ) recs.get(offset++);
+		CFHeaderRecord header = (CFHeaderRecord)rec;
+		int nRules = header.getNumberOfConditionalFormats();
 
-		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);
+		CFRuleRecord[] rules = new CFRuleRecord[nRules];
+		int offset = pOffset;
+		int countFound = 0;
+		while (countFound < rules.length) {
+			offset++;
+			if(offset>=recs.size()) {
+				break;
+			}
+			rec = (Record)recs.get(offset);
+			if(rec instanceof CFRuleRecord) {
+				rules[countFound] = (CFRuleRecord) rec;
+				countFound++;
+			} else {
+				break;
 			}
+		}
 
-			if (nRules != cfRecords.rules.size())
+		if (countFound < nRules)
+		{ // TODO -(MAR-2008) can this ever happen? write junit 
+			
+			if (log.check(POILogger.DEBUG))
 			{
-				if (log.check(POILogger.DEBUG))
-				{
-					log.log(POILogger.DEBUG, "Expected  " + nRules + " Conditional Formats, "
-							+ "but found " + cfRecords.rules.size() + " rules");
-				}
-				cfRecords.header.setNumberOfConditionalFormats(nRules);
+				log.log(POILogger.DEBUG, "Expected  " + nRules + " Conditional Formats, "
+						+ "but found " + countFound + " rules");
 			}
-
+			header.setNumberOfConditionalFormats(nRules);
+			CFRuleRecord[] lessRules = new CFRuleRecord[countFound];
+			System.arraycopy(rules, 0, lessRules, 0, countFound);
+			rules = lessRules;
 		}
-		return cfRecords;
+		return new CFRecordsAggregate(header, rules);
 	}
 
 	/**
@@ -104,22 +128,17 @@
 	 */
 	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);
+		CFRuleRecord[] newRecs = new CFRuleRecord[rules.size()];
+		for (int i = 0; i < newRecs.length; i++) {
+			newRecs[i] = (CFRuleRecord) getRule(i).clone();
+		}
+		return new CFRecordsAggregate((CFHeaderRecord) header.clone(), newRecs);
 	}
 
-	/** You never fill an aggregate */
 	protected void fillFields(RecordInputStream in)
 	{
+	     // You never fill an aggregate record
 	}
 
 	public short getSid()
@@ -139,17 +158,14 @@
 
 	public int serialize(int offset, byte[] data)
 	{
-		int pos = offset;
-		if( header != null && rules.size()>0 )
-		{
-			header.setNumberOfConditionalFormats(rules.size());
+		int nRules = rules.size();
+		header.setNumberOfConditionalFormats(nRules);
 
-			pos += (( Record ) header).serialize(pos, data);
+		int pos = offset;
 
-			for(Iterator itr = rules.iterator(); itr.hasNext();)
-			{
-				pos += (( Record ) itr.next()).serialize(pos, data);
-			}
+		pos += header.serialize(pos, data);
+		for(int i=0; i< nRules; i++) {
+			pos += getRule(i).serialize(pos, data);
 		}
 		return pos - offset;
 	}
@@ -160,19 +176,37 @@
 	}
 
 	/**
-	 * @return the header
+	 * @return the header. Never <code>null</code>.
 	 */
 	public CFHeaderRecord getHeader()
 	{
 		return header;
 	}
-
-	/**
-	 * @return the rules
-	 */
-	public List getRules()
-	{
-		return rules;
+	
+	private void checkRuleIndex(int idx) {
+		if(idx < 0 || idx >= rules.size()) {
+			throw new IllegalArgumentException("Bad rule record index (" + idx 
+					+ ") nRules=" + rules.size());
+		}
+	}
+	public CFRuleRecord getRule(int idx) {
+		checkRuleIndex(idx);
+		return (CFRuleRecord) rules.get(idx);
+	}
+	public void setRule(int idx, CFRuleRecord r) {
+		checkRuleIndex(idx);
+		rules.set(idx, r);
+	}
+	public void addRule(CFRuleRecord r) {
+		if(rules.size() >= MAX_CONDTIONAL_FORMAT_RULES) {
+			throw new IllegalStateException("Cannot have more than " 
+					+ MAX_CONDTIONAL_FORMAT_RULES + " conditional format rules");
+		}
+		rules.add(r);
+		header.setNumberOfConditionalFormats(rules.size());
+	}
+	public int getNumberOfRules() {
+		return rules.size();
 	}
 
 	/**

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/CellRange.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/CellRange.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/CellRange.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/CellRange.java Sat Apr  5 06:07:22 2008
@@ -17,79 +17,127 @@
 
 package org.apache.poi.hssf.record.cf;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.poi.hssf.util.Region;
+
 /**
- * CellRange.java
- * Created on January 22, 2008, 10:05 PM
  * 
  * @author Dmitriy Kumshayev
  */
-
-public class CellRange
+public final 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()
+	/** 
+	 * max index for both row and column<p/>
+	 * 
+	 * Note - this value converts to <tt>-1</tt> when cast to a <tt>short</tt> 
+	 */
+	private static final int MAX_INDEX = Integer.MAX_VALUE;
+
+	private static final Region[] EMPTY_REGION_ARRAY = { };
+	
+	private int _firstRow;
+	private int _lastRow;
+	private int _firstColumn;
+	private int _lastColumn;
+	
+	/**
+	 * 
+	 * @param firstRow
+	 * @param lastRow pass <tt>-1</tt> for full column ranges
+	 * @param firstColumn
+	 * @param lastColumn  pass <tt>-1</tt> for full row ranges
+	 */
+	public CellRange(int firstRow, int lastRow, int firstColumn, int lastColumn)
 	{
-		return field_1_first_row;
+		if(!isValid(firstRow, lastRow, firstColumn, lastColumn)) {
+			throw new IllegalArgumentException("invalid cell range (" + firstRow + ", " + lastRow 
+					+ ", " + firstColumn + ", " + lastColumn + ")");
+		}
+		_firstRow = firstRow;
+		_lastRow = convertM1ToMax(lastRow);
+		_firstColumn = firstColumn;
+		_lastColumn = convertM1ToMax(lastColumn);
 	}
-	private void setFirstRow(int firstRow)
-	{
-		this.field_1_first_row = firstRow;
+	
+	private static int convertM1ToMax(int lastIx) {
+		if(lastIx < 0) {
+			return MAX_INDEX;
+		}
+		return lastIx;
 	}
-	public int getLastRow()
-	{
-		return field_2_last_row;
+	private static int convertMaxToM1(int lastIx) {
+		if(lastIx == MAX_INDEX) {
+			return -1;
+		}
+		return lastIx;
+	}
+
+	public boolean isFullColumnRange() {
+		return _firstColumn == 0 && _lastColumn == MAX_INDEX;
+	}
+	public boolean isFullRowRange() {
+		return _firstRow == 0 && _lastRow == MAX_INDEX;
+	}
+	
+	public CellRange(Region r) {
+		this(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r.getColumnTo());
 	}
-	private void setLastRow(int lastRow)
+
+	
+	
+	private static boolean isValid(int firstRow, int lastRow, int firstColumn, int lastColumn)
 	{
-		this.field_2_last_row = lastRow;
+		if(lastRow == -1) {
+			if(firstRow !=0) {
+				return false;
+			}
+		}
+		if(firstRow < 0 || lastRow < -1) {
+			return false;
+		}
+		
+		if(lastColumn == -1) {
+			if(firstColumn !=0) {
+				return false;
+			}
+		}
+		if(firstColumn < 0 || lastColumn < -1) {
+			return false;
+		}
+		return true;
 	}
-	public short getFirstColumn()
+	
+	public int getFirstRow()
 	{
-		return field_3_first_column;
+		return _firstRow;
 	}
-	private void setFirstColumn(short firstColumn)
+	/**
+	 * @return <tt>-1</tt> for whole column ranges
+	 */
+	public int getLastRow()
 	{
-		this.field_3_first_column = firstColumn;
+		return convertMaxToM1(_lastRow);
 	}
-	public short getLastColumn()
+	public int getFirstColumn()
 	{
-		return field_4_last_column;
+		return _firstColumn;
 	}
-	private void setLastColumn(short lastColumn)
+	/**
+	 * @return <tt>-1</tt> for whole row ranges
+	 */
+	public int getLastColumn()
 	{
-		this.field_4_last_column = lastColumn;
+		return convertMaxToM1(_lastColumn);
 	}
 	
 	public static final int NO_INTERSECTION = 1;
 	public static final int OVERLAP = 2;
+	/** first range is within the second range */
 	public static final int INSIDE = 3;
+	/** first range encloses or is equal to the second */
 	public static final int ENCLOSES = 4;
 	
 	/**
@@ -101,30 +149,31 @@
 	 * 		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/>
+	 * 		ENCLOSES - the specified range encloses (possibly exactly the same as) this range<br/>
 	 */
 	public int intersect(CellRange another )
 	{
-		int   firstRow = another.getFirstRow();
-		int   lastRow  = another.getLastRow();
-		short firstCol = another.getFirstColumn();
-		short lastCol  = another.getLastColumn();
+		
+		int firstRow = another.getFirstRow();
+		int lastRow  = another.getLastRow();
+		int firstCol = another.getFirstColumn();
+		int lastCol  = another.getLastColumn();
 		
 		if
 		( 
-				gt(this.getFirstRow(),lastRow) || 
-				lt(this.getLastRow(),firstRow) ||
-				gt(this.getFirstColumn(),lastCol) || 
-				lt(this.getLastColumn(),firstCol) 
+				gt(getFirstRow(),lastRow) || 
+				lt(getLastRow(),firstRow) ||
+				gt(getFirstColumn(),lastCol) || 
+				lt(getLastColumn(),firstCol) 
 		)
 		{
 			return NO_INTERSECTION;
 		}
-		else if( this.contains(another) )
+		else if( contains(another) )
 		{
 			return INSIDE;
 		}
-		else if( another.contains(this) )
+		else if( another.contains(this))
 		{
 			return ENCLOSES;
 		}
@@ -134,7 +183,234 @@
 		}
 			
 	}
+	
+	/**
+	 * 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
+	 */
+	public static CellRange[] mergeCellRanges(CellRange[] cellRanges) {
+		if(cellRanges.length < 1) {
+			return cellRanges;
+		}
+		List temp = mergeCellRanges(Arrays.asList(cellRanges));
+		return toArray(temp);
+	}
+	private static List mergeCellRanges(List cellRangeList)
+	{
+
+		while(cellRangeList.size() > 1)
+		{
+			boolean somethingGotMerged = false;
+			
+			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);
+					
+					CellRange[] mergeResult = mergeRanges(range1, range2);
+					if(mergeResult == null) {
+						continue;
+					}
+					somethingGotMerged = true;
+					// overwrite range1 with first result 
+					cellRangeList.set(i, mergeResult[0]);
+					// remove range2
+					cellRangeList.remove(j--);
+					// add any extra results beyond the first
+					for(int k=1; k<mergeResult.length; k++) {
+						j++;
+						cellRangeList.add(j, mergeResult[k]);
+					}
+				}
+			}
+			if(!somethingGotMerged) {
+				break;
+			}
+		}
 		
+
+		return cellRangeList;
+	}
+	
+	/**
+	 * @return the new range(s) to replace the supplied ones.  <code>null</code> if no merge is possible
+	 */
+	private static CellRange[] mergeRanges(CellRange range1, CellRange range2) {
+		
+		int x = range1.intersect(range2);
+		switch(x)
+		{
+			case CellRange.NO_INTERSECTION: 
+				if( range1.hasExactSharedBorder(range2))
+				{
+					return new CellRange[] { range1.createEnclosingCellRange(range2), };
+				}
+				// else - No intersection and no shared border: do nothing 
+				return null;
+			case CellRange.OVERLAP:
+				return resolveRangeOverlap(range1, range2);
+			case CellRange.INSIDE:
+				// Remove range2, since it is completely inside of range1
+				return new CellRange[] { range1, };
+			case CellRange.ENCLOSES:
+				// range2 encloses range1, so replace it with the enclosing one
+				return new CellRange[] { range2, };
+		}
+		throw new RuntimeException("unexpected intersection result (" + x + ")");
+	}
+	
+	// TODO - write junit test for this
+	static CellRange[] resolveRangeOverlap(CellRange rangeA, CellRange rangeB) {
+		
+		if(rangeA.isFullColumnRange()) {
+			if(rangeB.isFullRowRange()) {
+				// Excel seems to leave these unresolved
+				return null;
+			}
+			return rangeA.sliceUp(rangeB);
+		}
+		if(rangeA.isFullRowRange()) {
+			if(rangeB.isFullColumnRange()) {
+				// Excel seems to leave these unresolved
+				return null;
+			}
+			return rangeA.sliceUp(rangeB);
+		}
+		if(rangeB.isFullColumnRange()) {
+			return rangeB.sliceUp(rangeA);
+		}
+		if(rangeB.isFullRowRange()) {
+			return rangeB.sliceUp(rangeA);
+		}
+		return rangeA.sliceUp(rangeB);
+	}
+
+	/**
+	 * @param range never a full row or full column range
+	 * @return an array including <b>this</b> <tt>CellRange</tt> and all parts of <tt>range</tt> 
+	 * outside of this range  
+	 */
+	private CellRange[] sliceUp(CellRange range) {
+		
+		List temp = new ArrayList();
+		
+		// Chop up range horizontally and vertically
+		temp.add(range);
+		if(!isFullColumnRange()) {
+			temp = cutHorizontally(_firstRow, temp);
+			temp = cutHorizontally(_lastRow+1, temp);
+		}
+		if(!isFullRowRange()) {
+			temp = cutVertically(_firstColumn, temp);
+			temp = cutVertically(_lastColumn+1, temp);
+		}
+		CellRange[] crParts = toArray(temp);
+
+		// form result array
+		temp.clear();
+		temp.add(this);
+		
+		for (int i = 0; i < crParts.length; i++) {
+			CellRange crPart = crParts[i];
+			// only include parts that are not enclosed by this
+			if(intersect(crPart) != ENCLOSES) {
+				temp.add(crPart);
+			}
+		}
+		return toArray(temp);
+	}
+
+	private static List cutHorizontally(int cutRow, List input) {
+		
+		List result = new ArrayList();
+		CellRange[] crs = toArray(input);
+		for (int i = 0; i < crs.length; i++) {
+			CellRange cr = crs[i];
+			if(cr._firstRow < cutRow && cutRow < cr._lastRow) {
+				result.add(new CellRange(cr._firstRow, cutRow, cr._firstColumn, cr._lastColumn));
+				result.add(new CellRange(cutRow+1, cr._lastRow, cr._firstColumn, cr._lastColumn));
+			} else {
+				result.add(cr);
+			}
+		}
+		return result;
+	}
+	private static List cutVertically(int cutColumn, List input) {
+		
+		List result = new ArrayList();
+		CellRange[] crs = toArray(input);
+		for (int i = 0; i < crs.length; i++) {
+			CellRange cr = crs[i];
+			if(cr._firstColumn < cutColumn && cutColumn < cr._lastColumn) {
+				result.add(new CellRange(cr._firstRow, cr._lastRow, cr._firstColumn, cutColumn));
+				result.add(new CellRange(cr._firstRow, cr._lastRow, cutColumn+1, cr._lastColumn));
+			} else {
+				result.add(cr);
+			}
+		}
+		return result;
+	}
+
+
+	private static CellRange[] toArray(List temp) {
+		CellRange[] result = new CellRange[temp.size()];
+		temp.toArray(result);
+		return result;
+	}
+
+	/**
+	 * Convert array of regions to a List of CellRange objects
+	 *  
+	 * @param regions
+	 * @return List of CellRange objects
+	 */
+	public static CellRange[] convertRegionsToCellRanges(Region[] regions)
+	{
+		CellRange[] result = new CellRange[regions.length];
+		for( int i=0; i<regions.length; i++)
+		{
+			result[i] = new CellRange(regions[i]);
+		}
+		return result;
+	}
+	
+	/**
+	 * Convert a List of CellRange objects to an array of regions 
+	 *  
+	 * @param List of CellRange objects
+	 * @return regions
+	 */
+	public static Region[] convertCellRangesToRegions(CellRange[] cellRanges)
+	{
+		int size = cellRanges.length;
+		if(size < 1) {
+			return EMPTY_REGION_ARRAY;
+		}
+		
+		Region[] result = new Region[size];
+
+		for (int i = 0; i != size; i++)
+		{
+			result[i] = cellRanges[i].convertToRegion();
+		}
+		return result;
+	}
+
+
+		
+	private Region convertToRegion() {
+		int lastRow = convertMaxToM1(_lastRow);
+		int lastColumn = convertMaxToM1(_lastColumn);
+		
+		return new Region(_firstRow, (short)_firstColumn, lastRow, (short)lastColumn);
+	}
+
+
 	/**
 	 *  Check if the specified range is located inside of this cell range.
 	 *  
@@ -145,44 +421,52 @@
    {
 		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);
+		int firstCol = range.getFirstColumn();
+		int lastCol = range.getLastColumn();
+		return le(getFirstRow(), firstRow) && ge(getLastRow(), lastRow)
+				&& le(getFirstColumn(), firstCol) && ge(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);
+		return le(getFirstRow(), row) && ge(getLastRow(), row)
+				&& le(getFirstColumn(), column) && ge(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)
+	* Check if the specified cell range has a shared border with the current range.
+	* 
+	* @return <code>true</code> if the ranges have a complete shared border (i.e.
+	* the two ranges together make a simple rectangular region.
+	*/
+   	public boolean hasExactSharedBorder(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)
-		;
+		int oFirstRow = range._firstRow;
+		int oLastRow  = range._lastRow;
+		int oFirstCol = range._firstColumn;
+		int oLastCol  = range._lastColumn;
+		
+		if (_firstRow > 0 && _firstRow-1 == oLastRow || 
+			oFirstRow > 0 && oFirstRow-1 == _lastRow) {
+			// ranges have a horizontal border in common
+			// make sure columns are identical:
+			return _firstColumn == oFirstCol && _lastColumn == oLastCol;
+		}
+
+		if (_firstColumn>0 && _firstColumn - 1 == oLastCol ||
+			oFirstCol>0 && _lastColumn == oFirstCol -1) {
+			// ranges have a vertical border in common
+			// make sure rows are identical:
+			return _firstRow == oFirstRow && _lastRow == oLastRow;
+		}
+		return false;
    	}
    	
-    /**
-     * Create an enclosing CellRange for the two cell ranges.
-     * 
-     * @return enclosing CellRange
-     */
+	/**
+	 * Create an enclosing CellRange for the two cell ranges.
+	 * 
+	 * @return enclosing CellRange
+	 */
 	public CellRange createEnclosingCellRange(CellRange range)
 	{
 		if( range == null)
@@ -206,18 +490,6 @@
 	{
 		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
@@ -253,7 +525,7 @@
 	
 	public String toString()
 	{
-		return "("+this.getFirstRow()+","+this.getLastRow()+","+this.getFirstColumn()+","+this.getLastColumn()+")";
+		return "("+getFirstRow()+","+getLastRow()+","+getFirstColumn()+","+getLastColumn()+")";
 	}
-    
+	
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java Sat Apr  5 06:07:22 2008
@@ -23,27 +23,27 @@
 
 
 /**
- * This class provides the base functionality for Excel sheet functions 
+ * This class provides the base functionality for Excel sheet functions
  * There are two kinds of function Ptgs - tFunc and tFuncVar
  * Therefore, this class will have ONLY two subclasses
  * @author  Avik Sengupta
  * @author Andrew C. Oliver (acoliver at apache dot org)
  */
 public abstract class AbstractFunctionPtg extends OperationPtg {
-    
+
     /**
      * The name of the IF function (i.e. "IF").  Extracted as a constant for clarity.
-     */ 
+     */
     public static final String FUNCTION_NAME_IF = "IF";
     /** All external functions have function index 255 */
     private static final short FUNCTION_INDEX_EXTERNAL = 255;
-    
+
     protected byte returnClass;
     protected byte[] paramClass;
-    
+
     protected byte field_1_num_args;
     protected short field_2_fnc_index;
- 
+
     public String toString() {
         StringBuffer sb = new StringBuffer(64);
         sb.append(getClass().getName()).append(" [");
@@ -51,17 +51,17 @@
         sb.append("]");
         return sb.toString();
     }
-   
+
     public int getType() {
         return -1;
-    }   
-    
-   
-    
+    }
+
+
+
     public short getFunctionIndex() {
         return field_2_fnc_index;
     }
-    
+
     public String getName() {
         return lookupName(field_2_fnc_index);
     }
@@ -72,14 +72,14 @@
     public boolean isExternalFunction() {
         return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
     }
-    
+
     public String toFormulaString(Workbook book) {
         return getName();
     }
-    
+
     public String toFormulaString(String[] operands) {
-        StringBuffer buf = new StringBuffer();        
-        
+        StringBuffer buf = new StringBuffer();
+
         if(isExternalFunction()) {
             buf.append(operands[0]); // first operand is actually the function name
             appendArgs(buf, 1, operands);
@@ -100,23 +100,23 @@
         }
         buf.append(")");
     }
-    
+
     public abstract void writeBytes(byte[] array, int offset);
     public abstract int getSize();
-    
-   
+
+
     /**
      * Used to detect whether a function name found in a formula is one of the standard excel functions
      * <p>
      * The name matching is case insensitive.
-     * @return <code>true</code> if the name specifies a standard worksheet function, 
+     * @return <code>true</code> if the name specifies a standard worksheet function,
      *  <code>false</code> if the name should be assumed to be an external function.
      */
     public static final boolean isInternalFunctionName(String name) {
         short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase());
         return ix >= 0;
     }
-    
+
     protected String lookupName(short index) {
         if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) {
             return "#external#";
@@ -127,7 +127,7 @@
         }
         return fm.getName();
     }
-    
+
     /**
      * Resolves internal function names into function indexes.
      * <p>
@@ -145,7 +145,7 @@
     public byte getDefaultOperandClass() {
         return returnClass;
     }
-    
+
     public byte getParameterClass(int index) {
         try {
             return paramClass[index];

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java Sat Apr  5 06:07:22 2008
@@ -27,11 +27,11 @@
  * @author Danny Mui (dmui at apache dot org) (Leftover handling)
  */
 public final class FuncPtg extends AbstractFunctionPtg {
-    
+
     public final static byte sid  = 0x21;
     public final static int  SIZE = 3;
     private int numParams=0;
-    
+
     /**
      * FuncPtgs are defined to be 4 bytes but the actual FuncPtg uses only 2 bytes.
      * If we have leftOvers that are read from the file we should serialize them back out.
@@ -39,18 +39,18 @@
      * If the leftovers are removed, a prompt "Warning: Data may have been lost occurs in Excel"
      */
 	//protected byte[] leftOvers = null;
-    
+
     private FuncPtg() {
-      //Required for clone methods      
+      //Required for clone methods
     }
 
-    /**Creates new function pointer from a byte array 
-     * usually called while reading an excel file. 
+    /**Creates new function pointer from a byte array
+     * usually called while reading an excel file.
      */
     public FuncPtg(RecordInputStream in) {
         //field_1_num_args = data[ offset + 0 ];
         field_2_fnc_index  = in.readShort();
-        
+
         FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index);
         if(fm == null) {
             throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")");
@@ -62,12 +62,12 @@
         numParams = numberOfParameters;
         paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO
     }
-    
+
     public void writeBytes(byte[] array, int offset) {
         array[offset+0]= (byte) (sid + ptgClass);
         LittleEndian.putShort(array,offset+1,field_2_fnc_index);
     }
-    
+
     public int getNumberOfOperands() {
         return numParams;
     }
@@ -79,11 +79,11 @@
       ptg.setClass(ptgClass);
      return ptg;
     }
-    
+
     public int getSize() {
         return SIZE;
     }
-    
+
     public String toString() {
         StringBuffer sb = new StringBuffer(64);
         sb.append(getClass().getName()).append(" [");

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java Sat Apr  5 06:07:22 2008
@@ -15,7 +15,6 @@
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.hssf.record.formula;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.hssf.record.RecordInputStream;
@@ -27,22 +26,22 @@
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 public final class FuncVarPtg extends AbstractFunctionPtg{
-    
+
     public final static byte sid  = 0x22;
-    private final static int  SIZE = 4;  
-    
+    private final static int  SIZE = 4;
+
     private FuncVarPtg() {
       //Required for clone methods
     }
 
- /**Creates new function pointer from a byte array 
-     * usually called while reading an excel file. 
+ /**Creates new function pointer from a byte array
+     * usually called while reading an excel file.
      */
     public FuncVarPtg(RecordInputStream in) {
         field_1_num_args = in.readByte();
         field_2_fnc_index  = in.readShort();
     }
-    
+
     /**
      * Create a function ptg from a string tokenised by the parser
      */
@@ -59,17 +58,17 @@
             paramClass = new byte[] {Ptg.CLASS_VALUE};
         }
     }
-    
+
      public void writeBytes(byte[] array, int offset) {
         array[offset+0]=(byte) (sid + ptgClass);
         array[offset+1]=field_1_num_args;
         LittleEndian.putShort(array,offset+2,field_2_fnc_index);
     }
-    
+
      public int getNumberOfOperands() {
         return field_1_num_args;
     }
-    
+
     public Object clone() {
       FuncVarPtg ptg = new FuncVarPtg();
       ptg.field_1_num_args = field_1_num_args;
@@ -77,11 +76,11 @@
       ptg.setClass(ptgClass);
       return ptg;
     }
-    
+
     public int getSize() {
         return SIZE;
     }
-    
+
     public String toString() {
         StringBuffer sb = new StringBuffer(64);
         sb.append(getClass().getName()).append(" [");

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java?rev=645088&r1=645087&r2=645088&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java Sat Apr  5 06:07:22 2008
@@ -23,7 +23,7 @@
 import java.util.Set;
 
 /**
- * Temporarily collects <tt>FunctionMetadata</tt> instances for creation of a 
+ * Temporarily collects <tt>FunctionMetadata</tt> instances for creation of a
  * <tt>FunctionMetadataRegistry</tt>.
  * 
  * @author Josh Micich



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org