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 70KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002
  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.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword;
  17. import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword;
  18. import java.io.ByteArrayInputStream;
  19. import java.io.ByteArrayOutputStream;
  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.HashMap;
  27. import java.util.Iterator;
  28. import java.util.LinkedList;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.regex.Pattern;
  32. import javax.xml.namespace.QName;
  33. import org.apache.poi.POIXMLDocument;
  34. import org.apache.poi.POIXMLDocumentPart;
  35. import org.apache.poi.POIXMLException;
  36. import org.apache.poi.POIXMLProperties;
  37. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  38. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  39. import org.apache.poi.openxml4j.opc.OPCPackage;
  40. import org.apache.poi.openxml4j.opc.PackagePart;
  41. import org.apache.poi.openxml4j.opc.PackagePartName;
  42. import org.apache.poi.openxml4j.opc.PackageRelationship;
  43. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  44. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  45. import org.apache.poi.openxml4j.opc.TargetMode;
  46. import org.apache.poi.poifs.crypt.HashAlgorithm;
  47. import org.apache.poi.ss.formula.SheetNameFormatter;
  48. import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
  49. import org.apache.poi.ss.formula.udf.UDFFinder;
  50. import org.apache.poi.ss.usermodel.Row;
  51. import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
  52. import org.apache.poi.ss.usermodel.Sheet;
  53. import org.apache.poi.ss.usermodel.Workbook;
  54. import org.apache.poi.ss.util.CellRangeAddress;
  55. import org.apache.poi.ss.util.CellReference;
  56. import org.apache.poi.ss.util.WorkbookUtil;
  57. import org.apache.poi.util.Beta;
  58. import org.apache.poi.util.IOUtils;
  59. import org.apache.poi.util.Internal;
  60. import org.apache.poi.util.POILogFactory;
  61. import org.apache.poi.util.POILogger;
  62. import org.apache.poi.util.PackageHelper;
  63. import org.apache.poi.xssf.XLSBUnsupportedException;
  64. import org.apache.poi.xssf.model.CalculationChain;
  65. import org.apache.poi.xssf.model.ExternalLinksTable;
  66. import org.apache.poi.xssf.model.MapInfo;
  67. import org.apache.poi.xssf.model.SharedStringsTable;
  68. import org.apache.poi.xssf.model.StylesTable;
  69. import org.apache.poi.xssf.model.ThemesTable;
  70. import org.apache.poi.xssf.usermodel.helpers.XSSFFormulaUtils;
  71. import org.apache.xmlbeans.XmlException;
  72. import org.apache.xmlbeans.XmlObject;
  73. import org.apache.xmlbeans.XmlOptions;
  74. import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
  75. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView;
  76. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews;
  77. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr;
  78. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
  79. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames;
  80. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet;
  81. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalReference;
  82. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotCache;
  83. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotCaches;
  84. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
  85. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets;
  86. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
  87. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr;
  88. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookProtection;
  89. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
  90. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCalcMode;
  91. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STSheetState;
  92. import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument;
  93. /**
  94. * High level representation of a SpreadsheetML workbook. This is the first object most users
  95. * will construct whether they are reading or writing a workbook. It is also the
  96. * top level object for creating new sheets/etc.
  97. */
  98. public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
  99. private static final Pattern COMMA_PATTERN = Pattern.compile(",");
  100. /**
  101. * Width of one character of the default font in pixels. Same for Calibry and Arial.
  102. */
  103. public static final float DEFAULT_CHARACTER_WIDTH = 7.0017f;
  104. /**
  105. * Excel silently truncates long sheet names to 31 chars.
  106. * This constant is used to ensure uniqueness in the first 31 chars
  107. */
  108. private static final int MAX_SENSITIVE_SHEET_NAME_LEN = 31;
  109. /**
  110. * Images formats supported by XSSF but not by HSSF
  111. */
  112. public static final int PICTURE_TYPE_GIF = 8;
  113. public static final int PICTURE_TYPE_TIFF = 9;
  114. public static final int PICTURE_TYPE_EPS = 10;
  115. public static final int PICTURE_TYPE_BMP = 11;
  116. public static final int PICTURE_TYPE_WPG = 12;
  117. /**
  118. * The underlying XML bean
  119. */
  120. private CTWorkbook workbook;
  121. /**
  122. * this holds the XSSFSheet objects attached to this workbook
  123. */
  124. private List<XSSFSheet> sheets;
  125. /**
  126. * this holds the XSSFName objects attached to this workbook
  127. */
  128. private List<XSSFName> namedRanges;
  129. /**
  130. * shared string table - a cache of strings in this workbook
  131. */
  132. private SharedStringsTable sharedStringSource;
  133. /**
  134. * A collection of shared objects used for styling content,
  135. * e.g. fonts, cell styles, colors, etc.
  136. */
  137. private StylesTable stylesSource;
  138. private ThemesTable theme;
  139. /**
  140. * The locator of user-defined functions.
  141. * By default includes functions from the Excel Analysis Toolpack
  142. */
  143. private IndexedUDFFinder _udfFinder = new IndexedUDFFinder(UDFFinder.DEFAULT);
  144. /**
  145. * TODO
  146. */
  147. private CalculationChain calcChain;
  148. /**
  149. * External Links, for referencing names or cells in other workbooks.
  150. */
  151. private List<ExternalLinksTable> externalLinks;
  152. /**
  153. * A collection of custom XML mappings
  154. */
  155. private MapInfo mapInfo;
  156. /**
  157. * Used to keep track of the data formatter so that all
  158. * createDataFormatter calls return the same one for a given
  159. * book. This ensures that updates from one places is visible
  160. * someplace else.
  161. */
  162. private XSSFDataFormat formatter;
  163. /**
  164. * The policy to apply in the event of missing or
  165. * blank cells when fetching from a row.
  166. * See {@link org.apache.poi.ss.usermodel.Row.MissingCellPolicy}
  167. */
  168. private MissingCellPolicy _missingCellPolicy = Row.RETURN_NULL_AND_BLANK;
  169. /**
  170. * array of pictures for this workbook
  171. */
  172. private List<XSSFPictureData> pictures;
  173. private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class);
  174. /**
  175. * cached instance of XSSFCreationHelper for this workbook
  176. * @see {@link #getCreationHelper()}
  177. */
  178. private XSSFCreationHelper _creationHelper;
  179. /**
  180. * List of all pivot tables in workbook
  181. */
  182. private List<XSSFPivotTable> pivotTables;
  183. private List<CTPivotCache> pivotCaches;
  184. /**
  185. * Create a new SpreadsheetML workbook.
  186. */
  187. public XSSFWorkbook() {
  188. super(newPackage());
  189. onWorkbookCreate();
  190. }
  191. /**
  192. * Constructs a XSSFWorkbook object given a OpenXML4J <code>Package</code> object,
  193. * see <a href="http://poi.apache.org/oxml4j/">http://poi.apache.org/oxml4j/</a>.
  194. *
  195. * <p>Once you have finished working with the Workbook, you should close the package
  196. * by calling either {@link #close()} or {@link OPCPackage#close()}, to avoid
  197. * leaving file handles open.
  198. *
  199. * <p>Creating a XSSFWorkbook from a file-backed OPC Package has a lower memory
  200. * footprint than an InputStream backed one.
  201. *
  202. * @param pkg the OpenXML4J <code>OPC Package</code> object.
  203. */
  204. public XSSFWorkbook(OPCPackage pkg) throws IOException {
  205. super(pkg);
  206. beforeDocumentRead();
  207. // Build a tree of POIXMLDocumentParts, this workbook being the root
  208. load(XSSFFactory.getInstance());
  209. }
  210. /**
  211. * Constructs a XSSFWorkbook object, by buffering the whole stream into memory
  212. * and then opening an {@link OPCPackage} object for it.
  213. *
  214. * <p>Using an {@link InputStream} requires more memory than using a File, so
  215. * if a {@link File} is available then you should instead do something like
  216. * <pre><code>
  217. * OPCPackage pkg = OPCPackage.open(path);
  218. * XSSFWorkbook wb = new XSSFWorkbook(pkg);
  219. * // work with the wb object
  220. * ......
  221. * pkg.close(); // gracefully closes the underlying zip file
  222. * </code></pre>
  223. */
  224. public XSSFWorkbook(InputStream is) throws IOException {
  225. super(PackageHelper.open(is));
  226. beforeDocumentRead();
  227. // Build a tree of POIXMLDocumentParts, this workbook being the root
  228. load(XSSFFactory.getInstance());
  229. }
  230. /**
  231. * Constructs a XSSFWorkbook object from a given file.
  232. *
  233. * <p>Once you have finished working with the Workbook, you should close
  234. * the package by calling {@link #close()}, to avoid leaving file
  235. * handles open.
  236. *
  237. * <p>Opening a XSSFWorkbook from a file has a lower memory footprint
  238. * than opening from an InputStream
  239. *
  240. * @param file the file to open
  241. */
  242. public XSSFWorkbook(File file) throws IOException, InvalidFormatException {
  243. this(OPCPackage.open(file));
  244. }
  245. /**
  246. * Constructs a XSSFWorkbook object given a file name.
  247. *
  248. *
  249. * <p>Once you have finished working with the Workbook, you should close
  250. * the package by calling {@link #close()}, to avoid leaving file
  251. * handles open.
  252. *
  253. * <p>Opening a XSSFWorkbook from a file has a lower memory footprint
  254. * than opening from an InputStream
  255. *
  256. * @param path the file name.
  257. */
  258. public XSSFWorkbook(String path) throws IOException {
  259. this(openPackage(path));
  260. }
  261. protected void beforeDocumentRead() {
  262. // Ensure it isn't a XLSB file, which we don't support
  263. if (getCorePart().getContentType().equals(XSSFRelation.XLSB_BINARY_WORKBOOK.getContentType())) {
  264. throw new XLSBUnsupportedException();
  265. }
  266. // Create arrays for parts attached to the workbook itself
  267. pivotTables = new ArrayList<XSSFPivotTable>();
  268. pivotCaches = new ArrayList<CTPivotCache>();
  269. }
  270. @Override
  271. @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
  272. protected void onDocumentRead() throws IOException {
  273. try {
  274. WorkbookDocument doc = WorkbookDocument.Factory.parse(getPackagePart().getInputStream());
  275. this.workbook = doc.getWorkbook();
  276. Map<String, XSSFSheet> shIdMap = new HashMap<String, XSSFSheet>();
  277. Map<String, ExternalLinksTable> elIdMap = new HashMap<String, ExternalLinksTable>();
  278. for(POIXMLDocumentPart p : getRelations()){
  279. if(p instanceof SharedStringsTable) sharedStringSource = (SharedStringsTable)p;
  280. else if(p instanceof StylesTable) stylesSource = (StylesTable)p;
  281. else if(p instanceof ThemesTable) theme = (ThemesTable)p;
  282. else if(p instanceof CalculationChain) calcChain = (CalculationChain)p;
  283. else if(p instanceof MapInfo) mapInfo = (MapInfo)p;
  284. else if (p instanceof XSSFSheet) {
  285. shIdMap.put(p.getPackageRelationship().getId(), (XSSFSheet)p);
  286. }
  287. else if (p instanceof ExternalLinksTable) {
  288. elIdMap.put(p.getPackageRelationship().getId(), (ExternalLinksTable)p);
  289. }
  290. }
  291. if (stylesSource == null) {
  292. // Create Styles if it is missing
  293. stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, XSSFFactory.getInstance());
  294. }
  295. stylesSource.setTheme(theme);
  296. if (sharedStringSource == null) {
  297. // Create SST if it is missing
  298. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
  299. }
  300. // Load individual sheets. The order of sheets is defined by the order
  301. // of CTSheet elements in the workbook
  302. sheets = new ArrayList<XSSFSheet>(shIdMap.size());
  303. for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) {
  304. XSSFSheet sh = shIdMap.get(ctSheet.getId());
  305. if(sh == null) {
  306. logger.log(POILogger.WARN, "Sheet with name " + ctSheet.getName() + " and r:id " + ctSheet.getId()+ " was defined, but didn't exist in package, skipping");
  307. continue;
  308. }
  309. sh.sheet = ctSheet;
  310. sh.onDocumentRead();
  311. sheets.add(sh);
  312. }
  313. // Load the external links tables. Their order is defined by the order
  314. // of CTExternalReference elements in the workbook
  315. externalLinks = new ArrayList<ExternalLinksTable>(elIdMap.size());
  316. if (this.workbook.isSetExternalReferences()) {
  317. for (CTExternalReference er : this.workbook.getExternalReferences().getExternalReferenceArray()) {
  318. ExternalLinksTable el = elIdMap.get(er.getId());
  319. if(el == null) {
  320. logger.log(POILogger.WARN, "ExternalLinksTable with r:id " + er.getId()+ " was defined, but didn't exist in package, skipping");
  321. continue;
  322. }
  323. externalLinks.add(el);
  324. }
  325. }
  326. // Process the named ranges
  327. reprocessNamedRanges();
  328. } catch (XmlException e) {
  329. throw new POIXMLException(e);
  330. }
  331. }
  332. /**
  333. * Create a new CTWorkbook with all values set to default
  334. */
  335. private void onWorkbookCreate() {
  336. workbook = CTWorkbook.Factory.newInstance();
  337. // don't EVER use the 1904 date system
  338. CTWorkbookPr workbookPr = workbook.addNewWorkbookPr();
  339. workbookPr.setDate1904(false);
  340. CTBookViews bvs = workbook.addNewBookViews();
  341. CTBookView bv = bvs.addNewWorkbookView();
  342. bv.setActiveTab(0);
  343. workbook.addNewSheets();
  344. POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
  345. expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR);
  346. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
  347. stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, XSSFFactory.getInstance());
  348. namedRanges = new ArrayList<XSSFName>();
  349. sheets = new ArrayList<XSSFSheet>();
  350. pivotTables = new ArrayList<XSSFPivotTable>();
  351. }
  352. /**
  353. * Create a new SpreadsheetML package and setup the default minimal content
  354. */
  355. protected static OPCPackage newPackage() {
  356. try {
  357. OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
  358. // Main part
  359. PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
  360. // Create main part relationship
  361. pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
  362. // Create main document part
  363. pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());
  364. pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
  365. return pkg;
  366. } catch (Exception e){
  367. throw new POIXMLException(e);
  368. }
  369. }
  370. /**
  371. * Return the underlying XML bean
  372. *
  373. * @return the underlying CTWorkbook bean
  374. */
  375. @Internal
  376. public CTWorkbook getCTWorkbook() {
  377. return this.workbook;
  378. }
  379. /**
  380. * Adds a picture to the workbook.
  381. *
  382. * @param pictureData The bytes of the picture
  383. * @param format The format of the picture.
  384. *
  385. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  386. * @see Workbook#PICTURE_TYPE_EMF
  387. * @see Workbook#PICTURE_TYPE_WMF
  388. * @see Workbook#PICTURE_TYPE_PICT
  389. * @see Workbook#PICTURE_TYPE_JPEG
  390. * @see Workbook#PICTURE_TYPE_PNG
  391. * @see Workbook#PICTURE_TYPE_DIB
  392. * @see #getAllPictures()
  393. */
  394. @Override
  395. public int addPicture(byte[] pictureData, int format) {
  396. int imageNumber = getAllPictures().size() + 1;
  397. XSSFPictureData img = (XSSFPictureData)createRelationship(XSSFPictureData.RELATIONS[format], XSSFFactory.getInstance(), imageNumber, true);
  398. try {
  399. OutputStream out = img.getPackagePart().getOutputStream();
  400. out.write(pictureData);
  401. out.close();
  402. } catch (IOException e){
  403. throw new POIXMLException(e);
  404. }
  405. pictures.add(img);
  406. return imageNumber - 1;
  407. }
  408. /**
  409. * Adds a picture to the workbook.
  410. *
  411. * @param is The sream to read image from
  412. * @param format The format of the picture.
  413. *
  414. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  415. * @see Workbook#PICTURE_TYPE_EMF
  416. * @see Workbook#PICTURE_TYPE_WMF
  417. * @see Workbook#PICTURE_TYPE_PICT
  418. * @see Workbook#PICTURE_TYPE_JPEG
  419. * @see Workbook#PICTURE_TYPE_PNG
  420. * @see Workbook#PICTURE_TYPE_DIB
  421. * @see #getAllPictures()
  422. */
  423. public int addPicture(InputStream is, int format) throws IOException {
  424. int imageNumber = getAllPictures().size() + 1;
  425. XSSFPictureData img = (XSSFPictureData)createRelationship(XSSFPictureData.RELATIONS[format], XSSFFactory.getInstance(), imageNumber, true);
  426. OutputStream out = img.getPackagePart().getOutputStream();
  427. IOUtils.copy(is, out);
  428. out.close();
  429. pictures.add(img);
  430. return imageNumber - 1;
  431. }
  432. /**
  433. * Create an XSSFSheet from an existing sheet in the XSSFWorkbook.
  434. * The cloned sheet is a deep copy of the original.
  435. *
  436. * @return XSSFSheet representing the cloned sheet.
  437. * @throws IllegalArgumentException if the sheet index in invalid
  438. * @throws POIXMLException if there were errors when cloning
  439. */
  440. @Override
  441. public XSSFSheet cloneSheet(int sheetNum) {
  442. validateSheetIndex(sheetNum);
  443. XSSFSheet srcSheet = sheets.get(sheetNum);
  444. String srcName = srcSheet.getSheetName();
  445. String clonedName = getUniqueSheetName(srcName);
  446. XSSFSheet clonedSheet = createSheet(clonedName);
  447. try {
  448. ByteArrayOutputStream out = new ByteArrayOutputStream();
  449. srcSheet.write(out);
  450. clonedSheet.read(new ByteArrayInputStream(out.toByteArray()));
  451. } catch (IOException e){
  452. throw new POIXMLException("Failed to clone sheet", e);
  453. }
  454. CTWorksheet ct = clonedSheet.getCTWorksheet();
  455. if(ct.isSetLegacyDrawing()) {
  456. logger.log(POILogger.WARN, "Cloning sheets with comments is not yet supported.");
  457. ct.unsetLegacyDrawing();
  458. }
  459. if (ct.isSetPageSetup()) {
  460. logger.log(POILogger.WARN, "Cloning sheets with page setup is not yet supported.");
  461. ct.unsetPageSetup();
  462. }
  463. clonedSheet.setSelected(false);
  464. // copy sheet's relations
  465. List<POIXMLDocumentPart> rels = srcSheet.getRelations();
  466. // if the sheet being cloned has a drawing then rememebr it and re-create tpoo
  467. XSSFDrawing dg = null;
  468. for(POIXMLDocumentPart r : rels) {
  469. // do not copy the drawing relationship, it will be re-created
  470. if(r instanceof XSSFDrawing) {
  471. dg = (XSSFDrawing)r;
  472. continue;
  473. }
  474. PackageRelationship rel = r.getPackageRelationship();
  475. clonedSheet.getPackagePart().addRelationship(
  476. rel.getTargetURI(), rel.getTargetMode(),rel.getRelationshipType());
  477. clonedSheet.addRelation(rel.getId(), r);
  478. }
  479. // clone the sheet drawing alongs with its relationships
  480. if (dg != null) {
  481. if(ct.isSetDrawing()) {
  482. // unset the existing reference to the drawing,
  483. // so that subsequent call of clonedSheet.createDrawingPatriarch() will create a new one
  484. ct.unsetDrawing();
  485. }
  486. XSSFDrawing clonedDg = clonedSheet.createDrawingPatriarch();
  487. // copy drawing contents
  488. clonedDg.getCTDrawing().set(dg.getCTDrawing());
  489. // Clone drawing relations
  490. List<POIXMLDocumentPart> srcRels = srcSheet.createDrawingPatriarch().getRelations();
  491. for (POIXMLDocumentPart rel : srcRels) {
  492. PackageRelationship relation = rel.getPackageRelationship();
  493. clonedSheet
  494. .createDrawingPatriarch()
  495. .getPackagePart()
  496. .addRelationship(relation.getTargetURI(), relation.getTargetMode(),
  497. relation.getRelationshipType(), relation.getId());
  498. }
  499. }
  500. return clonedSheet;
  501. }
  502. /**
  503. * Generate a valid sheet name based on the existing one. Used when cloning sheets.
  504. *
  505. * @param srcName the original sheet name to
  506. * @return clone sheet name
  507. */
  508. private String getUniqueSheetName(String srcName) {
  509. int uniqueIndex = 2;
  510. String baseName = srcName;
  511. int bracketPos = srcName.lastIndexOf('(');
  512. if (bracketPos > 0 && srcName.endsWith(")")) {
  513. String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length());
  514. try {
  515. uniqueIndex = Integer.parseInt(suffix.trim());
  516. uniqueIndex++;
  517. baseName = srcName.substring(0, bracketPos).trim();
  518. } catch (NumberFormatException e) {
  519. // contents of brackets not numeric
  520. }
  521. }
  522. while (true) {
  523. // Try and find the next sheet name that is unique
  524. String index = Integer.toString(uniqueIndex++);
  525. String name;
  526. if (baseName.length() + index.length() + 2 < 31) {
  527. name = baseName + " (" + index + ")";
  528. } else {
  529. name = baseName.substring(0, 31 - index.length() - 2) + "(" + index + ")";
  530. }
  531. //If the sheet name is unique, then set it otherwise move on to the next number.
  532. if (getSheetIndex(name) == -1) {
  533. return name;
  534. }
  535. }
  536. }
  537. /**
  538. * Create a new XSSFCellStyle and add it to the workbook's style table
  539. *
  540. * @return the new XSSFCellStyle object
  541. */
  542. @Override
  543. public XSSFCellStyle createCellStyle() {
  544. return stylesSource.createCellStyle();
  545. }
  546. /**
  547. * Returns the instance of XSSFDataFormat for this workbook.
  548. *
  549. * @return the XSSFDataFormat object
  550. * @see org.apache.poi.ss.usermodel.DataFormat
  551. */
  552. @Override
  553. public XSSFDataFormat createDataFormat() {
  554. if (formatter == null)
  555. formatter = new XSSFDataFormat(stylesSource);
  556. return formatter;
  557. }
  558. /**
  559. * Create a new Font and add it to the workbook's font table
  560. *
  561. * @return new font object
  562. */
  563. @Override
  564. public XSSFFont createFont() {
  565. XSSFFont font = new XSSFFont();
  566. font.registerTo(stylesSource);
  567. return font;
  568. }
  569. @Override
  570. public XSSFName createName() {
  571. CTDefinedName ctName = CTDefinedName.Factory.newInstance();
  572. ctName.setName("");
  573. XSSFName name = new XSSFName(ctName, this);
  574. namedRanges.add(name);
  575. return name;
  576. }
  577. /**
  578. * Create an XSSFSheet for this workbook, adds it to the sheets and returns
  579. * the high level representation. Use this to create new sheets.
  580. *
  581. * @return XSSFSheet representing the new sheet.
  582. */
  583. @Override
  584. public XSSFSheet createSheet() {
  585. String sheetname = "Sheet" + (sheets.size());
  586. int idx = 0;
  587. while(getSheet(sheetname) != null) {
  588. sheetname = "Sheet" + idx;
  589. idx++;
  590. }
  591. return createSheet(sheetname);
  592. }
  593. /**
  594. * Create a new sheet for this Workbook and return the high level representation.
  595. * Use this to create new sheets.
  596. *
  597. * <p>
  598. * Note that Excel allows sheet names up to 31 chars in length but other applications
  599. * (such as OpenOffice) allow more. Some versions of Excel crash with names longer than 31 chars,
  600. * others - truncate such names to 31 character.
  601. * </p>
  602. * <p>
  603. * POI's SpreadsheetAPI silently truncates the input argument to 31 characters.
  604. * Example:
  605. *
  606. * <pre><code>
  607. * Sheet sheet = workbook.createSheet("My very long sheet name which is longer than 31 chars"); // will be truncated
  608. * assert 31 == sheet.getSheetName().length();
  609. * assert "My very long sheet name which i" == sheet.getSheetName();
  610. * </code></pre>
  611. * </p>
  612. *
  613. * Except the 31-character constraint, Excel applies some other rules:
  614. * <p>
  615. * Sheet name MUST be unique in the workbook and MUST NOT contain the any of the following characters:
  616. * <ul>
  617. * <li> 0x0000 </li>
  618. * <li> 0x0003 </li>
  619. * <li> colon (:) </li>
  620. * <li> backslash (\) </li>
  621. * <li> asterisk (*) </li>
  622. * <li> question mark (?) </li>
  623. * <li> forward slash (/) </li>
  624. * <li> opening square bracket ([) </li>
  625. * <li> closing square bracket (]) </li>
  626. * </ul>
  627. * The string MUST NOT begin or end with the single quote (') character.
  628. * </p>
  629. *
  630. * <p>
  631. * See {@link org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)}
  632. * for a safe way to create valid names
  633. * </p>
  634. * @param sheetname sheetname to set for the sheet.
  635. * @return Sheet representing the new sheet.
  636. * @throws IllegalArgumentException if the name is null or invalid
  637. * or workbook already contains a sheet with this name
  638. * @see org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)
  639. */
  640. @Override
  641. public XSSFSheet createSheet(String sheetname) {
  642. if (sheetname == null) {
  643. throw new IllegalArgumentException("sheetName must not be null");
  644. }
  645. if (containsSheet( sheetname, sheets.size() ))
  646. throw new IllegalArgumentException( "The workbook already contains a sheet of this name");
  647. // YK: Mimic Excel and silently truncate sheet names longer than 31 characters
  648. if(sheetname.length() > 31) sheetname = sheetname.substring(0, 31);
  649. WorkbookUtil.validateSheetName(sheetname);
  650. CTSheet sheet = addSheet(sheetname);
  651. int sheetNumber = 1;
  652. for(XSSFSheet sh : sheets) {
  653. sheetNumber = (int)Math.max(sh.sheet.getSheetId() + 1, sheetNumber);
  654. }
  655. XSSFSheet wrapper = (XSSFSheet)createRelationship(XSSFRelation.WORKSHEET, XSSFFactory.getInstance(), sheetNumber);
  656. wrapper.sheet = sheet;
  657. sheet.setId(wrapper.getPackageRelationship().getId());
  658. sheet.setSheetId(sheetNumber);
  659. if(sheets.size() == 0) wrapper.setSelected(true);
  660. sheets.add(wrapper);
  661. return wrapper;
  662. }
  663. protected XSSFDialogsheet createDialogsheet(String sheetname, CTDialogsheet dialogsheet) {
  664. XSSFSheet sheet = createSheet(sheetname);
  665. return new XSSFDialogsheet(sheet);
  666. }
  667. private CTSheet addSheet(String sheetname) {
  668. CTSheet sheet = workbook.getSheets().addNewSheet();
  669. sheet.setName(sheetname);
  670. return sheet;
  671. }
  672. /**
  673. * Finds a font that matches the one with the supplied attributes
  674. */
  675. @Override
  676. public XSSFFont findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  677. return stylesSource.findFont(boldWeight, color, fontHeight, name, italic, strikeout, typeOffset, underline);
  678. }
  679. /**
  680. * Convenience method to get the active sheet. The active sheet is is the sheet
  681. * which is currently displayed when the workbook is viewed in Excel.
  682. * 'Selected' sheet(s) is a distinct concept.
  683. */
  684. @Override
  685. public int getActiveSheetIndex() {
  686. //activeTab (Active Sheet Index) Specifies an unsignedInt
  687. //that contains the index to the active sheet in this book view.
  688. return (int)workbook.getBookViews().getWorkbookViewArray(0).getActiveTab();
  689. }
  690. /**
  691. * Gets all pictures from the Workbook.
  692. *
  693. * @return the list of pictures (a list of {@link XSSFPictureData} objects.)
  694. * @see #addPicture(byte[], int)
  695. */
  696. @Override
  697. public List<XSSFPictureData> getAllPictures() {
  698. if(pictures == null){
  699. List<PackagePart> mediaParts = getPackage().getPartsByName(Pattern.compile("/xl/media/.*?"));
  700. pictures = new ArrayList<XSSFPictureData>(mediaParts.size());
  701. for(PackagePart part : mediaParts){
  702. pictures.add(new XSSFPictureData(part, null));
  703. }
  704. }
  705. return pictures; //YK: should return Collections.unmodifiableList(pictures);
  706. }
  707. /**
  708. * Get the cell style object at the given index
  709. *
  710. * @param idx index within the set of styles
  711. * @return XSSFCellStyle object at the index
  712. */
  713. @Override
  714. public XSSFCellStyle getCellStyleAt(short idx) {
  715. return stylesSource.getStyleAt(idx);
  716. }
  717. /**
  718. * Get the font at the given index number
  719. *
  720. * @param idx index number
  721. * @return XSSFFont at the index
  722. */
  723. @Override
  724. public XSSFFont getFontAt(short idx) {
  725. return stylesSource.getFontAt(idx);
  726. }
  727. @Override
  728. public XSSFName getName(String name) {
  729. int nameIndex = getNameIndex(name);
  730. if (nameIndex < 0) {
  731. return null;
  732. }
  733. return namedRanges.get(nameIndex);
  734. }
  735. @Override
  736. public XSSFName getNameAt(int nameIndex) {
  737. int nNames = namedRanges.size();
  738. if (nNames < 1) {
  739. throw new IllegalStateException("There are no defined names in this workbook");
  740. }
  741. if (nameIndex < 0 || nameIndex > nNames) {
  742. throw new IllegalArgumentException("Specified name index " + nameIndex
  743. + " is outside the allowable range (0.." + (nNames-1) + ").");
  744. }
  745. return namedRanges.get(nameIndex);
  746. }
  747. /**
  748. * Gets the named range index by his name
  749. * <i>Note:</i>Excel named ranges are case-insensitive and
  750. * this method performs a case-insensitive search.
  751. *
  752. * @param name named range name
  753. * @return named range index
  754. */
  755. @Override
  756. public int getNameIndex(String name) {
  757. int i = 0;
  758. for(XSSFName nr : namedRanges) {
  759. if(nr.getNameName().equals(name)) {
  760. return i;
  761. }
  762. i++;
  763. }
  764. return -1;
  765. }
  766. /**
  767. * Get the number of styles the workbook contains
  768. *
  769. * @return count of cell styles
  770. */
  771. @Override
  772. public short getNumCellStyles() {
  773. return (short) (stylesSource).getNumCellStyles();
  774. }
  775. /**
  776. * Get the number of fonts in the this workbook
  777. *
  778. * @return number of fonts
  779. */
  780. @Override
  781. public short getNumberOfFonts() {
  782. return (short)stylesSource.getFonts().size();
  783. }
  784. /**
  785. * Get the number of named ranges in the this workbook
  786. *
  787. * @return number of named ranges
  788. */
  789. @Override
  790. public int getNumberOfNames() {
  791. return namedRanges.size();
  792. }
  793. /**
  794. * Get the number of worksheets in the this workbook
  795. *
  796. * @return number of worksheets
  797. */
  798. @Override
  799. public int getNumberOfSheets() {
  800. return sheets.size();
  801. }
  802. /**
  803. * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
  804. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  805. * @return String Null if no print area has been defined
  806. */
  807. @Override
  808. public String getPrintArea(int sheetIndex) {
  809. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  810. if (name == null) return null;
  811. //adding one here because 0 indicates a global named region; doesnt make sense for print areas
  812. return name.getRefersToFormula();
  813. }
  814. /**
  815. * Get sheet with the given name (case insensitive match)
  816. *
  817. * @param name of the sheet
  818. * @return XSSFSheet with the name provided or <code>null</code> if it does not exist
  819. */
  820. @Override
  821. public XSSFSheet getSheet(String name) {
  822. for (XSSFSheet sheet : sheets) {
  823. if (name.equalsIgnoreCase(sheet.getSheetName())) {
  824. return sheet;
  825. }
  826. }
  827. return null;
  828. }
  829. /**
  830. * Get the XSSFSheet object at the given index.
  831. *
  832. * @param index of the sheet number (0-based physical & logical)
  833. * @return XSSFSheet at the provided index
  834. * @throws IllegalArgumentException if the index is out of range (index
  835. * &lt; 0 || index &gt;= getNumberOfSheets()).
  836. */
  837. @Override
  838. public XSSFSheet getSheetAt(int index) {
  839. validateSheetIndex(index);
  840. return sheets.get(index);
  841. }
  842. /**
  843. * Returns the index of the sheet by his name (case insensitive match)
  844. *
  845. * @param name the sheet name
  846. * @return index of the sheet (0 based) or <tt>-1</tt if not found
  847. */
  848. @Override
  849. public int getSheetIndex(String name) {
  850. for (int i = 0 ; i < sheets.size() ; ++i) {
  851. XSSFSheet sheet = sheets.get(i);
  852. if (name.equalsIgnoreCase(sheet.getSheetName())) {
  853. return i;
  854. }
  855. }
  856. return -1;
  857. }
  858. /**
  859. * Returns the index of the given sheet
  860. *
  861. * @param sheet the sheet to look up
  862. * @return index of the sheet (0 based). <tt>-1</tt> if not found
  863. */
  864. @Override
  865. public int getSheetIndex(Sheet sheet) {
  866. int idx = 0;
  867. for(XSSFSheet sh : sheets){
  868. if(sh == sheet) return idx;
  869. idx++;
  870. }
  871. return -1;
  872. }
  873. /**
  874. * Get the sheet name
  875. *
  876. * @param sheetIx Number
  877. * @return Sheet name
  878. */
  879. @Override
  880. public String getSheetName(int sheetIx) {
  881. validateSheetIndex(sheetIx);
  882. return sheets.get(sheetIx).getSheetName();
  883. }
  884. /**
  885. * Allows foreach loops:
  886. * <pre><code>
  887. * XSSFWorkbook wb = new XSSFWorkbook(package);
  888. * for(XSSFSheet sheet : wb){
  889. *
  890. * }
  891. * </code></pre>
  892. */
  893. @Override
  894. public Iterator<XSSFSheet> iterator() {
  895. return sheets.iterator();
  896. }
  897. /**
  898. * Are we a normal workbook (.xlsx), or a
  899. * macro enabled workbook (.xlsm)?
  900. */
  901. public boolean isMacroEnabled() {
  902. return getPackagePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType());
  903. }
  904. @Override
  905. public void removeName(int nameIndex) {
  906. namedRanges.remove(nameIndex);
  907. }
  908. @Override
  909. public void removeName(String name) {
  910. for (int i = 0; i < namedRanges.size(); i++) {
  911. XSSFName nm = namedRanges.get(i);
  912. if(nm.getNameName().equalsIgnoreCase(name)) {
  913. removeName(i);
  914. return;
  915. }
  916. }
  917. throw new IllegalArgumentException("Named range was not found: " + name);
  918. }
  919. /**
  920. * As {@link #removeName(String)} is not necessarily unique
  921. * (name + sheet index is unique), this method is more accurate.
  922. *
  923. * @param name the name to remove.
  924. */
  925. void removeName(XSSFName name) {
  926. if (!namedRanges.remove(name)) {
  927. throw new IllegalArgumentException("Name was not found: " + name);
  928. }
  929. }
  930. /**
  931. * Delete the printarea for the sheet specified
  932. *
  933. * @param sheetIndex 0-based sheet index (0 = First Sheet)
  934. */
  935. @Override
  936. public void removePrintArea(int sheetIndex) {
  937. int cont = 0;
  938. for (XSSFName name : namedRanges) {
  939. if (name.getNameName().equals(XSSFName.BUILTIN_PRINT_AREA) && name.getSheetIndex() == sheetIndex) {
  940. namedRanges.remove(cont);
  941. break;
  942. }
  943. cont++;
  944. }
  945. }
  946. /**
  947. * Removes sheet at the given index.<p/>
  948. *
  949. * Care must be taken if the removed sheet is the currently active or only selected sheet in
  950. * the workbook. There are a few situations when Excel must have a selection and/or active
  951. * sheet. (For example when printing - see Bug 40414).<br/>
  952. *
  953. * This method makes sure that if the removed sheet was active, another sheet will become
  954. * active in its place. Furthermore, if the removed sheet was the only selected sheet, another
  955. * sheet will become selected. The newly active/selected sheet will have the same index, or
  956. * one less if the removed sheet was the last in the workbook.
  957. *
  958. * @param index of the sheet (0-based)
  959. */
  960. @Override
  961. public void removeSheetAt(int index) {
  962. validateSheetIndex(index);
  963. onSheetDelete(index);
  964. XSSFSheet sheet = getSheetAt(index);
  965. removeRelation(sheet);
  966. sheets.remove(index);
  967. // only set new sheet if there are still some left
  968. if(sheets.size() == 0) {
  969. return;
  970. }
  971. // the index of the closest remaining sheet to the one just deleted
  972. int newSheetIndex = index;
  973. if (newSheetIndex >= sheets.size()) {
  974. newSheetIndex = sheets.size()-1;
  975. }
  976. // adjust active sheet
  977. int active = getActiveSheetIndex();
  978. if(active == index) {
  979. // removed sheet was the active one, reset active sheet if there is still one left now
  980. setActiveSheet(newSheetIndex);
  981. } else if (active > index) {
  982. // removed sheet was below the active one => active is one less now
  983. setActiveSheet(active-1);
  984. }
  985. }
  986. /**
  987. * Gracefully remove references to the sheet being deleted
  988. *
  989. * @param index the 0-based index of the sheet to delete
  990. */
  991. private void onSheetDelete(int index) {
  992. //delete the CTSheet reference from workbook.xml
  993. workbook.getSheets().removeSheet(index);
  994. //calculation chain is auxiliary, remove it as it may contain orphan references to deleted cells
  995. if(calcChain != null) {
  996. removeRelation(calcChain);
  997. calcChain = null;
  998. }
  999. //adjust indices of names ranges
  1000. for (Iterator<XSSFName> it = namedRanges.iterator(); it.hasNext();) {
  1001. XSSFName nm = it.next();
  1002. CTDefinedName ct = nm.getCTName();
  1003. if(!ct.isSetLocalSheetId()) continue;
  1004. if (ct.getLocalSheetId() == index) {
  1005. it.remove();
  1006. } else if (ct.getLocalSheetId() > index){
  1007. // Bump down by one, so still points at the same sheet
  1008. ct.setLocalSheetId(ct.getLocalSheetId()-1);
  1009. }
  1010. }
  1011. }
  1012. /**
  1013. * Retrieves the current policy on what to do when
  1014. * getting missing or blank cells from a row.
  1015. * The default is to return blank and null cells.
  1016. * {@link MissingCellPolicy}
  1017. */
  1018. @Override
  1019. public MissingCellPolicy getMissingCellPolicy() {
  1020. return _missingCellPolicy;
  1021. }
  1022. /**
  1023. * Sets the policy on what to do when
  1024. * getting missing or blank cells from a row.
  1025. * This will then apply to all calls to
  1026. * {@link Row#getCell(int)}}. See
  1027. * {@link MissingCellPolicy}
  1028. */
  1029. @Override
  1030. public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
  1031. _missingCellPolicy = missingCellPolicy;
  1032. }
  1033. /**
  1034. * Convenience method to set the active sheet. The active sheet is is the sheet
  1035. * which is currently displayed when the workbook is viewed in Excel.
  1036. * 'Selected' sheet(s) is a distinct concept.
  1037. */
  1038. @Override
  1039. @SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support
  1040. public void setActiveSheet(int index) {
  1041. validateSheetIndex(index);
  1042. for (CTBookView arrayBook : workbook.getBookViews().getWorkbookViewArray()) {
  1043. arrayBook.setActiveTab(index);
  1044. }
  1045. }
  1046. /**
  1047. * Validate sheet index
  1048. *
  1049. * @param index the index to validate
  1050. * @throws IllegalArgumentException if the index is out of range (index
  1051. * &lt; 0 || index &gt;= getNumberOfSheets()).
  1052. */
  1053. private void validateSheetIndex(int index) {
  1054. int lastSheetIx = sheets.size() - 1;
  1055. if (index < 0 || index > lastSheetIx) {
  1056. String range = "(0.." + lastSheetIx + ")";
  1057. if (lastSheetIx == -1) {
  1058. range = "(no sheets)";
  1059. }
  1060. throw new IllegalArgumentException("Sheet index ("
  1061. + index +") is out of range " + range);
  1062. }
  1063. }
  1064. /**
  1065. * Gets the first tab that is displayed in the list of tabs in excel.
  1066. *
  1067. * @return integer that contains the index to the active sheet in this book view.
  1068. */
  1069. @Override
  1070. public int getFirstVisibleTab() {
  1071. CTBookViews bookViews = workbook.getBookViews();
  1072. CTBookView bookView = bookViews.getWorkbookViewArray(0);
  1073. return (short) bookView.getActiveTab();
  1074. }
  1075. /**
  1076. * Sets the first tab that is displayed in the list of tabs in excel.
  1077. *
  1078. * @param index integer that contains the index to the active sheet in this book view.
  1079. */
  1080. @Override
  1081. public void setFirstVisibleTab(int index) {
  1082. CTBookViews bookViews = workbook.getBookViews();
  1083. CTBookView bookView= bookViews.getWorkbookViewArray(0);
  1084. bookView.setActiveTab(index);
  1085. }
  1086. /**
  1087. * Sets the printarea for the sheet provided
  1088. * <p>
  1089. * i.e. Reference = $A$1:$B$2
  1090. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  1091. * @param reference Valid name Reference for the Print Area
  1092. */
  1093. @Override
  1094. public void setPrintArea(int sheetIndex, String reference) {
  1095. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1096. if (name == null) {
  1097. name = createBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  1098. }
  1099. //short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
  1100. //name.setExternSheetNumber(externSheetIndex);
  1101. String[] parts = COMMA_PATTERN.split(reference);
  1102. StringBuffer sb = new StringBuffer(32);
  1103. for (int i = 0; i < parts.length; i++) {
  1104. if(i>0) {
  1105. sb.append(",");
  1106. }
  1107. SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
  1108. sb.append("!");
  1109. sb.append(parts[i]);
  1110. }
  1111. name.setRefersToFormula(sb.toString());
  1112. }
  1113. /**
  1114. * For the Convenience of Java Programmers maintaining pointers.
  1115. * @see #setPrintArea(int, String)
  1116. * @param sheetIndex Zero-based sheet index (0 = First Sheet)
  1117. * @param startColumn Column to begin printarea
  1118. * @param endColumn Column to end the printarea
  1119. * @param startRow Row to begin the printarea
  1120. * @param endRow Row to end the printarea
  1121. */
  1122. @Override
  1123. public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) {
  1124. String reference=getReferencePrintArea(getSheetName(sheetIndex), startColumn, endColumn, startRow, endRow);
  1125. setPrintArea(sheetIndex, reference);
  1126. }
  1127. /**
  1128. * Sets the repeating rows and columns for a sheet.
  1129. * <p/>
  1130. * To set just repeating columns:
  1131. * <pre>
  1132. * workbook.setRepeatingRowsAndColumns(0,0,1,-1,-1);
  1133. * </pre>
  1134. * To set just repeating rows:
  1135. * <pre>
  1136. * workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
  1137. * </pre>
  1138. * To remove all repeating rows and columns for a sheet.
  1139. * <pre>
  1140. * workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
  1141. * </pre>
  1142. *
  1143. * @param sheetIndex 0 based index to sheet.
  1144. * @param startColumn 0 based start of repeating columns.
  1145. * @param endColumn 0 based end of repeating columns.
  1146. * @param startRow 0 based start of repeating rows.
  1147. * @param endRow 0 based end of repeating rows.
  1148. *
  1149. * @deprecated use {@link XSSFSheet#setRepeatingRows(CellRangeAddress)}
  1150. * or {@link XSSFSheet#setRepeatingColumns(CellRangeAddress)}
  1151. */
  1152. @Deprecated
  1153. @Override
  1154. public void setRepeatingRowsAndColumns(int sheetIndex,
  1155. int startColumn, int endColumn,
  1156. int startRow, int endRow) {
  1157. XSSFSheet sheet = getSheetAt(sheetIndex);
  1158. CellRangeAddress rows = null;
  1159. CellRangeAddress cols = null;
  1160. if (startRow != -1) {
  1161. rows = new CellRangeAddress(startRow, endRow, -1, -1);
  1162. }
  1163. if (startColumn != -1) {
  1164. cols = new CellRangeAddress(-1, -1, startColumn, endColumn);
  1165. }
  1166. sheet.setRepeatingRows(rows);
  1167. sheet.setRepeatingColumns(cols);
  1168. }
  1169. private static String getReferencePrintArea(String sheetName, int startC, int endC, int startR, int endR) {
  1170. //windows excel example: Sheet1!$C$3:$E$4
  1171. CellReference colRef = new CellReference(sheetName, startR, startC, true, true);
  1172. CellReference colRef2 = new CellReference(sheetName, endR, endC, true, true);
  1173. return "$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1];
  1174. }
  1175. XSSFName getBuiltInName(String builtInCode, int sheetNumber) {
  1176. for (XSSFName name : namedRanges) {
  1177. if (name.getNameName().equalsIgnoreCase(builtInCode) && name.getSheetIndex() == sheetNumber) {
  1178. return name;
  1179. }
  1180. }
  1181. return null;
  1182. }
  1183. /**
  1184. * Generates a NameRecord to represent a built-in region
  1185. *
  1186. * @return a new NameRecord
  1187. * @throws IllegalArgumentException if sheetNumber is invalid
  1188. * @throws POIXMLException if such a name already exists in the workbook
  1189. */
  1190. XSSFName createBuiltInName(String builtInName, int sheetNumber) {
  1191. validateSheetIndex(sheetNumber);
  1192. CTDefinedNames names = workbook.getDefinedNames() == null ? workbook.addNewDefinedNames() : workbook.getDefinedNames();
  1193. CTDefinedName nameRecord = names.addNewDefinedName();
  1194. nameRecord.setName(builtInName);
  1195. nameRecord.setLocalSheetId(sheetNumber);
  1196. XSSFName name = new XSSFName(nameRecord, this);
  1197. for (XSSFName nr : namedRanges) {
  1198. if (nr.equals(name))
  1199. throw new POIXMLException("Builtin (" + builtInName
  1200. + ") already exists for sheet (" + sheetNumber + ")");
  1201. }
  1202. namedRanges.add(name);
  1203. return name;
  1204. }
  1205. /**
  1206. * We only set one sheet as selected for compatibility with HSSF.
  1207. */
  1208. @Override
  1209. public void setSelectedTab(int index) {
  1210. for (int i = 0 ; i < sheets.size() ; ++i) {
  1211. XSSFSheet sheet = sheets.get(i);
  1212. sheet.setSelected(i == index);
  1213. }
  1214. }
  1215. /**
  1216. * Set the sheet name.
  1217. *
  1218. * @param sheetIndex sheet number (0 based)
  1219. * @param sheetname the new sheet name
  1220. * @throws IllegalArgumentException if the name is null or invalid
  1221. * or workbook already contains a sheet with this name
  1222. * @see #createSheet(String)
  1223. * @see org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)
  1224. */
  1225. @Override
  1226. public void setSheetName(int sheetIndex, String sheetname) {
  1227. validateSheetIndex(sheetIndex);
  1228. String oldSheetName = getSheetName(sheetIndex);
  1229. // YK: Mimic Excel and silently truncate sheet names longer than 31 characters
  1230. if(sheetname != null && sheetname.length() > 31) sheetname = sheetname.substring(0, 31);
  1231. WorkbookUtil.validateSheetName(sheetname);
  1232. // findbugs fix - validateSheetName has already checked for null value
  1233. assert(sheetname != null);
  1234. // Do nothing if no change
  1235. if (sheetname.equals(oldSheetName)) return;
  1236. // Check it isn't already taken
  1237. if (containsSheet(sheetname, sheetIndex ))
  1238. throw new IllegalArgumentException( "The workbook already contains a sheet of this name" );
  1239. // Update references to the name
  1240. XSSFFormulaUtils utils = new XSSFFormulaUtils(this);
  1241. utils.updateSheetName(sheetIndex, oldSheetName, sheetname);
  1242. workbook.getSheets().getSheetArray(sheetIndex).setName(sheetname);
  1243. }
  1244. /**
  1245. * sets the order of appearance for a given sheet.
  1246. *
  1247. * @param sheetname the name of the sheet to reorder
  1248. * @param pos the position that we want to insert the sheet into (0 based)
  1249. */
  1250. @Override
  1251. @SuppressWarnings("deprecation")
  1252. public void setSheetOrder(String sheetname, int pos) {
  1253. int idx = getSheetIndex(sheetname);
  1254. sheets.add(pos, sheets.remove(idx));
  1255. // Reorder CTSheets
  1256. CTSheets ct = workbook.getSheets();
  1257. XmlObject cts = ct.getSheetArray(idx).copy();
  1258. workbook.getSheets().removeSheet(idx);
  1259. CTSheet newcts = ct.insertNewSheet(pos);
  1260. newcts.set(cts);
  1261. //notify sheets
  1262. CTSheet[] sheetArray = ct.getSheetArray();
  1263. for(int i=0; i < sheetArray.length; i++) {
  1264. sheets.get(i).sheet = sheetArray[i];
  1265. }
  1266. // adjust active sheet if necessary
  1267. int active = getActiveSheetIndex();
  1268. if(active == idx) {
  1269. // moved sheet was the active one
  1270. setActiveSheet(pos);
  1271. } else if ((active < idx && active < pos) ||
  1272. (active > idx && active > pos)) {
  1273. // not affected
  1274. } else if (pos > idx) {
  1275. // moved sheet was below before and is above now => active is one less
  1276. setActiveSheet(active-1);
  1277. } else {
  1278. // remaining case: moved sheet was higher than active before and is lower now => active is one more
  1279. setActiveSheet(active+1);
  1280. }
  1281. }
  1282. /**
  1283. * marshal named ranges from the {@link #namedRanges} collection to the underlying CTWorkbook bean
  1284. */
  1285. private void saveNamedRanges(){
  1286. // Named ranges
  1287. if(namedRanges.size() > 0) {
  1288. CTDefinedNames names = CTDefinedNames.Factory.newInstance();
  1289. CTDefinedName[] nr = new CTDefinedName[namedRanges.size()];
  1290. int i = 0;
  1291. for(XSSFName name : namedRanges) {
  1292. nr[i] = name.getCTName();
  1293. i++;
  1294. }
  1295. names.setDefinedNameArray(nr);
  1296. if(workbook.isSetDefinedNames()) {
  1297. workbook.unsetDefinedNames();
  1298. }
  1299. workbook.setDefinedNames(names);
  1300. // Re-process the named ranges
  1301. reprocessNamedRanges();
  1302. } else {
  1303. if(workbook.isSetDefinedNames()) {
  1304. workbook.unsetDefinedNames();
  1305. }
  1306. }
  1307. }
  1308. @SuppressWarnings("deprecation")
  1309. private void reprocessNamedRanges() {
  1310. namedRanges = new ArrayList<XSSFName>();
  1311. if(workbook.isSetDefinedNames()) {
  1312. for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) {
  1313. namedRanges.add(new XSSFName(ctName, this));
  1314. }
  1315. }
  1316. }
  1317. private void saveCalculationChain(){
  1318. if(calcChain != null){
  1319. int count = calcChain.getCTCalcChain().sizeOfCArray();
  1320. if(count == 0){
  1321. removeRelation(calcChain);
  1322. calcChain = null;
  1323. }
  1324. }
  1325. }
  1326. @Override
  1327. protected void commit() throws IOException {
  1328. saveNamedRanges();
  1329. saveCalculationChain();
  1330. XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
  1331. xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorkbook.type.getName().getNamespaceURI(), "workbook"));
  1332. Map<String, String> map = new HashMap<String, String>();
  1333. map.put(STRelationshipId.type.getName().getNamespaceURI(), "r");
  1334. xmlOptions.setSaveSuggestedPrefixes(map);
  1335. PackagePart part = getPackagePart();
  1336. OutputStream out = part.getOutputStream();
  1337. workbook.save(out, xmlOptions);
  1338. out.close();
  1339. }
  1340. /**
  1341. * Closes the underlying {@link OPCPackage} from which
  1342. * the Workbook was read, if any. Has no effect on newly
  1343. * created Workbooks.
  1344. */
  1345. @Override
  1346. public void close() throws IOException {
  1347. super.close();
  1348. }
  1349. /**
  1350. * Returns SharedStringsTable - tha cache of string for this workbook
  1351. *
  1352. * @return the shared string table
  1353. */
  1354. @Internal
  1355. public SharedStringsTable getSharedStringSource() {
  1356. return this.sharedStringSource;
  1357. }
  1358. /**
  1359. * Return a object representing a collection of shared objects used for styling content,
  1360. * e.g. fonts, cell styles, colors, etc.
  1361. */
  1362. public StylesTable getStylesSource() {
  1363. return this.stylesSource;
  1364. }
  1365. /**
  1366. * Returns the Theme of current workbook.
  1367. */
  1368. public ThemesTable getTheme() {
  1369. return theme;
  1370. }
  1371. /**
  1372. * Returns an object that handles instantiating concrete
  1373. * classes of the various instances for XSSF.
  1374. */
  1375. @Override
  1376. public XSSFCreationHelper getCreationHelper() {
  1377. if(_creationHelper == null) _creationHelper = new XSSFCreationHelper(this);
  1378. return _creationHelper;
  1379. }
  1380. /**
  1381. * Determines whether a workbook contains the provided sheet name.
  1382. * For the purpose of comparison, long names are truncated to 31 chars.
  1383. *
  1384. * @param name the name to test (case insensitive match)
  1385. * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
  1386. * @return true if the sheet contains the name, false otherwise.
  1387. */
  1388. @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
  1389. private boolean containsSheet(String name, int excludeSheetIdx) {
  1390. CTSheet[] ctSheetArray = workbook.getSheets().getSheetArray();
  1391. if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1392. name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1393. }
  1394. for (int i = 0; i < ctSheetArray.length; i++) {
  1395. String ctName = ctSheetArray[i].getName();
  1396. if (ctName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1397. ctName = ctName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1398. }
  1399. if (excludeSheetIdx != i && name.equalsIgnoreCase(ctName))
  1400. return true;
  1401. }
  1402. return false;
  1403. }
  1404. /**
  1405. * Gets a boolean value that indicates whether the date systems used in the workbook starts in 1904.
  1406. * <p>
  1407. * The default value is false, meaning that the workbook uses the 1900 date system,
  1408. * where 1/1/1900 is the first day in the system..
  1409. * </p>
  1410. * @return true if the date systems used in the workbook starts in 1904
  1411. */
  1412. protected boolean isDate1904(){
  1413. CTWorkbookPr workbookPr = workbook.getWorkbookPr();
  1414. return workbookPr != null && workbookPr.getDate1904();
  1415. }
  1416. /**
  1417. * Get the document's embedded files.
  1418. */
  1419. @Override
  1420. public List<PackagePart> getAllEmbedds() throws OpenXML4JException {
  1421. List<PackagePart> embedds = new LinkedList<PackagePart>();
  1422. for(XSSFSheet sheet : sheets){
  1423. // Get the embeddings for the workbook
  1424. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.OLEEMBEDDINGS.getRelation())) {
  1425. embedds.add( sheet.getPackagePart().getRelatedPart(rel) );
  1426. }
  1427. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.PACKEMBEDDINGS.getRelation())) {
  1428. embedds.add( sheet.getPackagePart().getRelatedPart(rel) );
  1429. }
  1430. }
  1431. return embedds;
  1432. }
  1433. @Override
  1434. public boolean isHidden() {
  1435. throw new RuntimeException("Not implemented yet");
  1436. }
  1437. @Override
  1438. public void setHidden(boolean hiddenFlag) {
  1439. throw new RuntimeException("Not implemented yet");
  1440. }
  1441. /**
  1442. * Check whether a sheet is hidden.
  1443. * <p>
  1444. * Note that a sheet could instead be set to be very hidden, which is different
  1445. * ({@link #isSheetVeryHidden(int)})
  1446. * </p>
  1447. * @param sheetIx Number
  1448. * @return <code>true</code> if sheet is hidden
  1449. */
  1450. @Override
  1451. public boolean isSheetHidden(int sheetIx) {
  1452. validateSheetIndex(sheetIx);
  1453. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1454. return ctSheet.getState() == STSheetState.HIDDEN;
  1455. }
  1456. /**
  1457. * Check whether a sheet is very hidden.
  1458. * <p>
  1459. * This is different from the normal hidden status
  1460. * ({@link #isSheetHidden(int)})
  1461. * </p>
  1462. * @param sheetIx sheet index to check
  1463. * @return <code>true</code> if sheet is very hidden
  1464. */
  1465. @Override
  1466. public boolean isSheetVeryHidden(int sheetIx) {
  1467. validateSheetIndex(sheetIx);
  1468. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1469. return ctSheet.getState() == STSheetState.VERY_HIDDEN;
  1470. }
  1471. /**
  1472. * Sets the visible state of this sheet.
  1473. * <p>
  1474. * Calling <code>setSheetHidden(sheetIndex, true)</code> is equivalent to
  1475. * <code>setSheetHidden(sheetIndex, Workbook.SHEET_STATE_HIDDEN)</code>.
  1476. * <br/>
  1477. * Calling <code>setSheetHidden(sheetIndex, false)</code> is equivalent to
  1478. * <code>setSheetHidden(sheetIndex, Workbook.SHEET_STATE_VISIBLE)</code>.
  1479. * </p>
  1480. *
  1481. * @param sheetIx the 0-based index of the sheet
  1482. * @param hidden whether this sheet is hidden
  1483. * @see #setSheetHidden(int, int)
  1484. */
  1485. @Override
  1486. public void setSheetHidden(int sheetIx, boolean hidden) {
  1487. setSheetHidden(sheetIx, hidden ? SHEET_STATE_HIDDEN : SHEET_STATE_VISIBLE);
  1488. }
  1489. /**
  1490. * Hide or unhide a sheet.
  1491. *
  1492. * <ul>
  1493. * <li>0 - visible. </li>
  1494. * <li>1 - hidden. </li>
  1495. * <li>2 - very hidden.</li>
  1496. * </ul>
  1497. * @param sheetIx the sheet index (0-based)
  1498. * @param state one of the following <code>Workbook</code> constants:
  1499. * <code>Workbook.SHEET_STATE_VISIBLE</code>,
  1500. * <code>Workbook.SHEET_STATE_HIDDEN</code>, or
  1501. * <code>Workbook.SHEET_STATE_VERY_HIDDEN</code>.
  1502. * @throws IllegalArgumentException if the supplied sheet index or state is invalid
  1503. */
  1504. @Override
  1505. public void setSheetHidden(int sheetIx, int state) {
  1506. validateSheetIndex(sheetIx);
  1507. WorkbookUtil.validateSheetState(state);
  1508. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1509. ctSheet.setState(STSheetState.Enum.forInt(state + 1));
  1510. }
  1511. /**
  1512. * Fired when a formula is deleted from this workbook,
  1513. * for example when calling cell.setCellFormula(null)
  1514. *
  1515. * @see XSSFCell#setCellFormula(String)
  1516. */
  1517. protected void onDeleteFormula(XSSFCell cell){
  1518. if(calcChain != null) {
  1519. int sheetId = (int)cell.getSheet().sheet.getSheetId();
  1520. calcChain.removeItem(sheetId, cell.getReference());
  1521. }
  1522. }
  1523. /**
  1524. * Return the {@link CalculationChain} object for this workbook
  1525. * <p>
  1526. * The calculation chain object specifies the order in which the cells in a workbook were last calculated
  1527. * </p>
  1528. *
  1529. * @return the <code>CalculationChain</code> object or <code>null</code> if not defined
  1530. */
  1531. @Internal
  1532. public CalculationChain getCalculationChain() {
  1533. return calcChain;
  1534. }
  1535. /**
  1536. * Returns the list of {@link ExternalLinksTable} object for this workbook
  1537. *
  1538. * <p>The external links table specifies details of named ranges etc
  1539. * that are referenced from other workbooks, along with the last seen
  1540. * values of what they point to.</p>
  1541. *
  1542. * <p>Note that Excel uses index 0 for the current workbook, so the first
  1543. * External Links in a formula would be '[1]Foo' which corresponds to
  1544. * entry 0 in this list.</p>
  1545. * @return the <code>ExternalLinksTable</code> list, which may be empty
  1546. */
  1547. @Internal
  1548. public List<ExternalLinksTable> getExternalLinksTable() {
  1549. return externalLinks;
  1550. }
  1551. /**
  1552. *
  1553. * @return a collection of custom XML mappings defined in this workbook
  1554. */
  1555. public Collection<XSSFMap> getCustomXMLMappings(){
  1556. return mapInfo == null ? new ArrayList<XSSFMap>() : mapInfo.getAllXSSFMaps();
  1557. }
  1558. /**
  1559. *
  1560. * @return the helper class used to query the custom XML mapping defined in this workbook
  1561. */
  1562. @Internal
  1563. public MapInfo getMapInfo(){
  1564. return mapInfo;
  1565. }
  1566. /**
  1567. * Adds the External Link Table part and relations required to allow formulas
  1568. * referencing the specified external workbook to be added to this one. Allows
  1569. * formulas such as "[MyOtherWorkbook.xlsx]Sheet3!$A$5" to be added to the
  1570. * file, for workbooks not already linked / referenced.
  1571. *
  1572. * @param name The name the workbook will be referenced as in formulas
  1573. * @param workbook The open workbook to fetch the link required information from
  1574. */
  1575. public int linkExternalWorkbook(String name, Workbook workbook) {
  1576. throw new RuntimeException("Not Implemented - see bug #57184");
  1577. }
  1578. /**
  1579. * Specifies a boolean value that indicates whether structure of workbook is locked. <br/>
  1580. * A value true indicates the structure of the workbook is locked. Worksheets in the workbook can't be moved,
  1581. * deleted, hidden, unhidden, or renamed, and new worksheets can't be inserted.<br/>
  1582. * A value of false indicates the structure of the workbook is not locked.<br/>
  1583. *
  1584. * @return true if structure of workbook is locked
  1585. */
  1586. public boolean isStructureLocked() {
  1587. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockStructure();
  1588. }
  1589. /**
  1590. * Specifies a boolean value that indicates whether the windows that comprise the workbook are locked. <br/>
  1591. * A value of true indicates the workbook windows are locked. Windows are the same size and position each time the
  1592. * workbook is opened.<br/>
  1593. * A value of false indicates the workbook windows are not locked.
  1594. *
  1595. * @return true if windows that comprise the workbook are locked
  1596. */
  1597. public boolean isWindowsLocked() {
  1598. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockWindows();
  1599. }
  1600. /**
  1601. * Specifies a boolean value that indicates whether the workbook is locked for revisions.
  1602. *
  1603. * @return true if the workbook is locked for revisions.
  1604. */
  1605. public boolean isRevisionLocked() {
  1606. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockRevision();
  1607. }
  1608. /**
  1609. * Locks the structure of workbook.
  1610. */
  1611. public void lockStructure() {
  1612. safeGetWorkbookProtection().setLockStructure(true);
  1613. }
  1614. /**
  1615. * Unlocks the structure of workbook.
  1616. */
  1617. public void unLockStructure() {
  1618. safeGetWorkbookProtection().setLockStructure(false);
  1619. }
  1620. /**
  1621. * Locks the windows that comprise the workbook.
  1622. */
  1623. public void lockWindows() {
  1624. safeGetWorkbookProtection().setLockWindows(true);
  1625. }
  1626. /**
  1627. * Unlocks the windows that comprise the workbook.
  1628. */
  1629. public void unLockWindows() {
  1630. safeGetWorkbookProtection().setLockWindows(false);
  1631. }
  1632. /**
  1633. * Locks the workbook for revisions.
  1634. */
  1635. public void lockRevision() {
  1636. safeGetWorkbookProtection().setLockRevision(true);
  1637. }
  1638. /**
  1639. * Unlocks the workbook for revisions.
  1640. */
  1641. public void unLockRevision() {
  1642. safeGetWorkbookProtection().setLockRevision(false);
  1643. }
  1644. /**
  1645. * Sets the workbook password.
  1646. *
  1647. * @param password if null, the password will be removed
  1648. * @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
  1649. * otherwise the given algorithm is used for calculating the hash password (Excel 2013)
  1650. */
  1651. public void setWorkbookPassword(String password, HashAlgorithm hashAlgo) {
  1652. if (password == null && !workbookProtectionPresent()) return;
  1653. setPassword(safeGetWorkbookProtection(), password, hashAlgo, "workbook");
  1654. }
  1655. /**
  1656. * Validate the password against the stored hash, the hashing method will be determined
  1657. * by the existing password attributes
  1658. * @return true, if the hashes match (... though original password may differ ...)
  1659. */
  1660. public boolean validateWorkbookPassword(String password) {
  1661. if (!workbookProtectionPresent()) return (password == null);
  1662. return validatePassword(safeGetWorkbookProtection(), password, "workbook");
  1663. }
  1664. /**
  1665. * Sets the revisions password.
  1666. *
  1667. * @param password if null, the password will be removed
  1668. * @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
  1669. * otherwise the given algorithm is used for calculating the hash password (Excel 2013)
  1670. */
  1671. public void setRevisionsPassword(String password, HashAlgorithm hashAlgo) {
  1672. if (password == null && !workbookProtectionPresent()) return;
  1673. setPassword(safeGetWorkbookProtection(), password, hashAlgo, "revisions");
  1674. }
  1675. /**
  1676. * Validate the password against the stored hash, the hashing method will be determined
  1677. * by the existing password attributes
  1678. * @return true if the hashes match (... though original password may differ ...)
  1679. */
  1680. public boolean validateRevisionsPassword(String password) {
  1681. if (!workbookProtectionPresent()) return (password == null);
  1682. return validatePassword(safeGetWorkbookProtection(), password, "revisions");
  1683. }
  1684. /**
  1685. * Removes the workbook protection settings
  1686. */
  1687. public void unLock() {
  1688. if (workbookProtectionPresent()) {
  1689. workbook.unsetWorkbookProtection();
  1690. }
  1691. }
  1692. private boolean workbookProtectionPresent() {
  1693. return workbook.isSetWorkbookProtection();
  1694. }
  1695. private CTWorkbookProtection safeGetWorkbookProtection() {
  1696. if (!workbookProtectionPresent()){
  1697. return workbook.addNewWorkbookProtection();
  1698. }
  1699. return workbook.getWorkbookProtection();
  1700. }
  1701. /**
  1702. *
  1703. * Returns the locator of user-defined functions.
  1704. * <p>
  1705. * The default instance extends the built-in functions with the Excel Analysis Tool Pack.
  1706. * To set / evaluate custom functions you need to register them as follows:
  1707. *
  1708. *
  1709. *
  1710. * </p>
  1711. * @return wrapped instance of UDFFinder that allows seeking functions both by index and name
  1712. */
  1713. /*package*/ UDFFinder getUDFFinder() {
  1714. return _udfFinder;
  1715. }
  1716. /**
  1717. * Register a new toolpack in this workbook.
  1718. *
  1719. * @param toopack the toolpack to register
  1720. */
  1721. @Override
  1722. public void addToolPack(UDFFinder toopack){
  1723. _udfFinder.add(toopack);
  1724. }
  1725. /**
  1726. * Whether the application shall perform a full recalculation when the workbook is opened.
  1727. * <p>
  1728. * Typically you want to force formula recalculation when you modify cell formulas or values
  1729. * of a workbook previously created by Excel. When set to true, this flag will tell Excel
  1730. * that it needs to recalculate all formulas in the workbook the next time the file is opened.
  1731. * </p>
  1732. * <p>
  1733. * Note, that recalculation updates cached formula results and, thus, modifies the workbook.
  1734. * Depending on the version, Excel may prompt you with "Do you want to save the changes in <em>filename</em>?"
  1735. * on close.
  1736. * </p>
  1737. *
  1738. * @param value true if the application will perform a full recalculation of
  1739. * workbook values when the workbook is opened
  1740. * @since 3.8
  1741. */
  1742. @Override
  1743. public void setForceFormulaRecalculation(boolean value){
  1744. CTWorkbook ctWorkbook = getCTWorkbook();
  1745. CTCalcPr calcPr = ctWorkbook.isSetCalcPr() ? ctWorkbook.getCalcPr() : ctWorkbook.addNewCalcPr();
  1746. // when set to 0, will tell Excel that it needs to recalculate all formulas
  1747. // in the workbook the next time the file is opened.
  1748. calcPr.setCalcId(0);
  1749. if(value && calcPr.getCalcMode() == STCalcMode.MANUAL) {
  1750. calcPr.setCalcMode(STCalcMode.AUTO);
  1751. }
  1752. }
  1753. /**
  1754. * Whether Excel will be asked to recalculate all formulas when the workbook is opened.
  1755. *
  1756. * @since 3.8
  1757. */
  1758. @Override
  1759. public boolean getForceFormulaRecalculation(){
  1760. CTWorkbook ctWorkbook = getCTWorkbook();
  1761. CTCalcPr calcPr = ctWorkbook.getCalcPr();
  1762. return calcPr != null && calcPr.getCalcId() != 0;
  1763. }
  1764. /**
  1765. * Add pivotCache to the workbook
  1766. */
  1767. @Beta
  1768. protected CTPivotCache addPivotCache(String rId) {
  1769. CTWorkbook ctWorkbook = getCTWorkbook();
  1770. CTPivotCaches caches;
  1771. if (ctWorkbook.isSetPivotCaches()) {
  1772. caches = ctWorkbook.getPivotCaches();
  1773. } else {
  1774. caches = ctWorkbook.addNewPivotCaches();
  1775. }
  1776. CTPivotCache cache = caches.addNewPivotCache();
  1777. int tableId = getPivotTables().size()+1;
  1778. cache.setCacheId(tableId);
  1779. cache.setId(rId);
  1780. if(pivotCaches == null) {
  1781. pivotCaches = new ArrayList<CTPivotCache>();
  1782. }
  1783. pivotCaches.add(cache);
  1784. return cache;
  1785. }
  1786. @Beta
  1787. public List<XSSFPivotTable> getPivotTables() {
  1788. return pivotTables;
  1789. }
  1790. @Beta
  1791. protected void setPivotTables(List<XSSFPivotTable> pivotTables) {
  1792. this.pivotTables = pivotTables;
  1793. }
  1794. }