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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526
  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 java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.LinkedList;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.regex.Pattern;
  29. import javax.xml.namespace.QName;
  30. import org.apache.poi.POIXMLDocument;
  31. import org.apache.poi.POIXMLDocumentPart;
  32. import org.apache.poi.POIXMLException;
  33. import org.apache.poi.POIXMLProperties;
  34. import org.apache.poi.ss.formula.SheetNameFormatter;
  35. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  36. import org.apache.poi.openxml4j.opc.OPCPackage;
  37. import org.apache.poi.openxml4j.opc.PackagePart;
  38. import org.apache.poi.openxml4j.opc.PackagePartName;
  39. import org.apache.poi.openxml4j.opc.PackageRelationship;
  40. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  41. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  42. import org.apache.poi.openxml4j.opc.TargetMode;
  43. import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
  44. import org.apache.poi.ss.formula.udf.UDFFinder;
  45. import org.apache.poi.ss.usermodel.Row;
  46. import org.apache.poi.ss.usermodel.Sheet;
  47. import org.apache.poi.ss.usermodel.Workbook;
  48. import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
  49. import org.apache.poi.ss.util.CellReference;
  50. import org.apache.poi.ss.util.WorkbookUtil;
  51. import org.apache.poi.util.*;
  52. import org.apache.poi.xssf.model.*;
  53. import org.apache.xmlbeans.XmlException;
  54. import org.apache.xmlbeans.XmlObject;
  55. import org.apache.xmlbeans.XmlOptions;
  56. import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
  57. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView;
  58. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews;
  59. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
  60. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedNames;
  61. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDialogsheet;
  62. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
  63. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets;
  64. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
  65. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr;
  66. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookProtection;
  67. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
  68. import org.openxmlformats.schemas.spreadsheetml.x2006.main.STSheetState;
  69. import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument;
  70. /**
  71. * High level representation of a SpreadsheetML workbook. This is the first object most users
  72. * will construct whether they are reading or writing a workbook. It is also the
  73. * top level object for creating new sheets/etc.
  74. */
  75. public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
  76. private static final Pattern COMMA_PATTERN = Pattern.compile(",");
  77. /**
  78. * Width of one character of the default font in pixels. Same for Calibry and Arial.
  79. */
  80. public static final float DEFAULT_CHARACTER_WIDTH = 7.0017f;
  81. /**
  82. * Excel silently truncates long sheet names to 31 chars.
  83. * This constant is used to ensure uniqueness in the first 31 chars
  84. */
  85. private static final int MAX_SENSITIVE_SHEET_NAME_LEN = 31;
  86. /**
  87. * The underlying XML bean
  88. */
  89. private CTWorkbook workbook;
  90. /**
  91. * this holds the XSSFSheet objects attached to this workbook
  92. */
  93. private List<XSSFSheet> sheets;
  94. /**
  95. * this holds the XSSFName objects attached to this workbook
  96. */
  97. private List<XSSFName> namedRanges;
  98. /**
  99. * shared string table - a cache of strings in this workbook
  100. */
  101. private SharedStringsTable sharedStringSource;
  102. /**
  103. * A collection of shared objects used for styling content,
  104. * e.g. fonts, cell styles, colors, etc.
  105. */
  106. private StylesTable stylesSource;
  107. private ThemesTable theme;
  108. /**
  109. * The locator of user-defined functions.
  110. * By default includes functions from the Excel Analysis Toolpack
  111. */
  112. private IndexedUDFFinder _udfFinder = new IndexedUDFFinder(UDFFinder.DEFAULT);
  113. /**
  114. * TODO
  115. */
  116. private CalculationChain calcChain;
  117. /**
  118. * A collection of custom XML mappings
  119. */
  120. private MapInfo mapInfo;
  121. /**
  122. * Used to keep track of the data formatter so that all
  123. * createDataFormatter calls return the same one for a given
  124. * book. This ensures that updates from one places is visible
  125. * someplace else.
  126. */
  127. private XSSFDataFormat formatter;
  128. /**
  129. * The policy to apply in the event of missing or
  130. * blank cells when fetching from a row.
  131. * See {@link org.apache.poi.ss.usermodel.Row.MissingCellPolicy}
  132. */
  133. private MissingCellPolicy _missingCellPolicy = Row.RETURN_NULL_AND_BLANK;
  134. /**
  135. * array of pictures for this workbook
  136. */
  137. private List<XSSFPictureData> pictures;
  138. private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class);
  139. /**
  140. * cached instance of XSSFCreationHelper for this workbook
  141. * @see {@link #getCreationHelper()}
  142. */
  143. private XSSFCreationHelper _creationHelper;
  144. /**
  145. * Create a new SpreadsheetML workbook.
  146. */
  147. public XSSFWorkbook() {
  148. super(newPackage());
  149. onWorkbookCreate();
  150. }
  151. /**
  152. * Constructs a XSSFWorkbook object given a OpenXML4J <code>Package</code> object,
  153. * see <a href="http://openxml4j.org/">www.openxml4j.org</a>.
  154. *
  155. * @param pkg the OpenXML4J <code>Package</code> object.
  156. */
  157. public XSSFWorkbook(OPCPackage pkg) throws IOException {
  158. super(pkg);
  159. //build a tree of POIXMLDocumentParts, this workbook being the root
  160. load(XSSFFactory.getInstance());
  161. }
  162. public XSSFWorkbook(InputStream is) throws IOException {
  163. super(PackageHelper.open(is));
  164. //build a tree of POIXMLDocumentParts, this workbook being the root
  165. load(XSSFFactory.getInstance());
  166. }
  167. /**
  168. * Constructs a XSSFWorkbook object given a file name.
  169. *
  170. * @param path the file name.
  171. */
  172. public XSSFWorkbook(String path) throws IOException {
  173. this(openPackage(path));
  174. }
  175. @Override
  176. @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
  177. protected void onDocumentRead() throws IOException {
  178. try {
  179. WorkbookDocument doc = WorkbookDocument.Factory.parse(getPackagePart().getInputStream());
  180. this.workbook = doc.getWorkbook();
  181. Map<String, XSSFSheet> shIdMap = new HashMap<String, XSSFSheet>();
  182. for(POIXMLDocumentPart p : getRelations()){
  183. if(p instanceof SharedStringsTable) sharedStringSource = (SharedStringsTable)p;
  184. else if(p instanceof StylesTable) stylesSource = (StylesTable)p;
  185. else if(p instanceof ThemesTable) theme = (ThemesTable)p;
  186. else if(p instanceof CalculationChain) calcChain = (CalculationChain)p;
  187. else if(p instanceof MapInfo) mapInfo = (MapInfo)p;
  188. else if (p instanceof XSSFSheet) {
  189. shIdMap.put(p.getPackageRelationship().getId(), (XSSFSheet)p);
  190. }
  191. }
  192. stylesSource.setTheme(theme);
  193. if(sharedStringSource == null) {
  194. //Create SST if it is missing
  195. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
  196. }
  197. // Load individual sheets. The order of sheets is defined by the order of CTSheet elements in the workbook
  198. sheets = new ArrayList<XSSFSheet>(shIdMap.size());
  199. for (CTSheet ctSheet : this.workbook.getSheets().getSheetArray()) {
  200. XSSFSheet sh = shIdMap.get(ctSheet.getId());
  201. if(sh == null) {
  202. logger.log(POILogger.WARN, "Sheet with name " + ctSheet.getName() + " and r:id " + ctSheet.getId()+ " was defined, but didn't exist in package, skipping");
  203. continue;
  204. }
  205. sh.sheet = ctSheet;
  206. sh.onDocumentRead();
  207. sheets.add(sh);
  208. }
  209. // Process the named ranges
  210. namedRanges = new ArrayList<XSSFName>();
  211. if(workbook.isSetDefinedNames()) {
  212. for(CTDefinedName ctName : workbook.getDefinedNames().getDefinedNameArray()) {
  213. namedRanges.add(new XSSFName(ctName, this));
  214. }
  215. }
  216. } catch (XmlException e) {
  217. throw new POIXMLException(e);
  218. }
  219. }
  220. /**
  221. * Create a new CTWorkbook with all values set to default
  222. */
  223. private void onWorkbookCreate() {
  224. workbook = CTWorkbook.Factory.newInstance();
  225. // don't EVER use the 1904 date system
  226. CTWorkbookPr workbookPr = workbook.addNewWorkbookPr();
  227. workbookPr.setDate1904(false);
  228. CTBookViews bvs = workbook.addNewBookViews();
  229. CTBookView bv = bvs.addNewWorkbookView();
  230. bv.setActiveTab(0);
  231. workbook.addNewSheets();
  232. POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
  233. expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR);
  234. sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
  235. stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, XSSFFactory.getInstance());
  236. namedRanges = new ArrayList<XSSFName>();
  237. sheets = new ArrayList<XSSFSheet>();
  238. }
  239. /**
  240. * Create a new SpreadsheetML package and setup the default minimal content
  241. */
  242. protected static OPCPackage newPackage() {
  243. try {
  244. OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
  245. // Main part
  246. PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
  247. // Create main part relationship
  248. pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
  249. // Create main document part
  250. pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());
  251. pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
  252. return pkg;
  253. } catch (Exception e){
  254. throw new POIXMLException(e);
  255. }
  256. }
  257. /**
  258. * Return the underlying XML bean
  259. *
  260. * @return the underlying CTWorkbook bean
  261. */
  262. @Internal
  263. public CTWorkbook getCTWorkbook() {
  264. return this.workbook;
  265. }
  266. /**
  267. * Adds a picture to the workbook.
  268. *
  269. * @param pictureData The bytes of the picture
  270. * @param format The format of the picture.
  271. *
  272. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  273. * @see Workbook#PICTURE_TYPE_EMF
  274. * @see Workbook#PICTURE_TYPE_WMF
  275. * @see Workbook#PICTURE_TYPE_PICT
  276. * @see Workbook#PICTURE_TYPE_JPEG
  277. * @see Workbook#PICTURE_TYPE_PNG
  278. * @see Workbook#PICTURE_TYPE_DIB
  279. * @see #getAllPictures()
  280. */
  281. public int addPicture(byte[] pictureData, int format) {
  282. int imageNumber = getAllPictures().size() + 1;
  283. XSSFPictureData img = (XSSFPictureData)createRelationship(XSSFPictureData.RELATIONS[format], XSSFFactory.getInstance(), imageNumber, true);
  284. try {
  285. OutputStream out = img.getPackagePart().getOutputStream();
  286. out.write(pictureData);
  287. out.close();
  288. } catch (IOException e){
  289. throw new POIXMLException(e);
  290. }
  291. pictures.add(img);
  292. return imageNumber - 1;
  293. }
  294. /**
  295. * Adds a picture to the workbook.
  296. *
  297. * @param is The sream to read image from
  298. * @param format The format of the picture.
  299. *
  300. * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} .
  301. * @see Workbook#PICTURE_TYPE_EMF
  302. * @see Workbook#PICTURE_TYPE_WMF
  303. * @see Workbook#PICTURE_TYPE_PICT
  304. * @see Workbook#PICTURE_TYPE_JPEG
  305. * @see Workbook#PICTURE_TYPE_PNG
  306. * @see Workbook#PICTURE_TYPE_DIB
  307. * @see #getAllPictures()
  308. */
  309. public int addPicture(InputStream is, int format) throws IOException {
  310. int imageNumber = getAllPictures().size() + 1;
  311. XSSFPictureData img = (XSSFPictureData)createRelationship(XSSFPictureData.RELATIONS[format], XSSFFactory.getInstance(), imageNumber, true);
  312. OutputStream out = img.getPackagePart().getOutputStream();
  313. IOUtils.copy(is, out);
  314. out.close();
  315. pictures.add(img);
  316. return imageNumber - 1;
  317. }
  318. /**
  319. * Create an XSSFSheet from an existing sheet in the XSSFWorkbook.
  320. * The cloned sheet is a deep copy of the original.
  321. *
  322. * @return XSSFSheet representing the cloned sheet.
  323. * @throws IllegalArgumentException if the sheet index in invalid
  324. * @throws POIXMLException if there were errors when cloning
  325. */
  326. public XSSFSheet cloneSheet(int sheetNum) {
  327. validateSheetIndex(sheetNum);
  328. XSSFSheet srcSheet = sheets.get(sheetNum);
  329. String srcName = srcSheet.getSheetName();
  330. String clonedName = getUniqueSheetName(srcName);
  331. XSSFSheet clonedSheet = createSheet(clonedName);
  332. try {
  333. ByteArrayOutputStream out = new ByteArrayOutputStream();
  334. srcSheet.write(out);
  335. clonedSheet.read(new ByteArrayInputStream(out.toByteArray()));
  336. } catch (IOException e){
  337. throw new POIXMLException("Failed to clone sheet", e);
  338. }
  339. CTWorksheet ct = clonedSheet.getCTWorksheet();
  340. if(ct.isSetDrawing()) {
  341. logger.log(POILogger.WARN, "Cloning sheets with drawings is not yet supported.");
  342. ct.unsetDrawing();
  343. }
  344. if(ct.isSetLegacyDrawing()) {
  345. logger.log(POILogger.WARN, "Cloning sheets with comments is not yet supported.");
  346. ct.unsetLegacyDrawing();
  347. }
  348. clonedSheet.setSelected(false);
  349. return clonedSheet;
  350. }
  351. /**
  352. * Generate a valid sheet name based on the existing one. Used when cloning sheets.
  353. *
  354. * @param srcName the original sheet name to
  355. * @return clone sheet name
  356. */
  357. private String getUniqueSheetName(String srcName) {
  358. int uniqueIndex = 2;
  359. String baseName = srcName;
  360. int bracketPos = srcName.lastIndexOf('(');
  361. if (bracketPos > 0 && srcName.endsWith(")")) {
  362. String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length());
  363. try {
  364. uniqueIndex = Integer.parseInt(suffix.trim());
  365. uniqueIndex++;
  366. baseName = srcName.substring(0, bracketPos).trim();
  367. } catch (NumberFormatException e) {
  368. // contents of brackets not numeric
  369. }
  370. }
  371. while (true) {
  372. // Try and find the next sheet name that is unique
  373. String index = Integer.toString(uniqueIndex++);
  374. String name;
  375. if (baseName.length() + index.length() + 2 < 31) {
  376. name = baseName + " (" + index + ")";
  377. } else {
  378. name = baseName.substring(0, 31 - index.length() - 2) + "(" + index + ")";
  379. }
  380. //If the sheet name is unique, then set it otherwise move on to the next number.
  381. if (getSheetIndex(name) == -1) {
  382. return name;
  383. }
  384. }
  385. }
  386. /**
  387. * Create a new XSSFCellStyle and add it to the workbook's style table
  388. *
  389. * @return the new XSSFCellStyle object
  390. */
  391. public XSSFCellStyle createCellStyle() {
  392. return stylesSource.createCellStyle();
  393. }
  394. /**
  395. * Returns the instance of XSSFDataFormat for this workbook.
  396. *
  397. * @return the XSSFDataFormat object
  398. * @see org.apache.poi.ss.usermodel.DataFormat
  399. */
  400. public XSSFDataFormat createDataFormat() {
  401. if (formatter == null)
  402. formatter = new XSSFDataFormat(stylesSource);
  403. return formatter;
  404. }
  405. /**
  406. * Create a new Font and add it to the workbook's font table
  407. *
  408. * @return new font object
  409. */
  410. public XSSFFont createFont() {
  411. XSSFFont font = new XSSFFont();
  412. font.registerTo(stylesSource);
  413. return font;
  414. }
  415. public XSSFName createName() {
  416. CTDefinedName ctName = CTDefinedName.Factory.newInstance();
  417. ctName.setName("");
  418. XSSFName name = new XSSFName(ctName, this);
  419. namedRanges.add(name);
  420. return name;
  421. }
  422. /**
  423. * Create an XSSFSheet for this workbook, adds it to the sheets and returns
  424. * the high level representation. Use this to create new sheets.
  425. *
  426. * @return XSSFSheet representing the new sheet.
  427. */
  428. public XSSFSheet createSheet() {
  429. String sheetname = "Sheet" + (sheets.size());
  430. int idx = 0;
  431. while(getSheet(sheetname) != null) {
  432. sheetname = "Sheet" + idx;
  433. idx++;
  434. }
  435. return createSheet(sheetname);
  436. }
  437. /**
  438. * Create an XSSFSheet for this workbook, adds it to the sheets and returns
  439. * the high level representation. Use this to create new sheets.
  440. *
  441. * @param sheetname sheetname to set for the sheet, can't be duplicate, greater than 31 chars or contain /\?*[]
  442. * @return XSSFSheet representing the new sheet.
  443. * @throws IllegalArgumentException if the sheetname is invalid or the workbook already contains a sheet of this name
  444. */
  445. public XSSFSheet createSheet(String sheetname) {
  446. if (containsSheet( sheetname, sheets.size() ))
  447. throw new IllegalArgumentException( "The workbook already contains a sheet of this name");
  448. CTSheet sheet = addSheet(sheetname);
  449. int sheetNumber = 1;
  450. for(XSSFSheet sh : sheets) sheetNumber = (int)Math.max(sh.sheet.getSheetId() + 1, sheetNumber);
  451. XSSFSheet wrapper = (XSSFSheet)createRelationship(XSSFRelation.WORKSHEET, XSSFFactory.getInstance(), sheetNumber);
  452. wrapper.sheet = sheet;
  453. sheet.setId(wrapper.getPackageRelationship().getId());
  454. sheet.setSheetId(sheetNumber);
  455. if(sheets.size() == 0) wrapper.setSelected(true);
  456. sheets.add(wrapper);
  457. return wrapper;
  458. }
  459. protected XSSFDialogsheet createDialogsheet(String sheetname, CTDialogsheet dialogsheet) {
  460. XSSFSheet sheet = createSheet(sheetname);
  461. return new XSSFDialogsheet(sheet);
  462. }
  463. private CTSheet addSheet(String sheetname) {
  464. WorkbookUtil.validateSheetName(sheetname);
  465. CTSheet sheet = workbook.getSheets().addNewSheet();
  466. sheet.setName(sheetname);
  467. return sheet;
  468. }
  469. /**
  470. * Finds a font that matches the one with the supplied attributes
  471. */
  472. public XSSFFont findFont(short boldWeight, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
  473. return stylesSource.findFont(boldWeight, color, fontHeight, name, italic, strikeout, typeOffset, underline);
  474. }
  475. /**
  476. * Convenience method to get the active sheet. The active sheet is is the sheet
  477. * which is currently displayed when the workbook is viewed in Excel.
  478. * 'Selected' sheet(s) is a distinct concept.
  479. */
  480. public int getActiveSheetIndex() {
  481. //activeTab (Active Sheet Index) Specifies an unsignedInt
  482. //that contains the index to the active sheet in this book view.
  483. return (int)workbook.getBookViews().getWorkbookViewArray(0).getActiveTab();
  484. }
  485. /**
  486. * Gets all pictures from the Workbook.
  487. *
  488. * @return the list of pictures (a list of {@link XSSFPictureData} objects.)
  489. * @see #addPicture(byte[], int)
  490. */
  491. public List<XSSFPictureData> getAllPictures() {
  492. if(pictures == null) {
  493. //In OOXML pictures are referred to in sheets,
  494. //dive into sheet's relations, select drawings and their images
  495. pictures = new ArrayList<XSSFPictureData>();
  496. for(XSSFSheet sh : sheets){
  497. for(POIXMLDocumentPart dr : sh.getRelations()){
  498. if(dr instanceof XSSFDrawing){
  499. for(POIXMLDocumentPart img : dr.getRelations()){
  500. if(img instanceof XSSFPictureData){
  501. pictures.add((XSSFPictureData)img);
  502. }
  503. }
  504. }
  505. }
  506. }
  507. }
  508. return pictures;
  509. }
  510. /**
  511. * gGet the cell style object at the given index
  512. *
  513. * @param idx index within the set of styles
  514. * @return XSSFCellStyle object at the index
  515. */
  516. public XSSFCellStyle getCellStyleAt(short idx) {
  517. return stylesSource.getStyleAt(idx);
  518. }
  519. /**
  520. * Get the font at the given index number
  521. *
  522. * @param idx index number
  523. * @return XSSFFont at the index
  524. */
  525. public XSSFFont getFontAt(short idx) {
  526. return stylesSource.getFontAt(idx);
  527. }
  528. public XSSFName getName(String name) {
  529. int nameIndex = getNameIndex(name);
  530. if (nameIndex < 0) {
  531. return null;
  532. }
  533. return namedRanges.get(nameIndex);
  534. }
  535. public XSSFName getNameAt(int nameIndex) {
  536. int nNames = namedRanges.size();
  537. if (nNames < 1) {
  538. throw new IllegalStateException("There are no defined names in this workbook");
  539. }
  540. if (nameIndex < 0 || nameIndex > nNames) {
  541. throw new IllegalArgumentException("Specified name index " + nameIndex
  542. + " is outside the allowable range (0.." + (nNames-1) + ").");
  543. }
  544. return namedRanges.get(nameIndex);
  545. }
  546. /**
  547. * Gets the named range index by his name
  548. * <i>Note:</i>Excel named ranges are case-insensitive and
  549. * this method performs a case-insensitive search.
  550. *
  551. * @param name named range name
  552. * @return named range index
  553. */
  554. public int getNameIndex(String name) {
  555. int i = 0;
  556. for(XSSFName nr : namedRanges) {
  557. if(nr.getNameName().equals(name)) {
  558. return i;
  559. }
  560. i++;
  561. }
  562. return -1;
  563. }
  564. /**
  565. * Get the number of styles the workbook contains
  566. *
  567. * @return count of cell styles
  568. */
  569. public short getNumCellStyles() {
  570. return (short) (stylesSource).getNumCellStyles();
  571. }
  572. /**
  573. * Get the number of fonts in the this workbook
  574. *
  575. * @return number of fonts
  576. */
  577. public short getNumberOfFonts() {
  578. return (short)stylesSource.getFonts().size();
  579. }
  580. /**
  581. * Get the number of named ranges in the this workbook
  582. *
  583. * @return number of named ranges
  584. */
  585. public int getNumberOfNames() {
  586. return namedRanges.size();
  587. }
  588. /**
  589. * Get the number of worksheets in the this workbook
  590. *
  591. * @return number of worksheets
  592. */
  593. public int getNumberOfSheets() {
  594. return sheets.size();
  595. }
  596. /**
  597. * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
  598. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  599. * @return String Null if no print area has been defined
  600. */
  601. public String getPrintArea(int sheetIndex) {
  602. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  603. if (name == null) return null;
  604. //adding one here because 0 indicates a global named region; doesnt make sense for print areas
  605. return name.getRefersToFormula();
  606. }
  607. /**
  608. * Get sheet with the given name (case insensitive match)
  609. *
  610. * @param name of the sheet
  611. * @return XSSFSheet with the name provided or <code>null</code> if it does not exist
  612. */
  613. public XSSFSheet getSheet(String name) {
  614. for (XSSFSheet sheet : sheets) {
  615. if (name.equalsIgnoreCase(sheet.getSheetName())) {
  616. return sheet;
  617. }
  618. }
  619. return null;
  620. }
  621. /**
  622. * Get the XSSFSheet object at the given index.
  623. *
  624. * @param index of the sheet number (0-based physical & logical)
  625. * @return XSSFSheet at the provided index
  626. * @throws IllegalArgumentException if the index is out of range (index
  627. * &lt; 0 || index &gt;= getNumberOfSheets()).
  628. */
  629. public XSSFSheet getSheetAt(int index) {
  630. validateSheetIndex(index);
  631. return sheets.get(index);
  632. }
  633. /**
  634. * Returns the index of the sheet by his name (case insensitive match)
  635. *
  636. * @param name the sheet name
  637. * @return index of the sheet (0 based) or <tt>-1</tt if not found
  638. */
  639. public int getSheetIndex(String name) {
  640. for (int i = 0 ; i < sheets.size() ; ++i) {
  641. XSSFSheet sheet = sheets.get(i);
  642. if (name.equalsIgnoreCase(sheet.getSheetName())) {
  643. return i;
  644. }
  645. }
  646. return -1;
  647. }
  648. /**
  649. * Returns the index of the given sheet
  650. *
  651. * @param sheet the sheet to look up
  652. * @return index of the sheet (0 based). <tt>-1</tt> if not found
  653. */
  654. public int getSheetIndex(Sheet sheet) {
  655. int idx = 0;
  656. for(XSSFSheet sh : sheets){
  657. if(sh == sheet) return idx;
  658. idx++;
  659. }
  660. return -1;
  661. }
  662. /**
  663. * Get the sheet name
  664. *
  665. * @param sheetIx Number
  666. * @return Sheet name
  667. */
  668. public String getSheetName(int sheetIx) {
  669. validateSheetIndex(sheetIx);
  670. return sheets.get(sheetIx).getSheetName();
  671. }
  672. /**
  673. * Allows foreach loops:
  674. * <pre><code>
  675. * XSSFWorkbook wb = new XSSFWorkbook(package);
  676. * for(XSSFSheet sheet : wb){
  677. *
  678. * }
  679. * </code></pre>
  680. */
  681. public Iterator<XSSFSheet> iterator() {
  682. return sheets.iterator();
  683. }
  684. /**
  685. * Are we a normal workbook (.xlsx), or a
  686. * macro enabled workbook (.xlsm)?
  687. */
  688. public boolean isMacroEnabled() {
  689. return getPackagePart().getContentType().equals(XSSFRelation.MACROS_WORKBOOK.getContentType());
  690. }
  691. public void removeName(int nameIndex) {
  692. namedRanges.remove(nameIndex);
  693. }
  694. public void removeName(String name) {
  695. for (int i = 0; i < namedRanges.size(); i++) {
  696. XSSFName nm = namedRanges.get(i);
  697. if(nm.getNameName().equalsIgnoreCase(name)) {
  698. removeName(i);
  699. return;
  700. }
  701. }
  702. throw new IllegalArgumentException("Named range was not found: " + name);
  703. }
  704. /**
  705. * Delete the printarea for the sheet specified
  706. *
  707. * @param sheetIndex 0-based sheet index (0 = First Sheet)
  708. */
  709. public void removePrintArea(int sheetIndex) {
  710. int cont = 0;
  711. for (XSSFName name : namedRanges) {
  712. if (name.getNameName().equals(XSSFName.BUILTIN_PRINT_AREA) && name.getSheetIndex() == sheetIndex) {
  713. namedRanges.remove(cont);
  714. break;
  715. }
  716. cont++;
  717. }
  718. }
  719. /**
  720. * Removes sheet at the given index.<p/>
  721. *
  722. * Care must be taken if the removed sheet is the currently active or only selected sheet in
  723. * the workbook. There are a few situations when Excel must have a selection and/or active
  724. * sheet. (For example when printing - see Bug 40414).<br/>
  725. *
  726. * This method makes sure that if the removed sheet was active, another sheet will become
  727. * active in its place. Furthermore, if the removed sheet was the only selected sheet, another
  728. * sheet will become selected. The newly active/selected sheet will have the same index, or
  729. * one less if the removed sheet was the last in the workbook.
  730. *
  731. * @param index of the sheet (0-based)
  732. */
  733. public void removeSheetAt(int index) {
  734. validateSheetIndex(index);
  735. onSheetDelete(index);
  736. XSSFSheet sheet = getSheetAt(index);
  737. removeRelation(sheet);
  738. sheets.remove(index);
  739. }
  740. /**
  741. * Gracefully remove references to the sheet being deleted
  742. *
  743. * @param index the 0-based index of the sheet to delete
  744. */
  745. private void onSheetDelete(int index) {
  746. //delete the CTSheet reference from workbook.xml
  747. workbook.getSheets().removeSheet(index);
  748. //calculation chain is auxiliary, remove it as it may contain orphan references to deleted cells
  749. if(calcChain != null) {
  750. removeRelation(calcChain);
  751. calcChain = null;
  752. }
  753. //adjust indices of names ranges
  754. for (Iterator<XSSFName> it = namedRanges.iterator(); it.hasNext();) {
  755. XSSFName nm = it.next();
  756. CTDefinedName ct = nm.getCTName();
  757. if(!ct.isSetLocalSheetId()) continue;
  758. if (ct.getLocalSheetId() == index) {
  759. it.remove();
  760. } else if (ct.getLocalSheetId() > index){
  761. // Bump down by one, so still points at the same sheet
  762. ct.setLocalSheetId(ct.getLocalSheetId()-1);
  763. }
  764. }
  765. }
  766. /**
  767. * Retrieves the current policy on what to do when
  768. * getting missing or blank cells from a row.
  769. * The default is to return blank and null cells.
  770. * {@link MissingCellPolicy}
  771. */
  772. public MissingCellPolicy getMissingCellPolicy() {
  773. return _missingCellPolicy;
  774. }
  775. /**
  776. * Sets the policy on what to do when
  777. * getting missing or blank cells from a row.
  778. * This will then apply to all calls to
  779. * {@link Row#getCell(int)}}. See
  780. * {@link MissingCellPolicy}
  781. */
  782. public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
  783. _missingCellPolicy = missingCellPolicy;
  784. }
  785. /**
  786. * Convenience method to set the active sheet. The active sheet is is the sheet
  787. * which is currently displayed when the workbook is viewed in Excel.
  788. * 'Selected' sheet(s) is a distinct concept.
  789. */
  790. @SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support
  791. public void setActiveSheet(int index) {
  792. validateSheetIndex(index);
  793. for (CTBookView arrayBook : workbook.getBookViews().getWorkbookViewArray()) {
  794. arrayBook.setActiveTab(index);
  795. }
  796. }
  797. /**
  798. * Validate sheet index
  799. *
  800. * @param index the index to validate
  801. * @throws IllegalArgumentException if the index is out of range (index
  802. * &lt; 0 || index &gt;= getNumberOfSheets()).
  803. */
  804. private void validateSheetIndex(int index) {
  805. int lastSheetIx = sheets.size() - 1;
  806. if (index < 0 || index > lastSheetIx) {
  807. throw new IllegalArgumentException("Sheet index ("
  808. + index +") is out of range (0.." + lastSheetIx + ")");
  809. }
  810. }
  811. /**
  812. * Gets the first tab that is displayed in the list of tabs in excel.
  813. *
  814. * @return integer that contains the index to the active sheet in this book view.
  815. */
  816. public int getFirstVisibleTab() {
  817. CTBookViews bookViews = workbook.getBookViews();
  818. CTBookView bookView = bookViews.getWorkbookViewArray(0);
  819. return (short) bookView.getActiveTab();
  820. }
  821. /**
  822. * Sets the first tab that is displayed in the list of tabs in excel.
  823. *
  824. * @param index integer that contains the index to the active sheet in this book view.
  825. */
  826. public void setFirstVisibleTab(int index) {
  827. CTBookViews bookViews = workbook.getBookViews();
  828. CTBookView bookView= bookViews.getWorkbookViewArray(0);
  829. bookView.setActiveTab(index);
  830. }
  831. /**
  832. * Sets the printarea for the sheet provided
  833. * <p>
  834. * i.e. Reference = $A$1:$B$2
  835. * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
  836. * @param reference Valid name Reference for the Print Area
  837. */
  838. public void setPrintArea(int sheetIndex, String reference) {
  839. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  840. if (name == null) {
  841. name = createBuiltInName(XSSFName.BUILTIN_PRINT_AREA, sheetIndex);
  842. namedRanges.add(name);
  843. }
  844. //short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
  845. //name.setExternSheetNumber(externSheetIndex);
  846. String[] parts = COMMA_PATTERN.split(reference);
  847. StringBuffer sb = new StringBuffer(32);
  848. for (int i = 0; i < parts.length; i++) {
  849. if(i>0) {
  850. sb.append(",");
  851. }
  852. SheetNameFormatter.appendFormat(sb, getSheetName(sheetIndex));
  853. sb.append("!");
  854. sb.append(parts[i]);
  855. }
  856. name.setRefersToFormula(sb.toString());
  857. }
  858. /**
  859. * For the Convenience of Java Programmers maintaining pointers.
  860. * @see #setPrintArea(int, String)
  861. * @param sheetIndex Zero-based sheet index (0 = First Sheet)
  862. * @param startColumn Column to begin printarea
  863. * @param endColumn Column to end the printarea
  864. * @param startRow Row to begin the printarea
  865. * @param endRow Row to end the printarea
  866. */
  867. public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) {
  868. String reference=getReferencePrintArea(getSheetName(sheetIndex), startColumn, endColumn, startRow, endRow);
  869. setPrintArea(sheetIndex, reference);
  870. }
  871. /**
  872. * Sets the repeating rows and columns for a sheet.
  873. * <p/>
  874. * To set just repeating columns:
  875. * <pre>
  876. * workbook.setRepeatingRowsAndColumns(0,0,1,-1,-1);
  877. * </pre>
  878. * To set just repeating rows:
  879. * <pre>
  880. * workbook.setRepeatingRowsAndColumns(0,-1,-1,0,4);
  881. * </pre>
  882. * To remove all repeating rows and columns for a sheet.
  883. * <pre>
  884. * workbook.setRepeatingRowsAndColumns(0,-1,-1,-1,-1);
  885. * </pre>
  886. *
  887. * @param sheetIndex 0 based index to sheet.
  888. * @param startColumn 0 based start of repeating columns.
  889. * @param endColumn 0 based end of repeating columns.
  890. * @param startRow 0 based start of repeating rows.
  891. * @param endRow 0 based end of repeating rows.
  892. */
  893. public void setRepeatingRowsAndColumns(int sheetIndex,
  894. int startColumn, int endColumn,
  895. int startRow, int endRow) {
  896. // Check arguments
  897. if ((startColumn == -1 && endColumn != -1) || startColumn < -1 || endColumn < -1 || startColumn > endColumn)
  898. throw new IllegalArgumentException("Invalid column range specification");
  899. if ((startRow == -1 && endRow != -1) || startRow < -1 || endRow < -1 || startRow > endRow)
  900. throw new IllegalArgumentException("Invalid row range specification");
  901. XSSFSheet sheet = getSheetAt(sheetIndex);
  902. boolean removingRange = startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
  903. XSSFName name = getBuiltInName(XSSFName.BUILTIN_PRINT_TITLE, sheetIndex);
  904. if (removingRange) {
  905. if(name != null)namedRanges.remove(name);
  906. return;
  907. }
  908. if (name == null) {
  909. name = createBuiltInName(XSSFName.BUILTIN_PRINT_TITLE, sheetIndex);
  910. namedRanges.add(name);
  911. }
  912. String reference = getReferenceBuiltInRecord(name.getSheetName(), startColumn, endColumn, startRow, endRow);
  913. name.setRefersToFormula(reference);
  914. XSSFPrintSetup printSetup = sheet.getPrintSetup();
  915. printSetup.setValidSettings(false);
  916. }
  917. private static String getReferenceBuiltInRecord(String sheetName, int startC, int endC, int startR, int endR) {
  918. //windows excel example for built-in title: 'second sheet'!$E:$F,'second sheet'!$2:$3
  919. CellReference colRef = new CellReference(sheetName, 0, startC, true, true);
  920. CellReference colRef2 = new CellReference(sheetName, 0, endC, true, true);
  921. String escapedName = SheetNameFormatter.format(sheetName);
  922. String c;
  923. if(startC == -1 && endC == -1) c= "";
  924. else c = escapedName + "!$" + colRef.getCellRefParts()[2] + ":$" + colRef2.getCellRefParts()[2];
  925. CellReference rowRef = new CellReference(sheetName, startR, 0, true, true);
  926. CellReference rowRef2 = new CellReference(sheetName, endR, 0, true, true);
  927. String r = "";
  928. if(startR == -1 && endR == -1) r = "";
  929. else {
  930. if (!rowRef.getCellRefParts()[1].equals("0") && !rowRef2.getCellRefParts()[1].equals("0")) {
  931. r = escapedName + "!$" + rowRef.getCellRefParts()[1] + ":$" + rowRef2.getCellRefParts()[1];
  932. }
  933. }
  934. StringBuffer rng = new StringBuffer();
  935. rng.append(c);
  936. if(rng.length() > 0 && r.length() > 0) rng.append(',');
  937. rng.append(r);
  938. return rng.toString();
  939. }
  940. private static String getReferencePrintArea(String sheetName, int startC, int endC, int startR, int endR) {
  941. //windows excel example: Sheet1!$C$3:$E$4
  942. CellReference colRef = new CellReference(sheetName, startR, startC, true, true);
  943. CellReference colRef2 = new CellReference(sheetName, endR, endC, true, true);
  944. return "$" + colRef.getCellRefParts()[2] + "$" + colRef.getCellRefParts()[1] + ":$" + colRef2.getCellRefParts()[2] + "$" + colRef2.getCellRefParts()[1];
  945. }
  946. private XSSFName getBuiltInName(String builtInCode, int sheetNumber) {
  947. for (XSSFName name : namedRanges) {
  948. if (name.getNameName().equalsIgnoreCase(builtInCode) && name.getSheetIndex() == sheetNumber) {
  949. return name;
  950. }
  951. }
  952. return null;
  953. }
  954. /**
  955. * Generates a NameRecord to represent a built-in region
  956. *
  957. * @return a new NameRecord
  958. * @throws IllegalArgumentException if sheetNumber is invalid
  959. * @throws POIXMLException if such a name already exists in the workbook
  960. */
  961. private XSSFName createBuiltInName(String builtInName, int sheetNumber) {
  962. validateSheetIndex(sheetNumber);
  963. CTDefinedNames names = workbook.getDefinedNames() == null ? workbook.addNewDefinedNames() : workbook.getDefinedNames();
  964. CTDefinedName nameRecord = names.addNewDefinedName();
  965. nameRecord.setName(builtInName);
  966. nameRecord.setLocalSheetId(sheetNumber);
  967. XSSFName name = new XSSFName(nameRecord, this);
  968. for (XSSFName nr : namedRanges) {
  969. if (nr.equals(name))
  970. throw new POIXMLException("Builtin (" + builtInName
  971. + ") already exists for sheet (" + sheetNumber + ")");
  972. }
  973. return name;
  974. }
  975. /**
  976. * We only set one sheet as selected for compatibility with HSSF.
  977. */
  978. public void setSelectedTab(int index) {
  979. for (int i = 0 ; i < sheets.size() ; ++i) {
  980. XSSFSheet sheet = sheets.get(i);
  981. sheet.setSelected(i == index);
  982. }
  983. }
  984. /**
  985. * Set the sheet name.
  986. * Will throw IllegalArgumentException if the name is greater than 31 chars
  987. * or contains /\?*[]
  988. *
  989. * @param sheet number (0 based)
  990. */
  991. public void setSheetName(int sheet, String name) {
  992. validateSheetIndex(sheet);
  993. WorkbookUtil.validateSheetName(name);
  994. if (containsSheet(name, sheet ))
  995. throw new IllegalArgumentException( "The workbook already contains a sheet of this name" );
  996. workbook.getSheets().getSheetArray(sheet).setName(name);
  997. }
  998. /**
  999. * sets the order of appearance for a given sheet.
  1000. *
  1001. * @param sheetname the name of the sheet to reorder
  1002. * @param pos the position that we want to insert the sheet into (0 based)
  1003. */
  1004. public void setSheetOrder(String sheetname, int pos) {
  1005. int idx = getSheetIndex(sheetname);
  1006. sheets.add(pos, sheets.remove(idx));
  1007. // Reorder CTSheets
  1008. CTSheets ct = workbook.getSheets();
  1009. XmlObject cts = ct.getSheetArray(idx).copy();
  1010. workbook.getSheets().removeSheet(idx);
  1011. CTSheet newcts = ct.insertNewSheet(pos);
  1012. newcts.set(cts);
  1013. //notify sheets
  1014. for(int i=0; i < sheets.size(); i++) {
  1015. sheets.get(i).sheet = ct.getSheetArray(i);
  1016. }
  1017. }
  1018. /**
  1019. * marshal named ranges from the {@link #namedRanges} collection to the underlying CTWorkbook bean
  1020. */
  1021. private void saveNamedRanges(){
  1022. // Named ranges
  1023. if(namedRanges.size() > 0) {
  1024. CTDefinedNames names = CTDefinedNames.Factory.newInstance();
  1025. CTDefinedName[] nr = new CTDefinedName[namedRanges.size()];
  1026. int i = 0;
  1027. for(XSSFName name : namedRanges) {
  1028. nr[i] = name.getCTName();
  1029. i++;
  1030. }
  1031. names.setDefinedNameArray(nr);
  1032. workbook.setDefinedNames(names);
  1033. } else {
  1034. if(workbook.isSetDefinedNames()) {
  1035. workbook.unsetDefinedNames();
  1036. }
  1037. }
  1038. }
  1039. private void saveCalculationChain(){
  1040. if(calcChain != null){
  1041. int count = calcChain.getCTCalcChain().sizeOfCArray();
  1042. if(count == 0){
  1043. removeRelation(calcChain);
  1044. calcChain = null;
  1045. }
  1046. }
  1047. }
  1048. @Override
  1049. protected void commit() throws IOException {
  1050. saveNamedRanges();
  1051. saveCalculationChain();
  1052. XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
  1053. xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorkbook.type.getName().getNamespaceURI(), "workbook"));
  1054. Map<String, String> map = new HashMap<String, String>();
  1055. map.put(STRelationshipId.type.getName().getNamespaceURI(), "r");
  1056. xmlOptions.setSaveSuggestedPrefixes(map);
  1057. PackagePart part = getPackagePart();
  1058. OutputStream out = part.getOutputStream();
  1059. workbook.save(out, xmlOptions);
  1060. out.close();
  1061. }
  1062. /**
  1063. * Returns SharedStringsTable - tha cache of string for this workbook
  1064. *
  1065. * @return the shared string table
  1066. */
  1067. @Internal
  1068. public SharedStringsTable getSharedStringSource() {
  1069. return this.sharedStringSource;
  1070. }
  1071. /**
  1072. * Return a object representing a collection of shared objects used for styling content,
  1073. * e.g. fonts, cell styles, colors, etc.
  1074. */
  1075. public StylesTable getStylesSource() {
  1076. return this.stylesSource;
  1077. }
  1078. /**
  1079. * Returns the Theme of current workbook.
  1080. */
  1081. public ThemesTable getTheme() {
  1082. return theme;
  1083. }
  1084. /**
  1085. * Returns an object that handles instantiating concrete
  1086. * classes of the various instances for XSSF.
  1087. */
  1088. public XSSFCreationHelper getCreationHelper() {
  1089. if(_creationHelper == null) _creationHelper = new XSSFCreationHelper(this);
  1090. return _creationHelper;
  1091. }
  1092. /**
  1093. * Determines whether a workbook contains the provided sheet name.
  1094. * For the purpose of comparison, long names are truncated to 31 chars.
  1095. *
  1096. * @param name the name to test (case insensitive match)
  1097. * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
  1098. * @return true if the sheet contains the name, false otherwise.
  1099. */
  1100. @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
  1101. private boolean containsSheet(String name, int excludeSheetIdx) {
  1102. CTSheet[] ctSheetArray = workbook.getSheets().getSheetArray();
  1103. if (name.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1104. name = name.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1105. }
  1106. for (int i = 0; i < ctSheetArray.length; i++) {
  1107. String ctName = ctSheetArray[i].getName();
  1108. if (ctName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  1109. ctName = ctName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  1110. }
  1111. if (excludeSheetIdx != i && name.equalsIgnoreCase(ctName))
  1112. return true;
  1113. }
  1114. return false;
  1115. }
  1116. /**
  1117. * Gets a boolean value that indicates whether the date systems used in the workbook starts in 1904.
  1118. * <p>
  1119. * The default value is false, meaning that the workbook uses the 1900 date system,
  1120. * where 1/1/1900 is the first day in the system..
  1121. * </p>
  1122. * @return true if the date systems used in the workbook starts in 1904
  1123. */
  1124. protected boolean isDate1904(){
  1125. CTWorkbookPr workbookPr = workbook.getWorkbookPr();
  1126. return workbookPr != null && workbookPr.getDate1904();
  1127. }
  1128. /**
  1129. * Get the document's embedded files.
  1130. */
  1131. public List<PackagePart> getAllEmbedds() throws OpenXML4JException {
  1132. List<PackagePart> embedds = new LinkedList<PackagePart>();
  1133. for(XSSFSheet sheet : sheets){
  1134. // Get the embeddings for the workbook
  1135. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.OLEEMBEDDINGS.getRelation()))
  1136. embedds.add(getTargetPart(rel));
  1137. for(PackageRelationship rel : sheet.getPackagePart().getRelationshipsByType(XSSFRelation.PACKEMBEDDINGS.getRelation()))
  1138. embedds.add(getTargetPart(rel));
  1139. }
  1140. return embedds;
  1141. }
  1142. public boolean isHidden() {
  1143. throw new RuntimeException("Not implemented yet");
  1144. }
  1145. public void setHidden(boolean hiddenFlag) {
  1146. throw new RuntimeException("Not implemented yet");
  1147. }
  1148. /**
  1149. * Check whether a sheet is hidden.
  1150. * <p>
  1151. * Note that a sheet could instead be set to be very hidden, which is different
  1152. * ({@link #isSheetVeryHidden(int)})
  1153. * </p>
  1154. * @param sheetIx Number
  1155. * @return <code>true</code> if sheet is hidden
  1156. */
  1157. public boolean isSheetHidden(int sheetIx) {
  1158. validateSheetIndex(sheetIx);
  1159. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1160. return ctSheet.getState() == STSheetState.HIDDEN;
  1161. }
  1162. /**
  1163. * Check whether a sheet is very hidden.
  1164. * <p>
  1165. * This is different from the normal hidden status
  1166. * ({@link #isSheetHidden(int)})
  1167. * </p>
  1168. * @param sheetIx sheet index to check
  1169. * @return <code>true</code> if sheet is very hidden
  1170. */
  1171. public boolean isSheetVeryHidden(int sheetIx) {
  1172. validateSheetIndex(sheetIx);
  1173. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1174. return ctSheet.getState() == STSheetState.VERY_HIDDEN;
  1175. }
  1176. /**
  1177. * Sets the visible state of this sheet.
  1178. * <p>
  1179. * Calling <code>setSheetHidden(sheetIndex, true)</code> is equivalent to
  1180. * <code>setSheetHidden(sheetIndex, Workbook.SHEET_STATE_HIDDEN)</code>.
  1181. * <br/>
  1182. * Calling <code>setSheetHidden(sheetIndex, false)</code> is equivalent to
  1183. * <code>setSheetHidden(sheetIndex, Workbook.SHEET_STATE_VISIBLE)</code>.
  1184. * </p>
  1185. *
  1186. * @param sheetIx the 0-based index of the sheet
  1187. * @param hidden whether this sheet is hidden
  1188. * @see #setSheetHidden(int, int)
  1189. */
  1190. public void setSheetHidden(int sheetIx, boolean hidden) {
  1191. setSheetHidden(sheetIx, hidden ? SHEET_STATE_HIDDEN : SHEET_STATE_VISIBLE);
  1192. }
  1193. /**
  1194. * Hide or unhide a sheet.
  1195. *
  1196. * <ul>
  1197. * <li>0 - visible. </li>
  1198. * <li>1 - hidden. </li>
  1199. * <li>2 - very hidden.</li>
  1200. * </ul>
  1201. * @param sheetIx the sheet index (0-based)
  1202. * @param state one of the following <code>Workbook</code> constants:
  1203. * <code>Workbook.SHEET_STATE_VISIBLE</code>,
  1204. * <code>Workbook.SHEET_STATE_HIDDEN</code>, or
  1205. * <code>Workbook.SHEET_STATE_VERY_HIDDEN</code>.
  1206. * @throws IllegalArgumentException if the supplied sheet index or state is invalid
  1207. */
  1208. public void setSheetHidden(int sheetIx, int state) {
  1209. validateSheetIndex(sheetIx);
  1210. WorkbookUtil.validateSheetState(state);
  1211. CTSheet ctSheet = sheets.get(sheetIx).sheet;
  1212. ctSheet.setState(STSheetState.Enum.forInt(state + 1));
  1213. }
  1214. /**
  1215. * Fired when a formula is deleted from this workbook,
  1216. * for example when calling cell.setCellFormula(null)
  1217. *
  1218. * @see XSSFCell#setCellFormula(String)
  1219. */
  1220. protected void onDeleteFormula(XSSFCell cell){
  1221. if(calcChain != null) {
  1222. int sheetId = (int)cell.getSheet().sheet.getSheetId();
  1223. calcChain.removeItem(sheetId, cell.getReference());
  1224. }
  1225. }
  1226. /**
  1227. * Return the CalculationChain object for this workbook
  1228. * <p>
  1229. * The calculation chain object specifies the order in which the cells in a workbook were last calculated
  1230. * </p>
  1231. *
  1232. * @return the <code>CalculationChain</code> object or <code>null</code> if not defined
  1233. */
  1234. @Internal
  1235. public CalculationChain getCalculationChain(){
  1236. return calcChain;
  1237. }
  1238. /**
  1239. *
  1240. * @return a collection of custom XML mappings defined in this workbook
  1241. */
  1242. public Collection<XSSFMap> getCustomXMLMappings(){
  1243. return mapInfo == null ? new ArrayList<XSSFMap>() : mapInfo.getAllXSSFMaps();
  1244. }
  1245. /**
  1246. *
  1247. * @return the helper class used to query the custom XML mapping defined in this workbook
  1248. */
  1249. @Internal
  1250. public MapInfo getMapInfo(){
  1251. return mapInfo;
  1252. }
  1253. /**
  1254. * Specifies a boolean value that indicates whether structure of workbook is locked. <br/>
  1255. * A value true indicates the structure of the workbook is locked. Worksheets in the workbook can't be moved,
  1256. * deleted, hidden, unhidden, or renamed, and new worksheets can't be inserted.<br/>
  1257. * A value of false indicates the structure of the workbook is not locked.<br/>
  1258. *
  1259. * @return true if structure of workbook is locked
  1260. */
  1261. public boolean isStructureLocked() {
  1262. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockStructure();
  1263. }
  1264. /**
  1265. * Specifies a boolean value that indicates whether the windows that comprise the workbook are locked. <br/>
  1266. * A value of true indicates the workbook windows are locked. Windows are the same size and position each time the
  1267. * workbook is opened.<br/>
  1268. * A value of false indicates the workbook windows are not locked.
  1269. *
  1270. * @return true if windows that comprise the workbook are locked
  1271. */
  1272. public boolean isWindowsLocked() {
  1273. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockWindows();
  1274. }
  1275. /**
  1276. * Specifies a boolean value that indicates whether the workbook is locked for revisions.
  1277. *
  1278. * @return true if the workbook is locked for revisions.
  1279. */
  1280. public boolean isRevisionLocked() {
  1281. return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockRevision();
  1282. }
  1283. /**
  1284. * Locks the structure of workbook.
  1285. */
  1286. public void lockStructure() {
  1287. createProtectionFieldIfNotPresent();
  1288. workbook.getWorkbookProtection().setLockStructure(true);
  1289. }
  1290. /**
  1291. * Unlocks the structure of workbook.
  1292. */
  1293. public void unLockStructure() {
  1294. createProtectionFieldIfNotPresent();
  1295. workbook.getWorkbookProtection().setLockStructure(false);
  1296. }
  1297. /**
  1298. * Locks the windows that comprise the workbook.
  1299. */
  1300. public void lockWindows() {
  1301. createProtectionFieldIfNotPresent();
  1302. workbook.getWorkbookProtection().setLockWindows(true);
  1303. }
  1304. /**
  1305. * Unlocks the windows that comprise the workbook.
  1306. */
  1307. public void unLockWindows() {
  1308. createProtectionFieldIfNotPresent();
  1309. workbook.getWorkbookProtection().setLockWindows(false);
  1310. }
  1311. /**
  1312. * Locks the workbook for revisions.
  1313. */
  1314. public void lockRevision() {
  1315. createProtectionFieldIfNotPresent();
  1316. workbook.getWorkbookProtection().setLockRevision(true);
  1317. }
  1318. /**
  1319. * Unlocks the workbook for revisions.
  1320. */
  1321. public void unLockRevision() {
  1322. createProtectionFieldIfNotPresent();
  1323. workbook.getWorkbookProtection().setLockRevision(false);
  1324. }
  1325. private boolean workbookProtectionPresent() {
  1326. return workbook.getWorkbookProtection() != null;
  1327. }
  1328. private void createProtectionFieldIfNotPresent() {
  1329. if (workbook.getWorkbookProtection() == null){
  1330. workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance());
  1331. }
  1332. }
  1333. /**
  1334. *
  1335. * Returns the locator of user-defined functions.
  1336. * <p>
  1337. * The default instance extends the built-in functions with the Excel Analysis Tool Pack.
  1338. * To set / evaluate custom functions you need to register them as follows:
  1339. *
  1340. *
  1341. *
  1342. * </p>
  1343. * @return wrapped instance of UDFFinder that allows seeking functions both by index and name
  1344. */
  1345. /*package*/ UDFFinder getUDFFinder() {
  1346. return _udfFinder;
  1347. }
  1348. /**
  1349. * Register a new toolpack in this workbook.
  1350. *
  1351. * @param toopack the toolpack to register
  1352. */
  1353. public void addToolPack(UDFFinder toopack){
  1354. _udfFinder.add(toopack);
  1355. }
  1356. }