You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by jo...@apache.org on 2008/10/30 23:06:12 UTC

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

Author: josh
Date: Thu Oct 30 15:06:10 2008
New Revision: 709262

URL: http://svn.apache.org/viewvc?rev=709262&view=rev
Log:
Merged revisions 708385,708996,709054,709217,709221,709235 via svnmerge from 
https://svn.apache.org/repos/asf/poi/trunk

........
  r708385 | josh | 2008-10-27 16:44:44 -0700 (Mon, 27 Oct 2008) | 1 line
  
  Small fix for bug in RecordInputStream.readAllContinuedRemainder()  introduced in r707778.  It seems like only BiffViewer was affected.
........
  r708996 | josh | 2008-10-29 13:13:58 -0700 (Wed, 29 Oct 2008) | 1 line
  
  Allowed for quad-byte padding alignment on ObjRecord
........
  r709054 | josh | 2008-10-29 17:21:24 -0700 (Wed, 29 Oct 2008) | 1 line
  
  removed obsolete methods
........
  r709217 | josh | 2008-10-30 10:56:34 -0700 (Thu, 30 Oct 2008) | 1 line
  
  Fixed compiler warnings / simplified code
........
  r709221 | josh | 2008-10-30 11:33:35 -0700 (Thu, 30 Oct 2008) | 1 line
  
  Optimised slow test case (after reviewing original purpose)
........
  r709235 | josh | 2008-10-30 13:17:04 -0700 (Thu, 30 Oct 2008) | 1 line
  
  Fix for bug 15716 - - converted Ptg arrays into Formula objects to optimise memory usage
........

Added:
    poi/branches/ooxml/src/java/org/apache/poi/ss/formula/Formula.java
      - copied unchanged from r709235, poi/trunk/src/java/org/apache/poi/ss/formula/Formula.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/record/ArrayRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CFRuleRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/DVRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/FormulaRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/NameRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ObjRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TableRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/cf/PatternFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFBorderFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFPatternFormatting.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/util/CellRangeAddressList.java
    poi/branches/ooxml/src/java/org/apache/poi/ss/formula/SheetRefEvaluator.java   (props changed)
    poi/branches/ooxml/src/java/org/apache/poi/ss/util/CellRangeAddress.java
    poi/branches/ooxml/src/java/org/apache/poi/ss/util/CellRangeAddressList.java
    poi/branches/ooxml/src/java/org/apache/poi/util/LittleEndianByteArrayOutputStream.java
    poi/branches/ooxml/src/java/org/apache/poi/util/LittleEndianOutput.java
    poi/branches/ooxml/src/java/org/apache/poi/util/LittleEndianOutputStream.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestNameRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestObjRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java

Propchange: poi/branches/ooxml/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Oct 30 15:06:10 2008
@@ -1 +1 @@
-/poi/trunk:693591-694881,695264-695420,695621,695649-708286
+/poi/trunk:693591-694881,695264-695420,695621,695649-709235

Propchange: poi/branches/ooxml/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Thu Oct 30 15:06:10 2008
@@ -1 +1 @@
-/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576-642736,642739-650914,650916-708286
+/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576-642736,642739-650914,650916-709235

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=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/changes.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/changes.xml Thu Oct 30 15:06:10 2008
@@ -37,6 +37,7 @@
 
 		<!-- Don't forget to update status.xml too! -->
         <release version="3.5-beta4" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">15716 - memory usage optimisation - converted Ptg arrays into Formula objects</action>
            <action dev="POI-DEVELOPERS" type="add">46065 - added implementation for VALUE function</action>
            <action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
            <action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</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=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/status.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/status.xml Thu Oct 30 15:06:10 2008
@@ -34,6 +34,7 @@
 	<!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.5-beta4" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">15716 - memory usage optimisation - converted Ptg arrays into Formula objects</action>
            <action dev="POI-DEVELOPERS" type="add">46065 - added implementation for VALUE function</action>
            <action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
            <action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</action>

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ArrayRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ArrayRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ArrayRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ArrayRecord.java Thu Oct 30 15:06:10 2008
@@ -18,8 +18,9 @@
 package org.apache.poi.hssf.record;
 
 import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianOutput;
 
 /**
  * ARRAY (0x0221)<p/>
@@ -36,14 +37,15 @@
 	
 	private int	_options;
 	private int _field3notUsed;
-	private Ptg[] _formulaTokens;
+	private Formula _formula;
 
 	public ArrayRecord(RecordInputStream in) {
 		super(in);
 		_options = in.readUShort();
 		_field3notUsed = in.readInt();
-		int formulaLen = in.readUShort();
-		_formulaTokens = Ptg.readTokens(formulaLen, in);
+		int formulaTokenLen = in.readUShort();
+		int totalFormulaLen = in.available();
+		_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
 	}
 
 	public boolean isAlwaysRecalculate() {
@@ -55,18 +57,12 @@
 
 	protected int getExtraDataSize() {
 		return 2 + 4
-			+ 2 + Ptg.getEncodedSize(_formulaTokens);
+			+ _formula.getEncodedSize();
 	}
-	protected void serializeExtraData(int offset, byte[] data) {
-		int pos = offset;
-		LittleEndian.putUShort(data, pos, _options);
-		pos+=2;
-		LittleEndian.putInt(data, pos, _field3notUsed);
-		pos+=4;
-		int tokenSize = Ptg.getEncodedSizeWithoutArrayData(_formulaTokens);
-		LittleEndian.putUShort(data, pos, tokenSize);
-		pos+=2;
-		Ptg.serializePtgs(_formulaTokens, data, pos);
+	protected void serializeExtraData(LittleEndianOutput out) {
+		out.writeShort(_options);
+		out.writeInt(_field3notUsed);
+		_formula.serialize(out);
 	}
 
 	public short getSid() {
@@ -80,8 +76,9 @@
 		sb.append(" options=").append(HexDump.shortToHex(_options)).append("\n");
 		sb.append(" notUsed=").append(HexDump.intToHex(_field3notUsed)).append("\n");
 		sb.append(" formula:").append("\n");
-		for (int i = 0; i < _formulaTokens.length; i++) {
-			Ptg ptg = _formulaTokens[i];
+		Ptg[] ptgs = _formula.getTokens();
+		for (int i = 0; i < ptgs.length; i++) {
+			Ptg ptg = ptgs[i];
 			sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n");
 		}
 		sb.append("]");

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=709262&r1=709261&r2=709262&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 Thu Oct 30 15:06:10 2008
@@ -23,9 +23,10 @@
 import org.apache.poi.hssf.record.cf.PatternFormatting;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
 /**
  * Conditional Formatting Rule Record.
@@ -94,18 +95,12 @@
 
 	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;
+	private Formula field_17_formula1;
+	private Formula field_18_formula2;
 	
 	/** Creates new CFRuleRecord */
 	private CFRuleRecord(byte conditionType, byte comparisonOperation)
@@ -121,23 +116,18 @@
 
 		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;
+		field_17_formula1=Formula.create(Ptg.EMPTY_PTG_ARRAY);
+		field_18_formula2=Formula.create(Ptg.EMPTY_PTG_ARRAY);
 	}
 	
 	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;
-		field_17_formula1 = formula1;
-		field_18_formula2 = formula2;
+		field_17_formula1 = Formula.create(formula1);
+		field_18_formula2 = Formula.create(formula2);
 	}
 
 	/**
@@ -178,12 +168,9 @@
 			patternFormatting = new PatternFormatting(in);
 		}
 
-		if (field_3_formula1_len > 0) {
-			field_17_formula1 = Ptg.readTokens(field_3_formula1_len, in);
-		}
-		if (field_4_formula2_len > 0) {
-			field_18_formula2 = Ptg.readTokens(field_4_formula2_len, in);
-		}
+		// "You may not use unions, intersections or array constants in Conditional Formatting criteria"
+		field_17_formula1 = Formula.read(field_3_formula1_len, in);
+		field_18_formula2 = Formula.read(field_4_formula2_len, in);
 	}
 
 	public byte getConditionType()
@@ -414,33 +401,22 @@
 
 	public Ptg[] getParsedExpression1()
 	{
-		return field_17_formula1;
+		return field_17_formula1.getTokens();
 	}
 	public void setParsedExpression1(Ptg[] ptgs) {
-		field_17_formula1 = safeClone(ptgs);
-	}
-	private static Ptg[] safeClone(Ptg[] ptgs) {
-		if (ptgs == null) {
-			return null;
-		}
-		return (Ptg[]) ptgs.clone();
+		field_17_formula1 = Formula.create(ptgs);
 	}
 
 	/**
 	 * 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!
+	 * @return array of {@link Ptg}s, possibly <code>null</code>
 	 */
-
-	public Ptg[] getParsedExpression2()
-	{
-		return field_18_formula2;
+	public Ptg[] getParsedExpression2() {
+		return Formula.getTokens(field_18_formula2);
 	}
 	public void setParsedExpression2(Ptg[] ptgs) {
-		field_18_formula2 = safeClone(ptgs);
+		field_18_formula2 = Formula.create(ptgs);
 	}
 
 	public short getSid()
@@ -449,14 +425,11 @@
 	}
 
 	/**
-	 * @param ptgs may be <code>null</code>
-	 * @return encoded size of the formula
+	 * @param ptgs must not be <code>null</code>
+	 * @return encoded size of the formula tokens (does not include 2 bytes for ushort length)
 	 */
-	private static int getFormulaSize(Ptg[] ptgs) {
-		if (ptgs == null) {
-			return 0;
-		}
-		return Ptg.getEncodedSize(ptgs);
+	private static int getFormulaSize(Formula formula) {
+		return formula.getEncodedTokenSize();
 	}
 	
 	/**
@@ -468,51 +441,43 @@
 	 * @param data byte array containing instance data
 	 * @return number of bytes written
 	 */
-	public int serialize(int pOffset, byte [] data)
-	{
+	public int serialize(int pOffset, byte [] data) {
 		
 		int formula1Len=getFormulaSize(field_17_formula1);
 		int formula2Len=getFormulaSize(field_18_formula2);
 		
-		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.putUShort(data, 6 + offset, formula1Len);
-		LittleEndian.putUShort(data, 8 + offset, formula2Len);
-		LittleEndian.putInt(data,  10 + offset, field_5_options);
-		LittleEndian.putShort(data,14 + offset, field_6_not_used);
 		
-		offset += 16;
+		LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(data, pOffset, recordsize);
 		
-		if( containsFontFormattingBlock() )
-		{
+		out.writeShort(sid);
+		out.writeShort(recordsize-4);
+		out.writeByte(field_1_condition_type);
+		out.writeByte(field_2_comparison_operator);
+		out.writeShort(formula1Len);
+		out.writeShort(formula2Len);
+		out.writeInt(field_5_options);
+		out.writeShort(field_6_not_used);
+		
+		if (containsFontFormattingBlock()) {
 			byte[] fontFormattingRawRecord  = fontFormatting.getRawRecord();
-			System.arraycopy(fontFormattingRawRecord, 0, data, offset, fontFormattingRawRecord.length);
-			offset += fontFormattingRawRecord.length;
+			out.write(fontFormattingRawRecord);
 		}
 		
-		if( containsBorderFormattingBlock())
-		{
-			offset += borderFormatting.serialize(offset, data);
+		if (containsBorderFormattingBlock()) {
+			borderFormatting.serialize(out);
 		}
 		
-		if( containsPatternFormattingBlock() )
-		{
-			offset += patternFormatting.serialize(offset, data);
+		if (containsPatternFormattingBlock()) {
+			patternFormatting.serialize(out);
 		}
 		
-		if (field_17_formula1 != null) {
-			offset += Ptg.serializePtgs(field_17_formula1, data, offset);
-		}
-
-		if (field_18_formula2 != null) {
-			offset += Ptg.serializePtgs(field_18_formula2, data, offset);
-		}
-		if(offset - pOffset != recordsize) {
-			throw new IllegalStateException("write mismatch (" + (offset - pOffset) + "!=" + recordsize + ")");
+		field_17_formula1.serializeTokens(out);
+		field_18_formula2.serializeTokens(out);
+		
+		if(out.getWriteIndex() - pOffset != recordsize) {
+			throw new IllegalStateException("write mismatch (" 
+					+ (out.getWriteIndex() - pOffset) + "!=" + recordsize + ")");
 		}
 		return recordsize;
 	}
@@ -531,25 +496,22 @@
 	}
 
 
-	public String toString()
-	{
+	public String toString() {
 		StringBuffer buffer = new StringBuffer();
 		buffer.append("[CFRULE]\n");
 		buffer.append("    OPTION FLAGS=0x"+Integer.toHexString(getOptions()));
-		/*
-		if( containsFontFormattingBlock())
-		{
-			buffer.append(fontFormatting.toString());
+		if (false) {
+			if (containsFontFormattingBlock()) {
+				buffer.append(fontFormatting.toString());
+			}
+			if (containsBorderFormattingBlock()) {
+				buffer.append(borderFormatting.toString());
+			}
+			if (containsPatternFormattingBlock()) {
+				buffer.append(patternFormatting.toString());
+			}
+			buffer.append("[/CFRULE]\n");
 		}
-		if( containsBorderFormattingBlock())
-		{
-			buffer.append(borderFormatting.toString());
-		}
-		if( containsPatternFormattingBlock())
-		{
-			buffer.append(patternFormatting.toString());
-		}
-		buffer.append("[/CFRULE]\n");*/
 		return buffer.toString();
 	}
 	
@@ -566,12 +528,8 @@
 		if (containsPatternFormattingBlock()) {
 			rec.patternFormatting = (PatternFormatting) patternFormatting.clone();
 		}
-		if (field_17_formula1 != null) {
-			rec.field_17_formula1 = (Ptg[]) field_17_formula1.clone();
-		}
-		if (field_18_formula2 != null) {
-			rec.field_18_formula2 = (Ptg[]) field_18_formula2.clone();
-		}
+		rec.field_17_formula1 = field_17_formula1.copy();
+		rec.field_18_formula2 = field_17_formula1.copy();
 
 		return rec;
 	}

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/DVRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/DVRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/DVRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/DVRecord.java Thu Oct 30 15:06:10 2008
@@ -16,14 +16,16 @@
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.usermodel.DVConstraint;
 import org.apache.poi.hssf.usermodel.HSSFDataValidation;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.BitField;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianOutput;
+import org.apache.poi.util.StringUtil;
 
 /**
  * Title:        DATAVALIDATION Record (0x01BE)<p/>
@@ -53,11 +55,11 @@
 	/** Not used - Excel seems to always write 0x3FE0 */
 	private short _not_used_1 = 0x3FE0;
 	/** Formula data for first condition (RPN token array without size field) */
-	private Ptg[] _formula1;
+	private Formula _formula1;
 	/** Not used - Excel seems to always write 0x0000 */
 	private short _not_used_2 = 0x0000;
 	/** Formula data for second condition (RPN token array without size field) */
-	private Ptg[] _formula2;
+	private Formula _formula2;
 	/** Cell range address list with all affected ranges */
 	private CellRangeAddressList _regions;
 
@@ -96,34 +98,36 @@
 		_promptText = resolveTitleText(promptText);
 		_errorTitle = resolveTitleText(errorTitle);
 		_errorText = resolveTitleText(errorText);
-		_formula1 = formula1;
-		_formula2 = formula2;
+		_formula1 = Formula.create(formula1);
+		_formula2 = Formula.create(formula2);
 		_regions = regions;
 	}
 
 	public DVRecord(RecordInputStream in) {
-		
-	   _option_flags = in.readInt();
-	   
-	   _promptTitle = readUnicodeString(in);
-	   _errorTitle = readUnicodeString(in);
-	   _promptText = readUnicodeString(in);
-	   _errorText = readUnicodeString(in);
-
-	   int field_size_first_formula = in.readUShort(); 
-	   _not_used_1 = in.readShort();
-
-	   //read first formula data condition
-	   _formula1 = Ptg.readTokens(field_size_first_formula, in);
 
-	   int field_size_sec_formula = in.readUShort(); 
-	   _not_used_2 = in.readShort();
+		_option_flags = in.readInt();
+
+		_promptTitle = readUnicodeString(in);
+		_errorTitle = readUnicodeString(in);
+		_promptText = readUnicodeString(in);
+		_errorText = readUnicodeString(in);
+
+		int field_size_first_formula = in.readUShort();
+		_not_used_1 = in.readShort();
+
+		// "You may not use unions, intersections or array constants in Data Validation criteria"
 
-	   //read sec formula data condition
-	   _formula2 = Ptg.readTokens(field_size_sec_formula, in);
+		// read first formula data condition
+		_formula1 = Formula.read(field_size_first_formula, in);
 
-	   //read cell range address list with all affected ranges
-	   _regions = new org.apache.poi.hssf.util.CellRangeAddressList(in);
+		int field_size_sec_formula = in.readUShort();
+		_not_used_2 = in.readShort();
+
+		// read sec formula data condition
+		_formula2 = Formula.read(field_size_sec_formula, in);
+
+		// read cell range address list with all affected ranges
+		_regions = new CellRangeAddressList(in);
 	}
 
 	// --> start option flags
@@ -235,45 +239,43 @@
 		return str;
 	}
 
-	private void appendFormula(StringBuffer sb, String label, Ptg[] ptgs) {
+	private static void appendFormula(StringBuffer sb, String label, Formula f) {
 		sb.append(label);
-		if (ptgs.length < 1) {
+		
+		if (f == null) {
 			sb.append("<empty>\n");
 			return;
 		}
-		sb.append("\n");
+		Ptg[] ptgs = f.getTokens();
+		sb.append('\n');
 		for (int i = 0; i < ptgs.length; i++) {
 			sb.append('\t').append(ptgs[i].toString()).append('\n');
 		}
 	}
 
 	public int serialize(int offset, byte [] data) {
-		int size = this.getRecordSize();
-		LittleEndian.putShort(data, 0 + offset, sid);
-		LittleEndian.putShort(data, 2 + offset, ( short ) (size-4));
-
-		int pos = 4;
-		LittleEndian.putInt(data, pos + offset, _option_flags);
-		pos += 4;
+		int recSize = getRecordSize();
+		LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
 		
-		pos += serializeUnicodeString(_promptTitle, pos+offset, data);
-		pos += serializeUnicodeString(_errorTitle, pos+offset, data);
-		pos += serializeUnicodeString(_promptText, pos+offset, data);
-		pos += serializeUnicodeString(_errorText, pos+offset, data);
-		LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula1));
-		pos += 2;
-		LittleEndian.putUShort(data, offset+pos, _not_used_1);
-		pos += 2;
-
-		pos += Ptg.serializePtgs(_formula1, data, pos+offset);
-
-		LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula2));
-		pos += 2;
-		LittleEndian.putShort(data, offset+pos, _not_used_2);
-		pos += 2;
-		pos += Ptg.serializePtgs(_formula2, data, pos+offset);
-		_regions.serialize(pos+offset, data);
-		return size;
+		out.writeShort(sid);
+		out.writeShort(recSize-4);
+
+		out.writeInt(_option_flags);
+		
+		serializeUnicodeString(_promptTitle, out);
+		serializeUnicodeString(_errorTitle, out);
+		serializeUnicodeString(_promptText, out);
+		serializeUnicodeString(_errorText, out);
+		out.writeShort(_formula1.getEncodedTokenSize());
+		out.writeShort(_not_used_1);
+		_formula1.serializeTokens(out);
+		
+		out.writeShort(_formula2.getEncodedTokenSize());
+		out.writeShort(_not_used_2);
+		_formula2.serializeTokens(out);
+		
+		_regions.serialize(out);
+		return recSize;
 	}
 
 	/**
@@ -293,13 +295,12 @@
 		return new UnicodeString(in);
 	}
 
-	private static int serializeUnicodeString(UnicodeString us, int offset, byte[] data) {
-		UnicodeRecordStats urs = new UnicodeRecordStats();
-		us.serialize(urs, offset, data);
-		return urs.recordSize;
+	private static void serializeUnicodeString(UnicodeString us, LittleEndianOutput out) {
+		StringUtil.writeUnicodeString(out, us.getString());
 	}
-	private static int getUnicodeStringSize(UnicodeString str) {
-		return 3 + str.getString().length();
+	private static int getUnicodeStringSize(UnicodeString us) {
+		String str = us.getString();
+		return 3 + str.length() * (StringUtil.hasMultibyte(str) ? 2 : 1);
 	}
 
 	public int getRecordSize()  {
@@ -308,8 +309,8 @@
 		size += getUnicodeStringSize(_errorTitle);
 		size += getUnicodeStringSize(_promptText);
 		size += getUnicodeStringSize(_errorText);
-		size += Ptg.getEncodedSize(_formula1);
-		size += Ptg.getEncodedSize(_formula2);
+		size += _formula1.getEncodedTokenSize();
+		size += _formula2.getEncodedTokenSize();
 		size += _regions.getSize();
 		return size;
 	}

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=709262&r1=709261&r2=709262&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 Thu Oct 30 15:06:10 2008
@@ -17,20 +17,19 @@
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.ss.formula.Formula;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianOutput;
 import org.apache.poi.util.StringUtil;
 
 /**
- * EXTERNALNAME<p/>
+ * EXTERNALNAME (0x0023)<p/>
  * 
  * @author Josh Micich
  */
 public final class ExternalNameRecord extends Record {
 
-	private static final Ptg[] EMPTY_PTG_ARRAY = { };
-
-	public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223)
+	public final static short sid = 0x0023; // as per BIFF8. (some old versions used 0x223)
 
 	private static final int OPT_BUILTIN_NAME          = 0x0001;
 	private static final int OPT_AUTOMATIC_LINK        = 0x0002; // m$ doc calls this fWantAdvise 
@@ -45,7 +44,7 @@
 	private short  field_2_index;
 	private short  field_3_not_used;
 	private String field_4_name;
-	private Ptg[]  field_5_name_definition;
+	private Formula  field_5_name_definition;
 
 	/**
 	 * Convenience Function to determine if the name is a built-in name
@@ -88,7 +87,7 @@
 		int result = 3 * 2  // 3 short fields
 			+ 2 + field_4_name.length(); // nameLen and name
 		if(hasFormula()) {
-			result += 2 + getNameDefinitionSize(); // nameDefLen and nameDef
+			result += field_5_name_definition.getEncodedSize();
 		}
 		return result;
 	}
@@ -104,28 +103,23 @@
 	 */
 	public int serialize( int offset, byte[] data ) {
 		int dataSize = getDataSize();
+		int recSize = dataSize + 4;
+		LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
 
-		LittleEndian.putShort( data, 0 + offset, sid );
-		LittleEndian.putShort( data, 2 + offset, (short) dataSize );
-		LittleEndian.putShort( data, 4 + offset, field_1_option_flag );
-		LittleEndian.putShort( data, 6 + offset, field_2_index );
-		LittleEndian.putShort( data, 8 + offset, field_3_not_used );
+		out.writeShort(sid);
+		out.writeShort(dataSize);
+		out.writeShort(field_1_option_flag);
+		out.writeShort(field_2_index);
+		out.writeShort(field_3_not_used);
 		int nameLen = field_4_name.length();
-		LittleEndian.putUShort( data, 10 + offset, nameLen );
-		StringUtil.putCompressedUnicode( field_4_name, data, 12 + offset );
-		if(hasFormula()) {
-			int defLen = getNameDefinitionSize();
-			LittleEndian.putUShort( data, 12 + nameLen + offset, defLen );
-			Ptg.serializePtgs(field_5_name_definition, data, 14 + nameLen + offset );
+		out.writeShort(nameLen);
+		StringUtil.putCompressedUnicode(field_4_name, out);
+		if (hasFormula()) {
+			field_5_name_definition.serialize(out);
 		}
-		return dataSize + 4;
+		return recSize;
 	}
 
-	private int getNameDefinitionSize() {
-		return Ptg.getEncodedSize(field_5_name_definition);
-	}
-
-
 	public int getRecordSize(){
 		return 4 + getDataSize();
 	}
@@ -141,14 +135,16 @@
 			if(in.remaining() > 0) {
 				throw readFail("Some unread data (is formula present?)");
 			}
-			field_5_name_definition = EMPTY_PTG_ARRAY;
+			field_5_name_definition = null;
 			return;
 		}
-		if(in.remaining() <= 0) {
+		int nBytesRemaining = in.available();
+		if(nBytesRemaining <= 0) {
 			throw readFail("Ran out of record data trying to read formula.");
 		}
-		short formulaLen = in.readShort();
-		field_5_name_definition = Ptg.readTokens(formulaLen, in);
+		int formulaLen = in.readUShort();
+		nBytesRemaining -=2;
+		field_5_name_definition = Formula.read(formulaLen, in, nBytesRemaining);
 	}
 	/*
 	 * Makes better error messages (while hasFormula() is not reliable) 
@@ -157,7 +153,7 @@
 	private RuntimeException readFail(String msg) {
 		String fullMsg = msg + " fields: (option=" + field_1_option_flag + " index=" + field_2_index 
 		+ " not_used=" + field_3_not_used + " name='" + field_4_name + "')";
-		return new RuntimeException(fullMsg);
+		return new RecordFormatException(fullMsg);
 	}
 
 	private boolean hasFormula() {

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/FormulaRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/FormulaRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/FormulaRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/FormulaRecord.java Thu Oct 30 15:06:10 2008
@@ -20,10 +20,13 @@
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
 
 /**
  * Formula Record (0x0006).
@@ -35,7 +38,7 @@
 public final class FormulaRecord extends Record implements CellValueRecordInterface {
 
 	public static final short sid = 0x0006;   // docs say 406...because of a bug Microsoft support site article #Q184647)
-	private static int FIXED_SIZE = 22;
+	private static int FIXED_SIZE = 20;
 
 	private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
 	private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
@@ -92,9 +95,9 @@
 			}
 			return new SpecialCachedValue(result);
 		}
-		public void serialize(byte[] data, int offset) {
-			System.arraycopy(_variableData, 0, data, offset, VARIABLE_DATA_LENGTH);
-			LittleEndian.putUShort(data, offset+VARIABLE_DATA_LENGTH, 0xFFFF);
+		public void serialize(LittleEndianOutput out) {
+			out.write(_variableData);
+			out.writeShort(0xFFFF);
 		}
 		public String formatDebugString() {
 			return formatValue() + ' ' + HexDump.toHex(_variableData);
@@ -172,8 +175,13 @@
 	private short  field_3_xf;
 	private double field_4_value;
 	private short  field_5_options;
-	private int    field_6_zero;
-	private Ptg[]  field_8_parsed_expr;
+	/**
+	 * Unused field.  As it turns out this field is often not zero..
+	 * According to Microsoft Excel Developer's Kit Page 318:
+	 * when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
+	 */
+	private int field_6_zero;
+	private Formula field_8_parsed_expr;
 
 	/**
 	 * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
@@ -183,13 +191,14 @@
 	/** Creates new FormulaRecord */
 
 	public FormulaRecord() {
-		field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
+		field_8_parsed_expr = Formula.create(Ptg.EMPTY_PTG_ARRAY);
 	}
 
-	public FormulaRecord(RecordInputStream in) {
-		field_1_row	 = in.readUShort();
-		field_2_column  = in.readShort();
-		field_3_xf	  = in.readShort();
+	public FormulaRecord(RecordInputStream ris) {
+		LittleEndianInput in = ris;
+		field_1_row = in.readUShort();
+		field_2_column = in.readShort();
+		field_3_xf = in.readShort();
 		long valueLongBits  = in.readLong();
 		field_5_options = in.readShort();
 		specialCachedValue = SpecialCachedValue.create(valueLongBits);
@@ -197,14 +206,11 @@
 			field_4_value = Double.longBitsToDouble(valueLongBits);
 		}
 
-		field_6_zero		   = in.readInt();
+		field_6_zero = in.readInt();
+
 		int field_7_expression_len = in.readShort(); // this length does not include any extra array data
-		field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in);
-		if (in.remaining() == 10) {
-			// TODO - this seems to occur when IntersectionPtg is present
-			// 10 extra bytes are just 0x01 and 0x00
-			// This causes POI stderr: "WARN. Unread 10 bytes of record 0x6"
-		}
+		int nBytesAvailable = in.available();
+		field_8_parsed_expr = Formula.read(field_7_expression_len, in, nBytesAvailable);
 	}
 
 
@@ -336,11 +342,11 @@
 	 * @return the formula tokens. never <code>null</code>
 	 */
 	public Ptg[] getParsedExpression() {
-		return (Ptg[]) field_8_parsed_expr.clone();
+		return field_8_parsed_expr.getTokens();
 	}
 
 	public void setParsedExpression(Ptg[] ptgs) {
-		field_8_parsed_expr = ptgs;
+		field_8_parsed_expr = Formula.create(ptgs);
 	}
 
 	public short getSid() {
@@ -348,33 +354,30 @@
 	}
 
 	private int getDataSize() {
-		return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr);
+		return FIXED_SIZE + field_8_parsed_expr.getEncodedSize();
 	}
 	public int serialize(int offset, byte [] data) {
 
 		int dataSize = getDataSize();
-
-		LittleEndian.putShort(data, 0 + offset, sid);
-		LittleEndian.putUShort(data, 2 + offset, dataSize);
-		LittleEndian.putUShort(data, 4 + offset, getRow());
-		LittleEndian.putShort(data, 6 + offset, getColumn());
-		LittleEndian.putShort(data, 8 + offset, getXFIndex());
+		int recSize = 4 + dataSize;
+		LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
+		out.writeShort(sid);
+		out.writeShort(dataSize);
+		out.writeShort(getRow());
+		out.writeShort(getColumn());
+		out.writeShort(getXFIndex());
 
 		if (specialCachedValue == null) {
-			LittleEndian.putDouble(data, 10 + offset, field_4_value);
+			out.writeDouble(field_4_value);
 		} else {
-			specialCachedValue.serialize(data, 10+offset);
+			specialCachedValue.serialize(out);
 		}
 
-		LittleEndian.putShort(data, 18 + offset, getOptions());
+		out.writeShort(getOptions());
 
-		//when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
-		//Microsoft Excel Developer's Kit Page 318
-		LittleEndian.putInt(data, 20 + offset, 0);
-		int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr);
-		LittleEndian.putUShort(data, 24 + offset, formulaTokensSize);
-		Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset);
-		return 4 + dataSize;
+		out.writeInt(field_6_zero); // may as well write original data back so as to minimise differences from original
+		field_8_parsed_expr.serialize(out);
+		return recSize;
 	}
 
 	public int getRecordSize() {
@@ -385,24 +388,25 @@
 
 		StringBuffer sb = new StringBuffer();
 		sb.append("[FORMULA]\n");
-		sb.append("	.row	   = ").append(HexDump.shortToHex(getRow())).append("\n");
-		sb.append("	.column	= ").append(HexDump.shortToHex(getColumn())).append("\n");
-		sb.append("	.xf		= ").append(HexDump.shortToHex(getXFIndex())).append("\n");
-		sb.append("	.value	 = ");
+		sb.append("  .row	   = ").append(HexDump.shortToHex(getRow())).append("\n");
+		sb.append("  .column	= ").append(HexDump.shortToHex(getColumn())).append("\n");
+		sb.append("  .xf		= ").append(HexDump.shortToHex(getXFIndex())).append("\n");
+		sb.append("  .value	 = ");
 		if (specialCachedValue == null) {
 			sb.append(field_4_value).append("\n");
 		} else {
 			sb.append(specialCachedValue.formatDebugString()).append("\n");
 		}
-		sb.append("	.options   = ").append(HexDump.shortToHex(getOptions())).append("\n");
-		sb.append("	.alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n");
-		sb.append("	.calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n");
-		sb.append("	.shared	= ").append(sharedFormula.isSet(getOptions())).append("\n");
-		sb.append("	.zero	  = ").append(HexDump.intToHex(field_6_zero)).append("\n");
-
-		for (int k = 0; k < field_8_parsed_expr.length; k++ ) {
-			sb.append("	 Ptg[").append(k).append("]=");
-			Ptg ptg = field_8_parsed_expr[k];
+		sb.append("  .options   = ").append(HexDump.shortToHex(getOptions())).append("\n");
+		sb.append("    .alwaysCalc= ").append(isAlwaysCalc()).append("\n");
+		sb.append("    .calcOnLoad= ").append(isCalcOnLoad()).append("\n");
+		sb.append("    .shared    = ").append(isSharedFormula()).append("\n");
+		sb.append("  .zero      = ").append(HexDump.intToHex(field_6_zero)).append("\n");
+
+		Ptg[] ptgs = field_8_parsed_expr.getTokens();
+		for (int k = 0; k < ptgs.length; k++ ) {
+			sb.append("    Ptg[").append(k).append("]=");
+			Ptg ptg = ptgs[k];
 			sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n");
 		}
 		sb.append("[/FORMULA]\n");
@@ -417,12 +421,7 @@
 		rec.field_4_value = field_4_value;
 		rec.field_5_options = field_5_options;
 		rec.field_6_zero = field_6_zero;
-		int nTokens = field_8_parsed_expr.length;
-		Ptg[] ptgs = new Ptg[nTokens];
-		for (int i = 0; i < nTokens; i++) {
-			ptgs[i] = field_8_parsed_expr[i].copy();
-		}
-		rec.field_8_parsed_expr = ptgs;
+		rec.field_8_parsed_expr = field_8_parsed_expr;
 		rec.specialCachedValue = specialCachedValue;
 		return rec;
 	}

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/NameRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/NameRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/NameRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/NameRecord.java Thu Oct 30 15:06:10 2008
@@ -27,9 +27,11 @@
 import org.apache.poi.hssf.record.formula.UnionPtg;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.RangeAddress;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.ss.util.AreaReference;
 import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianInput;
 import org.apache.poi.util.StringUtil;
 
 /**
@@ -89,7 +91,7 @@
 	private boolean           field_11_nameIsMultibyte;
 	private byte              field_12_built_in_code;
 	private String            field_12_name_text;
-	private Ptg[]             field_13_name_definition;
+	private Formula           field_13_name_definition;
 	private String            field_14_custom_menu_text;
 	private String            field_15_description_text;
 	private String            field_16_help_topic_text;
@@ -98,7 +100,7 @@
 
 	/** Creates new NameRecord */
 	public NameRecord() {
-		field_13_name_definition = Ptg.EMPTY_PTG_ARRAY;
+		field_13_name_definition = Formula.create(Ptg.EMPTY_PTG_ARRAY);
 
 		field_12_name_text = "";
 		field_14_custom_menu_text = "";
@@ -245,7 +247,7 @@
 	 * @return <code>true</code> if name has a formula (named range or defined value)
 	 */
 	public boolean hasFormula() {
-		return field_1_option_flag == 0 && field_13_name_definition.length > 0;
+		return field_1_option_flag == 0 && field_13_name_definition.getEncodedTokenSize() > 0;
 	}
 
 	/**
@@ -296,11 +298,11 @@
 	 * @return the name formula. never <code>null</code>
 	 */
 	public Ptg[] getNameDefinition() {
-		return (Ptg[]) field_13_name_definition.clone();
+		return field_13_name_definition.getTokens();
 	}
 
 	public void setNameDefinition(Ptg[] ptgs) {
-		field_13_name_definition = (Ptg[]) ptgs.clone();
+		field_13_name_definition = Formula.create(ptgs);
 	}
 
 	/** get the custom menu text
@@ -346,9 +348,9 @@
 		int field_9_length_help_topic_text = field_16_help_topic_text.length();
 		int field_10_length_status_bar_text = field_17_status_bar_text.length();
 		int rawNameSize = getNameRawSize();
-		
-		int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition);
-		int dataSize = 15 // 4 shorts + 7 bytes
+
+		int formulaTotalSize = field_13_name_definition.getEncodedSize();
+		int dataSize = 13 // 3 shorts + 7 bytes
 			+ rawNameSize
 			+ field_7_length_custom_menu
 			+ field_8_length_description_text
@@ -356,48 +358,45 @@
 			+ field_10_length_status_bar_text
 			+ formulaTotalSize;
 		
-		LittleEndian.putShort(data, 0 + offset, sid);
-		LittleEndian.putUShort(data, 2 + offset, dataSize);
+		int recSize = 4 + dataSize;
+		LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
+		
+		out.writeShort(sid);
+		out.writeShort(dataSize);
 		// size defined below
-		LittleEndian.putShort(data, 4 + offset, getOptionFlag());
-		LittleEndian.putByte(data, 6 + offset, getKeyboardShortcut());
-		LittleEndian.putByte(data, 7 + offset, getNameTextLength());
-		// Note -
-		LittleEndian.putUShort(data, 8 + offset, Ptg.getEncodedSizeWithoutArrayData(field_13_name_definition));
-		LittleEndian.putUShort(data, 10 + offset, field_5_externSheetIndex_plus1);
-		LittleEndian.putUShort(data, 12 + offset, field_6_sheetNumber);
-		LittleEndian.putByte(data, 14 + offset, field_7_length_custom_menu);
-		LittleEndian.putByte(data, 15 + offset, field_8_length_description_text);
-		LittleEndian.putByte(data, 16 + offset, field_9_length_help_topic_text);
-		LittleEndian.putByte(data, 17 + offset, field_10_length_status_bar_text);
-		LittleEndian.putByte(data, 18 + offset, field_11_nameIsMultibyte ? 1 : 0);
-		int pos = 19 + offset;
+		out.writeShort(getOptionFlag());
+		out.writeByte(getKeyboardShortcut());
+		out.writeByte(getNameTextLength());
+		// Note - formula size is not immediately before encoded formula, and does not include any array constant data
+		out.writeShort(field_13_name_definition.getEncodedTokenSize());
+		out.writeShort(field_5_externSheetIndex_plus1);
+		out.writeShort(field_6_sheetNumber);
+		out.writeByte(field_7_length_custom_menu);
+		out.writeByte(field_8_length_description_text);
+		out.writeByte(field_9_length_help_topic_text);
+		out.writeByte(field_10_length_status_bar_text);
+		out.writeByte(field_11_nameIsMultibyte ? 1 : 0);
 
 		if (isBuiltInName()) {
 			//can send the builtin name directly in
-			LittleEndian.putByte(data, pos,  field_12_built_in_code);
+			out.writeByte(field_12_built_in_code);
 		} else {
 			String nameText = field_12_name_text;
 			if (field_11_nameIsMultibyte) {
-    			StringUtil.putUnicodeLE(nameText, data, pos);
-     		} else {
-    			StringUtil.putCompressedUnicode(nameText, data, pos);
-    		}
+				StringUtil.putUnicodeLE(nameText, out);
+			} else {
+				StringUtil.putCompressedUnicode(nameText, out);
+			}
 		}
-		pos += rawNameSize;
-
-		Ptg.serializePtgs(field_13_name_definition,  data, pos);
-		pos += formulaTotalSize;
+		field_13_name_definition.serializeTokens(out);
+		field_13_name_definition.serializeArrayConstantData(out);
 		
-		StringUtil.putCompressedUnicode( getCustomMenuText(), data, pos);
-		pos += field_7_length_custom_menu;
-		StringUtil.putCompressedUnicode( getDescriptionText(), data, pos);
-		pos += field_8_length_description_text;
-		StringUtil.putCompressedUnicode( getHelpTopicText(), data, pos);
-		pos += field_9_length_help_topic_text;
-		StringUtil.putCompressedUnicode( getStatusBarText(), data, pos);
+		StringUtil.putCompressedUnicode( getCustomMenuText(), out);
+		StringUtil.putCompressedUnicode( getDescriptionText(), out);
+		StringUtil.putCompressedUnicode( getHelpTopicText(), out);
+		StringUtil.putCompressedUnicode( getStatusBarText(), out);
 
-		return 4 + dataSize;
+		return recSize;
 	}
 	private int getNameRawSize() {
 		if (isBuiltInName()) {
@@ -412,23 +411,23 @@
 
 	public int getRecordSize(){
 		return 4 // sid + size
-			+ 15 // 4 shorts + 7 bytes
+			+ 13 // 3 shorts + 7 bytes
 			+ getNameRawSize()
 			+ field_14_custom_menu_text.length()
 			+ field_15_description_text.length()
 			+ field_16_help_topic_text.length()
 			+ field_17_status_bar_text.length()
-			+ Ptg.getEncodedSize(field_13_name_definition);
+			+ field_13_name_definition.getEncodedSize();
 	}
 
 	/** gets the extern sheet number
 	 * @return extern sheet index
 	 */
 	public int getExternSheetNumber(){
-		if (field_13_name_definition.length < 1) {
+		if (field_13_name_definition.getEncodedSize() < 1) {
 			return 0;
 		}
-		Ptg ptg = field_13_name_definition[0];
+		Ptg ptg = field_13_name_definition.getTokens()[0];
 
 		if (ptg.getClass() == Area3DPtg.class){
 			return ((Area3DPtg) ptg).getExternSheetIndex();
@@ -444,15 +443,14 @@
 	 * @param externSheetNumber extern sheet number
 	 */
 	public void setExternSheetNumber(short externSheetNumber){
+		Ptg[] ptgs = field_13_name_definition.getTokens();
 		Ptg ptg;
 
-		if (field_13_name_definition.length < 1){
+		if (ptgs.length < 1){
 			ptg = createNewPtg();
-			field_13_name_definition = new Ptg[] {
-				ptg,
-			};
+			ptgs = new Ptg[] { ptg, };
 		} else {
-			ptg = field_13_name_definition[0];
+			ptg = ptgs[0];
 		}
 
 		if (ptg.getClass() == Area3DPtg.class){
@@ -461,7 +459,7 @@
 		} else if (ptg.getClass() == Ref3DPtg.class){
 			((Ref3DPtg) ptg).setExternSheetIndex(externSheetNumber);
 		}
-
+		field_13_name_definition = Formula.create(ptgs);
 	}
 
 	private static Ptg createNewPtg(){
@@ -472,7 +470,7 @@
 	 * @return area reference
 	 */
 	public String getAreaReference(HSSFWorkbook book){
-		return HSSFFormulaParser.toFormulaString(book, field_13_name_definition);
+		return HSSFFormulaParser.toFormulaString(book, field_13_name_definition.getTokens());
 	}
 
 	/** sets the reference , the area only (range)
@@ -483,11 +481,11 @@
 		RangeAddress ra = new RangeAddress(ref);
 		Ptg oldPtg;
 
-		if (field_13_name_definition.length < 1){
+		if (field_13_name_definition.getEncodedTokenSize() < 1){
 			oldPtg = createNewPtg();
 		} else {
 			//Trying to find extern sheet index
-			oldPtg = field_13_name_definition[0];
+			oldPtg = field_13_name_definition.getTokens()[0];
 		}
 		List temp = new ArrayList();
 		int externSheetIndex = 0;
@@ -519,7 +517,7 @@
 		}
 		Ptg[] ptgs = new Ptg[temp.size()];
 		temp.toArray(ptgs);
-		field_13_name_definition = ptgs;
+		field_13_name_definition = Formula.create(ptgs);
 	}
 
 	/**
@@ -528,17 +526,18 @@
 	 *
 	 * @param in the RecordInputstream to read the record from
 	 */
-	public NameRecord(RecordInputStream in) {
+	public NameRecord(RecordInputStream ris) {
+		LittleEndianInput in = ris;
 		field_1_option_flag                 = in.readShort();
 		field_2_keyboard_shortcut           = in.readByte();
 		int field_3_length_name_text        = in.readByte();
 		int field_4_length_name_definition  = in.readShort();
 		field_5_externSheetIndex_plus1      = in.readShort();
 		field_6_sheetNumber                 = in.readUShort();
-		int field_7_length_custom_menu      = in.readUByte();
-		int field_8_length_description_text = in.readUByte();
-		int field_9_length_help_topic_text  = in.readUByte();
-		int field_10_length_status_bar_text = in.readUByte();
+		int f7_customMenuLen      = in.readUByte();
+		int f8_descriptionTextLen = in.readUByte();
+		int f9_helpTopicTextLen  = in.readUByte();
+		int f10_statusBarTextLen = in.readUByte();
 
 		//store the name in byte form if it's a built-in name
 		field_11_nameIsMultibyte = (in.readByte() != 0);
@@ -546,19 +545,21 @@
 			field_12_built_in_code = in.readByte();
 		} else {
 			if (field_11_nameIsMultibyte) {
-				field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
+				field_12_name_text = StringUtil.readUnicodeLE(in, field_3_length_name_text);
 			} else {
-				field_12_name_text = in.readCompressedUnicode(field_3_length_name_text);
+				field_12_name_text = StringUtil.readCompressedUnicode(in, field_3_length_name_text);
 			}
 		}
 
-		field_13_name_definition = Ptg.readTokens(field_4_length_name_definition, in);
+		int nBytesAvailable = in.available() - (f7_customMenuLen 
+				+ f8_descriptionTextLen + f9_helpTopicTextLen + f10_statusBarTextLen);
+		field_13_name_definition = Formula.read(field_4_length_name_definition, in, nBytesAvailable);
 
 		//Who says that this can only ever be compressed unicode???
-		field_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu);
-		field_15_description_text = in.readCompressedUnicode(field_8_length_description_text);
-		field_16_help_topic_text  = in.readCompressedUnicode(field_9_length_help_topic_text);
-		field_17_status_bar_text  = in.readCompressedUnicode(field_10_length_status_bar_text);
+		field_14_custom_menu_text = StringUtil.readCompressedUnicode(in, f7_customMenuLen);
+		field_15_description_text = StringUtil.readCompressedUnicode(in, f8_descriptionTextLen);
+		field_16_help_topic_text  = StringUtil.readCompressedUnicode(in, f9_helpTopicTextLen);
+		field_17_status_bar_text  = StringUtil.readCompressedUnicode(in, f10_statusBarTextLen);
 	}
 
 	/**
@@ -633,9 +634,10 @@
 		sb.append("    .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n");
 		sb.append("    .NameIsMultibyte        = ").append(field_11_nameIsMultibyte).append("\n");
 		sb.append("    .Name (Unicode text)    = ").append( getNameText() ).append("\n");
-		sb.append("    .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n");
-		for (int i = 0; i < field_13_name_definition.length; i++) {
-			Ptg ptg = field_13_name_definition[i];
+		Ptg[] ptgs = field_13_name_definition.getTokens();
+		sb.append("    .Formula (nTokens=").append(ptgs.length).append("):") .append("\n");
+		for (int i = 0; i < ptgs.length; i++) {
+			Ptg ptg = ptgs[i];
 			sb.append("       " + ptg.toString()).append(ptg.getRVAType()).append("\n");
 		}
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ObjRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ObjRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ObjRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ObjRecord.java Thu Oct 30 15:06:10 2008
@@ -18,15 +18,13 @@
 package org.apache.poi.hssf.record;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 import org.apache.poi.util.LittleEndianInputStream;
-import org.apache.poi.util.LittleEndianOutput;
-import org.apache.poi.util.LittleEndianOutputStream;
 
 /**
  * OBJRECORD (0x005D)<p/>
@@ -37,10 +35,17 @@
  */
 public final class ObjRecord extends Record {
 	public final static short sid = 0x005D;
+
+	private static final int NORMAL_PAD_ALIGNMENT = 2;
+	private static int MAX_PAD_ALIGNMENT = 4;
 	
 	private List subrecords;
 	/** used when POI has no idea what is going on */
 	private byte[] _uninterpretedData;
+	/**
+	 * Excel seems to tolerate padding to quad or double byte length
+	 */
+	private boolean _isPaddedToQuadByteMultiple;
 
 	//00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
 	//00000010 26 01 00 00 00 00 00 00 00 00                   &.........
@@ -71,6 +76,10 @@
 			_uninterpretedData = subRecordData;
 			return;
 		}
+		if (subRecordData.length % 2 != 0) {
+			String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData);
+			throw new RecordFormatException(msg);
+		}
 
 //		System.out.println(HexDump.toHex(subRecordData));
 
@@ -84,12 +93,17 @@
 				break;
 			}
 		}
-		if (bais.available() > 0) {
-			// earlier versions of the code had allowances for padding
-			// At present (Oct-2008), no unit test samples exhibit such padding
-			String msg = "Leftover " + bais.available() 
+		int nRemainingBytes = bais.available();
+		if (nRemainingBytes > 0) {
+			// At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
+			_isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;
+			if (nRemainingBytes >= (_isPaddedToQuadByteMultiple ? MAX_PAD_ALIGNMENT : NORMAL_PAD_ALIGNMENT)) {
+				String msg = "Leftover " + nRemainingBytes 
 				+ " bytes in subrecord data " + HexDump.toHex(subRecordData);
-			throw new RecordFormatException(msg);
+				throw new RecordFormatException(msg);
+			}
+		} else {
+			_isPaddedToQuadByteMultiple = false;
 		}
 	}
 
@@ -114,34 +128,41 @@
 			SubRecord record = (SubRecord) subrecords.get(i);
 			size += record.getDataSize()+4;
 		}
+		if (_isPaddedToQuadByteMultiple) {
+			while (size % MAX_PAD_ALIGNMENT != 0) {
+				size++;
+			}
+		} else {
+			while (size % NORMAL_PAD_ALIGNMENT != 0) {
+				size++;
+			}
+		}
 		return size;
 	}
 
 	public int serialize(int offset, byte[] data) {
 		int dataSize = getDataSize();
+		int recSize = 4 + dataSize;
+		LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
 
-		LittleEndian.putUShort(data, 0 + offset, sid);
-		LittleEndian.putUShort(data, 2 + offset, dataSize);
+		out.writeShort(sid);
+		out.writeShort(dataSize);
 
-		byte[] subRecordBytes;
 		if (_uninterpretedData == null) {
-			ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
-			LittleEndianOutput leo = new LittleEndianOutputStream(baos);
 
 			for (int i = 0; i < subrecords.size(); i++) {
 				SubRecord record = (SubRecord) subrecords.get(i);
-				record.serialize(leo);
+				record.serialize(out);
 			}
+			int expectedEndIx = offset+dataSize;
 			// padding
-			while (baos.size() < dataSize) {
-				baos.write(0);
+			while (out.getWriteIndex() < expectedEndIx) {
+				out.writeByte(0);
 			}
-			subRecordBytes = baos.toByteArray();
 		} else {
-			subRecordBytes = _uninterpretedData;
+			out.write(_uninterpretedData);
 		}
-		System.arraycopy(subRecordBytes, 0, data, offset + 4, dataSize);
-		return 4 + dataSize;
+		return recSize;
 	}
 
 	public int getRecordSize() {

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java Thu Oct 30 15:06:10 2008
@@ -355,14 +355,14 @@
     //growable array of the data.
     ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE);
 
-    while (isContinueNext()) {
+    while (true) {
       byte[] b = readRemainder();
       out.write(b, 0, b.length);
+      if (!isContinueNext()) {
+          break;
+      }
       nextRecord();
     }
-    byte[] b = readRemainder();
-    out.write(b, 0, b.length);
-
     return out.toByteArray();
   }
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java Thu Oct 30 15:06:10 2008
@@ -22,7 +22,10 @@
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.RefNPtg;
 import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
+import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndianOutput;
 
 /**
  * Title:        SHAREDFMLA (0x04BC) SharedFormulaRecord
@@ -39,10 +42,15 @@
     public final static short   sid = 0x04BC;
 
     private int field_5_reserved;
-    private Ptg[] field_7_parsed_expr;
+    private Formula field_7_parsed_expr;
 
+    // for testing only
     public SharedFormulaRecord() {
-        field_7_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
+        this(new CellRangeAddress8Bit(0,0,0,0));
+    }
+    private SharedFormulaRecord(CellRangeAddress8Bit range) {
+        super(range);
+        field_7_parsed_expr = Formula.create(Ptg.EMPTY_PTG_ARRAY);
     }
 
     /**
@@ -52,17 +60,17 @@
         super(in);
         field_5_reserved        = in.readShort();
         int field_6_expression_len = in.readShort();
-        field_7_parsed_expr = Ptg.readTokens(field_6_expression_len, in);
+        int nAvailableBytes = in.available();
+        field_7_parsed_expr = Formula.read(field_6_expression_len, in, nAvailableBytes);
     }
-    protected void serializeExtraData(int offset, byte[] data) {
-        //Because this record is converted to individual Formula records, this method is not required.
-        throw new UnsupportedOperationException("Cannot serialize a SharedFormulaRecord");
+
+    protected void serializeExtraData(LittleEndianOutput out) {
+        out.writeShort(field_5_reserved);
+        field_7_parsed_expr.serialize(out);
     }
     
     protected int getExtraDataSize() {
-        //Because this record is converted to individual Formula records, this method is not required.
-        throw new UnsupportedOperationException("Cannot get the size for a SharedFormulaRecord");
-
+        return 2 + field_7_parsed_expr.getEncodedSize();
     }
 
     /**
@@ -77,9 +85,10 @@
         buffer.append("    .range      = ").append(getRange().toString()).append("\n");
         buffer.append("    .reserved    = ").append(HexDump.shortToHex(field_5_reserved)).append("\n");
 
-        for (int k = 0; k < field_7_parsed_expr.length; k++ ) {
+        Ptg[] ptgs = field_7_parsed_expr.getTokens();
+        for (int k = 0; k < ptgs.length; k++ ) {
            buffer.append("Formula[").append(k).append("]");
-           Ptg ptg = field_7_parsed_expr[k];
+           Ptg ptg = ptgs[k];
            buffer.append(ptg.toString()).append(ptg.getRVAType()).append("\n");
         }
 
@@ -92,20 +101,13 @@
     }
 
     /**
-     * Creates a non shared formula from the shared formula
-     * counter part
+     * Creates a non shared formula from the shared formula counterpart<br/>
+     * 
+     * Perhaps this functionality could be implemented in terms of the raw 
+     * byte array inside {@link Formula}.
      */
-    protected static Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
-        if(false) {
-            /*
-             * TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
-             * If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records.
-             * Disabling this code breaks one existing junit.
-             * Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly.
-             * That method will need 2 extra params: rowIx and colIx.
-             */
-            return ptgs;
-        }
+    static Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
+
         Ptg[] newPtgStack = new Ptg[ptgs.length];
 
         for (int k = 0; k < ptgs.length; k++) {
@@ -145,10 +147,9 @@
     }
 
     /**
-     * Creates a non shared formula from the shared formula
-     * counter part
+     * @return the equivalent {@link Ptg} array that the formula would have, were it not shared.
      */
-    public void convertSharedFormulaRecord(FormulaRecord formula) {
+    public Ptg[] getFormulaTokens(FormulaRecord formula) {
         int formulaRow = formula.getRow();
         int formulaColumn = formula.getColumn();
         //Sanity checks
@@ -156,10 +157,7 @@
             throw new RuntimeException("Shared Formula Conversion: Coding Error");
         }
 
-        Ptg[] ptgs = convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
-        formula.setParsedExpression(ptgs);
-        //Now its not shared!
-        formula.setSharedFormula(false);
+        return convertSharedFormulas(field_7_parsed_expr.getTokens(), formulaRow, formulaColumn);
     }
 
     private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
@@ -179,7 +177,9 @@
     }
 
     public Object clone() {
-        //Because this record is converted to individual Formula records, this method is not required.
-        throw new UnsupportedOperationException("Cannot clone a SharedFormulaRecord");
+        SharedFormulaRecord result = new SharedFormulaRecord(getRange());
+        result.field_5_reserved = field_5_reserved;
+        result.field_7_parsed_expr = field_7_parsed_expr.copy();
+        return result;
     }
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java Thu Oct 30 15:06:10 2008
@@ -18,7 +18,9 @@
 package org.apache.poi.hssf.record;
 
 import org.apache.poi.hssf.util.CellRangeAddress8Bit;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
 
 /**
  * Common base class for {@link SharedFormulaRecord}, {@link ArrayRecord} and
@@ -41,7 +43,7 @@
 	/**
 	 * reads only the range (1 {@link CellRangeAddress8Bit}) from the stream
 	 */
-	public SharedValueRecordBase(RecordInputStream in) {
+	public SharedValueRecordBase(LittleEndianInput in) {
 		_range = new CellRangeAddress8Bit(in);
 	}
 
@@ -71,19 +73,19 @@
 
 	protected abstract int getExtraDataSize();
 
-	protected abstract void serializeExtraData(int offset, byte[] data);
+	protected abstract void serializeExtraData(LittleEndianOutput out);
 
 	public final int serialize(int offset, byte[] data) {
 		int dataSize = CellRangeAddress8Bit.ENCODED_SIZE + getExtraDataSize();
-
-		LittleEndian.putShort(data, 0 + offset, getSid());
-		LittleEndian.putUShort(data, 2 + offset, dataSize);
-
-		int pos = offset + 4;
-		_range.serialize(pos, data);
-		pos += CellRangeAddress8Bit.ENCODED_SIZE;
-		serializeExtraData(pos, data);
-		return dataSize + 4;
+		
+		int totalRecSize = dataSize + 4;
+		LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, totalRecSize);
+		out.writeShort(getSid());
+		out.writeShort(dataSize);
+
+		_range.serialize(out);
+		serializeExtraData(out);
+		return totalRecSize;
 	}
 
 	/**

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TableRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TableRecord.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TableRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TableRecord.java Thu Oct 30 15:06:10 2008
@@ -23,7 +23,7 @@
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianOutput;
 /**
  * DATATABLE (0x0236)<p/>
  *
@@ -146,13 +146,13 @@
 		2 // 2 byte fields
 		+ 8; // 4 short fields
 	}
-	protected void serializeExtraData(int offset, byte[] data) {
-		LittleEndian.putByte(data, 0 + offset, field_5_flags);
-		LittleEndian.putByte(data, 1 + offset, field_6_res);
-		LittleEndian.putUShort(data, 2 + offset, field_7_rowInputRow);
-		LittleEndian.putUShort(data, 4 + offset, field_8_colInputRow);
-		LittleEndian.putUShort(data, 6 + offset, field_9_rowInputCol);
-		LittleEndian.putUShort(data, 8 + offset, field_10_colInputCol);
+	protected void serializeExtraData(LittleEndianOutput out) {
+		out.writeByte(field_5_flags);
+		out.writeByte(field_6_res);
+		out.writeShort(field_7_rowInputRow);
+		out.writeShort(field_8_colInputRow);
+		out.writeShort(field_9_rowInputCol);
+		out.writeShort(field_10_colInputCol);
 	}
 
 	public String toString() {

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java Thu Oct 30 15:06:10 2008
@@ -21,7 +21,10 @@
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.record.SharedFormulaRecord;
 import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.formula.ExpPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 
 /**
  * The formula record aggregate is used to join together the formula record and it's
@@ -31,113 +34,177 @@
  */
 public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
 
-    private final FormulaRecord _formulaRecord;
-    private SharedValueManager _sharedValueManager;
-    /** caches the calculated result of the formula */
-    private StringRecord _stringRecord;
-
-    /**
-     * @param stringRec may be <code>null</code> if this formula does not have a cached text
-     * value.
-     * @param svm the {@link SharedValueManager} for the current sheet
-     */
-    public FormulaRecordAggregate(FormulaRecord formulaRec, StringRecord stringRec, SharedValueManager svm) {
-        if (svm == null) {
-            throw new IllegalArgumentException("sfm must not be null");
-        }
-        boolean hasStringRec = stringRec != null;
-        boolean hasCachedStringFlag = formulaRec.hasCachedResultString();
-        if (hasStringRec != hasCachedStringFlag) {
-            throw new RecordFormatException("String record was "
-                    + (hasStringRec ? "": "not ") + " supplied but formula record flag is "
-                    + (hasCachedStringFlag ? "" : "not ") + " set");
-        }
-
-        if (formulaRec.isSharedFormula()) {
-            svm.convertSharedFormulaRecord(formulaRec);
-        }
-        _formulaRecord = formulaRec;
-        _sharedValueManager = svm;
-        _stringRecord = stringRec;
-    }
-
-    public FormulaRecord getFormulaRecord() {
-        return _formulaRecord;
-    }
-
-    /**
-     * debug only
-     * TODO - encapsulate
-     */
-    public StringRecord getStringRecord() {
-        return _stringRecord;
-    }
-
-    public short getXFIndex() {
-        return _formulaRecord.getXFIndex();
-    }
-
-    public void setXFIndex(short xf) {
-        _formulaRecord.setXFIndex(xf);
-    }
-
-    public void setColumn(short col) {
-        _formulaRecord.setColumn(col);
-    }
-
-    public void setRow(int row) {
-        _formulaRecord.setRow(row);
-    }
-
-    public short getColumn() {
-        return _formulaRecord.getColumn();
-    }
-
-    public int getRow() {
-        return _formulaRecord.getRow();
-    }
-
-    public String toString() {
-        return _formulaRecord.toString();
-    }
-
-    public void visitContainedRecords(RecordVisitor rv) {
-         rv.visitRecord(_formulaRecord);
-         Record sharedFormulaRecord = _sharedValueManager.getRecordForFirstCell(_formulaRecord);
-         if (sharedFormulaRecord != null) {
-             rv.visitRecord(sharedFormulaRecord);
-         }
-         if (_stringRecord != null) {
-             rv.visitRecord(_stringRecord);
-         }
-    }
-
-    public String getStringValue() {
-        if(_stringRecord==null) {
-            return null;
-        }
-        return _stringRecord.getString();
-    }
-
-    public void setCachedStringResult(String value) {
-
-        // Save the string into a String Record, creating one if required
-        if(_stringRecord == null) {
-            _stringRecord = new StringRecord();
-        }
-        _stringRecord.setString(value);
-        if (value.length() < 1) {
-            _formulaRecord.setCachedResultTypeEmptyString();
-        } else {
-            _formulaRecord.setCachedResultTypeString();
-        }
-    }
-    public void setCachedBooleanResult(boolean value) {
-        _stringRecord = null;
-        _formulaRecord.setCachedResultBoolean(value);
-    }
-    public void setCachedErrorResult(int errorCode) {
-        _stringRecord = null;
-        _formulaRecord.setCachedResultErrorCode(errorCode);
-    }
+	private final FormulaRecord _formulaRecord;
+	private SharedValueManager _sharedValueManager;
+	/** caches the calculated result of the formula */
+	private StringRecord _stringRecord;
+	private SharedFormulaRecord _sharedFormulaRecord;
+
+	/**
+	 * @param stringRec may be <code>null</code> if this formula does not have a cached text
+	 * value.
+	 * @param svm the {@link SharedValueManager} for the current sheet
+	 */
+	public FormulaRecordAggregate(FormulaRecord formulaRec, StringRecord stringRec, SharedValueManager svm) {
+		if (svm == null) {
+			throw new IllegalArgumentException("sfm must not be null");
+		}
+		boolean hasStringRec = stringRec != null;
+		boolean hasCachedStringFlag = formulaRec.hasCachedResultString();
+		if (hasStringRec != hasCachedStringFlag) {
+			throw new RecordFormatException("String record was "
+					+ (hasStringRec ? "": "not ") + " supplied but formula record flag is "
+					+ (hasCachedStringFlag ? "" : "not ") + " set");
+		}
+
+		_formulaRecord = formulaRec;
+		_sharedValueManager = svm;
+		_stringRecord = stringRec;
+		if (formulaRec.isSharedFormula()) {
+			_sharedFormulaRecord = svm.linkSharedFormulaRecord(this);
+			if (_sharedFormulaRecord == null) {
+				handleMissingSharedFormulaRecord(formulaRec);
+			}
+		}
+	}
+	/**
+	 * Sometimes the shared formula flag "seems" to be erroneously set (because the corresponding
+	 * {@link SharedFormulaRecord} does not exist). Normally this would leave no way of determining
+	 * the {@link Ptg} tokens for the formula.  However as it turns out in these 
+	 * cases, Excel encodes the unshared {@link Ptg} tokens in the right place (inside the {@link
+	 * FormulaRecord}).  So the the only thing that needs to be done is to ignore the erroneous
+	 * shared formula flag.<br/>
+	 * 
+	 * This method may also be used for setting breakpoints to help diagnose issues regarding the
+	 * abnormally-set 'shared formula' flags. 
+	 * (see TestValueRecordsAggregate.testSpuriousSharedFormulaFlag()).<p/>
+	 */
+	private static void handleMissingSharedFormulaRecord(FormulaRecord formula) {
+		// make sure 'unshared' formula is actually available
+		Ptg firstToken = formula.getParsedExpression()[0]; 
+		if (firstToken instanceof ExpPtg) {
+			throw new RecordFormatException(
+					"SharedFormulaRecord not found for FormulaRecord with (isSharedFormula=true)");
+		}
+		// could log an info message here since this is a fairly unusual occurrence.
+		formula.setSharedFormula(false); // no point leaving the flag erroneously set
+	}
+
+	public FormulaRecord getFormulaRecord() {
+		return _formulaRecord;
+	}
+
+	/**
+	 * debug only
+	 * TODO - encapsulate
+	 */
+	public StringRecord getStringRecord() {
+		return _stringRecord;
+	}
+
+	public short getXFIndex() {
+		return _formulaRecord.getXFIndex();
+	}
+
+	public void setXFIndex(short xf) {
+		_formulaRecord.setXFIndex(xf);
+	}
+
+	public void setColumn(short col) {
+		_formulaRecord.setColumn(col);
+	}
+
+	public void setRow(int row) {
+		_formulaRecord.setRow(row);
+	}
+
+	public short getColumn() {
+		return _formulaRecord.getColumn();
+	}
+
+	public int getRow() {
+		return _formulaRecord.getRow();
+	}
+
+	public String toString() {
+		return _formulaRecord.toString();
+	}
+
+	public void visitContainedRecords(RecordVisitor rv) {
+		 rv.visitRecord(_formulaRecord);
+		 // TODO - only bother with this if array or table formula
+		 Record sharedFormulaRecord = _sharedValueManager.getRecordForFirstCell(_formulaRecord);
+		 if (sharedFormulaRecord != null) {
+			 rv.visitRecord(sharedFormulaRecord);
+		 }
+		 if (_stringRecord != null) {
+			 rv.visitRecord(_stringRecord);
+		 }
+	}
+
+	public String getStringValue() {
+		if(_stringRecord==null) {
+			return null;
+		}
+		return _stringRecord.getString();
+	}
+
+	public void setCachedStringResult(String value) {
+
+		// Save the string into a String Record, creating one if required
+		if(_stringRecord == null) {
+			_stringRecord = new StringRecord();
+		}
+		_stringRecord.setString(value);
+		if (value.length() < 1) {
+			_formulaRecord.setCachedResultTypeEmptyString();
+		} else {
+			_formulaRecord.setCachedResultTypeString();
+		}
+	}
+	public void setCachedBooleanResult(boolean value) {
+		_stringRecord = null;
+		_formulaRecord.setCachedResultBoolean(value);
+	}
+	public void setCachedErrorResult(int errorCode) {
+		_stringRecord = null;
+		_formulaRecord.setCachedResultErrorCode(errorCode);
+	}
+
+	public Ptg[] getFormulaTokens() {
+		if (_sharedFormulaRecord == null) {
+			return _formulaRecord.getParsedExpression();
+		}
+		return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
+	}
+
+	/**
+	 * Also checks for a related shared formula and unlinks it if found
+	 */
+	public void setParsedExpression(Ptg[] ptgs) {
+		notifyFormulaChanging();
+		_formulaRecord.setParsedExpression(ptgs);
+	}
+
+	public void unlinkSharedFormula() {
+		SharedFormulaRecord sfr = _sharedFormulaRecord;
+		if (sfr == null) {
+			throw new IllegalStateException("Formula not linked to shared formula");
+		}
+		Ptg[] ptgs = sfr.getFormulaTokens(_formulaRecord);
+		_formulaRecord.setParsedExpression(ptgs);
+		//Now its not shared!
+		_formulaRecord.setSharedFormula(false);
+		_sharedFormulaRecord = null;
+	}
+	/**
+	 * Should be called by any code which is either deleting this formula cell, or changing
+	 * its type.  This method gives the aggregate a chance to unlink any shared formula
+	 * that may be involved with this cell formula.
+	 */
+	public void notifyFormulaChanging() {
+		if (_sharedFormulaRecord != null) {
+			_sharedValueManager.unlink(_sharedFormulaRecord);
+		}
+	}
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RecordAggregate.java Thu Oct 30 15:06:10 2008
@@ -19,7 +19,6 @@
 
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordBase;
-import org.apache.poi.hssf.record.RecordInputStream;
 
 /**
  * <tt>RecordAggregate</tt>s are groups of of BIFF <tt>Record</tt>s that are typically stored 
@@ -29,16 +28,6 @@
  * @author Josh Micich
  */
 public abstract class RecordAggregate extends RecordBase {
-	// TODO - delete these methods when all subclasses have been converted
-	protected final void validateSid(short id) {
-		throw new RuntimeException("Should not be called");
-	}
-	protected final void fillFields(RecordInputStream in) {
-		throw new RuntimeException("Should not be called");
-	}
-	public final short getSid() {
-		throw new RuntimeException("Should not be called");
-	}
 
 	/**
 	 * Visit each of the atomic BIFF records contained in this {@link RecordAggregate} in the order

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java Thu Oct 30 15:06:10 2008
@@ -321,39 +321,38 @@
         return currentRow-1;
     }
 
-    public int writeHidden( RowRecord rowRecord, int row, boolean hidden )
-    {
+    /**
+     * Hide all rows at or below the current outline level
+     * @return index of the <em>next<em> row after the last row that gets hidden
+     */
+    private int writeHidden(RowRecord pRowRecord, int row) {
+        int rowIx = row;
+        RowRecord rowRecord = pRowRecord;
         int level = rowRecord.getOutlineLevel();
-        while (rowRecord != null && this.getRow(row).getOutlineLevel() >= level)
-        {
-            rowRecord.setZeroHeight( hidden );
-            row++;
-            rowRecord = this.getRow( row );
+        while (rowRecord != null && getRow(rowIx).getOutlineLevel() >= level) {
+            rowRecord.setZeroHeight(true);
+            rowIx++;
+            rowRecord = getRow(rowIx);
         }
-        return row - 1;
+        return rowIx;
     }
 
-    public void collapseRow( int rowNumber )
-    {
+    public void collapseRow(int rowNumber) {
 
         // Find the start of the group.
-        int startRow = findStartOfRowOutlineGroup( rowNumber );
-        RowRecord rowRecord = getRow( startRow );
+        int startRow = findStartOfRowOutlineGroup(rowNumber);
+        RowRecord rowRecord = getRow(startRow);
 
         // Hide all the columns until the end of the group
-        int lastRow = writeHidden( rowRecord, startRow, true );
+        int nextRowIx = writeHidden(rowRecord, startRow);
 
-        // Write collapse field
-        if (getRow(lastRow + 1) != null)
-        {
-            getRow(lastRow + 1).setColapsed( true );
-        }
-        else
-        {
-            RowRecord row = createRow( lastRow + 1);
-            row.setColapsed( true );
-            insertRow( row );
+        RowRecord row = getRow(nextRowIx);
+        if (row == null) {
+            row = createRow(nextRowIx);
+            insertRow(row);
         }
+        // Write collapse field
+        row.setColapsed(true);
     }
 
     /**
@@ -500,6 +499,9 @@
         _valuesAgg.insertCell(cvRec);
     }
     public void removeCell(CellValueRecordInterface cvRec) {
+        if (cvRec instanceof FormulaRecordAggregate) {
+            ((FormulaRecordAggregate)cvRec).notifyFormulaChanging();
+        }
         _valuesAgg.removeCell(cvRec);
     }
     public FormulaRecordAggregate createFormula(int row, int col) {

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java Thu Oct 30 15:06:10 2008
@@ -17,6 +17,9 @@
 
 package org.apache.poi.hssf.record.aggregates;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.poi.hssf.record.ArrayRecord;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.SharedFormulaRecord;
@@ -35,18 +38,72 @@
  * @author Josh Micich
  */
 public final class SharedValueManager {
+	
+	// This class should probably be generalised to handle array and table groups too
+	private static final class SharedValueGroup {
+		private final SharedValueRecordBase _svr;
+		private final FormulaRecordAggregate[] _frAggs;
+		private int _numberOfFormulas;
+
+		public SharedValueGroup(SharedValueRecordBase svr) {
+			_svr = svr;
+			int width = svr.getLastColumn() - svr.getFirstColumn() + 1;
+			int height = svr.getLastRow() - svr.getFirstRow() + 1;
+			_frAggs = new FormulaRecordAggregate[width * height];
+			_numberOfFormulas = 0;
+		}
+
+		public void add(FormulaRecordAggregate agg) {
+			_frAggs[_numberOfFormulas++] = agg;
+		}
+
+		public void unlinkSharedFormulas() {
+			for (int i = 0; i < _numberOfFormulas; i++) {
+				_frAggs[i].unlinkSharedFormula();
+			}
+		}
+
+		public boolean isInRange(int rowIx, int columnIx) {
+			return _svr.isInRange(rowIx, columnIx);
+		}
+
+		public SharedValueRecordBase getSVR() {
+			return _svr;
+		}
+
+		/**
+		 * Note - Sometimes the first formula in a group is not present (because the range
+		 * is sparsely populated), so this method can return <code>true</code> for a cell
+		 * that is not the top-left corner of the range.
+		 * @return <code>true</code> if this is the first formula cell in the group
+		 */
+		public boolean isFirstCell(int row, int column) {
+			// hack for the moment, just check against the first formula that 
+			// came in through the add() method.
+			FormulaRecordAggregate fra = _frAggs[0];
+			return fra.getRow() == row && fra.getColumn() == column;
+		}
+		
+	}
 
 	public static final SharedValueManager EMPTY = new SharedValueManager(
 			new SharedFormulaRecord[0], new ArrayRecord[0], new TableRecord[0]);
-	private final SharedFormulaRecord[] _sfrs;
 	private final ArrayRecord[] _arrayRecords;
 	private final TableRecord[] _tableRecords;
+	private final Map _groupsBySharedFormulaRecord;
+	/** cached for optimization purposes */
+	private SharedValueGroup[] _groups;
 
 	private SharedValueManager(SharedFormulaRecord[] sharedFormulaRecords,
 			ArrayRecord[] arrayRecords, TableRecord[] tableRecords) {
-		_sfrs = sharedFormulaRecords;
 		_arrayRecords = arrayRecords;
 		_tableRecords = tableRecords;
+		Map m = new HashMap(sharedFormulaRecords.length * 3 / 2);
+		for (int i = 0; i < sharedFormulaRecords.length; i++) {
+			SharedFormulaRecord sfr = sharedFormulaRecords[i];
+			m.put(sfr, new SharedValueGroup(sfr));
+		}
+		_groupsBySharedFormulaRecord = m;
 	}
 
 	/**
@@ -64,42 +121,42 @@
 		return new SharedValueManager(sharedFormulaRecords, arrayRecords, tableRecords);
 	}
 
-	public void convertSharedFormulaRecord(FormulaRecord formula) {
+
+	/**
+	 * @return <code>null</code> if the specified formula does not have any corresponding
+	 * {@link SharedFormulaRecord}
+	 */
+	public SharedFormulaRecord linkSharedFormulaRecord(FormulaRecordAggregate agg) {
+		FormulaRecord formula = agg.getFormulaRecord();
 		int row = formula.getRow();
 		int column = formula.getColumn();
 		// Traverse the list of shared formulas in
 		// reverse order, and try to find the correct one
 		// for us
-		for (int i = 0; i < _sfrs.length; i++) {
-			SharedFormulaRecord shrd = _sfrs[i];
-			if (shrd.isInRange(row, column)) {
-				shrd.convertSharedFormulaRecord(formula);
-				return;
+		
+		SharedValueGroup[] groups = getGroups();
+		for (int i = 0; i < groups.length; i++) {
+			SharedValueGroup svr = groups[i];
+			if (svr.isInRange(row, column)) {
+				svr.add(agg);
+				return (SharedFormulaRecord) svr.getSVR();
 			}
 		}
-		// not found
-		handleMissingSharedFormulaRecord(formula);
+		return null;
 	}
 
-	/**
-	 * Sometimes the shared formula flag "seems" to be erroneously set, in which case there is no 
-	 * call to <tt>SharedFormulaRecord.convertSharedFormulaRecord</tt> and hence the 
-	 * <tt>parsedExpression</tt> field of this <tt>FormulaRecord</tt> will not get updated.<br/>
-	 * As it turns out, this is not a problem, because in these circumstances, the existing value
-	 * for <tt>parsedExpression</tt> is perfectly OK.<p/>
-	 * 
-	 * This method may also be used for setting breakpoints to help diagnose issues regarding the
-	 * abnormally-set 'shared formula' flags. 
-	 * (see TestValueRecordsAggregate.testSpuriousSharedFormulaFlag()).<p/>
-	 * 
-	 * The method currently does nothing but do not delete it without finding a nice home for this 
-	 * comment.
-	 */
-	private static void handleMissingSharedFormulaRecord(FormulaRecord formula) {
-		// could log an info message here since this is a fairly unusual occurrence.
-		formula.setSharedFormula(false); // no point leaving the flag erroneously set
+	private SharedValueGroup[] getGroups() {
+		if (_groups == null) {
+			SharedValueGroup[] groups = new SharedValueGroup[_groupsBySharedFormulaRecord.size()];
+			_groupsBySharedFormulaRecord.values().toArray(groups);
+			_groups = groups;
+			
+		}
+		return _groups;
 	}
 
+
+
 	/**
 	 * Note - does not return SharedFormulaRecords currently, because the corresponding formula
 	 * records have been converted to 'unshared'. POI does not attempt to re-share formulas. On
@@ -125,6 +182,26 @@
 				return ar;
 			}
 		}
+		SharedValueGroup[] groups = getGroups();
+		for (int i = 0; i < groups.length; i++) {
+			SharedValueGroup svg = groups[i];
+			if (svg.isFirstCell(row, column)) {
+				return svg.getSVR();
+			}
+		}
 		return null;
 	}
+
+	/**
+	 * Converts all {@link FormulaRecord}s handled by <tt>sharedFormulaRecord</tt> 
+	 * to plain unshared formulas
+	 */
+	public void unlink(SharedFormulaRecord sharedFormulaRecord) {
+		SharedValueGroup svg = (SharedValueGroup) _groupsBySharedFormulaRecord.remove(sharedFormulaRecord);
+		_groups = null; // be sure to reset cached value
+		if (svg == null) {
+			throw new IllegalStateException("Failed to find formulas for shared formula");
+		}
+		svg.unlinkSharedFormulas();
+	}
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java?rev=709262&r1=709261&r2=709262&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java Thu Oct 30 15:06:10 2008
@@ -145,9 +145,6 @@
     public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) {
         if (rec instanceof FormulaRecord) {
             FormulaRecord formulaRec = (FormulaRecord)rec;
-            if (formulaRec.isSharedFormula()) {
-                sfh.convertSharedFormulaRecord(formulaRec);
-            }
             // read optional cached text value
             StringRecord cachedText;
             Class nextClass = rs.peekNextClass();



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