|
|
@@ -39,488 +39,429 @@ import org.apache.poi.util.LittleEndianOutput; |
|
|
|
* and {@link CFExRuleRecord} for their rules. |
|
|
|
*/ |
|
|
|
public final class CFRuleRecord extends StandardRecord { |
|
|
|
public static final short sid = 0x01B1; |
|
|
|
|
|
|
|
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 |
|
|
|
*/ |
|
|
|
public static final short sid = 0x01B1; |
|
|
|
|
|
|
|
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 |
|
|
|
*/ |
|
|
|
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"); |
|
|
|
/** |
|
|
|
* 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. |
|
|
|
*/ |
|
|
|
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; |