You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2015/07/19 21:00:38 UTC

svn commit: r1691843 [4/30] - in /poi/branches/common_sl: ./ .settings/ legal/ osgi/ osgi/src/ src/examples/src/org/apache/poi/hpsf/examples/ src/examples/src/org/apache/poi/hssf/usermodel/examples/ src/examples/src/org/apache/poi/ss/examples/ src/exam...

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java Sun Jul 19 19:00:32 2015
@@ -17,144 +17,39 @@
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.record.cf.CellRangeUtil;
 import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.ss.util.CellRangeAddressList;
-import org.apache.poi.util.LittleEndianOutput;
 
 /**
- * Conditional Formatting Header record CFHEADER (0x01B0)
- * 
- * @author Dmitriy Kumshayev
+ * Conditional Formatting Header record CFHEADER (0x01B0).
+ * Used to describe a {@link CFRuleRecord}.
+ * @see CFHeader12Record
  */
-public final class CFHeaderRecord extends StandardRecord {
-	public static final short sid = 0x01B0;
+public final class CFHeaderRecord extends CFHeaderBase {
+    public static final short sid = 0x01B0;
 
-	private int field_1_numcf;
-	private int field_2_need_recalculation;
-	private CellRangeAddress field_3_enclosing_cell_range;
-	private CellRangeAddressList field_4_cell_ranges;
-
-	/** Creates new CFHeaderRecord */
-	public CFHeaderRecord()
-	{
-		field_4_cell_ranges = new CellRangeAddressList();
-	}
-	public CFHeaderRecord(CellRangeAddress[] regions, int nRules) {
-		CellRangeAddress[] unmergedRanges = regions;
-		CellRangeAddress[] mergeCellRanges = CellRangeUtil.mergeCellRanges(unmergedRanges);
-		setCellRanges(mergeCellRanges);
-		field_1_numcf = nRules;
-	}
-
-	public CFHeaderRecord(RecordInputStream in)
-	{
-		field_1_numcf = in.readShort();
-		field_2_need_recalculation = in.readShort();
-		field_3_enclosing_cell_range = new CellRangeAddress(in);
-		field_4_cell_ranges = new CellRangeAddressList(in);
-	}
-	
-	public int getNumberOfConditionalFormats()
-	{
-		return field_1_numcf;
-	}
-	public void setNumberOfConditionalFormats(int n)
-	{
-		field_1_numcf=n;
-	}
-	
-	public boolean getNeedRecalculation()
-	{
-		return field_2_need_recalculation==1?true:false;
-	}
-
-	public void setNeedRecalculation(boolean b)
-	{
-		field_2_need_recalculation=b?1:0;
-	}
-	
-	public CellRangeAddress getEnclosingCellRange()
-	{
-		return field_3_enclosing_cell_range;
-	}
-
-	public void setEnclosingCellRange(CellRangeAddress cr)
-	{
-		field_3_enclosing_cell_range = cr;
-	}
-
-	/**
-	 * Set cell ranges list to a single cell range and 
-	 * modify the enclosing cell range accordingly.
-	 * @param cellRanges - list of CellRange objects
-	 */
-	public void setCellRanges(CellRangeAddress[] cellRanges)
-	{
-		if(cellRanges == null)
-		{
-			throw new IllegalArgumentException("cellRanges must not be null");
-		}
-		CellRangeAddressList cral = new CellRangeAddressList();
-		CellRangeAddress enclosingRange = null;
-		for (int i = 0; i < cellRanges.length; i++)
-		{
-			CellRangeAddress cr = cellRanges[i];
-			enclosingRange = CellRangeUtil.createEnclosingCellRange(cr, enclosingRange);
-			cral.addCellRangeAddress(cr);
-		}
-		field_3_enclosing_cell_range = enclosingRange;
-		field_4_cell_ranges = cral;
-	}
-	
-	public CellRangeAddress[] getCellRanges() {
-		return field_4_cell_ranges.getCellRangeAddresses();
-	}
-
-	public String toString()
-	{
-		StringBuffer buffer = new StringBuffer();
-
-		buffer.append("[CFHEADER]\n");
-		buffer.append("	.id		= ").append(Integer.toHexString(sid)).append("\n");
-		buffer.append("	.numCF			= ").append(getNumberOfConditionalFormats()).append("\n");
-		buffer.append("	.needRecalc	   = ").append(getNeedRecalculation()).append("\n");
-		buffer.append("	.enclosingCellRange= ").append(getEnclosingCellRange()).append("\n");
-		buffer.append("	.cfranges=[");
-		for( int i=0; i<field_4_cell_ranges.countRanges(); i++)
-		{
-			buffer.append(i==0?"":",").append(field_4_cell_ranges.getCellRangeAddress(i).toString());
-		}
-		buffer.append("]\n");
-		buffer.append("[/CFHEADER]\n");
-		return buffer.toString();
-	}
-
-	protected int getDataSize() {
-		return 4 // 2 short fields
-			+ CellRangeAddress.ENCODED_SIZE
-			+ field_4_cell_ranges.getSize();
-	}
-	
-	public void serialize(LittleEndianOutput out) {
-
-		out.writeShort(field_1_numcf);
-		out.writeShort(field_2_need_recalculation);
-		field_3_enclosing_cell_range.serialize(out);
-		field_4_cell_ranges.serialize(out);
-	}
-
-	public short getSid() {
-		return sid;
-	}
-
-	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;
-		result.field_4_cell_ranges = field_4_cell_ranges.copy();
-		return result;
-	}
+    /** Creates new CFHeaderRecord */
+    public CFHeaderRecord() {
+        createEmpty();
+    }
+    public CFHeaderRecord(CellRangeAddress[] regions, int nRules) {
+        super(regions, nRules);
+    }
+
+    public CFHeaderRecord(RecordInputStream in) {
+        read(in);
+    }
+
+    protected String getRecordName() {
+        return "CFHEADER";
+    }
+
+    public short getSid() {
+        return sid;
+    }
+
+    public Object clone() {
+        CFHeaderRecord result = new CFHeaderRecord();
+        super.copyTo(result);
+        return result;
+    }
 }

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFRuleRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFRuleRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFRuleRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/CFRuleRecord.java Sun Jul 19 19:00:32 2015
@@ -19,512 +19,129 @@ package org.apache.poi.hssf.record;
 
 import java.util.Arrays;
 
-import org.apache.poi.hssf.model.HSSFFormulaParser;
-import org.apache.poi.hssf.record.cf.BorderFormatting;
-import org.apache.poi.hssf.record.cf.FontFormatting;
-import org.apache.poi.hssf.record.cf.PatternFormatting;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.ss.formula.Formula;
-import org.apache.poi.ss.formula.FormulaType;
 import org.apache.poi.ss.formula.ptg.Ptg;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.LittleEndianOutput;
 
 /**
- * Conditional Formatting Rule Record (0x01B1).<br/>
- *
- * @author Dmitriy Kumshayev
+ * Conditional Formatting Rule Record (0x01B1). 
+ * 
+ * <p>This is for the older-style Excel conditional formattings,
+ *  new-style (Excel 2007+) also make use of {@link CFRule12Record}
+ *  and {@link CFExRuleRecord} for their rules.
  */
-public final class CFRuleRecord extends StandardRecord {
+public final class CFRuleRecord extends CFRuleBase {
+    public static final short sid = 0x01B1;
 
-	public static final short sid = 0x01B1;
+    /** Creates new CFRuleRecord */
+    private CFRuleRecord(byte conditionType, byte comparisonOperation) {
+        super(conditionType, comparisonOperation);
+        setDefaults();
+    }
 
-	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 int   field_5_options;
-
-	private static final BitField modificationBits = bf(0x003FFFFF); // 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 BorderFormatting _borderFormatting;
-
-	private PatternFormatting _patternFormatting;
-
-	private Formula field_17_formula1;
-	private Formula field_18_formula2;
-
-	/** Creates new CFRuleRecord */
-	private CFRuleRecord(byte conditionType, byte comparisonOperation)
-	{
-		field_1_condition_type=conditionType;
-		field_2_comparison_operator=comparisonOperation;
-
-		// 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;
-		_borderFormatting=null;
-		_patternFormatting=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_17_formula1 = Formula.create(formula1);
-		field_18_formula2 = Formula.create(formula2);
-	}
-
-	/**
-	 * Creates a new comparison operation rule
-	 */
+    private CFRuleRecord(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) {
+        super(conditionType, comparisonOperation, formula1, formula2);
+        setDefaults();
+    }
+    private void setDefaults() {
+        // Set modification flags to 1: by default options are not modified
+        formatting_options = modificationBits.setValue(formatting_options, -1);
+        // Set formatting block flags to 0 (no formatting blocks)
+        formatting_options = fmtBlockBits.setValue(formatting_options, 0);
+        formatting_options = undocumented.clear(formatting_options);
+
+        formatting_not_used = (short)0x8002; // Excel seems to write this value, but it doesn't seem to care what it reads
+        _fontFormatting = null;
+        _borderFormatting = null;
+        _patternFormatting = null;
+    }
+
+    /**
+     * Creates a new comparison operation rule
+     */
     public static CFRuleRecord create(HSSFSheet sheet, String formulaText) {
         Ptg[] formula1 = parseFormula(formulaText, sheet);
         return new CFRuleRecord(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON,
                 formula1, null);
     }
-	/**
-	 * Creates a new comparison operation rule
-	 */
-	public static CFRuleRecord create(HSSFSheet sheet, byte comparisonOperation,
-			String formulaText1, String formulaText2) {
-		Ptg[] formula1 = parseFormula(formulaText1, sheet);
-		Ptg[] formula2 = parseFormula(formulaText2, sheet);
-		return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2);
-	}
-
-	public CFRuleRecord(RecordInputStream in) {
-		field_1_condition_type = in.readByte();
-		field_2_comparison_operator = in.readByte();
-		int field_3_formula1_len = in.readUShort();
-		int field_4_formula2_len = in.readUShort();
-		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);
-		}
-
-		// "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()
-	{
-		return field_1_condition_type;
-	}
-
-	public boolean containsFontFormattingBlock()
-	{
-		return getOptionFlag(font);
-	}
-	public void setFontFormatting(FontFormatting fontFormatting)
-	{
-		_fontFormatting = fontFormatting;
-		setOptionFlag(fontFormatting != null, font);
-	}
-	public FontFormatting getFontFormatting()
-	{
-		if( containsFontFormattingBlock())
-		{
-			return _fontFormatting;
-		}
-		return null;
-	}
-
-	public boolean containsAlignFormattingBlock()
-	{
-		return getOptionFlag(align);
-	}
-	public void setAlignFormattingUnchanged()
-	{
-		setOptionFlag(false,align);
-	}
-
-	public boolean containsBorderFormattingBlock()
-	{
-		return getOptionFlag(bord);
-	}
-	public void setBorderFormatting(BorderFormatting borderFormatting)
-	{
-		_borderFormatting = borderFormatting;
-		setOptionFlag(borderFormatting != null, bord);
-	}
-	public BorderFormatting getBorderFormatting()
-	{
-		if( containsBorderFormattingBlock())
-		{
-			return _borderFormatting;
-		}
-		return null;
-	}
-
-	public boolean containsPatternFormattingBlock()
-	{
-		return getOptionFlag(patt);
-	}
-	public void setPatternFormatting(PatternFormatting patternFormatting)
-	{
-		_patternFormatting = patternFormatting;
-		setOptionFlag(patternFormatting!=null, patt);
-	}
-	public PatternFormatting getPatternFormatting()
-	{
-		if( containsPatternFormattingBlock())
-		{
-			return _patternFormatting;
-		}
-		return null;
-	}
-
-	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;
-	}
-
-
-	/**
-	 * get the option flags
-	 *
-	 * @return bit mask
-	 */
-	public int getOptions()
-	{
-		return field_5_options;
-	}
-
-	private boolean isModified(BitField field)
-	{
-		return !field.isSet(field_5_options);
-	}
-
-	private void setModified(boolean modified, BitField field)
-	{
-		field_5_options = field.setBoolean(field_5_options, !modified);
-	}
-
-	public boolean isLeftBorderModified()
-	{
-		return isModified(bordLeft);
-	}
-
-	public void setLeftBorderModified(boolean modified)
-	{
-		setModified(modified,bordLeft);
-	}
-
-	public boolean isRightBorderModified()
-	{
-		return isModified(bordRight);
-	}
-
-	public void setRightBorderModified(boolean modified)
-	{
-		setModified(modified,bordRight);
-	}
-
-	public boolean isTopBorderModified()
-	{
-		return isModified(bordTop);
-	}
-
-	public void setTopBorderModified(boolean modified)
-	{
-		setModified(modified,bordTop);
-	}
-
-	public boolean isBottomBorderModified()
-	{
-		return isModified(bordBot);
-	}
-
-	public void setBottomBorderModified(boolean modified)
-	{
-		setModified(modified,bordBot);
-	}
-
-	public boolean isTopLeftBottomRightBorderModified()
-	{
-		return isModified(bordTlBr);
-	}
-
-	public void setTopLeftBottomRightBorderModified(boolean modified)
-	{
-		setModified(modified,bordTlBr);
-	}
-
-	public boolean isBottomLeftTopRightBorderModified()
-	{
-		return isModified(bordBlTr);
-	}
-
-	public void setBottomLeftTopRightBorderModified(boolean modified)
-	{
-		setModified(modified,bordBlTr);
-	}
-
-	public boolean isPatternStyleModified()
-	{
-		return isModified(pattStyle);
-	}
-
-	public void setPatternStyleModified(boolean modified)
-	{
-		setModified(modified,pattStyle);
-	}
-
-	public boolean isPatternColorModified()
-	{
-		return isModified(pattCol);
-	}
-
-	public void setPatternColorModified(boolean modified)
-	{
-		setModified(modified,pattCol);
-	}
-
-	public boolean isPatternBackgroundColorModified()
-	{
-		return isModified(pattBgCol);
-	}
-
-	public void setPatternBackgroundColorModified(boolean modified)
-	{
-		setModified(modified,pattBgCol);
-	}
-
-	private boolean getOptionFlag(BitField field)
-	{
-		return field.isSet(field_5_options);
-	}
-
-	private void setOptionFlag(boolean flag, BitField field)
-	{
-		field_5_options = field.setBoolean(field_5_options, flag);
-	}
-
-	/**
-	 * get the stack of the 1st expression as a list
-	 *
-	 * @return list of tokens (casts stack to a list and returns it!)
-	 * this method can return null is we are unable to create Ptgs from
-	 *	 existing excel file
-	 * callers should check for null!
-	 */
-
-	public Ptg[] getParsedExpression1()
-	{
-		return field_17_formula1.getTokens();
-	}
-	public void setParsedExpression1(Ptg[] ptgs) {
-		field_17_formula1 = Formula.create(ptgs);
-	}
-
-	/**
-	 * get the stack of the 2nd expression as a list
-	 *
-	 * @return array of {@link Ptg}s, possibly <code>null</code>
-	 */
-	public Ptg[] getParsedExpression2() {
-		return Formula.getTokens(field_18_formula2);
-	}
-	public void setParsedExpression2(Ptg[] ptgs) {
-		field_18_formula2 = Formula.create(ptgs);
-	}
-
-	public short getSid()
-	{
-		return sid;
-	}
-
-	/**
-	 * @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(Formula formula) {
-		return formula.getEncodedTokenSize();
-	}
-
-	/**
-	 * 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 out the stream to write to
-	 */
-	public void serialize(LittleEndianOutput out) {
-
-		int formula1Len=getFormulaSize(field_17_formula1);
-		int formula2Len=getFormulaSize(field_18_formula2);
-
-		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();
-			out.write(fontFormattingRawRecord);
-		}
-
-		if (containsBorderFormattingBlock()) {
-			_borderFormatting.serialize(out);
-		}
-
-		if (containsPatternFormattingBlock()) {
-			_patternFormatting.serialize(out);
-		}
-
-		field_17_formula1.serializeTokens(out);
-		field_18_formula2.serializeTokens(out);
-	}
-
-	protected int getDataSize() {
-		int i = 12 +
-					(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+
-					(containsBorderFormattingBlock()?8:0)+
-					(containsPatternFormattingBlock()?4:0)+
-					getFormulaSize(field_17_formula1)+
-					getFormulaSize(field_18_formula2);
-        return i
-					;
-	}
-
-
-	public String toString() {
-		StringBuffer buffer = new StringBuffer();
-		buffer.append("[CFRULE]\n");
-        buffer.append("    .condition_type   =").append(field_1_condition_type).append("\n");
-		buffer.append("    OPTION FLAGS=0x").append(Integer.toHexString(getOptions())).append("\n");
-		if (containsFontFormattingBlock()) {
-			buffer.append(_fontFormatting.toString()).append("\n");
-		}
-		if (containsBorderFormattingBlock()) {
-			buffer.append(_borderFormatting.toString()).append("\n");
-		}
-		if (containsPatternFormattingBlock()) {
-			buffer.append(_patternFormatting.toString()).append("\n");
-		}
-		buffer.append("    Formula 1 =").append(Arrays.toString(field_17_formula1.getTokens())).append("\n");
-		buffer.append("    Formula 2 =").append(Arrays.toString(field_18_formula2.getTokens())).append("\n");
-		buffer.append("[/CFRULE]\n");
-		return buffer.toString();
-	}
-
-	public Object clone() {
-		CFRuleRecord rec = new CFRuleRecord(field_1_condition_type, field_2_comparison_operator);
-		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();
-		}
-		rec.field_17_formula1 = field_17_formula1.copy();
-		rec.field_18_formula2 = field_18_formula2.copy();
-
-		return rec;
-	}
-
-	/**
-	 * TODO - parse conditional format formulas properly i.e. produce tRefN and tAreaN instead of tRef and tArea
-	 * this call will produce the wrong results if the formula contains any cell references
-	 * One approach might be to apply the inverse of SharedFormulaRecord.convertSharedFormulas(Stack, int, int)
-	 * Note - two extra parameters (rowIx & colIx) will be required. They probably come from one of the Region objects.
-	 *
-	 * @return <code>null</code> if <tt>formula</tt> was null.
-	 */
-    private static Ptg[] parseFormula(String formula, HSSFSheet sheet) {
-        if(formula == null) {
-            return null;
+    /**
+     * Creates a new comparison operation rule
+     */
+    public static CFRuleRecord create(HSSFSheet sheet, byte comparisonOperation,
+            String formulaText1, String formulaText2) {
+        Ptg[] formula1 = parseFormula(formulaText1, sheet);
+        Ptg[] formula2 = parseFormula(formulaText2, sheet);
+        return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2);
+    }
+
+    public CFRuleRecord(RecordInputStream in) {
+        setConditionType(in.readByte());
+        setComparisonOperation(in.readByte());
+        int field_3_formula1_len = in.readUShort();
+        int field_4_formula2_len = in.readUShort();
+        readFormatOptions(in);
+
+        // "You may not use unions, intersections or array constants in Conditional Formatting criteria"
+        setFormula1(Formula.read(field_3_formula1_len, in));
+        setFormula2(Formula.read(field_4_formula2_len, in));
+    }
+
+    public short getSid() {
+        return sid;
+    }
+
+    /**
+     * called by the class that is responsible for writing this sucker.
+     * Subclasses should implement this so that their data is passed back in a
+     * byte array.
+     *
+     * @param out the stream to write to
+     */
+    public void serialize(LittleEndianOutput out) {
+        int formula1Len=getFormulaSize(getFormula1());
+        int formula2Len=getFormulaSize(getFormula2());
+
+        out.writeByte(getConditionType());
+        out.writeByte(getComparisonOperation());
+        out.writeShort(formula1Len);
+        out.writeShort(formula2Len);
+        
+        serializeFormattingBlock(out);
+
+        getFormula1().serializeTokens(out);
+        getFormula2().serializeTokens(out);
+    }
+
+    protected int getDataSize() {
+        return 6 + getFormattingBlockSize() +
+               getFormulaSize(getFormula1())+
+               getFormulaSize(getFormula2());
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("[CFRULE]\n");
+        buffer.append("    .condition_type   =").append(getConditionType()).append("\n");
+        buffer.append("    OPTION FLAGS=0x").append(Integer.toHexString(getOptions())).append("\n");
+        if (containsFontFormattingBlock()) {
+            buffer.append(_fontFormatting.toString()).append("\n");
         }
-        int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
-        return HSSFFormulaParser.parse(formula, sheet.getWorkbook(), FormulaType.CELL, sheetIndex);
+        if (containsBorderFormattingBlock()) {
+            buffer.append(_borderFormatting.toString()).append("\n");
+        }
+        if (containsPatternFormattingBlock()) {
+            buffer.append(_patternFormatting.toString()).append("\n");
+        }
+        buffer.append("    Formula 1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n");
+        buffer.append("    Formula 2 =").append(Arrays.toString(getFormula2().getTokens())).append("\n");
+        buffer.append("[/CFRULE]\n");
+        return buffer.toString();
+    }
+
+    public Object clone() {
+        CFRuleRecord rec = new CFRuleRecord(getConditionType(), getComparisonOperation());
+        super.copyTo(rec);
+        return rec;
     }
 }

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/FeatRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/FeatRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/FeatRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/FeatRecord.java Sun Jul 19 19:00:32 2015
@@ -34,8 +34,11 @@ import org.apache.poi.util.POILogger;
  *  up with a {@link FeatHdrRecord}.
  */
 public final class FeatRecord extends StandardRecord  {
-	private static POILogger logger = POILogFactory.getLogger(FeatRecord.class);
-	public final static short sid = 0x0868;
+    private static POILogger logger = POILogFactory.getLogger(FeatRecord.class);
+    public final static short sid = 0x0868;
+    // SIDs from newer versions
+    public final static short v11_sid = 0x0872;
+    public final static short v12_sid = 0x0878;
 	
 	private FtrHeader futureHeader;
 	

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameCommentRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameCommentRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameCommentRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameCommentRecord.java Sun Jul 19 19:00:32 2015
@@ -62,17 +62,27 @@ public final class NameCommentRecord ext
     out.writeShort(field_4_name_length);
     out.writeShort(field_5_comment_length);
 
-    out.writeByte(0);
-    StringUtil.putCompressedUnicode(field_6_name_text, out);
-    out.writeByte(0);
-    StringUtil.putCompressedUnicode(field_7_comment_text, out);
+    boolean isNameMultiByte = StringUtil.hasMultibyte(field_6_name_text);
+    out.writeByte(isNameMultiByte ? 1 : 0);
+    if (isNameMultiByte) {
+        StringUtil.putUnicodeLE(field_6_name_text, out);
+    } else {
+        StringUtil.putCompressedUnicode(field_6_name_text, out);
+    }
+    boolean isCommentMultiByte = StringUtil.hasMultibyte(field_7_comment_text);
+    out.writeByte(isCommentMultiByte ? 1 : 0);
+    if (isCommentMultiByte) {
+        StringUtil.putUnicodeLE(field_7_comment_text, out);
+    } else {
+        StringUtil.putCompressedUnicode(field_7_comment_text, out);
+    }
   }
 
   @Override
   protected int getDataSize() {
     return 18 // 4 shorts + 1 long + 2 spurious 'nul's
-         + field_6_name_text.length()
-         + field_7_comment_text.length();
+         + (StringUtil.hasMultibyte(field_6_name_text) ? field_6_name_text.length()*2 : field_6_name_text.length())
+         + (StringUtil.hasMultibyte(field_7_comment_text) ? field_7_comment_text.length()*2 : field_7_comment_text.length());
   }
 
   /**
@@ -86,10 +96,16 @@ public final class NameCommentRecord ext
     final int field_4_name_length = in.readShort();
     final int field_5_comment_length = in.readShort();
 
-    in.readByte(); //spurious NUL
-    field_6_name_text = StringUtil.readCompressedUnicode(in, field_4_name_length);
-    in.readByte(); //spurious NUL
-    field_7_comment_text = StringUtil.readCompressedUnicode(in, field_5_comment_length);
+    if (in.readByte() == 0) {
+        field_6_name_text = StringUtil.readCompressedUnicode(in, field_4_name_length);
+    } else {
+        field_6_name_text = StringUtil.readUnicodeLE(in, field_4_name_length);
+    }
+    if (in.readByte() == 0) {
+        field_7_comment_text = StringUtil.readCompressedUnicode(in, field_5_comment_length);
+    } else {
+        field_7_comment_text = StringUtil.readUnicodeLE(in, field_5_comment_length);
+    }    
   }
 
   /**

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/NameRecord.java Sun Jul 19 19:00:32 2015
@@ -411,11 +411,12 @@ public final class NameRecord extends Co
 	 * @return extern sheet index
 	 */
 	public int getExternSheetNumber(){
-		if (field_13_name_definition.getEncodedSize() < 1) {
+	    Ptg[] tokens = field_13_name_definition.getTokens();
+		if (tokens.length == 0) {
 			return 0;
 		}
-		Ptg ptg = field_13_name_definition.getTokens()[0];
 
+		Ptg ptg = tokens[0];
 		if (ptg.getClass() == Area3DPtg.class){
 			return ((Area3DPtg) ptg).getExternSheetIndex();
 

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordFactory.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordFactory.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordFactory.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordFactory.java Sun Jul 19 19:00:32 2015
@@ -62,425 +62,420 @@ import org.apache.poi.hssf.record.pivott
  * Description:  Takes a stream and outputs an array of Record objects.<P>
  *
  * @see org.apache.poi.hssf.eventmodel.EventRecordFactory
- * @author Andrew C. Oliver (acoliver at apache dot org)
- * @author Marc Johnson (mjohnson at apache dot org)
- * @author Glen Stampoultzis (glens at apache.org)
- * @author Csaba Nagy (ncsaba at yahoo dot com)
  */
 public final class RecordFactory {
-	private static final int NUM_RECORDS = 512;
+    private static final int NUM_RECORDS = 512;
 
-	private interface I_RecordCreator {
-		Record create(RecordInputStream in);
+    private interface I_RecordCreator {
+        Record create(RecordInputStream in);
 
-		Class<? extends Record> getRecordClass();
-	}
-	private static final class ReflectionConstructorRecordCreator implements I_RecordCreator {
-
-		private final Constructor<? extends Record> _c;
-		public ReflectionConstructorRecordCreator(Constructor<? extends Record> c) {
-			_c = c;
-		}
-		public Record create(RecordInputStream in) {
-			Object[] args = { in, };
-			try {
-				return _c.newInstance(args);
-			} catch (IllegalArgumentException e) {
-				throw new RuntimeException(e);
-			} catch (InstantiationException e) {
-				throw new RuntimeException(e);
-			} catch (IllegalAccessException e) {
-				throw new RuntimeException(e);
-			} catch (InvocationTargetException e) {
-				Throwable t = e.getTargetException();
-				if (t instanceof RecordFormatException) {
-					throw (RecordFormatException)t;
-				} else if (t instanceof EncryptedDocumentException) {
-					throw (EncryptedDocumentException)t;
-				} else {
-				    throw new RecordFormatException("Unable to construct record instance" , t);
-				}
-			}
-		}
-		public Class<? extends Record> getRecordClass() {
-			return _c.getDeclaringClass();
-		}
-	}
-	/**
-	 * A "create" method is used instead of the usual constructor if the created record might
-	 * be of a different class to the declaring class.
-	 */
-	private static final class ReflectionMethodRecordCreator implements I_RecordCreator {
-
-		private final Method _m;
-		public ReflectionMethodRecordCreator(Method m) {
-			_m = m;
-		}
-		public Record create(RecordInputStream in) {
-			Object[] args = { in, };
-			try {
-				return (Record) _m.invoke(null, args);
-			} catch (IllegalArgumentException e) {
-				throw new RuntimeException(e);
-			} catch (IllegalAccessException e) {
-				throw new RuntimeException(e);
-			} catch (InvocationTargetException e) {
-				throw new RecordFormatException("Unable to construct record instance" , e.getTargetException());
-			}
-		}
-		@SuppressWarnings("unchecked")
-		public Class<? extends Record> getRecordClass() {
-			return (Class<? extends Record>) _m.getDeclaringClass();
-		}
-	}
-
-
-	private static final Class<?>[] CONSTRUCTOR_ARGS = { RecordInputStream.class, };
-
-	/**
-	 * contains the classes for all the records we want to parse.<br/>
-	 * Note - this most but not *every* subclass of Record.
-	 */
-	@SuppressWarnings("unchecked")
-	private static final Class<? extends Record>[] recordClasses = new Class[] {
-		ArrayRecord.class,
+        Class<? extends Record> getRecordClass();
+    }
+    private static final class ReflectionConstructorRecordCreator implements I_RecordCreator {
+
+        private final Constructor<? extends Record> _c;
+        public ReflectionConstructorRecordCreator(Constructor<? extends Record> c) {
+            _c = c;
+        }
+        public Record create(RecordInputStream in) {
+            Object[] args = { in, };
+            try {
+                return _c.newInstance(args);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(e);
+            } catch (InstantiationException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            } catch (InvocationTargetException e) {
+                Throwable t = e.getTargetException();
+                if (t instanceof RecordFormatException) {
+                    throw (RecordFormatException)t;
+                } else if (t instanceof EncryptedDocumentException) {
+                    throw (EncryptedDocumentException)t;
+                } else {
+                    throw new RecordFormatException("Unable to construct record instance" , t);
+                }
+            }
+        }
+        public Class<? extends Record> getRecordClass() {
+            return _c.getDeclaringClass();
+        }
+    }
+    /**
+     * A "create" method is used instead of the usual constructor if the created record might
+     * be of a different class to the declaring class.
+     */
+    private static final class ReflectionMethodRecordCreator implements I_RecordCreator {
+        private final Method _m;
+        public ReflectionMethodRecordCreator(Method m) {
+            _m = m;
+        }
+        public Record create(RecordInputStream in) {
+            Object[] args = { in, };
+            try {
+                return (Record) _m.invoke(null, args);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            } catch (InvocationTargetException e) {
+                throw new RecordFormatException("Unable to construct record instance" , e.getTargetException());
+            }
+        }
+        @SuppressWarnings("unchecked")
+        public Class<? extends Record> getRecordClass() {
+            return (Class<? extends Record>) _m.getDeclaringClass();
+        }
+    }
+
+    private static final Class<?>[] CONSTRUCTOR_ARGS = { RecordInputStream.class, };
+
+    /**
+     * contains the classes for all the records we want to parse.<br/>
+     * Note - this most but not *every* subclass of Record.
+     */
+    @SuppressWarnings("unchecked")
+    private static final Class<? extends Record>[] recordClasses = new Class[] {
+        ArrayRecord.class,
         AutoFilterInfoRecord.class,
         BackupRecord.class,
-		BlankRecord.class,
-		BOFRecord.class,
-		BookBoolRecord.class,
-		BoolErrRecord.class,
-		BottomMarginRecord.class,
-		BoundSheetRecord.class,
-		CalcCountRecord.class,
-		CalcModeRecord.class,
-		CFHeaderRecord.class,
-		CFRuleRecord.class,
-		ChartRecord.class,
-		ChartTitleFormatRecord.class,
-		CodepageRecord.class,
-		ColumnInfoRecord.class,
-		ContinueRecord.class,
-		CountryRecord.class,
-		CRNCountRecord.class,
-		CRNRecord.class,
-		DateWindow1904Record.class,
-		DBCellRecord.class,
-                DConRefRecord.class,
-		DefaultColWidthRecord.class,
-		DefaultRowHeightRecord.class,
-		DeltaRecord.class,
-		DimensionsRecord.class,
-		DrawingGroupRecord.class,
-		DrawingRecord.class,
-		DrawingSelectionRecord.class,
-		DSFRecord.class,
-		DVALRecord.class,
-		DVRecord.class,
-		EOFRecord.class,
-		ExtendedFormatRecord.class,
-		ExternalNameRecord.class,
-		ExternSheetRecord.class,
-		ExtSSTRecord.class,
-		FeatRecord.class,
-		FeatHdrRecord.class,
-		FilePassRecord.class,
-		FileSharingRecord.class,
-		FnGroupCountRecord.class,
-		FontRecord.class,
-		FooterRecord.class,
-		FormatRecord.class,
-		FormulaRecord.class,
-		GridsetRecord.class,
-		GutsRecord.class,
-		HCenterRecord.class,
-		HeaderRecord.class,
+        BlankRecord.class,
+        BOFRecord.class,
+        BookBoolRecord.class,
+        BoolErrRecord.class,
+        BottomMarginRecord.class,
+        BoundSheetRecord.class,
+        CalcCountRecord.class,
+        CalcModeRecord.class,
+        CFHeaderRecord.class,
+        CFHeader12Record.class,
+        CFRuleRecord.class,
+        CFRule12Record.class,
+        ChartRecord.class,
+        ChartTitleFormatRecord.class,
+        CodepageRecord.class,
+        ColumnInfoRecord.class,
+        ContinueRecord.class,
+        CountryRecord.class,
+        CRNCountRecord.class,
+        CRNRecord.class,
+        DateWindow1904Record.class,
+        DBCellRecord.class,
+        DConRefRecord.class,
+        DefaultColWidthRecord.class,
+        DefaultRowHeightRecord.class,
+        DeltaRecord.class,
+        DimensionsRecord.class,
+        DrawingGroupRecord.class,
+        DrawingRecord.class,
+        DrawingSelectionRecord.class,
+        DSFRecord.class,
+        DVALRecord.class,
+        DVRecord.class,
+        EOFRecord.class,
+        ExtendedFormatRecord.class,
+        ExternalNameRecord.class,
+        ExternSheetRecord.class,
+        ExtSSTRecord.class,
+        FeatRecord.class,
+        FeatHdrRecord.class,
+        FilePassRecord.class,
+        FileSharingRecord.class,
+        FnGroupCountRecord.class,
+        FontRecord.class,
+        FooterRecord.class,
+        FormatRecord.class,
+        FormulaRecord.class,
+        GridsetRecord.class,
+        GutsRecord.class,
+        HCenterRecord.class,
+        HeaderRecord.class,
         HeaderFooterRecord.class,
-		HideObjRecord.class,
-		HorizontalPageBreakRecord.class,
-		HyperlinkRecord.class,
-		IndexRecord.class,
-		InterfaceEndRecord.class,
-		InterfaceHdrRecord.class,
-		IterationRecord.class,
-		LabelRecord.class,
-		LabelSSTRecord.class,
-		LeftMarginRecord.class,
-		LegendRecord.class,
-		MergeCellsRecord.class,
-		MMSRecord.class,
-		MulBlankRecord.class,
-		MulRKRecord.class,
-		NameRecord.class,
-		NameCommentRecord.class,
-		NoteRecord.class,
-		NumberRecord.class,
-		ObjectProtectRecord.class,
-		ObjRecord.class,
-		PaletteRecord.class,
-		PaneRecord.class,
-		PasswordRecord.class,
-		PasswordRev4Record.class,
-		PrecisionRecord.class,
-		PrintGridlinesRecord.class,
-		PrintHeadersRecord.class,
-		PrintSetupRecord.class,
-		ProtectionRev4Record.class,
-		ProtectRecord.class,
-		RecalcIdRecord.class,
-		RefModeRecord.class,
-		RefreshAllRecord.class,
-		RightMarginRecord.class,
-		RKRecord.class,
-		RowRecord.class,
-		SaveRecalcRecord.class,
-		ScenarioProtectRecord.class,
-		SelectionRecord.class,
-		SeriesRecord.class,
-		SeriesTextRecord.class,
-		SharedFormulaRecord.class,
-		SSTRecord.class,
-		StringRecord.class,
-		StyleRecord.class,
-		SupBookRecord.class,
-		TabIdRecord.class,
-		TableRecord.class,
-		TableStylesRecord.class,
-		TextObjectRecord.class,
-		TopMarginRecord.class,
-		UncalcedRecord.class,
-		UseSelFSRecord.class,
-		UserSViewBegin.class,
-		UserSViewEnd.class,
-		ValueRangeRecord.class,
-		VCenterRecord.class,
-		VerticalPageBreakRecord.class,
-		WindowOneRecord.class,
-		WindowProtectRecord.class,
-		WindowTwoRecord.class,
-		WriteAccessRecord.class,
-		WriteProtectRecord.class,
-		WSBoolRecord.class,
-
-		// chart records
-		BeginRecord.class,
-		ChartFRTInfoRecord.class,
-		ChartStartBlockRecord.class,
-		ChartEndBlockRecord.class,
-// TODO ChartFormatRecord.class,
-		ChartStartObjectRecord.class,
-		ChartEndObjectRecord.class,
-		CatLabRecord.class,
-      DataFormatRecord.class,
-		EndRecord.class,
-		LinkedDataRecord.class,
-		SeriesToChartGroupRecord.class,
-
-		// pivot table records
-		DataItemRecord.class,
-		ExtendedPivotTableViewFieldsRecord.class,
-		PageItemRecord.class,
-		StreamIDRecord.class,
-		ViewDefinitionRecord.class,
-		ViewFieldsRecord.class,
-		ViewSourceRecord.class,
-	};
-
-	/**
-	 * cache of the recordsToMap();
-	 */
-	private static final Map<Integer, I_RecordCreator> _recordCreatorsById  = recordsToMap(recordClasses);
-
-	private static short[] _allKnownRecordSIDs;
-
-	/**
-	 * Debug / diagnosis method<br/>
-	 * Gets the POI implementation class for a given <tt>sid</tt>.  Only a subset of the any BIFF
-	 * records are actually interpreted by POI.  A few others are known but not interpreted
-	 * (see {@link UnknownRecord#getBiffName(int)}).
-	 * @return the POI implementation class for the specified record <tt>sid</tt>.
-	 * <code>null</code> if the specified record is not interpreted by POI.
-	 */
-	public static Class<? extends Record> getRecordClass(int sid) {
-		I_RecordCreator rc = _recordCreatorsById.get(Integer.valueOf(sid));
-		if (rc == null) {
-			return null;
-		}
-		return rc.getRecordClass();
-	}
-	/**
-	 * create a record, if there are MUL records than multiple records
-	 * are returned digested into the non-mul form.
-	 */
-	public static Record [] createRecord(RecordInputStream in) {
-
-		Record record = createSingleRecord(in);
-		if (record instanceof DBCellRecord) {
-			// Not needed by POI.  Regenerated from scratch by POI when spreadsheet is written
-			return new Record[] { null, };
-		}
-		if (record instanceof RKRecord) {
-			return new Record[] { convertToNumberRecord((RKRecord) record), };
-		}
-		if (record instanceof MulRKRecord) {
-			return convertRKRecords((MulRKRecord)record);
-		}
-		return new Record[] { record, };
-	}
-
-	public static Record createSingleRecord(RecordInputStream in) {
-		I_RecordCreator constructor = _recordCreatorsById.get(Integer.valueOf(in.getSid()));
-
-		if (constructor == null) {
-			return new UnknownRecord(in);
-		}
-
-		return constructor.create(in);
-	}
-
-	/**
-	 * RK record is a slightly smaller alternative to NumberRecord
-	 * POI likes NumberRecord better
-	 */
-	public static NumberRecord convertToNumberRecord(RKRecord rk) {
-		NumberRecord num = new NumberRecord();
-
-		num.setColumn(rk.getColumn());
-		num.setRow(rk.getRow());
-		num.setXFIndex(rk.getXFIndex());
-		num.setValue(rk.getRKNumber());
-		return num;
-	}
-
-	/**
-	 * Converts a {@link MulRKRecord} into an equivalent array of {@link NumberRecord}s
-	 */
-	public static NumberRecord[] convertRKRecords(MulRKRecord mrk) {
-		NumberRecord[] mulRecs = new NumberRecord[mrk.getNumColumns()];
-		for (int k = 0; k < mrk.getNumColumns(); k++) {
-			NumberRecord nr = new NumberRecord();
-
-			nr.setColumn((short) (k + mrk.getFirstColumn()));
-			nr.setRow(mrk.getRow());
-			nr.setXFIndex(mrk.getXFAt(k));
-			nr.setValue(mrk.getRKNumberAt(k));
-			mulRecs[k] = nr;
-		}
-		return mulRecs;
-	}
-
-	/**
-	 * Converts a {@link MulBlankRecord} into an equivalent array of {@link BlankRecord}s
-	 */
-	public static BlankRecord[] convertBlankRecords(MulBlankRecord mbk) {
-		BlankRecord[] mulRecs = new BlankRecord[mbk.getNumColumns()];
-		for (int k = 0; k < mbk.getNumColumns(); k++) {
-			BlankRecord br = new BlankRecord();
-
-			br.setColumn((short) (k + mbk.getFirstColumn()));
-			br.setRow(mbk.getRow());
-			br.setXFIndex(mbk.getXFAt(k));
-			mulRecs[k] = br;
-		}
-		return mulRecs;
-	}
-
-	/**
-	 * @return an array of all the SIDS for all known records
-	 */
-	public static short[] getAllKnownRecordSIDs() {
-		if (_allKnownRecordSIDs == null) {
-			short[] results = new short[ _recordCreatorsById.size() ];
-			int i = 0;
-
-			for (Iterator<Integer> iterator = _recordCreatorsById.keySet().iterator(); iterator.hasNext(); ) {
-				Integer sid = iterator.next();
-
-				results[i++] = sid.shortValue();
-			}
-			Arrays.sort(results);
- 			_allKnownRecordSIDs = results;
-		}
-
-		return _allKnownRecordSIDs.clone();
-	}
-
-	/**
-	 * gets the record constructors and sticks them in the map by SID
-	 * @return map of SIDs to short,short,byte[] constructors for Record classes
-	 * most of org.apache.poi.hssf.record.*
-	 */
-	private static Map<Integer, I_RecordCreator> recordsToMap(Class<? extends Record> [] records) {
-		Map<Integer, I_RecordCreator> result = new HashMap<Integer, I_RecordCreator>();
-		Set<Class<?>> uniqueRecClasses = new HashSet<Class<?>>(records.length * 3 / 2);
-
-		for (int i = 0; i < records.length; i++) {
-
-			Class<? extends Record> recClass = records[ i ];
-			if(!Record.class.isAssignableFrom(recClass)) {
-				throw new RuntimeException("Invalid record sub-class (" + recClass.getName() + ")");
-			}
-			if(Modifier.isAbstract(recClass.getModifiers())) {
-				throw new RuntimeException("Invalid record class (" + recClass.getName() + ") - must not be abstract");
-			}
-			if(!uniqueRecClasses.add(recClass)) {
-				throw new RuntimeException("duplicate record class (" + recClass.getName() + ")");
-			}
-
-			int sid;
-			try {
-				sid = recClass.getField("sid").getShort(null);
-			} catch (Exception illegalArgumentException) {
-				throw new RecordFormatException(
-					"Unable to determine record types");
-			}
-			Integer key = Integer.valueOf(sid);
-			if (result.containsKey(key)) {
-				Class<?> prevClass = result.get(key).getRecordClass();
-				throw new RuntimeException("duplicate record sid 0x" + Integer.toHexString(sid).toUpperCase()
-						+ " for classes (" + recClass.getName() + ") and (" + prevClass.getName() + ")");
-			}
-			result.put(key, getRecordCreator(recClass));
-		}
-//		result.put(Integer.valueOf(0x0406), result.get(Integer.valueOf(0x06)));
-		return result;
-	}
-
-	private static I_RecordCreator getRecordCreator(Class<? extends Record> recClass) {
-		try {
-			Constructor<? extends Record> constructor;
-			constructor = recClass.getConstructor(CONSTRUCTOR_ARGS);
-			return new ReflectionConstructorRecordCreator(constructor);
-		} catch (NoSuchMethodException e) {
-			// fall through and look for other construction methods
-		}
-		try {
-			Method m = recClass.getDeclaredMethod("create", CONSTRUCTOR_ARGS);
-			return new ReflectionMethodRecordCreator(m);
-		} catch (NoSuchMethodException e) {
-			throw new RuntimeException("Failed to find constructor or create method for (" + recClass.getName() + ").");
-		}
-	}
-	/**
-	 * Create an array of records from an input stream
-	 *
-	 * @param in the InputStream from which the records will be obtained
-	 *
-	 * @return an array of Records created from the InputStream
-	 *
-	 * @exception RecordFormatException on error processing the InputStream
-	 */
-	public static List<Record> createRecords(InputStream in) throws RecordFormatException {
-
-		List<Record> records = new ArrayList<Record>(NUM_RECORDS);
-
-		RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, true);
-
-		Record record;
-		while ((record = recStream.nextRecord())!=null) {
-			records.add(record);
-		}
+        HideObjRecord.class,
+        HorizontalPageBreakRecord.class,
+        HyperlinkRecord.class,
+        IndexRecord.class,
+        InterfaceEndRecord.class,
+        InterfaceHdrRecord.class,
+        IterationRecord.class,
+        LabelRecord.class,
+        LabelSSTRecord.class,
+        LeftMarginRecord.class,
+        LegendRecord.class,
+        MergeCellsRecord.class,
+        MMSRecord.class,
+        MulBlankRecord.class,
+        MulRKRecord.class,
+        NameRecord.class,
+        NameCommentRecord.class,
+        NoteRecord.class,
+        NumberRecord.class,
+        ObjectProtectRecord.class,
+        ObjRecord.class,
+        PaletteRecord.class,
+        PaneRecord.class,
+        PasswordRecord.class,
+        PasswordRev4Record.class,
+        PrecisionRecord.class,
+        PrintGridlinesRecord.class,
+        PrintHeadersRecord.class,
+        PrintSetupRecord.class,
+        ProtectionRev4Record.class,
+        ProtectRecord.class,
+        RecalcIdRecord.class,
+        RefModeRecord.class,
+        RefreshAllRecord.class,
+        RightMarginRecord.class,
+        RKRecord.class,
+        RowRecord.class,
+        SaveRecalcRecord.class,
+        ScenarioProtectRecord.class,
+        SelectionRecord.class,
+        SeriesRecord.class,
+        SeriesTextRecord.class,
+        SharedFormulaRecord.class,
+        SSTRecord.class,
+        StringRecord.class,
+        StyleRecord.class,
+        SupBookRecord.class,
+        TabIdRecord.class,
+        TableRecord.class,
+        TableStylesRecord.class,
+        TextObjectRecord.class,
+        TopMarginRecord.class,
+        UncalcedRecord.class,
+        UseSelFSRecord.class,
+        UserSViewBegin.class,
+        UserSViewEnd.class,
+        ValueRangeRecord.class,
+        VCenterRecord.class,
+        VerticalPageBreakRecord.class,
+        WindowOneRecord.class,
+        WindowProtectRecord.class,
+        WindowTwoRecord.class,
+        WriteAccessRecord.class,
+        WriteProtectRecord.class,
+        WSBoolRecord.class,
+
+        // chart records
+        BeginRecord.class,
+        ChartFRTInfoRecord.class,
+        ChartStartBlockRecord.class,
+        ChartEndBlockRecord.class,
+        // TODO ChartFormatRecord.class,
+        ChartStartObjectRecord.class,
+        ChartEndObjectRecord.class,
+        CatLabRecord.class,
+        DataFormatRecord.class,
+        EndRecord.class,
+        LinkedDataRecord.class,
+        SeriesToChartGroupRecord.class,
+
+        // pivot table records
+        DataItemRecord.class,
+        ExtendedPivotTableViewFieldsRecord.class,
+        PageItemRecord.class,
+        StreamIDRecord.class,
+        ViewDefinitionRecord.class,
+        ViewFieldsRecord.class,
+        ViewSourceRecord.class,
+    };
+
+    /**
+     * cache of the recordsToMap();
+     */
+    private static final Map<Integer, I_RecordCreator> _recordCreatorsById  = recordsToMap(recordClasses);
+
+    private static short[] _allKnownRecordSIDs;
+
+    /**
+     * Debug / diagnosis method<br/>
+     * Gets the POI implementation class for a given <tt>sid</tt>.  Only a subset of the any BIFF
+     * records are actually interpreted by POI.  A few others are known but not interpreted
+     * (see {@link UnknownRecord#getBiffName(int)}).
+     * @return the POI implementation class for the specified record <tt>sid</tt>.
+     * <code>null</code> if the specified record is not interpreted by POI.
+     */
+    public static Class<? extends Record> getRecordClass(int sid) {
+        I_RecordCreator rc = _recordCreatorsById.get(Integer.valueOf(sid));
+        if (rc == null) {
+            return null;
+        }
+        return rc.getRecordClass();
+    }
+    /**
+     * create a record, if there are MUL records than multiple records
+     * are returned digested into the non-mul form.
+     */
+    public static Record [] createRecord(RecordInputStream in) {
+        Record record = createSingleRecord(in);
+        if (record instanceof DBCellRecord) {
+            // Not needed by POI.  Regenerated from scratch by POI when spreadsheet is written
+            return new Record[] { null, };
+        }
+        if (record instanceof RKRecord) {
+            return new Record[] { convertToNumberRecord((RKRecord) record), };
+        }
+        if (record instanceof MulRKRecord) {
+            return convertRKRecords((MulRKRecord)record);
+        }
+        return new Record[] { record, };
+    }
+
+    public static Record createSingleRecord(RecordInputStream in) {
+        I_RecordCreator constructor = _recordCreatorsById.get(Integer.valueOf(in.getSid()));
+
+        if (constructor == null) {
+            return new UnknownRecord(in);
+        }
+
+        return constructor.create(in);
+    }
+
+    /**
+     * RK record is a slightly smaller alternative to NumberRecord
+     * POI likes NumberRecord better
+     */
+    public static NumberRecord convertToNumberRecord(RKRecord rk) {
+        NumberRecord num = new NumberRecord();
+
+        num.setColumn(rk.getColumn());
+        num.setRow(rk.getRow());
+        num.setXFIndex(rk.getXFIndex());
+        num.setValue(rk.getRKNumber());
+        return num;
+    }
+
+    /**
+     * Converts a {@link MulRKRecord} into an equivalent array of {@link NumberRecord}s
+     */
+    public static NumberRecord[] convertRKRecords(MulRKRecord mrk) {
+        NumberRecord[] mulRecs = new NumberRecord[mrk.getNumColumns()];
+        for (int k = 0; k < mrk.getNumColumns(); k++) {
+            NumberRecord nr = new NumberRecord();
+
+            nr.setColumn((short) (k + mrk.getFirstColumn()));
+            nr.setRow(mrk.getRow());
+            nr.setXFIndex(mrk.getXFAt(k));
+            nr.setValue(mrk.getRKNumberAt(k));
+            mulRecs[k] = nr;
+        }
+        return mulRecs;
+    }
+
+    /**
+     * Converts a {@link MulBlankRecord} into an equivalent array of {@link BlankRecord}s
+     */
+    public static BlankRecord[] convertBlankRecords(MulBlankRecord mbk) {
+        BlankRecord[] mulRecs = new BlankRecord[mbk.getNumColumns()];
+        for (int k = 0; k < mbk.getNumColumns(); k++) {
+            BlankRecord br = new BlankRecord();
+
+            br.setColumn((short) (k + mbk.getFirstColumn()));
+            br.setRow(mbk.getRow());
+            br.setXFIndex(mbk.getXFAt(k));
+            mulRecs[k] = br;
+        }
+        return mulRecs;
+    }
+
+    /**
+     * @return an array of all the SIDS for all known records
+     */
+    public static short[] getAllKnownRecordSIDs() {
+        if (_allKnownRecordSIDs == null) {
+            short[] results = new short[ _recordCreatorsById.size() ];
+            int i = 0;
+
+            for (Iterator<Integer> iterator = _recordCreatorsById.keySet().iterator(); iterator.hasNext(); ) {
+                Integer sid = iterator.next();
+
+                results[i++] = sid.shortValue();
+            }
+            Arrays.sort(results);
+            _allKnownRecordSIDs = results;
+        }
+
+        return _allKnownRecordSIDs.clone();
+    }
+
+    /**
+     * gets the record constructors and sticks them in the map by SID
+     * @return map of SIDs to short,short,byte[] constructors for Record classes
+     * most of org.apache.poi.hssf.record.*
+     */
+    private static Map<Integer, I_RecordCreator> recordsToMap(Class<? extends Record> [] records) {
+        Map<Integer, I_RecordCreator> result = new HashMap<Integer, I_RecordCreator>();
+        Set<Class<?>> uniqueRecClasses = new HashSet<Class<?>>(records.length * 3 / 2);
+
+        for (int i = 0; i < records.length; i++) {
+
+            Class<? extends Record> recClass = records[ i ];
+            if(!Record.class.isAssignableFrom(recClass)) {
+                throw new RuntimeException("Invalid record sub-class (" + recClass.getName() + ")");
+            }
+            if(Modifier.isAbstract(recClass.getModifiers())) {
+                throw new RuntimeException("Invalid record class (" + recClass.getName() + ") - must not be abstract");
+            }
+            if(!uniqueRecClasses.add(recClass)) {
+                throw new RuntimeException("duplicate record class (" + recClass.getName() + ")");
+            }
+
+            int sid;
+            try {
+                sid = recClass.getField("sid").getShort(null);
+            } catch (Exception illegalArgumentException) {
+                throw new RecordFormatException(
+                        "Unable to determine record types");
+            }
+            Integer key = Integer.valueOf(sid);
+            if (result.containsKey(key)) {
+                Class<?> prevClass = result.get(key).getRecordClass();
+                throw new RuntimeException("duplicate record sid 0x" + Integer.toHexString(sid).toUpperCase()
+                        + " for classes (" + recClass.getName() + ") and (" + prevClass.getName() + ")");
+            }
+            result.put(key, getRecordCreator(recClass));
+        }
+        // result.put(Integer.valueOf(0x0406), result.get(Integer.valueOf(0x06)));
+        return result;
+    }
+
+    private static I_RecordCreator getRecordCreator(Class<? extends Record> recClass) {
+        try {
+            Constructor<? extends Record> constructor;
+            constructor = recClass.getConstructor(CONSTRUCTOR_ARGS);
+            return new ReflectionConstructorRecordCreator(constructor);
+        } catch (NoSuchMethodException e) {
+            // fall through and look for other construction methods
+        }
+        try {
+            Method m = recClass.getDeclaredMethod("create", CONSTRUCTOR_ARGS);
+            return new ReflectionMethodRecordCreator(m);
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException("Failed to find constructor or create method for (" + recClass.getName() + ").");
+        }
+    }
+    /**
+     * Create an array of records from an input stream
+     *
+     * @param in the InputStream from which the records will be obtained
+     *
+     * @return an array of Records created from the InputStream
+     *
+     * @exception RecordFormatException on error processing the InputStream
+     */
+    public static List<Record> createRecords(InputStream in) throws RecordFormatException {
+
+        List<Record> records = new ArrayList<Record>(NUM_RECORDS);
+
+        RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, true);
+
+        Record record;
+        while ((record = recStream.nextRecord())!=null) {
+            records.add(record);
+        }
 
-		return records;
-	}
+        return records;
+    }
 }

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RecordInputStream.java Sun Jul 19 19:00:32 2015
@@ -52,8 +52,17 @@ public final class RecordInputStream imp
 	public static final class LeftoverDataException extends RuntimeException {
 		public LeftoverDataException(int sid, int remainingByteCount) {
 			super("Initialisation of record 0x" + Integer.toHexString(sid).toUpperCase()
-					+ " left " + remainingByteCount + " bytes remaining still to be read.");
+					+ "(" + getRecordName(sid) + ") left " + remainingByteCount 
+					+ " bytes remaining still to be read.");
 		}
+
+        private static String getRecordName(int sid) {
+            Class<? extends Record> recordClass = RecordFactory.getRecordClass(sid);
+            if(recordClass == null) {
+                return null;
+            }
+            return recordClass.getSimpleName();
+        }
 	}
 
 	/** Header {@link LittleEndianInput} facet of the wrapped {@link InputStream} */

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RowRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RowRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RowRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/RowRecord.java Sun Jul 19 19:00:32 2015
@@ -64,6 +64,9 @@ public final class RowRecord extends Sta
     // bit 15 is unused
 
     public RowRecord(int rowNumber) {
+    	if(rowNumber < 0) {
+    		throw new IllegalArgumentException("Invalid row number (" + rowNumber + ")");
+    	}
         field_1_row_number = rowNumber;
         field_4_height = (short)0xFF;
         field_5_optimize = ( short ) 0;
@@ -76,6 +79,9 @@ public final class RowRecord extends Sta
 
     public RowRecord(RecordInputStream in) {
         field_1_row_number   = in.readUShort();
+    	if(field_1_row_number < 0) {
+    		throw new IllegalArgumentException("Invalid row number " + field_1_row_number + " found in InputStream");
+    	}
         field_2_first_col    = in.readShort();
         field_3_last_col     = in.readShort();
         field_4_height       = in.readShort();

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/UnknownRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/UnknownRecord.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/UnknownRecord.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/UnknownRecord.java Sun Jul 19 19:00:32 2015
@@ -179,8 +179,10 @@ public final class UnknownRecord extends
             case SHEETPROTECTION_0867: return "SHEETPROTECTION";
             case 0x086B: return "DATALABEXTCONTENTS";
             case 0x086C: return "CELLWATCH";
+            case FeatRecord.v11_sid: return "SHARED FEATURE v11";
             case 0x0874: return "DROPDOWNOBJIDS";
             case 0x0876: return "DCONN";
+            case FeatRecord.v12_sid: return "SHARED FEATURE v12";
             case 0x087B: return "CFEX";
             case 0x087C: return "XFCRC";
             case 0x087D: return "XFEXT";
@@ -194,15 +196,21 @@ public final class UnknownRecord extends
             case 0x089A: return "MTRSETTINGS";
             case 0x089B: return "COMPRESSPICTURES";
             case HEADER_FOOTER_089C: return "HEADERFOOTER";
+            case 0x089D: return "CRTLAYOUT12";
+            case 0x089E: return "CRTMLFRT";
+            case 0x089F: return "CRTMLFRTCONTINUE";
             case 0x08A1: return "SHAPEPROPSSTREAM";
             case 0x08A3: return "FORCEFULLCALCULATION";
             case 0x08A4: return "SHAPEPROPSSTREAM";
             case 0x08A5: return "TEXTPROPSSTREAM";
             case 0x08A6: return "RICHTEXTSTREAM";
+            case 0x08A7: return "CRTLAYOUT12A";
 
             case 0x08C8: return "PLV{Mac Excel}";
-
-
+            
+            case 0x1001: return "UNITS";
+            case 0x1006: return "CHARTDATAFORMAT";
+            case 0x1007: return "CHARTLINEFORMAT";
         }
         if (isObservedButUnknown(sid)) {
             return "UNKNOWN-" + Integer.toHexString(sid).toUpperCase();
@@ -215,6 +223,7 @@ public final class UnknownRecord extends
      * @return <code>true</code> if the unknown record id has been observed in POI unit tests
      */
     private static boolean isObservedButUnknown(int sid) {
+        // TODO Look up more of these in the latest [MS-XLS] doc and move to getBiffName
         switch (sid) {
             case 0x0033:
                 // contains 2 bytes of data: 0x0001 or 0x0003
@@ -227,13 +236,7 @@ public final class UnknownRecord extends
                 // Written by Excel 2007
                 // rawData is multiple of 12 bytes long
                 // appears after last cell value record and before WINDOW2 or drawing records
-            case 0x089D:
-            case 0x089E:
-            case 0x08A7:
 
-            case 0x1001:
-            case 0x1006:
-            case 0x1007:
             case 0x1009:
             case 0x100A:
             case 0x100B:

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java Sun Jul 19 19:00:32 2015
@@ -21,7 +21,11 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.poi.hssf.model.RecordStream;
+import org.apache.poi.hssf.record.CFHeader12Record;
+import org.apache.poi.hssf.record.CFHeaderBase;
 import org.apache.poi.hssf.record.CFHeaderRecord;
+import org.apache.poi.hssf.record.CFRule12Record;
+import org.apache.poi.hssf.record.CFRuleBase;
 import org.apache.poi.hssf.record.CFRuleRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.ss.formula.FormulaShifter;
@@ -38,212 +42,240 @@ import org.apache.poi.util.POILogger;
  * <p>Note that Excel versions before 2007 can only cope with a maximum of 3
  *  Conditional Formatting rules per sheet. Excel 2007 or newer can cope with
  *  unlimited numbers, as can Apache OpenOffice. This is an Excel limitation,
- *  not a file format one.</p> 
+ *  not a file format one.</p>
  */
 public final class CFRecordsAggregate extends RecordAggregate {
-	/** Excel 97-2003 allows up to 3 conditional formating rules */
-	private static final int MAX_97_2003_CONDTIONAL_FORMAT_RULES = 3;
-	private static final POILogger logger = POILogFactory.getLogger(CFRecordsAggregate.class);
-
-	private final CFHeaderRecord header;
-
-	/** List of CFRuleRecord objects */
-	private final List<CFRuleRecord> rules;
-
-	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_97_2003_CONDTIONAL_FORMAT_RULES) {
-		    logger.log(POILogger.WARN, "Excel versions before 2007 require that "
-			        + "No more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES 
-			        + " rules may be specified, " + pRules.length + " were found,"
-			        + " this file will cause problems with old Excel versions");
-		}
-		if (pRules.length != pHeader.getNumberOfConditionalFormats()) {
-			throw new RuntimeException("Mismatch number of rules");
-		}
-		header = pHeader;
-		rules = new ArrayList<CFRuleRecord>(3);
-		for (int i = 0; i < pRules.length; i++) {
-			rules.add(pRules[i]);
-		}
-	}
-
-	public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleRecord[] rules) {
-		this(new CFHeaderRecord(regions, rules.length), rules);
-	}
-
-	/**
-	 * Create CFRecordsAggregate from a list of CF Records
-	 * @param rs - the stream to read from
-	 * @return CFRecordsAggregate object
-	 */
-	public static CFRecordsAggregate createCFAggregate(RecordStream rs) {
-		Record rec = rs.getNext();
-		if (rec.getSid() != CFHeaderRecord.sid) {
-			throw new IllegalStateException("next record sid was " + rec.getSid() 
-					+ " instead of " + CFHeaderRecord.sid + " as expected");
-		}
-
-		CFHeaderRecord header = (CFHeaderRecord)rec;
-		int nRules = header.getNumberOfConditionalFormats();
-
-		CFRuleRecord[] rules = new CFRuleRecord[nRules];
-		for (int i = 0; i < rules.length; i++) {
-			rules[i] = (CFRuleRecord) rs.getNext();
-		}
-		
-		return new CFRecordsAggregate(header, rules);
-	}
-
-	/**
-	 * Create a deep clone of the record
-	 */
-	public CFRecordsAggregate cloneCFAggregate()
-	{
-	  
-		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);
-	}
-
-	/**
-	 * @return the header. Never <code>null</code>.
-	 */
-	public CFHeaderRecord getHeader()
-	{
-		return header;
-	}
-	
-	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 rules.get(idx);
-	}
-	public void setRule(int idx, CFRuleRecord r) {
-		if (r == null) {
-			throw new IllegalArgumentException("r must not be null");
-		}
-		checkRuleIndex(idx);
-		rules.set(idx, r);
-	}
-	public void addRule(CFRuleRecord r) {
-		if (r == null) {
-			throw new IllegalArgumentException("r must not be null");
-		}
-		if(rules.size() >= MAX_97_2003_CONDTIONAL_FORMAT_RULES) {
+    /** Excel 97-2003 allows up to 3 conditional formating rules */
+    private static final int MAX_97_2003_CONDTIONAL_FORMAT_RULES = 3;
+    private static final POILogger logger = POILogFactory.getLogger(CFRecordsAggregate.class);
+
+    private final CFHeaderBase header;
+
+    /** List of CFRuleRecord objects */
+    private final List<CFRuleBase> rules;
+
+    private CFRecordsAggregate(CFHeaderBase pHeader, CFRuleBase[] 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_97_2003_CONDTIONAL_FORMAT_RULES) {
+            logger.log(POILogger.WARN, "Excel versions before 2007 require that "
+                    + "No more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES 
+                    + " rules may be specified, " + pRules.length + " were found,"
+                    + " this file will cause problems with old Excel versions");
+        }
+        if (pRules.length != pHeader.getNumberOfConditionalFormats()) {
+            throw new RuntimeException("Mismatch number of rules");
+        }
+        header = pHeader;
+        rules = new ArrayList<CFRuleBase>(pRules.length);
+        for (int i = 0; i < pRules.length; i++) {
+            checkRuleType(pRules[i]);
+            rules.add(pRules[i]);
+        }
+    }
+
+    public CFRecordsAggregate(CellRangeAddress[] regions, CFRuleBase[] rules) {
+        this(createHeader(regions, rules), rules);
+    }
+    private static CFHeaderBase createHeader(CellRangeAddress[] regions, CFRuleBase[] rules) {
+        if (rules.length == 0 || rules[0] instanceof CFRuleRecord) {
+            return new CFHeaderRecord(regions, rules.length);
+        }
+        return new CFHeader12Record(regions, rules.length);
+    }
+
+    /**
+     * Create CFRecordsAggregate from a list of CF Records
+     * @param rs - the stream to read from
+     * @return CFRecordsAggregate object
+     */
+    public static CFRecordsAggregate createCFAggregate(RecordStream rs) {
+        Record rec = rs.getNext();
+        if (rec.getSid() != CFHeaderRecord.sid &&
+            rec.getSid() != CFHeader12Record.sid) {
+            throw new IllegalStateException("next record sid was " + rec.getSid() 
+                    + " instead of " + CFHeaderRecord.sid + " or " +
+                    CFHeader12Record.sid + " as expected");
+        }
+
+        CFHeaderBase header = (CFHeaderBase)rec;
+        int nRules = header.getNumberOfConditionalFormats();
+
+        CFRuleBase[] rules = new CFRuleBase[nRules];
+        for (int i = 0; i < rules.length; i++) {
+            rules[i] = (CFRuleBase) rs.getNext();
+        }
+
+        return new CFRecordsAggregate(header, rules);
+    }
+
+    /**
+     * Create a deep clone of the record
+     */
+    public CFRecordsAggregate cloneCFAggregate() {
+        CFRuleBase[] newRecs = new CFRuleBase[rules.size()];
+        for (int i = 0; i < newRecs.length; i++) {
+            newRecs[i] = (CFRuleRecord) getRule(i).clone();
+        }
+        return new CFRecordsAggregate((CFHeaderBase)header.clone(), newRecs);
+    }
+
+    /**
+     * @return the header. Never <code>null</code>.
+     */
+    public CFHeaderBase getHeader() {
+        return header;
+    }
+
+    private void checkRuleIndex(int idx) {
+        if(idx < 0 || idx >= rules.size()) {
+            throw new IllegalArgumentException("Bad rule record index (" + idx 
+                    + ") nRules=" + rules.size());
+        }
+    }
+    private void checkRuleType(CFRuleBase r) {
+        if (header instanceof CFHeaderRecord &&
+                 r instanceof CFRuleRecord) {
+            return;
+        }
+        if (header instanceof CFHeader12Record &&
+                 r instanceof CFRule12Record) {
+           return;
+        }
+        throw new IllegalArgumentException("Header and Rule must both be CF or both be CF12, can't mix");
+    }
+
+    public CFRuleBase getRule(int idx) {
+        checkRuleIndex(idx);
+        return rules.get(idx);
+    }
+    public void setRule(int idx, CFRuleBase r) {
+        if (r == null) {
+            throw new IllegalArgumentException("r must not be null");
+        }
+        checkRuleIndex(idx);
+        checkRuleType(r);
+        rules.set(idx, r);
+    }
+    public void addRule(CFRuleBase r) {
+        if (r == null) {
+            throw new IllegalArgumentException("r must not be null");
+        }
+        if(rules.size() >= MAX_97_2003_CONDTIONAL_FORMAT_RULES) {
             logger.log(POILogger.WARN, "Excel versions before 2007 cannot cope with" 
-		            + " any more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES 
+                    + " any more than " + MAX_97_2003_CONDTIONAL_FORMAT_RULES 
                     + " - this file will cause problems with old Excel versions");
-		}
-		rules.add(r);
-		header.setNumberOfConditionalFormats(rules.size());
-	}
-	public int getNumberOfRules() {
-		return rules.size();
-	}
-
-	/**
-	 * String representation of CFRecordsAggregate
-	 */
-	public String toString()
-	{
-		StringBuffer buffer = new StringBuffer();
-
-		buffer.append("[CF]\n");
-		if( header != null )
-		{
-			buffer.append(header.toString());
-		}
-		for(int i=0; i<rules.size(); i++)
-		{
-			CFRuleRecord cfRule = rules.get(i);
-			buffer.append(cfRule.toString());
-		}
-		buffer.append("[/CF]\n");
-		return buffer.toString();
-	}
-
-	public void visitContainedRecords(RecordVisitor rv) {
-		rv.visitRecord(header);
-		for(int i=0; i<rules.size(); i++) {
-			CFRuleRecord rule = rules.get(i);
-			rv.visitRecord(rule);
-		}
-	}
-
-	/**
-	 * @return <code>false</code> if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted
-	 */
-	public boolean updateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx) {
-		CellRangeAddress[] cellRanges = header.getCellRanges();
-		boolean changed = false;
-		List<CellRangeAddress> temp = new ArrayList<CellRangeAddress>();
-		for (int i = 0; i < cellRanges.length; i++) {
-			CellRangeAddress craOld = cellRanges[i];
-			CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx);
-			if (craNew == null) {
-				changed = true;
-				continue;
-			}
-			temp.add(craNew);
-			if (craNew != craOld) {
-				changed = true;
-			}
-		}
-
-		if (changed) {
-			int nRanges = temp.size();
-			if (nRanges == 0) {
-				return false;
-			}
-			CellRangeAddress[] newRanges = new CellRangeAddress[nRanges];
-			temp.toArray(newRanges);
-			header.setCellRanges(newRanges);
-		}
-		
-		for(int i=0; i<rules.size(); i++) {
-			CFRuleRecord rule = rules.get(i);
-			Ptg[] ptgs;
-			ptgs = rule.getParsedExpression1();
-			if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
-				rule.setParsedExpression1(ptgs);
-			}
-			ptgs = rule.getParsedExpression2();
-			if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
-				rule.setParsedExpression2(ptgs);
-			}
-		}
-		return true;
-	}
-
-	private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
-		// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
-		AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
-		Ptg[] ptgs = { aptg, };
-		
-		if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) {
-			return cra;
-		}
-		Ptg ptg0 = ptgs[0];
-		if (ptg0 instanceof AreaPtg) {
-			AreaPtg bptg = (AreaPtg) ptg0;
-			return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
-		}
-		if (ptg0 instanceof AreaErrPtg) {
-			return null;
-		}
-		throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
-	}
+        }
+        checkRuleType(r);
+        rules.add(r);
+        header.setNumberOfConditionalFormats(rules.size());
+    }
+    public int getNumberOfRules() {
+        return rules.size();
+    }
+
+    /**
+     * String representation of CFRecordsAggregate
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        String type = "CF";
+        if (header instanceof CFHeader12Record) {
+            type = "CF12";
+        }
+
+        buffer.append("[").append(type).append("]\n");
+        if( header != null ) {
+            buffer.append(header.toString());
+        }
+        for(int i=0; i<rules.size(); i++) {
+            CFRuleBase cfRule = rules.get(i);
+            buffer.append(cfRule.toString());
+        }
+        buffer.append("[/").append(type).append("]\n");
+        return buffer.toString();
+    }
+
+    public void visitContainedRecords(RecordVisitor rv) {
+        rv.visitRecord(header);
+        for(int i=0; i<rules.size(); i++) {
+            CFRuleBase rule = rules.get(i);
+            rv.visitRecord(rule);
+        }
+    }
+
+    /**
+     * @return <code>false</code> if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted
+     */
+    public boolean updateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx) {
+        CellRangeAddress[] cellRanges = header.getCellRanges();
+        boolean changed = false;
+        List<CellRangeAddress> temp = new ArrayList<CellRangeAddress>();
+        for (int i = 0; i < cellRanges.length; i++) {
+            CellRangeAddress craOld = cellRanges[i];
+            CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx);
+            if (craNew == null) {
+                changed = true;
+                continue;
+            }
+            temp.add(craNew);
+            if (craNew != craOld) {
+                changed = true;
+            }
+        }
+
+        if (changed) {
+            int nRanges = temp.size();
+            if (nRanges == 0) {
+                return false;
+            }
+            CellRangeAddress[] newRanges = new CellRangeAddress[nRanges];
+            temp.toArray(newRanges);
+            header.setCellRanges(newRanges);
+        }
+
+        for(int i=0; i<rules.size(); i++) {
+            CFRuleBase rule = rules.get(i);
+            Ptg[] ptgs;
+            ptgs = rule.getParsedExpression1();
+            if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+                rule.setParsedExpression1(ptgs);
+            }
+            ptgs = rule.getParsedExpression2();
+            if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+                rule.setParsedExpression2(ptgs);
+            }
+            if (rule instanceof CFRule12Record) {
+                CFRule12Record rule12 = (CFRule12Record)rule;
+                ptgs = rule12.getParsedExpressionScale();
+                if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+                    rule12.setParsedExpressionScale(ptgs);
+                }
+            }
+        }
+        return true;
+    }
+
+    private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
+        // FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
+        AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
+        Ptg[] ptgs = { aptg, };
+
+        if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+            return cra;
+        }
+        Ptg ptg0 = ptgs[0];
+        if (ptg0 instanceof AreaPtg) {
+            AreaPtg bptg = (AreaPtg) ptg0;
+            return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
+        }
+        if (ptg0 instanceof AreaErrPtg) {
+            return null;
+        }
+        throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
+    }
 }

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/ConditionalFormattingTable.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/ConditionalFormattingTable.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/ConditionalFormattingTable.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/aggregates/ConditionalFormattingTable.java Sun Jul 19 19:00:32 2015
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.poi.hssf.model.RecordStream;
+import org.apache.poi.hssf.record.CFHeader12Record;
 import org.apache.poi.hssf.record.CFHeaderRecord;
 import org.apache.poi.ss.formula.FormulaShifter;
 
@@ -28,11 +29,8 @@ import org.apache.poi.ss.formula.Formula
  * Holds all the conditional formatting for a workbook sheet.<p/>
  * 
  * See OOO exelfileformat.pdf sec 4.12 'Conditional Formatting Table'
- * 
- * @author Josh Micich
  */
 public final class ConditionalFormattingTable extends RecordAggregate {
-
 	private final List<CFRecordsAggregate> _cfHeaders;
 
 	/**
@@ -45,7 +43,8 @@ public final class ConditionalFormatting
 	public ConditionalFormattingTable(RecordStream rs) {
 
 		List<CFRecordsAggregate> temp = new ArrayList<CFRecordsAggregate>();
-		while (rs.peekNextClass() == CFHeaderRecord.class) {
+		while (rs.peekNextClass() == CFHeaderRecord.class ||
+		       rs.peekNextClass() == CFHeader12Record.class) {
 			temp.add(CFRecordsAggregate.createCFAggregate(rs));
 		}
 		_cfHeaders = temp;
@@ -62,6 +61,7 @@ public final class ConditionalFormatting
 	 * @return index of the newly added CF header aggregate
 	 */
 	public int add(CFRecordsAggregate cfAggregate) {
+	    cfAggregate.getHeader().setID(_cfHeaders.size());
 		_cfHeaders.add(cfAggregate);
 		return _cfHeaders.size() - 1;
 	}

Modified: poi/branches/common_sl/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java?rev=1691843&r1=1691842&r2=1691843&view=diff
==============================================================================
--- poi/branches/common_sl/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java (original)
+++ poi/branches/common_sl/src/java/org/apache/poi/hssf/record/cf/BorderFormatting.java Sun Jul 19 19:00:32 2015
@@ -25,11 +25,8 @@ import org.apache.poi.util.LittleEndianO
 
 /**
  * Border Formatting Block of the Conditional Formatting Rule Record.
- *
- * @author Dmitriy Kumshayev
  */
 public final class BorderFormatting {
-
     /** No border */
     public final static short    BORDER_NONE                = 0x0;
     /** Thin border */
@@ -89,6 +86,9 @@ public final class BorderFormatting {
         field_14_border_styles2    = in.readInt();
     }
 
+    public int getDataLength() {
+        return 8;
+    }
 
     /**
      * set the type of border to use for the left border of the cell



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