123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- package org.apache.poi.hssf.record;
-
- import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
-
- import java.util.Map;
- import java.util.function.Supplier;
-
- import org.apache.poi.hssf.record.common.UnicodeString;
- import org.apache.poi.hssf.usermodel.HSSFDataValidation;
- import org.apache.poi.ss.formula.Formula;
- import org.apache.poi.ss.formula.ptg.Ptg;
- import org.apache.poi.ss.util.CellRangeAddressList;
- import org.apache.poi.util.BitField;
- import org.apache.poi.util.GenericRecordUtil;
- import org.apache.poi.util.LittleEndianOutput;
- import org.apache.poi.util.StringUtil;
-
- /**
- * This record stores data validation settings and a list of cell ranges which contain these settings.
- * The data validation settings of a sheet are stored in a sequential list of DV records.
- * This list is followed by DVAL record(s)
- */
- public final class DVRecord extends StandardRecord {
- public static final short sid = 0x01BE;
-
- /** the unicode string used for error/prompt title/text when not present */
- private static final UnicodeString NULL_TEXT_STRING = new UnicodeString("\0");
-
- /**
- * Option flags field
- *
- * @see HSSFDataValidation utility class
- */
- private static final BitField opt_data_type = new BitField(0x0000000F);
- private static final BitField opt_error_style = new BitField(0x00000070);
- private static final BitField opt_string_list_formula = new BitField(0x00000080);
- private static final BitField opt_empty_cell_allowed = new BitField(0x00000100);
- private static final BitField opt_suppress_dropdown_arrow = new BitField(0x00000200);
- private static final BitField opt_show_prompt_on_cell_selected = new BitField(0x00040000);
- private static final BitField opt_show_error_on_invalid_value = new BitField(0x00080000);
- private static final BitField opt_condition_operator = new BitField(0x00700000);
-
- private static final int[] FLAG_MASKS = { 0x0000000F,0x00000070,0x00000080,0x00000100,
- 0x00000200,0x00040000,0x00080000,0x00700000 };
-
- private static final String[] FLAG_NAMES = { "DATA_TYPE", "ERROR_STYLE", "STRING_LIST_FORMULA",
- "EMPTY_CELL_ALLOWED", "SUPPRESS_DROPDOWN_ARROW", "SHOW_PROMPT_ON_CELL_SELECTED",
- "SHOW_ERROR_ON_INVALID_VALUE", "CONDITION_OPERATOR" };
-
-
-
- /** Option flags */
- private int _option_flags;
- /** Title of the prompt box, cannot be longer than 32 chars */
- private final UnicodeString _promptTitle;
- /** Title of the error box, cannot be longer than 32 chars */
- private final UnicodeString _errorTitle;
- /** Text of the prompt box, cannot be longer than 255 chars */
- private final UnicodeString _promptText;
- /** Text of the error box, cannot be longer than 255 chars */
- private final UnicodeString _errorText;
- /** Not used - Excel seems to always write 0x3FE0 */
- private short _not_used_1 = 0x3FE0;
- /** Formula data for first condition (RPN token array without size field) */
- private final Formula _formula1;
- /** Not used - Excel seems to always write 0x0000 */
- @SuppressWarnings("RedundantFieldInitialization")
- private short _not_used_2 = 0x0000;
- /** Formula data for second condition (RPN token array without size field) */
- private final Formula _formula2;
- /** Cell range address list with all affected ranges */
- private final CellRangeAddressList _regions;
-
- public DVRecord(DVRecord other) {
- super(other);
- _option_flags = other._option_flags;
- _promptTitle = other._promptTitle.copy();
- _errorTitle = other._errorTitle.copy();
- _promptText = other._promptText.copy();
- _errorText = other._errorText.copy();
- _not_used_1 = other._not_used_1;
- _formula1 = (other._formula1 == null) ? null : other._formula1.copy();
- _not_used_2 = other._not_used_2;
- _formula2 = (other._formula2 == null) ? null : other._formula2.copy();
- _regions = (other._regions == null) ? null : other._regions.copy();
- }
-
- public DVRecord(int validationType, int operator, int errorStyle, boolean emptyCellAllowed,
- boolean suppressDropDownArrow, boolean isExplicitList,
- boolean showPromptBox, String promptTitle, String promptText,
- boolean showErrorBox, String errorTitle, String errorText,
- Ptg[] formula1, Ptg[] formula2,
- CellRangeAddressList regions) {
-
- // check length-limits
- if(promptTitle != null && promptTitle.length() > 32) {
- throw new IllegalStateException("Prompt-title cannot be longer than 32 characters, but had: " + promptTitle);
- }
- if(promptText != null && promptText.length() > 255) {
- throw new IllegalStateException("Prompt-text cannot be longer than 255 characters, but had: " + promptText);
- }
-
- if(errorTitle != null && errorTitle.length() > 32) {
- throw new IllegalStateException("Error-title cannot be longer than 32 characters, but had: " + errorTitle);
- }
- if(errorText != null && errorText.length() > 255) {
- throw new IllegalStateException("Error-text cannot be longer than 255 characters, but had: " + errorText);
- }
-
- int flags = 0;
- flags = opt_data_type.setValue(flags, validationType);
- flags = opt_condition_operator.setValue(flags, operator);
- flags = opt_error_style.setValue(flags, errorStyle);
- flags = opt_empty_cell_allowed.setBoolean(flags, emptyCellAllowed);
- flags = opt_suppress_dropdown_arrow.setBoolean(flags, suppressDropDownArrow);
- flags = opt_string_list_formula.setBoolean(flags, isExplicitList);
- flags = opt_show_prompt_on_cell_selected.setBoolean(flags, showPromptBox);
- flags = opt_show_error_on_invalid_value.setBoolean(flags, showErrorBox);
- _option_flags = flags;
- _promptTitle = resolveTitleText(promptTitle);
- _promptText = resolveTitleText(promptText);
- _errorTitle = resolveTitleText(errorTitle);
- _errorText = resolveTitleText(errorText);
- _formula1 = Formula.create(formula1);
- _formula2 = Formula.create(formula2);
- _regions = regions;
- }
-
- public DVRecord(RecordInputStream in) {
- _option_flags = in.readInt();
-
- _promptTitle = readUnicodeString(in);
- _errorTitle = readUnicodeString(in);
- _promptText = readUnicodeString(in);
- _errorText = readUnicodeString(in);
-
- int field_size_first_formula = in.readUShort();
- _not_used_1 = in.readShort();
-
- // "You may not use unions, intersections or array constants in Data Validation criteria"
-
- // read first formula data condition
- _formula1 = Formula.read(field_size_first_formula, in);
-
- int field_size_sec_formula = in.readUShort();
- _not_used_2 = in.readShort();
-
- // read sec formula data condition
- _formula2 = Formula.read(field_size_sec_formula, in);
-
- // read cell range address list with all affected ranges
- _regions = new CellRangeAddressList(in);
- }
-
- /**
- * @return the condition data type
- * @see org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType
- */
- public int getDataType() {
- return opt_data_type.getValue(_option_flags);
- }
-
- /**
- * @return the condition error style
- * @see org.apache.poi.ss.usermodel.DataValidation.ErrorStyle
- */
- public int getErrorStyle() {
- return opt_error_style.getValue(_option_flags);
- }
-
- /**
- * @return <code>true</code> if in list validations the string list is explicitly given in the
- * formula, <code>false</code> otherwise
- */
- public boolean getListExplicitFormula() {
- return (opt_string_list_formula.isSet(_option_flags));
- }
-
- /**
- * @return <code>true</code> if empty values are allowed in cells, <code>false</code> otherwise
- */
- public boolean getEmptyCellAllowed() {
- return (opt_empty_cell_allowed.isSet(_option_flags));
- }
-
-
- /**
- * @return <code>true</code> if drop down arrow should be suppressed when list validation is
- * used, <code>false</code> otherwise
- */
- public boolean getSuppressDropdownArrow() {
- return (opt_suppress_dropdown_arrow.isSet(_option_flags));
- }
-
- /**
- * @return <code>true</code> if a prompt window should appear when cell is selected, <code>false</code> otherwise
- */
- public boolean getShowPromptOnCellSelected() {
- return (opt_show_prompt_on_cell_selected.isSet(_option_flags));
- }
-
- /**
- * @return <code>true</code> if an error window should appear when an invalid value is entered
- * in the cell, <code>false</code> otherwise
- */
- public boolean getShowErrorOnInvalidValue() {
- return (opt_show_error_on_invalid_value.isSet(_option_flags));
- }
-
- /**
- * get the condition operator
- * @return the condition operator
- * @see HSSFDataValidation utility class
- */
- public int getConditionOperator() {
- return opt_condition_operator.getValue(_option_flags);
- }
- // <-- end option flags
-
- public String getPromptTitle() {
- return resolveTitleString(_promptTitle);
- }
-
- public String getErrorTitle() {
- return resolveTitleString(_errorTitle);
- }
-
- public String getPromptText() {
- return resolveTitleString(_promptText);
- }
-
- public String getErrorText() {
- return resolveTitleString(_errorText);
- }
-
- public Ptg[] getFormula1() {
- return Formula.getTokens(_formula1);
- }
-
- public Ptg[] getFormula2() {
- return Formula.getTokens(_formula2);
- }
-
- public CellRangeAddressList getCellRangeAddress() {
- return this._regions;
- }
-
-
- public void serialize(LittleEndianOutput out) {
-
- out.writeInt(_option_flags);
-
- serializeUnicodeString(_promptTitle, out);
- serializeUnicodeString(_errorTitle, out);
- serializeUnicodeString(_promptText, out);
- serializeUnicodeString(_errorText, out);
- out.writeShort(_formula1.getEncodedTokenSize());
- out.writeShort(_not_used_1);
- _formula1.serializeTokens(out);
-
- out.writeShort(_formula2.getEncodedTokenSize());
- out.writeShort(_not_used_2);
- _formula2.serializeTokens(out);
-
- _regions.serialize(out);
- }
-
- /**
- * When entered via the UI, Excel translates empty string into "\0"
- * While it is possible to encode the title/text as empty string (Excel doesn't exactly crash),
- * the resulting tool-tip text / message box looks wrong. It is best to do the same as the
- * Excel UI and encode 'not present' as "\0".
- */
- private static UnicodeString resolveTitleText(String str) {
- if (str == null || str.length() < 1) {
- return NULL_TEXT_STRING;
- }
- return new UnicodeString(str);
- }
-
- private static String resolveTitleString(UnicodeString us) {
- if (us == null || us.equals(NULL_TEXT_STRING)) {
- return null;
- }
- return us.getString();
- }
-
- private static UnicodeString readUnicodeString(RecordInputStream in) {
- return new UnicodeString(in);
- }
-
- private static void serializeUnicodeString(UnicodeString us, LittleEndianOutput out) {
- StringUtil.writeUnicodeString(out, us.getString());
- }
- private static int getUnicodeStringSize(UnicodeString us) {
- String str = us.getString();
- return 3 + str.length() * (StringUtil.hasMultibyte(str) ? 2 : 1);
- }
-
- protected int getDataSize() {
- int size = 4+2+2+2+2;//options_field+first_formula_size+first_unused+sec_formula_size+sec+unused;
- size += getUnicodeStringSize(_promptTitle);
- size += getUnicodeStringSize(_errorTitle);
- size += getUnicodeStringSize(_promptText);
- size += getUnicodeStringSize(_errorText);
- size += _formula1.getEncodedTokenSize();
- size += _formula2.getEncodedTokenSize();
- size += _regions.getSize();
- return size;
- }
-
- public short getSid() {
- return sid;
- }
-
- /** Clones the object. */
- @Override
- public DVRecord copy() {
- return new DVRecord(this);
- }
-
- @Override
- public HSSFRecordTypes getGenericRecordType() {
- return HSSFRecordTypes.DV;
- }
-
- @Override
- public Map<String, Supplier<?>> getGenericProperties() {
- return GenericRecordUtil.getGenericProperties(
- "optionFlags", getBitsAsString(() -> _option_flags, FLAG_MASKS, FLAG_NAMES),
- "promptTitle", this::getPromptTitle,
- "errorTitle", this::getErrorTitle,
- "promptText", this::getPromptText,
- "errorText", this::getErrorText,
- "formula1", this::getFormula1,
- "formula2", this::getFormula2,
- "regions", () -> _regions
- );
- }
- }
|