You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CFRuleBase.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hssf.record;
  16. import java.util.Map;
  17. import java.util.function.Supplier;
  18. import org.apache.logging.log4j.LogManager;
  19. import org.apache.logging.log4j.Logger;
  20. import org.apache.poi.hssf.model.HSSFFormulaParser;
  21. import org.apache.poi.hssf.record.cf.BorderFormatting;
  22. import org.apache.poi.hssf.record.cf.FontFormatting;
  23. import org.apache.poi.hssf.record.cf.PatternFormatting;
  24. import org.apache.poi.hssf.usermodel.HSSFSheet;
  25. import org.apache.poi.ss.formula.Formula;
  26. import org.apache.poi.ss.formula.FormulaType;
  27. import org.apache.poi.ss.formula.ptg.Ptg;
  28. import org.apache.poi.util.BitField;
  29. import org.apache.poi.util.BitFieldFactory;
  30. import org.apache.poi.util.GenericRecordUtil;
  31. import org.apache.poi.util.LittleEndianOutput;
  32. /**
  33. * Conditional Formatting Rules. This can hold old-style rules
  34. *
  35. *
  36. * <p>This is for the older-style Excel conditional formattings,
  37. * new-style (Excel 2007+) also make use of {@link CFRule12Record}
  38. * for their rules.</p>
  39. */
  40. public abstract class CFRuleBase extends StandardRecord {
  41. // FIXME: Merge with org.apache.poi.ss.usermodel.ComparisonOperator and rewrite as an enum
  42. public interface ComparisonOperator {
  43. byte NO_COMPARISON = 0;
  44. byte BETWEEN = 1;
  45. byte NOT_BETWEEN = 2;
  46. byte EQUAL = 3;
  47. byte NOT_EQUAL = 4;
  48. byte GT = 5;
  49. byte LT = 6;
  50. byte GE = 7;
  51. byte LE = 8;
  52. byte max_operator = 8;
  53. }
  54. // The only kinds that CFRuleRecord handles
  55. public static final byte CONDITION_TYPE_CELL_VALUE_IS = 1;
  56. public static final byte CONDITION_TYPE_FORMULA = 2;
  57. // These are CFRule12Rule only
  58. public static final byte CONDITION_TYPE_COLOR_SCALE = 3;
  59. public static final byte CONDITION_TYPE_DATA_BAR = 4;
  60. public static final byte CONDITION_TYPE_FILTER = 5;
  61. public static final byte CONDITION_TYPE_ICON_SET = 6;
  62. public static final int TEMPLATE_CELL_VALUE = 0x0000;
  63. public static final int TEMPLATE_FORMULA = 0x0001;
  64. public static final int TEMPLATE_COLOR_SCALE_FORMATTING = 0x0002;
  65. public static final int TEMPLATE_DATA_BAR_FORMATTING = 0x0003;
  66. public static final int TEMPLATE_ICON_SET_FORMATTING = 0x0004;
  67. public static final int TEMPLATE_FILTER = 0x0005;
  68. public static final int TEMPLATE_UNIQUE_VALUES = 0x0007;
  69. public static final int TEMPLATE_CONTAINS_TEXT = 0x0008;
  70. public static final int TEMPLATE_CONTAINS_BLANKS = 0x0009;
  71. public static final int TEMPLATE_CONTAINS_NO_BLANKS = 0x000A;
  72. public static final int TEMPLATE_CONTAINS_ERRORS = 0x000B;
  73. public static final int TEMPLATE_CONTAINS_NO_ERRORS = 0x000C;
  74. public static final int TEMPLATE_TODAY = 0x000F;
  75. public static final int TEMPLATE_TOMORROW = 0x0010;
  76. public static final int TEMPLATE_YESTERDAY = 0x0011;
  77. public static final int TEMPLATE_LAST_7_DAYS = 0x0012;
  78. public static final int TEMPLATE_LAST_MONTH = 0x0013;
  79. public static final int TEMPLATE_NEXT_MONTH = 0x0014;
  80. public static final int TEMPLATE_THIS_WEEK = 0x0015;
  81. public static final int TEMPLATE_NEXT_WEEK = 0x0016;
  82. public static final int TEMPLATE_LAST_WEEK = 0x0017;
  83. public static final int TEMPLATE_THIS_MONTH = 0x0018;
  84. public static final int TEMPLATE_ABOVE_AVERAGE = 0x0019;
  85. public static final int TEMPLATE_BELOW_AVERAGE = 0x001A;
  86. public static final int TEMPLATE_DUPLICATE_VALUES = 0x001B;
  87. public static final int TEMPLATE_ABOVE_OR_EQUAL_TO_AVERAGE = 0x001D;
  88. public static final int TEMPLATE_BELOW_OR_EQUAL_TO_AVERAGE = 0x001E;
  89. protected static final Logger LOG = LogManager.getLogger(CFRuleBase.class);
  90. static final BitField modificationBits = bf(0x003FFFFF); // Bits: font,align,bord,patt,prot
  91. static final BitField alignHor = bf(0x00000001); // 0 = Horizontal alignment modified
  92. static final BitField alignVer = bf(0x00000002); // 0 = Vertical alignment modified
  93. static final BitField alignWrap = bf(0x00000004); // 0 = Text wrapped flag modified
  94. static final BitField alignRot = bf(0x00000008); // 0 = Text rotation modified
  95. static final BitField alignJustLast = bf(0x00000010); // 0 = Justify last line flag modified
  96. static final BitField alignIndent = bf(0x00000020); // 0 = Indentation modified
  97. static final BitField alignShrin = bf(0x00000040); // 0 = Shrink to fit flag modified
  98. static final BitField mergeCell = bf(0x00000080); // Normally 1, 0 = Merge Cell flag modified
  99. static final BitField protLocked = bf(0x00000100); // 0 = Cell locked flag modified
  100. static final BitField protHidden = bf(0x00000200); // 0 = Cell hidden flag modified
  101. static final BitField bordLeft = bf(0x00000400); // 0 = Left border style and colour modified
  102. static final BitField bordRight = bf(0x00000800); // 0 = Right border style and colour modified
  103. static final BitField bordTop = bf(0x00001000); // 0 = Top border style and colour modified
  104. static final BitField bordBot = bf(0x00002000); // 0 = Bottom border style and colour modified
  105. static final BitField bordTlBr = bf(0x00004000); // 0 = Top-left to bottom-right border flag modified
  106. static final BitField bordBlTr = bf(0x00008000); // 0 = Bottom-left to top-right border flag modified
  107. static final BitField pattStyle = bf(0x00010000); // 0 = Pattern style modified
  108. static final BitField pattCol = bf(0x00020000); // 0 = Pattern colour modified
  109. static final BitField pattBgCol = bf(0x00040000); // 0 = Pattern background colour modified
  110. static final BitField notUsed2 = bf(0x00380000); // Always 111 (ifmt / ifnt / 1)
  111. static final BitField undocumented = bf(0x03C00000); // Undocumented bits
  112. static final BitField fmtBlockBits = bf(0x7C000000); // Bits: font,align,bord,patt,prot
  113. static final BitField font = bf(0x04000000); // 1 = Record contains font formatting block
  114. static final BitField align = bf(0x08000000); // 1 = Record contains alignment formatting block
  115. static final BitField bord = bf(0x10000000); // 1 = Record contains border formatting block
  116. static final BitField patt = bf(0x20000000); // 1 = Record contains pattern formatting block
  117. static final BitField prot = bf(0x40000000); // 1 = Record contains protection formatting block
  118. static final BitField alignTextDir = bf(0x80000000); // 0 = Text direction modified
  119. private static BitField bf(int i) {
  120. return BitFieldFactory.getInstance(i);
  121. }
  122. private byte condition_type;
  123. private byte comparison_operator;
  124. protected int formatting_options;
  125. // TODO Decode this properly
  126. protected short formatting_not_used;
  127. protected FontFormatting _fontFormatting;
  128. protected BorderFormatting _borderFormatting;
  129. protected PatternFormatting _patternFormatting;
  130. private Formula formula1;
  131. private Formula formula2;
  132. /**
  133. * Creates new CFRuleRecord
  134. *
  135. * @param conditionType the condition type
  136. * @param comparisonOperation the comparison operation
  137. */
  138. protected CFRuleBase(byte conditionType, byte comparisonOperation) {
  139. setConditionType(conditionType);
  140. setComparisonOperation(comparisonOperation);
  141. formula1 = Formula.create(Ptg.EMPTY_PTG_ARRAY);
  142. formula2 = Formula.create(Ptg.EMPTY_PTG_ARRAY);
  143. }
  144. protected CFRuleBase(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2) {
  145. this(conditionType, comparisonOperation);
  146. this.formula1 = Formula.create(formula1);
  147. this.formula2 = Formula.create(formula2);
  148. }
  149. protected CFRuleBase() {}
  150. protected CFRuleBase(CFRuleBase other) {
  151. super(other);
  152. setConditionType(other.getConditionType());
  153. setComparisonOperation(other.getComparisonOperation());
  154. formatting_options = other.formatting_options;
  155. formatting_not_used = other.formatting_not_used;
  156. _fontFormatting = (!other.containsFontFormattingBlock()) ? null : other.getFontFormatting().copy();
  157. _borderFormatting = (!other.containsBorderFormattingBlock()) ? null : other.getBorderFormatting().copy();
  158. _patternFormatting = (!other.containsPatternFormattingBlock()) ? null : other.getPatternFormatting().copy();
  159. formula1 = other.getFormula1().copy();
  160. formula2 = other.getFormula2().copy();
  161. }
  162. protected int readFormatOptions(RecordInputStream in) {
  163. formatting_options = in.readInt();
  164. formatting_not_used = in.readShort();
  165. int len = 6;
  166. if (containsFontFormattingBlock()) {
  167. _fontFormatting = new FontFormatting(in);
  168. len += _fontFormatting.getDataLength();
  169. }
  170. if (containsBorderFormattingBlock()) {
  171. _borderFormatting = new BorderFormatting(in);
  172. len += _borderFormatting.getDataLength();
  173. }
  174. if (containsPatternFormattingBlock()) {
  175. _patternFormatting = new PatternFormatting(in);
  176. len += _patternFormatting.getDataLength();
  177. }
  178. return len;
  179. }
  180. public byte getConditionType() {
  181. return condition_type;
  182. }
  183. protected void setConditionType(byte condition_type) {
  184. if ((this instanceof CFRuleRecord)) {
  185. if (!(condition_type == CONDITION_TYPE_CELL_VALUE_IS ||
  186. condition_type == CONDITION_TYPE_FORMULA)) {
  187. throw new IllegalArgumentException("CFRuleRecord only accepts Value-Is and Formula types");
  188. }
  189. }
  190. this.condition_type = condition_type;
  191. }
  192. public void setComparisonOperation(byte operation) {
  193. if (operation < 0 || operation > ComparisonOperator.max_operator)
  194. throw new IllegalArgumentException(
  195. "Valid operators are only in the range 0 to " +ComparisonOperator.max_operator);
  196. this.comparison_operator = operation;
  197. }
  198. public byte getComparisonOperation() {
  199. return comparison_operator;
  200. }
  201. public boolean containsFontFormattingBlock() {
  202. return getOptionFlag(font);
  203. }
  204. public void setFontFormatting(FontFormatting fontFormatting) {
  205. _fontFormatting = fontFormatting;
  206. setOptionFlag(fontFormatting != null, font);
  207. }
  208. public FontFormatting getFontFormatting() {
  209. if( containsFontFormattingBlock()) {
  210. return _fontFormatting;
  211. }
  212. return null;
  213. }
  214. public boolean containsAlignFormattingBlock() {
  215. return getOptionFlag(align);
  216. }
  217. public void setAlignFormattingUnchanged() {
  218. setOptionFlag(false,align);
  219. }
  220. public boolean containsBorderFormattingBlock() {
  221. return getOptionFlag(bord);
  222. }
  223. public void setBorderFormatting(BorderFormatting borderFormatting) {
  224. _borderFormatting = borderFormatting;
  225. setOptionFlag(borderFormatting != null, bord);
  226. }
  227. public BorderFormatting getBorderFormatting() {
  228. if( containsBorderFormattingBlock()) {
  229. return _borderFormatting;
  230. }
  231. return null;
  232. }
  233. public boolean containsPatternFormattingBlock() {
  234. return getOptionFlag(patt);
  235. }
  236. public void setPatternFormatting(PatternFormatting patternFormatting) {
  237. _patternFormatting = patternFormatting;
  238. setOptionFlag(patternFormatting!=null, patt);
  239. }
  240. public PatternFormatting getPatternFormatting() {
  241. if( containsPatternFormattingBlock())
  242. {
  243. return _patternFormatting;
  244. }
  245. return null;
  246. }
  247. public boolean containsProtectionFormattingBlock() {
  248. return getOptionFlag(prot);
  249. }
  250. public void setProtectionFormattingUnchanged() {
  251. setOptionFlag(false,prot);
  252. }
  253. /**
  254. * get the option flags
  255. *
  256. * @return bit mask
  257. */
  258. public int getOptions() {
  259. return formatting_options;
  260. }
  261. private boolean isModified(BitField field) {
  262. return !field.isSet(formatting_options);
  263. }
  264. private void setModified(boolean modified, BitField field) {
  265. formatting_options = field.setBoolean(formatting_options, !modified);
  266. }
  267. public boolean isLeftBorderModified() {
  268. return isModified(bordLeft);
  269. }
  270. public void setLeftBorderModified(boolean modified) {
  271. setModified(modified,bordLeft);
  272. }
  273. public boolean isRightBorderModified() {
  274. return isModified(bordRight);
  275. }
  276. public void setRightBorderModified(boolean modified)
  277. {
  278. setModified(modified,bordRight);
  279. }
  280. public boolean isTopBorderModified() {
  281. return isModified(bordTop);
  282. }
  283. public void setTopBorderModified(boolean modified) {
  284. setModified(modified,bordTop);
  285. }
  286. public boolean isBottomBorderModified() {
  287. return isModified(bordBot);
  288. }
  289. public void setBottomBorderModified(boolean modified) {
  290. setModified(modified,bordBot);
  291. }
  292. public boolean isTopLeftBottomRightBorderModified() {
  293. return isModified(bordTlBr);
  294. }
  295. public void setTopLeftBottomRightBorderModified(boolean modified) {
  296. setModified(modified,bordTlBr);
  297. }
  298. public boolean isBottomLeftTopRightBorderModified() {
  299. return isModified(bordBlTr);
  300. }
  301. public void setBottomLeftTopRightBorderModified(boolean modified) {
  302. setModified(modified,bordBlTr);
  303. }
  304. public boolean isPatternStyleModified() {
  305. return isModified(pattStyle);
  306. }
  307. public void setPatternStyleModified(boolean modified) {
  308. setModified(modified,pattStyle);
  309. }
  310. public boolean isPatternColorModified() {
  311. return isModified(pattCol);
  312. }
  313. public void setPatternColorModified(boolean modified) {
  314. setModified(modified,pattCol);
  315. }
  316. public boolean isPatternBackgroundColorModified() {
  317. return isModified(pattBgCol);
  318. }
  319. public void setPatternBackgroundColorModified(boolean modified) {
  320. setModified(modified,pattBgCol);
  321. }
  322. private boolean getOptionFlag(BitField field) {
  323. return field.isSet(formatting_options);
  324. }
  325. private void setOptionFlag(boolean flag, BitField field) {
  326. formatting_options = field.setBoolean(formatting_options, flag);
  327. }
  328. protected int getFormattingBlockSize() {
  329. return 6 +
  330. (containsFontFormattingBlock()?_fontFormatting.getRawRecord().length:0)+
  331. (containsBorderFormattingBlock()?8:0)+
  332. (containsPatternFormattingBlock()?4:0);
  333. }
  334. protected void serializeFormattingBlock(LittleEndianOutput out) {
  335. out.writeInt(formatting_options);
  336. out.writeShort(formatting_not_used);
  337. if (containsFontFormattingBlock()) {
  338. byte[] fontFormattingRawRecord = _fontFormatting.getRawRecord();
  339. out.write(fontFormattingRawRecord);
  340. }
  341. if (containsBorderFormattingBlock()) {
  342. _borderFormatting.serialize(out);
  343. }
  344. if (containsPatternFormattingBlock()) {
  345. _patternFormatting.serialize(out);
  346. }
  347. }
  348. /**
  349. * get the stack of the 1st expression as a list
  350. *
  351. * @return list of tokens (casts stack to a list and returns it!)
  352. * this method can return null is we are unable to create Ptgs from
  353. * existing excel file
  354. * callers should check for null!
  355. */
  356. public Ptg[] getParsedExpression1() {
  357. return formula1.getTokens();
  358. }
  359. public void setParsedExpression1(Ptg[] ptgs) {
  360. formula1 = Formula.create(ptgs);
  361. }
  362. protected Formula getFormula1() {
  363. return formula1;
  364. }
  365. protected void setFormula1(Formula formula1) {
  366. this.formula1 = formula1;
  367. }
  368. /**
  369. * get the stack of the 2nd expression as a list
  370. *
  371. * @return array of {@link Ptg}s, possibly <code>null</code>
  372. */
  373. public Ptg[] getParsedExpression2() {
  374. return Formula.getTokens(formula2);
  375. }
  376. public void setParsedExpression2(Ptg[] ptgs) {
  377. formula2 = Formula.create(ptgs);
  378. }
  379. protected Formula getFormula2() {
  380. return formula2;
  381. }
  382. protected void setFormula2(Formula formula2) {
  383. this.formula2 = formula2;
  384. }
  385. /**
  386. * @param formula must not be <code>null</code>
  387. * @return encoded size of the formula tokens (does not include 2 bytes for ushort length)
  388. */
  389. protected static int getFormulaSize(Formula formula) {
  390. return formula.getEncodedTokenSize();
  391. }
  392. /**
  393. * TODO - parse conditional format formulas properly i.e. produce tRefN and tAreaN instead of tRef and tArea
  394. * this call will produce the wrong results if the formula contains any cell references
  395. * One approach might be to apply the inverse of SharedFormulaRecord.convertSharedFormulas(Stack, int, int)
  396. * Note - two extra parameters (rowIx &amp; colIx) will be required. They probably come from one of the Region objects.
  397. *
  398. * @param formula The formula to parse, excluding the leading equals sign.
  399. * @param sheet The sheet that the formula is on.
  400. * @return <code>null</code> if <tt>formula</tt> was null.
  401. */
  402. public static Ptg[] parseFormula(String formula, HSSFSheet sheet) {
  403. if(formula == null) {
  404. return null;
  405. }
  406. int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
  407. return HSSFFormulaParser.parse(formula, sheet.getWorkbook(), FormulaType.CELL, sheetIndex);
  408. }
  409. @Override
  410. public abstract CFRuleBase copy();
  411. @Override
  412. public Map<String, Supplier<?>> getGenericProperties() {
  413. return GenericRecordUtil.getGenericProperties(
  414. "conditionType", this::getConditionType,
  415. "comparisonOperation", this::getComparisonOperation,
  416. "formattingOptions", this::getOptions,
  417. "formattingNotUsed", () -> formatting_not_used,
  418. "fontFormatting", this::getFontFormatting,
  419. "borderFormatting", this::getBorderFormatting,
  420. "patternFormatting", this::getPatternFormatting,
  421. "formula1", this::getFormula1,
  422. "formula2", this::getFormula2
  423. );
  424. }
  425. }