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.

XSSFWorkbook.java 89KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513
  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.xssf.usermodel;
  16. import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
  17. import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.setPassword;
  18. import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.validatePassword;
  19. import static org.openxmlformats.schemas.spreadsheetml.x2006.main.STRefMode.*;
  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.Collections;
  27. import java.util.HashMap;
  28. import java.util.Iterator;
  29. import java.util.LinkedList;
  30. import java.util.List;
  31. import java.util.Locale;
  32. import java.util.Map;
  33. import java.util.NoSuchElementException;
  34. import java.util.Spliterator;
  35. import java.util.regex.Pattern;
  36. import javax.xml.namespace.QName;
  37. import org.apache.commons.collections4.ListValuedMap;
  38. import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
  39. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  40. import org.apache.logging.log4j.LogManager;
  41. import org.apache.logging.log4j.Logger;
  42. import org.apache.poi.hpsf.ClassIDPredefined;
  43. import org.apache.poi.ooxml.POIXMLDocument;
  44. import org.apache.poi.ooxml.POIXMLDocumentPart;
  45. import org.apache.poi.ooxml.POIXMLException;
  46. import org.apache.poi.ooxml.POIXMLProperties;
  47. import org.apache.poi.ooxml.util.PackageHelper;
  48. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  49. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  50. import org.apache.poi.openxml4j.opc.OPCPackage;
  51. import org.apache.poi.openxml4j.opc.PackageAccess;
  52. import org.apache.poi.openxml4j.opc.PackagePart;
  53. import org.apache.poi.openxml4j.opc.PackagePartName;
  54. import org.apache.poi.openxml4j.opc.PackageRelationship;
  55. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  56. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  57. import org.apache.poi.openxml4j.opc.TargetMode;
  58. import org.apache.poi.poifs.crypt.HashAlgorithm;
  59. import org.apache.poi.poifs.filesystem.DirectoryNode;
  60. import org.apache.poi.poifs.filesystem.Ole10Native;
  61. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  62. import org.apache.poi.ss.SpreadsheetVersion;
  63. import org.apache.poi.ss.formula.SheetNameFormatter;
  64. import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
  65. import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
  66. import org.apache.poi.ss.formula.udf.UDFFinder;
  67. import org.apache.poi.ss.usermodel.CellReferenceType;
  68. import org.apache.poi.ss.usermodel.DataFormat;
  69. import org.apache.poi.ss.usermodel.Date1904Support;
  70. import org.apache.poi.ss.usermodel.Name;
  71. import org.apache.poi.ss.usermodel.Row;
  72. import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
  73. import org.apache.poi.ss.usermodel.Sheet;
  74. import org.apache.poi.ss.usermodel.SheetVisibility;
  75. import org.apache.poi.ss.usermodel.Workbook;
  76. import org.apache.poi.ss.util.CellReference;
  77. import org.apache.poi.ss.util.WorkbookUtil;
  78. import org.apache.poi.util.Beta;
  79. import org.apache.poi.util.IOUtils;
  80. import org.apache.poi.util.Internal;
  81. import org.apache.poi.util.NotImplemented;
  82. import org.apache.poi.util.Removal;
  83. import org.apache.poi.xssf.XLSBUnsupportedException;
  84. import org.apache.poi.xssf.model.CalculationChain;
  85. import org.apache.poi.xssf.model.ExternalLinksTable;
  86. import org.apache.poi.xssf.model.MapInfo;
  87. import org.apache.poi.xssf.model.SharedStringsTable;
  88. import org.apache.poi.xssf.model.StylesTable;
  89. import org.apache.poi.xssf.model.ThemesTable;
  90. import org.apache.poi.xssf.usermodel.helpers.XSSFFormulaUtils;
  91. import org.apache.xmlbeans.XmlException;
  92. import org.apache.xmlbeans.XmlObject;
  93. import org.apache.xmlbeans.XmlOptions;
  94. import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
  95. /**
  96. * High level representation of a SpreadsheetML workbook. This is the first object most users
  97. * will construct whether they are reading or writing a workbook. It is also the
  98. * top level object for creating new sheets/etc.
  99. */
  100. public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Support {
  101. private static final Pattern COMMA_PATTERN = Pattern.compile(",");
  102. private static final Pattern GET_ALL_PICTURES_PATTERN = Pattern.compile("/xl/media/.*?");
  103. /**
  104. * Images formats supported by XSSF but not by HSSF
  105. */
  106. public static final int PICTURE_TYPE_GIF = 8;
  107. public static final int PICTURE_TYPE_TIFF = 9;
  108. public static final int PICTURE_TYPE_EPS = 10;
  109. public static final int PICTURE_TYPE_BMP = 11;
  110. public static final int PICTURE_TYPE_WPG = 12;
  111. /**
  112. * The underlying XML bean
  113. */
  114. private CTWorkbook workbook;
  115. /**
  116. * this holds the XSSFSheet objects attached to this workbook
  117. */
  118. private List<XSSFSheet> sheets;
  119. /**
  120. * this holds the XSSFName objects attached to this workbook, keyed by lower-case name
  121. */
  122. private ListValuedMap<String, XSSFName> namedRangesByName;
  123. /**
  124. * this holds the XSSFName objects attached to this workbook
  125. */
  126. private List<XSSFName> namedRanges;
  127. /**
  128. * shared string table - a cache of strings in this workbook
  129. */
  130. private SharedStringsTable sharedStringSource;
  131. /**
  132. * A collection of shared objects used for styling content,
  133. * e.g. fonts, cell styles, colors, etc.
  134. */
  135. private StylesTable stylesSource;
  136. /**
  137. * The locator of user-defined functions.
  138. * By default, includes functions from the Excel Analysis Toolpack
  139. */
  140. private final IndexedUDFFinder _udfFinder = new IndexedUDFFinder(AggregatingUDFFinder.DEFAULT);
  141. private CalculationChain calcChain;
  142. /**
  143. * External Links, for referencing names or cells in other workbooks.
  144. */
  145. private List<ExternalLinksTable> externalLinks;
  146. /**
  147. * A collection of custom XML mappings
  148. */
  149. private MapInfo mapInfo;
  150. /**
  151. * Used to keep track of the data formatter so that all
  152. * createDataFormatter calls return the same one for a given
  153. * book. This ensures that updates from one places is visible
  154. * someplace else.
  155. */
  156. private XSSFDataFormat formatter;
  157. /**
  158. * The policy to apply in the event of missing or
  159. * blank cells when fetching from a row.
  160. * See {@link MissingCellPolicy}
  161. */
  162. private MissingCellPolicy _missingCellPolicy = MissingCellPolicy.RETURN_NULL_AND_BLANK;
  163. /**
  164. * Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
  165. */
  166. private boolean cellFormulaValidation = true;
  167. /**
  168. * array of pictures for this workbook
  169. */
  170. private List<XSSFPictureData> pictures;
  171. private static final Logger LOG = LogManager.getLogger(XSSFWorkbook.class);
  172. /**
  173. * cached instance of XSSFCreationHelper for this workbook
  174. * @see #getCreationHelper()
  175. */
  176. private XSSFCreationHelper _creationHelper;
  177. /**
  178. * List of all pivot tables in workbook
  179. */
  180. private List<XSSFPivotTable> pivotTables;
  181. private List<CTPivotCache> pivotCaches;
  182. private final XSSFFactory xssfFactory;
  183. /**
  184. * Create a new SpreadsheetML workbook.
  185. */
  186. public XSSFWorkbook() {
  187. this(XSSFWorkbookType.XLSX);
  188. }
  189. public XSSFWorkbook(XSSFFactory factory) {
  190. this(XSSFWorkbookType.XLSX, factory);
  191. }
  192. /**
  193. * Create a new SpreadsheetML workbook.
  194. * @param workbookType The type of workbook to make (.xlsx or .xlsm).
  195. */
  196. public XSSFWorkbook(XSSFWorkbookType workbookType) {
  197. this(workbookType, null);
  198. }
  199. private XSSFWorkbook(XSSFWorkbookType workbookType, XSSFFactory factory) {
  200. super(newPackage(workbookType));
  201. this.xssfFactory = (factory == null) ? XSSFFactory.getInstance() : factory;
  202. onWorkbookCreate();
  203. }
  204. /**
  205. * Constructs a XSSFWorkbook object given a OpenXML4J {@code Package} object,
  206. * see <a href="https://poi.apache.org/oxml4j/">https://poi.apache.org/oxml4j/</a>.
  207. *
  208. * <p>Once you have finished working with the Workbook, you should close the package
  209. * by calling either {@link #close()} or {@link OPCPackage#close()}, to avoid
  210. * leaving file handles open.
  211. *
  212. * <p>Creating a XSSFWorkbook from a file-backed OPC Package has a lower memory
  213. * footprint than an InputStream backed one.
  214. *
  215. * @param pkg the OpenXML4J {@code OPC Package} object.
  216. * @throws IOException If reading data from the package fails
  217. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  218. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  219. * input format
  220. */
  221. public XSSFWorkbook(OPCPackage pkg) throws IOException {
  222. super(pkg);
  223. this.xssfFactory = XSSFFactory.getInstance();
  224. beforeDocumentRead();
  225. // Build a tree of POIXMLDocumentParts, this workbook being the root
  226. load(this.xssfFactory);
  227. // some broken Workbooks miss this...
  228. setBookViewsIfMissing();
  229. }
  230. /**
  231. * Constructs a XSSFWorkbook object, by buffering the whole stream into memory
  232. * and then opening an {@link OPCPackage} object for it.
  233. *
  234. * <p>Using an {@link InputStream} requires more memory than using a File, so
  235. * if a {@link File} is available then you should instead do something like
  236. * <pre>{@code
  237. * OPCPackage pkg = OPCPackage.open(path);
  238. * XSSFWorkbook wb = new XSSFWorkbook(pkg);
  239. * // work with the wb object
  240. * ......
  241. * pkg.close(); // gracefully closes the underlying zip file
  242. * }</pre>
  243. *
  244. * @throws IOException If reading data from the stream fails
  245. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  246. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  247. * input format
  248. */
  249. public XSSFWorkbook(InputStream is) throws IOException {
  250. this(is, false);
  251. }
  252. private XSSFWorkbook(InputStream is, boolean closeStream) throws IOException {
  253. this(PackageHelper.open(is, closeStream));
  254. }
  255. /**
  256. * Constructs a XSSFWorkbook object from a given file.
  257. *
  258. * <p>Once you have finished working with the Workbook, you should close
  259. * the package by calling {@link #close()}, to avoid leaving file
  260. * handles open.
  261. *
  262. * <p>Opening a XSSFWorkbook from a file has a lower memory footprint
  263. * than opening from an InputStream
  264. *
  265. * @param file the file to open
  266. * @throws IOException If reading data from the file fails
  267. * @throws InvalidFormatException If the file has a format that cannot be read or if the file is corrupted
  268. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  269. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  270. * input format
  271. */
  272. public XSSFWorkbook(File file) throws IOException, InvalidFormatException {
  273. this(OPCPackage.open(file));
  274. }
  275. /**
  276. * Constructs a XSSFWorkbook object given a file name.
  277. *
  278. *
  279. * <p>Once you have finished working with the Workbook, you should close
  280. * the package by calling {@link #close()}, to avoid leaving file
  281. * handles open.
  282. *
  283. * <p>Opening a XSSFWorkbook from a file has a lower memory footprint
  284. * than opening from an InputStream
  285. *
  286. * @param path the file name.
  287. * @throws IOException If reading data from the file fails
  288. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  289. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  290. * input format
  291. *
  292. */
  293. public XSSFWorkbook(String path) throws IOException {
  294. this(openPackage(path));
  295. }
  296. /**
  297. * Constructs a XSSFWorkbook object using Package Part.
  298. * @param part package part
  299. * @throws IOException If reading data from the Package Part fails
  300. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  301. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  302. * input format
  303. * @since POI 4.0.0
  304. */
  305. public XSSFWorkbook(PackagePart part) throws IOException {
  306. this(part.getInputStream(), true);
  307. }
  308. /**
  309. * @return the XSSFFactory
  310. * @since POI 5.1.0
  311. */
  312. public XSSFFactory getXssfFactory() {
  313. return xssfFactory;
  314. }
  315. protected void beforeDocumentRead() {
  316. // Ensure it isn't a XLSB file, which we don't support
  317. if (getCorePart().getContentType().equals(XSSFRelation.XLSB_BINARY_WORKBOOK.getContentType())) {
  318. throw new XLSBUnsupportedException();
  319. }
  320. // Create arrays for parts attached to the workbook itself
  321. pivotTables = new ArrayList<>();
  322. pivotCaches = new ArrayList<>();
  323. }
  324. @Override
  325. protected void onDocumentRead() throws IOException {
  326. try {
  327. try (InputStream stream = getPackagePart().getInputStream()) {
  328. WorkbookDocument doc = WorkbookDocument.Factory.parse(stream, DEFAULT_XML_OPTIONS);
  329. this.workbook = doc.getWorkbook();
  330. }
  331. ThemesTable theme = null;
  332. Map<String, XSSFSheet> shIdMap = new HashMap<>();
  333. Map<String, ExternalLinksTable> elIdMap = new HashMap<>();
  334. for(RelationPart rp : getRelationParts()){
  335. POIXMLDocumentPart p = rp.getDocumentPart();
  336. if(p instanceof SharedStringsTable) {
  337. sharedStringSource = (SharedStringsTable)p;
  338. } else if(p instanceof StylesTable) {
  339. stylesSource = (StylesTable)p;
  340. } else if(p instanceof ThemesTable) {
  341. theme = (ThemesTable)p;
  342. } else if(p instanceof CalculationChain) {
  343. calcChain = (CalculationChain)p;
  344. } else if(p instanceof MapInfo) {
  345. mapInfo = (MapInfo)p;
  346. } else if (p instanceof XSSFSheet) {
  347. shIdMap.put(rp.getRelationship().getId(), (XSSFSheet)p);
  348. } else if (p instanceof ExternalLinksTable) {
  349. elIdMap.put(rp.getRelationship().getId(), (ExternalLinksTable)p);
  350. }
  351. }
  352. boolean packageReadOnly = (getPackage().getPackageAccess() == PackageAccess.READ);
  353. if (stylesSource == null) {
  354. // Create Styles if it is missing
  355. if (packageReadOnly) {
  356. stylesSource = new StylesTable();
  357. } else {
  358. stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, this.xssfFactory);
  359. }
  360. }
  361. stylesSource.setWorkbook(this);
  362. stylesSource.setTheme(theme);
  363. if (sharedStringSource == null) {
  364. // Create SST if it is missing
  365. if (packageReadOnly) {
  366. sharedStringSource = new SharedStringsTable();
  367. } else {
  368. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, this.xssfFactory);
  369. }
  370. }
  371. // Load individual sheets. The order of sheets is defined by the order
  372. // of CTSheet elements in the workbook
  373. sheets = new ArrayList<>(shIdMap.size());
  374. if (this.workbook == null || this.workbook.getSheets() == null ||
  375. this.workbook.getSheets().getSheetArray() == null) {
  376. throw new POIXMLException("Cannot read a workbook without sheets");
  377. }
  378. for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) {
  379. parseSheet(shIdMap, ctSheet);
  380. }
  381. // Load the external links tables. Their order is defined by the order
  382. // of CTExternalReference elements in the workbook
  383. externalLinks = new ArrayList<>(elIdMap.size());
  384. if (this.workbook.isSetExternalReferences()) {
  385. for (CTExternalReference er : this.workbook.getExternalReferences().getExternalReferenceArray()) {
  386. ExternalLinksTable el = elIdMap.get(er.getId());
  387. if(el == null) {
  388. LOG.atWarn().log("ExternalLinksTable with r:id {} was defined, but didn't exist in package, skipping", er.getId());
  389. continue;
  390. }
  391. externalLinks.add(el);
  392. }
  393. }
  394. // Process the named ranges
  395. reprocessNamedRanges();
  396. } catch (XmlException e) {
  397. throw new POIXMLException(e);
  398. }
  399. }
  400. /**
  401. * Not normally to be called externally, but possibly to be overridden to avoid
  402. * the DOM based parse of large sheets (see examples).
  403. *
  404. * @throws POIXMLException a RuntimeException that can be caused by invalid OOXML data
  405. * @throws IllegalStateException a number of other runtime exceptions can be thrown, especially if there are problems with the
  406. * input format
  407. */
  408. public void parseSheet(Map<String, XSSFSheet> shIdMap, CTSheet ctSheet) {
  409. XSSFSheet sh = shIdMap.get(ctSheet.getId());
  410. if(sh == null) {
  411. LOG.atWarn().log("Sheet with name {} and r:id {} was defined, but didn't exist in package, skipping", ctSheet.getName(), ctSheet.getId());
  412. return;
  413. }
  414. sh.sheet = ctSheet;
  415. sh.onDocumentRead();
  416. sheets.add(sh);
  417. }
  418. /**
  419. * Create a new CTWorkbook with all values set to default
  420. */
  421. private void onWorkbookCreate() {
  422. workbook = CTWorkbook.Factory.newInstance();
  423. // don't EVER use the 1904 date system
  424. CTWorkbookPr workbookPr = workbook.addNewWorkbookPr();
  425. workbookPr.setDate1904(false);
  426. setBookViewsIfMissing();
  427. workbook.addNewSheets();
  428. POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
  429. expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR);
  430. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, this.xssfFactory);
  431. stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, this.xssfFactory);
  432. stylesSource.setWorkbook(this);
  433. namedRanges = new ArrayList<>();
  434. namedRangesByName = new ArrayListValuedHashMap<>();
  435. sheets = new ArrayList<>();
  436. pivotTables = new ArrayList<>();
  437. externalLinks = new ArrayList<>();
  438. }
  439. private void setBookViewsIfMissing() {
  440. if(!workbook.isSetBookViews()) {
  441. CTBookViews bvs = workbook.addNewBookViews();
  442. CTBookView bv = bvs.addNewWorkbookView();
  443. bv.setActiveTab(0);
  444. }
  445. }
  446. /**
  447. * Create a new SpreadsheetML package and setup the default minimal content
  448. */
  449. protected static OPCPackage newPackage(XSSFWorkbookType workbookType) {
  450. OPCPackage pkg = null;
  451. try {
  452. pkg = OPCPackage.create(new UnsynchronizedByteArrayOutputStream()); // NOSONAR - we do not want to close this here
  453. // Main part
  454. PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
  455. // Create main part relationship
  456. pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
  457. // Create main document part
  458. pkg.createPart(corePartName, workbookType.getContentType());
  459. pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
  460. } catch (Exception e) {
  461. IOUtils.closeQuietly(pkg);
  462. throw new POIXMLException(e);
  463. }
  464. return pkg;
  465. }
  466. /**
  467. * Return the underlying XML bean
  468. *
  469. * @return the underlying CTWorkbook bean
  470. */
  471. @Internal
  472. public CTWorkbook getCTWorkbook() {
  473. return this.workbook;
  474. }
  475. /**
  476. * Adds a picture to the workbook.
  477. *
  478. * @param pictureData The bytes of the picture
  479. * @param format The format of the picture.
  480. *
  481. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  482. * @see Workbook#PICTURE_TYPE_EMF
  483. * @see Workbook#PICTURE_TYPE_WMF
  484. * @see Workbook#PICTURE_TYPE_PICT
  485. * @see Workbook#PICTURE_TYPE_JPEG
  486. * @see Workbook#PICTURE_TYPE_PNG
  487. * @see Workbook#PICTURE_TYPE_DIB
  488. * @see #getAllPictures()
  489. */
  490. @Override
  491. public int addPicture(byte[] pictureData, int format) {
  492. int imageNumber = getAllPictures().size() + 1;
  493. XSSFPictureData img = createRelationship(XSSFPictureData.RELATIONS[format], this.xssfFactory, imageNumber, true).getDocumentPart();
  494. try (OutputStream out = img.getPackagePart().getOutputStream()) {
  495. out.write(pictureData);
  496. } catch (IOException e) {
  497. throw new POIXMLException(e);
  498. }
  499. pictures.add(img);
  500. return imageNumber - 1;
  501. }
  502. /**
  503. * Adds a picture to the workbook.
  504. *
  505. * @param is The sream to read image from
  506. * @param format The format of the picture.
  507. *
  508. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  509. * @see Workbook#PICTURE_TYPE_EMF
  510. * @see Workbook#PICTURE_TYPE_WMF
  511. * @see Workbook#PICTURE_TYPE_PICT
  512. * @see Workbook#PICTURE_TYPE_JPEG
  513. * @see Workbook#PICTURE_TYPE_PNG
  514. * @see Workbook#PICTURE_TYPE_DIB
  515. * @see #getAllPictures()
  516. */
  517. public int addPicture(InputStream is, int format) throws IOException {
  518. int imageNumber = getAllPictures().size() + 1;
  519. XSSFPictureData img = createRelationship(XSSFPictureData.RELATIONS[format], this.xssfFactory, imageNumber, true).getDocumentPart();
  520. try (OutputStream out = img.getPackagePart().getOutputStream()) {
  521. IOUtils.copy(is, out);
  522. }
  523. pictures.add(img);
  524. return imageNumber - 1;
  525. }
  526. /**
  527. * Create an XSSFSheet from an existing sheet in the XSSFWorkbook.
  528. * The cloned sheet is a deep copy of the original.
  529. *
  530. * @param sheetNum The index of the sheet to clone
  531. * @return XSSFSheet representing the cloned sheet.
  532. * @throws IllegalArgumentException if the sheet index in invalid
  533. * @throws POIXMLException if there were errors when cloning
  534. */
  535. @Override
  536. public XSSFSheet cloneSheet(int sheetNum) {
  537. return cloneSheet(sheetNum, null);
  538. }
  539. @Override
  540. public void close() throws IOException {
  541. try {
  542. super.close();
  543. } finally {
  544. IOUtils.closeQuietly(sharedStringSource);
  545. }
  546. }
  547. /**
  548. * Create an XSSFSheet from an existing sheet in the XSSFWorkbook.
  549. * The cloned sheet is a deep copy of the original but with a new given
  550. * name.
  551. *
  552. * @param sheetNum The index of the sheet to clone
  553. * @param newName The name to set for the newly created sheet
  554. * @return XSSFSheet representing the cloned sheet.
  555. * @throws IllegalArgumentException if the sheet index or the sheet
  556. * name is invalid
  557. * @throws POIXMLException if there were errors when cloning
  558. */
  559. public XSSFSheet cloneSheet(int sheetNum, String newName) {
  560. validateSheetIndex(sheetNum);
  561. XSSFSheet srcSheet = sheets.get(sheetNum);
  562. if (newName == null) {
  563. String srcName = srcSheet.getSheetName();
  564. newName = getUniqueSheetName(srcName);
  565. } else {
  566. validateSheetName(newName);
  567. }
  568. XSSFSheet clonedSheet = createSheet(newName);
  569. // copy sheet's relations
  570. List<RelationPart> rels = srcSheet.getRelationParts();
  571. // if the sheet being cloned has a drawing then remember it and re-create it too
  572. XSSFDrawing dg = null;
  573. for(RelationPart rp : rels) {
  574. POIXMLDocumentPart r = rp.getDocumentPart();
  575. // do not copy the drawing relationship, it will be re-created
  576. if(r instanceof XSSFDrawing) {
  577. dg = (XSSFDrawing)r;
  578. continue;
  579. }
  580. addRelation(rp, clonedSheet);
  581. }
  582. try {
  583. for(PackageRelationship pr : srcSheet.getPackagePart().getRelationships()) {
  584. if (pr.getTargetMode() == TargetMode.EXTERNAL) {
  585. clonedSheet.getPackagePart().addExternalRelationship
  586. (pr.getTargetURI().toASCIIString(), pr.getRelationshipType(), pr.getId());
  587. }
  588. }
  589. } catch (InvalidFormatException e) {
  590. throw new POIXMLException("Failed to clone sheet", e);
  591. }
  592. try (UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream()) {
  593. srcSheet.write(out);
  594. try (InputStream bis = out.toInputStream()) {
  595. clonedSheet.read(bis);
  596. }
  597. } catch (IOException e){
  598. throw new POIXMLException("Failed to clone sheet", e);
  599. }
  600. CTWorksheet ct = clonedSheet.getCTWorksheet();
  601. if(ct.isSetLegacyDrawing()) {
  602. LOG.atWarn().log("Cloning sheets with comments is not yet supported.");
  603. ct.unsetLegacyDrawing();
  604. }
  605. if (ct.isSetPageSetup()) {
  606. LOG.atWarn().log("Cloning sheets with page setup is not yet supported.");
  607. ct.unsetPageSetup();
  608. }
  609. clonedSheet.setSelected(false);
  610. // clone the sheet drawing along with its relationships
  611. if (dg != null) {
  612. if(ct.isSetDrawing()) {
  613. // unset the existing reference to the drawing,
  614. // so that subsequent call of clonedSheet.createDrawingPatriarch() will create a new one
  615. ct.unsetDrawing();
  616. }
  617. XSSFDrawing clonedDg = clonedSheet.createDrawingPatriarch();
  618. // copy drawing contents
  619. clonedDg.getCTDrawing().set(dg.getCTDrawing().copy());
  620. // Clone drawing relations
  621. XSSFDrawing drawingPatriarch = srcSheet.getDrawingPatriarch();
  622. if (drawingPatriarch != null) {
  623. List<RelationPart> srcRels = drawingPatriarch.getRelationParts();
  624. for (RelationPart rp : srcRels) {
  625. POIXMLDocumentPart r = rp.getDocumentPart();
  626. if (r instanceof XSSFChart) {
  627. // Replace chart relation part with new relationship, cloning the chart's content
  628. RelationPart chartPart = clonedDg.createChartRelationPart();
  629. XSSFChart chart = chartPart.getDocumentPart();
  630. chart.importContent((XSSFChart) r);
  631. chart.replaceReferences(clonedSheet);
  632. } else {
  633. addRelation(rp, clonedDg);
  634. }
  635. }
  636. }
  637. }
  638. XSSFSheet.cloneTables(clonedSheet);
  639. return clonedSheet;
  640. }
  641. /**
  642. * Modified in POI 5.1.0 to only log issues with unknown relationship types
  643. * - see https://bz.apache.org/bugzilla/show_bug.cgi?id=64759
  644. *
  645. * @since 3.14-Beta1
  646. */
  647. private static boolean addRelation(RelationPart rp, POIXMLDocumentPart target) {
  648. PackageRelationship rel = rp.getRelationship();
  649. if (rel.getTargetMode() == TargetMode.EXTERNAL) {
  650. target.getPackagePart().addRelationship(
  651. rel.getTargetURI(), rel.getTargetMode(), rel.getRelationshipType(), rel.getId());
  652. } else {
  653. XSSFRelation xssfRel = XSSFRelation.getInstance(rel.getRelationshipType());
  654. if (xssfRel == null) {
  655. // Don't copy all relations blindly, but only the ones we know about
  656. LOG.atWarn().log("Can't clone sheet relationship (some data will be lost in the cloned sheet) - unknown relation type found: {}", rel.getRelationshipType());
  657. return false;
  658. }
  659. target.addRelation(rel.getId(), xssfRel, rp.getDocumentPart());
  660. }
  661. return true;
  662. }
  663. /**
  664. * Generate a valid sheet name based on the existing one. Used when cloning sheets.
  665. *
  666. * @param srcName the original sheet name to
  667. * @return clone sheet name
  668. */
  669. private String getUniqueSheetName(String srcName) {
  670. int uniqueIndex = 2;
  671. String baseName = srcName;
  672. int bracketPos = srcName.lastIndexOf('(');
  673. if (bracketPos > 0 && srcName.endsWith(")")) {
  674. String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length());
  675. try {
  676. uniqueIndex = Integer.parseInt(suffix.trim());
  677. uniqueIndex++;
  678. baseName = srcName.substring(0, bracketPos).trim();
  679. } catch (NumberFormatException e) {
  680. // contents of brackets not numeric
  681. }
  682. }
  683. while (true) {
  684. // Try and find the next sheet name that is unique
  685. String index = Integer.toString(uniqueIndex++);
  686. String name;
  687. if (baseName.length() + index.length() + 2 < MAX_SENSITIVE_SHEET_NAME_LEN) {
  688. name = baseName + " (" + index + ")";
  689. } else {
  690. name = baseName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN - index.length() - 2) + "(" + index + ")";
  691. }
  692. //If the sheet name is unique, then set it otherwise move on to the next number.
  693. if (getSheetIndex(name) == -1) {
  694. return name;
  695. }
  696. }
  697. }
  698. /**
  699. * Create a new XSSFCellStyle and add it to the workbook's style table
  700. *
  701. * @return the new XSSFCellStyle object
  702. */
  703. @Override
  704. public XSSFCellStyle createCellStyle() {
  705. return stylesSource.createCellStyle();
  706. }
  707. /**
  708. * Returns the workbook's data format table (a factory for creating data format strings).
  709. *
  710. * @return the XSSFDataFormat object
  711. * @see DataFormat
  712. */
  713. @Override
  714. public XSSFDataFormat createDataFormat() {
  715. if (formatter == null) {
  716. formatter = new XSSFDataFormat(stylesSource);
  717. }
  718. return formatter;
  719. }
  720. /**
  721. * Create a new Font and add it to the workbook's font table
  722. *
  723. * @return new font object
  724. */
  725. @Override
  726. public XSSFFont createFont() {
  727. XSSFFont font = new XSSFFont();
  728. font.registerTo(stylesSource);
  729. return font;
  730. }
  731. @Override
  732. public XSSFName createName() {
  733. CTDefinedName ctName = CTDefinedName.Factory.newInstance();
  734. ctName.setName("");
  735. return createAndStoreName(ctName);
  736. }
  737. private XSSFName createAndStoreName(CTDefinedName ctName) {
  738. XSSFName name = new XSSFName(ctName, this);
  739. namedRanges.add(name);
  740. namedRangesByName.put(ctName.getName().toLowerCase(Locale.ENGLISH), name);
  741. return name;
  742. }
  743. /**
  744. * Create an XSSFSheet for this workbook, adds it to the sheets and returns
  745. * the high level representation. Use this to create new sheets.
  746. *
  747. * @return XSSFSheet representing the new sheet.
  748. */
  749. @Override
  750. public XSSFSheet createSheet() {
  751. String sheetname = "Sheet" + (sheets.size());
  752. int idx = 0;
  753. while(getSheet(sheetname) != null) {
  754. sheetname = "Sheet" + idx;
  755. idx++;
  756. }
  757. return createSheet(sheetname);
  758. }
  759. /**
  760. * Create a new sheet for this Workbook and return the high level representation.
  761. * Use this to create new sheets.
  762. * <p>
  763. * Note that Excel allows sheet names up to 31 chars in length but other applications
  764. * (such as OpenOffice) allow more. Some versions of Excel crash with names longer than 31 chars,
  765. * others - truncate such names to 31 character.
  766. * <p>
  767. * POI's SpreadsheetAPI silently truncates the input argument to 31 characters.
  768. * Example:
  769. *
  770. * <pre>{@code
  771. * Sheet sheet = workbook.createSheet("My very long sheet name which is longer than 31 chars"); // will be truncated
  772. * assert 31 == sheet.getSheetName().length();
  773. * assert "My very long sheet name which i" == sheet.getSheetName();
  774. * }</pre>
  775. *
  776. * Except the 31-character constraint, Excel applies some other rules:
  777. * <p>
  778. * Sheet name MUST be unique in the workbook and MUST NOT contain the any of the following characters:
  779. * <ul>
  780. * <li> 0x0000 </li>
  781. * <li> 0x0003 </li>
  782. * <li> colon (:) </li>
  783. * <li> backslash (\) </li>
  784. * <li> asterisk (*) </li>
  785. * <li> question mark (?) </li>
  786. * <li> forward slash (/) </li>
  787. * <li> opening square bracket ([) </li>
  788. * <li> closing square bracket (]) </li>
  789. * </ul>
  790. * The string MUST NOT begin or end with the single quote (') character.
  791. * <p>
  792. * See {@link WorkbookUtil#createSafeSheetName(String nameProposal)}
  793. * for a safe way to create valid names
  794. *
  795. * @param sheetname sheetname to set for the sheet.
  796. * @return Sheet representing the new sheet.
  797. * @throws IllegalArgumentException if the name is null or invalid
  798. * or workbook already contains a sheet with this name
  799. * @see WorkbookUtil#createSafeSheetName(String nameProposal)
  800. */
  801. @Override
  802. public XSSFSheet createSheet(String sheetname) {
  803. if (sheetname == null) {
  804. throw new IllegalArgumentException("sheetName must not be null");
  805. }
  806. validateSheetName(sheetname);
  807. // YK: Mimic Excel and silently truncate sheet names longer than 31 characters
  808. // Issue a WARNING though in order to prevent a situation, where the provided long sheet name is
  809. // not accessible due to the trimming while we are not even aware of the reason and continue to use
  810. // the long name in generated formulas
  811. if(sheetname.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  812. String trimmedSheetname = sheetname.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  813. // we still need to warn about the trimming as the original sheet name won't be available
  814. // e.g. when referenced by formulas
  815. LOG.atWarn().log("Sheet '{}' will be added with a trimmed name '{}' for MS Excel compliance.",
  816. sheetname, trimmedSheetname);
  817. sheetname = trimmedSheetname;
  818. }
  819. WorkbookUtil.validateSheetName(sheetname);
  820. CTSheet sheet = addSheet(sheetname);
  821. int sheetNumber = 1;
  822. outerloop:
  823. while(true) {
  824. for(XSSFSheet sh : sheets) {
  825. sheetNumber = (int)Math.max(sh.sheet.getSheetId() + 1, sheetNumber);
  826. }
  827. // Bug 57165: We also need to check that the resulting file name is not already taken
  828. // this can happen when moving/cloning sheets
  829. String sheetName = XSSFRelation.WORKSHEET.getFileName(sheetNumber);
  830. for(POIXMLDocumentPart relation : getRelations()) {
  831. if(relation.getPackagePart() != null &&
  832. sheetName.equals(relation.getPackagePart().getPartName().getName())) {
  833. // name is taken => try next one
  834. sheetNumber++;
  835. continue outerloop;
  836. }
  837. }
  838. // no duplicate found => use this one
  839. break;
  840. }
  841. RelationPart rp = createRelationship(XSSFRelation.WORKSHEET, this.xssfFactory, sheetNumber, false);
  842. XSSFSheet wrapper = rp.getDocumentPart();
  843. wrapper.sheet = sheet;
  844. sheet.setId(rp.getRelationship().getId());
  845. sheet.setSheetId(sheetNumber);
  846. if (sheets.isEmpty()) {
  847. wrapper.setSelected(true);
  848. }
  849. sheets.add(wrapper);
  850. return wrapper;
  851. }
  852. private void validateSheetName(final String sheetName) throws IllegalArgumentException {
  853. if (containsSheet( sheetName, sheets.size() )) {
  854. throw new IllegalArgumentException("The workbook already contains a sheet named '" + sheetName + "'");
  855. }
  856. }
  857. protected XSSFDialogsheet createDialogsheet(String sheetname, CTDialogsheet dialogsheet) {
  858. XSSFSheet sheet = createSheet(sheetname);
  859. return new XSSFDialogsheet(sheet);
  860. }
  861. private CTSheet addSheet(String sheetname) {
  862. CTSheet sheet = workbook.getSheets().addNewSheet();
  863. sheet.setName(sheetname);
  864. return sheet;
  865. }
  866. /**
  867. * Finds a font that matches the one with the supplied attributes
  868. *
  869. * @return the font with the matched attributes or {@code null}
  870. */
  871. @Override
  872. public XSSFFont findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  873. return stylesSource.findFont(bold, color, fontHeight, name, italic, strikeout, typeOffset, underline);
  874. }
  875. /**
  876. * Convenience method to get the active sheet. The active sheet is is the sheet
  877. * which is currently displayed when the workbook is viewed in Excel.
  878. * 'Selected' sheet(s) is a distinct concept.
  879. */
  880. @Override
  881. public int getActiveSheetIndex() {
  882. //activeTab (Active Sheet Index) Specifies an unsignedInt
  883. //that contains the index to the active sheet in this book view.
  884. return (int)workbook.getBookViews().getWorkbookViewArray(0).getActiveTab();
  885. }
  886. /**
  887. * Gets all pictures from the Workbook.
  888. *
  889. * @return the list of pictures (a list of {@link XSSFPictureData} objects.)
  890. * @see #addPicture(byte[], int)
  891. */
  892. @Override
  893. public List<XSSFPictureData> getAllPictures() {
  894. if(pictures == null){
  895. List<PackagePart> mediaParts = getPackage().getPartsByName(GET_ALL_PICTURES_PATTERN);
  896. pictures = new ArrayList<>(mediaParts.size());
  897. for(PackagePart part : mediaParts){
  898. pictures.add(new XSSFPictureData(part));
  899. }
  900. }
  901. return pictures; //YK: should return Collections.unmodifiableList(pictures);
  902. }
  903. /**
  904. * Get the cell style object at the given index
  905. *
  906. * @param idx index within the set of styles
  907. * @return XSSFCellStyle object at the index
  908. */
  909. @Override
  910. public XSSFCellStyle getCellStyleAt(int idx) {
  911. return stylesSource.getStyleAt(idx);
  912. }
  913. @Override
  914. public XSSFFont getFontAt(int idx) {
  915. return stylesSource.getFontAt(idx);
  916. }
  917. /**
  918. * Get the first named range with the given name.
  919. *
  920. * Note: names of named ranges are not unique as they are scoped by sheet.
  921. * {@link #getNames(String name)} returns all named ranges with the given name.
  922. *
  923. * @param name named range name
  924. * @return XSSFName with the given name.
  925. * {@code null} is returned no named range could be found.
  926. */
  927. @Override
  928. public XSSFName getName(String name) {
  929. Collection<XSSFName> list = getNames(name);
  930. if (list.isEmpty()) {
  931. return null;
  932. }
  933. return list.iterator().next();
  934. }
  935. /**
  936. * Get the named ranges with the given name.
  937. * <i>Note:</i>Excel named ranges are case-insensitive and
  938. * this method performs a case-insensitive search.
  939. *
  940. * @param name named range name
  941. * @return list of XSSFNames with the given name. An empty list if no named ranges could be found
  942. */
  943. @Override
  944. public List<XSSFName> getNames(String name) {
  945. return Collections.unmodifiableList(namedRangesByName.get(name.toLowerCase(Locale.ENGLISH)));
  946. }
  947. /**
  948. * Get the named range at the given index. No longer public and only used in tests.
  949. *
  950. * @param nameIndex the index of the named range
  951. * @return the XSSFName at the given index
  952. */
  953. @Deprecated
  954. XSSFName getNameAt(int nameIndex) {
  955. int nNames = namedRanges.size();
  956. if (nNames < 1) {
  957. throw new IllegalStateException("There are no defined names in this workbook");
  958. }
  959. if (nameIndex < 0 || nameIndex > nNames) {
  960. throw new IllegalArgumentException("Specified name index " + nameIndex
  961. + " is outside the allowable range (0.." + (nNames-1) + ").");
  962. }
  963. return namedRanges.get(nameIndex);
  964. }
  965. /**
  966. * Get a list of all the named ranges in the workbook.
  967. *
  968. * @return list of XSSFNames in the workbook
  969. */
  970. @Override
  971. public List<XSSFName> getAllNames() {
  972. return Collections.unmodifiableList(namedRanges);
  973. }
  974. /**
  975. * Gets the named range index by name. No longer public and only used in tests.
  976. *
  977. * @param name named range name
  978. * @return named range index. {@code -1} is returned if no named ranges could be found.
  979. *
  980. * @deprecated 3.16. New projects should avoid accessing named ranges by index.
  981. * Use {@link #getName(String)} instead.
  982. */
  983. @Deprecated
  984. int getNameIndex(String name) {
  985. XSSFName nm = getName(name);
  986. if (nm != null) {
  987. return namedRanges.indexOf(nm);
  988. }
  989. return -1;
  990. }
  991. /**
  992. * Get the number of styles the workbook contains
  993. *
  994. * @return count of cell styles
  995. */
  996. @Override
  997. public int getNumCellStyles() {
  998. return stylesSource.getNumCellStyles();
  999. }
  1000. @Override
  1001. public int getNumberOfFonts() {
  1002. return stylesSource.getFonts().size();
  1003. }
  1004. @Override
  1005. @Deprecated
  1006. @Removal(version = "6.0.0")
  1007. public int getNumberOfFontsAsInt() {
  1008. return getNumberOfFonts();
  1009. }
  1010. /**
  1011. * Get the number of named ranges in the this workbook
  1012. *
  1013. * @return number of named ranges
  1014. */
  1015. @Override
  1016. public int getNumberOfNames() {
  1017. return namedRanges.size();
  1018. }
  1019. /**
  1020. * Get the number of worksheets in the this workbook
  1021. *
  1022. * @return number of worksheets
  1023. */
  1024. @Override
  1025. public int getNumberOfSheets() {
  1026. return sheets.size();
  1027. }
  1028. /**
  1029. * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
  1030. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  1031. * @return String Null if no print area has been defined
  1032. */
  1033. @Override
  1034. public String getPrintArea(int sheetIndex) {
  1035. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1036. if (name == null) {
  1037. return null;
  1038. }
  1039. //adding one here because 0 indicates a global named region; doesnt make sense for print areas
  1040. return name.getRefersToFormula();
  1041. }
  1042. /**
  1043. * Get sheet with the given name (case insensitive match)
  1044. *
  1045. * @param name of the sheet
  1046. * @return XSSFSheet with the name provided or {@code null} if it does not exist
  1047. */
  1048. @Override
  1049. public XSSFSheet getSheet(String name) {
  1050. for (XSSFSheet sheet : sheets) {
  1051. if (name.equalsIgnoreCase(sheet.getSheetName())) {
  1052. return sheet;
  1053. }
  1054. }
  1055. return null;
  1056. }
  1057. /**
  1058. * Get the XSSFSheet object at the given index.
  1059. *
  1060. * @param index of the sheet number (0-based physical &amp; logical)
  1061. * @return XSSFSheet at the provided index
  1062. * @throws IllegalArgumentException if the index is out of range (index
  1063. * &lt; 0 || index &gt;= getNumberOfSheets()).
  1064. */
  1065. @Override
  1066. public XSSFSheet getSheetAt(int index) {
  1067. validateSheetIndex(index);
  1068. return sheets.get(index);
  1069. }
  1070. /**
  1071. * Returns the index of the sheet by his name (case insensitive match)
  1072. *
  1073. * @param name the sheet name
  1074. * @return index of the sheet (0 based) or {@code -1} if not found
  1075. */
  1076. @Override
  1077. public int getSheetIndex(String name) {
  1078. int idx = 0;
  1079. for (XSSFSheet sh : sheets) {
  1080. if (name.equalsIgnoreCase(sh.getSheetName())) {
  1081. return idx;
  1082. }
  1083. idx++;
  1084. }
  1085. return -1;
  1086. }
  1087. /**
  1088. * Returns the index of the given sheet
  1089. *
  1090. * @param sheet the sheet to look up
  1091. * @return index of the sheet (0 based). {@code -1} if not found
  1092. */
  1093. @Override
  1094. public int getSheetIndex(Sheet sheet) {
  1095. int idx = 0;
  1096. for(XSSFSheet sh : sheets){
  1097. if(sh == sheet) {
  1098. return idx;
  1099. }
  1100. idx++;
  1101. }
  1102. return -1;
  1103. }
  1104. /**
  1105. * Get the sheet name
  1106. *
  1107. * @param sheetIx Number
  1108. * @return Sheet name
  1109. */
  1110. @Override
  1111. public String getSheetName(int sheetIx) {
  1112. validateSheetIndex(sheetIx);
  1113. return sheets.get(sheetIx).getSheetName();
  1114. }
  1115. /**
  1116. * Returns an iterator of the sheets in the workbook
  1117. * in sheet order. Includes hidden and very hidden sheets.
  1118. *
  1119. * Note: remove() is not supported on this iterator.
  1120. * Use {@link #removeSheetAt(int)} to remove sheets instead.
  1121. *
  1122. * @return an iterator of the sheets.
  1123. */
  1124. @Override
  1125. public Iterator<Sheet> sheetIterator() {
  1126. return new SheetIterator<>();
  1127. }
  1128. /**
  1129. * Alias for {@link #sheetIterator()} to allow
  1130. * foreach loops
  1131. *
  1132. * Note: remove() is not supported on this iterator.
  1133. * Use {@link #removeSheetAt(int)} to remove sheets instead.
  1134. *
  1135. * @return an iterator of the sheets.
  1136. */
  1137. @Override
  1138. public Iterator<Sheet> iterator() {
  1139. return sheetIterator();
  1140. }
  1141. /**
  1142. * Returns a spliterator of the sheets in the workbook
  1143. * in sheet order. Includes hidden and very hidden sheets.
  1144. *
  1145. * @return a spliterator of the sheets.
  1146. *
  1147. * @since POI 5.2.0
  1148. */
  1149. @Override
  1150. @SuppressWarnings("unchecked")
  1151. public Spliterator<Sheet> spliterator() {
  1152. return (Spliterator<Sheet>)(Spliterator<? extends Sheet>) sheets.spliterator();
  1153. }
  1154. private final class SheetIterator<T extends Sheet> implements Iterator<T> {
  1155. final private Iterator<T> it;
  1156. @SuppressWarnings("unchecked")
  1157. public SheetIterator() {
  1158. it = (Iterator<T>) sheets.iterator();
  1159. }
  1160. @Override
  1161. public boolean hasNext() {
  1162. return it.hasNext();
  1163. }
  1164. @Override
  1165. public T next() throws NoSuchElementException {
  1166. return it.next();
  1167. }
  1168. /**
  1169. * Unexpected behavior may occur if sheets are reordered after iterator
  1170. * has been created. Support for the remove method may be added in the future
  1171. * if someone can figure out a reliable implementation.
  1172. */
  1173. @Override
  1174. public void remove() throws IllegalStateException {
  1175. throw new UnsupportedOperationException("remove method not supported on XSSFWorkbook.iterator(). "+
  1176. "Use Sheet.removeSheetAt(int) instead.");
  1177. }
  1178. }
  1179. /**
  1180. * Are we a normal workbook (.xlsx), or a
  1181. * macro enabled workbook (.xlsm)?
  1182. */
  1183. public boolean isMacroEnabled() {
  1184. return getPackagePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType());
  1185. }
  1186. /**
  1187. * @param name the name to remove.
  1188. *
  1189. * @throws IllegalArgumentException if the named range is not a part of this XSSFWorkbook
  1190. */
  1191. @Override
  1192. public void removeName(Name name) {
  1193. if (!namedRangesByName.removeMapping(name.getNameName().toLowerCase(Locale.ENGLISH), name)
  1194. || !namedRanges.remove(name)) {
  1195. throw new IllegalArgumentException("Name was not found: " + name);
  1196. }
  1197. }
  1198. void updateName(XSSFName name, String oldName) {
  1199. if (!namedRangesByName.removeMapping(oldName.toLowerCase(Locale.ENGLISH), name)) {
  1200. throw new IllegalArgumentException("Name was not found: " + name);
  1201. }
  1202. namedRangesByName.put(name.getNameName().toLowerCase(Locale.ENGLISH), name);
  1203. }
  1204. /**
  1205. * Delete the printarea for the sheet specified
  1206. *
  1207. * @param sheetIndex 0-based sheet index (0 = First Sheet)
  1208. */
  1209. @Override
  1210. public void removePrintArea(int sheetIndex) {
  1211. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1212. if (name != null) {
  1213. removeName(name);
  1214. }
  1215. }
  1216. /**
  1217. * Removes sheet at the given index.<p>
  1218. *
  1219. * Care must be taken if the removed sheet is the currently active or only selected sheet in
  1220. * the workbook. There are a few situations when Excel must have a selection and/or active
  1221. * sheet. (For example when printing - see Bug 40414).<br>
  1222. *
  1223. * This method makes sure that if the removed sheet was active, another sheet will become
  1224. * active in its place. Furthermore, if the removed sheet was the only selected sheet, another
  1225. * sheet will become selected. The newly active/selected sheet will have the same index, or
  1226. * one less if the removed sheet was the last in the workbook.
  1227. *
  1228. * @param index of the sheet (0-based)
  1229. */
  1230. @Override
  1231. public void removeSheetAt(int index) {
  1232. validateSheetIndex(index);
  1233. onSheetDelete(index);
  1234. XSSFSheet sheet = getSheetAt(index);
  1235. removeRelation(sheet);
  1236. sheets.remove(index);
  1237. // only set new sheet if there are still some left
  1238. if(sheets.isEmpty()) {
  1239. return;
  1240. }
  1241. // the index of the closest remaining sheet to the one just deleted
  1242. int newSheetIndex = index;
  1243. if (newSheetIndex >= sheets.size()) {
  1244. newSheetIndex = sheets.size()-1;
  1245. }
  1246. // adjust active sheet
  1247. int active = getActiveSheetIndex();
  1248. if(active == index) {
  1249. // removed sheet was the active one, reset active sheet if there is still one left now
  1250. setActiveSheet(newSheetIndex);
  1251. } else if (active > index) {
  1252. // removed sheet was below the active one => active is one less now
  1253. setActiveSheet(active-1);
  1254. }
  1255. }
  1256. /**
  1257. * Gracefully remove references to the sheet being deleted
  1258. *
  1259. * @param index the 0-based index of the sheet to delete
  1260. */
  1261. private void onSheetDelete(int index) {
  1262. // remove all sheet relations
  1263. final XSSFSheet sheet = getSheetAt(index);
  1264. sheet.onSheetDelete();
  1265. //delete the CTSheet reference from workbook.xml
  1266. workbook.getSheets().removeSheet(index);
  1267. //calculation chain is auxiliary, remove it as it may contain orphan references to deleted cells
  1268. if(calcChain != null) {
  1269. removeRelation(calcChain);
  1270. calcChain = null;
  1271. }
  1272. //adjust indices of names ranges
  1273. List<XSSFName> toRemove = new ArrayList<>();
  1274. for (XSSFName nm : namedRanges) {
  1275. CTDefinedName ct = nm.getCTName();
  1276. if(!ct.isSetLocalSheetId()) {
  1277. continue;
  1278. }
  1279. if (ct.getLocalSheetId() == index) {
  1280. toRemove.add(nm);
  1281. } else if (ct.getLocalSheetId() > index){
  1282. // Bump down by one, so still points at the same sheet
  1283. ct.setLocalSheetId(ct.getLocalSheetId()-1);
  1284. }
  1285. }
  1286. for (XSSFName nm : toRemove) {
  1287. removeName(nm);
  1288. }
  1289. }
  1290. /**
  1291. * Retrieves the current policy on what to do when
  1292. * getting missing or blank cells from a row.
  1293. * The default is to return blank and null cells.
  1294. * {@link MissingCellPolicy}
  1295. */
  1296. @Override
  1297. public MissingCellPolicy getMissingCellPolicy() {
  1298. return _missingCellPolicy;
  1299. }
  1300. /**
  1301. * Sets the policy on what to do when
  1302. * getting missing or blank cells from a row.
  1303. * This will then apply to all calls to
  1304. * {@link Row#getCell(int)}}. See
  1305. * {@link MissingCellPolicy}
  1306. */
  1307. @Override
  1308. public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
  1309. _missingCellPolicy = missingCellPolicy;
  1310. }
  1311. /**
  1312. * Convenience method to set the active sheet. The active sheet is is the sheet
  1313. * which is currently displayed when the workbook is viewed in Excel.
  1314. * 'Selected' sheet(s) is a distinct concept.
  1315. */
  1316. @Override
  1317. public void setActiveSheet(int index) {
  1318. validateSheetIndex(index);
  1319. for (CTBookView arrayBook : workbook.getBookViews().getWorkbookViewArray()) {
  1320. arrayBook.setActiveTab(index);
  1321. }
  1322. }
  1323. /**
  1324. * Validate sheet index
  1325. *
  1326. * @param index the index to validate
  1327. * @throws IllegalArgumentException if the index is out of range (index
  1328. * &lt; 0 || index &gt;= getNumberOfSheets()).
  1329. */
  1330. private void validateSheetIndex(int index) {
  1331. int lastSheetIx = sheets.size() - 1;
  1332. if (index < 0 || index > lastSheetIx) {
  1333. String range = "(0.." + lastSheetIx + ")";
  1334. if (lastSheetIx == -1) {
  1335. range = "(no sheets)";
  1336. }
  1337. throw new IllegalArgumentException("Sheet index ("
  1338. + index +") is out of range " + range);
  1339. }
  1340. }
  1341. /**
  1342. * Gets the first tab that is displayed in the list of tabs in excel.
  1343. *
  1344. * @return integer that contains the index to the active sheet in this book view.
  1345. */
  1346. @Override
  1347. public int getFirstVisibleTab() {
  1348. CTBookViews bookViews = workbook.getBookViews();
  1349. CTBookView bookView = bookViews.getWorkbookViewArray(0);
  1350. return (short) bookView.getFirstSheet();
  1351. }
  1352. /**
  1353. * Sets the first tab that is displayed in the list of tabs in excel.
  1354. *
  1355. * @param index integer that contains the index to the active sheet in this book view.
  1356. */
  1357. @Override
  1358. public void setFirstVisibleTab(int index) {
  1359. CTBookViews bookViews = workbook.getBookViews();
  1360. CTBookView bookView= bookViews.getWorkbookViewArray(0);
  1361. bookView.setFirstSheet(index);
  1362. }
  1363. /**
  1364. * Sets the printarea for the sheet provided
  1365. * <p>
  1366. * i.e. Reference = $A$1:$B$2
  1367. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  1368. * @param reference Valid name Reference for the Print Area
  1369. */
  1370. @Override
  1371. public void setPrintArea(int sheetIndex, String reference) {
  1372. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1373. if (name == null) {
  1374. name = createBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1375. }
  1376. //short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
  1377. //name.setExternSheetNumber(externSheetIndex);
  1378. String[] parts = COMMA_PATTERN.split(reference);
  1379. StringBuilder sb = new StringBuilder(32);
  1380. for (int i = 0; i < parts.length; i++) {
  1381. if(i>0) {
  1382. sb.append(',');
  1383. }
  1384. SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
  1385. sb.append('!');
  1386. sb.append(parts[i]);
  1387. }
  1388. name.setRefersToFormula(sb.toString());
  1389. }
  1390. /**
  1391. * For the Convenience of Java Programmers maintaining pointers.
  1392. * @see #setPrintArea(int, String)
  1393. * @param sheetIndex Zero-based sheet index (0 = First Sheet)
  1394. * @param startColumn Column to begin printarea
  1395. * @param endColumn Column to end the printarea
  1396. * @param startRow Row to begin the printarea
  1397. * @param endRow Row to end the printarea
  1398. */
  1399. @Override
  1400. public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) {
  1401. String reference=getReferencePrintArea(getSheetName(sheetIndex), startColumn, endColumn, startRow, endRow);
  1402. setPrintArea(sheetIndex, reference);
  1403. }
  1404. @Override
  1405. public CellReferenceType getCellReferenceType() {
  1406. final CTCalcPr calcPr = getCTWorkbook().getCalcPr();
  1407. if (calcPr == null) {
  1408. return CellReferenceType.UNKNOWN;
  1409. } else if (calcPr.getRefMode() == R_1_C_1) {
  1410. return CellReferenceType.R1C1;
  1411. } else if (calcPr.getRefMode() == A_1) {
  1412. return CellReferenceType.A1;
  1413. }
  1414. return CellReferenceType.UNKNOWN;
  1415. }
  1416. @Override
  1417. public void setCellReferenceType(CellReferenceType cellReferenceType) {
  1418. CTCalcPr calcPr = getCTWorkbook().getCalcPr();
  1419. if (cellReferenceType == CellReferenceType.UNKNOWN) {
  1420. if (calcPr != null) {
  1421. calcPr.unsetRefMode();
  1422. }
  1423. } else {
  1424. if (calcPr == null) calcPr = getCTWorkbook().addNewCalcPr();
  1425. STRefMode.Enum refMode = cellReferenceType == CellReferenceType.R1C1 ? R_1_C_1 : A_1;
  1426. calcPr.setRefMode(refMode);
  1427. }
  1428. }
  1429. private static String getReferencePrintArea(String sheetName, int startC, int endC, int startR, int endR) {
  1430. //windows excel example: Sheet1!$C$3:$E$4
  1431. CellReference colRef = new CellReference(sheetName, startR, startC, true, true);
  1432. CellReference colRef2 = new CellReference(sheetName, endR, endC, true, true);
  1433. return "$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1];
  1434. }
  1435. XSSFName getBuiltInName(String builtInCode, int sheetNumber) {
  1436. for (XSSFName name : namedRangesByName.get(builtInCode.toLowerCase(Locale.ENGLISH))) {
  1437. if (name.getSheetIndex() == sheetNumber) {
  1438. return name;
  1439. }
  1440. }
  1441. return null;
  1442. }
  1443. /**
  1444. * Generates a NameRecord to represent a built-in region
  1445. *
  1446. * @return a new NameRecord
  1447. * @throws IllegalArgumentException if sheetNumber is invalid
  1448. * @throws POIXMLException if such a name already exists in the workbook
  1449. */
  1450. XSSFName createBuiltInName(String builtInName, int sheetNumber) {
  1451. validateSheetIndex(sheetNumber);
  1452. CTDefinedNames names = workbook.getDefinedNames() == null ? workbook.addNewDefinedNames() : workbook.getDefinedNames();
  1453. CTDefinedName nameRecord = names.addNewDefinedName();
  1454. nameRecord.setName(builtInName);
  1455. nameRecord.setLocalSheetId(sheetNumber);
  1456. if (getBuiltInName(builtInName, sheetNumber) != null) {
  1457. throw new POIXMLException("Builtin (" + builtInName
  1458. + ") already exists for sheet (" + sheetNumber + ")");
  1459. }
  1460. return createAndStoreName(nameRecord);
  1461. }
  1462. /**
  1463. * We only set one sheet as selected for compatibility with HSSF.
  1464. */
  1465. @Override
  1466. public void setSelectedTab(int index) {
  1467. int idx = 0;
  1468. for (XSSFSheet sh : sheets) {
  1469. sh.setSelected(idx == index);
  1470. idx++;
  1471. }
  1472. }
  1473. /**
  1474. * Set the sheet name.
  1475. *
  1476. * @param sheetIndex sheet number (0 based)
  1477. * @param sheetname the new sheet name
  1478. * @throws IllegalArgumentException if the name is null or invalid
  1479. * or workbook already contains a sheet with this name
  1480. * @see #createSheet(String)
  1481. * @see WorkbookUtil#createSafeSheetName(String nameProposal)
  1482. */
  1483. @Override
  1484. public void setSheetName(int sheetIndex, String sheetname) {
  1485. if (sheetname == null) {
  1486. throw new IllegalArgumentException( "sheetName must not be null" );
  1487. }
  1488. validateSheetIndex(sheetIndex);
  1489. String oldSheetName = getSheetName(sheetIndex);
  1490. // YK: Mimic Excel and silently truncate sheet names longer than 31 characters
  1491. if(sheetname.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1492. sheetname = sheetname.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1493. }
  1494. WorkbookUtil.validateSheetName(sheetname);
  1495. // Do nothing if no change
  1496. if (sheetname.equals(oldSheetName)) {
  1497. return;
  1498. }
  1499. // Check it isn't already taken
  1500. if (containsSheet(sheetname, sheetIndex )) {
  1501. throw new IllegalArgumentException( "The workbook already contains a sheet of this name" );
  1502. }
  1503. // Update references to the name
  1504. XSSFFormulaUtils utils = new XSSFFormulaUtils(this);
  1505. utils.updateSheetName(sheetIndex, oldSheetName, sheetname);
  1506. workbook.getSheets().getSheetArray(sheetIndex).setName(sheetname);
  1507. }
  1508. /**
  1509. * sets the order of appearance for a given sheet.
  1510. *
  1511. * @param sheetname the name of the sheet to reorder
  1512. * @param pos the position that we want to insert the sheet into (0 based)
  1513. */
  1514. @Override
  1515. public void setSheetOrder(String sheetname, int pos) {
  1516. int idx = getSheetIndex(sheetname);
  1517. sheets.add(pos, sheets.remove(idx));
  1518. // Reorder CTSheets
  1519. CTSheets ct = workbook.getSheets();
  1520. XmlObject cts = ct.getSheetArray(idx).copy();
  1521. workbook.getSheets().removeSheet(idx);
  1522. CTSheet newcts = ct.insertNewSheet(pos);
  1523. newcts.set(cts);
  1524. //notify sheets
  1525. CTSheet[] sheetArray = ct.getSheetArray();
  1526. for(int i=0; i < sheetArray.length; i++) {
  1527. sheets.get(i).sheet = sheetArray[i];
  1528. }
  1529. updateNamedRangesAfterSheetReorder(idx, pos);
  1530. updateActiveSheetAfterSheetReorder(idx, pos);
  1531. }
  1532. /**
  1533. * update sheet-scoped named ranges in this workbook after changing the sheet order
  1534. * of a sheet at oldIndex to newIndex.
  1535. * Sheets between these indices will move left or right by 1.
  1536. *
  1537. * @param oldIndex the original index of the re-ordered sheet
  1538. * @param newIndex the new index of the re-ordered sheet
  1539. */
  1540. private void updateNamedRangesAfterSheetReorder(int oldIndex, int newIndex) {
  1541. // update sheet index of sheet-scoped named ranges
  1542. for (final XSSFName name : namedRanges) {
  1543. final int i = name.getSheetIndex();
  1544. // name has sheet-level scope
  1545. if (i != -1) {
  1546. // name refers to this sheet
  1547. if (i == oldIndex) {
  1548. name.setSheetIndex(newIndex);
  1549. }
  1550. // if oldIndex > newIndex then this sheet moved left and sheets between newIndex and oldIndex moved right
  1551. else if (newIndex <= i && i < oldIndex) {
  1552. name.setSheetIndex(i+1);
  1553. }
  1554. // if oldIndex < newIndex then this sheet moved right and sheets between oldIndex and newIndex moved left
  1555. else if (oldIndex < i && i <= newIndex) {
  1556. name.setSheetIndex(i-1);
  1557. }
  1558. }
  1559. }
  1560. }
  1561. private void updateActiveSheetAfterSheetReorder(int oldIndex, int newIndex) {
  1562. // adjust active sheet if necessary
  1563. int active = getActiveSheetIndex();
  1564. if(active == oldIndex) {
  1565. // moved sheet was the active one
  1566. setActiveSheet(newIndex);
  1567. } else if ((active < oldIndex && active < newIndex) ||
  1568. (active > oldIndex && active > newIndex)) {
  1569. // not affected
  1570. } else if (newIndex > oldIndex) {
  1571. // moved sheet was below before and is above now => active is one less
  1572. setActiveSheet(active-1);
  1573. } else {
  1574. // remaining case: moved sheet was higher than active before and is lower now => active is one more
  1575. setActiveSheet(active+1);
  1576. }
  1577. }
  1578. /**
  1579. * marshal named ranges from the {@link #namedRanges} collection to the underlying CTWorkbook bean
  1580. */
  1581. private void saveNamedRanges(){
  1582. // Named ranges
  1583. if (!namedRanges.isEmpty()) {
  1584. CTDefinedNames names = CTDefinedNames.Factory.newInstance();
  1585. CTDefinedName[] nr = new CTDefinedName[namedRanges.size()];
  1586. int i = 0;
  1587. for(XSSFName name : namedRanges) {
  1588. nr[i] = name.getCTName();
  1589. i++;
  1590. }
  1591. names.setDefinedNameArray(nr);
  1592. if(workbook.isSetDefinedNames()) {
  1593. workbook.unsetDefinedNames();
  1594. }
  1595. workbook.setDefinedNames(names);
  1596. // Re-process the named ranges
  1597. reprocessNamedRanges();
  1598. } else {
  1599. if(workbook.isSetDefinedNames()) {
  1600. workbook.unsetDefinedNames();
  1601. }
  1602. }
  1603. }
  1604. private void reprocessNamedRanges() {
  1605. namedRangesByName = new ArrayListValuedHashMap<>();
  1606. namedRanges = new ArrayList<>();
  1607. if(workbook.isSetDefinedNames()) {
  1608. for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) {
  1609. createAndStoreName(ctName);
  1610. }
  1611. }
  1612. }
  1613. private void saveCalculationChain(){
  1614. if(calcChain != null){
  1615. int count = calcChain.getCTCalcChain().sizeOfCArray();
  1616. if(count == 0){
  1617. removeRelation(calcChain);
  1618. calcChain = null;
  1619. }
  1620. }
  1621. }
  1622. @Override
  1623. protected void commit() throws IOException {
  1624. saveNamedRanges();
  1625. saveCalculationChain();
  1626. XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
  1627. xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorkbook.type.getName().getNamespaceURI(), "workbook"));
  1628. PackagePart part = getPackagePart();
  1629. try (OutputStream out = part.getOutputStream()) {
  1630. workbook.save(out, xmlOptions);
  1631. }
  1632. }
  1633. /**
  1634. * Returns SharedStringsTable - tha cache of string for this workbook
  1635. *
  1636. * @return the shared string table
  1637. */
  1638. @Internal
  1639. public SharedStringsTable getSharedStringSource() {
  1640. return this.sharedStringSource;
  1641. }
  1642. /**
  1643. * Return a object representing a collection of shared objects used for styling content,
  1644. * e.g. fonts, cell styles, colors, etc.
  1645. */
  1646. public StylesTable getStylesSource() {
  1647. return this.stylesSource;
  1648. }
  1649. /**
  1650. * Returns the Theme of current workbook.
  1651. */
  1652. public ThemesTable getTheme() {
  1653. if (stylesSource == null) {
  1654. return null;
  1655. }
  1656. return stylesSource.getTheme();
  1657. }
  1658. /**
  1659. * Returns an object that handles instantiating concrete
  1660. * classes of the various instances for XSSF.
  1661. */
  1662. @Override
  1663. public XSSFCreationHelper getCreationHelper() {
  1664. if(_creationHelper == null) {
  1665. _creationHelper = new XSSFCreationHelper(this);
  1666. }
  1667. return _creationHelper;
  1668. }
  1669. /**
  1670. * Determines whether a workbook contains the provided sheet name.
  1671. * For the purpose of comparison, long names are truncated to 31 chars.
  1672. *
  1673. * @param name the name to test (case insensitive match)
  1674. * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
  1675. * @return true if the sheet contains the name, false otherwise.
  1676. */
  1677. private boolean containsSheet(String name, int excludeSheetIdx) {
  1678. CTSheet[] ctSheetArray = workbook.getSheets().getSheetArray();
  1679. if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1680. name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1681. }
  1682. for (int i = 0; i < ctSheetArray.length; i++) {
  1683. String ctName = ctSheetArray[i].getName();
  1684. if (ctName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1685. ctName = ctName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1686. }
  1687. if (excludeSheetIdx != i && name.equalsIgnoreCase(ctName)) {
  1688. return true;
  1689. }
  1690. }
  1691. return false;
  1692. }
  1693. /**
  1694. * Gets a boolean value that indicates whether the date systems used in the workbook starts in 1904.
  1695. * <p>
  1696. * The default value is false, meaning that the workbook uses the 1900 date system,
  1697. * where 1/1/1900 is the first day in the system..
  1698. * </p>
  1699. * @return true if the date systems used in the workbook starts in 1904
  1700. */
  1701. @Internal
  1702. @Override
  1703. public boolean isDate1904() {
  1704. CTWorkbookPr workbookPr = workbook.getWorkbookPr();
  1705. return workbookPr != null && workbookPr.getDate1904();
  1706. }
  1707. /**
  1708. * Get the document's embedded files.
  1709. */
  1710. @Override
  1711. public List<PackagePart> getAllEmbeddedParts() throws OpenXML4JException {
  1712. List<PackagePart> embedds = new LinkedList<>();
  1713. for(XSSFSheet sheet : sheets){
  1714. // Get the embeddings for the workbook
  1715. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.OLEEMBEDDINGS.getRelation())) {
  1716. embedds.add( sheet.getPackagePart().getRelatedPart(rel) );
  1717. }
  1718. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.PACKEMBEDDINGS.getRelation())) {
  1719. embedds.add( sheet.getPackagePart().getRelatedPart(rel) );
  1720. }
  1721. }
  1722. return embedds;
  1723. }
  1724. @Override
  1725. @NotImplemented
  1726. public boolean isHidden() {
  1727. throw new IllegalStateException("Not implemented yet");
  1728. }
  1729. @Override
  1730. @NotImplemented
  1731. public void setHidden(boolean hiddenFlag) {
  1732. throw new IllegalStateException("Not implemented yet");
  1733. }
  1734. @Override
  1735. public boolean isSheetHidden(int sheetIx) {
  1736. validateSheetIndex(sheetIx);
  1737. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1738. return ctSheet.getState() == STSheetState.HIDDEN;
  1739. }
  1740. @Override
  1741. public boolean isSheetVeryHidden(int sheetIx) {
  1742. validateSheetIndex(sheetIx);
  1743. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1744. return ctSheet.getState() == STSheetState.VERY_HIDDEN;
  1745. }
  1746. @Override
  1747. public SheetVisibility getSheetVisibility(int sheetIx) {
  1748. validateSheetIndex(sheetIx);
  1749. final CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1750. final STSheetState.Enum state = ctSheet.getState();
  1751. if (state == STSheetState.VISIBLE) {
  1752. return SheetVisibility.VISIBLE;
  1753. }
  1754. if (state == STSheetState.HIDDEN) {
  1755. return SheetVisibility.HIDDEN;
  1756. }
  1757. if (state == STSheetState.VERY_HIDDEN) {
  1758. return SheetVisibility.VERY_HIDDEN;
  1759. }
  1760. throw new IllegalArgumentException("This should never happen");
  1761. }
  1762. @Override
  1763. public void setSheetHidden(int sheetIx, boolean hidden) {
  1764. setSheetVisibility(sheetIx, hidden ? SheetVisibility.HIDDEN : SheetVisibility.VISIBLE);
  1765. }
  1766. @Override
  1767. public void setSheetVisibility(int sheetIx, SheetVisibility visibility) {
  1768. validateSheetIndex(sheetIx);
  1769. final CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1770. switch (visibility) {
  1771. case VISIBLE:
  1772. ctSheet.setState(STSheetState.VISIBLE);
  1773. break;
  1774. case HIDDEN:
  1775. ctSheet.setState(STSheetState.HIDDEN);
  1776. break;
  1777. case VERY_HIDDEN:
  1778. ctSheet.setState(STSheetState.VERY_HIDDEN);
  1779. break;
  1780. default:
  1781. throw new IllegalArgumentException("This should never happen");
  1782. }
  1783. }
  1784. /**
  1785. * Fired when a formula is deleted from this workbook,
  1786. * for example when calling cell.setCellFormula(null)
  1787. *
  1788. * @see XSSFCell#setCellFormula(String)
  1789. */
  1790. protected void onDeleteFormula(XSSFCell cell){
  1791. if(calcChain != null) {
  1792. int sheetId = (int)cell.getSheet().sheet.getSheetId();
  1793. calcChain.removeItem(sheetId, cell.getReference());
  1794. }
  1795. }
  1796. /**
  1797. * Return the {@link CalculationChain} object for this workbook
  1798. * <p>
  1799. * The calculation chain object specifies the order in which the cells in a workbook were last calculated
  1800. * </p>
  1801. *
  1802. * @return the {@code CalculationChain} object or {@code null} if not defined
  1803. */
  1804. @Internal
  1805. public CalculationChain getCalculationChain() {
  1806. return calcChain;
  1807. }
  1808. /**
  1809. * Returns the list of {@link ExternalLinksTable} object for this workbook
  1810. *
  1811. * <p>The external links table specifies details of named ranges etc
  1812. * that are referenced from other workbooks, along with the last seen
  1813. * values of what they point to.</p>
  1814. *
  1815. * <p>Note that Excel uses index 0 for the current workbook, so the first
  1816. * External Links in a formula would be '[1]Foo' which corresponds to
  1817. * entry 0 in this list.</p>
  1818. * @return the {@code ExternalLinksTable} list, which may be empty
  1819. */
  1820. @Internal
  1821. public List<ExternalLinksTable> getExternalLinksTable() {
  1822. return externalLinks;
  1823. }
  1824. /**
  1825. *
  1826. * @return a collection of custom XML mappings defined in this workbook
  1827. */
  1828. public Collection<XSSFMap> getCustomXMLMappings(){
  1829. return mapInfo == null ? new ArrayList<>() : mapInfo.getAllXSSFMaps();
  1830. }
  1831. /**
  1832. *
  1833. * @return the helper class used to query the custom XML mapping defined in this workbook
  1834. */
  1835. @Internal
  1836. public MapInfo getMapInfo(){
  1837. return mapInfo;
  1838. }
  1839. /**
  1840. * Adds the External Link Table part and relations required to allow formulas
  1841. * referencing the specified external workbook to be added to this one. Allows
  1842. * formulas such as "[MyOtherWorkbook.xlsx]Sheet3!$A$5" to be added to the
  1843. * file, for workbooks not already linked / referenced.
  1844. * <p>
  1845. * This support is still regarded as in beta and may change
  1846. * <p>
  1847. * see https://bz.apache.org/bugzilla/show_bug.cgi?id=57184
  1848. *
  1849. * @param name The name the workbook will be referenced as in formulas
  1850. * @param workbook The open workbook to fetch the link required information from
  1851. * @return index position for external workbook
  1852. * @since POI 5.1.0
  1853. */
  1854. @Beta
  1855. @Override
  1856. public int linkExternalWorkbook(String name, Workbook workbook) {
  1857. int externalLinkIdx=-1;
  1858. if (!getCreationHelper().getReferencedWorkbooks().containsKey(name)){
  1859. externalLinkIdx = this.getNextPartNumber(XSSFRelation.EXTERNAL_LINKS,
  1860. this.getPackagePart().getPackage().getPartsByContentType(XSSFRelation.EXTERNAL_LINKS.getContentType()).size() + 1);
  1861. POIXMLDocumentPart.RelationPart rp = this.createRelationship(XSSFRelation.EXTERNAL_LINKS, xssfFactory, externalLinkIdx, false);
  1862. ExternalLinksTable linksTable = rp.getDocumentPart();
  1863. linksTable.setLinkedFileName(name);
  1864. this.getExternalLinksTable().add(linksTable);
  1865. CTExternalReference ctExternalReference = this.getCTWorkbook().addNewExternalReferences().addNewExternalReference();
  1866. ctExternalReference.setId(rp.getRelationship().getId());
  1867. } else {
  1868. List<RelationPart> relationParts = getRelationParts();
  1869. for (RelationPart relationPart : relationParts) {
  1870. if (relationPart.getDocumentPart() instanceof ExternalLinksTable) {
  1871. ExternalLinksTable linksTable = relationPart.getDocumentPart();
  1872. String linkedFileName = linksTable.getLinkedFileName();
  1873. if(linkedFileName.equals(name)){
  1874. String s = relationPart.getRelationship().getTargetURI().toString();
  1875. String s2 = XSSFRelation.EXTERNAL_LINKS.getDefaultFileName();
  1876. String numStr = s.substring(s2.indexOf('#'), s2.indexOf('.'));
  1877. externalLinkIdx = Integer.parseInt(numStr);
  1878. break;
  1879. }
  1880. }
  1881. }
  1882. }
  1883. XSSFCreationHelper creationHelper = getCreationHelper();
  1884. creationHelper.addExternalWorkbook(name,workbook);
  1885. return externalLinkIdx;
  1886. }
  1887. /**
  1888. * Specifies a boolean value that indicates whether structure of workbook is locked. <br>
  1889. * A value true indicates the structure of the workbook is locked. Worksheets in the workbook can't be moved,
  1890. * deleted, hidden, unhidden, or renamed, and new worksheets can't be inserted.<br>
  1891. * A value of false indicates the structure of the workbook is not locked.<br>
  1892. *
  1893. * @return true if structure of workbook is locked
  1894. */
  1895. public boolean isStructureLocked() {
  1896. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockStructure();
  1897. }
  1898. /**
  1899. * Specifies a boolean value that indicates whether the windows that comprise the workbook are locked. <br>
  1900. * A value of true indicates the workbook windows are locked. Windows are the same size and position each time the
  1901. * workbook is opened.<br>
  1902. * A value of false indicates the workbook windows are not locked.
  1903. *
  1904. * @return true if windows that comprise the workbook are locked
  1905. */
  1906. public boolean isWindowsLocked() {
  1907. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockWindows();
  1908. }
  1909. /**
  1910. * Specifies a boolean value that indicates whether the workbook is locked for revisions.
  1911. *
  1912. * @return true if the workbook is locked for revisions.
  1913. */
  1914. public boolean isRevisionLocked() {
  1915. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockRevision();
  1916. }
  1917. /**
  1918. * Locks the structure of workbook.
  1919. */
  1920. public void lockStructure() {
  1921. safeGetWorkbookProtection().setLockStructure(true);
  1922. }
  1923. /**
  1924. * Unlocks the structure of workbook.
  1925. */
  1926. public void unLockStructure() {
  1927. safeGetWorkbookProtection().setLockStructure(false);
  1928. }
  1929. /**
  1930. * Locks the windows that comprise the workbook.
  1931. */
  1932. public void lockWindows() {
  1933. safeGetWorkbookProtection().setLockWindows(true);
  1934. }
  1935. /**
  1936. * Unlocks the windows that comprise the workbook.
  1937. */
  1938. public void unLockWindows() {
  1939. safeGetWorkbookProtection().setLockWindows(false);
  1940. }
  1941. /**
  1942. * Locks the workbook for revisions.
  1943. */
  1944. public void lockRevision() {
  1945. safeGetWorkbookProtection().setLockRevision(true);
  1946. }
  1947. /**
  1948. * Unlocks the workbook for revisions.
  1949. */
  1950. public void unLockRevision() {
  1951. safeGetWorkbookProtection().setLockRevision(false);
  1952. }
  1953. /**
  1954. * Sets the workbook password.
  1955. *
  1956. * @param password if null, the password will be removed
  1957. * @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
  1958. * otherwise the given algorithm is used for calculating the hash password (Excel 2013)
  1959. */
  1960. public void setWorkbookPassword(String password, HashAlgorithm hashAlgo) {
  1961. if (password == null && !workbookProtectionPresent()) {
  1962. return;
  1963. }
  1964. setPassword(safeGetWorkbookProtection(), password, hashAlgo, "workbook");
  1965. }
  1966. /**
  1967. * Validate the password against the stored hash, the hashing method will be determined
  1968. * by the existing password attributes
  1969. * @return true, if the hashes match (... though original password may differ ...)
  1970. */
  1971. public boolean validateWorkbookPassword(String password) {
  1972. if (!workbookProtectionPresent()) {
  1973. return (password == null);
  1974. }
  1975. return validatePassword(safeGetWorkbookProtection(), password, "workbook");
  1976. }
  1977. /**
  1978. * Sets the revisions password.
  1979. *
  1980. * @param password if null, the password will be removed
  1981. * @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
  1982. * otherwise the given algorithm is used for calculating the hash password (Excel 2013)
  1983. */
  1984. public void setRevisionsPassword(String password, HashAlgorithm hashAlgo) {
  1985. if (password == null && !workbookProtectionPresent()) {
  1986. return;
  1987. }
  1988. setPassword(safeGetWorkbookProtection(), password, hashAlgo, "revisions");
  1989. }
  1990. /**
  1991. * Validate the password against the stored hash, the hashing method will be determined
  1992. * by the existing password attributes
  1993. * @return true if the hashes match (... though original password may differ ...)
  1994. */
  1995. public boolean validateRevisionsPassword(String password) {
  1996. if (!workbookProtectionPresent()) {
  1997. return (password == null);
  1998. }
  1999. return validatePassword(safeGetWorkbookProtection(), password, "revisions");
  2000. }
  2001. /**
  2002. * Removes the workbook protection settings
  2003. */
  2004. public void unLock() {
  2005. if (workbookProtectionPresent()) {
  2006. workbook.unsetWorkbookProtection();
  2007. }
  2008. }
  2009. private boolean workbookProtectionPresent() {
  2010. return workbook.isSetWorkbookProtection();
  2011. }
  2012. private CTWorkbookProtection safeGetWorkbookProtection() {
  2013. if (!workbookProtectionPresent()){
  2014. return workbook.addNewWorkbookProtection();
  2015. }
  2016. return workbook.getWorkbookProtection();
  2017. }
  2018. /**
  2019. *
  2020. * Returns the locator of user-defined functions.
  2021. * <p>
  2022. * The default instance extends the built-in functions with the Excel Analysis Tool Pack.
  2023. * To set / evaluate custom functions you need to register them as follows:
  2024. *
  2025. *
  2026. *
  2027. * </p>
  2028. * @return wrapped instance of UDFFinder that allows seeking functions both by index and name
  2029. */
  2030. /*package*/ UDFFinder getUDFFinder() {
  2031. return _udfFinder;
  2032. }
  2033. /**
  2034. * Register a new toolpack in this workbook.
  2035. *
  2036. * @param toolpack the toolpack to register
  2037. */
  2038. @Override
  2039. public void addToolPack(UDFFinder toolpack){
  2040. _udfFinder.add(toolpack);
  2041. }
  2042. /**
  2043. * Whether the application shall perform a full recalculation when the workbook is opened.
  2044. * <p>
  2045. * Typically you want to force formula recalculation when you modify cell formulas or values
  2046. * of a workbook previously created by Excel. When set to true, this flag will tell Excel
  2047. * that it needs to recalculate all formulas in the workbook the next time the file is opened.
  2048. * </p>
  2049. * <p>
  2050. * Note, that recalculation updates cached formula results and, thus, modifies the workbook.
  2051. * Depending on the version, Excel may prompt you with "Do you want to save the changes in <em>filename</em>?"
  2052. * on close.
  2053. * </p>
  2054. *
  2055. * @param value true if the application will perform a full recalculation of
  2056. * workbook values when the workbook is opened
  2057. * @since 3.8
  2058. */
  2059. @Override
  2060. public void setForceFormulaRecalculation(boolean value){
  2061. CTWorkbook ctWorkbook = getCTWorkbook();
  2062. CTCalcPr calcPr = ctWorkbook.isSetCalcPr() ? ctWorkbook.getCalcPr() : ctWorkbook.addNewCalcPr();
  2063. // when set to true, will tell Excel that it needs to recalculate all formulas
  2064. // in the workbook the next time the file is opened.
  2065. calcPr.setFullCalcOnLoad(value);
  2066. if(value && calcPr.getCalcMode() == STCalcMode.MANUAL) {
  2067. calcPr.setCalcMode(STCalcMode.AUTO);
  2068. }
  2069. }
  2070. /**
  2071. * Whether Excel will be asked to recalculate all formulas when the workbook is opened.
  2072. *
  2073. * @since 3.8
  2074. */
  2075. @Override
  2076. public boolean getForceFormulaRecalculation(){
  2077. CTWorkbook ctWorkbook = getCTWorkbook();
  2078. CTCalcPr calcPr = ctWorkbook.getCalcPr();
  2079. return calcPr != null && calcPr.isSetFullCalcOnLoad() && calcPr.getFullCalcOnLoad();
  2080. }
  2081. /**
  2082. * Add pivotCache to the workbook
  2083. */
  2084. @Beta
  2085. protected CTPivotCache addPivotCache(String rId) {
  2086. CTWorkbook ctWorkbook = getCTWorkbook();
  2087. CTPivotCaches caches;
  2088. if (ctWorkbook.isSetPivotCaches()) {
  2089. caches = ctWorkbook.getPivotCaches();
  2090. } else {
  2091. caches = ctWorkbook.addNewPivotCaches();
  2092. }
  2093. CTPivotCache cache = caches.addNewPivotCache();
  2094. int tableId = getPivotTables().size()+1;
  2095. cache.setCacheId(tableId);
  2096. cache.setId(rId);
  2097. if(pivotCaches == null) {
  2098. pivotCaches = new ArrayList<>();
  2099. }
  2100. pivotCaches.add(cache);
  2101. return cache;
  2102. }
  2103. @Beta
  2104. public List<XSSFPivotTable> getPivotTables() {
  2105. return pivotTables;
  2106. }
  2107. @Beta
  2108. protected void setPivotTables(List<XSSFPivotTable> pivotTables) {
  2109. this.pivotTables = pivotTables;
  2110. }
  2111. public XSSFWorkbookType getWorkbookType() {
  2112. return isMacroEnabled() ? XSSFWorkbookType.XLSM : XSSFWorkbookType.XLSX;
  2113. }
  2114. /**
  2115. * Sets whether the workbook will be an .xlsx or .xlsm (macro-enabled) file.
  2116. */
  2117. public void setWorkbookType(XSSFWorkbookType type) {
  2118. try {
  2119. getPackagePart().setContentType(type.getContentType());
  2120. } catch (InvalidFormatException e) {
  2121. throw new POIXMLException(e);
  2122. }
  2123. }
  2124. /**
  2125. * Adds a vbaProject.bin file to the workbook. This will change the workbook
  2126. * type if necessary.
  2127. *
  2128. * @throws IOException If copying data from the stream fails.
  2129. */
  2130. public void setVBAProject(InputStream vbaProjectStream) throws IOException {
  2131. if (!isMacroEnabled()) {
  2132. setWorkbookType(XSSFWorkbookType.XLSM);
  2133. }
  2134. PackagePartName ppName;
  2135. try {
  2136. ppName = PackagingURIHelper.createPartName(XSSFRelation.VBA_MACROS.getDefaultFileName());
  2137. } catch (InvalidFormatException e) {
  2138. throw new POIXMLException(e);
  2139. }
  2140. OPCPackage opc = getPackage();
  2141. OutputStream outputStream;
  2142. if (!opc.containPart(ppName)) {
  2143. POIXMLDocumentPart relationship = createRelationship(XSSFRelation.VBA_MACROS, this.xssfFactory);
  2144. outputStream = relationship.getPackagePart().getOutputStream();
  2145. } else {
  2146. PackagePart part = opc.getPart(ppName);
  2147. outputStream = part.getOutputStream();
  2148. }
  2149. try {
  2150. IOUtils.copy(vbaProjectStream, outputStream);
  2151. } finally {
  2152. IOUtils.closeQuietly(outputStream);
  2153. }
  2154. }
  2155. /**
  2156. * Adds a vbaProject.bin file taken from another, given workbook to this one.
  2157. * @throws IOException If copying the VBAProject stream fails.
  2158. * @throws InvalidFormatException If an error occurs while handling parts of the XSSF format
  2159. */
  2160. public void setVBAProject(XSSFWorkbook macroWorkbook) throws IOException, InvalidFormatException {
  2161. if (!macroWorkbook.isMacroEnabled()) {
  2162. return;
  2163. }
  2164. InputStream vbaProjectStream = XSSFRelation.VBA_MACROS.getContents(macroWorkbook.getCorePart());
  2165. if (vbaProjectStream != null) {
  2166. setVBAProject(vbaProjectStream);
  2167. }
  2168. }
  2169. /**
  2170. * Returns the spreadsheet version (EXCLE2007) of this workbook
  2171. *
  2172. * @return EXCEL2007 SpreadsheetVersion enum
  2173. * @since 3.14 beta 2
  2174. */
  2175. @Override
  2176. public SpreadsheetVersion getSpreadsheetVersion() {
  2177. return SpreadsheetVersion.EXCEL2007;
  2178. }
  2179. /**
  2180. * Returns the data table with the given name (case insensitive).
  2181. *
  2182. * @param name the data table name (case-insensitive)
  2183. * @return The Data table in the workbook named {@code name}, or {@code null} if no table is named {@code name}.
  2184. * @since 3.15 beta 2
  2185. */
  2186. public XSSFTable getTable(String name) {
  2187. if (name != null && sheets != null) {
  2188. for (XSSFSheet sheet : sheets) {
  2189. for (XSSFTable tbl : sheet.getTables()) {
  2190. if (name.equalsIgnoreCase(tbl.getName())) {
  2191. return tbl;
  2192. }
  2193. }
  2194. }
  2195. }
  2196. return null;
  2197. }
  2198. @Override
  2199. public int addOlePackage(byte[] oleData, String label, String fileName, String command)
  2200. throws IOException {
  2201. final XSSFRelation rel = XSSFRelation.OLEEMBEDDINGS;
  2202. // find an unused part name
  2203. OPCPackage opc = getPackage();
  2204. PackagePartName pnOLE;
  2205. int oleId;
  2206. try {
  2207. oleId = opc.getUnusedPartIndex(rel.getDefaultFileName());
  2208. pnOLE = PackagingURIHelper.createPartName(rel.getFileName(oleId));
  2209. } catch (InvalidFormatException e) {
  2210. throw new IOException("ole object name not recognized", e);
  2211. }
  2212. PackagePart pp = opc.createPart( pnOLE, rel.getContentType() );
  2213. Ole10Native ole10 = new Ole10Native(label, fileName, command, oleData);
  2214. try (UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(oleData.length+500)) {
  2215. ole10.writeOut(bos);
  2216. try (POIFSFileSystem poifs = new POIFSFileSystem()) {
  2217. DirectoryNode root = poifs.getRoot();
  2218. root.createDocument(Ole10Native.OLE10_NATIVE, bos.toInputStream());
  2219. root.setStorageClsid(ClassIDPredefined.OLE_V1_PACKAGE.getClassID());
  2220. // TODO: generate CombObj stream
  2221. try (OutputStream os = pp.getOutputStream()) {
  2222. poifs.writeFilesystem(os);
  2223. }
  2224. }
  2225. }
  2226. return oleId;
  2227. }
  2228. /**
  2229. * Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
  2230. * When enabled (which is the default), this option can lead to formulas being modified by POI formula renderer.
  2231. *
  2232. * @param value true if the application will validate the formula is correct
  2233. * @since 3.17
  2234. */
  2235. public void setCellFormulaValidation(final boolean value) {
  2236. this.cellFormulaValidation = value;
  2237. }
  2238. /**
  2239. * Whether a call to {@link XSSFCell#setCellFormula(String)} will validate the formula or not.
  2240. * When enabled (which is the default), this option can lead to formulas being modified by POI formula renderer.
  2241. *
  2242. * @since 3.17
  2243. */
  2244. public boolean getCellFormulaValidation() {
  2245. return this.cellFormulaValidation;
  2246. }
  2247. @Override
  2248. public XSSFEvaluationWorkbook createEvaluationWorkbook() {
  2249. return XSSFEvaluationWorkbook.create(this);
  2250. }
  2251. }