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).
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];
}
/**
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();
+ }
}
/**
* 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() {
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();
}
// TODO The other fields
- rec.formulaScale = formulaScale.copy();
+ rec.formula_scale = formula_scale.copy();
return rec;
}
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
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
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;
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;
* @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() {
}
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);
+ }
}
/**
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();
}
}
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;
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() {