Further CFRule12 parsing

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1690742 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-07-13 16:07:39 +00:00
parent 363a2723c3
commit c723be5dad
3 changed files with 207 additions and 80 deletions

View File

@ -24,6 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.formula.Formula; import org.apache.poi.ss.formula.Formula;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.POILogger;
/** /**
* Conditional Formatting v12 Rule Record (0x087A). * Conditional Formatting v12 Rule Record (0x087A).
@ -39,20 +40,46 @@ public final class CFRule12Record extends CFRuleBase {
public static final short sid = 0x087A; public static final short sid = 0x087A;
private FtrHeader futureHeader; 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 */ /** Creates new CFRuleRecord */
private CFRule12Record(byte conditionType, byte comparisonOperation) { private CFRule12Record(byte conditionType, byte comparisonOperation) {
super(conditionType, comparisonOperation); super(conditionType, comparisonOperation);
futureHeader = new FtrHeader(); setDefaults();
futureHeader.setRecordType(sid);
// TODO Remaining fields
} }
private CFRule12Record(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2, Ptg[] formulaScale) { private CFRule12Record(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2, Ptg[] formulaScale) {
super(conditionType, comparisonOperation, formula1, formula2); super(conditionType, comparisonOperation, formula1, formula2);
this.formulaScale = Formula.create(formulaScale); setDefaults();
// TODO Remaining fields 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_3_formula1_len = in.readUShort();
int field_4_formula2_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! * callers should check for null!
*/ */
public Ptg[] getParsedExpressionScale() { public Ptg[] getParsedExpressionScale() {
return formulaScale.getTokens(); return formula_scale.getTokens();
} }
public void setParsedExpressionScale(Ptg[] ptgs) { public void setParsedExpressionScale(Ptg[] ptgs) {
formulaScale = Formula.create(ptgs); formula_scale = Formula.create(ptgs);
} }
public short getSid() { public short getSid() {
@ -132,22 +199,71 @@ public final class CFRule12Record extends CFRuleBase {
out.writeShort(formula1Len); out.writeShort(formula1Len);
out.writeShort(formula2Len); 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() { protected int getDataSize() {
// TODO Calculate int len = FtrHeader.getDataSize() + 6;
return 0; 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() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("[CFRULE12]\n"); buffer.append("[CFRULE12]\n");
buffer.append(" .condition_type =").append(getConditionType()).append("\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 1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n");
buffer.append(" Formula 2 =").append(Arrays.toString(getFormula2().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"); buffer.append("[/CFRULE12]\n");
return buffer.toString(); return buffer.toString();
} }
@ -158,7 +274,7 @@ public final class CFRule12Record extends CFRuleBase {
// TODO The other fields // TODO The other fields
rec.formulaScale = formulaScale.copy(); rec.formula_scale = formula_scale.copy();
return rec; return rec;
} }

View File

@ -27,6 +27,9 @@ import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; 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 * 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; public static final byte LE = 8;
private static final byte max_operator = 8; private static final byte max_operator = 8;
} }
protected static final POILogger logger = POILogFactory.getLogger(CFRuleBase.class);
private byte condition_type; private byte condition_type;
// The only kinds that CFRuleRecord handles // 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; 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 modificationBits = bf(0x003FFFFF); // Bits: font,align,bord,patt,prot
static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified
static final BitField alignVer = bf(0x00000002); // 0 = Vertical 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 alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified
static final BitField alignRot = bf(0x00000008); // 0 = Text rotation 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 alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified
static final BitField alignIndent = bf(0x00000020); // 0 = Indentation 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 alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified
static final BitField notUsed1 = bf(0x00000080); // Always 1 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 protLocked = bf(0x00000100); // 0 = Cell locked flag modified
static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden 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 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 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 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 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 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 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 pattStyle = bf(0x00010000); // 0 = Pattern style modified
static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour 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 pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified
static final BitField notUsed2 = bf(0x00380000); // Always 111 static final BitField notUsed2 = bf(0x00380000); // Always 111 (ifmt / ifnt / 1)
static final BitField undocumented = bf(0x03C00000); // Undocumented bits static final BitField undocumented = bf(0x03C00000); // Undocumented bits
static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot 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 font = bf(0x04000000); // 1 = Record contains font formatting block
static final BitField align = bf(0x08000000); // 1 = Record contains alignment 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 bord = bf(0x10000000); // 1 = Record contains border formatting block
static final BitField patt = bf(0x20000000); // 1 = Record contains pattern 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 prot = bf(0x40000000); // 1 = Record contains protection formatting block
static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified
private static BitField bf(int i) { private static BitField bf(int i) {
return BitFieldFactory.getInstance(i); return BitFieldFactory.getInstance(i);
} }
protected int field_5_options; // TODO Rename me protected int formatting_options;
protected short field_6_not_used; // TODO Rename me protected short formatting_not_used; // TODO Decode this properly
protected FontFormatting _fontFormatting; protected FontFormatting _fontFormatting;
protected BorderFormatting _borderFormatting; protected BorderFormatting _borderFormatting;
@ -149,8 +153,8 @@ public abstract class CFRuleBase extends StandardRecord {
protected CFRuleBase() {} protected CFRuleBase() {}
protected int readFormatOptions(RecordInputStream in) { protected int readFormatOptions(RecordInputStream in) {
field_5_options = in.readInt(); formatting_options = in.readInt();
field_6_not_used = in.readShort(); formatting_not_used = in.readShort();
int len = 6; int len = 6;
@ -261,14 +265,14 @@ public abstract class CFRuleBase extends StandardRecord {
* @return bit mask * @return bit mask
*/ */
public int getOptions() { public int getOptions() {
return field_5_options; return formatting_options;
} }
private boolean isModified(BitField field) { private boolean isModified(BitField field) {
return !field.isSet(field_5_options); return !field.isSet(formatting_options);
} }
private void setModified(boolean modified, BitField field) { 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() { public boolean isLeftBorderModified() {
@ -336,10 +340,34 @@ public abstract class CFRuleBase extends StandardRecord {
} }
private boolean getOptionFlag(BitField field) { private boolean getOptionFlag(BitField field) {
return field.isSet(field_5_options); return field.isSet(formatting_options);
} }
private void setOptionFlag(boolean flag, BitField field) { 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.condition_type = condition_type;
rec.comparison_operator = comparison_operator; rec.comparison_operator = comparison_operator;
rec.field_5_options = field_5_options; rec.formatting_options = formatting_options;
rec.field_6_not_used = field_6_not_used; rec.formatting_not_used = formatting_not_used;
if (containsFontFormattingBlock()) { if (containsFontFormattingBlock()) {
rec._fontFormatting = (FontFormatting) _fontFormatting.clone(); rec._fontFormatting = (FontFormatting) _fontFormatting.clone();
} }

View File

@ -46,12 +46,12 @@ public final class CFRuleRecord extends CFRuleBase {
} }
private void setDefaults() { private void setDefaults() {
// Set modification flags to 1: by default options are not modified // 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) // Set formatting block flags to 0 (no formatting blocks)
field_5_options = fmtBlockBits.setValue(field_5_options, 0); formatting_options = fmtBlockBits.setValue(formatting_options, 0);
field_5_options = undocumented.clear(field_5_options); 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; _fontFormatting = null;
_borderFormatting = null; _borderFormatting = null;
_patternFormatting = null; _patternFormatting = null;
@ -106,34 +106,17 @@ public final class CFRuleRecord extends CFRuleBase {
out.writeByte(getComparisonOperation()); out.writeByte(getComparisonOperation());
out.writeShort(formula1Len); out.writeShort(formula1Len);
out.writeShort(formula2Len); out.writeShort(formula2Len);
out.writeInt(field_5_options);
out.writeShort(field_6_not_used); serializeFormattingBlock(out);
if (containsFontFormattingBlock()) {
byte[] fontFormattingRawRecord = _fontFormatting.getRawRecord();
out.write(fontFormattingRawRecord);
}
if (containsBorderFormattingBlock()) {
_borderFormatting.serialize(out);
}
if (containsPatternFormattingBlock()) {
_patternFormatting.serialize(out);
}
getFormula1().serializeTokens(out); getFormula1().serializeTokens(out);
getFormula2().serializeTokens(out); getFormula2().serializeTokens(out);
} }
protected int getDataSize() { protected int getDataSize() {
int i = 12 + return 12 + getFormattingBlockSize() +
(containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+ getFormulaSize(getFormula1())+
(containsBorderFormattingBlock()?8:0)+ getFormulaSize(getFormula2());
(containsPatternFormattingBlock()?4:0)+
getFormulaSize(getFormula1())+
getFormulaSize(getFormula2());
return i;
} }
public String toString() { public String toString() {