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.

LbsDataSubRecord.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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 org.apache.poi.common.Duplicatable;
  17. import org.apache.poi.ss.formula.ptg.Ptg;
  18. import org.apache.poi.util.HexDump;
  19. import org.apache.poi.util.LittleEndianInput;
  20. import org.apache.poi.util.LittleEndianOutput;
  21. import org.apache.poi.util.RecordFormatException;
  22. import org.apache.poi.util.Removal;
  23. import org.apache.poi.util.StringUtil;
  24. /**
  25. * This structure specifies the properties of a list or drop-down list embedded object in a sheet.
  26. */
  27. public class LbsDataSubRecord extends SubRecord {
  28. public static final int sid = 0x0013;
  29. /**
  30. * From [MS-XLS].pdf 2.5.147 FtLbsData:
  31. *
  32. * An unsigned integer that indirectly specifies whether
  33. * some of the data in this structure appear in a subsequent Continue record.
  34. * If _cbFContinued is 0x00, all of the fields in this structure except sid and _cbFContinued
  35. * MUST NOT exist. If this entire structure is contained within the same record,
  36. * then _cbFContinued MUST be greater than or equal to the size, in bytes,
  37. * of this structure, not including the four bytes for the ft and _cbFContinued fields
  38. */
  39. private int _cbFContinued;
  40. /**
  41. * a formula that specifies the range of cell values that are the items in this list.
  42. */
  43. private int _unknownPreFormulaInt;
  44. private Ptg _linkPtg;
  45. private Byte _unknownPostFormulaByte;
  46. /**
  47. * An unsigned integer that specifies the number of items in the list.
  48. */
  49. private int _cLines;
  50. /**
  51. * An unsigned integer that specifies the one-based index of the first selected item in this list.
  52. * A value of 0x00 specifies there is no currently selected item.
  53. */
  54. private int _iSel;
  55. /**
  56. * flags that tell what data follows
  57. */
  58. private int _flags;
  59. /**
  60. * An ObjId that specifies the edit box associated with this list.
  61. * A value of 0x00 specifies that there is no edit box associated with this list.
  62. */
  63. private int _idEdit;
  64. /**
  65. * An optional LbsDropData that specifies properties for this dropdown control.
  66. * This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
  67. */
  68. private LbsDropData _dropData;
  69. /**
  70. * An optional array of strings where each string specifies an item in the list.
  71. * The number of elements in this array, if it exists, MUST be {@link #_cLines}
  72. */
  73. private String[] _rgLines;
  74. /**
  75. * An optional array of booleans that specifies
  76. * which items in the list are part of a multiple selection
  77. */
  78. private boolean[] _bsels;
  79. LbsDataSubRecord() {}
  80. public LbsDataSubRecord(LbsDataSubRecord other) {
  81. super(other);
  82. _cbFContinued = other._cbFContinued;
  83. _unknownPreFormulaInt = other._unknownPreFormulaInt;
  84. _linkPtg = (other._linkPtg == null) ? null : other._linkPtg.copy();
  85. _unknownPostFormulaByte = other._unknownPostFormulaByte;
  86. _cLines = other._cLines;
  87. _iSel = other._iSel;
  88. _flags = other._flags;
  89. _idEdit = other._idEdit;
  90. _dropData = (other._dropData == null) ? null : other._dropData.copy();
  91. _rgLines = (other._rgLines == null) ? null : other._rgLines.clone();
  92. _bsels = (other._bsels == null) ? null : other._bsels.clone();
  93. }
  94. /**
  95. * @param in the stream to read data from
  96. * @param cbFContinued the seconf short in the record header
  97. * @param cmoOt the containing Obj's {@link CommonObjectDataSubRecord#field_1_objectType}
  98. */
  99. public LbsDataSubRecord(LittleEndianInput in, int cbFContinued, int cmoOt) {
  100. _cbFContinued = cbFContinued;
  101. int encodedTokenLen = in.readUShort();
  102. if (encodedTokenLen > 0) {
  103. int formulaSize = in.readUShort();
  104. _unknownPreFormulaInt = in.readInt();
  105. Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
  106. if (ptgs.length != 1) {
  107. throw new RecordFormatException("Read " + ptgs.length
  108. + " tokens but expected exactly 1");
  109. }
  110. _linkPtg = ptgs[0];
  111. switch (encodedTokenLen - formulaSize - 6) {
  112. case 1:
  113. _unknownPostFormulaByte = in.readByte();
  114. break;
  115. case 0:
  116. _unknownPostFormulaByte = null;
  117. break;
  118. default:
  119. throw new RecordFormatException("Unexpected leftover bytes");
  120. }
  121. }
  122. _cLines = in.readUShort();
  123. _iSel = in.readUShort();
  124. _flags = in.readUShort();
  125. _idEdit = in.readUShort();
  126. // From [MS-XLS].pdf 2.5.147 FtLbsData:
  127. // This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
  128. if(cmoOt == 0x14) {
  129. _dropData = new LbsDropData(in);
  130. }
  131. // From [MS-XLS].pdf 2.5.147 FtLbsData:
  132. // This array MUST exist if and only if the fValidPlex flag (0x2) is set
  133. if((_flags & 0x2) != 0) {
  134. _rgLines = new String[_cLines];
  135. for(int i=0; i < _cLines; i++) {
  136. _rgLines[i] = StringUtil.readUnicodeString(in);
  137. }
  138. }
  139. // bits 5-6 in the _flags specify the type
  140. // of selection behavior this list control is expected to support
  141. // From [MS-XLS].pdf 2.5.147 FtLbsData:
  142. // This array MUST exist if and only if the wListType field is not equal to 0.
  143. if(((_flags >> 4) & 0x1) + (_flags >> 5 & 0x1) != 0) {
  144. _bsels = new boolean[_cLines];
  145. for(int i=0; i < _cLines; i++) {
  146. _bsels[i] = in.readByte() == 1;
  147. }
  148. }
  149. }
  150. /**
  151. *
  152. * @return a new instance of LbsDataSubRecord to construct auto-filters
  153. * @see org.apache.poi.hssf.usermodel.HSSFCombobox
  154. */
  155. public static LbsDataSubRecord newAutoFilterInstance(){
  156. LbsDataSubRecord lbs = new LbsDataSubRecord();
  157. lbs._cbFContinued = 0x1FEE; //autofilters seem to alway have this magic number
  158. lbs._iSel = 0x000;
  159. lbs._flags = 0x0301;
  160. lbs._dropData = new LbsDropData();
  161. lbs._dropData._wStyle = LbsDropData.STYLE_COMBO_SIMPLE_DROPDOWN;
  162. // the number of lines to be displayed in the dropdown
  163. lbs._dropData._cLine = 8;
  164. return lbs;
  165. }
  166. /**
  167. * @return true as LbsDataSubRecord is always the last sub-record
  168. */
  169. @Override
  170. public boolean isTerminating(){
  171. return true;
  172. }
  173. @Override
  174. protected int getDataSize() {
  175. int result = 2; // 2 initial shorts
  176. // optional link formula
  177. if (_linkPtg != null) {
  178. result += 2; // encoded Ptg size
  179. result += 4; // unknown int
  180. result += _linkPtg.getSize();
  181. if (_unknownPostFormulaByte != null) {
  182. result += 1;
  183. }
  184. }
  185. result += 4 * 2; // 4 shorts
  186. if(_dropData != null) {
  187. result += _dropData.getDataSize();
  188. }
  189. if(_rgLines != null) {
  190. for(String str : _rgLines){
  191. result += StringUtil.getEncodedSize(str);
  192. }
  193. }
  194. if(_bsels != null) {
  195. result += _bsels.length;
  196. }
  197. return result;
  198. }
  199. @Override
  200. public void serialize(LittleEndianOutput out) {
  201. out.writeShort(sid);
  202. out.writeShort(_cbFContinued);
  203. if (_linkPtg == null) {
  204. out.writeShort(0);
  205. } else {
  206. int formulaSize = _linkPtg.getSize();
  207. int linkSize = formulaSize + 6;
  208. if (_unknownPostFormulaByte != null) {
  209. linkSize++;
  210. }
  211. out.writeShort(linkSize);
  212. out.writeShort(formulaSize);
  213. out.writeInt(_unknownPreFormulaInt);
  214. _linkPtg.write(out);
  215. if (_unknownPostFormulaByte != null) {
  216. out.writeByte(_unknownPostFormulaByte.intValue());
  217. }
  218. }
  219. out.writeShort(_cLines);
  220. out.writeShort(_iSel);
  221. out.writeShort(_flags);
  222. out.writeShort(_idEdit);
  223. if(_dropData != null) {
  224. _dropData.serialize(out);
  225. }
  226. if(_rgLines != null) {
  227. for(String str : _rgLines){
  228. StringUtil.writeUnicodeString(out, str);
  229. }
  230. }
  231. if(_bsels != null) {
  232. for(boolean val : _bsels){
  233. out.writeByte(val ? 1 : 0);
  234. }
  235. }
  236. }
  237. @Override
  238. @SuppressWarnings("squid:S2975")
  239. @Deprecated
  240. @Removal(version = "5.0.0")
  241. public LbsDataSubRecord clone() {
  242. return copy();
  243. }
  244. @Override
  245. public LbsDataSubRecord copy() {
  246. return new LbsDataSubRecord(this);
  247. }
  248. @Override
  249. public String toString() {
  250. StringBuilder sb = new StringBuilder(256);
  251. sb.append("[ftLbsData]\n");
  252. sb.append(" .unknownShort1 =").append(HexDump.shortToHex(_cbFContinued)).append("\n");
  253. sb.append(" .formula = ").append('\n');
  254. if(_linkPtg != null) {
  255. sb.append(_linkPtg).append(_linkPtg.getRVAType()).append('\n');
  256. }
  257. sb.append(" .nEntryCount =").append(HexDump.shortToHex(_cLines)).append("\n");
  258. sb.append(" .selEntryIx =").append(HexDump.shortToHex(_iSel)).append("\n");
  259. sb.append(" .style =").append(HexDump.shortToHex(_flags)).append("\n");
  260. sb.append(" .unknownShort10=").append(HexDump.shortToHex(_idEdit)).append("\n");
  261. if(_dropData != null) {
  262. sb.append('\n').append(_dropData);
  263. }
  264. sb.append("[/ftLbsData]\n");
  265. return sb.toString();
  266. }
  267. /**
  268. *
  269. * @return the formula that specifies the range of cell values that are the items in this list.
  270. */
  271. public Ptg getFormula(){
  272. return _linkPtg;
  273. }
  274. /**
  275. * @return the number of items in the list
  276. */
  277. public int getNumberOfItems(){
  278. return _cLines;
  279. }
  280. /**
  281. * This structure specifies properties of the dropdown list control
  282. */
  283. public static class LbsDropData implements Duplicatable {
  284. /**
  285. * Combo dropdown control
  286. */
  287. public static final int STYLE_COMBO_DROPDOWN = 0;
  288. /**
  289. * Combo Edit dropdown control
  290. */
  291. public static final int STYLE_COMBO_EDIT_DROPDOWN = 1;
  292. /**
  293. * Simple dropdown control (just the dropdown button)
  294. */
  295. public static final int STYLE_COMBO_SIMPLE_DROPDOWN = 2;
  296. /**
  297. * An unsigned integer that specifies the style of this dropdown.
  298. */
  299. private int _wStyle;
  300. /**
  301. * An unsigned integer that specifies the number of lines to be displayed in the dropdown.
  302. */
  303. private int _cLine;
  304. /**
  305. * An unsigned integer that specifies the smallest width in pixels allowed for the dropdown window
  306. */
  307. private int _dxMin;
  308. /**
  309. * a string that specifies the current string value in the dropdown
  310. */
  311. private final String _str;
  312. /**
  313. * Optional, undefined and MUST be ignored.
  314. * This field MUST exist if and only if the size of str in bytes is an odd number
  315. */
  316. private Byte _unused;
  317. public LbsDropData() {
  318. _str = "";
  319. _unused = 0;
  320. }
  321. public LbsDropData(LbsDropData other) {
  322. _wStyle = other._wStyle;
  323. _cLine = other._cLine;
  324. _dxMin = other._dxMin;
  325. _str = other._str;
  326. _unused = other._unused;
  327. }
  328. public LbsDropData(LittleEndianInput in) {
  329. _wStyle = in.readUShort();
  330. _cLine = in.readUShort();
  331. _dxMin = in.readUShort();
  332. _str = StringUtil.readUnicodeString(in);
  333. if(StringUtil.getEncodedSize(_str) % 2 != 0){
  334. _unused = in.readByte();
  335. }
  336. }
  337. /**
  338. * Set the style of this dropdown.<p>
  339. *
  340. * Possible values:
  341. * <ul>
  342. * <li>0: Combo dropdown control</li>
  343. * <li>1: Combo Edit dropdown control</li>
  344. * <li>2: Simple dropdown control (just the dropdown button)</li>
  345. * </ul>
  346. *
  347. * @param style the style - see possible values
  348. */
  349. public void setStyle(int style){
  350. _wStyle = style;
  351. }
  352. /**
  353. * Set the number of lines to be displayed in the dropdown.
  354. *
  355. * @param num the number of lines to be displayed in the dropdown
  356. */
  357. public void setNumLines(int num){
  358. _cLine = num;
  359. }
  360. public void serialize(LittleEndianOutput out) {
  361. out.writeShort(_wStyle);
  362. out.writeShort(_cLine);
  363. out.writeShort(_dxMin);
  364. StringUtil.writeUnicodeString(out, _str);
  365. if(_unused != null) {
  366. out.writeByte(_unused);
  367. }
  368. }
  369. public int getDataSize() {
  370. int size = 6;
  371. size += StringUtil.getEncodedSize(_str);
  372. if(_unused != null) {
  373. size++;
  374. }
  375. return size;
  376. }
  377. @Override
  378. public String toString(){
  379. StringBuilder sb = new StringBuilder();
  380. sb.append("[LbsDropData]\n");
  381. sb.append(" ._wStyle: ").append(_wStyle).append('\n');
  382. sb.append(" ._cLine: ").append(_cLine).append('\n');
  383. sb.append(" ._dxMin: ").append(_dxMin).append('\n');
  384. sb.append(" ._str: ").append(_str).append('\n');
  385. if(_unused != null) {
  386. sb.append(" ._unused: ").append(_unused).append('\n');
  387. }
  388. sb.append("[/LbsDropData]\n");
  389. return sb.toString();
  390. }
  391. @Override
  392. public LbsDropData copy() {
  393. return new LbsDropData(this);
  394. }
  395. }
  396. }