git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1690742 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_13_BETA1
@@ -24,6 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.ss.formula.Formula; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Conditional Formatting v12 Rule Record (0x087A). | |||
@@ -39,20 +40,46 @@ public final class CFRule12Record extends CFRuleBase { | |||
public static final short sid = 0x087A; | |||
private FtrHeader futureHeader; | |||
private Formula formulaScale; | |||
private int ext_formatting_length; | |||
private byte[] ext_formatting_data; | |||
private Formula formula_scale; | |||
private byte ext_opts; | |||
private int priority; | |||
private int template_type; | |||
private byte template_param_length; | |||
private byte[] template_params; | |||
// TODO Parse these | |||
private byte[] gradient_data; | |||
private byte[] databar_data; | |||
private byte[] filter_data; | |||
private byte[] multistate_data; | |||
/** Creates new CFRuleRecord */ | |||
private CFRule12Record(byte conditionType, byte comparisonOperation) { | |||
super(conditionType, comparisonOperation); | |||
futureHeader = new FtrHeader(); | |||
futureHeader.setRecordType(sid); | |||
// TODO Remaining fields | |||
setDefaults(); | |||
} | |||
private CFRule12Record(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2, Ptg[] formulaScale) { | |||
super(conditionType, comparisonOperation, formula1, formula2); | |||
this.formulaScale = Formula.create(formulaScale); | |||
// TODO Remaining fields | |||
setDefaults(); | |||
this.formula_scale = Formula.create(formulaScale); | |||
} | |||
private void setDefaults() { | |||
futureHeader = new FtrHeader(); | |||
futureHeader.setRecordType(sid); | |||
ext_formatting_length = 0; | |||
ext_formatting_data = new byte[4]; | |||
formula_scale = Formula.create(Ptg.EMPTY_PTG_ARRAY); | |||
ext_opts = 0; | |||
priority = 0; | |||
template_type = getConditionType(); | |||
template_param_length = 16; | |||
template_params = new byte[template_param_length]; | |||
} | |||
/** | |||
@@ -92,7 +119,47 @@ public final class CFRule12Record extends CFRuleBase { | |||
int field_3_formula1_len = in.readUShort(); | |||
int field_4_formula2_len = in.readUShort(); | |||
// TODO Handle the remainder | |||
ext_formatting_length = in.readInt(); | |||
ext_formatting_data = new byte[0]; | |||
if (ext_formatting_length == 0) { | |||
// 2 bytes reserved | |||
in.readUShort(); | |||
} else { | |||
int len = readFormatOptions(in); | |||
if (len < ext_formatting_length) { | |||
ext_formatting_data = new byte[ext_formatting_length-len]; | |||
in.readFully(ext_formatting_data); | |||
} | |||
} | |||
setFormula1(Formula.read(field_3_formula1_len, in)); | |||
setFormula2(Formula.read(field_4_formula2_len, in)); | |||
int formula_scale_len = in.readUShort(); | |||
formula_scale = Formula.read(formula_scale_len, in); | |||
ext_opts = in.readByte(); | |||
priority = in.readUShort(); | |||
template_type = in.readUShort(); | |||
template_param_length = in.readByte(); | |||
if (template_param_length == 0 || template_param_length == 16) { | |||
template_params = new byte[template_param_length]; | |||
in.readFully(template_params); | |||
} else { | |||
logger.log(POILogger.WARN, "CF Rule v12 template params length should be 0 or 16, found " + template_param_length); | |||
in.readRemainder(); | |||
} | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
gradient_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
databar_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
filter_data = in.readRemainder(); | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
multistate_data = in.readRemainder(); | |||
} | |||
} | |||
/** | |||
@@ -104,10 +171,10 @@ public final class CFRule12Record extends CFRuleBase { | |||
* callers should check for null! | |||
*/ | |||
public Ptg[] getParsedExpressionScale() { | |||
return formulaScale.getTokens(); | |||
return formula_scale.getTokens(); | |||
} | |||
public void setParsedExpressionScale(Ptg[] ptgs) { | |||
formulaScale = Formula.create(ptgs); | |||
formula_scale = Formula.create(ptgs); | |||
} | |||
public short getSid() { | |||
@@ -132,22 +199,71 @@ public final class CFRule12Record extends CFRuleBase { | |||
out.writeShort(formula1Len); | |||
out.writeShort(formula2Len); | |||
// TODO Output the rest | |||
// TODO Update ext_formatting_length | |||
if (ext_formatting_length == 0) { | |||
out.writeInt(0); | |||
out.writeShort(0); | |||
} else { | |||
out.writeInt(ext_formatting_length); | |||
serializeFormattingBlock(out); | |||
out.write(ext_formatting_data); | |||
} | |||
getFormula1().serializeTokens(out); | |||
getFormula2().serializeTokens(out); | |||
formula_scale.serializeTokens(out); | |||
out.writeByte(ext_opts); | |||
out.writeShort(priority); | |||
out.writeShort(template_type); | |||
out.writeByte(template_param_length); | |||
out.write(template_params); | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
out.write(gradient_data); | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
out.write(databar_data); | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
out.write(filter_data); | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
out.write(multistate_data); | |||
} | |||
} | |||
protected int getDataSize() { | |||
// TODO Calculate | |||
return 0; | |||
int len = FtrHeader.getDataSize() + 6; | |||
if (ext_formatting_length == 0) { | |||
len += 6; | |||
} else { | |||
len += 4 + getFormattingBlockSize() + ext_formatting_data.length; | |||
} | |||
len += getFormulaSize(getFormula1()); | |||
len += getFormulaSize(getFormula2()); | |||
len += 4 + getFormulaSize(formula_scale); | |||
len += 6 + template_params.length; | |||
byte type = getConditionType(); | |||
if (type == CONDITION_TYPE_COLOR_SCALE) { | |||
len += gradient_data.length; | |||
} else if (type == CONDITION_TYPE_DATA_BAR) { | |||
len += databar_data.length; | |||
} else if (type == CONDITION_TYPE_FILTER) { | |||
len += filter_data.length; | |||
} else if (type == CONDITION_TYPE_ICON_SET) { | |||
len += multistate_data.length; | |||
} | |||
return len; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[CFRULE12]\n"); | |||
buffer.append(" .condition_type =").append(getConditionType()).append("\n"); | |||
buffer.append(" TODO The rest!\n"); | |||
buffer.append(" TODO The rest!\n"); // TODO The Rest | |||
buffer.append(" Formula 1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n"); | |||
buffer.append(" Formula 2 =").append(Arrays.toString(getFormula2().getTokens())).append("\n"); | |||
buffer.append(" Formula S =").append(Arrays.toString(formulaScale.getTokens())).append("\n"); | |||
buffer.append(" Formula S =").append(Arrays.toString(formula_scale.getTokens())).append("\n"); | |||
buffer.append("[/CFRULE12]\n"); | |||
return buffer.toString(); | |||
} | |||
@@ -158,7 +274,7 @@ public final class CFRule12Record extends CFRuleBase { | |||
// TODO The other fields | |||
rec.formulaScale = formulaScale.copy(); | |||
rec.formula_scale = formula_scale.copy(); | |||
return rec; | |||
} |
@@ -27,6 +27,9 @@ 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; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Conditional Formatting Rules. This can hold old-style rules | |||
@@ -49,6 +52,7 @@ public abstract class CFRuleBase extends StandardRecord { | |||
public static final byte LE = 8; | |||
private static final byte max_operator = 8; | |||
} | |||
protected static final POILogger logger = POILogFactory.getLogger(CFRuleBase.class); | |||
private byte condition_type; | |||
// The only kinds that CFRuleRecord handles | |||
@@ -91,41 +95,41 @@ public abstract class CFRuleBase extends StandardRecord { | |||
public static final int TEMPLATE_BELOW_OR_EQUAL_TO_AVERAGE = 0x001E; | |||
static final BitField modificationBits = bf(0x003FFFFF); // Bits: font,align,bord,patt,prot | |||
static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified | |||
static final BitField alignVer = bf(0x00000002); // 0 = Vertical alignment modified | |||
static final BitField alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified | |||
static final BitField alignRot = bf(0x00000008); // 0 = Text rotation modified | |||
static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified | |||
static final BitField alignIndent = bf(0x00000020); // 0 = Indentation modified | |||
static final BitField alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified | |||
static final BitField notUsed1 = bf(0x00000080); // Always 1 | |||
static final BitField protLocked = bf(0x00000100); // 0 = Cell locked flag modified | |||
static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden flag modified | |||
static final BitField bordLeft = bf(0x00000400); // 0 = Left border style and colour modified | |||
static final BitField bordRight = bf(0x00000800); // 0 = Right border style and colour modified | |||
static final BitField bordTop = bf(0x00001000); // 0 = Top border style and colour modified | |||
static final BitField bordBot = bf(0x00002000); // 0 = Bottom border style and colour modified | |||
static final BitField bordTlBr = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified | |||
static final BitField bordBlTr = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified | |||
static final BitField pattStyle = bf(0x00010000); // 0 = Pattern style modified | |||
static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour modified | |||
static final BitField pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified | |||
static final BitField notUsed2 = bf(0x00380000); // Always 111 | |||
static final BitField undocumented = bf(0x03C00000); // Undocumented bits | |||
static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot | |||
static final BitField font = bf(0x04000000); // 1 = Record contains font formatting block | |||
static final BitField align = bf(0x08000000); // 1 = Record contains alignment formatting block | |||
static final BitField bord = bf(0x10000000); // 1 = Record contains border formatting block | |||
static final BitField patt = bf(0x20000000); // 1 = Record contains pattern formatting block | |||
static final BitField prot = bf(0x40000000); // 1 = Record contains protection formatting block | |||
static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified | |||
static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified | |||
static final BitField alignVer = bf(0x00000002); // 0 = Vertical alignment modified | |||
static final BitField alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified | |||
static final BitField alignRot = bf(0x00000008); // 0 = Text rotation modified | |||
static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified | |||
static final BitField alignIndent = bf(0x00000020); // 0 = Indentation modified | |||
static final BitField alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified | |||
static final BitField mergeCell = bf(0x00000080); // Normally 1, 0 = Merge Cell flag modified | |||
static final BitField protLocked = bf(0x00000100); // 0 = Cell locked flag modified | |||
static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden flag modified | |||
static final BitField bordLeft = bf(0x00000400); // 0 = Left border style and colour modified | |||
static final BitField bordRight = bf(0x00000800); // 0 = Right border style and colour modified | |||
static final BitField bordTop = bf(0x00001000); // 0 = Top border style and colour modified | |||
static final BitField bordBot = bf(0x00002000); // 0 = Bottom border style and colour modified | |||
static final BitField bordTlBr = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified | |||
static final BitField bordBlTr = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified | |||
static final BitField pattStyle = bf(0x00010000); // 0 = Pattern style modified | |||
static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour modified | |||
static final BitField pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified | |||
static final BitField notUsed2 = bf(0x00380000); // Always 111 (ifmt / ifnt / 1) | |||
static final BitField undocumented = bf(0x03C00000); // Undocumented bits | |||
static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot | |||
static final BitField font = bf(0x04000000); // 1 = Record contains font formatting block | |||
static final BitField align = bf(0x08000000); // 1 = Record contains alignment formatting block | |||
static final BitField bord = bf(0x10000000); // 1 = Record contains border formatting block | |||
static final BitField patt = bf(0x20000000); // 1 = Record contains pattern formatting block | |||
static final BitField prot = bf(0x40000000); // 1 = Record contains protection formatting block | |||
static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified | |||
private static BitField bf(int i) { | |||
return BitFieldFactory.getInstance(i); | |||
} | |||
protected int field_5_options; // TODO Rename me | |||
protected short field_6_not_used; // TODO Rename me | |||
protected int formatting_options; | |||
protected short formatting_not_used; // TODO Decode this properly | |||
protected FontFormatting _fontFormatting; | |||
protected BorderFormatting _borderFormatting; | |||
@@ -149,8 +153,8 @@ public abstract class CFRuleBase extends StandardRecord { | |||
protected CFRuleBase() {} | |||
protected int readFormatOptions(RecordInputStream in) { | |||
field_5_options = in.readInt(); | |||
field_6_not_used = in.readShort(); | |||
formatting_options = in.readInt(); | |||
formatting_not_used = in.readShort(); | |||
int len = 6; | |||
@@ -261,14 +265,14 @@ public abstract class CFRuleBase extends StandardRecord { | |||
* @return bit mask | |||
*/ | |||
public int getOptions() { | |||
return field_5_options; | |||
return formatting_options; | |||
} | |||
private boolean isModified(BitField field) { | |||
return !field.isSet(field_5_options); | |||
return !field.isSet(formatting_options); | |||
} | |||
private void setModified(boolean modified, BitField field) { | |||
field_5_options = field.setBoolean(field_5_options, !modified); | |||
formatting_options = field.setBoolean(formatting_options, !modified); | |||
} | |||
public boolean isLeftBorderModified() { | |||
@@ -336,10 +340,34 @@ public abstract class CFRuleBase extends StandardRecord { | |||
} | |||
private boolean getOptionFlag(BitField field) { | |||
return field.isSet(field_5_options); | |||
return field.isSet(formatting_options); | |||
} | |||
private void setOptionFlag(boolean flag, BitField field) { | |||
field_5_options = field.setBoolean(field_5_options, flag); | |||
formatting_options = field.setBoolean(formatting_options, flag); | |||
} | |||
protected int getFormattingBlockSize() { | |||
return | |||
(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+ | |||
(containsBorderFormattingBlock()?8:0)+ | |||
(containsPatternFormattingBlock()?4:0); | |||
} | |||
protected void serializeFormattingBlock(LittleEndianOutput out) { | |||
out.writeInt(formatting_options); | |||
out.writeShort(formatting_not_used); | |||
if (containsFontFormattingBlock()) { | |||
byte[] fontFormattingRawRecord = _fontFormatting.getRawRecord(); | |||
out.write(fontFormattingRawRecord); | |||
} | |||
if (containsBorderFormattingBlock()) { | |||
_borderFormatting.serialize(out); | |||
} | |||
if (containsPatternFormattingBlock()) { | |||
_patternFormatting.serialize(out); | |||
} | |||
} | |||
/** | |||
@@ -409,8 +437,8 @@ public abstract class CFRuleBase extends StandardRecord { | |||
rec.condition_type = condition_type; | |||
rec.comparison_operator = comparison_operator; | |||
rec.field_5_options = field_5_options; | |||
rec.field_6_not_used = field_6_not_used; | |||
rec.formatting_options = formatting_options; | |||
rec.formatting_not_used = formatting_not_used; | |||
if (containsFontFormattingBlock()) { | |||
rec._fontFormatting = (FontFormatting) _fontFormatting.clone(); | |||
} |
@@ -46,12 +46,12 @@ public final class CFRuleRecord extends CFRuleBase { | |||
} | |||
private void setDefaults() { | |||
// Set modification flags to 1: by default options are not modified | |||
field_5_options = modificationBits.setValue(field_5_options, -1); | |||
formatting_options = modificationBits.setValue(formatting_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); | |||
formatting_options = fmtBlockBits.setValue(formatting_options, 0); | |||
formatting_options = undocumented.clear(formatting_options); | |||
field_6_not_used = (short)0x8002; // Excel seems to write this value, but it doesn't seem to care what it reads | |||
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; | |||
@@ -106,34 +106,17 @@ public final class CFRuleRecord extends CFRuleBase { | |||
out.writeByte(getComparisonOperation()); | |||
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); | |||
} | |||
serializeFormattingBlock(out); | |||
getFormula1().serializeTokens(out); | |||
getFormula2().serializeTokens(out); | |||
} | |||
protected int getDataSize() { | |||
int i = 12 + | |||
(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+ | |||
(containsBorderFormattingBlock()?8:0)+ | |||
(containsPatternFormattingBlock()?4:0)+ | |||
getFormulaSize(getFormula1())+ | |||
getFormulaSize(getFormula2()); | |||
return i; | |||
return 12 + getFormattingBlockSize() + | |||
getFormulaSize(getFormula1())+ | |||
getFormulaSize(getFormula2()); | |||
} | |||
public String toString() { |