Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

HSSFName.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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.usermodel;
  16. import org.apache.poi.hssf.model.HSSFFormulaParser;
  17. import org.apache.poi.hssf.model.InternalWorkbook;
  18. import org.apache.poi.hssf.record.NameCommentRecord;
  19. import org.apache.poi.hssf.record.NameRecord;
  20. import org.apache.poi.ss.SpreadsheetVersion;
  21. import org.apache.poi.ss.formula.FormulaType;
  22. import org.apache.poi.ss.formula.ptg.Ptg;
  23. import org.apache.poi.ss.usermodel.Name;
  24. import org.apache.poi.ss.util.CellReference;
  25. /**
  26. * High Level Representation of a 'defined name' which could be a 'built-in' name,
  27. * 'named range' or name of a user defined function.
  28. */
  29. public final class HSSFName implements Name {
  30. private final HSSFWorkbook _book;
  31. private final NameRecord _definedNameRec;
  32. private final NameCommentRecord _commentRec;
  33. /**
  34. * Creates new HSSFName - called by HSSFWorkbook to create a name from
  35. * scratch.
  36. *
  37. * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName()
  38. * @param name the Name Record
  39. * @param book workbook object associated with the sheet.
  40. */
  41. /* package */ HSSFName(HSSFWorkbook book, NameRecord name) {
  42. this(book, name, null);
  43. }
  44. /**
  45. * Creates new HSSFName - called by HSSFWorkbook to create a name from
  46. * scratch.
  47. *
  48. * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName()
  49. * @param name the Name Record
  50. * @param comment the Name Comment Record, optional.
  51. * @param book workbook object associated with the sheet.
  52. */
  53. /* package */ HSSFName(final HSSFWorkbook book, final NameRecord name, final NameCommentRecord comment) {
  54. _book = book;
  55. _definedNameRec = name;
  56. _commentRec = comment;
  57. }
  58. /** Get the sheets name which this named range is referenced to
  59. * @return sheet name, which this named range referred to
  60. */
  61. @Override
  62. public String getSheetName() {
  63. int indexToExternSheet = _definedNameRec.getExternSheetNumber();
  64. return _book.getWorkbook().findSheetFirstNameFromExternSheet(indexToExternSheet);
  65. }
  66. /**
  67. * @return text name of this defined name
  68. */
  69. @Override
  70. public String getNameName(){
  71. return _definedNameRec.getNameText();
  72. }
  73. /**
  74. * Sets the name of the named range
  75. *
  76. * <p>The following is a list of syntax rules that you need to be aware of when you create and edit names.</p>
  77. * <ul>
  78. * <li><strong>Valid characters</strong>
  79. * The first character of a name must be a letter, an underscore character (_), or a backslash (\).
  80. * Remaining characters in the name can be letters, numbers, periods, and underscore characters.
  81. * </li>
  82. * <li><strong>Cell references disallowed</strong>
  83. * Names cannot be the same as a cell reference, such as Z$100 or R1C1.</li>
  84. * <li><strong>Spaces are not valid</strong>
  85. * Spaces are not allowed as part of a name. Use the underscore character (_) and period (.) as word separators, such as, Sales_Tax or First.Quarter.
  86. * </li>
  87. * <li><strong>Name length</strong>
  88. * A name can contain up to 255 characters.
  89. * </li>
  90. * <li><strong>Case sensitivity</strong>
  91. * Names can contain uppercase and lowercase letters.
  92. * </li>
  93. * </ul>
  94. *
  95. * <p>
  96. * A name must always be unique within its scope. POI prevents you from defining a name that is not unique
  97. * within its scope. However you can use the same name in different scopes. Example:
  98. * <pre>{@code
  99. * //by default names are workbook-global
  100. * HSSFName name;
  101. * name = workbook.createName();
  102. * name.setNameName("sales_08");
  103. *
  104. * name = workbook.createName();
  105. * name.setNameName("sales_08"); //will throw an exception: "The workbook already contains this name (case-insensitive)"
  106. *
  107. * //create sheet-level name
  108. * name = workbook.createName();
  109. * name.setSheetIndex(0); //the scope of the name is the first sheet
  110. * name.setNameName("sales_08"); //ok
  111. *
  112. * name = workbook.createName();
  113. * name.setSheetIndex(0);
  114. * name.setNameName("sales_08"); //will throw an exception: "The sheet already contains this name (case-insensitive)"
  115. *
  116. * }</pre>
  117. *
  118. * @param nameName named range name to set
  119. * @throws IllegalArgumentException if the name is invalid or the name already exists (case-insensitive)
  120. */
  121. @Override
  122. public void setNameName(String nameName){
  123. validateName(nameName);
  124. InternalWorkbook wb = _book.getWorkbook();
  125. _definedNameRec.setNameText(nameName);
  126. int sheetNumber = _definedNameRec.getSheetNumber();
  127. //Check to ensure no other names have the same case-insensitive name
  128. final int lastNameIndex = wb.getNumNames()-1;
  129. for ( int i = lastNameIndex; i >=0; i-- )
  130. {
  131. NameRecord rec = wb.getNameRecord(i);
  132. if (rec != _definedNameRec) {
  133. if (rec.getNameText().equalsIgnoreCase(nameName) && sheetNumber == rec.getSheetNumber()){
  134. String msg = "The "+(sheetNumber == 0 ? "workbook" : "sheet")+" already contains this name: " + nameName;
  135. _definedNameRec.setNameText(nameName + "(2)");
  136. throw new IllegalArgumentException(msg);
  137. }
  138. }
  139. }
  140. // Update our comment, if there is one
  141. if(_commentRec != null) {
  142. _commentRec.setNameText(nameName);
  143. _book.getWorkbook().updateNameCommentRecordCache(_commentRec);
  144. }
  145. }
  146. /**
  147. * https://support.office.com/en-us/article/Define-and-use-names-in-formulas-4D0F13AC-53B7-422E-AFD2-ABD7FF379C64#bmsyntax_rules_for_names
  148. *
  149. * Valid characters:
  150. * First character: { letter | underscore | backslash }
  151. * Remaining characters: { letter | number | period | underscore }
  152. *
  153. * Cell shorthand: cannot be { "C" | "c" | "R" | "r" }
  154. *
  155. * Cell references disallowed: cannot be a cell reference $A$1 or R1C1
  156. *
  157. * Spaces are not valid (follows from valid characters above)
  158. *
  159. * Name length: (XSSF-specific?) 255 characters maximum
  160. *
  161. * Case sensitivity: all names are case-insensitive
  162. *
  163. * Uniqueness: must be unique (for names with the same scope)
  164. */
  165. private static void validateName(String name) {
  166. if (name.length() == 0) {
  167. throw new IllegalArgumentException("Name cannot be blank");
  168. }
  169. if (name.length() > 255) {
  170. throw new IllegalArgumentException("Invalid name: '"+name+"': cannot exceed 255 characters in length");
  171. }
  172. if (name.equalsIgnoreCase("R") || name.equalsIgnoreCase("C")) {
  173. throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be special shorthand R or C");
  174. }
  175. // is first character valid?
  176. char c = name.charAt(0);
  177. String allowedSymbols = "_\\";
  178. boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1);
  179. if (!characterIsValid) {
  180. throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter");
  181. }
  182. // are all other characters valid?
  183. allowedSymbols = "_.\\"; //backslashes needed for unicode escape
  184. for (final char ch : name.toCharArray()) {
  185. characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1);
  186. if (!characterIsValid) {
  187. throw new IllegalArgumentException("Invalid name: '"+name+"': name must be letter, digit, period, or underscore");
  188. }
  189. }
  190. // Is the name a valid $A$1 cell reference
  191. // Because $, :, and ! are disallowed characters, A1-style references become just a letter-number combination
  192. if (name.matches("[A-Za-z]+\\d+")) {
  193. String col = name.replaceAll("\\d", "");
  194. String row = name.replaceAll("[A-Za-z]", "");
  195. if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) {
  196. throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
  197. }
  198. }
  199. // Is the name a valid R1C1 cell reference?
  200. if (name.matches("[Rr]\\d+[Cc]\\d+")) {
  201. throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be R1C1-style cell reference");
  202. }
  203. }
  204. @Override
  205. public void setRefersToFormula(String formulaText) {
  206. Ptg[] ptgs = HSSFFormulaParser.parse(formulaText, _book, FormulaType.NAMEDRANGE, getSheetIndex());
  207. _definedNameRec.setNameDefinition(ptgs);
  208. }
  209. @Override
  210. public String getRefersToFormula() {
  211. if (_definedNameRec.isFunctionName()) {
  212. throw new IllegalStateException("Only applicable to named ranges");
  213. }
  214. Ptg[] ptgs = _definedNameRec.getNameDefinition();
  215. if (ptgs.length < 1) {
  216. // 'refersToFormula' has not been set yet
  217. return null;
  218. }
  219. return HSSFFormulaParser.toFormulaString(_book, ptgs);
  220. }
  221. /**
  222. * Sets the NameParsedFormula structure that specifies the formula for the
  223. * defined name.
  224. *
  225. * @param ptgs the sequence of {@link Ptg}s for the formula.
  226. */
  227. void setNameDefinition(Ptg[] ptgs) {
  228. _definedNameRec.setNameDefinition(ptgs);
  229. }
  230. @Override
  231. public boolean isDeleted(){
  232. Ptg[] ptgs = _definedNameRec.getNameDefinition();
  233. return Ptg.doesFormulaReferToDeletedCell(ptgs);
  234. }
  235. /**
  236. * Checks if this name is a function name
  237. *
  238. * @return true if this name is a function name
  239. */
  240. @Override
  241. public boolean isFunctionName() {
  242. return _definedNameRec.isFunctionName();
  243. }
  244. /**
  245. * Checks if this name is hidden, eg one of the built-in Excel
  246. * internal names
  247. *
  248. * @return true if this name is a hidden one
  249. */
  250. @Override
  251. public boolean isHidden() {
  252. return _definedNameRec.isHiddenName();
  253. }
  254. public String toString() {
  255. return getClass().getName() + " [" +
  256. _definedNameRec.getNameText() +
  257. "]";
  258. }
  259. /**
  260. * Specifies if the defined name is a local name, and if so, which sheet it is on.
  261. *
  262. * @param index if greater than 0, the defined name is a local name and the value MUST be a 0-based index
  263. * to the collection of sheets as they appear in the workbook.
  264. * @throws IllegalArgumentException if the sheet index is invalid.
  265. */
  266. @Override
  267. public void setSheetIndex(int index){
  268. int lastSheetIx = _book.getNumberOfSheets() - 1;
  269. if (index < -1 || index > lastSheetIx) {
  270. throw new IllegalArgumentException("Sheet index (" + index +") is out of range" +
  271. (lastSheetIx == -1 ? "" : (" (0.." + lastSheetIx + ")")));
  272. }
  273. _definedNameRec.setSheetNumber(index + 1);
  274. }
  275. /**
  276. * Returns the sheet index this name applies to.
  277. *
  278. * @return the sheet index this name applies to, -1 if this name applies to the entire workbook
  279. */
  280. @Override
  281. public int getSheetIndex(){
  282. return _definedNameRec.getSheetNumber() - 1;
  283. }
  284. /**
  285. * Returns the comment the user provided when the name was created.
  286. *
  287. * @return the user comment for this named range
  288. */
  289. @Override
  290. public String getComment() {
  291. if(_commentRec != null) {
  292. // Prefer the comment record if it has text in it
  293. if(_commentRec.getCommentText() != null &&
  294. _commentRec.getCommentText().length() > 0) {
  295. return _commentRec.getCommentText();
  296. }
  297. }
  298. return _definedNameRec.getDescriptionText();
  299. }
  300. /**
  301. * Sets the comment the user provided when the name was created.
  302. *
  303. * @param comment the user comment for this named range
  304. */
  305. @Override
  306. public void setComment(String comment){
  307. // Update the main record
  308. _definedNameRec.setDescriptionText(comment);
  309. // If we have a comment record too, update that as well
  310. if(_commentRec != null) {
  311. _commentRec.setCommentText(comment);
  312. }
  313. }
  314. /**
  315. * Indicates that the defined name refers to a user-defined function.
  316. * This attribute is used when there is an add-in or other code project associated with the file.
  317. *
  318. * @param value {@code true} indicates the name refers to a function.
  319. */
  320. @Override
  321. public void setFunction(boolean value) {
  322. _definedNameRec.setFunction(value);
  323. }
  324. }