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.

NameRecord.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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.hssf.record.cont.ContinuableRecord;
  17. import org.apache.poi.hssf.record.cont.ContinuableRecordOutput;
  18. import org.apache.poi.ss.formula.ptg.Area3DPtg;
  19. import org.apache.poi.ss.formula.ptg.Ptg;
  20. import org.apache.poi.ss.formula.ptg.Ref3DPtg;
  21. import org.apache.poi.ss.formula.Formula;
  22. import org.apache.poi.util.*;
  23. /**
  24. * Title: DEFINEDNAME Record (0x0018) <p/>
  25. * Description: Defines a named range within a workbook. <P>
  26. * REFERENCE: <P>
  27. * @author Libin Roman (Vista Portal LDT. Developer)
  28. * @author Sergei Kozello (sergeikozello at mail.ru)
  29. * @author Glen Stampoultzis (glens at apache.org)
  30. * @author Petr Udalau - added method setFunction(boolean)
  31. */
  32. public final class NameRecord extends ContinuableRecord {
  33. public final static short sid = 0x0018;
  34. /**Included for completeness sake, not implemented */
  35. public final static byte BUILTIN_CONSOLIDATE_AREA = 1;
  36. /**Included for completeness sake, not implemented */
  37. public final static byte BUILTIN_AUTO_OPEN = 2;
  38. /**Included for completeness sake, not implemented */
  39. public final static byte BUILTIN_AUTO_CLOSE = 3;
  40. /**Included for completeness sake, not implemented */
  41. public final static byte BUILTIN_DATABASE = 4;
  42. /**Included for completeness sake, not implemented */
  43. public final static byte BUILTIN_CRITERIA = 5;
  44. public final static byte BUILTIN_PRINT_AREA = 6;
  45. public final static byte BUILTIN_PRINT_TITLE = 7;
  46. /**Included for completeness sake, not implemented */
  47. public final static byte BUILTIN_RECORDER = 8;
  48. /**Included for completeness sake, not implemented */
  49. public final static byte BUILTIN_DATA_FORM = 9;
  50. /**Included for completeness sake, not implemented */
  51. public final static byte BUILTIN_AUTO_ACTIVATE = 10;
  52. /**Included for completeness sake, not implemented */
  53. public final static byte BUILTIN_AUTO_DEACTIVATE = 11;
  54. /**Included for completeness sake, not implemented */
  55. public final static byte BUILTIN_SHEET_TITLE = 12;
  56. public final static byte BUILTIN_FILTER_DB = 13;
  57. private static final class Option {
  58. public static final int OPT_HIDDEN_NAME = 0x0001;
  59. public static final int OPT_FUNCTION_NAME = 0x0002;
  60. public static final int OPT_COMMAND_NAME = 0x0004;
  61. public static final int OPT_MACRO = 0x0008;
  62. public static final int OPT_COMPLEX = 0x0010;
  63. public static final int OPT_BUILTIN = 0x0020;
  64. public static final int OPT_BINDATA = 0x1000;
  65. public static final boolean isFormula(int optValue) {
  66. return (optValue & 0x0F) == 0;
  67. }
  68. }
  69. private short field_1_option_flag;
  70. private byte field_2_keyboard_shortcut;
  71. /** One-based extern index of sheet (resolved via LinkTable). Zero if this is a global name */
  72. private short field_5_externSheetIndex_plus1;
  73. /** the one based sheet number. */
  74. private int field_6_sheetNumber;
  75. private boolean field_11_nameIsMultibyte;
  76. private byte field_12_built_in_code;
  77. private String field_12_name_text;
  78. private Formula field_13_name_definition;
  79. private String field_14_custom_menu_text;
  80. private String field_15_description_text;
  81. private String field_16_help_topic_text;
  82. private String field_17_status_bar_text;
  83. /** Creates new NameRecord */
  84. public NameRecord() {
  85. field_13_name_definition = Formula.create(Ptg.EMPTY_PTG_ARRAY);
  86. field_12_name_text = "";
  87. field_14_custom_menu_text = "";
  88. field_15_description_text = "";
  89. field_16_help_topic_text = "";
  90. field_17_status_bar_text = "";
  91. }
  92. /**
  93. * Constructor to create a built-in named region
  94. * @param builtin Built-in byte representation for the name record, use the public constants
  95. */
  96. public NameRecord(byte builtin, int sheetNumber)
  97. {
  98. this();
  99. field_12_built_in_code = builtin;
  100. setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN));
  101. field_6_sheetNumber = sheetNumber; //the extern sheets are set through references
  102. }
  103. /** sets the option flag for the named range
  104. * @param flag option flag
  105. */
  106. public void setOptionFlag(short flag){
  107. field_1_option_flag = flag;
  108. }
  109. /** sets the keyboard shortcut
  110. * @param shortcut keyboard shortcut
  111. */
  112. public void setKeyboardShortcut(byte shortcut){
  113. field_2_keyboard_shortcut = shortcut;
  114. }
  115. /**
  116. * For named ranges, and built-in names
  117. * @return the 1-based sheet number.
  118. */
  119. public int getSheetNumber()
  120. {
  121. return field_6_sheetNumber;
  122. }
  123. /**
  124. * @return function group
  125. * @see FnGroupCountRecord
  126. */
  127. public byte getFnGroup() {
  128. int masked = field_1_option_flag & 0x0fc0;
  129. return (byte) (masked >> 4);
  130. }
  131. public void setSheetNumber(int value)
  132. {
  133. field_6_sheetNumber = value;
  134. }
  135. /** sets the name of the named range
  136. * @param name named range name
  137. */
  138. public void setNameText(String name){
  139. field_12_name_text = name;
  140. field_11_nameIsMultibyte = StringUtil.hasMultibyte(name);
  141. }
  142. /** sets the custom menu text
  143. * @param text custom menu text
  144. */
  145. public void setCustomMenuText(String text){
  146. field_14_custom_menu_text = text;
  147. }
  148. /** sets the description text
  149. * @param text the description text
  150. */
  151. public void setDescriptionText(String text){
  152. field_15_description_text = text;
  153. }
  154. /** sets the help topic text
  155. * @param text help topix text
  156. */
  157. public void setHelpTopicText(String text){
  158. field_16_help_topic_text = text;
  159. }
  160. /** sets the status bar text
  161. * @param text status bar text
  162. */
  163. public void setStatusBarText(String text){
  164. field_17_status_bar_text = text;
  165. }
  166. /** gets the option flag
  167. * @return option flag
  168. */
  169. public short getOptionFlag(){
  170. return field_1_option_flag;
  171. }
  172. /** returns the keyboard shortcut
  173. * @return keyboard shortcut
  174. */
  175. public byte getKeyboardShortcut(){
  176. return field_2_keyboard_shortcut ;
  177. }
  178. /**
  179. * gets the name length, in characters
  180. * @return name length
  181. */
  182. private int getNameTextLength(){
  183. if (isBuiltInName()) {
  184. return 1;
  185. }
  186. return field_12_name_text.length();
  187. }
  188. /**
  189. * @return true if name is hidden
  190. */
  191. public boolean isHiddenName() {
  192. return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0;
  193. }
  194. public void setHidden(boolean b) {
  195. if (b) {
  196. field_1_option_flag |= Option.OPT_HIDDEN_NAME;
  197. } else {
  198. field_1_option_flag &= (~Option.OPT_HIDDEN_NAME);
  199. }
  200. }
  201. /**
  202. * @return <code>true</code> if name is a function
  203. */
  204. public boolean isFunctionName() {
  205. return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0;
  206. }
  207. /**
  208. * Indicates that the defined name refers to a user-defined function.
  209. * This attribute is used when there is an add-in or other code project associated with the file.
  210. *
  211. * @param function <code>true</code> indicates the name refers to a function.
  212. */
  213. public void setFunction(boolean function){
  214. if (function) {
  215. field_1_option_flag |= Option.OPT_FUNCTION_NAME;
  216. } else {
  217. field_1_option_flag &= (~Option.OPT_FUNCTION_NAME);
  218. }
  219. }
  220. /**
  221. * @return <code>true</code> if name has a formula (named range or defined value)
  222. */
  223. public boolean hasFormula() {
  224. return Option.isFormula(field_1_option_flag) && field_13_name_definition.getEncodedTokenSize() > 0;
  225. }
  226. /**
  227. * @return true if name is a command
  228. */
  229. public boolean isCommandName() {
  230. return (field_1_option_flag & Option.OPT_COMMAND_NAME) != 0;
  231. }
  232. /**
  233. * @return true if function macro or command macro
  234. */
  235. public boolean isMacro() {
  236. return (field_1_option_flag & Option.OPT_MACRO) != 0;
  237. }
  238. /**
  239. * @return true if array formula or user defined
  240. */
  241. public boolean isComplexFunction() {
  242. return (field_1_option_flag & Option.OPT_COMPLEX) != 0;
  243. }
  244. /**Convenience Function to determine if the name is a built-in name
  245. */
  246. public boolean isBuiltInName()
  247. {
  248. return ((field_1_option_flag & Option.OPT_BUILTIN) != 0);
  249. }
  250. /** gets the name
  251. * @return name
  252. */
  253. public String getNameText(){
  254. return isBuiltInName() ? translateBuiltInName(getBuiltInName()) : field_12_name_text;
  255. }
  256. /** Gets the Built In Name
  257. * @return the built in Name
  258. */
  259. public byte getBuiltInName()
  260. {
  261. return field_12_built_in_code;
  262. }
  263. /** gets the definition, reference (Formula)
  264. * @return the name formula. never <code>null</code>
  265. */
  266. public Ptg[] getNameDefinition() {
  267. return field_13_name_definition.getTokens();
  268. }
  269. public void setNameDefinition(Ptg[] ptgs) {
  270. field_13_name_definition = Formula.create(ptgs);
  271. }
  272. /** get the custom menu text
  273. * @return custom menu text
  274. */
  275. public String getCustomMenuText(){
  276. return field_14_custom_menu_text;
  277. }
  278. /** gets the description text
  279. * @return description text
  280. */
  281. public String getDescriptionText(){
  282. return field_15_description_text;
  283. }
  284. /** get the help topic text
  285. * @return gelp topic text
  286. */
  287. public String getHelpTopicText(){
  288. return field_16_help_topic_text;
  289. }
  290. /** gets the status bar text
  291. * @return status bar text
  292. */
  293. public String getStatusBarText(){
  294. return field_17_status_bar_text;
  295. }
  296. /**
  297. * NameRecord can span into
  298. *
  299. * @param out a data output stream
  300. */
  301. public void serialize(ContinuableRecordOutput out) {
  302. int field_7_length_custom_menu = field_14_custom_menu_text.length();
  303. int field_8_length_description_text = field_15_description_text.length();
  304. int field_9_length_help_topic_text = field_16_help_topic_text.length();
  305. int field_10_length_status_bar_text = field_17_status_bar_text.length();
  306. // size defined below
  307. out.writeShort(getOptionFlag());
  308. out.writeByte(getKeyboardShortcut());
  309. out.writeByte(getNameTextLength());
  310. // Note - formula size is not immediately before encoded formula, and does not include any array constant data
  311. out.writeShort(field_13_name_definition.getEncodedTokenSize());
  312. out.writeShort(field_5_externSheetIndex_plus1);
  313. out.writeShort(field_6_sheetNumber);
  314. out.writeByte(field_7_length_custom_menu);
  315. out.writeByte(field_8_length_description_text);
  316. out.writeByte(field_9_length_help_topic_text);
  317. out.writeByte(field_10_length_status_bar_text);
  318. out.writeByte(field_11_nameIsMultibyte ? 1 : 0);
  319. if (isBuiltInName()) {
  320. //can send the builtin name directly in
  321. out.writeByte(field_12_built_in_code);
  322. } else {
  323. String nameText = field_12_name_text;
  324. if (field_11_nameIsMultibyte) {
  325. StringUtil.putUnicodeLE(nameText, out);
  326. } else {
  327. StringUtil.putCompressedUnicode(nameText, out);
  328. }
  329. }
  330. field_13_name_definition.serializeTokens(out);
  331. field_13_name_definition.serializeArrayConstantData(out);
  332. StringUtil.putCompressedUnicode( getCustomMenuText(), out);
  333. StringUtil.putCompressedUnicode( getDescriptionText(), out);
  334. StringUtil.putCompressedUnicode( getHelpTopicText(), out);
  335. StringUtil.putCompressedUnicode( getStatusBarText(), out);
  336. }
  337. private int getNameRawSize() {
  338. if (isBuiltInName()) {
  339. return 1;
  340. }
  341. int nChars = field_12_name_text.length();
  342. if(field_11_nameIsMultibyte) {
  343. return 2 * nChars;
  344. }
  345. return nChars;
  346. }
  347. protected int getDataSize() {
  348. return 13 // 3 shorts + 7 bytes
  349. + getNameRawSize()
  350. + field_14_custom_menu_text.length()
  351. + field_15_description_text.length()
  352. + field_16_help_topic_text.length()
  353. + field_17_status_bar_text.length()
  354. + field_13_name_definition.getEncodedSize();
  355. }
  356. /** gets the extern sheet number
  357. * @return extern sheet index
  358. */
  359. public int getExternSheetNumber(){
  360. Ptg[] tokens = field_13_name_definition.getTokens();
  361. if (tokens.length == 0) {
  362. return 0;
  363. }
  364. Ptg ptg = tokens[0];
  365. if (ptg.getClass() == Area3DPtg.class){
  366. return ((Area3DPtg) ptg).getExternSheetIndex();
  367. }
  368. if (ptg.getClass() == Ref3DPtg.class){
  369. return ((Ref3DPtg) ptg).getExternSheetIndex();
  370. }
  371. return 0;
  372. }
  373. /**
  374. * called by the constructor, should set class level fields. Should throw
  375. * runtime exception for bad/icomplete data.
  376. *
  377. * @param ris the RecordInputstream to read the record from
  378. */
  379. public NameRecord(RecordInputStream ris) {
  380. // YK: Formula data can span into continue records, for example,
  381. // when containing a large array of strings. See Bugzilla 50244
  382. // read all remaining bytes and wrap into a LittleEndianInput
  383. byte[] remainder = ris.readAllContinuedRemainder();
  384. LittleEndianInput in = new LittleEndianByteArrayInputStream(remainder);
  385. field_1_option_flag = in.readShort();
  386. field_2_keyboard_shortcut = in.readByte();
  387. int field_3_length_name_text = in.readUByte();
  388. int field_4_length_name_definition = in.readShort();
  389. field_5_externSheetIndex_plus1 = in.readShort();
  390. field_6_sheetNumber = in.readUShort();
  391. int f7_customMenuLen = in.readUByte();
  392. int f8_descriptionTextLen = in.readUByte();
  393. int f9_helpTopicTextLen = in.readUByte();
  394. int f10_statusBarTextLen = in.readUByte();
  395. //store the name in byte form if it's a built-in name
  396. field_11_nameIsMultibyte = (in.readByte() != 0);
  397. if (isBuiltInName()) {
  398. field_12_built_in_code = in.readByte();
  399. } else {
  400. if (field_11_nameIsMultibyte) {
  401. field_12_name_text = StringUtil.readUnicodeLE(in, field_3_length_name_text);
  402. } else {
  403. field_12_name_text = StringUtil.readCompressedUnicode(in, field_3_length_name_text);
  404. }
  405. }
  406. int nBytesAvailable = in.available() - (f7_customMenuLen
  407. + f8_descriptionTextLen + f9_helpTopicTextLen + f10_statusBarTextLen);
  408. field_13_name_definition = Formula.read(field_4_length_name_definition, in, nBytesAvailable);
  409. //Who says that this can only ever be compressed unicode???
  410. field_14_custom_menu_text = StringUtil.readCompressedUnicode(in, f7_customMenuLen);
  411. field_15_description_text = StringUtil.readCompressedUnicode(in, f8_descriptionTextLen);
  412. field_16_help_topic_text = StringUtil.readCompressedUnicode(in, f9_helpTopicTextLen);
  413. field_17_status_bar_text = StringUtil.readCompressedUnicode(in, f10_statusBarTextLen);
  414. }
  415. /**
  416. * return the non static version of the id for this record.
  417. */
  418. public short getSid() {
  419. return sid;
  420. }
  421. /*
  422. 20 00
  423. 00
  424. 01
  425. 1A 00 // sz = 0x1A = 26
  426. 00 00
  427. 01 00
  428. 00
  429. 00
  430. 00
  431. 00
  432. 00 // unicode flag
  433. 07 // name
  434. 29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26
  435. 00 07 00 07 00 00 00 FF 00 10 // }
  436. 20 00
  437. 00
  438. 01
  439. 0B 00 // sz = 0xB = 11
  440. 00 00
  441. 01 00
  442. 00
  443. 00
  444. 00
  445. 00
  446. 00 // unicode flag
  447. 07 // name
  448. 3B 00 00 07 00 07 00 00 00 FF 00 // { 11 }
  449. */
  450. /*
  451. 18, 00,
  452. 1B, 00,
  453. 20, 00,
  454. 00,
  455. 01,
  456. 0B, 00,
  457. 00,
  458. 00,
  459. 00,
  460. 00,
  461. 00,
  462. 07,
  463. 3B 00 00 07 00 07 00 00 00 FF 00 ]
  464. */
  465. public String toString() {
  466. StringBuffer sb = new StringBuffer();
  467. sb.append("[NAME]\n");
  468. sb.append(" .option flags = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n");
  469. sb.append(" .keyboard shortcut = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n");
  470. sb.append(" .length of the name = ").append(getNameTextLength()).append("\n");
  471. sb.append(" .extSheetIx(1-based, 0=Global)= ").append( field_5_externSheetIndex_plus1 ).append("\n");
  472. sb.append(" .sheetTabIx = ").append(field_6_sheetNumber ).append("\n");
  473. sb.append(" .Menu text length = ").append(field_14_custom_menu_text.length()).append("\n");
  474. sb.append(" .Description text length= ").append(field_15_description_text.length()).append("\n");
  475. sb.append(" .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n");
  476. sb.append(" .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n");
  477. sb.append(" .NameIsMultibyte = ").append(field_11_nameIsMultibyte).append("\n");
  478. sb.append(" .Name (Unicode text) = ").append( getNameText() ).append("\n");
  479. Ptg[] ptgs = field_13_name_definition.getTokens();
  480. sb.append(" .Formula (nTokens=").append(ptgs.length).append("):") .append("\n");
  481. for (int i = 0; i < ptgs.length; i++) {
  482. Ptg ptg = ptgs[i];
  483. sb.append(" " + ptg.toString()).append(ptg.getRVAType()).append("\n");
  484. }
  485. sb.append(" .Menu text = ").append(field_14_custom_menu_text).append("\n");
  486. sb.append(" .Description text= ").append(field_15_description_text).append("\n");
  487. sb.append(" .Help topic text = ").append(field_16_help_topic_text).append("\n");
  488. sb.append(" .Status bar text = ").append(field_17_status_bar_text).append("\n");
  489. sb.append("[/NAME]\n");
  490. return sb.toString();
  491. }
  492. /**Creates a human readable name for built in types
  493. * @return Unknown if the built-in name cannot be translated
  494. */
  495. private static String translateBuiltInName(byte name)
  496. {
  497. switch (name)
  498. {
  499. case NameRecord.BUILTIN_AUTO_ACTIVATE : return "Auto_Activate";
  500. case NameRecord.BUILTIN_AUTO_CLOSE : return "Auto_Close";
  501. case NameRecord.BUILTIN_AUTO_DEACTIVATE : return "Auto_Deactivate";
  502. case NameRecord.BUILTIN_AUTO_OPEN : return "Auto_Open";
  503. case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area";
  504. case NameRecord.BUILTIN_CRITERIA : return "Criteria";
  505. case NameRecord.BUILTIN_DATABASE : return "Database";
  506. case NameRecord.BUILTIN_DATA_FORM : return "Data_Form";
  507. case NameRecord.BUILTIN_PRINT_AREA : return "Print_Area";
  508. case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles";
  509. case NameRecord.BUILTIN_RECORDER : return "Recorder";
  510. case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title";
  511. case NameRecord.BUILTIN_FILTER_DB : return "_FilterDatabase";
  512. }
  513. return "Unknown";
  514. }
  515. }