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.

FormulaRecord.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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.poi.ss.formula.Formula;
  19. import org.apache.poi.ss.formula.ptg.Ptg;
  20. import org.apache.poi.ss.usermodel.CellType;
  21. import org.apache.poi.util.BitField;
  22. import org.apache.poi.util.BitFieldFactory;
  23. import org.apache.poi.util.GenericRecordUtil;
  24. import org.apache.poi.util.LittleEndianOutput;
  25. import org.apache.poi.util.Removal;
  26. /**
  27. * Formula Record (0x0006).
  28. */
  29. public final class FormulaRecord extends CellRecord {
  30. // docs say 406...because of a bug Microsoft support site article #Q184647)
  31. public static final short sid = 0x0006;
  32. // double + short + int
  33. private static final int FIXED_SIZE = 14;
  34. private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
  35. private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
  36. private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
  37. private double field_4_value;
  38. private short field_5_options;
  39. /**
  40. * Unused field. As it turns out this field is often not zero..
  41. * According to Microsoft Excel Developer's Kit Page 318:
  42. * when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
  43. */
  44. private int field_6_zero;
  45. private Formula field_8_parsed_expr;
  46. /**
  47. * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
  48. */
  49. private FormulaSpecialCachedValue specialCachedValue;
  50. /** Creates new FormulaRecord */
  51. public FormulaRecord() {
  52. field_8_parsed_expr = Formula.create(Ptg.EMPTY_PTG_ARRAY);
  53. }
  54. public FormulaRecord(FormulaRecord other) {
  55. super(other);
  56. field_4_value = other.field_4_value;
  57. field_5_options = other.field_5_options;
  58. field_6_zero = other.field_6_zero;
  59. field_8_parsed_expr = (other.field_8_parsed_expr == null) ? null : new Formula(other.field_8_parsed_expr);
  60. specialCachedValue = (other.specialCachedValue == null) ? null : new FormulaSpecialCachedValue(other.specialCachedValue);
  61. }
  62. public FormulaRecord(RecordInputStream ris) {
  63. super(ris);
  64. long valueLongBits = ris.readLong();
  65. field_5_options = ris.readShort();
  66. specialCachedValue = FormulaSpecialCachedValue.create(valueLongBits);
  67. if (specialCachedValue == null) {
  68. field_4_value = Double.longBitsToDouble(valueLongBits);
  69. }
  70. field_6_zero = ris.readInt();
  71. int field_7_expression_len = ris.readShort(); // this length does not include any extra array data
  72. int nBytesAvailable = ris.available();
  73. field_8_parsed_expr = Formula.read(field_7_expression_len, ris, nBytesAvailable);
  74. }
  75. /**
  76. * set the calculated value of the formula
  77. *
  78. * @param value calculated value
  79. */
  80. public void setValue(double value) {
  81. field_4_value = value;
  82. specialCachedValue = null;
  83. }
  84. public void setCachedResultTypeEmptyString() {
  85. specialCachedValue = FormulaSpecialCachedValue.createCachedEmptyValue();
  86. }
  87. public void setCachedResultTypeString() {
  88. specialCachedValue = FormulaSpecialCachedValue.createForString();
  89. }
  90. public void setCachedResultErrorCode(int errorCode) {
  91. specialCachedValue = FormulaSpecialCachedValue.createCachedErrorCode(errorCode);
  92. }
  93. public void setCachedResultBoolean(boolean value) {
  94. specialCachedValue = FormulaSpecialCachedValue.createCachedBoolean(value);
  95. }
  96. /**
  97. * @return <code>true</code> if this {@link FormulaRecord} is followed by a
  98. * {@link StringRecord} representing the cached text result of the formula
  99. * evaluation.
  100. */
  101. public boolean hasCachedResultString() {
  102. return specialCachedValue != null &&
  103. specialCachedValue.getTypeCode() == FormulaSpecialCachedValue.STRING;
  104. }
  105. /**
  106. * @deprecated POI 5.0.0, will be removed in 6.0, use getCachedResultTypeEnum until switch to enum is fully done
  107. */
  108. @Deprecated
  109. @Removal(version = "6.0.0")
  110. public int getCachedResultType() {
  111. if (specialCachedValue == null) {
  112. return CellType.NUMERIC.getCode();
  113. }
  114. return specialCachedValue.getValueType();
  115. }
  116. /**
  117. * Returns the type of the cached result
  118. * @return A CellType
  119. * @since POI 5.0.0
  120. */
  121. public CellType getCachedResultTypeEnum() {
  122. if (specialCachedValue == null) {
  123. return CellType.NUMERIC;
  124. }
  125. return specialCachedValue.getValueTypeEnum();
  126. }
  127. public boolean getCachedBooleanValue() {
  128. return specialCachedValue.getBooleanValue();
  129. }
  130. public int getCachedErrorValue() {
  131. return specialCachedValue.getErrorValue();
  132. }
  133. /**
  134. * set the option flags
  135. *
  136. * @param options bitmask
  137. */
  138. public void setOptions(short options) {
  139. field_5_options = options;
  140. }
  141. /**
  142. * get the calculated value of the formula
  143. *
  144. * @return calculated value
  145. */
  146. public double getValue() {
  147. return field_4_value;
  148. }
  149. /**
  150. * get the option flags
  151. *
  152. * @return bitmask
  153. */
  154. public short getOptions() {
  155. return field_5_options;
  156. }
  157. public boolean isSharedFormula() {
  158. return sharedFormula.isSet(field_5_options);
  159. }
  160. public void setSharedFormula(boolean flag) {
  161. field_5_options =
  162. sharedFormula.setShortBoolean(field_5_options, flag);
  163. }
  164. public boolean isAlwaysCalc() {
  165. return alwaysCalc.isSet(field_5_options);
  166. }
  167. public void setAlwaysCalc(boolean flag) {
  168. field_5_options =
  169. alwaysCalc.setShortBoolean(field_5_options, flag);
  170. }
  171. public boolean isCalcOnLoad() {
  172. return calcOnLoad.isSet(field_5_options);
  173. }
  174. public void setCalcOnLoad(boolean flag) {
  175. field_5_options =
  176. calcOnLoad.setShortBoolean(field_5_options, flag);
  177. }
  178. /**
  179. * @return the formula tokens. never <code>null</code>
  180. */
  181. public Ptg[] getParsedExpression() {
  182. return field_8_parsed_expr.getTokens();
  183. }
  184. public Formula getFormula() {
  185. return field_8_parsed_expr;
  186. }
  187. public void setParsedExpression(Ptg[] ptgs) {
  188. field_8_parsed_expr = Formula.create(ptgs);
  189. }
  190. @Override
  191. public short getSid() {
  192. return sid;
  193. }
  194. @Override
  195. protected int getValueDataSize() {
  196. return FIXED_SIZE + field_8_parsed_expr.getEncodedSize();
  197. }
  198. @Override
  199. protected void serializeValue(LittleEndianOutput out) {
  200. if (specialCachedValue == null) {
  201. out.writeDouble(field_4_value);
  202. } else {
  203. specialCachedValue.serialize(out);
  204. }
  205. out.writeShort(getOptions());
  206. out.writeInt(field_6_zero); // may as well write original data back so as to minimise differences from original
  207. field_8_parsed_expr.serialize(out);
  208. }
  209. @Override
  210. protected String getRecordName() {
  211. return "FORMULA";
  212. }
  213. @Override
  214. public FormulaRecord copy() {
  215. return new FormulaRecord(this);
  216. }
  217. @Override
  218. public HSSFRecordTypes getGenericRecordType() {
  219. return HSSFRecordTypes.FORMULA;
  220. }
  221. @Override
  222. public Map<String, Supplier<?>> getGenericProperties() {
  223. return GenericRecordUtil.getGenericProperties(
  224. "base", super::getGenericProperties,
  225. "options", this::getOptions,
  226. "alwaysCalc", this::isAlwaysCalc,
  227. "calcOnLoad", this::isCalcOnLoad,
  228. "shared", this::isSharedFormula,
  229. "zero", () -> field_6_zero,
  230. "value", () -> specialCachedValue == null ? field_4_value : specialCachedValue,
  231. "formula", this::getFormula
  232. );
  233. }
  234. }