| /* ==================================================================== |
| Copyright 2002-2004 Apache Software Foundation |
| |
| Licensed 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 org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats; |
| import org.apache.poi.hssf.record.formula.Ptg; |
| import org.apache.poi.hssf.usermodel.DVConstraint; |
| import org.apache.poi.hssf.usermodel.HSSFDataValidation; |
| import org.apache.poi.hssf.util.CellRangeAddress; |
| import org.apache.poi.hssf.util.CellRangeAddressList; |
| import org.apache.poi.util.BitField; |
| import org.apache.poi.util.LittleEndian; |
| |
| /** |
| * Title: DATAVALIDATION Record (0x01BE)<p/> |
| * Description: 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) |
| * @author Dragos Buleandra (dragos.buleandra@trade2b.ro) |
| * @author Josh Micich |
| */ |
| public final class DVRecord extends Record { |
| public final static 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 */ |
| private int _option_flags; |
| /** Title of the prompt box */ |
| private UnicodeString _promptTitle; |
| /** Title of the error box */ |
| private UnicodeString _errorTitle; |
| /** Text of the prompt box */ |
| private UnicodeString _promptText; |
| /** Text of the error box */ |
| private 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 Ptg[] _formula1; |
| /** Not used - Excel seems to always write 0x0000 */ |
| private short _not_used_2 = 0x0000; |
| /** Formula data for second condition (RPN token array without size field) */ |
| private Ptg[] _formula2; |
| /** Cell range address list with all affected ranges */ |
| private CellRangeAddressList _regions; |
| |
| /** |
| * Option flags field |
| * |
| * @see org.apache.poi.hssf.util.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); |
| |
| 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) { |
| |
| 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 = formula1; |
| _formula2 = 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(); |
| |
| //read first formula data condition |
| _formula1 = Ptg.readTokens(field_size_first_formula, in); |
| |
| int field_size_sec_formula = in.readUShort(); |
| _not_used_2 = in.readShort(); |
| |
| //read sec formula data condition |
| _formula2 = Ptg.readTokens(field_size_sec_formula, in); |
| |
| //read cell range address list with all affected ranges |
| _regions = new CellRangeAddressList(in); |
| } |
| |
| // --> start option flags |
| /** |
| * @return the condition data type |
| * @see DVConstraint.ValidationType |
| */ |
| public int getDataType() { |
| return opt_data_type.getValue(_option_flags); |
| } |
| |
| /** |
| * @return the condition error style |
| * @see HSSFDataValidation.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 org.apache.poi.hssf.util.HSSFDataValidation utility class |
| */ |
| public int getConditionOperator() { |
| return opt_condition_operator.getValue(_option_flags); |
| } |
| // <-- end option flags |
| |
| |
| |
| |
| public CellRangeAddressList getCellRangeAddress() { |
| return this._regions; |
| } |
| |
| |
| public String toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("[DV]\n"); |
| sb.append(" options=").append(Integer.toHexString(_option_flags)); |
| sb.append(" title-prompt=").append(formatTextTitle(_promptTitle)); |
| sb.append(" title-error=").append(formatTextTitle(_errorTitle)); |
| sb.append(" text-prompt=").append(formatTextTitle(_promptText)); |
| sb.append(" text-error=").append(formatTextTitle(_errorText)); |
| sb.append("\n"); |
| appendFormula(sb, "Formula 1:", _formula1); |
| appendFormula(sb, "Formula 2:", _formula2); |
| sb.append("Regions: "); |
| int nRegions = _regions.countRanges(); |
| for(int i=0; i<nRegions; i++) { |
| if (i>0) { |
| sb.append(", "); |
| } |
| CellRangeAddress addr = _regions.getCellRangeAddress(i); |
| sb.append('(').append(addr.getFirstRow()).append(',').append(addr.getLastRow()); |
| sb.append(',').append(addr.getFirstColumn()).append(',').append(addr.getLastColumn()).append(')'); |
| } |
| sb.append("\n"); |
| sb.append("[/DV]"); |
| |
| return sb.toString(); |
| } |
| |
| private static String formatTextTitle(UnicodeString us) { |
| String str = us.getString(); |
| if (str.length() == 1 && str.charAt(0) == '\0') { |
| return "'\\0'"; |
| } |
| return str; |
| } |
| |
| private void appendFormula(StringBuffer sb, String label, Ptg[] ptgs) { |
| sb.append(label); |
| if (ptgs.length < 1) { |
| sb.append("<empty>\n"); |
| return; |
| } |
| sb.append("\n"); |
| for (int i = 0; i < ptgs.length; i++) { |
| sb.append('\t').append(ptgs[i].toString()).append('\n'); |
| } |
| } |
| |
| public int serialize(int offset, byte [] data) { |
| int size = this.getRecordSize(); |
| LittleEndian.putShort(data, 0 + offset, sid); |
| LittleEndian.putShort(data, 2 + offset, ( short ) (size-4)); |
| |
| int pos = 4; |
| LittleEndian.putInt(data, pos + offset, _option_flags); |
| pos += 4; |
| |
| pos += serializeUnicodeString(_promptTitle, pos+offset, data); |
| pos += serializeUnicodeString(_errorTitle, pos+offset, data); |
| pos += serializeUnicodeString(_promptText, pos+offset, data); |
| pos += serializeUnicodeString(_errorText, pos+offset, data); |
| LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula1)); |
| pos += 2; |
| LittleEndian.putUShort(data, offset+pos, _not_used_1); |
| pos += 2; |
| |
| pos += Ptg.serializePtgs(_formula1, data, pos+offset); |
| |
| LittleEndian.putUShort(data, offset+pos, Ptg.getEncodedSize(_formula2)); |
| pos += 2; |
| LittleEndian.putShort(data, offset+pos, _not_used_2); |
| pos += 2; |
| pos += Ptg.serializePtgs(_formula2, data, pos+offset); |
| _regions.serialize(pos+offset, data); |
| return size; |
| } |
| |
| /** |
| * 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 UnicodeString readUnicodeString(RecordInputStream in) { |
| return new UnicodeString(in); |
| } |
| |
| private static int serializeUnicodeString(UnicodeString us, int offset, byte[] data) { |
| UnicodeRecordStats urs = new UnicodeRecordStats(); |
| us.serialize(urs, offset, data); |
| return urs.recordSize; |
| } |
| private static int getUnicodeStringSize(UnicodeString str) { |
| return 3 + str.getString().length(); |
| } |
| |
| public int getRecordSize() { |
| int size = 4+4+2+2+2+2;//header+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 += Ptg.getEncodedSize(_formula1); |
| size += Ptg.getEncodedSize(_formula2); |
| size += _regions.getSize(); |
| return size; |
| } |
| |
| public short getSid() { |
| return sid; |
| } |
| |
| /** |
| * Clones the object. Uses serialisation, as the |
| * contents are somewhat complex |
| */ |
| public Object clone() { |
| return cloneViaReserialise(); |
| } |
| } |