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.

CFRule12Record.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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.Arrays;
  17. import org.apache.poi.hssf.record.cf.IconMultiStateFormatting;
  18. import org.apache.poi.hssf.record.cf.Threshold;
  19. import org.apache.poi.hssf.record.common.FtrHeader;
  20. import org.apache.poi.hssf.record.common.FutureRecord;
  21. import org.apache.poi.hssf.usermodel.HSSFSheet;
  22. import org.apache.poi.ss.formula.Formula;
  23. import org.apache.poi.ss.formula.ptg.Ptg;
  24. import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet;
  25. import org.apache.poi.ss.util.CellRangeAddress;
  26. import org.apache.poi.util.HexDump;
  27. import org.apache.poi.util.LittleEndianOutput;
  28. import org.apache.poi.util.POILogger;
  29. /**
  30. * Conditional Formatting v12 Rule Record (0x087A).
  31. *
  32. * <p>This is for newer-style Excel conditional formattings,
  33. * from Excel 2007 onwards.
  34. *
  35. * <p>{@link CFRuleRecord} is used where the condition type is
  36. * {@link #CONDITION_TYPE_CELL_VALUE_IS} or {@link #CONDITION_TYPE_FORMULA},
  37. * this is only used for the other types
  38. */
  39. public final class CFRule12Record extends CFRuleBase implements FutureRecord {
  40. public static final short sid = 0x087A;
  41. private FtrHeader futureHeader;
  42. private int ext_formatting_length;
  43. private byte[] ext_formatting_data;
  44. private Formula formula_scale;
  45. private byte ext_opts;
  46. private int priority;
  47. private int template_type;
  48. private byte template_param_length;
  49. private byte[] template_params;
  50. private IconMultiStateFormatting multistate;
  51. // TODO Parse these
  52. private byte[] gradient_data;
  53. private byte[] databar_data;
  54. private byte[] filter_data;
  55. /** Creates new CFRuleRecord */
  56. private CFRule12Record(byte conditionType, byte comparisonOperation) {
  57. super(conditionType, comparisonOperation);
  58. setDefaults();
  59. }
  60. private CFRule12Record(byte conditionType, byte comparisonOperation, Ptg[] formula1, Ptg[] formula2, Ptg[] formulaScale) {
  61. super(conditionType, comparisonOperation, formula1, formula2);
  62. setDefaults();
  63. this.formula_scale = Formula.create(formulaScale);
  64. }
  65. private void setDefaults() {
  66. futureHeader = new FtrHeader();
  67. futureHeader.setRecordType(sid);
  68. ext_formatting_length = 0;
  69. ext_formatting_data = new byte[4];
  70. formula_scale = Formula.create(Ptg.EMPTY_PTG_ARRAY);
  71. ext_opts = 0;
  72. priority = 0;
  73. template_type = getConditionType();
  74. template_param_length = 16;
  75. template_params = new byte[template_param_length];
  76. }
  77. /**
  78. * Creates a new comparison operation rule
  79. */
  80. public static CFRule12Record create(HSSFSheet sheet, String formulaText) {
  81. Ptg[] formula1 = parseFormula(formulaText, sheet);
  82. return new CFRule12Record(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON,
  83. formula1, null, null);
  84. }
  85. /**
  86. * Creates a new comparison operation rule
  87. */
  88. public static CFRule12Record create(HSSFSheet sheet, byte comparisonOperation,
  89. String formulaText1, String formulaText2) {
  90. Ptg[] formula1 = parseFormula(formulaText1, sheet);
  91. Ptg[] formula2 = parseFormula(formulaText2, sheet);
  92. return new CFRule12Record(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation,
  93. formula1, formula2, null);
  94. }
  95. /**
  96. * Creates a new comparison operation rule
  97. */
  98. public static CFRule12Record create(HSSFSheet sheet, byte comparisonOperation,
  99. String formulaText1, String formulaText2, String formulaTextScale) {
  100. Ptg[] formula1 = parseFormula(formulaText1, sheet);
  101. Ptg[] formula2 = parseFormula(formulaText2, sheet);
  102. Ptg[] formula3 = parseFormula(formulaTextScale, sheet);
  103. return new CFRule12Record(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation,
  104. formula1, formula2, formula3);
  105. }
  106. /**
  107. * Creates a new Icon Set / Multi-State formatting
  108. */
  109. public static CFRule12Record create(HSSFSheet sheet, IconSet iconSet) {
  110. Threshold[] ts = new Threshold[iconSet.num];
  111. for (int i=0; i<ts.length; i++) {
  112. ts[i] = new Threshold();
  113. }
  114. CFRule12Record r = new CFRule12Record(CONDITION_TYPE_COLOR_SCALE,
  115. ComparisonOperator.NO_COMPARISON);
  116. IconMultiStateFormatting imf = r.createMultiStateFormatting();
  117. imf.setIconSet(iconSet);
  118. imf.setThresholds(ts);
  119. return r;
  120. }
  121. // TODO Static creators for the other record types
  122. public CFRule12Record(RecordInputStream in) {
  123. futureHeader = new FtrHeader(in);
  124. setConditionType(in.readByte());
  125. setComparisonOperation(in.readByte());
  126. int field_3_formula1_len = in.readUShort();
  127. int field_4_formula2_len = in.readUShort();
  128. ext_formatting_length = in.readInt();
  129. ext_formatting_data = new byte[0];
  130. if (ext_formatting_length == 0) {
  131. // 2 bytes reserved
  132. in.readUShort();
  133. } else {
  134. int len = readFormatOptions(in);
  135. if (len < ext_formatting_length) {
  136. ext_formatting_data = new byte[ext_formatting_length-len];
  137. in.readFully(ext_formatting_data);
  138. }
  139. }
  140. setFormula1(Formula.read(field_3_formula1_len, in));
  141. setFormula2(Formula.read(field_4_formula2_len, in));
  142. int formula_scale_len = in.readUShort();
  143. formula_scale = Formula.read(formula_scale_len, in);
  144. ext_opts = in.readByte();
  145. priority = in.readUShort();
  146. template_type = in.readUShort();
  147. template_param_length = in.readByte();
  148. if (template_param_length == 0 || template_param_length == 16) {
  149. template_params = new byte[template_param_length];
  150. in.readFully(template_params);
  151. } else {
  152. logger.log(POILogger.WARN, "CF Rule v12 template params length should be 0 or 16, found " + template_param_length);
  153. in.readRemainder();
  154. }
  155. byte type = getConditionType();
  156. if (type == CONDITION_TYPE_COLOR_SCALE) {
  157. gradient_data = in.readRemainder();
  158. } else if (type == CONDITION_TYPE_DATA_BAR) {
  159. databar_data = in.readRemainder();
  160. } else if (type == CONDITION_TYPE_FILTER) {
  161. filter_data = in.readRemainder();
  162. } else if (type == CONDITION_TYPE_ICON_SET) {
  163. multistate = new IconMultiStateFormatting(in);
  164. }
  165. }
  166. public boolean containsMultiStateBlock() {
  167. return (multistate != null);
  168. }
  169. public IconMultiStateFormatting getMultiStateFormatting() {
  170. return multistate;
  171. }
  172. public IconMultiStateFormatting createMultiStateFormatting() {
  173. if (multistate != null) return multistate;
  174. // Convert, setup and return
  175. setConditionType(CONDITION_TYPE_ICON_SET);
  176. multistate = new IconMultiStateFormatting();
  177. return multistate;
  178. }
  179. /**
  180. * get the stack of the scale expression as a list
  181. *
  182. * @return list of tokens (casts stack to a list and returns it!)
  183. * this method can return null is we are unable to create Ptgs from
  184. * existing excel file
  185. * callers should check for null!
  186. */
  187. public Ptg[] getParsedExpressionScale() {
  188. return formula_scale.getTokens();
  189. }
  190. public void setParsedExpressionScale(Ptg[] ptgs) {
  191. formula_scale = Formula.create(ptgs);
  192. }
  193. public short getSid() {
  194. return sid;
  195. }
  196. /**
  197. * called by the class that is responsible for writing this sucker.
  198. * Subclasses should implement this so that their data is passed back in a
  199. * byte array.
  200. *
  201. * @param out the stream to write to
  202. */
  203. public void serialize(LittleEndianOutput out) {
  204. futureHeader.serialize(out);
  205. int formula1Len=getFormulaSize(getFormula1());
  206. int formula2Len=getFormulaSize(getFormula2());
  207. out.writeByte(getConditionType());
  208. out.writeByte(getComparisonOperation());
  209. out.writeShort(formula1Len);
  210. out.writeShort(formula2Len);
  211. // TODO Update ext_formatting_length
  212. if (ext_formatting_length == 0) {
  213. out.writeInt(0);
  214. out.writeShort(0);
  215. } else {
  216. out.writeInt(ext_formatting_length);
  217. serializeFormattingBlock(out);
  218. out.write(ext_formatting_data);
  219. }
  220. getFormula1().serializeTokens(out);
  221. getFormula2().serializeTokens(out);
  222. out.writeShort(getFormulaSize(formula_scale));
  223. formula_scale.serializeTokens(out);
  224. out.writeByte(ext_opts);
  225. out.writeShort(priority);
  226. out.writeShort(template_type);
  227. out.writeByte(template_param_length);
  228. out.write(template_params);
  229. byte type = getConditionType();
  230. if (type == CONDITION_TYPE_COLOR_SCALE) {
  231. out.write(gradient_data);
  232. } else if (type == CONDITION_TYPE_DATA_BAR) {
  233. out.write(databar_data);
  234. } else if (type == CONDITION_TYPE_FILTER) {
  235. out.write(filter_data);
  236. } else if (type == CONDITION_TYPE_ICON_SET) {
  237. multistate.serialize(out);
  238. }
  239. }
  240. protected int getDataSize() {
  241. int len = FtrHeader.getDataSize() + 6;
  242. if (ext_formatting_length == 0) {
  243. len += 6;
  244. } else {
  245. len += 4 + getFormattingBlockSize() + ext_formatting_data.length;
  246. }
  247. len += getFormulaSize(getFormula1());
  248. len += getFormulaSize(getFormula2());
  249. len += 2 + getFormulaSize(formula_scale);
  250. len += 6 + template_params.length;
  251. byte type = getConditionType();
  252. if (type == CONDITION_TYPE_COLOR_SCALE) {
  253. len += gradient_data.length;
  254. } else if (type == CONDITION_TYPE_DATA_BAR) {
  255. len += databar_data.length;
  256. } else if (type == CONDITION_TYPE_FILTER) {
  257. len += filter_data.length;
  258. } else if (type == CONDITION_TYPE_ICON_SET) {
  259. len += multistate.getDataLength();
  260. }
  261. return len;
  262. }
  263. public String toString() {
  264. StringBuffer buffer = new StringBuffer();
  265. buffer.append("[CFRULE12]\n");
  266. buffer.append(" .condition_type=").append(getConditionType()).append("\n");
  267. buffer.append(" .dxfn12_length =0x").append(Integer.toHexString(ext_formatting_length)).append("\n");
  268. buffer.append(" .option_flags =0x").append(Integer.toHexString(getOptions())).append("\n");
  269. if (containsFontFormattingBlock()) {
  270. buffer.append(_fontFormatting.toString()).append("\n");
  271. }
  272. if (containsBorderFormattingBlock()) {
  273. buffer.append(_borderFormatting.toString()).append("\n");
  274. }
  275. if (containsPatternFormattingBlock()) {
  276. buffer.append(_patternFormatting.toString()).append("\n");
  277. }
  278. buffer.append(" .dxfn12_ext=").append(HexDump.toHex(ext_formatting_data)).append("\n");
  279. buffer.append(" .formula_1 =").append(Arrays.toString(getFormula1().getTokens())).append("\n");
  280. buffer.append(" .formula_2 =").append(Arrays.toString(getFormula2().getTokens())).append("\n");
  281. buffer.append(" .formula_S =").append(Arrays.toString(formula_scale.getTokens())).append("\n");
  282. buffer.append(" .ext_opts =").append(ext_opts).append("\n");
  283. buffer.append(" .priority =").append(priority).append("\n");
  284. buffer.append(" .template_type =").append(template_type).append("\n");
  285. buffer.append(" .template_params=").append(HexDump.toHex(template_params)).append("\n");
  286. buffer.append(" .gradient_data =").append(HexDump.toHex(gradient_data)).append("\n");
  287. buffer.append(" .databar_data =").append(HexDump.toHex(databar_data)).append("\n");
  288. buffer.append(" .filter_data =").append(HexDump.toHex(filter_data)).append("\n");
  289. if (multistate != null) {
  290. buffer.append(multistate);
  291. }
  292. buffer.append("[/CFRULE12]\n");
  293. return buffer.toString();
  294. }
  295. public Object clone() {
  296. CFRule12Record rec = new CFRule12Record(getConditionType(), getComparisonOperation());
  297. rec.futureHeader.setAssociatedRange(futureHeader.getAssociatedRange().copy());
  298. super.copyTo(rec);
  299. rec.ext_formatting_length = ext_formatting_length;
  300. rec.ext_formatting_data = new byte[ext_formatting_length];
  301. System.arraycopy(ext_formatting_data, 0, rec.ext_formatting_data, 0, ext_formatting_length);
  302. rec.formula_scale = formula_scale.copy();
  303. rec.ext_opts = ext_opts;
  304. rec.priority = priority;
  305. rec.template_type = template_type;
  306. rec.template_param_length = template_param_length;
  307. rec.template_params = new byte[template_param_length];
  308. System.arraycopy(template_params, 0, rec.template_params, 0, template_param_length);
  309. // TODO Clone the rgbCT data like Gradients, Databars etc
  310. return rec;
  311. }
  312. public short getFutureRecordType() {
  313. return futureHeader.getRecordType();
  314. }
  315. public FtrHeader getFutureHeader() {
  316. return futureHeader;
  317. }
  318. public CellRangeAddress getAssociatedRange() {
  319. return futureHeader.getAssociatedRange();
  320. }
  321. }