2008-01-16 11:08:22 -05:00
|
|
|
/* ====================================================================
|
|
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
|
|
this work for additional information regarding copyright ownership.
|
|
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
|
(the "License"); you may not use this file except in compliance with
|
|
|
|
the License. You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
==================================================================== */
|
|
|
|
|
|
|
|
package org.apache.poi.xssf.usermodel;
|
|
|
|
|
2015-11-21 14:37:26 -05:00
|
|
|
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
|
2016-08-01 14:20:43 -04:00
|
|
|
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.setPassword;
|
|
|
|
import static org.apache.poi.xssf.usermodel.helpers.XSSFPasswordHelper.validatePassword;
|
2014-09-04 18:50:28 -04:00
|
|
|
|
2008-03-21 15:14:43 -04:00
|
|
|
import java.io.IOException;
|
2008-10-28 06:03:51 -04:00
|
|
|
import java.io.InputStream;
|
2009-05-17 14:32:55 -04:00
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
2015-11-29 10:27:34 -05:00
|
|
|
import java.util.Collection;
|
2015-10-29 02:53:55 -04:00
|
|
|
import java.util.Collections;
|
2015-03-15 16:52:55 -04:00
|
|
|
import java.util.Comparator;
|
2009-05-17 14:32:55 -04:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Iterator;
|
2016-01-13 12:54:24 -05:00
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
import java.util.LinkedHashSet;
|
2009-05-17 14:32:55 -04:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2016-01-13 12:54:24 -05:00
|
|
|
import java.util.Set;
|
2015-03-15 16:52:55 -04:00
|
|
|
import java.util.SortedMap;
|
2009-05-17 14:32:55 -04:00
|
|
|
import java.util.TreeMap;
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
import javax.xml.namespace.QName;
|
2016-12-31 16:50:47 -05:00
|
|
|
import javax.xml.stream.XMLStreamException;
|
|
|
|
import javax.xml.stream.XMLStreamReader;
|
2008-01-16 11:08:22 -05:00
|
|
|
|
2009-05-17 14:32:55 -04:00
|
|
|
import org.apache.poi.POIXMLDocumentPart;
|
|
|
|
import org.apache.poi.POIXMLException;
|
|
|
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
2011-09-22 06:37:54 -04:00
|
|
|
import org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException;
|
2009-05-17 14:32:55 -04:00
|
|
|
import org.apache.poi.openxml4j.opc.PackagePart;
|
|
|
|
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
|
|
|
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
|
2014-07-30 11:27:09 -04:00
|
|
|
import org.apache.poi.openxml4j.opc.TargetMode;
|
2014-09-04 18:50:28 -04:00
|
|
|
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
2009-05-17 14:32:55 -04:00
|
|
|
import org.apache.poi.ss.SpreadsheetVersion;
|
2011-04-08 16:30:38 -04:00
|
|
|
import org.apache.poi.ss.formula.FormulaShifter;
|
2012-08-04 04:51:49 -04:00
|
|
|
import org.apache.poi.ss.formula.SheetNameFormatter;
|
2014-09-14 18:57:38 -04:00
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
2015-11-01 21:50:33 -05:00
|
|
|
import org.apache.poi.ss.usermodel.CellCopyPolicy;
|
2014-09-14 18:57:38 -04:00
|
|
|
import org.apache.poi.ss.usermodel.CellRange;
|
|
|
|
import org.apache.poi.ss.usermodel.CellStyle;
|
2016-07-04 06:15:18 -04:00
|
|
|
import org.apache.poi.ss.usermodel.CellType;
|
2014-09-14 18:57:38 -04:00
|
|
|
import org.apache.poi.ss.usermodel.DataValidation;
|
|
|
|
import org.apache.poi.ss.usermodel.DataValidationHelper;
|
|
|
|
import org.apache.poi.ss.usermodel.Footer;
|
|
|
|
import org.apache.poi.ss.usermodel.Header;
|
2016-02-15 09:40:24 -05:00
|
|
|
import org.apache.poi.ss.usermodel.IgnoredErrorType;
|
2016-04-17 03:38:15 -04:00
|
|
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
2016-09-20 03:55:13 -04:00
|
|
|
import org.apache.poi.ss.usermodel.Name;
|
2014-09-14 18:57:38 -04:00
|
|
|
import org.apache.poi.ss.usermodel.Row;
|
|
|
|
import org.apache.poi.ss.usermodel.Sheet;
|
2016-09-20 03:55:13 -04:00
|
|
|
import org.apache.poi.ss.usermodel.Table;
|
2014-07-30 11:27:09 -04:00
|
|
|
import org.apache.poi.ss.util.AreaReference;
|
2015-11-23 10:11:28 -05:00
|
|
|
import org.apache.poi.ss.util.CellAddress;
|
2011-07-29 00:54:34 -04:00
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
2011-04-08 16:30:38 -04:00
|
|
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
|
|
|
import org.apache.poi.ss.util.CellReference;
|
2016-07-02 05:00:13 -04:00
|
|
|
import org.apache.poi.ss.util.PaneInformation;
|
2011-04-08 16:30:38 -04:00
|
|
|
import org.apache.poi.ss.util.SSCellRange;
|
|
|
|
import org.apache.poi.ss.util.SheetUtil;
|
2014-07-30 11:27:09 -04:00
|
|
|
import org.apache.poi.util.Beta;
|
2009-12-25 18:04:04 -05:00
|
|
|
import org.apache.poi.util.Internal;
|
2009-05-17 14:32:55 -04:00
|
|
|
import org.apache.poi.util.POILogFactory;
|
|
|
|
import org.apache.poi.util.POILogger;
|
2016-07-15 02:35:21 -04:00
|
|
|
import org.apache.poi.util.Removal;
|
2008-03-31 19:15:38 -04:00
|
|
|
import org.apache.poi.xssf.model.CommentsTable;
|
2016-09-20 03:55:13 -04:00
|
|
|
import org.apache.poi.xssf.usermodel.XSSFPivotTable.PivotTableReferenceConfigurator;
|
2008-01-25 11:25:14 -05:00
|
|
|
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
|
2016-02-15 09:40:24 -05:00
|
|
|
import org.apache.poi.xssf.usermodel.helpers.XSSFIgnoredErrorHelper;
|
2009-02-25 14:12:06 -05:00
|
|
|
import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter;
|
2016-12-31 16:50:47 -05:00
|
|
|
import org.apache.xmlbeans.XmlCursor;
|
2008-09-30 09:57:36 -04:00
|
|
|
import org.apache.xmlbeans.XmlException;
|
2016-12-31 16:50:47 -05:00
|
|
|
import org.apache.xmlbeans.XmlObject;
|
2009-05-17 14:32:55 -04:00
|
|
|
import org.apache.xmlbeans.XmlOptions;
|
2016-01-13 12:54:24 -05:00
|
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
|
2008-01-16 11:08:22 -05:00
|
|
|
|
2008-09-26 02:02:38 -04:00
|
|
|
/**
|
2008-09-30 09:57:36 -04:00
|
|
|
* High level representation of a SpreadsheetML worksheet.
|
2008-09-26 02:02:38 -04:00
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Sheets are the central structures within a workbook, and are where a user does most of his spreadsheet work.
|
|
|
|
* The most common type of sheet is the worksheet, which is represented as a grid of cells. Worksheet cells can
|
2008-09-30 09:57:36 -04:00
|
|
|
* contain text, numbers, dates, and formulas. Cells can also be formatted.
|
2008-09-26 02:02:38 -04:00
|
|
|
* </p>
|
|
|
|
*/
|
2016-12-02 19:49:08 -05:00
|
|
|
public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
|
2009-02-15 15:47:36 -05:00
|
|
|
private static final POILogger logger = POILogFactory.getLogger(XSSFSheet.class);
|
2008-10-10 10:54:32 -04:00
|
|
|
|
2016-04-12 18:44:28 -04:00
|
|
|
private static final double DEFAULT_ROW_HEIGHT = 15.0;
|
|
|
|
private static final double DEFAULT_MARGIN_HEADER = 0.3;
|
|
|
|
private static final double DEFAULT_MARGIN_FOOTER = 0.3;
|
|
|
|
private static final double DEFAULT_MARGIN_TOP = 0.75;
|
|
|
|
private static final double DEFAULT_MARGIN_BOTTOM = 0.75;
|
|
|
|
private static final double DEFAULT_MARGIN_LEFT = 0.7;
|
|
|
|
private static final double DEFAULT_MARGIN_RIGHT = 0.7;
|
|
|
|
public static final int TWIPS_PER_POINT = 20;
|
|
|
|
|
2009-11-30 09:09:03 -05:00
|
|
|
//TODO make the two variable below private!
|
2008-09-30 09:57:36 -04:00
|
|
|
protected CTSheet sheet;
|
|
|
|
protected CTWorksheet worksheet;
|
2009-11-30 09:09:03 -05:00
|
|
|
|
2016-06-14 22:56:56 -04:00
|
|
|
private final SortedMap<Integer, XSSFRow> _rows = new TreeMap<Integer, XSSFRow>();
|
2008-10-25 07:48:50 -04:00
|
|
|
private List<XSSFHyperlink> hyperlinks;
|
|
|
|
private ColumnHelper columnHelper;
|
|
|
|
private CommentsTable sheetComments;
|
2010-09-12 04:54:45 -04:00
|
|
|
/**
|
|
|
|
* cache of master shared formulas in this sheet.
|
|
|
|
* Master shared formula is the first formula in a group of shared formulas is saved in the f element.
|
|
|
|
*/
|
|
|
|
private Map<Integer, CTCellFormula> sharedFormulas;
|
2015-03-15 16:52:55 -04:00
|
|
|
private SortedMap<String,XSSFTable> tables;
|
2009-12-23 15:58:01 -05:00
|
|
|
private List<CellRangeAddress> arrayFormulas;
|
2013-08-14 10:57:44 -04:00
|
|
|
private XSSFDataValidationHelper dataValidationHelper;
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Creates new XSSFSheet - called by XSSFWorkbook to create a sheet from scratch.
|
|
|
|
*
|
|
|
|
* @see org.apache.poi.xssf.usermodel.XSSFWorkbook#createSheet()
|
|
|
|
*/
|
|
|
|
protected XSSFSheet() {
|
|
|
|
super();
|
2010-05-16 11:49:21 -04:00
|
|
|
dataValidationHelper = new XSSFDataValidationHelper(this);
|
2008-10-25 07:48:50 -04:00
|
|
|
onDocumentCreate();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Creates an XSSFSheet representing the given package part and relationship.
|
2016-01-10 15:44:17 -05:00
|
|
|
* Should only be called by XSSFWorkbook when reading in an existing file.
|
2008-10-25 07:48:50 -04:00
|
|
|
*
|
2016-01-10 15:44:17 -05:00
|
|
|
* @param part - The package part that holds xml data representing this sheet.
|
|
|
|
*
|
|
|
|
* @since POI 3.14-Beta1
|
2008-10-25 07:48:50 -04:00
|
|
|
*/
|
2016-01-10 15:44:17 -05:00
|
|
|
protected XSSFSheet(PackagePart part) {
|
|
|
|
super(part);
|
2010-05-16 11:49:21 -04:00
|
|
|
dataValidationHelper = new XSSFDataValidationHelper(this);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the parent XSSFWorkbook
|
|
|
|
*
|
|
|
|
* @return the parent XSSFWorkbook
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public XSSFWorkbook getWorkbook() {
|
|
|
|
return (XSSFWorkbook)getParent();
|
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Initialize worksheet data when reading in an exisiting file.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void onDocumentRead() {
|
|
|
|
try {
|
2008-10-28 06:03:51 -04:00
|
|
|
read(getPackagePart().getInputStream());
|
2008-10-25 07:48:50 -04:00
|
|
|
} catch (IOException e){
|
|
|
|
throw new POIXMLException(e);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2008-10-28 06:03:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void read(InputStream is) throws IOException {
|
|
|
|
try {
|
2015-11-21 14:37:26 -05:00
|
|
|
worksheet = WorksheetDocument.Factory.parse(is, DEFAULT_XML_OPTIONS).getWorksheet();
|
2008-10-28 06:03:51 -04:00
|
|
|
} catch (XmlException e){
|
|
|
|
throw new POIXMLException(e);
|
|
|
|
}
|
2008-10-25 07:48:50 -04:00
|
|
|
|
|
|
|
initRows(worksheet);
|
|
|
|
columnHelper = new ColumnHelper(worksheet);
|
2010-06-14 11:43:47 -04:00
|
|
|
// Look for bits we're interested in
|
2016-01-10 15:44:17 -05:00
|
|
|
for(RelationPart rp : getRelationParts()){
|
|
|
|
POIXMLDocumentPart p = rp.getDocumentPart();
|
2010-06-14 11:43:47 -04:00
|
|
|
if(p instanceof CommentsTable) {
|
|
|
|
sheetComments = (CommentsTable)p;
|
|
|
|
}
|
2011-04-14 10:32:49 -04:00
|
|
|
if(p instanceof XSSFTable) {
|
2016-01-10 15:44:17 -05:00
|
|
|
tables.put( rp.getRelationship().getId(), (XSSFTable)p );
|
2011-04-08 11:07:35 -04:00
|
|
|
}
|
2014-07-30 11:27:09 -04:00
|
|
|
if(p instanceof XSSFPivotTable) {
|
|
|
|
getWorkbook().getPivotTables().add((XSSFPivotTable) p);
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
// Process external hyperlinks for the sheet, if there are any
|
|
|
|
initHyperlinks();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize worksheet data when creating a new sheet.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void onDocumentCreate(){
|
|
|
|
worksheet = newSheet();
|
|
|
|
initRows(worksheet);
|
|
|
|
columnHelper = new ColumnHelper(worksheet);
|
|
|
|
hyperlinks = new ArrayList<XSSFHyperlink>();
|
|
|
|
}
|
|
|
|
|
2013-08-14 10:57:44 -04:00
|
|
|
private void initRows(CTWorksheet worksheetParam) {
|
2016-06-14 22:56:56 -04:00
|
|
|
_rows.clear();
|
2011-04-14 10:32:49 -04:00
|
|
|
tables = new TreeMap<String, XSSFTable>();
|
2010-09-12 04:54:45 -04:00
|
|
|
sharedFormulas = new HashMap<Integer, CTCellFormula>();
|
2009-12-23 15:58:01 -05:00
|
|
|
arrayFormulas = new ArrayList<CellRangeAddress>();
|
2013-08-14 10:57:44 -04:00
|
|
|
for (CTRow row : worksheetParam.getSheetData().getRowArray()) {
|
2008-10-25 07:48:50 -04:00
|
|
|
XSSFRow r = new XSSFRow(row, this);
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
|
|
|
final Integer rownumI = new Integer(r.getRowNum()); // NOSONAR
|
2016-06-12 12:57:15 -04:00
|
|
|
_rows.put(rownumI, r);
|
2008-10-25 07:48:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read hyperlink relations, link them with CTHyperlink beans in this worksheet
|
|
|
|
* and initialize the internal array of XSSFHyperlink objects
|
|
|
|
*/
|
|
|
|
private void initHyperlinks() {
|
2008-09-30 09:57:36 -04:00
|
|
|
hyperlinks = new ArrayList<XSSFHyperlink>();
|
2008-10-25 07:48:50 -04:00
|
|
|
|
2017-01-07 19:38:41 -05:00
|
|
|
if(!worksheet.isSetHyperlinks()) {
|
|
|
|
return;
|
|
|
|
}
|
2008-10-25 07:48:50 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
PackageRelationshipCollection hyperRels =
|
|
|
|
getPackagePart().getRelationshipsByType(XSSFRelation.SHEET_HYPERLINKS.getRelation());
|
|
|
|
|
|
|
|
// Turn each one into a XSSFHyperlink
|
2010-10-14 04:40:06 -04:00
|
|
|
for(CTHyperlink hyperlink : worksheet.getHyperlinks().getHyperlinkArray()) {
|
2008-10-25 07:48:50 -04:00
|
|
|
PackageRelationship hyperRel = null;
|
|
|
|
if(hyperlink.getId() != null) {
|
|
|
|
hyperRel = hyperRels.getRelationshipByID(hyperlink.getId());
|
|
|
|
}
|
|
|
|
|
|
|
|
hyperlinks.add( new XSSFHyperlink(hyperlink, hyperRel) );
|
|
|
|
}
|
|
|
|
} catch (InvalidFormatException e){
|
|
|
|
throw new POIXMLException(e);
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-25 07:48:50 -04:00
|
|
|
* Create a new CTWorksheet instance with all values set to defaults
|
2008-09-30 09:57:36 -04:00
|
|
|
*
|
|
|
|
* @return a new instance
|
|
|
|
*/
|
2008-10-25 07:48:50 -04:00
|
|
|
private static CTWorksheet newSheet(){
|
2008-09-30 09:57:36 -04:00
|
|
|
CTWorksheet worksheet = CTWorksheet.Factory.newInstance();
|
|
|
|
CTSheetFormatPr ctFormat = worksheet.addNewSheetFormatPr();
|
2016-04-12 18:44:28 -04:00
|
|
|
ctFormat.setDefaultRowHeight(DEFAULT_ROW_HEIGHT);
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
CTSheetView ctView = worksheet.addNewSheetViews().addNewSheetView();
|
|
|
|
ctView.setWorkbookViewId(0);
|
|
|
|
|
|
|
|
worksheet.addNewDimension().setRef("A1");
|
|
|
|
|
|
|
|
worksheet.addNewSheetData();
|
|
|
|
|
|
|
|
CTPageMargins ctMargins = worksheet.addNewPageMargins();
|
2016-04-12 18:44:28 -04:00
|
|
|
ctMargins.setBottom(DEFAULT_MARGIN_BOTTOM);
|
|
|
|
ctMargins.setFooter(DEFAULT_MARGIN_FOOTER);
|
|
|
|
ctMargins.setHeader(DEFAULT_MARGIN_HEADER);
|
|
|
|
ctMargins.setLeft(DEFAULT_MARGIN_LEFT);
|
|
|
|
ctMargins.setRight(DEFAULT_MARGIN_RIGHT);
|
|
|
|
ctMargins.setTop(DEFAULT_MARGIN_TOP);
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
return worksheet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-25 07:48:50 -04:00
|
|
|
* Provide access to the CTWorksheet bean holding this sheet's data
|
2008-09-30 09:57:36 -04:00
|
|
|
*
|
2008-10-25 07:48:50 -04:00
|
|
|
* @return the CTWorksheet bean holding this sheet's data
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2009-12-03 11:50:34 -05:00
|
|
|
@Internal
|
2008-10-25 07:48:50 -04:00
|
|
|
public CTWorksheet getCTWorksheet() {
|
2008-09-30 09:57:36 -04:00
|
|
|
return this.worksheet;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ColumnHelper getColumnHelper() {
|
|
|
|
return columnHelper;
|
|
|
|
}
|
|
|
|
|
2008-10-27 16:28:44 -04:00
|
|
|
/**
|
|
|
|
* Returns the name of this sheet
|
2009-08-18 01:29:53 -04:00
|
|
|
*
|
2008-10-27 16:28:44 -04:00
|
|
|
* @return the name of this sheet
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-27 16:28:44 -04:00
|
|
|
public String getSheetName() {
|
|
|
|
return sheet.getName();
|
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
2016-01-30 22:27:35 -05:00
|
|
|
* Adds a merged region of cells on a sheet.
|
2008-10-25 07:48:50 -04:00
|
|
|
*
|
2016-01-30 22:27:35 -05:00
|
|
|
* @param region to merge
|
2008-10-25 07:48:50 -04:00
|
|
|
* @return index of this region
|
2016-02-17 20:33:14 -05:00
|
|
|
* @throws IllegalArgumentException if region contains fewer than 2 cells
|
2015-10-31 06:22:19 -04:00
|
|
|
* @throws IllegalStateException if region intersects with a multi-cell array formula
|
|
|
|
* @throws IllegalStateException if region intersects with an existing region on this sheet
|
2008-10-25 07:48:50 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-12-29 14:47:38 -05:00
|
|
|
public int addMergedRegion(CellRangeAddress region) {
|
2016-01-30 22:27:35 -05:00
|
|
|
return addMergedRegion(region, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a merged region of cells (hence those cells form one).
|
|
|
|
* Skips validation. It is possible to create overlapping merged regions
|
|
|
|
* or create a merged region that intersects a multi-cell array formula
|
|
|
|
* with this formula, which may result in a corrupt workbook.
|
|
|
|
*
|
|
|
|
* To check for merged regions overlapping array formulas or other merged regions
|
2016-01-30 23:04:56 -05:00
|
|
|
* after addMergedRegionUnsafe has been called, call {@link #validateMergedRegions()}, which runs in O(n^2) time.
|
2016-01-30 22:27:35 -05:00
|
|
|
*
|
|
|
|
* @param region to merge
|
|
|
|
* @return index of this region
|
2016-02-17 20:33:14 -05:00
|
|
|
* @throws IllegalArgumentException if region contains fewer than 2 cells
|
2016-01-30 22:27:35 -05:00
|
|
|
*/
|
2016-03-22 05:02:08 -04:00
|
|
|
@Override
|
2016-01-30 22:27:35 -05:00
|
|
|
public int addMergedRegionUnsafe(CellRangeAddress region) {
|
|
|
|
return addMergedRegion(region, false);
|
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
|
2016-01-30 22:27:35 -05:00
|
|
|
/**
|
|
|
|
* Adds a merged region of cells (hence those cells form one).
|
|
|
|
* If validate is true, check to make sure adding the merged region to the sheet doesn't create a corrupt workbook
|
|
|
|
* If validate is false, skips the expensive merged region checks, but may produce a corrupt workbook.
|
|
|
|
*
|
|
|
|
* @param region to merge
|
|
|
|
* @param validate whether to validate merged region
|
|
|
|
* @return index of this region
|
2016-02-17 20:33:14 -05:00
|
|
|
* @throws IllegalArgumentException if region contains fewer than 2 cells (this check is inexpensive and is performed regardless of <tt>validate</tt>)
|
2016-01-30 22:27:35 -05:00
|
|
|
* @throws IllegalStateException if region intersects with a multi-cell array formula
|
|
|
|
* @throws IllegalStateException if region intersects with an existing region on this sheet
|
|
|
|
*/
|
|
|
|
private int addMergedRegion(CellRangeAddress region, boolean validate) {
|
|
|
|
if (region.getNumberOfCells() < 2) {
|
|
|
|
throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");
|
|
|
|
}
|
|
|
|
region.validate(SpreadsheetVersion.EXCEL2007);
|
2015-10-31 06:22:19 -04:00
|
|
|
|
2016-01-30 22:27:35 -05:00
|
|
|
if (validate) {
|
|
|
|
// throw IllegalStateException if the argument CellRangeAddress intersects with
|
|
|
|
// a multi-cell array formula defined in this sheet
|
|
|
|
validateArrayFormulas(region);
|
|
|
|
|
|
|
|
// Throw IllegalStateException if the argument CellRangeAddress intersects with
|
|
|
|
// a merged region already in this sheet
|
|
|
|
validateMergedRegions(region);
|
|
|
|
}
|
2009-04-06 11:06:23 -04:00
|
|
|
|
2008-10-21 13:56:34 -04:00
|
|
|
CTMergeCells ctMergeCells = worksheet.isSetMergeCells() ? worksheet.getMergeCells() : worksheet.addNewMergeCells();
|
|
|
|
CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();
|
2009-12-29 14:47:38 -05:00
|
|
|
ctMergeCell.setRef(region.formatAsString());
|
2008-09-30 09:57:36 -04:00
|
|
|
return ctMergeCells.sizeOfMergeCellArray();
|
|
|
|
}
|
|
|
|
|
2015-10-31 06:22:19 -04:00
|
|
|
/**
|
|
|
|
* Verify that the candidate region does not intersect with an existing multi-cell array formula in this sheet
|
|
|
|
*
|
2016-11-07 16:32:12 -05:00
|
|
|
* @param region a region that is validated.
|
2015-10-31 06:22:19 -04:00
|
|
|
* @throws IllegalStateException if candidate region intersects an existing array formula in this sheet
|
|
|
|
*/
|
2016-06-19 18:24:17 -04:00
|
|
|
private void validateArrayFormulas(CellRangeAddress region) {
|
|
|
|
// FIXME: this may be faster if it looped over array formulas directly rather than looping over each cell in
|
|
|
|
// the region and searching if that cell belongs to an array formula
|
2009-12-29 14:47:38 -05:00
|
|
|
int firstRow = region.getFirstRow();
|
|
|
|
int firstColumn = region.getFirstColumn();
|
|
|
|
int lastRow = region.getLastRow();
|
|
|
|
int lastColumn = region.getLastColumn();
|
2016-01-30 22:27:35 -05:00
|
|
|
// for each cell in sheet, if cell belongs to an array formula, check if merged region intersects array formula cells
|
2009-12-29 14:47:38 -05:00
|
|
|
for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
|
2016-06-19 18:24:17 -04:00
|
|
|
XSSFRow row = getRow(rowIn);
|
2017-01-07 19:38:41 -05:00
|
|
|
if (row == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-06-19 18:24:17 -04:00
|
|
|
|
2009-12-29 14:47:38 -05:00
|
|
|
for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
|
|
|
|
XSSFCell cell = row.getCell(colIn);
|
2017-01-07 19:38:41 -05:00
|
|
|
if (cell == null) {
|
|
|
|
continue;
|
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
|
2016-06-19 18:24:17 -04:00
|
|
|
if (cell.isPartOfArrayFormulaGroup()) {
|
2009-12-29 14:47:38 -05:00
|
|
|
CellRangeAddress arrayRange = cell.getArrayFormulaRange();
|
2016-06-17 06:22:09 -04:00
|
|
|
if (arrayRange.getNumberOfCells() > 1 && region.intersects(arrayRange)) {
|
2009-12-29 14:47:38 -05:00
|
|
|
String msg = "The range " + region.formatAsString() + " intersects with a multi-cell array formula. " +
|
|
|
|
"You cannot merge cells of an array.";
|
|
|
|
throw new IllegalStateException(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-30 22:27:35 -05:00
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
|
2016-01-30 22:27:35 -05:00
|
|
|
/**
|
|
|
|
* Verify that none of the merged regions intersect a multi-cell array formula in this sheet
|
|
|
|
*
|
|
|
|
* @throws IllegalStateException if candidate region intersects an existing array formula in this sheet
|
|
|
|
*/
|
|
|
|
private void checkForMergedRegionsIntersectingArrayFormulas() {
|
|
|
|
for (CellRangeAddress region : getMergedRegions()) {
|
|
|
|
validateArrayFormulas(region);
|
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
}
|
2016-01-30 22:27:35 -05:00
|
|
|
|
2015-10-31 06:22:19 -04:00
|
|
|
/**
|
|
|
|
* Verify that candidate region does not intersect with an existing merged region in this sheet
|
|
|
|
*
|
|
|
|
* @param candidateRegion
|
2016-01-30 22:27:35 -05:00
|
|
|
* @throws IllegalStateException if candidate region intersects an existing merged region in this sheet (or candidateRegion is already merged in this sheet)
|
2015-10-31 06:22:19 -04:00
|
|
|
*/
|
|
|
|
private void validateMergedRegions(CellRangeAddress candidateRegion) {
|
|
|
|
for (final CellRangeAddress existingRegion : getMergedRegions()) {
|
|
|
|
if (existingRegion.intersects(candidateRegion)) {
|
|
|
|
throw new IllegalStateException("Cannot add merged region " + candidateRegion.formatAsString() +
|
|
|
|
" to sheet because it overlaps with an existing merged region (" + existingRegion.formatAsString() + ").");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-12-29 14:47:38 -05:00
|
|
|
|
2016-01-30 22:27:35 -05:00
|
|
|
/**
|
|
|
|
* Verify that no merged regions intersect another merged region in this sheet.
|
|
|
|
*
|
|
|
|
* @throws IllegalStateException if at least one region intersects with another merged region in this sheet
|
|
|
|
*/
|
|
|
|
private void checkForIntersectingMergedRegions() {
|
|
|
|
final List<CellRangeAddress> regions = getMergedRegions();
|
|
|
|
final int size = regions.size();
|
|
|
|
for (int i=0; i < size; i++) {
|
|
|
|
final CellRangeAddress region = regions.get(i);
|
|
|
|
for (final CellRangeAddress other : regions.subList(i+1, regions.size())) {
|
|
|
|
if (region.intersects(other)) {
|
|
|
|
String msg = "The range " + region.formatAsString() +
|
|
|
|
" intersects with another merged region " +
|
|
|
|
other.formatAsString() + " in this sheet";
|
|
|
|
throw new IllegalStateException(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Verify that merged regions do not intersect multi-cell array formulas and
|
|
|
|
* no merged regions intersect another merged region in this sheet.
|
|
|
|
*
|
|
|
|
* @throws IllegalStateException if region intersects with a multi-cell array formula
|
|
|
|
* @throws IllegalStateException if at least one region intersects with another merged region in this sheet
|
|
|
|
*/
|
2016-03-22 05:02:08 -04:00
|
|
|
@Override
|
2016-01-30 22:27:35 -05:00
|
|
|
public void validateMergedRegions() {
|
|
|
|
checkForMergedRegionsIntersectingArrayFormulas();
|
|
|
|
checkForIntersectingMergedRegions();
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Adjusts the column width to fit the contents.
|
|
|
|
*
|
|
|
|
* This process can be relatively slow on large sheets, so this should
|
|
|
|
* normally only be called once per column, at the end of your
|
|
|
|
* processing.
|
|
|
|
*
|
|
|
|
* @param column the column index
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
|
|
|
public void autoSizeColumn(int column) {
|
2008-09-30 09:57:36 -04:00
|
|
|
autoSizeColumn(column, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adjusts the column width to fit the contents.
|
2008-10-25 07:48:50 -04:00
|
|
|
* <p>
|
2008-09-30 09:57:36 -04:00
|
|
|
* This process can be relatively slow on large sheets, so this should
|
|
|
|
* normally only be called once per column, at the end of your
|
|
|
|
* processing.
|
2008-10-25 07:48:50 -04:00
|
|
|
* </p>
|
2008-09-30 09:57:36 -04:00
|
|
|
* You can specify whether the content of merged cells should be considered or ignored.
|
|
|
|
* Default is to ignore merged cells.
|
|
|
|
*
|
|
|
|
* @param column the column index
|
|
|
|
* @param useMergedCells whether to use the contents of merged cells when calculating the width of the column
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-11-14 15:29:42 -05:00
|
|
|
public void autoSizeColumn(int column, boolean useMergedCells) {
|
2010-11-09 10:04:55 -05:00
|
|
|
double width = SheetUtil.getColumnWidth(this, column, useMergedCells);
|
2011-06-16 04:57:55 -04:00
|
|
|
|
|
|
|
if (width != -1) {
|
|
|
|
width *= 256;
|
|
|
|
int maxColumnWidth = 255*256; // The maximum column width for an individual cell is 255 characters
|
|
|
|
if (width > maxColumnWidth) {
|
|
|
|
width = maxColumnWidth;
|
|
|
|
}
|
|
|
|
setColumnWidth(column, (int)(width));
|
2008-10-21 13:56:34 -04:00
|
|
|
columnHelper.setColBestFit(column, true);
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2015-01-18 18:45:04 -05:00
|
|
|
|
2008-10-10 10:54:32 -04:00
|
|
|
/**
|
2015-01-18 18:45:04 -05:00
|
|
|
* Return the sheet's existing drawing, or null if there isn't yet one.
|
|
|
|
*
|
|
|
|
* Use {@link #createDrawingPatriarch()} to get or create
|
2008-10-10 10:54:32 -04:00
|
|
|
*
|
|
|
|
* @return a SpreadsheetML drawing
|
|
|
|
*/
|
2015-11-29 10:27:34 -05:00
|
|
|
@Override
|
2015-01-18 18:45:04 -05:00
|
|
|
public XSSFDrawing getDrawingPatriarch() {
|
2011-04-08 16:30:38 -04:00
|
|
|
CTDrawing ctDrawing = getCTDrawing();
|
2015-01-18 18:45:04 -05:00
|
|
|
if (ctDrawing != null) {
|
|
|
|
// Search the referenced drawing in the list of the sheet's relations
|
2016-01-10 15:44:17 -05:00
|
|
|
for (RelationPart rp : getRelationParts()){
|
|
|
|
POIXMLDocumentPart p = rp.getDocumentPart();
|
2015-01-18 18:45:04 -05:00
|
|
|
if (p instanceof XSSFDrawing) {
|
2008-10-10 10:54:32 -04:00
|
|
|
XSSFDrawing dr = (XSSFDrawing)p;
|
2016-01-10 15:44:17 -05:00
|
|
|
String drId = rp.getRelationship().getId();
|
2015-01-18 18:45:04 -05:00
|
|
|
if (drId.equals(ctDrawing.getId())){
|
|
|
|
return dr;
|
2008-10-10 10:54:32 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-01-18 18:45:04 -05:00
|
|
|
logger.log(POILogger.ERROR, "Can't find drawing with id=" + ctDrawing.getId() + " in the list of the sheet's relationships");
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new SpreadsheetML drawing. If this sheet already contains a drawing - return that.
|
|
|
|
*
|
|
|
|
* @return a SpreadsheetML drawing
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public XSSFDrawing createDrawingPatriarch() {
|
|
|
|
CTDrawing ctDrawing = getCTDrawing();
|
|
|
|
if (ctDrawing != null) {
|
|
|
|
return getDrawingPatriarch();
|
2008-10-10 10:54:32 -04:00
|
|
|
}
|
2015-01-18 18:45:04 -05:00
|
|
|
|
2016-10-14 06:44:03 -04:00
|
|
|
// Default drawingNumber = #drawings.size() + 1
|
2015-01-18 18:45:04 -05:00
|
|
|
int drawingNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.DRAWINGS.getContentType()).size() + 1;
|
2016-10-14 06:44:03 -04:00
|
|
|
drawingNumber = getNextPartNumber(XSSFRelation.DRAWINGS, drawingNumber);
|
2016-01-10 15:44:17 -05:00
|
|
|
RelationPart rp = createRelationship(XSSFRelation.DRAWINGS, XSSFFactory.getInstance(), drawingNumber, false);
|
|
|
|
XSSFDrawing drawing = rp.getDocumentPart();
|
|
|
|
String relId = rp.getRelationship().getId();
|
2015-01-18 18:45:04 -05:00
|
|
|
|
|
|
|
//add CT_Drawing element which indicates that this sheet contains drawing components built on the drawingML platform.
|
|
|
|
//The relationship Id references the part containing the drawingML definitions.
|
|
|
|
ctDrawing = worksheet.addNewDrawing();
|
|
|
|
ctDrawing.setId(relId);
|
|
|
|
|
|
|
|
// Return the newly created drawing
|
2008-10-10 10:54:32 -04:00
|
|
|
return drawing;
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2009-11-27 12:39:17 -05:00
|
|
|
/**
|
|
|
|
* Get VML drawing for this sheet (aka 'legacy' drawig)
|
|
|
|
*
|
|
|
|
* @param autoCreate if true, then a new VML drawing part is created
|
|
|
|
*
|
|
|
|
* @return the VML drawing of <code>null</code> if the drawing was not found and autoCreate=false
|
|
|
|
*/
|
|
|
|
protected XSSFVMLDrawing getVMLDrawing(boolean autoCreate) {
|
|
|
|
XSSFVMLDrawing drawing = null;
|
2011-04-08 16:30:38 -04:00
|
|
|
CTLegacyDrawing ctDrawing = getCTLegacyDrawing();
|
2009-11-27 12:39:17 -05:00
|
|
|
if(ctDrawing == null) {
|
|
|
|
if(autoCreate) {
|
|
|
|
//drawingNumber = #drawings.size() + 1
|
|
|
|
int drawingNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.VML_DRAWINGS.getContentType()).size() + 1;
|
2016-01-10 15:44:17 -05:00
|
|
|
RelationPart rp = createRelationship(XSSFRelation.VML_DRAWINGS, XSSFFactory.getInstance(), drawingNumber, false);
|
|
|
|
drawing = rp.getDocumentPart();
|
|
|
|
String relId = rp.getRelationship().getId();
|
2009-11-27 12:39:17 -05:00
|
|
|
|
|
|
|
//add CTLegacyDrawing element which indicates that this sheet contains drawing components built on the drawingML platform.
|
|
|
|
//The relationship Id references the part containing the drawing definitions.
|
|
|
|
ctDrawing = worksheet.addNewLegacyDrawing();
|
|
|
|
ctDrawing.setId(relId);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//search the referenced drawing in the list of the sheet's relations
|
2016-09-11 00:05:42 -04:00
|
|
|
final String id = ctDrawing.getId();
|
2016-01-10 15:44:17 -05:00
|
|
|
for (RelationPart rp : getRelationParts()){
|
|
|
|
POIXMLDocumentPart p = rp.getDocumentPart();
|
2009-11-27 12:39:17 -05:00
|
|
|
if(p instanceof XSSFVMLDrawing) {
|
|
|
|
XSSFVMLDrawing dr = (XSSFVMLDrawing)p;
|
2016-01-10 15:44:17 -05:00
|
|
|
String drId = rp.getRelationship().getId();
|
2016-09-11 00:05:42 -04:00
|
|
|
if (drId.equals(id)) {
|
2009-11-27 12:39:17 -05:00
|
|
|
drawing = dr;
|
|
|
|
break;
|
|
|
|
}
|
2016-09-11 00:05:42 -04:00
|
|
|
// do not break here since drawing has not been found yet (see bug 52425)
|
2009-11-27 12:39:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(drawing == null){
|
2016-09-11 00:05:42 -04:00
|
|
|
logger.log(POILogger.ERROR, "Can't find VML drawing with id=" + id + " in the list of the sheet's relationships");
|
2009-11-27 12:39:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return drawing;
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-08 16:30:38 -04:00
|
|
|
protected CTDrawing getCTDrawing() {
|
|
|
|
return worksheet.getDrawing();
|
|
|
|
}
|
|
|
|
protected CTLegacyDrawing getCTLegacyDrawing() {
|
|
|
|
return worksheet.getLegacyDrawing();
|
|
|
|
}
|
2009-11-27 12:39:17 -05:00
|
|
|
|
2009-05-17 14:32:55 -04:00
|
|
|
/**
|
|
|
|
* Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
|
2014-12-21 16:07:06 -05:00
|
|
|
* @param colSplit Horizontal position of split.
|
2009-11-27 15:54:03 -05:00
|
|
|
* @param rowSplit Vertical position of split.
|
2009-05-17 14:32:55 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-05-17 14:32:55 -04:00
|
|
|
public void createFreezePane(int colSplit, int rowSplit) {
|
|
|
|
createFreezePane( colSplit, rowSplit, colSplit, rowSplit );
|
|
|
|
}
|
2009-08-18 01:29:53 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
|
2011-06-25 08:19:49 -04:00
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* If both colSplit and rowSplit are zero then the existing freeze pane is removed
|
|
|
|
* </p>
|
|
|
|
*
|
2014-12-21 16:07:06 -05:00
|
|
|
* @param colSplit Horizontal position of split.
|
2009-11-27 15:54:03 -05:00
|
|
|
* @param rowSplit Vertical position of split.
|
2008-09-30 09:57:36 -04:00
|
|
|
* @param leftmostColumn Left column visible in right pane.
|
2011-06-25 08:19:49 -04:00
|
|
|
* @param topRow Top row visible in bottom pane
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void createFreezePane(int colSplit, int rowSplit, int leftmostColumn, int topRow) {
|
2011-06-25 08:19:49 -04:00
|
|
|
CTSheetView ctView = getDefaultSheetView();
|
|
|
|
|
|
|
|
// If both colSplit and rowSplit are zero then the existing freeze pane is removed
|
|
|
|
if(colSplit == 0 && rowSplit == 0){
|
2017-01-07 19:38:41 -05:00
|
|
|
if(ctView.isSetPane()) {
|
|
|
|
ctView.unsetPane();
|
|
|
|
}
|
2011-06-25 08:19:49 -04:00
|
|
|
ctView.setSelectionArray(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctView.isSetPane()) {
|
|
|
|
ctView.addNewPane();
|
|
|
|
}
|
|
|
|
CTPane pane = ctView.getPane();
|
|
|
|
|
|
|
|
if (colSplit > 0) {
|
2011-04-08 11:41:36 -04:00
|
|
|
pane.setXSplit(colSplit);
|
|
|
|
} else {
|
2017-01-07 19:38:41 -05:00
|
|
|
if(pane.isSetXSplit()) {
|
|
|
|
pane.unsetXSplit();
|
|
|
|
}
|
2011-04-08 11:41:36 -04:00
|
|
|
}
|
|
|
|
if (rowSplit > 0) {
|
|
|
|
pane.setYSplit(rowSplit);
|
|
|
|
} else {
|
2017-01-07 19:38:41 -05:00
|
|
|
if(pane.isSetYSplit()) {
|
|
|
|
pane.unsetYSplit();
|
|
|
|
}
|
2011-04-08 11:41:36 -04:00
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
pane.setState(STPaneState.FROZEN);
|
|
|
|
if (rowSplit == 0) {
|
2011-04-08 11:41:36 -04:00
|
|
|
pane.setTopLeftCell(new CellReference(0, leftmostColumn).formatAsString());
|
2008-10-29 15:12:47 -04:00
|
|
|
pane.setActivePane(STPane.TOP_RIGHT);
|
|
|
|
} else if (colSplit == 0) {
|
2011-04-08 11:41:36 -04:00
|
|
|
pane.setTopLeftCell(new CellReference(topRow, 0).formatAsString());
|
2008-10-29 15:12:47 -04:00
|
|
|
pane.setActivePane(STPane.BOTTOM_LEFT);
|
|
|
|
} else {
|
2011-03-18 12:38:58 -04:00
|
|
|
pane.setTopLeftCell(new CellReference(topRow, leftmostColumn).formatAsString());
|
2008-10-29 15:12:47 -04:00
|
|
|
pane.setActivePane(STPane.BOTTOM_RIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
ctView.setSelectionArray(null);
|
|
|
|
CTSelection sel = ctView.addNewSelection();
|
|
|
|
sel.setPane(pane.getActivePane());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new row within the sheet and return the high level representation
|
|
|
|
*
|
2015-09-13 15:05:55 -04:00
|
|
|
* Note: If a row already exists at this position, it is removed/overwritten and
|
|
|
|
* any existing cell is removed!
|
|
|
|
*
|
2008-09-30 09:57:36 -04:00
|
|
|
* @param rownum row number
|
|
|
|
* @return High level {@link XSSFRow} object representing a row in the sheet
|
|
|
|
* @see #removeRow(org.apache.poi.ss.usermodel.Row)
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public XSSFRow createRow(int rownum) {
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
2016-06-12 12:57:15 -04:00
|
|
|
final Integer rownumI = new Integer(rownum); // NOSONAR
|
2009-11-30 09:09:03 -05:00
|
|
|
CTRow ctRow;
|
2016-06-12 12:57:15 -04:00
|
|
|
XSSFRow prev = _rows.get(rownumI);
|
2009-11-30 09:09:03 -05:00
|
|
|
if(prev != null){
|
2015-05-19 09:13:09 -04:00
|
|
|
// the Cells in an existing row are invalidated on-purpose, in order to clean up correctly, we
|
|
|
|
// need to call the remove, so things like ArrayFormulas and CalculationChain updates are done
|
|
|
|
// correctly.
|
|
|
|
// We remove the cell this way as the internal cell-list is changed by the remove call and
|
|
|
|
// thus would cause ConcurrentModificationException otherwise
|
|
|
|
while(prev.getFirstCellNum() != -1) {
|
|
|
|
prev.removeCell(prev.getCell(prev.getFirstCellNum()));
|
|
|
|
}
|
|
|
|
|
2009-11-30 09:09:03 -05:00
|
|
|
ctRow = prev.getCTRow();
|
|
|
|
ctRow.set(CTRow.Factory.newInstance());
|
|
|
|
} else {
|
2014-06-11 16:36:30 -04:00
|
|
|
if(_rows.isEmpty() || rownum > _rows.lastKey()) {
|
|
|
|
// we can append the new row at the end
|
|
|
|
ctRow = worksheet.getSheetData().addNewRow();
|
|
|
|
} else {
|
|
|
|
// get number of rows where row index < rownum
|
|
|
|
// --> this tells us where our row should go
|
2016-06-12 12:57:15 -04:00
|
|
|
int idx = _rows.headMap(rownumI).size();
|
2014-06-11 16:36:30 -04:00
|
|
|
ctRow = worksheet.getSheetData().insertNewRow(idx);
|
|
|
|
}
|
2009-11-30 09:09:03 -05:00
|
|
|
}
|
2008-10-23 14:57:28 -04:00
|
|
|
XSSFRow r = new XSSFRow(ctRow, this);
|
|
|
|
r.setRowNum(rownum);
|
2016-06-12 12:57:15 -04:00
|
|
|
_rows.put(rownumI, r);
|
2008-10-23 14:57:28 -04:00
|
|
|
return r;
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a split pane. Any existing freezepane or split pane is overwritten.
|
2014-12-21 16:07:06 -05:00
|
|
|
* @param xSplitPos Horizontal position of split (in 1/20th of a point).
|
2009-11-27 15:54:03 -05:00
|
|
|
* @param ySplitPos Vertical position of split (in 1/20th of a point).
|
|
|
|
* @param topRow Top row visible in bottom pane
|
2008-09-30 09:57:36 -04:00
|
|
|
* @param leftmostColumn Left column visible in right pane.
|
2009-11-27 15:54:03 -05:00
|
|
|
* @param activePane Active pane. One of: PANE_LOWER_RIGHT,
|
|
|
|
* PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
|
2009-05-17 14:32:55 -04:00
|
|
|
* @see org.apache.poi.ss.usermodel.Sheet#PANE_LOWER_LEFT
|
|
|
|
* @see org.apache.poi.ss.usermodel.Sheet#PANE_LOWER_RIGHT
|
|
|
|
* @see org.apache.poi.ss.usermodel.Sheet#PANE_UPPER_LEFT
|
|
|
|
* @see org.apache.poi.ss.usermodel.Sheet#PANE_UPPER_RIGHT
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane) {
|
|
|
|
createFreezePane(xSplitPos, ySplitPos, leftmostColumn, topRow);
|
2009-05-17 14:32:55 -04:00
|
|
|
getPane().setState(STPaneState.SPLIT);
|
2008-09-30 09:57:36 -04:00
|
|
|
getPane().setActivePane(STPane.Enum.forInt(activePane));
|
|
|
|
}
|
|
|
|
|
2015-11-23 10:11:28 -05:00
|
|
|
/**
|
|
|
|
* Return cell comment at row, column, if one exists. Otherwise returns null.
|
2015-11-23 18:34:33 -05:00
|
|
|
* @param row the row where the comment is located
|
|
|
|
* @param column the column where the comment is located
|
2015-11-23 10:11:28 -05:00
|
|
|
* @return the cell comment, if one exists. Otherwise return null.
|
2015-11-23 18:34:33 -05:00
|
|
|
* @deprecated as of 2015-11-23 (circa POI 3.14beta1). Use {@link #getCellComment(CellAddress)} instead.
|
2015-11-23 10:11:28 -05:00
|
|
|
*/
|
2017-01-07 19:38:41 -05:00
|
|
|
@Deprecated
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2017-01-07 19:38:41 -05:00
|
|
|
@Removal(version="3.16")
|
2008-09-30 09:57:36 -04:00
|
|
|
public XSSFComment getCellComment(int row, int column) {
|
2015-11-23 10:11:28 -05:00
|
|
|
return getCellComment(new CellAddress(row, column));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return cell comment at row, column, if one exists. Otherwise returns null.
|
|
|
|
*
|
|
|
|
* @param address the location of the cell comment
|
|
|
|
* @return the cell comment, if one exists. Otherwise return null.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public XSSFComment getCellComment(CellAddress address) {
|
2009-08-18 12:50:24 -04:00
|
|
|
if (sheetComments == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2009-11-27 12:39:17 -05:00
|
|
|
|
2015-11-23 10:11:28 -05:00
|
|
|
final int row = address.getRow();
|
|
|
|
final int column = address.getColumn();
|
|
|
|
|
|
|
|
CellAddress ref = new CellAddress(row, column);
|
2009-11-27 12:39:17 -05:00
|
|
|
CTComment ctComment = sheetComments.getCTComment(ref);
|
2017-01-07 19:38:41 -05:00
|
|
|
if(ctComment == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2009-11-27 12:39:17 -05:00
|
|
|
|
|
|
|
XSSFVMLDrawing vml = getVMLDrawing(false);
|
|
|
|
return new XSSFComment(sheetComments, ctComment,
|
|
|
|
vml == null ? null : vml.findCommentShape(row, column));
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2015-11-23 10:11:28 -05:00
|
|
|
/**
|
|
|
|
* Returns all cell comments on this sheet.
|
|
|
|
* @return A map of each Comment in the sheet, keyed on the cell address where
|
|
|
|
* the comment is located.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public Map<CellAddress, XSSFComment> getCellComments() {
|
2016-03-23 12:42:04 -04:00
|
|
|
if (sheetComments == null) {
|
|
|
|
return Collections.emptyMap();
|
|
|
|
}
|
2015-11-23 10:11:28 -05:00
|
|
|
return sheetComments.getCellComments();
|
|
|
|
}
|
|
|
|
|
2015-10-29 02:53:55 -04:00
|
|
|
/**
|
|
|
|
* Get a Hyperlink in this sheet anchored at row, column
|
|
|
|
*
|
|
|
|
* @param row
|
|
|
|
* @param column
|
|
|
|
* @return hyperlink if there is a hyperlink anchored at row, column; otherwise returns null
|
|
|
|
*/
|
2015-11-02 04:40:49 -05:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public XSSFHyperlink getHyperlink(int row, int column) {
|
2016-07-17 00:41:20 -04:00
|
|
|
return getHyperlink(new CellAddress(row, column));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a Hyperlink in this sheet located in a cell specified by {code addr}
|
|
|
|
*
|
|
|
|
* @param addr The address of the cell containing the hyperlink
|
|
|
|
* @return hyperlink if there is a hyperlink anchored at {@code addr}; otherwise returns {@code null}
|
2016-07-17 01:47:03 -04:00
|
|
|
* @since POI 3.15 beta 3
|
2016-07-17 00:41:20 -04:00
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public XSSFHyperlink getHyperlink(CellAddress addr) {
|
|
|
|
String ref = addr.formatAsString();
|
2008-09-30 09:57:36 -04:00
|
|
|
for(XSSFHyperlink hyperlink : hyperlinks) {
|
|
|
|
if(hyperlink.getCellRef().equals(ref)) {
|
|
|
|
return hyperlink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2015-10-29 02:53:55 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of Hyperlinks in this sheet
|
|
|
|
*
|
2015-10-29 05:28:38 -04:00
|
|
|
* @return Hyperlinks for the sheet
|
2015-10-29 02:53:55 -04:00
|
|
|
*/
|
2015-11-02 04:40:49 -05:00
|
|
|
@Override
|
2015-10-29 02:53:55 -04:00
|
|
|
public List<XSSFHyperlink> getHyperlinkList() {
|
|
|
|
return Collections.unmodifiableList(hyperlinks);
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
private int[] getBreaks(CTPageBreak ctPageBreak) {
|
|
|
|
CTBreak[] brkArray = ctPageBreak.getBrkArray();
|
|
|
|
int[] breaks = new int[brkArray.length];
|
|
|
|
for (int i = 0 ; i < brkArray.length ; i++) {
|
|
|
|
breaks[i] = (int) brkArray[i].getId() - 1;
|
|
|
|
}
|
|
|
|
return breaks;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void removeBreak(int index, CTPageBreak ctPageBreak) {
|
|
|
|
int index1 = index + 1;
|
|
|
|
CTBreak[] brkArray = ctPageBreak.getBrkArray();
|
|
|
|
for (int i = 0 ; i < brkArray.length ; i++) {
|
|
|
|
if (brkArray[i].getId() == index1) {
|
|
|
|
ctPageBreak.removeBrk(i);
|
|
|
|
// TODO: check if we can break here, i.e. if a page can have more than 1 break on the same id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Vertical page break information used for print layout view, page layout view, drawing print breaks
|
|
|
|
* in normal view, and for printing the worksheet.
|
|
|
|
*
|
|
|
|
* @return column indexes of all the vertical page breaks, never <code>null</code>
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int[] getColumnBreaks() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return worksheet.isSetColBreaks() ? getBreaks(worksheet.getColBreaks()) : new int[0];
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the actual column width (in units of 1/256th of a character width )
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Note, the returned value is always gerater that {@link #getDefaultColumnWidth()} because the latter does not include margins.
|
|
|
|
* Actual column width measured as the number of characters of the maximum digit width of the
|
|
|
|
* numbers 0, 1, 2, ..., 9 as rendered in the normal style's font. There are 4 pixels of margin
|
|
|
|
* padding (two on each side), plus 1 pixel padding for the gridlines.
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* @param columnIndex - the column to set (0-based)
|
|
|
|
* @return width - the width in units of 1/256th of a character width
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getColumnWidth(int columnIndex) {
|
2008-10-10 10:54:32 -04:00
|
|
|
CTCol col = columnHelper.getColumn(columnIndex, false);
|
2009-03-27 07:50:52 -04:00
|
|
|
double width = col == null || !col.isSetWidth() ? getDefaultColumnWidth() : col.getWidth();
|
2008-10-17 11:14:00 -04:00
|
|
|
return (int)(width*256);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-11-11 19:19:00 -05:00
|
|
|
/**
|
|
|
|
* Get the actual column width in pixels
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Please note, that this method works correctly only for workbooks
|
|
|
|
* with the default font size (Calibri 11pt for .xlsx).
|
|
|
|
* </p>
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public float getColumnWidthInPixels(int columnIndex) {
|
|
|
|
float widthIn256 = getColumnWidth(columnIndex);
|
|
|
|
return (float)(widthIn256/256.0*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the default column width for the sheet (if the columns do not define their own width) in
|
|
|
|
* characters.
|
|
|
|
* <p>
|
|
|
|
* Note, this value is different from {@link #getColumnWidth(int)}. The latter is always greater and includes
|
|
|
|
* 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
|
|
|
|
* </p>
|
2008-10-29 15:12:47 -04:00
|
|
|
* @return column width, default value is 8
|
2008-10-17 11:14:00 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getDefaultColumnWidth() {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTSheetFormatPr pr = worksheet.getSheetFormatPr();
|
|
|
|
return pr == null ? 8 : (int)pr.getBaseColWidth();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the default row height for the sheet (if the rows do not define their own height) in
|
2016-04-12 18:44:28 -04:00
|
|
|
* twips (1/20 of a point)
|
2008-10-17 11:14:00 -04:00
|
|
|
*
|
|
|
|
* @return default row height
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public short getDefaultRowHeight() {
|
2016-04-12 18:44:28 -04:00
|
|
|
return (short)(getDefaultRowHeightInPoints() * TWIPS_PER_POINT);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-11-11 19:19:00 -05:00
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the default row height for the sheet measued in point size (if the rows do not define their own height).
|
|
|
|
*
|
|
|
|
* @return default row height in points
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-17 11:14:00 -04:00
|
|
|
public float getDefaultRowHeightInPoints() {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTSheetFormatPr pr = worksheet.getSheetFormatPr();
|
|
|
|
return (float)(pr == null ? 0 : pr.getDefaultRowHeight());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
private CTSheetFormatPr getSheetTypeSheetFormatPr() {
|
2008-10-17 11:14:00 -04:00
|
|
|
return worksheet.isSetSheetFormatPr() ?
|
|
|
|
worksheet.getSheetFormatPr() :
|
|
|
|
worksheet.addNewSheetFormatPr();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2009-08-18 01:29:53 -04:00
|
|
|
|
2009-01-06 15:49:02 -05:00
|
|
|
/**
|
|
|
|
* Returns the CellStyle that applies to the given
|
|
|
|
* (0 based) column, or null if no style has been
|
|
|
|
* set for that column
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-01-06 15:49:02 -05:00
|
|
|
public CellStyle getColumnStyle(int column) {
|
2009-03-27 07:50:52 -04:00
|
|
|
int idx = columnHelper.getColDefaultStyle(column);
|
|
|
|
return getWorkbook().getCellStyleAt((short)(idx == -1 ? 0 : idx));
|
2009-01-06 15:49:02 -05:00
|
|
|
}
|
|
|
|
|
2011-07-28 18:53:46 -04:00
|
|
|
/**
|
|
|
|
* Sets whether the worksheet is displayed from right to left instead of from left to right.
|
|
|
|
*
|
|
|
|
* @param value true for right to left, false otherwise.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2014-09-14 18:57:38 -04:00
|
|
|
public void setRightToLeft(boolean value) {
|
2011-07-28 18:53:46 -04:00
|
|
|
CTSheetView view = getDefaultSheetView();
|
|
|
|
view.setRightToLeft(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the text is displayed in right-to-left mode in the window
|
|
|
|
*
|
|
|
|
* @return whether the text is displayed in right-to-left mode in the window
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2014-09-14 18:57:38 -04:00
|
|
|
public boolean isRightToLeft() {
|
2011-07-28 18:53:46 -04:00
|
|
|
CTSheetView view = getDefaultSheetView();
|
2014-09-14 18:57:38 -04:00
|
|
|
return view != null && view.getRightToLeft();
|
2011-07-28 18:53:46 -04:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2008-10-01 13:15:02 -04:00
|
|
|
/**
|
|
|
|
* Get whether to display the guts or not,
|
|
|
|
* default value is true
|
|
|
|
*
|
|
|
|
* @return boolean - guts or no guts
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getDisplayGuts() {
|
2008-10-01 13:15:02 -04:00
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
CTOutlinePr outlinePr = sheetPr.getOutlinePr() == null ? CTOutlinePr.Factory.newInstance() : sheetPr.getOutlinePr();
|
|
|
|
return outlinePr.getShowOutlineSymbols();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether to display the guts or not
|
|
|
|
*
|
|
|
|
* @param value - guts or no guts
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-01 13:15:02 -04:00
|
|
|
public void setDisplayGuts(boolean value) {
|
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
CTOutlinePr outlinePr = sheetPr.getOutlinePr() == null ? sheetPr.addNewOutlinePr() : sheetPr.getOutlinePr();
|
|
|
|
outlinePr.setShowOutlineSymbols(value);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2009-02-24 03:38:16 -05:00
|
|
|
/**
|
|
|
|
* Gets the flag indicating whether the window should show 0 (zero) in cells containing zero value.
|
|
|
|
* When false, cells with zero value appear blank instead of showing the number zero.
|
2009-08-18 01:29:53 -04:00
|
|
|
*
|
2009-02-24 03:38:16 -05:00
|
|
|
* @return whether all zero values on the worksheet are displayed
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-02-24 03:38:16 -05:00
|
|
|
public boolean isDisplayZeros(){
|
|
|
|
CTSheetView view = getDefaultSheetView();
|
2014-09-14 18:57:38 -04:00
|
|
|
return view == null || view.getShowZeros();
|
2009-02-24 03:38:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether the window should show 0 (zero) in cells containing zero value.
|
|
|
|
* When false, cells with zero value appear blank instead of showing the number zero.
|
|
|
|
*
|
|
|
|
* @param value whether to display or hide all zero values on the worksheet
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-02-24 03:38:16 -05:00
|
|
|
public void setDisplayZeros(boolean value){
|
|
|
|
CTSheetView view = getSheetTypeSheetView();
|
|
|
|
view.setShowZeros(value);
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Gets the first row on the sheet
|
|
|
|
*
|
|
|
|
* @return the number of the first logical row on the sheet, zero based
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getFirstRowNum() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return _rows.isEmpty() ? 0 : _rows.firstKey();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether the Fit to Page print option is enabled.
|
|
|
|
*
|
|
|
|
* @return <code>true</code>
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getFitToPage() {
|
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
CTPageSetUpPr psSetup = (sheetPr == null || !sheetPr.isSetPageSetUpPr()) ?
|
|
|
|
CTPageSetUpPr.Factory.newInstance() : sheetPr.getPageSetUpPr();
|
|
|
|
return psSetup.getFitToPage();
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
private CTSheetPr getSheetTypeSheetPr() {
|
2008-09-30 09:57:36 -04:00
|
|
|
if (worksheet.getSheetPr() == null) {
|
|
|
|
worksheet.setSheetPr(CTSheetPr.Factory.newInstance());
|
|
|
|
}
|
|
|
|
return worksheet.getSheetPr();
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
private CTHeaderFooter getSheetTypeHeaderFooter() {
|
2008-09-30 09:57:36 -04:00
|
|
|
if (worksheet.getHeaderFooter() == null) {
|
|
|
|
worksheet.setHeaderFooter(CTHeaderFooter.Factory.newInstance());
|
|
|
|
}
|
|
|
|
return worksheet.getHeaderFooter();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the default footer for the sheet,
|
|
|
|
* creating one as needed.
|
|
|
|
* You may also want to look at
|
|
|
|
* {@link #getFirstFooter()},
|
|
|
|
* {@link #getOddFooter()} and
|
|
|
|
* {@link #getEvenFooter()}
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public Footer getFooter() {
|
|
|
|
// The default footer is an odd footer
|
|
|
|
return getOddFooter();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the default header for the sheet,
|
|
|
|
* creating one as needed.
|
|
|
|
* You may also want to look at
|
|
|
|
* {@link #getFirstHeader()},
|
|
|
|
* {@link #getOddHeader()} and
|
|
|
|
* {@link #getEvenHeader()}
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public Header getHeader() {
|
|
|
|
// The default header is an odd header
|
|
|
|
return getOddHeader();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the odd footer. Used on all pages unless
|
|
|
|
* other footers also present, when used on only
|
|
|
|
* odd pages.
|
|
|
|
*/
|
|
|
|
public Footer getOddFooter() {
|
|
|
|
return new XSSFOddFooter(getSheetTypeHeaderFooter());
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the even footer. Not there by default, but
|
|
|
|
* when set, used on even pages.
|
|
|
|
*/
|
|
|
|
public Footer getEvenFooter() {
|
|
|
|
return new XSSFEvenFooter(getSheetTypeHeaderFooter());
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the first page footer. Not there by
|
|
|
|
* default, but when set, used on the first page.
|
|
|
|
*/
|
|
|
|
public Footer getFirstFooter() {
|
|
|
|
return new XSSFFirstFooter(getSheetTypeHeaderFooter());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the odd header. Used on all pages unless
|
|
|
|
* other headers also present, when used on only
|
|
|
|
* odd pages.
|
|
|
|
*/
|
|
|
|
public Header getOddHeader() {
|
|
|
|
return new XSSFOddHeader(getSheetTypeHeaderFooter());
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the even header. Not there by default, but
|
|
|
|
* when set, used on even pages.
|
|
|
|
*/
|
|
|
|
public Header getEvenHeader() {
|
2008-10-17 11:14:00 -04:00
|
|
|
return new XSSFEvenHeader(getSheetTypeHeaderFooter());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the first page header. Not there by
|
|
|
|
* default, but when set, used on the first page.
|
|
|
|
*/
|
|
|
|
public Header getFirstHeader() {
|
|
|
|
return new XSSFFirstHeader(getSheetTypeHeaderFooter());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Determine whether printed output for this sheet will be horizontally centered.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getHorizontallyCenter() {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTPrintOptions opts = worksheet.getPrintOptions();
|
|
|
|
return opts != null && opts.getHorizontalCentered();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getLastRowNum() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return _rows.isEmpty() ? 0 : _rows.lastKey();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public short getLeftCol() {
|
|
|
|
String cellRef = worksheet.getSheetViews().getSheetViewArray(0).getTopLeftCell();
|
2013-06-17 05:06:39 -04:00
|
|
|
if(cellRef == null) {
|
2014-06-11 16:36:30 -04:00
|
|
|
return 0;
|
2013-06-17 05:06:39 -04:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
CellReference cellReference = new CellReference(cellRef);
|
2008-10-12 09:10:30 -04:00
|
|
|
return cellReference.getCol();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Gets the size of the margin in inches.
|
|
|
|
*
|
|
|
|
* @param margin which margin to get
|
|
|
|
* @return the size of the margin
|
|
|
|
* @see Sheet#LeftMargin
|
|
|
|
* @see Sheet#RightMargin
|
|
|
|
* @see Sheet#TopMargin
|
|
|
|
* @see Sheet#BottomMargin
|
|
|
|
* @see Sheet#HeaderMargin
|
|
|
|
* @see Sheet#FooterMargin
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public double getMargin(short margin) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if (!worksheet.isSetPageMargins()) {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-25 07:48:50 -04:00
|
|
|
|
|
|
|
CTPageMargins pageMargins = worksheet.getPageMargins();
|
2008-09-30 09:57:36 -04:00
|
|
|
switch (margin) {
|
2008-10-25 07:48:50 -04:00
|
|
|
case LeftMargin:
|
|
|
|
return pageMargins.getLeft();
|
|
|
|
case RightMargin:
|
|
|
|
return pageMargins.getRight();
|
|
|
|
case TopMargin:
|
|
|
|
return pageMargins.getTop();
|
|
|
|
case BottomMargin:
|
|
|
|
return pageMargins.getBottom();
|
|
|
|
case HeaderMargin:
|
|
|
|
return pageMargins.getHeader();
|
|
|
|
case FooterMargin:
|
|
|
|
return pageMargins.getFooter();
|
|
|
|
default :
|
2009-03-27 07:50:52 -04:00
|
|
|
throw new IllegalArgumentException("Unknown margin constant: " + margin);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Sets the size of the margin in inches.
|
|
|
|
*
|
|
|
|
* @param margin which margin to get
|
|
|
|
* @param size the size of the margin
|
|
|
|
* @see Sheet#LeftMargin
|
|
|
|
* @see Sheet#RightMargin
|
|
|
|
* @see Sheet#TopMargin
|
|
|
|
* @see Sheet#BottomMargin
|
|
|
|
* @see Sheet#HeaderMargin
|
|
|
|
* @see Sheet#FooterMargin
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-25 07:48:50 -04:00
|
|
|
public void setMargin(short margin, double size) {
|
|
|
|
CTPageMargins pageMargins = worksheet.isSetPageMargins() ?
|
|
|
|
worksheet.getPageMargins() : worksheet.addNewPageMargins();
|
|
|
|
switch (margin) {
|
|
|
|
case LeftMargin:
|
|
|
|
pageMargins.setLeft(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
2008-10-25 07:48:50 -04:00
|
|
|
case RightMargin:
|
|
|
|
pageMargins.setRight(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
2008-10-25 07:48:50 -04:00
|
|
|
case TopMargin:
|
|
|
|
pageMargins.setTop(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
2008-10-25 07:48:50 -04:00
|
|
|
case BottomMargin:
|
|
|
|
pageMargins.setBottom(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
2008-10-25 07:48:50 -04:00
|
|
|
case HeaderMargin:
|
|
|
|
pageMargins.setHeader(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
2008-10-25 07:48:50 -04:00
|
|
|
case FooterMargin:
|
|
|
|
pageMargins.setFooter(size);
|
2009-03-27 07:50:52 -04:00
|
|
|
break;
|
|
|
|
default :
|
|
|
|
throw new IllegalArgumentException( "Unknown margin constant: " + margin );
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-21 13:56:34 -04:00
|
|
|
/**
|
2015-07-13 09:00:35 -04:00
|
|
|
* Returns the merged region at the specified index. If you want multiple
|
|
|
|
* regions, it is faster to call {@link #getMergedRegions()} than to call
|
|
|
|
* this each time.
|
|
|
|
*
|
2008-10-21 13:56:34 -04:00
|
|
|
* @return the merged region at the specified index
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-21 13:56:34 -04:00
|
|
|
public CellRangeAddress getMergedRegion(int index) {
|
|
|
|
CTMergeCells ctMergeCells = worksheet.getMergeCells();
|
2017-01-07 19:38:41 -05:00
|
|
|
if(ctMergeCells == null) {
|
|
|
|
throw new IllegalStateException("This worksheet does not contain merged regions");
|
|
|
|
}
|
2008-10-21 13:56:34 -04:00
|
|
|
|
|
|
|
CTMergeCell ctMergeCell = ctMergeCells.getMergeCellArray(index);
|
|
|
|
String ref = ctMergeCell.getRef();
|
2009-04-06 11:06:23 -04:00
|
|
|
return CellRangeAddress.valueOf(ref);
|
2008-10-21 13:56:34 -04:00
|
|
|
}
|
|
|
|
|
2015-07-13 09:00:35 -04:00
|
|
|
/**
|
|
|
|
* Returns the list of merged regions. If you want multiple regions, this is
|
|
|
|
* faster than calling {@link #getMergedRegion(int)} each time.
|
|
|
|
*
|
|
|
|
* @return the list of merged regions
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public List<CellRangeAddress> getMergedRegions() {
|
2015-09-23 08:53:13 -04:00
|
|
|
List<CellRangeAddress> addresses = new ArrayList<CellRangeAddress>();
|
2015-07-13 09:00:35 -04:00
|
|
|
CTMergeCells ctMergeCells = worksheet.getMergeCells();
|
2017-01-07 19:38:41 -05:00
|
|
|
if(ctMergeCells == null) {
|
|
|
|
return addresses;
|
|
|
|
}
|
2015-07-13 09:00:35 -04:00
|
|
|
|
|
|
|
for(CTMergeCell ctMergeCell : ctMergeCells.getMergeCellArray()) {
|
|
|
|
String ref = ctMergeCell.getRef();
|
|
|
|
addresses.add(CellRangeAddress.valueOf(ref));
|
|
|
|
}
|
|
|
|
return addresses;
|
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Returns the number of merged regions defined in this worksheet
|
|
|
|
*
|
|
|
|
* @return number of merged regions in this worksheet
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getNumMergedRegions() {
|
2008-10-21 13:56:34 -04:00
|
|
|
CTMergeCells ctMergeCells = worksheet.getMergeCells();
|
|
|
|
return ctMergeCells == null ? 0 : ctMergeCells.sizeOfMergeCellArray();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getNumHyperlinks() {
|
|
|
|
return hyperlinks.size();
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Returns the information regarding the currently configured pane (split or freeze).
|
|
|
|
*
|
|
|
|
* @return null if no pane configured, or the pane information.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public PaneInformation getPaneInformation() {
|
2011-06-25 08:19:49 -04:00
|
|
|
CTPane pane = getDefaultSheetView().getPane();
|
|
|
|
// no pane configured
|
2017-01-07 19:38:41 -05:00
|
|
|
if(pane == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2011-06-25 08:19:49 -04:00
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
CellReference cellRef = pane.isSetTopLeftCell() ? new CellReference(pane.getTopLeftCell()) : null;
|
|
|
|
return new PaneInformation((short)pane.getXSplit(), (short)pane.getYSplit(),
|
|
|
|
(short)(cellRef == null ? 0 : cellRef.getRow()),(cellRef == null ? 0 : cellRef.getCol()),
|
2011-06-25 08:19:49 -04:00
|
|
|
(byte)(pane.getActivePane().intValue() - 1), pane.getState() == STPaneState.FROZEN);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
2014-09-14 18:57:38 -04:00
|
|
|
* Returns the number of physically defined rows (NOT the number of rows in the sheet)
|
2008-10-25 07:48:50 -04:00
|
|
|
*
|
2014-09-14 18:57:38 -04:00
|
|
|
* @return the number of physically defined rows
|
2008-10-25 07:48:50 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int getPhysicalNumberOfRows() {
|
2010-05-24 01:31:48 -04:00
|
|
|
return _rows.size();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Gets the print setup object.
|
|
|
|
*
|
|
|
|
* @return The user model for the print setup object.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-12 06:25:58 -04:00
|
|
|
public XSSFPrintSetup getPrintSetup() {
|
2008-10-25 07:48:50 -04:00
|
|
|
return new XSSFPrintSetup(worksheet);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Answer whether protection is enabled or disabled
|
|
|
|
*
|
|
|
|
* @return true => protection enabled; false => protection disabled
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getProtect() {
|
2014-09-04 18:50:28 -04:00
|
|
|
return isSheetLocked();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2010-08-18 08:49:05 -04:00
|
|
|
/**
|
|
|
|
* Enables sheet protection and sets the password for the sheet.
|
|
|
|
* Also sets some attributes on the {@link CTSheetProtection} that correspond to
|
|
|
|
* the default values used by Excel
|
2013-08-14 10:57:44 -04:00
|
|
|
*
|
2010-08-18 08:49:05 -04:00
|
|
|
* @param password to set for protection. Pass <code>null</code> to remove protection
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2010-08-18 08:49:05 -04:00
|
|
|
public void protectSheet(String password) {
|
2014-09-04 18:50:28 -04:00
|
|
|
if (password != null) {
|
|
|
|
CTSheetProtection sheetProtection = safeGetProtectionField();
|
|
|
|
setSheetPassword(password, null); // defaults to xor password
|
2014-06-11 16:36:30 -04:00
|
|
|
sheetProtection.setSheet(true);
|
|
|
|
sheetProtection.setScenarios(true);
|
|
|
|
sheetProtection.setObjects(true);
|
|
|
|
} else {
|
|
|
|
worksheet.unsetSheetProtection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Sets the sheet password.
|
|
|
|
*
|
|
|
|
* @param password if null, the password will be removed
|
|
|
|
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
|
|
|
|
* otherwise the given algorithm is used for calculating the hash password (Excel 2013)
|
2014-06-11 16:36:30 -04:00
|
|
|
*/
|
2014-09-04 18:50:28 -04:00
|
|
|
public void setSheetPassword(String password, HashAlgorithm hashAlgo) {
|
2016-04-12 18:44:28 -04:00
|
|
|
if (password == null && !isSheetProtectionEnabled()) {
|
|
|
|
return;
|
|
|
|
}
|
2014-09-04 18:50:28 -04:00
|
|
|
setPassword(safeGetProtectionField(), password, hashAlgo, null);
|
2014-06-11 16:36:30 -04:00
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2014-09-04 18:50:28 -04:00
|
|
|
/**
|
|
|
|
* Validate the password against the stored hash, the hashing method will be determined
|
|
|
|
* by the existing password attributes
|
|
|
|
* @return true, if the hashes match (... though original password may differ ...)
|
|
|
|
*/
|
|
|
|
public boolean validateSheetPassword(String password) {
|
2016-04-12 18:44:28 -04:00
|
|
|
if (!isSheetProtectionEnabled()) {
|
|
|
|
return (password == null);
|
|
|
|
}
|
2014-09-04 18:50:28 -04:00
|
|
|
return validatePassword(safeGetProtectionField(), password, null);
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Returns the logical row ( 0-based). If you ask for a row that is not
|
|
|
|
* defined you get a null. This is to say row 4 represents the fifth row on a sheet.
|
|
|
|
*
|
|
|
|
* @param rownum row to get
|
|
|
|
* @return <code>XSSFRow</code> representing the rownumber or <code>null</code> if its not defined on the sheet
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public XSSFRow getRow(int rownum) {
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
|
|
|
final Integer rownumI = new Integer(rownum); // NOSONAR
|
2016-06-12 12:57:15 -04:00
|
|
|
return _rows.get(rownumI);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2015-11-01 21:50:33 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* returns all rows between startRow and endRow, inclusive.
|
|
|
|
* Rows between startRow and endRow that haven't been created are not included
|
|
|
|
* in result unless createRowIfMissing is true
|
|
|
|
*
|
2016-05-29 17:33:47 -04:00
|
|
|
* @param startRowNum the first row number in this sheet to return
|
|
|
|
* @param endRowNum the last row number in this sheet to return
|
2015-11-01 21:50:33 -05:00
|
|
|
* @param createRowIfMissing
|
2015-11-04 13:25:35 -05:00
|
|
|
* @return All rows between startRow and endRow, inclusive
|
2015-11-01 21:50:33 -05:00
|
|
|
* @throws IllegalArgumentException if startRowNum and endRowNum are not in ascending order
|
|
|
|
*/
|
|
|
|
private List<XSSFRow> getRows(int startRowNum, int endRowNum, boolean createRowIfMissing) {
|
|
|
|
if (startRowNum > endRowNum) {
|
|
|
|
throw new IllegalArgumentException("getRows: startRowNum must be less than or equal to endRowNum");
|
|
|
|
}
|
|
|
|
final List<XSSFRow> rows = new ArrayList<XSSFRow>();
|
|
|
|
if (createRowIfMissing) {
|
|
|
|
for (int i = startRowNum; i <= endRowNum; i++) {
|
|
|
|
XSSFRow row = getRow(i);
|
|
|
|
if (row == null) {
|
|
|
|
row = createRow(i);
|
|
|
|
}
|
|
|
|
rows.add(row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
|
|
|
final Integer startI = new Integer(startRowNum); // NOSONAR
|
|
|
|
final Integer endI = new Integer(endRowNum+1); // NOSONAR
|
|
|
|
final Collection<XSSFRow> inclusive = _rows.subMap(startI, endI).values();
|
|
|
|
rows.addAll(inclusive);
|
2015-11-01 21:50:33 -05:00
|
|
|
}
|
|
|
|
return rows;
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Horizontal page break information used for print layout view, page layout view, drawing print breaks in normal
|
|
|
|
* view, and for printing the worksheet.
|
|
|
|
*
|
|
|
|
* @return row indexes of all the horizontal page breaks, never <code>null</code>
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public int[] getRowBreaks() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return worksheet.isSetRowBreaks() ? getBreaks(worksheet.getRowBreaks()) : new int[0];
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether summary rows appear below detail in an outline, when applying an outline.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* When true a summary row is inserted below the detailed data being summarized and a
|
|
|
|
* new outline level is established on that row.
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* When false a summary row is inserted above the detailed data being summarized and a new outline level
|
|
|
|
* is established on that row.
|
|
|
|
* </p>
|
|
|
|
* @return <code>true</code> if row summaries appear below detail in the outline
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getRowSumsBelow() {
|
|
|
|
CTSheetPr sheetPr = worksheet.getSheetPr();
|
|
|
|
CTOutlinePr outlinePr = (sheetPr != null && sheetPr.isSetOutlinePr())
|
2008-10-25 07:48:50 -04:00
|
|
|
? sheetPr.getOutlinePr() : null;
|
|
|
|
return outlinePr == null || outlinePr.getSummaryBelow();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether summary rows appear below detail in an outline, when applying an outline.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* When true a summary row is inserted below the detailed data being summarized and a
|
|
|
|
* new outline level is established on that row.
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* When false a summary row is inserted above the detailed data being summarized and a new outline level
|
|
|
|
* is established on that row.
|
|
|
|
* </p>
|
|
|
|
* @param value <code>true</code> if row summaries appear below detail in the outline
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setRowSumsBelow(boolean value) {
|
|
|
|
ensureOutlinePr().setSummaryBelow(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether summary columns appear to the right of detail in an outline, when applying an outline.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* When true a summary column is inserted to the right of the detailed data being summarized
|
|
|
|
* and a new outline level is established on that column.
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* When false a summary column is inserted to the left of the detailed data being
|
|
|
|
* summarized and a new outline level is established on that column.
|
|
|
|
* </p>
|
|
|
|
* @return <code>true</code> if col summaries appear right of the detail in the outline
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getRowSumsRight() {
|
|
|
|
CTSheetPr sheetPr = worksheet.getSheetPr();
|
|
|
|
CTOutlinePr outlinePr = (sheetPr != null && sheetPr.isSetOutlinePr())
|
|
|
|
? sheetPr.getOutlinePr() : CTOutlinePr.Factory.newInstance();
|
|
|
|
return outlinePr.getSummaryRight();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether summary columns appear to the right of detail in an outline, when applying an outline.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* When true a summary column is inserted to the right of the detailed data being summarized
|
|
|
|
* and a new outline level is established on that column.
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* When false a summary column is inserted to the left of the detailed data being
|
|
|
|
* summarized and a new outline level is established on that column.
|
|
|
|
* </p>
|
|
|
|
* @param value <code>true</code> if col summaries appear right of the detail in the outline
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setRowSumsRight(boolean value) {
|
|
|
|
ensureOutlinePr().setSummaryRight(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure CTWorksheet.CTSheetPr.CTOutlinePr
|
|
|
|
*/
|
|
|
|
private CTOutlinePr ensureOutlinePr(){
|
|
|
|
CTSheetPr sheetPr = worksheet.isSetSheetPr() ? worksheet.getSheetPr() : worksheet.addNewSheetPr();
|
2008-10-25 07:48:50 -04:00
|
|
|
return sheetPr.isSetOutlinePr() ? sheetPr.getOutlinePr() : sheetPr.addNewOutlinePr();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A flag indicating whether scenarios are locked when the sheet is protected.
|
|
|
|
*
|
|
|
|
* @return true => protection enabled; false => protection disabled
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getScenarioProtect() {
|
2008-10-25 07:48:50 -04:00
|
|
|
return worksheet.isSetSheetProtection() && worksheet.getSheetProtection().getScenarios();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The top row in the visible view when the sheet is
|
|
|
|
* first viewed after opening it in a viewer
|
|
|
|
*
|
|
|
|
* @return integer indicating the rownum (0 based) of the top row
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public short getTopRow() {
|
|
|
|
String cellRef = getSheetTypeSheetView().getTopLeftCell();
|
2013-06-17 05:06:39 -04:00
|
|
|
if(cellRef == null) {
|
2014-06-11 16:36:30 -04:00
|
|
|
return 0;
|
2013-06-17 05:06:39 -04:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
CellReference cellReference = new CellReference(cellRef);
|
|
|
|
return (short) cellReference.getRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine whether printed output for this sheet will be vertically centered.
|
|
|
|
*
|
|
|
|
* @return whether printed output for this sheet will be vertically centered.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean getVerticallyCenter() {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTPrintOptions opts = worksheet.getPrintOptions();
|
|
|
|
return opts != null && opts.getVerticalCentered();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Group between (0 based) columns
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-11-14 15:29:42 -05:00
|
|
|
public void groupColumn(int fromColumn, int toColumn) {
|
2008-09-30 09:57:36 -04:00
|
|
|
groupColumn1Based(fromColumn+1, toColumn+1);
|
|
|
|
}
|
|
|
|
private void groupColumn1Based(int fromColumn, int toColumn) {
|
|
|
|
CTCols ctCols=worksheet.getColsArray(0);
|
|
|
|
CTCol ctCol=CTCol.Factory.newInstance();
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2014-01-25 21:18:53 -05:00
|
|
|
// copy attributes, as they might be removed by merging with the new column
|
|
|
|
// TODO: check if this fix is really necessary or if the sweeping algorithm
|
|
|
|
// in addCleanColIntoCols needs to be adapted ...
|
|
|
|
CTCol fixCol_before = this.columnHelper.getColumn1Based(toColumn, false);
|
|
|
|
if (fixCol_before != null) {
|
2014-06-11 16:36:30 -04:00
|
|
|
fixCol_before = (CTCol)fixCol_before.copy();
|
2014-01-25 21:18:53 -05:00
|
|
|
}
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
ctCol.setMin(fromColumn);
|
|
|
|
ctCol.setMax(toColumn);
|
|
|
|
this.columnHelper.addCleanColIntoCols(ctCols, ctCol);
|
2014-01-25 21:18:53 -05:00
|
|
|
|
|
|
|
CTCol fixCol_after = this.columnHelper.getColumn1Based(toColumn, false);
|
|
|
|
if (fixCol_before != null && fixCol_after != null) {
|
|
|
|
this.columnHelper.setColumnAttributes(fixCol_before, fixCol_after);
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
for(int index=fromColumn;index<=toColumn;index++){
|
|
|
|
CTCol col=columnHelper.getColumn1Based(index, false);
|
|
|
|
//col must exist
|
|
|
|
short outlineLevel=col.getOutlineLevel();
|
|
|
|
col.setOutlineLevel((short)(outlineLevel+1));
|
|
|
|
index=(int)col.getMax();
|
|
|
|
}
|
|
|
|
worksheet.setColsArray(0,ctCols);
|
|
|
|
setSheetFormatPrOutlineLevelCol();
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2013-08-05 12:17:17 -04:00
|
|
|
/**
|
|
|
|
* Do not leave the width attribute undefined (see #52186).
|
|
|
|
*/
|
|
|
|
private void setColWidthAttribute(CTCols ctCols) {
|
2014-08-27 20:08:41 -04:00
|
|
|
for (CTCol col : ctCols.getColArray()) {
|
2014-06-11 16:36:30 -04:00
|
|
|
if (!col.isSetWidth()) {
|
|
|
|
col.setWidth(getDefaultColumnWidth());
|
|
|
|
col.setCustomWidth(false);
|
|
|
|
}
|
2013-08-05 12:17:17 -04:00
|
|
|
}
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2008-11-14 06:56:41 -05:00
|
|
|
/**
|
|
|
|
* Tie a range of cell together so that they can be collapsed or expanded
|
|
|
|
*
|
|
|
|
* @param fromRow start row (0-based)
|
|
|
|
* @param toRow end row (0-based)
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void groupRow(int fromRow, int toRow) {
|
2008-11-14 06:56:41 -05:00
|
|
|
for (int i = fromRow; i <= toRow; i++) {
|
|
|
|
XSSFRow xrow = getRow(i);
|
|
|
|
if (xrow == null) {
|
|
|
|
xrow = createRow(i);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2008-11-14 06:56:41 -05:00
|
|
|
CTRow ctrow = xrow.getCTRow();
|
|
|
|
short outlineLevel = ctrow.getOutlineLevel();
|
|
|
|
ctrow.setOutlineLevel((short) (outlineLevel + 1));
|
|
|
|
}
|
|
|
|
setSheetFormatPrOutlineLevelRow();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2008-09-11 11:11:57 -04:00
|
|
|
|
2008-11-14 06:56:41 -05:00
|
|
|
private short getMaxOutlineLevelRows(){
|
2014-09-14 18:57:38 -04:00
|
|
|
int outlineLevel = 0;
|
|
|
|
for (XSSFRow xrow : _rows.values()) {
|
|
|
|
outlineLevel = Math.max(outlineLevel, xrow.getCTRow().getOutlineLevel());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
return (short) outlineLevel;
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2010-10-14 04:40:06 -04:00
|
|
|
private short getMaxOutlineLevelCols() {
|
|
|
|
CTCols ctCols = worksheet.getColsArray(0);
|
2014-09-14 18:57:38 -04:00
|
|
|
int outlineLevel = 0;
|
2014-08-27 20:08:41 -04:00
|
|
|
for (CTCol col : ctCols.getColArray()) {
|
2014-09-14 18:57:38 -04:00
|
|
|
outlineLevel = Math.max(outlineLevel, col.getOutlineLevel());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
return (short) outlineLevel;
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if there is a page break at the indicated column
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-03-27 07:50:52 -04:00
|
|
|
public boolean isColumnBroken(int column) {
|
2014-09-14 18:57:38 -04:00
|
|
|
for (int colBreak : getColumnBreaks()) {
|
2014-06-11 16:36:30 -04:00
|
|
|
if (colBreak == column) {
|
2008-09-30 09:57:36 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the hidden state for a given column.
|
|
|
|
*
|
|
|
|
* @param columnIndex - the column to set (0-based)
|
|
|
|
* @return hidden - <code>false</code> if the column is visible
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isColumnHidden(int columnIndex) {
|
2009-10-04 06:08:47 -04:00
|
|
|
CTCol col = columnHelper.getColumn(columnIndex, false);
|
|
|
|
return col != null && col.getHidden();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Gets the flag indicating whether this sheet should display formulas.
|
|
|
|
*
|
|
|
|
* @return <code>true</code> if this sheet should display formulas.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isDisplayFormulas() {
|
|
|
|
return getSheetTypeSheetView().getShowFormulas();
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
2008-10-29 15:12:47 -04:00
|
|
|
* Gets the flag indicating whether this sheet displays the lines
|
|
|
|
* between rows and columns to make editing and reading easier.
|
2008-10-17 11:14:00 -04:00
|
|
|
*
|
2008-10-29 15:12:47 -04:00
|
|
|
* @return <code>true</code> if this sheet displays gridlines.
|
|
|
|
* @see #isPrintGridlines() to check if printing of gridlines is turned on or off
|
2008-10-17 11:14:00 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isDisplayGridlines() {
|
|
|
|
return getSheetTypeSheetView().getShowGridLines();
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Sets the flag indicating whether this sheet should display the lines
|
|
|
|
* between rows and columns to make editing and reading easier.
|
|
|
|
* To turn printing of gridlines use {@link #setPrintGridlines(boolean)}
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param show <code>true</code> if this sheet should display gridlines.
|
|
|
|
* @see #setPrintGridlines(boolean)
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-29 15:12:47 -04:00
|
|
|
public void setDisplayGridlines(boolean show) {
|
|
|
|
getSheetTypeSheetView().setShowGridLines(show);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Gets the flag indicating whether this sheet should display row and column headings.
|
2008-10-29 15:12:47 -04:00
|
|
|
* <p>
|
|
|
|
* Row heading are the row numbers to the side of the sheet
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* Column heading are the letters or numbers that appear above the columns of the sheet
|
|
|
|
* </p>
|
2008-10-17 11:14:00 -04:00
|
|
|
*
|
|
|
|
* @return <code>true</code> if this sheet should display row and column headings.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isDisplayRowColHeadings() {
|
|
|
|
return getSheetTypeSheetView().getShowRowColHeaders();
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Sets the flag indicating whether this sheet should display row and column headings.
|
|
|
|
* <p>
|
|
|
|
* Row heading are the row numbers to the side of the sheet
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* Column heading are the letters or numbers that appear above the columns of the sheet
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* @param show <code>true</code> if this sheet should display row and column headings.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-29 15:12:47 -04:00
|
|
|
public void setDisplayRowColHeadings(boolean show) {
|
|
|
|
getSheetTypeSheetView().setShowRowColHeaders(show);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Returns whether gridlines are printed.
|
|
|
|
*
|
|
|
|
* @return whether gridlines are printed
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isPrintGridlines() {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTPrintOptions opts = worksheet.getPrintOptions();
|
|
|
|
return opts != null && opts.getGridLines();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Turns on or off the printing of gridlines.
|
|
|
|
*
|
|
|
|
* @param value boolean to turn on or off the printing of gridlines
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-10-29 15:12:47 -04:00
|
|
|
public void setPrintGridlines(boolean value) {
|
|
|
|
CTPrintOptions opts = worksheet.isSetPrintOptions() ?
|
|
|
|
worksheet.getPrintOptions() : worksheet.addNewPrintOptions();
|
|
|
|
opts.setGridLines(value);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2016-06-19 14:20:23 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether row and column headings are printed.
|
|
|
|
*
|
|
|
|
* @return whether row and column headings are printed
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public boolean isPrintRowAndColumnHeadings() {
|
|
|
|
CTPrintOptions opts = worksheet.getPrintOptions();
|
|
|
|
return opts != null && opts.getHeadings();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Turns on or off the printing of row and column headings.
|
|
|
|
*
|
|
|
|
* @param value boolean to turn on or off the printing of row and column headings
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void setPrintRowAndColumnHeadings(boolean value) {
|
|
|
|
CTPrintOptions opts = worksheet.isSetPrintOptions() ?
|
|
|
|
worksheet.getPrintOptions() : worksheet.addNewPrintOptions();
|
|
|
|
opts.setHeadings(value);
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests if there is a page break at the indicated row
|
|
|
|
*
|
|
|
|
* @param row index of the row to test
|
|
|
|
* @return <code>true</code> if there is a page break at the indicated row
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isRowBroken(int row) {
|
2014-09-14 18:57:38 -04:00
|
|
|
for (int rowBreak : getRowBreaks()) {
|
2014-06-11 16:36:30 -04:00
|
|
|
if (rowBreak == row) {
|
2008-09-30 09:57:36 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
private void setBreak(int id, CTPageBreak ctPgBreak, int lastIndex) {
|
|
|
|
CTBreak brk = ctPgBreak.addNewBrk();
|
|
|
|
brk.setId(id + 1); // this is id of the element which is 1-based: <row r="1" ... >
|
|
|
|
brk.setMan(true);
|
|
|
|
brk.setMax(lastIndex); //end column of the break
|
|
|
|
|
|
|
|
int nPageBreaks = ctPgBreak.sizeOfBrkArray();
|
|
|
|
ctPgBreak.setCount(nPageBreaks);
|
|
|
|
ctPgBreak.setManualBreakCount(nPageBreaks);
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Sets a page break at the indicated row
|
2011-06-24 08:15:16 -04:00
|
|
|
* Breaks occur above the specified row and left of the specified column inclusive.
|
|
|
|
*
|
|
|
|
* For example, <code>sheet.setColumnBreak(2);</code> breaks the sheet into two parts
|
|
|
|
* with columns A,B,C in the first and D,E,... in the second. Simuilar, <code>sheet.setRowBreak(2);</code>
|
|
|
|
* breaks the sheet into two parts with first three rows (rownum=1...3) in the first part
|
|
|
|
* and rows starting with rownum=4 in the second.
|
|
|
|
*
|
|
|
|
* @param row the row to break, inclusive
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setRowBreak(int row) {
|
2014-09-14 18:57:38 -04:00
|
|
|
if (!isRowBroken(row)) {
|
|
|
|
CTPageBreak pgBreak = worksheet.isSetRowBreaks() ? worksheet.getRowBreaks() : worksheet.addNewRowBreaks();
|
|
|
|
setBreak(row, pgBreak, SpreadsheetVersion.EXCEL2007.getLastColumnIndex());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a page break at the indicated column
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-03-27 07:50:52 -04:00
|
|
|
public void removeColumnBreak(int column) {
|
2014-09-14 18:57:38 -04:00
|
|
|
if (worksheet.isSetColBreaks()) {
|
|
|
|
removeBreak(column, worksheet.getColBreaks());
|
|
|
|
} // else no breaks
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-21 13:56:34 -04:00
|
|
|
/**
|
|
|
|
* Removes a merged region of cells (hence letting them free)
|
|
|
|
*
|
|
|
|
* @param index of the region to unmerge
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void removeMergedRegion(int index) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if (!worksheet.isSetMergeCells()) {
|
|
|
|
return;
|
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
|
2008-10-21 13:56:34 -04:00
|
|
|
CTMergeCells ctMergeCells = worksheet.getMergeCells();
|
2014-08-31 15:53:53 -04:00
|
|
|
int size = ctMergeCells.sizeOfMergeCellArray();
|
2014-09-14 18:57:38 -04:00
|
|
|
assert(0 <= index && index < size);
|
|
|
|
if (size > 1) {
|
|
|
|
ctMergeCells.removeMergeCell(index);
|
|
|
|
} else {
|
2010-09-11 09:48:00 -04:00
|
|
|
worksheet.unsetMergeCells();
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2014-09-04 18:50:28 -04:00
|
|
|
|
2014-08-31 16:24:42 -04:00
|
|
|
/**
|
|
|
|
* Removes a number of merged regions of cells (hence letting them free)
|
2014-09-14 18:57:38 -04:00
|
|
|
*
|
2014-08-31 16:24:42 -04:00
|
|
|
* This method can be used to bulk-remove merged regions in a way
|
2014-09-14 18:57:38 -04:00
|
|
|
* much faster than calling removeMergedRegion() for every single
|
2014-08-31 16:24:42 -04:00
|
|
|
* merged region.
|
|
|
|
*
|
|
|
|
* @param indices A set of the regions to unmerge
|
|
|
|
*/
|
2016-06-19 23:40:32 -04:00
|
|
|
@Override
|
2015-11-29 10:27:34 -05:00
|
|
|
public void removeMergedRegions(Collection<Integer> indices) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if (!worksheet.isSetMergeCells()) {
|
|
|
|
return;
|
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
|
2014-08-31 16:24:42 -04:00
|
|
|
CTMergeCells ctMergeCells = worksheet.getMergeCells();
|
2014-09-14 18:57:38 -04:00
|
|
|
List<CTMergeCell> newMergeCells = new ArrayList<CTMergeCell>(ctMergeCells.sizeOfMergeCellArray());
|
2014-08-31 16:24:42 -04:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
int idx = 0;
|
|
|
|
for (CTMergeCell mc : ctMergeCells.getMergeCellArray()) {
|
2016-04-12 18:44:28 -04:00
|
|
|
if (!indices.contains(idx++)) {
|
|
|
|
newMergeCells.add(mc);
|
|
|
|
}
|
2014-08-31 16:24:42 -04:00
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
|
|
|
|
if (newMergeCells.isEmpty()) {
|
2014-08-31 16:24:42 -04:00
|
|
|
worksheet.unsetMergeCells();
|
2016-04-12 18:44:28 -04:00
|
|
|
} else {
|
2014-09-14 18:57:38 -04:00
|
|
|
CTMergeCell[] newMergeCellsArray = new CTMergeCell[newMergeCells.size()];
|
|
|
|
ctMergeCells.setMergeCellArray(newMergeCells.toArray(newMergeCellsArray));
|
2014-08-31 16:24:42 -04:00
|
|
|
}
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
/**
|
|
|
|
* Remove a row from this sheet. All cells contained in the row are removed as well
|
|
|
|
*
|
|
|
|
* @param row the row to remove.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void removeRow(Row row) {
|
2009-03-27 07:50:52 -04:00
|
|
|
if (row.getSheet() != this) {
|
|
|
|
throw new IllegalArgumentException("Specified row does not belong to this sheet");
|
|
|
|
}
|
2010-11-12 07:03:56 -05:00
|
|
|
// collect cells into a temporary array to avoid ConcurrentModificationException
|
|
|
|
ArrayList<XSSFCell> cellsToDelete = new ArrayList<XSSFCell>();
|
2016-04-12 18:44:28 -04:00
|
|
|
for (Cell cell : row) {
|
|
|
|
cellsToDelete.add((XSSFCell)cell);
|
|
|
|
}
|
2010-11-12 07:03:56 -05:00
|
|
|
|
2016-04-12 18:44:28 -04:00
|
|
|
for (XSSFCell cell : cellsToDelete) {
|
|
|
|
row.removeCell(cell);
|
|
|
|
}
|
2010-11-12 07:03:56 -05:00
|
|
|
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
2016-09-22 03:50:33 -04:00
|
|
|
final int rowNum = row.getRowNum();
|
|
|
|
final Integer rowNumI = new Integer(rowNum); // NOSONAR
|
|
|
|
// this is not the physical row number!
|
|
|
|
final int idx = _rows.headMap(rowNumI).size();
|
|
|
|
_rows.remove(rowNumI);
|
2011-08-10 13:32:38 -04:00
|
|
|
worksheet.getSheetData().removeRow(idx);
|
2016-05-29 17:33:47 -04:00
|
|
|
|
|
|
|
// also remove any comment located in that row
|
|
|
|
if(sheetComments != null) {
|
|
|
|
for (CellAddress ref : getCellComments().keySet()) {
|
2016-09-22 03:50:33 -04:00
|
|
|
if (ref.getRow() == rowNum) {
|
2016-05-29 17:33:47 -04:00
|
|
|
sheetComments.removeComment(ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the page break at the indicated row
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void removeRowBreak(int row) {
|
2014-09-14 18:57:38 -04:00
|
|
|
if (worksheet.isSetRowBreaks()) {
|
|
|
|
removeBreak(row, worksheet.getRowBreaks());
|
|
|
|
} // else no breaks
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2011-04-21 07:52:52 -04:00
|
|
|
/**
|
2011-06-27 11:40:48 -04:00
|
|
|
* Control if Excel should be asked to recalculate all formulas on this sheet
|
|
|
|
* when the workbook is opened.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Calculating the formula values with {@link org.apache.poi.ss.usermodel.FormulaEvaluator} is the
|
2011-04-21 07:52:52 -04:00
|
|
|
* recommended solution, but this may be used for certain cases where
|
|
|
|
* evaluation in POI is not possible.
|
2011-06-27 11:40:48 -04:00
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* It is recommended to force recalcuation of formulas on workbook level using
|
|
|
|
* {@link org.apache.poi.ss.usermodel.Workbook#setForceFormulaRecalculation(boolean)}
|
|
|
|
* to ensure that all cross-worksheet formuals and external dependencies are updated.
|
|
|
|
* </p>
|
|
|
|
* @param value true if the application will perform a full recalculation of
|
|
|
|
* this worksheet values when the workbook is opened
|
|
|
|
*
|
|
|
|
* @see org.apache.poi.ss.usermodel.Workbook#setForceFormulaRecalculation(boolean)
|
2011-04-21 07:52:52 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2011-04-21 07:52:52 -04:00
|
|
|
public void setForceFormulaRecalculation(boolean value) {
|
2012-10-04 09:26:44 -04:00
|
|
|
CTCalcPr calcPr = getWorkbook().getCTWorkbook().getCalcPr();
|
|
|
|
|
|
|
|
if(worksheet.isSetSheetCalcPr()) {
|
2011-04-21 07:52:52 -04:00
|
|
|
// Change the current setting
|
|
|
|
CTSheetCalcPr calc = worksheet.getSheetCalcPr();
|
|
|
|
calc.setFullCalcOnLoad(value);
|
|
|
|
}
|
|
|
|
else if(value) {
|
|
|
|
// Add the Calc block and set it
|
|
|
|
CTSheetCalcPr calc = worksheet.addNewSheetCalcPr();
|
|
|
|
calc.setFullCalcOnLoad(value);
|
|
|
|
}
|
2012-10-04 09:26:44 -04:00
|
|
|
if(value && calcPr != null && calcPr.getCalcMode() == STCalcMode.MANUAL) {
|
|
|
|
calcPr.setCalcMode(STCalcMode.AUTO);
|
|
|
|
}
|
|
|
|
|
2011-04-21 07:52:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether Excel will be asked to recalculate all formulas when the
|
2013-08-14 10:57:44 -04:00
|
|
|
* workbook is opened.
|
2011-04-21 07:52:52 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2011-04-21 07:52:52 -04:00
|
|
|
public boolean getForceFormulaRecalculation() {
|
|
|
|
if(worksheet.isSetSheetCalcPr()) {
|
|
|
|
CTSheetCalcPr calc = worksheet.getSheetCalcPr();
|
|
|
|
return calc.getFullCalcOnLoad();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* @return an iterator of the PHYSICAL rows. Meaning the 3rd element may not
|
|
|
|
* be the third row if say for instance the second row is undefined.
|
|
|
|
* Call getRowNum() on each row if you care which one it is.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2013-08-20 14:44:44 -04:00
|
|
|
@SuppressWarnings("unchecked")
|
2014-06-11 16:36:30 -04:00
|
|
|
public Iterator<Row> rowIterator() {
|
2010-05-24 01:31:48 -04:00
|
|
|
return (Iterator<Row>)(Iterator<? extends Row>) _rows.values().iterator();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alias for {@link #rowIterator()} to
|
|
|
|
* allow foreach loops
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public Iterator<Row> iterator() {
|
|
|
|
return rowIterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether the sheet displays Automatic Page Breaks.
|
|
|
|
*
|
|
|
|
* @return <code>true</code> if the sheet displays Automatic Page Breaks.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
|
|
|
public boolean getAutobreaks() {
|
2008-09-30 09:57:36 -04:00
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
CTPageSetUpPr psSetup = (sheetPr == null || !sheetPr.isSetPageSetUpPr()) ?
|
|
|
|
CTPageSetUpPr.Factory.newInstance() : sheetPr.getPageSetUpPr();
|
|
|
|
return psSetup.getAutoPageBreaks();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag indicating whether the sheet displays Automatic Page Breaks.
|
|
|
|
*
|
|
|
|
* @param value <code>true</code> if the sheet displays Automatic Page Breaks.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setAutobreaks(boolean value) {
|
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
CTPageSetUpPr psSetup = sheetPr.isSetPageSetUpPr() ? sheetPr.getPageSetUpPr() : sheetPr.addNewPageSetUpPr();
|
|
|
|
psSetup.setAutoPageBreaks(value);
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
2011-06-24 08:15:16 -04:00
|
|
|
* Sets a page break at the indicated column.
|
|
|
|
* Breaks occur above the specified row and left of the specified column inclusive.
|
2008-10-29 15:12:47 -04:00
|
|
|
*
|
2011-06-24 08:15:16 -04:00
|
|
|
* For example, <code>sheet.setColumnBreak(2);</code> breaks the sheet into two parts
|
|
|
|
* with columns A,B,C in the first and D,E,... in the second. Simuilar, <code>sheet.setRowBreak(2);</code>
|
|
|
|
* breaks the sheet into two parts with first three rows (rownum=1...3) in the first part
|
|
|
|
* and rows starting with rownum=4 in the second.
|
|
|
|
*
|
|
|
|
* @param column the column to break, inclusive
|
2008-10-29 15:12:47 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-03-27 07:50:52 -04:00
|
|
|
public void setColumnBreak(int column) {
|
2014-09-14 18:57:38 -04:00
|
|
|
if (!isColumnBroken(column)) {
|
2011-06-24 08:15:16 -04:00
|
|
|
CTPageBreak pgBreak = worksheet.isSetColBreaks() ? worksheet.getColBreaks() : worksheet.addNewColBreaks();
|
2014-09-14 18:57:38 -04:00
|
|
|
setBreak(column, pgBreak, SpreadsheetVersion.EXCEL2007.getLastRowIndex());
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-11-14 15:29:42 -05:00
|
|
|
public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
|
2009-11-27 15:54:03 -05:00
|
|
|
if (collapsed) {
|
|
|
|
collapseColumn(columnNumber);
|
|
|
|
} else {
|
|
|
|
expandColumn(columnNumber);
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private void collapseColumn(int columnNumber) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
CTCol col = columnHelper.getColumn(columnNumber, false);
|
|
|
|
int colInfoIx = columnHelper.getIndexOfColumn(cols, col);
|
|
|
|
if (colInfoIx == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Find the start of the group.
|
|
|
|
int groupStartColInfoIx = findStartOfColumnOutlineGroup(colInfoIx);
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCol columnInfo = cols.getColArray(groupStartColInfoIx);
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
// Hide all the columns until the end of the group
|
|
|
|
int lastColMax = setGroupHidden(groupStartColInfoIx, columnInfo
|
|
|
|
.getOutlineLevel(), true);
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
// write collapse field
|
2014-09-14 18:57:38 -04:00
|
|
|
setColumn(lastColMax + 1, 0, null, null, Boolean.TRUE);
|
2009-05-17 14:32:55 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
private void setColumn(int targetColumnIx, Integer style,
|
|
|
|
Integer level, Boolean hidden, Boolean collapsed) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
CTCol ci = null;
|
2014-09-14 18:57:38 -04:00
|
|
|
for (CTCol tci : cols.getColArray()) {
|
|
|
|
long tciMin = tci.getMin();
|
|
|
|
long tciMax = tci.getMax();
|
|
|
|
if (tciMin >= targetColumnIx && tciMax <= targetColumnIx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
ci = tci;
|
|
|
|
break;
|
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
if (tciMin > targetColumnIx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// call column infos after k are for later columns
|
|
|
|
break; // exit now so k will be the correct insert pos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ci == null) {
|
|
|
|
// okay so there ISN'T a column info record that covers this column
|
|
|
|
// so lets create one!
|
|
|
|
CTCol nci = CTCol.Factory.newInstance();
|
|
|
|
nci.setMin(targetColumnIx);
|
|
|
|
nci.setMax(targetColumnIx);
|
|
|
|
unsetCollapsed(collapsed, nci);
|
|
|
|
this.columnHelper.addCleanColIntoCols(cols, nci);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
boolean styleChanged = style != null && ci.getStyle() != style;
|
|
|
|
boolean levelChanged = level != null && ci.getOutlineLevel() != level;
|
|
|
|
boolean hiddenChanged = hidden != null && ci.getHidden() != hidden;
|
|
|
|
boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed;
|
|
|
|
boolean columnChanged = levelChanged || hiddenChanged || collapsedChanged || styleChanged;
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!columnChanged) {
|
|
|
|
// do nothing...nothing changed.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
long ciMin = ci.getMin();
|
|
|
|
long ciMax = ci.getMax();
|
|
|
|
if (ciMin == targetColumnIx && ciMax == targetColumnIx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// ColumnInfo ci for a single column, the target column
|
|
|
|
unsetCollapsed(collapsed, ci);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
if (ciMin == targetColumnIx || ciMax == targetColumnIx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// The target column is at either end of the multi-column ColumnInfo
|
|
|
|
// ci
|
|
|
|
// we'll just divide the info and create a new one
|
2014-09-14 18:57:38 -04:00
|
|
|
if (ciMin == targetColumnIx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
ci.setMin(targetColumnIx + 1);
|
|
|
|
} else {
|
|
|
|
ci.setMax(targetColumnIx - 1);
|
|
|
|
}
|
|
|
|
CTCol nci = columnHelper.cloneCol(cols, ci);
|
|
|
|
nci.setMin(targetColumnIx);
|
|
|
|
unsetCollapsed(collapsed, nci);
|
|
|
|
this.columnHelper.addCleanColIntoCols(cols, nci);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// split to 3 records
|
|
|
|
CTCol ciMid = columnHelper.cloneCol(cols, ci);
|
|
|
|
CTCol ciEnd = columnHelper.cloneCol(cols, ci);
|
2014-09-14 18:57:38 -04:00
|
|
|
int lastcolumn = (int) ciMax;
|
2009-11-27 15:54:03 -05:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
ci.setMax(targetColumnIx - 1);
|
2009-11-27 15:54:03 -05:00
|
|
|
|
|
|
|
ciMid.setMin(targetColumnIx);
|
|
|
|
ciMid.setMax(targetColumnIx);
|
|
|
|
unsetCollapsed(collapsed, ciMid);
|
|
|
|
this.columnHelper.addCleanColIntoCols(cols, ciMid);
|
|
|
|
|
|
|
|
ciEnd.setMin(targetColumnIx + 1);
|
|
|
|
ciEnd.setMax(lastcolumn);
|
|
|
|
this.columnHelper.addCleanColIntoCols(cols, ciEnd);
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private void unsetCollapsed(boolean collapsed, CTCol ci) {
|
2009-11-27 15:54:03 -05:00
|
|
|
if (collapsed) {
|
|
|
|
ci.setCollapsed(collapsed);
|
|
|
|
} else {
|
|
|
|
ci.unsetCollapsed();
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets all adjacent columns of the same outline level to the specified
|
|
|
|
* hidden status.
|
2009-08-18 01:29:53 -04:00
|
|
|
*
|
2009-05-17 14:32:55 -04:00
|
|
|
* @param pIdx
|
|
|
|
* the col info index of the start of the outline group
|
|
|
|
* @return the column index of the last column in the outline group
|
|
|
|
*/
|
|
|
|
private int setGroupHidden(int pIdx, int level, boolean hidden) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
int idx = pIdx;
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
CTCol columnInfo = colArray[idx];
|
|
|
|
while (idx < colArray.length) {
|
2009-11-27 15:54:03 -05:00
|
|
|
columnInfo.setHidden(hidden);
|
2014-09-14 18:57:38 -04:00
|
|
|
if (idx + 1 < colArray.length) {
|
|
|
|
CTCol nextColumnInfo = colArray[idx + 1];
|
2009-11-27 15:54:03 -05:00
|
|
|
|
|
|
|
if (!isAdjacentBefore(columnInfo, nextColumnInfo)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nextColumnInfo.getOutlineLevel() < level) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
columnInfo = nextColumnInfo;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
return (int) columnInfo.getMax();
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
2016-04-12 18:44:28 -04:00
|
|
|
private boolean isAdjacentBefore(CTCol col, CTCol otherCol) {
|
|
|
|
return col.getMax() == otherCol.getMin() - 1;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private int findStartOfColumnOutlineGroup(int pIdx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// Find the start of the group.
|
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
CTCol columnInfo = colArray[pIdx];
|
2009-11-27 15:54:03 -05:00
|
|
|
int level = columnInfo.getOutlineLevel();
|
|
|
|
int idx = pIdx;
|
|
|
|
while (idx != 0) {
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol prevColumnInfo = colArray[idx - 1];
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!isAdjacentBefore(prevColumnInfo, columnInfo)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (prevColumnInfo.getOutlineLevel() < level) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
idx--;
|
|
|
|
columnInfo = prevColumnInfo;
|
|
|
|
}
|
|
|
|
return idx;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private int findEndOfColumnOutlineGroup(int colInfoIndex) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
// Find the end of the group.
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
CTCol columnInfo = colArray[colInfoIndex];
|
2009-11-27 15:54:03 -05:00
|
|
|
int level = columnInfo.getOutlineLevel();
|
|
|
|
int idx = colInfoIndex;
|
2014-09-14 18:57:38 -04:00
|
|
|
int lastIdx = colArray.length - 1;
|
|
|
|
while (idx < lastIdx) {
|
|
|
|
CTCol nextColumnInfo = colArray[idx + 1];
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!isAdjacentBefore(columnInfo, nextColumnInfo)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (nextColumnInfo.getOutlineLevel() < level) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
columnInfo = nextColumnInfo;
|
|
|
|
}
|
|
|
|
return idx;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private void expandColumn(int columnIndex) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
CTCol col = columnHelper.getColumn(columnIndex, false);
|
|
|
|
int colInfoIx = columnHelper.getIndexOfColumn(cols, col);
|
|
|
|
|
|
|
|
int idx = findColInfoIdx((int) col.getMax(), colInfoIx);
|
|
|
|
if (idx == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it is already expanded do nothing.
|
|
|
|
if (!isColumnGroupCollapsed(idx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the start/end of the group.
|
|
|
|
int startIdx = findStartOfColumnOutlineGroup(idx);
|
|
|
|
int endIdx = findEndOfColumnOutlineGroup(idx);
|
|
|
|
|
|
|
|
// expand:
|
|
|
|
// colapsed bit must be unset
|
|
|
|
// hidden bit gets unset _if_ surrounding groups are expanded you can
|
|
|
|
// determine
|
|
|
|
// this by looking at the hidden bit of the enclosing group. You will
|
|
|
|
// have
|
|
|
|
// to look at the start and the end of the current group to determine
|
|
|
|
// which
|
|
|
|
// is the enclosing group
|
|
|
|
// hidden bit only is altered for this outline level. ie. don't
|
|
|
|
// uncollapse contained groups
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
CTCol columnInfo = colArray[endIdx];
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!isColumnGroupHiddenByParent(idx)) {
|
2014-09-14 18:57:38 -04:00
|
|
|
short outlineLevel = columnInfo.getOutlineLevel();
|
2009-11-27 15:54:03 -05:00
|
|
|
boolean nestedGroup = false;
|
|
|
|
for (int i = startIdx; i <= endIdx; i++) {
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol ci = colArray[i];
|
2009-11-27 15:54:03 -05:00
|
|
|
if (outlineLevel == ci.getOutlineLevel()) {
|
|
|
|
ci.unsetHidden();
|
|
|
|
if (nestedGroup) {
|
|
|
|
nestedGroup = false;
|
|
|
|
ci.setCollapsed(true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nestedGroup = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Write collapse flag (stored in a single col info record after this
|
|
|
|
// outline group)
|
2014-09-14 18:57:38 -04:00
|
|
|
setColumn((int) columnInfo.getMax() + 1, null, null,
|
2009-11-27 15:54:03 -05:00
|
|
|
Boolean.FALSE, Boolean.FALSE);
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isColumnGroupHiddenByParent(int idx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
// Look out outline details of end
|
|
|
|
int endLevel = 0;
|
|
|
|
boolean endHidden = false;
|
|
|
|
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx);
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
if (endOfOutlineGroupIdx < colArray.length) {
|
|
|
|
CTCol nextInfo = colArray[endOfOutlineGroupIdx + 1];
|
|
|
|
if (isAdjacentBefore(colArray[endOfOutlineGroupIdx], nextInfo)) {
|
2009-11-27 15:54:03 -05:00
|
|
|
endLevel = nextInfo.getOutlineLevel();
|
|
|
|
endHidden = nextInfo.getHidden();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Look out outline details of start
|
|
|
|
int startLevel = 0;
|
|
|
|
boolean startHidden = false;
|
|
|
|
int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup(idx);
|
|
|
|
if (startOfOutlineGroupIdx > 0) {
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol prevInfo = colArray[startOfOutlineGroupIdx - 1];
|
2009-11-27 15:54:03 -05:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
if (isAdjacentBefore(prevInfo, colArray[startOfOutlineGroupIdx])) {
|
2009-11-27 15:54:03 -05:00
|
|
|
startLevel = prevInfo.getOutlineLevel();
|
|
|
|
startHidden = prevInfo.getHidden();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (endLevel > startLevel) {
|
|
|
|
return endHidden;
|
|
|
|
}
|
|
|
|
return startHidden;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private int findColInfoIdx(int columnValue, int fromColInfoIdx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
if (columnValue < 0) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"column parameter out of range: " + columnValue);
|
|
|
|
}
|
|
|
|
if (fromColInfoIdx < 0) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"fromIdx parameter out of range: " + fromColInfoIdx);
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
|
|
|
for (int k = fromColInfoIdx; k < colArray.length; k++) {
|
|
|
|
CTCol ci = colArray[k];
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
if (containsColumn(ci, columnValue)) {
|
|
|
|
return k;
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
if (ci.getMin() > fromColInfoIdx) {
|
|
|
|
break;
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
return -1;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean containsColumn(CTCol col, int columnIndex) {
|
2009-11-27 15:54:03 -05:00
|
|
|
return col.getMin() <= columnIndex && columnIndex <= col.getMax();
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 'Collapsed' state is stored in a single column col info record
|
|
|
|
* immediately after the outline group
|
2009-08-18 01:29:53 -04:00
|
|
|
*
|
2009-05-17 14:32:55 -04:00
|
|
|
* @param idx
|
|
|
|
* @return a boolean represented if the column is collapsed
|
|
|
|
*/
|
|
|
|
private boolean isColumnGroupCollapsed(int idx) {
|
2009-11-27 15:54:03 -05:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol[] colArray = cols.getColArray();
|
2009-11-27 15:54:03 -05:00
|
|
|
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx);
|
|
|
|
int nextColInfoIx = endOfOutlineGroupIdx + 1;
|
2014-09-14 18:57:38 -04:00
|
|
|
if (nextColInfoIx >= colArray.length) {
|
2009-11-27 15:54:03 -05:00
|
|
|
return false;
|
|
|
|
}
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol nextColInfo = colArray[nextColInfoIx];
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
CTCol col = colArray[endOfOutlineGroupIdx];
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!isAdjacentBefore(col, nextColInfo)) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-10-29 15:12:47 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
return nextColInfo.getCollapsed();
|
2008-10-29 15:12:47 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Get the visibility state for a given column.
|
|
|
|
*
|
|
|
|
* @param columnIndex - the column to get (0-based)
|
|
|
|
* @param hidden - the visiblity state of the column
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
|
|
|
public void setColumnHidden(int columnIndex, boolean hidden) {
|
2008-09-30 09:57:36 -04:00
|
|
|
columnHelper.setColHidden(columnIndex, hidden);
|
2008-10-17 11:14:00 -04:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Set the width (in units of 1/256th of a character width)
|
2011-06-20 11:16:46 -04:00
|
|
|
*
|
2009-02-06 13:59:24 -05:00
|
|
|
* <p>
|
|
|
|
* The maximum column width for an individual cell is 255 characters.
|
|
|
|
* This value represents the number of characters that can be displayed
|
2011-06-20 11:16:46 -04:00
|
|
|
* in a cell that is formatted with the standard font (first font in the workbook).
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Character width is defined as the maximum digit width
|
2011-06-25 06:56:46 -04:00
|
|
|
* of the numbers <code>0, 1, 2, ... 9</code> as rendered
|
2011-06-20 11:16:46 -04:00
|
|
|
* using the default font (first font in the workbook).
|
|
|
|
* <br/>
|
|
|
|
* Unless you are using a very special font, the default character is '0' (zero),
|
|
|
|
* this is true for Arial (default font font in HSSF) and Calibri (default font in XSSF)
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Please note, that the width set by this method includes 4 pixels of margin padding (two on each side),
|
|
|
|
* plus 1 pixel padding for the gridlines (Section 3.3.1.12 of the OOXML spec).
|
|
|
|
* This results is a slightly less value of visible characters than passed to this method (approx. 1/2 of a character).
|
2009-02-06 13:59:24 -05:00
|
|
|
* </p>
|
2011-06-20 11:16:46 -04:00
|
|
|
* <p>
|
|
|
|
* To compute the actual number of visible characters,
|
|
|
|
* Excel uses the following formula (Section 3.3.1.12 of the OOXML spec):
|
|
|
|
* </p>
|
|
|
|
* <code>
|
|
|
|
* width = Truncate([{Number of Visible Characters} *
|
|
|
|
* {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width}*256)/256
|
|
|
|
* </code>
|
|
|
|
* <p>Using the Calibri font as an example, the maximum digit width of 11 point font size is 7 pixels (at 96 dpi).
|
|
|
|
* If you set a column width to be eight characters wide, e.g. <code>setColumnWidth(columnIndex, 8*256)</code>,
|
|
|
|
* then the actual value of visible characters (the value shown in Excel) is derived from the following equation:
|
|
|
|
* <code>
|
|
|
|
Truncate([numChars*7+5]/7*256)/256 = 8;
|
|
|
|
* </code>
|
|
|
|
*
|
|
|
|
* which gives <code>7.29</code>.
|
2008-10-17 11:14:00 -04:00
|
|
|
*
|
|
|
|
* @param columnIndex - the column to set (0-based)
|
|
|
|
* @param width - the width in units of 1/256th of a character width
|
2011-06-20 11:16:46 -04:00
|
|
|
* @throws IllegalArgumentException if width > 255*256 (the maximum column width in Excel is 255 characters)
|
2008-10-17 11:14:00 -04:00
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setColumnWidth(int columnIndex, int width) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if(width > 255*256) {
|
|
|
|
throw new IllegalArgumentException("The maximum column width for an individual cell is 255 characters.");
|
|
|
|
}
|
2009-02-06 13:59:24 -05:00
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
columnHelper.setColWidth(columnIndex, (double)width/256);
|
2009-09-13 13:49:46 -04:00
|
|
|
columnHelper.setCustomWidth(columnIndex, true);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-11-14 15:29:42 -05:00
|
|
|
public void setDefaultColumnStyle(int column, CellStyle style) {
|
2008-09-30 09:57:36 -04:00
|
|
|
columnHelper.setColDefaultStyle(column, style);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Specifies the number of characters of the maximum digit width of the normal style's font.
|
|
|
|
* This value does not include margin padding or extra padding for gridlines. It is only the
|
|
|
|
* number of characters.
|
|
|
|
*
|
|
|
|
* @param width the number of characters. Default value is <code>8</code>.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setDefaultColumnWidth(int width) {
|
2008-10-17 11:14:00 -04:00
|
|
|
getSheetTypeSheetFormatPr().setBaseColWidth(width);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Set the default row height for the sheet (if the rows do not define their own height) in
|
|
|
|
* twips (1/20 of a point)
|
|
|
|
*
|
|
|
|
* @param height default row height in twips (1/20 of a point)
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setDefaultRowHeight(short height) {
|
2016-04-12 18:44:28 -04:00
|
|
|
setDefaultRowHeightInPoints((float)height / TWIPS_PER_POINT);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Sets default row height measured in point size.
|
|
|
|
*
|
|
|
|
* @param height default row height measured in point size.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setDefaultRowHeightInPoints(float height) {
|
2012-02-12 06:14:23 -05:00
|
|
|
CTSheetFormatPr pr = getSheetTypeSheetFormatPr();
|
|
|
|
pr.setDefaultRowHeight(height);
|
|
|
|
pr.setCustomHeight(true);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Sets the flag indicating whether this sheet should display formulas.
|
|
|
|
*
|
|
|
|
* @param show <code>true</code> if this sheet should display formulas.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setDisplayFormulas(boolean show) {
|
|
|
|
getSheetTypeSheetView().setShowFormulas(show);
|
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
private CTSheetView getSheetTypeSheetView() {
|
2008-09-30 09:57:36 -04:00
|
|
|
if (getDefaultSheetView() == null) {
|
|
|
|
getSheetTypeSheetViews().setSheetViewArray(0, CTSheetView.Factory.newInstance());
|
|
|
|
}
|
|
|
|
return getDefaultSheetView();
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Flag indicating whether the Fit to Page print option is enabled.
|
|
|
|
*
|
|
|
|
* @param b <code>true</code> if the Fit to Page print option is enabled.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
|
|
|
public void setFitToPage(boolean b) {
|
2008-09-30 09:57:36 -04:00
|
|
|
getSheetTypePageSetUpPr().setFitToPage(b);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Center on page horizontally when printing.
|
|
|
|
*
|
|
|
|
* @param value whether to center on page horizontally when printing.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setHorizontallyCenter(boolean value) {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTPrintOptions opts = worksheet.isSetPrintOptions() ?
|
|
|
|
worksheet.getPrintOptions() : worksheet.addNewPrintOptions();
|
|
|
|
opts.setHorizontalCentered(value);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-29 15:12:47 -04:00
|
|
|
/**
|
|
|
|
* Whether the output is vertically centered on the page.
|
|
|
|
*
|
|
|
|
* @param value true to vertically center, false otherwise.
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setVerticallyCenter(boolean value) {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTPrintOptions opts = worksheet.isSetPrintOptions() ?
|
|
|
|
worksheet.getPrintOptions() : worksheet.addNewPrintOptions();
|
|
|
|
opts.setVerticalCentered(value);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2009-05-17 14:32:55 -04:00
|
|
|
/**
|
|
|
|
* group the row It is possible for collapsed to be false and yet still have
|
|
|
|
* the rows in question hidden. This can be achieved by having a lower
|
|
|
|
* outline level collapsed, thus hiding all the child rows. Note that in
|
|
|
|
* this case, if the lowest level were expanded, the middle level would
|
|
|
|
* remain collapsed.
|
2009-08-18 01:29:53 -04:00
|
|
|
*
|
2009-05-17 14:32:55 -04:00
|
|
|
* @param rowIndex -
|
|
|
|
* the row involved, 0 based
|
|
|
|
* @param collapse -
|
|
|
|
* boolean value for collapse
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-05-17 14:32:55 -04:00
|
|
|
public void setRowGroupCollapsed(int rowIndex, boolean collapse) {
|
2009-11-27 15:54:03 -05:00
|
|
|
if (collapse) {
|
|
|
|
collapseRow(rowIndex);
|
|
|
|
} else {
|
|
|
|
expandRow(rowIndex);
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param rowIndex the zero based row index to collapse
|
|
|
|
*/
|
|
|
|
private void collapseRow(int rowIndex) {
|
2009-11-27 15:54:03 -05:00
|
|
|
XSSFRow row = getRow(rowIndex);
|
|
|
|
if (row != null) {
|
|
|
|
int startRow = findStartOfRowOutlineGroup(rowIndex);
|
|
|
|
|
|
|
|
// Hide all the columns until the end of the group
|
|
|
|
int lastRow = writeHidden(row, startRow, true);
|
|
|
|
if (getRow(lastRow) != null) {
|
|
|
|
getRow(lastRow).getCTRow().setCollapsed(true);
|
|
|
|
} else {
|
|
|
|
XSSFRow newRow = createRow(lastRow);
|
|
|
|
newRow.getCTRow().setCollapsed(true);
|
|
|
|
}
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param rowIndex the zero based row index to find from
|
|
|
|
*/
|
|
|
|
private int findStartOfRowOutlineGroup(int rowIndex) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// Find the start of the group.
|
2014-09-14 18:57:38 -04:00
|
|
|
short level = getRow(rowIndex).getCTRow().getOutlineLevel();
|
2009-11-27 15:54:03 -05:00
|
|
|
int currentRow = rowIndex;
|
|
|
|
while (getRow(currentRow) != null) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if (getRow(currentRow).getCTRow().getOutlineLevel() < level) {
|
2009-11-27 15:54:03 -05:00
|
|
|
return currentRow + 1;
|
2017-01-07 19:38:41 -05:00
|
|
|
}
|
2009-11-27 15:54:03 -05:00
|
|
|
currentRow--;
|
|
|
|
}
|
|
|
|
return currentRow;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private int writeHidden(XSSFRow xRow, int rowIndex, boolean hidden) {
|
2014-09-14 18:57:38 -04:00
|
|
|
short level = xRow.getCTRow().getOutlineLevel();
|
2009-11-27 15:54:03 -05:00
|
|
|
for (Iterator<Row> it = rowIterator(); it.hasNext();) {
|
|
|
|
xRow = (XSSFRow) it.next();
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2013-12-02 14:14:03 -05:00
|
|
|
// skip rows before the start of this group
|
|
|
|
if(xRow.getRowNum() < rowIndex) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
if (xRow.getCTRow().getOutlineLevel() >= level) {
|
|
|
|
xRow.getCTRow().setHidden(hidden);
|
|
|
|
rowIndex++;
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
return rowIndex;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-21 02:08:31 -04:00
|
|
|
* @param rowNumber the zero based row index to expand
|
2009-05-17 14:32:55 -04:00
|
|
|
*/
|
|
|
|
private void expandRow(int rowNumber) {
|
2017-01-07 19:38:41 -05:00
|
|
|
if (rowNumber == -1) {
|
2009-11-27 15:54:03 -05:00
|
|
|
return;
|
2017-01-07 19:38:41 -05:00
|
|
|
}
|
2009-11-27 15:54:03 -05:00
|
|
|
XSSFRow row = getRow(rowNumber);
|
|
|
|
// If it is already expanded do nothing.
|
2013-12-02 14:14:03 -05:00
|
|
|
if (!row.getCTRow().isSetHidden()) {
|
2009-11-27 15:54:03 -05:00
|
|
|
return;
|
2013-12-02 14:14:03 -05:00
|
|
|
}
|
2009-11-27 15:54:03 -05:00
|
|
|
|
|
|
|
// Find the start of the group.
|
|
|
|
int startIdx = findStartOfRowOutlineGroup(rowNumber);
|
|
|
|
|
|
|
|
// Find the end of the group.
|
|
|
|
int endIdx = findEndOfRowOutlineGroup(rowNumber);
|
|
|
|
|
|
|
|
// expand:
|
|
|
|
// collapsed must be unset
|
|
|
|
// hidden bit gets unset _if_ surrounding groups are expanded you can
|
|
|
|
// determine
|
|
|
|
// this by looking at the hidden bit of the enclosing group. You will
|
|
|
|
// have
|
|
|
|
// to look at the start and the end of the current group to determine
|
|
|
|
// which
|
|
|
|
// is the enclosing group
|
|
|
|
// hidden bit only is altered for this outline level. ie. don't
|
|
|
|
// un-collapse contained groups
|
2014-09-14 18:57:38 -04:00
|
|
|
short level = row.getCTRow().getOutlineLevel();
|
2009-11-27 15:54:03 -05:00
|
|
|
if (!isRowGroupHiddenByParent(rowNumber)) {
|
|
|
|
for (int i = startIdx; i < endIdx; i++) {
|
2014-09-14 18:57:38 -04:00
|
|
|
if (level == getRow(i).getCTRow().getOutlineLevel()) {
|
2009-11-27 15:54:03 -05:00
|
|
|
getRow(i).getCTRow().unsetHidden();
|
|
|
|
} else if (!isRowGroupCollapsed(i)) {
|
|
|
|
getRow(i).getCTRow().unsetHidden();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Write collapse field
|
2013-12-02 14:14:03 -05:00
|
|
|
CTRow ctRow = getRow(endIdx).getCTRow();
|
|
|
|
// This avoids an IndexOutOfBounds if multiple nested groups are collapsed/expanded
|
|
|
|
if(ctRow.getCollapsed()) {
|
|
|
|
ctRow.unsetCollapsed();
|
|
|
|
}
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-21 02:08:31 -04:00
|
|
|
* @param row the zero based row index to find from
|
2009-05-17 14:32:55 -04:00
|
|
|
*/
|
|
|
|
public int findEndOfRowOutlineGroup(int row) {
|
2014-09-14 18:57:38 -04:00
|
|
|
short level = getRow(row).getCTRow().getOutlineLevel();
|
2009-11-27 15:54:03 -05:00
|
|
|
int currentRow;
|
2016-07-02 14:28:43 -04:00
|
|
|
final int lastRowNum = getLastRowNum();
|
|
|
|
for (currentRow = row; currentRow < lastRowNum; currentRow++) {
|
2009-11-27 15:54:03 -05:00
|
|
|
if (getRow(currentRow) == null
|
|
|
|
|| getRow(currentRow).getCTRow().getOutlineLevel() < level) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return currentRow;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-21 02:08:31 -04:00
|
|
|
* @param row the zero based row index to find from
|
2009-05-17 14:32:55 -04:00
|
|
|
*/
|
|
|
|
private boolean isRowGroupHiddenByParent(int row) {
|
2009-11-27 15:54:03 -05:00
|
|
|
// Look out outline details of end
|
|
|
|
int endLevel;
|
|
|
|
boolean endHidden;
|
|
|
|
int endOfOutlineGroupIdx = findEndOfRowOutlineGroup(row);
|
|
|
|
if (getRow(endOfOutlineGroupIdx) == null) {
|
|
|
|
endLevel = 0;
|
|
|
|
endHidden = false;
|
|
|
|
} else {
|
|
|
|
endLevel = getRow(endOfOutlineGroupIdx).getCTRow().getOutlineLevel();
|
|
|
|
endHidden = getRow(endOfOutlineGroupIdx).getCTRow().getHidden();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look out outline details of start
|
|
|
|
int startLevel;
|
|
|
|
boolean startHidden;
|
|
|
|
int startOfOutlineGroupIdx = findStartOfRowOutlineGroup(row);
|
|
|
|
if (startOfOutlineGroupIdx < 0
|
|
|
|
|| getRow(startOfOutlineGroupIdx) == null) {
|
|
|
|
startLevel = 0;
|
|
|
|
startHidden = false;
|
|
|
|
} else {
|
|
|
|
startLevel = getRow(startOfOutlineGroupIdx).getCTRow()
|
|
|
|
.getOutlineLevel();
|
|
|
|
startHidden = getRow(startOfOutlineGroupIdx).getCTRow()
|
|
|
|
.getHidden();
|
|
|
|
}
|
|
|
|
if (endLevel > startLevel) {
|
|
|
|
return endHidden;
|
|
|
|
}
|
|
|
|
return startHidden;
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-21 02:08:31 -04:00
|
|
|
* @param row the zero based row index to find from
|
2009-05-17 14:32:55 -04:00
|
|
|
*/
|
|
|
|
private boolean isRowGroupCollapsed(int row) {
|
2009-11-27 15:54:03 -05:00
|
|
|
int collapseRow = findEndOfRowOutlineGroup(row) + 1;
|
|
|
|
if (getRow(collapseRow) == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return getRow(collapseRow).getCTRow().getCollapsed();
|
2009-05-17 14:32:55 -04:00
|
|
|
}
|
2009-08-18 01:29:53 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
2015-11-24 02:34:09 -05:00
|
|
|
* Sets the zoom magnification for the sheet. The zoom is expressed as a
|
2008-09-30 09:57:36 -04:00
|
|
|
* fraction. For example to express a zoom of 75% use 3 for the numerator
|
|
|
|
* and 4 for the denominator.
|
|
|
|
*
|
2009-11-27 15:54:03 -05:00
|
|
|
* @param numerator The numerator for the zoom magnification.
|
2008-09-30 09:57:36 -04:00
|
|
|
* @param denominator The denominator for the zoom magnification.
|
2015-11-24 02:43:53 -05:00
|
|
|
* @deprecated 2015-11-23 (circa POI 3.14beta1). Use {@link #setZoom(int)} instead.
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2017-01-07 19:38:41 -05:00
|
|
|
@Deprecated
|
2016-07-15 02:35:21 -04:00
|
|
|
@Removal(version="3.16")
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setZoom(int numerator, int denominator) {
|
2008-10-12 09:10:30 -04:00
|
|
|
int zoom = 100*numerator/denominator;
|
|
|
|
setZoom(zoom);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Window zoom magnification for current view representing percent values.
|
|
|
|
* Valid values range from 10 to 400. Horizontal & Vertical scale together.
|
|
|
|
*
|
|
|
|
* For example:
|
|
|
|
* <pre>
|
|
|
|
* 10 - 10%
|
|
|
|
* 20 - 20%
|
2008-11-09 14:45:45 -05:00
|
|
|
* ...
|
2008-09-30 09:57:36 -04:00
|
|
|
* 100 - 100%
|
2008-11-09 14:45:45 -05:00
|
|
|
* ...
|
2008-09-30 09:57:36 -04:00
|
|
|
* 400 - 400%
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* Current view can be Normal, Page Layout, or Page Break Preview.
|
|
|
|
*
|
|
|
|
* @param scale window zoom magnification
|
2008-10-29 15:12:47 -04:00
|
|
|
* @throws IllegalArgumentException if scale is invalid
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2015-11-24 02:43:53 -05:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setZoom(int scale) {
|
2016-04-12 18:44:28 -04:00
|
|
|
if (scale < 10 || scale > 400) {
|
|
|
|
throw new IllegalArgumentException("Valid scale values range from 10 to 400");
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
getSheetTypeSheetView().setZoomScale(scale);
|
|
|
|
}
|
|
|
|
|
2015-11-01 21:50:33 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* copyRows rows from srcRows to this sheet starting at destStartRow
|
|
|
|
*
|
|
|
|
* Additionally copies merged regions that are completely defined in these
|
|
|
|
* rows (ie. merged 2 cells on a row to be shifted).
|
|
|
|
* @param srcRows the rows to copy. Formulas will be offset by the difference
|
|
|
|
* in the row number of the first row in srcRows and destStartRow (even if srcRows
|
|
|
|
* are from a different sheet).
|
|
|
|
* @param destStartRow the row in this sheet to paste the first row of srcRows
|
|
|
|
* the remainder of srcRows will be pasted below destStartRow per the cell copy policy
|
|
|
|
* @param policy is the cell copy policy, which can be used to merge the source and destination
|
|
|
|
* when the source is blank, copy styles only, paste as value, etc
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public void copyRows(List<? extends Row> srcRows, int destStartRow, CellCopyPolicy policy) {
|
|
|
|
if (srcRows == null || srcRows.size() == 0) {
|
|
|
|
throw new IllegalArgumentException("No rows to copy");
|
|
|
|
}
|
|
|
|
final Row srcStartRow = srcRows.get(0);
|
|
|
|
final Row srcEndRow = srcRows.get(srcRows.size() - 1);
|
|
|
|
|
|
|
|
if (srcStartRow == null) {
|
|
|
|
throw new IllegalArgumentException("copyRows: First row cannot be null");
|
|
|
|
}
|
|
|
|
|
|
|
|
final int srcStartRowNum = srcStartRow.getRowNum();
|
|
|
|
final int srcEndRowNum = srcEndRow.getRowNum();
|
|
|
|
|
|
|
|
// check row numbers to make sure they are continuous and increasing (monotonic)
|
|
|
|
// and srcRows does not contain null rows
|
2016-07-03 03:20:47 -04:00
|
|
|
final int size = srcRows.size();
|
|
|
|
for (int index=1; index < size; index++) {
|
2015-11-01 21:50:33 -05:00
|
|
|
final Row curRow = srcRows.get(index);
|
2015-11-23 20:08:34 -05:00
|
|
|
if (curRow == null) {
|
|
|
|
throw new IllegalArgumentException("srcRows may not contain null rows. Found null row at index " + index + ".");
|
2015-11-01 21:50:33 -05:00
|
|
|
//} else if (curRow.getRowNum() != prevRow.getRowNum() + 1) {
|
|
|
|
// throw new IllegalArgumentException("srcRows must contain continuously increasing row numbers. " +
|
|
|
|
// "Got srcRows[" + (index-1) + "]=Row " + prevRow.getRowNum() + ", srcRows[" + index + "]=Row " + curRow.getRowNum() + ".");
|
|
|
|
// FIXME: assumes row objects belong to non-null sheets and sheets belong to non-null workbooks.
|
|
|
|
} else if (srcStartRow.getSheet().getWorkbook() != curRow.getSheet().getWorkbook()) {
|
|
|
|
throw new IllegalArgumentException("All rows in srcRows must belong to the same sheet in the same workbook." +
|
|
|
|
"Expected all rows from same workbook (" + srcStartRow.getSheet().getWorkbook() + "). " +
|
|
|
|
"Got srcRows[" + index + "] from different workbook (" + curRow.getSheet().getWorkbook() + ").");
|
|
|
|
} else if (srcStartRow.getSheet() != curRow.getSheet()) {
|
|
|
|
throw new IllegalArgumentException("All rows in srcRows must belong to the same sheet. " +
|
|
|
|
"Expected all rows from " + srcStartRow.getSheet().getSheetName() + ". " +
|
|
|
|
"Got srcRows[" + index + "] from " + curRow.getSheet().getSheetName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: is special behavior needed if srcRows and destRows belong to the same sheets and the regions overlap?
|
|
|
|
|
2015-11-24 03:52:45 -05:00
|
|
|
final CellCopyPolicy options = new CellCopyPolicy(policy);
|
2015-11-01 21:50:33 -05:00
|
|
|
// avoid O(N^2) performance scanning through all regions for each row
|
|
|
|
// merged regions will be copied after all the rows have been copied
|
|
|
|
options.setCopyMergedRegions(false);
|
|
|
|
|
|
|
|
// FIXME: if srcRows contains gaps or null values, clear out those rows that will be overwritten
|
|
|
|
// how will this work with merging (copy just values, leave cell styles in place?)
|
|
|
|
|
|
|
|
int r = destStartRow;
|
|
|
|
for (Row srcRow : srcRows) {
|
|
|
|
int destRowNum;
|
|
|
|
if (policy.isCondenseRows()) {
|
|
|
|
destRowNum = r++;
|
|
|
|
} else {
|
|
|
|
final int shift = (srcRow.getRowNum() - srcStartRowNum);
|
|
|
|
destRowNum = destStartRow + shift;
|
|
|
|
}
|
|
|
|
//removeRow(destRowNum); //this probably clears all external formula references to destRow, causing unwanted #REF! errors
|
|
|
|
final XSSFRow destRow = createRow(destRowNum);
|
|
|
|
destRow.copyRowFrom(srcRow, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
// Only do additional copy operations here that cannot be done with Row.copyFromRow(Row, options)
|
|
|
|
// reasons: operation needs to interact with multiple rows or sheets
|
|
|
|
|
|
|
|
// Copy merged regions that are contained within the copy region
|
|
|
|
if (policy.isCopyMergedRegions()) {
|
|
|
|
// FIXME: is this something that rowShifter could be doing?
|
|
|
|
final int shift = destStartRow - srcStartRowNum;
|
|
|
|
for (CellRangeAddress srcRegion : srcStartRow.getSheet().getMergedRegions()) {
|
|
|
|
if (srcStartRowNum <= srcRegion.getFirstRow() && srcRegion.getLastRow() <= srcEndRowNum) {
|
|
|
|
// srcRegion is fully inside the copied rows
|
|
|
|
final CellRangeAddress destRegion = srcRegion.copy();
|
|
|
|
destRegion.setFirstRow(destRegion.getFirstRow() + shift);
|
|
|
|
destRegion.setLastRow(destRegion.getLastRow() + shift);
|
|
|
|
addMergedRegion(destRegion);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies rows between srcStartRow and srcEndRow to the same sheet, starting at destStartRow
|
|
|
|
* Convenience function for {@link #copyRows(List, int, CellCopyPolicy)}
|
|
|
|
*
|
|
|
|
* Equivalent to copyRows(getRows(srcStartRow, srcEndRow, false), destStartRow, cellCopyPolicy)
|
|
|
|
*
|
|
|
|
* @param srcStartRow the index of the first row to copy the cells from in this sheet
|
|
|
|
* @param srcEndRow the index of the last row to copy the cells from in this sheet
|
|
|
|
* @param destStartRow the index of the first row to copy the cells to in this sheet
|
|
|
|
* @param cellCopyPolicy the policy to use to determine how cells are copied
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public void copyRows(int srcStartRow, int srcEndRow, int destStartRow, CellCopyPolicy cellCopyPolicy) {
|
|
|
|
final List<XSSFRow> srcRows = getRows(srcStartRow, srcEndRow, false); //FIXME: should be false, no need to create rows where src is only to copy them to dest
|
|
|
|
copyRows(srcRows, destStartRow, cellCopyPolicy);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Shifts rows between startRow and endRow n number of rows.
|
|
|
|
* If you use a negative number, it will shift rows up.
|
|
|
|
* Code ensures that rows don't wrap around.
|
|
|
|
*
|
|
|
|
* Calls shiftRows(startRow, endRow, n, false, false);
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Additionally shifts merged regions that are completely defined in these
|
|
|
|
* rows (ie. merged 2 cells on a row to be shifted).
|
|
|
|
* @param startRow the row to start shifting
|
|
|
|
* @param endRow the row to end shifting
|
|
|
|
* @param n the number of rows to shift
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void shiftRows(int startRow, int endRow, int n) {
|
|
|
|
shiftRows(startRow, endRow, n, false, false);
|
|
|
|
}
|
|
|
|
|
2008-10-17 11:14:00 -04:00
|
|
|
/**
|
|
|
|
* Shifts rows between startRow and endRow n number of rows.
|
|
|
|
* If you use a negative number, it will shift rows up.
|
|
|
|
* Code ensures that rows don't wrap around
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Additionally shifts merged regions that are completely defined in these
|
2016-07-16 16:52:39 -04:00
|
|
|
* rows (ie. merged 2 cells on a row to be shifted). All merged regions that are
|
|
|
|
* completely overlaid by shifting will be deleted.
|
2008-10-17 11:14:00 -04:00
|
|
|
* <p>
|
|
|
|
* @param startRow the row to start shifting
|
|
|
|
* @param endRow the row to end shifting
|
|
|
|
* @param n the number of rows to shift
|
|
|
|
* @param copyRowHeight whether to copy the row height during the shift
|
|
|
|
* @param resetOriginalRowHeight whether to set the original row's height to the default
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2015-03-15 16:52:55 -04:00
|
|
|
public void shiftRows(int startRow, int endRow, final int n, boolean copyRowHeight, boolean resetOriginalRowHeight) {
|
2015-11-24 02:53:17 -05:00
|
|
|
XSSFVMLDrawing vml = getVMLDrawing(false);
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-11-24 02:53:17 -05:00
|
|
|
// first remove all rows which will be overwritten
|
2014-06-11 16:36:30 -04:00
|
|
|
for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
|
2009-02-08 11:35:27 -05:00
|
|
|
XSSFRow row = (XSSFRow)it.next();
|
2009-02-15 15:47:36 -05:00
|
|
|
int rownum = row.getRowNum();
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2013-09-09 05:24:05 -04:00
|
|
|
// check if we should remove this row as it will be overwritten by the data later
|
2015-03-15 16:52:55 -04:00
|
|
|
if (shouldRemoveRow(startRow, endRow, n, rownum)) {
|
2014-06-11 16:36:30 -04:00
|
|
|
// remove row from worksheet.getSheetData row array
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
|
|
|
final Integer rownumI = new Integer(row.getRowNum()); // NOSONAR
|
2016-06-12 12:57:15 -04:00
|
|
|
int idx = _rows.headMap(rownumI).size();
|
2014-06-11 16:36:30 -04:00
|
|
|
worksheet.getSheetData().removeRow(idx);
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2011-08-10 13:32:38 -04:00
|
|
|
// remove row from _rows
|
2008-09-30 09:57:36 -04:00
|
|
|
it.remove();
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-10-29 02:53:55 -04:00
|
|
|
// FIXME: (performance optimization) this should be moved outside the for-loop so that comments only needs to be iterated over once.
|
2015-03-15 16:52:55 -04:00
|
|
|
// also remove any comments associated with this row
|
|
|
|
if(sheetComments != null){
|
|
|
|
CTCommentList lst = sheetComments.getCTComments().getCommentList();
|
|
|
|
for (CTComment comment : lst.getCommentArray()) {
|
2015-11-24 02:53:17 -05:00
|
|
|
String strRef = comment.getRef();
|
|
|
|
CellAddress ref = new CellAddress(strRef);
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2015-11-24 02:53:17 -05:00
|
|
|
// is this comment part of the current row?
|
|
|
|
if(ref.getRow() == rownum) {
|
2015-11-23 10:11:28 -05:00
|
|
|
sheetComments.removeComment(ref);
|
|
|
|
vml.removeCommentShape(ref.getRow(), ref.getColumn());
|
2015-11-24 02:53:17 -05:00
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
}
|
|
|
|
}
|
2015-10-29 02:53:55 -04:00
|
|
|
// FIXME: (performance optimization) this should be moved outside the for-loop so that hyperlinks only needs to be iterated over once.
|
|
|
|
// also remove any hyperlinks associated with this row
|
|
|
|
if (hyperlinks != null) {
|
|
|
|
for (XSSFHyperlink link : new ArrayList<XSSFHyperlink>(hyperlinks)) {
|
|
|
|
CellReference ref = new CellReference(link.getCellRef());
|
|
|
|
if (ref.getRow() == rownum) {
|
|
|
|
hyperlinks.remove(link);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2014-06-11 16:36:30 -04:00
|
|
|
}
|
2009-02-15 15:47:36 -05:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
// then do the actual moving and also adjust comments/rowHeight
|
2015-03-15 16:52:55 -04:00
|
|
|
// we need to sort it in a way so the shifting does not mess up the structures,
|
|
|
|
// i.e. when shifting down, start from down and go up, when shifting up, vice-versa
|
|
|
|
SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<XSSFComment, Integer>(new Comparator<XSSFComment>() {
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
2016-03-29 14:11:44 -04:00
|
|
|
public int compare(XSSFComment o1, XSSFComment o2) {
|
|
|
|
int row1 = o1.getRow();
|
|
|
|
int row2 = o2.getRow();
|
|
|
|
|
|
|
|
if(row1 == row2) {
|
|
|
|
// ordering is not important when row is equal, but don't return zero to still
|
|
|
|
// get multiple comments per row into the map
|
|
|
|
return o1.hashCode() - o2.hashCode();
|
|
|
|
}
|
2015-11-24 02:53:17 -05:00
|
|
|
|
|
|
|
// when shifting down, sort higher row-values first
|
|
|
|
if(n > 0) {
|
|
|
|
return row1 < row2 ? 1 : -1;
|
|
|
|
} else {
|
|
|
|
// sort lower-row values first when shifting up
|
|
|
|
return row1 > row2 ? 1 : -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-05-19 09:13:09 -04:00
|
|
|
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
|
2013-09-09 05:24:05 -04:00
|
|
|
XSSFRow row = (XSSFRow)it.next();
|
|
|
|
int rownum = row.getRowNum();
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2009-02-15 15:47:36 -05:00
|
|
|
if(sheetComments != null){
|
2015-11-24 02:53:17 -05:00
|
|
|
// calculate the new rownum
|
|
|
|
int newrownum = shiftedRowNum(startRow, endRow, n, rownum);
|
|
|
|
|
|
|
|
// is there a change necessary for the current row?
|
|
|
|
if(newrownum != rownum) {
|
2015-03-15 16:52:55 -04:00
|
|
|
CTCommentList lst = sheetComments.getCTComments().getCommentList();
|
|
|
|
for (CTComment comment : lst.getCommentArray()) {
|
2015-11-24 02:53:17 -05:00
|
|
|
String oldRef = comment.getRef();
|
|
|
|
CellReference ref = new CellReference(oldRef);
|
|
|
|
|
|
|
|
// is this comment part of the current row?
|
|
|
|
if(ref.getRow() == rownum) {
|
|
|
|
XSSFComment xssfComment = new XSSFComment(sheetComments, comment,
|
2015-03-15 16:52:55 -04:00
|
|
|
vml == null ? null : vml.findCommentShape(rownum, ref.getCol()));
|
2015-11-24 02:53:17 -05:00
|
|
|
|
|
|
|
// we should not perform the shifting right here as we would then find
|
|
|
|
// already shifted comments and would shift them again...
|
|
|
|
commentsToShift.put(xssfComment, newrownum);
|
2015-03-15 16:52:55 -04:00
|
|
|
}
|
2015-11-24 02:53:17 -05:00
|
|
|
}
|
2009-02-15 15:47:36 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2017-01-07 19:38:41 -05:00
|
|
|
if(rownum < startRow || rownum > endRow) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-09-09 05:24:05 -04:00
|
|
|
|
|
|
|
if (!copyRowHeight) {
|
|
|
|
row.setHeight((short)-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
row.shift(n);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
|
|
|
|
// adjust all the affected comment-structures now
|
|
|
|
// the Map is sorted and thus provides them in the order that we need here,
|
|
|
|
// i.e. from down to up if shifting down, vice-versa otherwise
|
|
|
|
for(Map.Entry<XSSFComment, Integer> entry : commentsToShift.entrySet()) {
|
2015-11-24 02:53:17 -05:00
|
|
|
entry.getKey().setRow(entry.getValue());
|
2015-03-15 16:52:55 -04:00
|
|
|
}
|
|
|
|
|
2009-02-25 14:12:06 -05:00
|
|
|
XSSFRowShifter rowShifter = new XSSFRowShifter(this);
|
|
|
|
|
2009-02-15 15:47:36 -05:00
|
|
|
int sheetIndex = getWorkbook().getSheetIndex(this);
|
2014-07-20 13:51:51 -04:00
|
|
|
String sheetName = getWorkbook().getSheetName(sheetIndex);
|
|
|
|
FormulaShifter shifter = FormulaShifter.createForRowShift(
|
2015-11-01 21:22:03 -05:00
|
|
|
sheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL2007);
|
2009-02-25 14:12:06 -05:00
|
|
|
|
|
|
|
rowShifter.updateNamedRanges(shifter);
|
|
|
|
rowShifter.updateFormulas(shifter);
|
2016-06-20 00:16:32 -04:00
|
|
|
rowShifter.shiftMergedRegions(startRow, endRow, n);
|
2011-07-29 00:47:25 -04:00
|
|
|
rowShifter.updateConditionalFormatting(shifter);
|
2015-10-29 02:53:55 -04:00
|
|
|
rowShifter.updateHyperlinks(shifter);
|
2009-02-25 14:12:06 -05:00
|
|
|
|
2010-05-24 01:31:48 -04:00
|
|
|
//rebuild the _rows map
|
2016-06-14 22:56:56 -04:00
|
|
|
Map<Integer, XSSFRow> map = new HashMap<Integer, XSSFRow>();
|
2010-05-24 01:31:48 -04:00
|
|
|
for(XSSFRow r : _rows.values()) {
|
2016-06-14 21:42:11 -04:00
|
|
|
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
|
|
|
|
final Integer rownumI = new Integer(r.getRowNum()); // NOSONAR
|
2016-06-12 12:57:15 -04:00
|
|
|
map.put(rownumI, r);
|
2009-02-15 15:47:36 -05:00
|
|
|
}
|
2016-06-14 22:56:56 -04:00
|
|
|
_rows.clear();
|
|
|
|
_rows.putAll(map);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2015-03-15 16:52:55 -04:00
|
|
|
private int shiftedRowNum(int startRow, int endRow, int n, int rownum) {
|
2015-11-24 02:53:17 -05:00
|
|
|
// no change if before any affected row
|
|
|
|
if(rownum < startRow && (n > 0 || (startRow - rownum) > n)) {
|
|
|
|
return rownum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// no change if after any affected row
|
|
|
|
if(rownum > endRow && (n < 0 || (rownum - endRow) > n)) {
|
|
|
|
return rownum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// row before and things are moved up
|
|
|
|
if(rownum < startRow) {
|
|
|
|
// row is moved down by the shifting
|
|
|
|
return rownum + (endRow - startRow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// row is after and things are moved down
|
|
|
|
if(rownum > endRow) {
|
|
|
|
// row is moved up by the shifting
|
|
|
|
return rownum - (endRow - startRow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// row is part of the shifted block
|
|
|
|
return rownum + n;
|
|
|
|
}
|
2015-03-15 16:52:55 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Location of the top left visible cell Location of the top left visible cell in the bottom right
|
|
|
|
* pane (when in Left-to-Right mode).
|
|
|
|
*
|
|
|
|
* @param toprow the top row to show in desktop window pane
|
|
|
|
* @param leftcol the left column to show in desktop window pane
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2013-07-13 01:37:41 -04:00
|
|
|
public void showInPane(int toprow, int leftcol) {
|
2008-09-30 09:57:36 -04:00
|
|
|
CellReference cellReference = new CellReference(toprow, leftcol);
|
|
|
|
String cellRef = cellReference.formatAsString();
|
2009-11-27 15:54:03 -05:00
|
|
|
getPane().setTopLeftCell(cellRef);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-11-14 15:29:42 -05:00
|
|
|
public void ungroupColumn(int fromColumn, int toColumn) {
|
2008-10-29 15:12:47 -04:00
|
|
|
CTCols cols = worksheet.getColsArray(0);
|
|
|
|
for (int index = fromColumn; index <= toColumn; index++) {
|
|
|
|
CTCol col = columnHelper.getColumn(index, false);
|
|
|
|
if (col != null) {
|
|
|
|
short outlineLevel = col.getOutlineLevel();
|
|
|
|
col.setOutlineLevel((short) (outlineLevel - 1));
|
|
|
|
index = (int) col.getMax();
|
|
|
|
|
|
|
|
if (col.getOutlineLevel() <= 0) {
|
|
|
|
int colIndex = columnHelper.getIndexOfColumn(cols, col);
|
2008-09-30 09:57:36 -04:00
|
|
|
worksheet.getColsArray(0).removeCol(colIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-10-29 15:12:47 -04:00
|
|
|
worksheet.setColsArray(0, cols);
|
2008-09-30 09:57:36 -04:00
|
|
|
setSheetFormatPrOutlineLevelCol();
|
|
|
|
}
|
|
|
|
|
2008-11-14 06:56:41 -05:00
|
|
|
/**
|
|
|
|
* Ungroup a range of rows that were previously groupped
|
|
|
|
*
|
|
|
|
* @param fromRow start row (0-based)
|
|
|
|
* @param toRow end row (0-based)
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void ungroupRow(int fromRow, int toRow) {
|
2008-10-29 15:12:47 -04:00
|
|
|
for (int i = fromRow; i <= toRow; i++) {
|
2008-11-14 06:56:41 -05:00
|
|
|
XSSFRow xrow = getRow(i);
|
2008-10-29 15:12:47 -04:00
|
|
|
if (xrow != null) {
|
2014-09-14 18:57:38 -04:00
|
|
|
CTRow ctRow = xrow.getCTRow();
|
|
|
|
int outlineLevel = ctRow.getOutlineLevel();
|
|
|
|
ctRow.setOutlineLevel((short) (outlineLevel - 1));
|
2008-09-30 09:57:36 -04:00
|
|
|
//remove a row only if the row has no cell and if the outline level is 0
|
2014-09-14 18:57:38 -04:00
|
|
|
if (outlineLevel == 1 && xrow.getFirstCellNum() == -1) {
|
2008-09-30 09:57:36 -04:00
|
|
|
removeRow(xrow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setSheetFormatPrOutlineLevelRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setSheetFormatPrOutlineLevelRow(){
|
|
|
|
short maxLevelRow=getMaxOutlineLevelRows();
|
2008-10-12 09:10:30 -04:00
|
|
|
getSheetTypeSheetFormatPr().setOutlineLevelRow(maxLevelRow);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private void setSheetFormatPrOutlineLevelCol(){
|
|
|
|
short maxLevelCol=getMaxOutlineLevelCols();
|
2008-10-12 09:10:30 -04:00
|
|
|
getSheetTypeSheetFormatPr().setOutlineLevelCol(maxLevelCol);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
2008-10-25 07:48:50 -04:00
|
|
|
private CTSheetViews getSheetTypeSheetViews() {
|
2008-09-30 09:57:36 -04:00
|
|
|
if (worksheet.getSheetViews() == null) {
|
|
|
|
worksheet.setSheetViews(CTSheetViews.Factory.newInstance());
|
|
|
|
worksheet.getSheetViews().addNewSheetView();
|
|
|
|
}
|
|
|
|
return worksheet.getSheetViews();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a flag indicating whether this sheet is selected.
|
|
|
|
* <p>
|
|
|
|
* When only 1 sheet is selected and active, this value should be in synch with the activeTab value.
|
|
|
|
* In case of a conflict, the Start Part setting wins and sets the active sheet tab.
|
|
|
|
* </p>
|
|
|
|
* Note: multiple sheets can be selected, but only one sheet can be active at one time.
|
|
|
|
*
|
|
|
|
* @return <code>true</code> if this sheet is selected
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public boolean isSelected() {
|
|
|
|
CTSheetView view = getDefaultSheetView();
|
|
|
|
return view != null && view.getTabSelected();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a flag indicating whether this sheet is selected.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* When only 1 sheet is selected and active, this value should be in synch with the activeTab value.
|
|
|
|
* In case of a conflict, the Start Part setting wins and sets the active sheet tab.
|
|
|
|
* </p>
|
|
|
|
* Note: multiple sheets can be selected, but only one sheet can be active at one time.
|
|
|
|
*
|
|
|
|
* @param value <code>true</code> if this sheet is selected
|
|
|
|
*/
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setSelected(boolean value) {
|
|
|
|
CTSheetViews views = getSheetTypeSheetViews();
|
2010-10-14 04:40:06 -04:00
|
|
|
for (CTSheetView view : views.getSheetViewArray()) {
|
2008-09-30 09:57:36 -04:00
|
|
|
view.setTabSelected(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-12 11:18:29 -04:00
|
|
|
/**
|
|
|
|
* Register a hyperlink in the collection of hyperlinks on this sheet
|
|
|
|
*
|
|
|
|
* @param hyperlink the link to add
|
|
|
|
*/
|
|
|
|
@Internal
|
|
|
|
public void addHyperlink(XSSFHyperlink hyperlink) {
|
2008-09-30 09:57:36 -04:00
|
|
|
hyperlinks.add(hyperlink);
|
|
|
|
}
|
|
|
|
|
2014-11-08 08:46:30 -05:00
|
|
|
/**
|
|
|
|
* Removes a hyperlink in the collection of hyperlinks on this sheet
|
|
|
|
*
|
|
|
|
* @param row row index
|
|
|
|
* @param column column index
|
|
|
|
*/
|
|
|
|
@Internal
|
|
|
|
public void removeHyperlink(int row, int column) {
|
2015-10-29 02:53:55 -04:00
|
|
|
// CTHyperlinks is regenerated from scratch when writing out the spreadsheet
|
|
|
|
// so don't worry about maintaining hyperlinks and CTHyperlinks in parallel.
|
|
|
|
// only maintain hyperlinks
|
2014-11-08 08:46:30 -05:00
|
|
|
String ref = new CellReference(row, column).formatAsString();
|
|
|
|
for (Iterator<XSSFHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
|
|
|
|
XSSFHyperlink hyperlink = it.next();
|
|
|
|
if (hyperlink.getCellRef().equals(ref)) {
|
|
|
|
it.remove();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Return location of the active cell, e.g. <code>A1</code>.
|
|
|
|
*
|
|
|
|
* @return the location of the active cell.
|
|
|
|
*/
|
2015-12-04 01:36:27 -05:00
|
|
|
@Override
|
|
|
|
public CellAddress getActiveCell() {
|
|
|
|
String address = getSheetTypeSelection().getActiveCell();
|
|
|
|
if (address == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return new CellAddress(address);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets location of the active cell
|
|
|
|
*
|
|
|
|
* @param cellRef the location of the active cell, e.g. <code>A1</code>..
|
2015-12-04 01:36:27 -05:00
|
|
|
* @deprecated 3.14beta2 (circa 2015-12-05). Use {@link #setActiveCell(CellAddress)} instead.
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2017-01-07 19:38:41 -05:00
|
|
|
@Deprecated
|
|
|
|
@Removal(version="3.16")
|
2008-09-30 09:57:36 -04:00
|
|
|
public void setActiveCell(String cellRef) {
|
|
|
|
CTSelection ctsel = getSheetTypeSelection();
|
|
|
|
ctsel.setActiveCell(cellRef);
|
|
|
|
ctsel.setSqref(Arrays.asList(cellRef));
|
|
|
|
}
|
|
|
|
|
2015-12-04 01:36:27 -05:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void setActiveCell(CellAddress address) {
|
|
|
|
String ref = address.formatAsString();
|
|
|
|
CTSelection ctsel = getSheetTypeSelection();
|
|
|
|
ctsel.setActiveCell(ref);
|
|
|
|
ctsel.setSqref(Arrays.asList(ref));
|
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
/**
|
|
|
|
* Does this sheet have any comments on it? We need to know,
|
|
|
|
* so we can decide about writing it to disk or not
|
|
|
|
*/
|
|
|
|
public boolean hasComments() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return sheetComments != null && sheetComments.getNumberOfComments() > 0;
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2008-10-25 07:48:50 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
protected int getNumberOfComments() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return sheetComments == null ? 0 : sheetComments.getNumberOfComments();
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private CTSelection getSheetTypeSelection() {
|
|
|
|
if (getSheetTypeSheetView().sizeOfSelectionArray() == 0) {
|
|
|
|
getSheetTypeSheetView().insertNewSelection(0);
|
|
|
|
}
|
|
|
|
return getSheetTypeSheetView().getSelectionArray(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the default sheet view. This is the last one if the sheet's views, according to sec. 3.3.1.83
|
|
|
|
* of the OOXML spec: "A single sheet view definition. When more than 1 sheet view is defined in the file,
|
|
|
|
* it means that when opening the workbook, each sheet view corresponds to a separate window within the
|
|
|
|
* spreadsheet application, where each window is showing the particular sheet. containing the same
|
|
|
|
* workbookViewId value, the last sheetView definition is loaded, and the others are discarded.
|
|
|
|
* When multiple windows are viewing the same sheet, multiple sheetView elements (with corresponding
|
|
|
|
* workbookView entries) are saved."
|
|
|
|
*/
|
|
|
|
private CTSheetView getDefaultSheetView() {
|
|
|
|
CTSheetViews views = getSheetTypeSheetViews();
|
2017-01-07 19:38:41 -05:00
|
|
|
if (views == null) {
|
2008-09-30 09:57:36 -04:00
|
|
|
return null;
|
|
|
|
}
|
2017-01-07 19:38:41 -05:00
|
|
|
int sz = views.sizeOfSheetViewArray();
|
|
|
|
return (sz == 0) ? null : views.getSheetViewArray(sz - 1);
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the sheet's comments object if there is one,
|
|
|
|
* or null if not
|
2009-11-27 12:39:17 -05:00
|
|
|
*
|
|
|
|
* @param create create a new comments table if it does not exist
|
2008-09-30 09:57:36 -04:00
|
|
|
*/
|
2009-11-27 12:39:17 -05:00
|
|
|
protected CommentsTable getCommentsTable(boolean create) {
|
|
|
|
if(sheetComments == null && create){
|
2011-09-22 06:37:54 -04:00
|
|
|
// Try to create a comments table with the same number as
|
|
|
|
// the sheet has (i.e. sheet 1 -> comments 1)
|
|
|
|
try {
|
|
|
|
sheetComments = (CommentsTable)createRelationship(
|
|
|
|
XSSFRelation.SHEET_COMMENTS, XSSFFactory.getInstance(), (int)sheet.getSheetId());
|
|
|
|
} catch(PartAlreadyExistsException e) {
|
|
|
|
// Technically a sheet doesn't need the same number as
|
|
|
|
// it's comments, and clearly someone has already pinched
|
|
|
|
// our number! Go for the next available one instead
|
|
|
|
sheetComments = (CommentsTable)createRelationship(
|
|
|
|
XSSFRelation.SHEET_COMMENTS, XSSFFactory.getInstance(), -1);
|
|
|
|
}
|
2009-11-27 12:39:17 -05:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
return sheetComments;
|
|
|
|
}
|
|
|
|
|
|
|
|
private CTPageSetUpPr getSheetTypePageSetUpPr() {
|
|
|
|
CTSheetPr sheetPr = getSheetTypeSheetPr();
|
|
|
|
return sheetPr.isSetPageSetUpPr() ? sheetPr.getPageSetUpPr() : sheetPr.addNewPageSetUpPr();
|
|
|
|
}
|
|
|
|
|
2016-04-12 18:44:28 -04:00
|
|
|
private static boolean shouldRemoveRow(int startRow, int endRow, int n, int rownum) {
|
2014-06-11 16:36:30 -04:00
|
|
|
// is this row in the target-window where the moved rows will land?
|
2008-09-30 09:57:36 -04:00
|
|
|
if (rownum >= (startRow + n) && rownum <= (endRow + n)) {
|
2014-06-11 16:36:30 -04:00
|
|
|
// only remove it if the current row is not part of the data that is copied
|
2008-09-30 09:57:36 -04:00
|
|
|
if (n > 0 && rownum > endRow) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (n < 0 && rownum < startRow) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private CTPane getPane() {
|
|
|
|
if (getDefaultSheetView().getPane() == null) {
|
|
|
|
getDefaultSheetView().addNewPane();
|
|
|
|
}
|
|
|
|
return getDefaultSheetView().getPane();
|
|
|
|
}
|
|
|
|
|
2008-12-22 14:32:44 -05:00
|
|
|
/**
|
2010-09-12 04:54:45 -04:00
|
|
|
* Return a master shared formula by index
|
2008-12-22 14:32:44 -05:00
|
|
|
*
|
|
|
|
* @param sid shared group index
|
2010-09-12 04:54:45 -04:00
|
|
|
* @return a CTCellFormula bean holding shared formula or <code>null</code> if not found
|
2008-12-22 14:32:44 -05:00
|
|
|
*/
|
2012-12-04 07:44:33 -05:00
|
|
|
@Internal
|
|
|
|
public CTCellFormula getSharedFormula(int sid){
|
2008-12-22 14:32:44 -05:00
|
|
|
return sharedFormulas.get(sid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void onReadCell(XSSFCell cell){
|
|
|
|
//collect cells holding shared formulas
|
|
|
|
CTCell ct = cell.getCTCell();
|
|
|
|
CTCellFormula f = ct.getF();
|
2009-12-23 15:58:01 -05:00
|
|
|
if (f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null) {
|
2010-09-12 04:54:45 -04:00
|
|
|
// save a detached copy to avoid XmlValueDisconnectedException,
|
|
|
|
// this may happen when the master cell of a shared formula is changed
|
2012-02-27 07:18:45 -05:00
|
|
|
CTCellFormula sf = (CTCellFormula)f.copy();
|
|
|
|
CellRangeAddress sfRef = CellRangeAddress.valueOf(sf.getRef());
|
|
|
|
CellReference cellRef = new CellReference(cell);
|
2014-09-14 18:57:38 -04:00
|
|
|
// If the shared formula range precedes the master cell then the preceding part is discarded, e.g.
|
2012-02-27 07:18:45 -05:00
|
|
|
// if the cell is E60 and the shared formula range is C60:M85 then the effective range is E60:M85
|
|
|
|
// see more details in https://issues.apache.org/bugzilla/show_bug.cgi?id=51710
|
|
|
|
if(cellRef.getCol() > sfRef.getFirstColumn() || cellRef.getRow() > sfRef.getFirstRow()){
|
|
|
|
String effectiveRef = new CellRangeAddress(
|
|
|
|
Math.max(cellRef.getRow(), sfRef.getFirstRow()), sfRef.getLastRow(),
|
|
|
|
Math.max(cellRef.getCol(), sfRef.getFirstColumn()), sfRef.getLastColumn()).formatAsString();
|
|
|
|
sf.setRef(effectiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
sharedFormulas.put((int)f.getSi(), sf);
|
2008-12-22 14:32:44 -05:00
|
|
|
}
|
2009-12-23 15:58:01 -05:00
|
|
|
if (f != null && f.getT() == STCellFormulaType.ARRAY && f.getRef() != null) {
|
|
|
|
arrayFormulas.add(CellRangeAddress.valueOf(f.getRef()));
|
2009-12-25 18:04:04 -05:00
|
|
|
}
|
2008-12-22 14:32:44 -05:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void commit() throws IOException {
|
2008-10-28 06:03:51 -04:00
|
|
|
PackagePart part = getPackagePart();
|
|
|
|
OutputStream out = part.getOutputStream();
|
|
|
|
write(out);
|
|
|
|
out.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void write(OutputStream out) throws IOException {
|
2014-06-11 16:36:30 -04:00
|
|
|
boolean setToNull = false;
|
2010-10-14 04:40:06 -04:00
|
|
|
if(worksheet.sizeOfColsArray() == 1) {
|
2014-06-11 16:36:30 -04:00
|
|
|
CTCols col = worksheet.getColsArray(0);
|
2013-08-20 11:03:18 -04:00
|
|
|
if(col.sizeOfColArray() == 0) {
|
2014-06-11 16:36:30 -04:00
|
|
|
setToNull = true;
|
|
|
|
// this is necessary so that we do not write an empty <cols/> item into the sheet-xml in the xlsx-file
|
|
|
|
// Excel complains about a corrupted file if this shows up there!
|
2013-08-20 11:03:18 -04:00
|
|
|
worksheet.setColsArray(null);
|
|
|
|
} else {
|
2014-06-11 16:36:30 -04:00
|
|
|
setColWidthAttribute(col);
|
2013-08-20 11:03:18 -04:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2008-10-21 13:56:34 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
// Now re-generate our CTHyperlinks, if needed
|
|
|
|
if(hyperlinks.size() > 0) {
|
|
|
|
if(worksheet.getHyperlinks() == null) {
|
|
|
|
worksheet.addNewHyperlinks();
|
|
|
|
}
|
|
|
|
CTHyperlink[] ctHls = new CTHyperlink[hyperlinks.size()];
|
|
|
|
for(int i=0; i<ctHls.length; i++) {
|
|
|
|
// If our sheet has hyperlinks, have them add
|
|
|
|
// any relationships that they might need
|
|
|
|
XSSFHyperlink hyperlink = hyperlinks.get(i);
|
|
|
|
hyperlink.generateRelationIfNeeded(getPackagePart());
|
|
|
|
// Now grab their underling object
|
|
|
|
ctHls[i] = hyperlink.getCTHyperlink();
|
|
|
|
}
|
|
|
|
worksheet.getHyperlinks().setHyperlinkArray(ctHls);
|
|
|
|
}
|
2017-01-19 02:44:25 -05:00
|
|
|
else {
|
2017-01-19 03:09:11 -05:00
|
|
|
if (worksheet.getHyperlinks() != null) {
|
|
|
|
final int count = worksheet.getHyperlinks().sizeOfHyperlinkArray();
|
|
|
|
for (int i=count-1; i>=0; i--) {
|
|
|
|
worksheet.getHyperlinks().removeHyperlink(i);
|
|
|
|
}
|
2017-01-19 04:01:39 -05:00
|
|
|
// For some reason, we have to remove the hyperlinks one by one from the CTHyperlinks array
|
|
|
|
// before unsetting the hyperlink array.
|
|
|
|
// Resetting the hyperlink array seems to break some XML nodes.
|
|
|
|
//worksheet.getHyperlinks().setHyperlinkArray(new CTHyperlink[0]);
|
|
|
|
worksheet.unsetHyperlinks();
|
2017-01-19 03:09:11 -05:00
|
|
|
} else {
|
|
|
|
// nothing to do
|
|
|
|
}
|
2017-01-19 02:44:25 -05:00
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
|
2016-10-29 03:38:46 -04:00
|
|
|
int minCell=Integer.MAX_VALUE, maxCell=Integer.MIN_VALUE;
|
2010-05-24 01:31:48 -04:00
|
|
|
for(XSSFRow row : _rows.values()){
|
2016-10-29 03:38:46 -04:00
|
|
|
// first perform the normal write actions for the row
|
2009-02-25 14:12:06 -05:00
|
|
|
row.onDocumentWrite();
|
2016-10-29 03:38:46 -04:00
|
|
|
|
|
|
|
// then calculate min/max cell-numbers for the worksheet-dimension
|
|
|
|
if(row.getFirstCellNum() != -1) {
|
|
|
|
minCell = Math.min(minCell, row.getFirstCellNum());
|
|
|
|
}
|
|
|
|
if(row.getLastCellNum() != -1) {
|
|
|
|
maxCell = Math.max(maxCell, row.getLastCellNum());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally, if we had at least one cell we can populate the optional dimension-field
|
|
|
|
if(minCell != Integer.MAX_VALUE) {
|
|
|
|
String ref = new CellRangeAddress(getFirstRowNum(), getLastRowNum(), minCell, maxCell).formatAsString();
|
|
|
|
if(worksheet.isSetDimension()) {
|
|
|
|
worksheet.getDimension().setRef(ref);
|
|
|
|
} else {
|
|
|
|
worksheet.addNewDimension().setRef(ref);
|
|
|
|
}
|
2008-10-23 14:57:28 -04:00
|
|
|
}
|
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
|
|
|
|
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorksheet.type.getName().getNamespaceURI(), "worksheet"));
|
2008-10-10 10:54:32 -04:00
|
|
|
|
2008-09-30 09:57:36 -04:00
|
|
|
worksheet.save(out, xmlOptions);
|
2013-08-20 14:44:44 -04:00
|
|
|
|
|
|
|
// Bug 52233: Ensure that we have a col-array even if write() removed it
|
2014-06-11 16:36:30 -04:00
|
|
|
if(setToNull) {
|
|
|
|
worksheet.addNewCols();
|
|
|
|
}
|
2008-09-30 09:57:36 -04:00
|
|
|
}
|
2009-10-28 12:05:39 -04:00
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
|
|
|
* @return true when Autofilters are locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isAutoFilterLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getAutoFilter();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Deleting columns is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isDeleteColumnsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getDeleteColumns();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Deleting rows is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isDeleteRowsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getDeleteRows();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Formatting cells is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isFormatCellsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getFormatCells();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Formatting columns is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isFormatColumnsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getFormatColumns();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Formatting rows is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isFormatRowsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getFormatRows();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Inserting columns is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isInsertColumnsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getInsertColumns();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Inserting hyperlinks is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isInsertHyperlinksLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getInsertHyperlinks();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Inserting rows is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isInsertRowsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getInsertRows();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Pivot tables are locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isPivotTablesLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getPivotTables();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Sorting is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isSortLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getSort();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Objects are locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isObjectsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getObjects();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Scenarios are locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isScenariosLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getScenarios();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Selection of locked cells is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isSelectLockedCellsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getSelectLockedCells();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Selection of unlocked cells is locked and the sheet is protected.
|
|
|
|
*/
|
|
|
|
public boolean isSelectUnlockedCellsLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return isSheetLocked() && safeGetProtectionField().getSelectUnlockedCells();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true when Sheet is Protected.
|
|
|
|
*/
|
|
|
|
public boolean isSheetLocked() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return worksheet.isSetSheetProtection() && safeGetProtectionField().getSheet();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable sheet protection
|
|
|
|
*/
|
|
|
|
public void enableLocking() {
|
2014-09-04 18:50:28 -04:00
|
|
|
safeGetProtectionField().setSheet(true);
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable sheet protection
|
|
|
|
*/
|
|
|
|
public void disableLocking() {
|
2014-09-04 18:50:28 -04:00
|
|
|
safeGetProtectionField().setSheet(false);
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Autofilters locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockAutoFilter(boolean enabled) {
|
|
|
|
safeGetProtectionField().setAutoFilter(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Deleting columns locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockDeleteColumns(boolean enabled) {
|
|
|
|
safeGetProtectionField().setDeleteColumns(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Deleting rows locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockDeleteRows(boolean enabled) {
|
|
|
|
safeGetProtectionField().setDeleteRows(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Formatting cells locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockFormatCells(boolean enabled) {
|
|
|
|
safeGetProtectionField().setFormatCells(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Formatting columns locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockFormatColumns(boolean enabled) {
|
|
|
|
safeGetProtectionField().setFormatColumns(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Formatting rows locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockFormatRows(boolean enabled) {
|
|
|
|
safeGetProtectionField().setFormatRows(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Inserting columns locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockInsertColumns(boolean enabled) {
|
|
|
|
safeGetProtectionField().setInsertColumns(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Inserting hyperlinks locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockInsertHyperlinks(boolean enabled) {
|
|
|
|
safeGetProtectionField().setInsertHyperlinks(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Inserting rows locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockInsertRows(boolean enabled) {
|
|
|
|
safeGetProtectionField().setInsertRows(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Pivot Tables locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockPivotTables(boolean enabled) {
|
|
|
|
safeGetProtectionField().setPivotTables(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Sort locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockSort(boolean enabled) {
|
|
|
|
safeGetProtectionField().setSort(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Objects locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockObjects(boolean enabled) {
|
|
|
|
safeGetProtectionField().setObjects(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Scenarios locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockScenarios(boolean enabled) {
|
|
|
|
safeGetProtectionField().setScenarios(enabled);
|
|
|
|
}
|
|
|
|
|
2009-11-27 15:54:03 -05:00
|
|
|
/**
|
2014-09-04 18:50:28 -04:00
|
|
|
* Enable or disable Selection of locked cells locking.
|
2009-11-27 15:54:03 -05:00
|
|
|
* This does not modify sheet protection status.
|
2014-09-04 18:50:28 -04:00
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockSelectLockedCells(boolean enabled) {
|
|
|
|
safeGetProtectionField().setSelectLockedCells(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable Selection of unlocked cells locking.
|
|
|
|
* This does not modify sheet protection status.
|
|
|
|
* To enforce this un-/locking, call {@link #disableLocking()} or {@link #enableLocking()}
|
|
|
|
*/
|
|
|
|
public void lockSelectUnlockedCells(boolean enabled) {
|
|
|
|
safeGetProtectionField().setSelectUnlockedCells(enabled);
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
2014-09-04 18:50:28 -04:00
|
|
|
private CTSheetProtection safeGetProtectionField() {
|
|
|
|
if (!isSheetProtectionEnabled()) {
|
|
|
|
return worksheet.addNewSheetProtection();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
2014-09-04 18:50:28 -04:00
|
|
|
return worksheet.getSheetProtection();
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
|
|
|
|
2014-09-04 18:50:28 -04:00
|
|
|
/* package */ boolean isSheetProtectionEnabled() {
|
|
|
|
return (worksheet.isSetSheetProtection());
|
2009-11-27 15:54:03 -05:00
|
|
|
}
|
2009-12-23 15:58:01 -05:00
|
|
|
|
|
|
|
/* package */ boolean isCellInArrayFormulaContext(XSSFCell cell) {
|
|
|
|
for (CellRangeAddress range : arrayFormulas) {
|
|
|
|
if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* package */ XSSFCell getFirstCellInArrayFormula(XSSFCell cell) {
|
|
|
|
for (CellRangeAddress range : arrayFormulas) {
|
|
|
|
if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
|
|
|
|
return getRow(range.getFirstRow()).getCell(range.getFirstColumn());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-12-25 18:04:04 -05:00
|
|
|
* Also creates cells if they don't exist
|
|
|
|
*/
|
|
|
|
private CellRange<XSSFCell> getCellRange(CellRangeAddress range) {
|
|
|
|
int firstRow = range.getFirstRow();
|
|
|
|
int firstColumn = range.getFirstColumn();
|
|
|
|
int lastRow = range.getLastRow();
|
|
|
|
int lastColumn = range.getLastColumn();
|
|
|
|
int height = lastRow - firstRow + 1;
|
|
|
|
int width = lastColumn - firstColumn + 1;
|
|
|
|
List<XSSFCell> temp = new ArrayList<XSSFCell>(height*width);
|
|
|
|
for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
|
|
|
|
for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
|
|
|
|
XSSFRow row = getRow(rowIn);
|
|
|
|
if (row == null) {
|
|
|
|
row = createRow(rowIn);
|
|
|
|
}
|
|
|
|
XSSFCell cell = row.getCell(colIn);
|
|
|
|
if (cell == null) {
|
|
|
|
cell = row.createCell(colIn);
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
2009-12-25 18:04:04 -05:00
|
|
|
temp.add(cell);
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
|
|
|
}
|
2009-12-25 18:04:04 -05:00
|
|
|
return SSCellRange.create(firstRow, firstColumn, height, width, temp, XSSFCell.class);
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-12-25 18:04:04 -05:00
|
|
|
public CellRange<XSSFCell> setArrayFormula(String formula, CellRangeAddress range) {
|
|
|
|
|
|
|
|
CellRange<XSSFCell> cr = getCellRange(range);
|
|
|
|
|
|
|
|
XSSFCell mainArrayFormulaCell = cr.getTopLeftCell();
|
|
|
|
mainArrayFormulaCell.setCellArrayFormula(formula, range);
|
|
|
|
arrayFormulas.add(range);
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2009-12-25 18:04:04 -05:00
|
|
|
public CellRange<XSSFCell> removeArrayFormula(Cell cell) {
|
|
|
|
if (cell.getSheet() != this) {
|
|
|
|
throw new IllegalArgumentException("Specified cell does not belong to this sheet.");
|
|
|
|
}
|
2009-12-23 15:58:01 -05:00
|
|
|
for (CellRangeAddress range : arrayFormulas) {
|
|
|
|
if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
|
|
|
|
arrayFormulas.remove(range);
|
2009-12-25 18:04:04 -05:00
|
|
|
CellRange<XSSFCell> cr = getCellRange(range);
|
|
|
|
for (XSSFCell c : cr) {
|
2016-07-04 06:15:18 -04:00
|
|
|
c.setCellType(CellType.BLANK);
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
2009-12-25 18:04:04 -05:00
|
|
|
return cr;
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
String ref = ((XSSFCell)cell).getCTCell().getR();
|
2009-12-25 18:04:04 -05:00
|
|
|
throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula.");
|
2009-12-23 15:58:01 -05:00
|
|
|
}
|
2010-05-16 11:49:21 -04:00
|
|
|
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
|
|
|
public DataValidationHelper getDataValidationHelper() {
|
|
|
|
return dataValidationHelper;
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
2010-05-16 11:49:21 -04:00
|
|
|
public List<XSSFDataValidation> getDataValidations() {
|
2014-06-11 16:36:30 -04:00
|
|
|
List<XSSFDataValidation> xssfValidations = new ArrayList<XSSFDataValidation>();
|
|
|
|
CTDataValidations dataValidations = this.worksheet.getDataValidations();
|
|
|
|
if( dataValidations!=null && dataValidations.getCount() > 0 ) {
|
|
|
|
for (CTDataValidation ctDataValidation : dataValidations.getDataValidationArray()) {
|
|
|
|
CellRangeAddressList addressList = new CellRangeAddressList();
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
List<String> sqref = ctDataValidation.getSqref();
|
|
|
|
for (String stRef : sqref) {
|
|
|
|
String[] regions = stRef.split(" ");
|
|
|
|
for (String region : regions) {
|
|
|
|
String[] parts = region.split(":");
|
|
|
|
CellReference begin = new CellReference(parts[0]);
|
|
|
|
CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin;
|
|
|
|
CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol());
|
|
|
|
addressList.addCellRangeAddress(cellRangeAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XSSFDataValidation xssfDataValidation = new XSSFDataValidation(addressList, ctDataValidation);
|
|
|
|
xssfValidations.add(xssfDataValidation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return xssfValidations;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void addValidationData(DataValidation dataValidation) {
|
|
|
|
XSSFDataValidation xssfDataValidation = (XSSFDataValidation)dataValidation;
|
|
|
|
CTDataValidations dataValidations = worksheet.getDataValidations();
|
|
|
|
if( dataValidations==null ) {
|
|
|
|
dataValidations = worksheet.addNewDataValidations();
|
|
|
|
}
|
|
|
|
int currentCount = dataValidations.sizeOfDataValidationArray();
|
2010-05-16 11:49:21 -04:00
|
|
|
CTDataValidation newval = dataValidations.addNewDataValidation();
|
2014-06-11 16:36:30 -04:00
|
|
|
newval.set(xssfDataValidation.getCtDdataValidation());
|
|
|
|
dataValidations.setCount(currentCount + 1);
|
2010-05-16 11:49:21 -04:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
}
|
2010-08-08 07:11:38 -04:00
|
|
|
|
2016-01-10 15:44:17 -05:00
|
|
|
@SuppressWarnings("resource")
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2010-08-08 07:11:38 -04:00
|
|
|
public XSSFAutoFilter setAutoFilter(CellRangeAddress range) {
|
|
|
|
CTAutoFilter af = worksheet.getAutoFilter();
|
2017-01-07 19:38:41 -05:00
|
|
|
if(af == null) {
|
|
|
|
af = worksheet.addNewAutoFilter();
|
|
|
|
}
|
2010-08-08 07:11:38 -04:00
|
|
|
|
|
|
|
CellRangeAddress norm = new CellRangeAddress(range.getFirstRow(), range.getLastRow(),
|
|
|
|
range.getFirstColumn(), range.getLastColumn());
|
|
|
|
String ref = norm.formatAsString();
|
|
|
|
af.setRef(ref);
|
|
|
|
|
2010-12-02 10:23:35 -05:00
|
|
|
XSSFWorkbook wb = getWorkbook();
|
|
|
|
int sheetIndex = getWorkbook().getSheetIndex(this);
|
|
|
|
XSSFName name = wb.getBuiltInName(XSSFName.BUILTIN_FILTER_DB, sheetIndex);
|
|
|
|
if (name == null) {
|
|
|
|
name = wb.createBuiltInName(XSSFName.BUILTIN_FILTER_DB, sheetIndex);
|
|
|
|
}
|
2014-06-11 16:36:30 -04:00
|
|
|
|
2014-01-01 17:15:44 -05:00
|
|
|
name.getCTName().setHidden(true);
|
|
|
|
CellReference r1 = new CellReference(getSheetName(), range.getFirstRow(), range.getFirstColumn(), true, true);
|
|
|
|
CellReference r2 = new CellReference(null, range.getLastRow(), range.getLastColumn(), true, true);
|
|
|
|
String fmla = r1.formatAsString() + ":" + r2.formatAsString();
|
|
|
|
name.setRefersToFormula(fmla);
|
2010-12-02 10:23:35 -05:00
|
|
|
|
2010-08-08 07:11:38 -04:00
|
|
|
return new XSSFAutoFilter(this);
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-08 11:07:35 -04:00
|
|
|
/**
|
|
|
|
* Creates a new Table, and associates it with this Sheet
|
|
|
|
*/
|
2011-04-14 10:32:49 -04:00
|
|
|
public XSSFTable createTable() {
|
2011-04-08 11:07:35 -04:00
|
|
|
if(! worksheet.isSetTableParts()) {
|
|
|
|
worksheet.addNewTableParts();
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-08 11:07:35 -04:00
|
|
|
CTTableParts tblParts = worksheet.getTableParts();
|
|
|
|
CTTablePart tbl = tblParts.addNewTablePart();
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-14 10:29:04 -04:00
|
|
|
// Table numbers need to be unique in the file, not just
|
|
|
|
// unique within the sheet. Find the next one
|
|
|
|
int tableNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.TABLE.getContentType()).size() + 1;
|
2016-01-10 15:44:17 -05:00
|
|
|
RelationPart rp = createRelationship(XSSFRelation.TABLE, XSSFFactory.getInstance(), tableNumber, false);
|
|
|
|
XSSFTable table = rp.getDocumentPart();
|
|
|
|
tbl.setId(rp.getRelationship().getId());
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-08 11:07:35 -04:00
|
|
|
tables.put(tbl.getId(), table);
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-04-08 11:07:35 -04:00
|
|
|
return table;
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2011-02-25 16:44:09 -05:00
|
|
|
/**
|
|
|
|
* Returns any tables associated with this Sheet
|
|
|
|
*/
|
2011-04-14 10:32:49 -04:00
|
|
|
public List<XSSFTable> getTables() {
|
2014-09-14 18:57:38 -04:00
|
|
|
return new ArrayList<XSSFTable>(tables.values());
|
2011-02-25 16:44:09 -05:00
|
|
|
}
|
2011-07-29 00:47:25 -04:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2011-07-29 00:47:25 -04:00
|
|
|
public XSSFSheetConditionalFormatting getSheetConditionalFormatting(){
|
|
|
|
return new XSSFSheetConditionalFormatting(this);
|
|
|
|
}
|
2016-04-17 03:38:15 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get background color of the sheet tab.
|
|
|
|
* Returns <tt>null</tt> if no sheet tab color is set.
|
|
|
|
*
|
|
|
|
* @return the background color of the sheet tab
|
|
|
|
*/
|
|
|
|
public XSSFColor getTabColor() {
|
|
|
|
CTSheetPr pr = worksheet.getSheetPr();
|
2017-01-07 19:38:41 -05:00
|
|
|
if(pr == null) {
|
|
|
|
pr = worksheet.addNewSheetPr();
|
|
|
|
}
|
2016-04-17 03:38:15 -04:00
|
|
|
if (!pr.isSetTabColor()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return new XSSFColor(pr.getTabColor());
|
|
|
|
}
|
2012-02-29 07:53:48 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set background color of the sheet tab
|
|
|
|
*
|
2016-04-17 03:38:15 -04:00
|
|
|
* @param colorIndex the indexed color to set, must be a constant from {@link org.apache.poi.ss.usermodel.IndexedColors}
|
|
|
|
* @deprecated 3.15-beta2. Removed in 3.17. Use {@link #setTabColor(XSSFColor)}.
|
|
|
|
*/
|
2017-01-07 19:38:41 -05:00
|
|
|
@Deprecated
|
|
|
|
@Removal(version="3.17")
|
2016-04-17 03:38:15 -04:00
|
|
|
public void setTabColor(int colorIndex) {
|
|
|
|
IndexedColors indexedColor = IndexedColors.fromInt(colorIndex);
|
|
|
|
XSSFColor color = new XSSFColor(indexedColor);
|
|
|
|
setTabColor(color);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set background color of the sheet tab
|
|
|
|
*
|
|
|
|
* @param color the color to set
|
2012-02-29 07:53:48 -05:00
|
|
|
*/
|
2016-04-17 03:38:15 -04:00
|
|
|
public void setTabColor(XSSFColor color) {
|
2012-02-29 07:53:48 -05:00
|
|
|
CTSheetPr pr = worksheet.getSheetPr();
|
2017-01-07 19:38:41 -05:00
|
|
|
if(pr == null) {
|
|
|
|
pr = worksheet.addNewSheetPr();
|
|
|
|
}
|
2016-04-17 03:38:15 -04:00
|
|
|
pr.setTabColor(color.getCTColor());
|
2012-02-29 07:53:48 -05:00
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2012-07-21 06:33:00 -04:00
|
|
|
public CellRangeAddress getRepeatingRows() {
|
|
|
|
return getRepeatingRowsOrColums(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2012-07-21 06:33:00 -04:00
|
|
|
public CellRangeAddress getRepeatingColumns() {
|
|
|
|
return getRepeatingRowsOrColums(false);
|
|
|
|
}
|
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2012-08-04 04:51:49 -04:00
|
|
|
public void setRepeatingRows(CellRangeAddress rowRangeRef) {
|
|
|
|
CellRangeAddress columnRangeRef = getRepeatingColumns();
|
|
|
|
setRepeatingRowsAndColumns(rowRangeRef, columnRangeRef);
|
|
|
|
}
|
|
|
|
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2014-06-11 16:36:30 -04:00
|
|
|
@Override
|
2012-08-04 04:51:49 -04:00
|
|
|
public void setRepeatingColumns(CellRangeAddress columnRangeRef) {
|
|
|
|
CellRangeAddress rowRangeRef = getRepeatingRows();
|
|
|
|
setRepeatingRowsAndColumns(rowRangeRef, columnRangeRef);
|
|
|
|
}
|
|
|
|
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2012-08-04 04:51:49 -04:00
|
|
|
private void setRepeatingRowsAndColumns(
|
|
|
|
CellRangeAddress rowDef, CellRangeAddress colDef) {
|
2013-08-14 10:57:44 -04:00
|
|
|
int col1 = -1;
|
2012-08-04 04:51:49 -04:00
|
|
|
int col2 = -1;
|
2013-08-14 10:57:44 -04:00
|
|
|
int row1 = -1;
|
2012-08-04 04:51:49 -04:00
|
|
|
int row2 = -1;
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2012-08-04 04:51:49 -04:00
|
|
|
if (rowDef != null) {
|
|
|
|
row1 = rowDef.getFirstRow();
|
|
|
|
row2 = rowDef.getLastRow();
|
2013-08-14 10:57:44 -04:00
|
|
|
if ((row1 == -1 && row2 != -1)
|
2012-08-04 04:51:49 -04:00
|
|
|
|| row1 < -1 || row2 < -1 || row1 > row2) {
|
|
|
|
throw new IllegalArgumentException("Invalid row range specification");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (colDef != null) {
|
|
|
|
col1 = colDef.getFirstColumn();
|
|
|
|
col2 = colDef.getLastColumn();
|
2013-08-14 10:57:44 -04:00
|
|
|
if ((col1 == -1 && col2 != -1)
|
2012-08-04 04:51:49 -04:00
|
|
|
|| col1 < -1 || col2 < -1 || col1 > col2) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"Invalid column range specification");
|
|
|
|
}
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
|
2012-08-04 04:51:49 -04:00
|
|
|
int sheetIndex = getWorkbook().getSheetIndex(this);
|
|
|
|
|
|
|
|
boolean removeAll = rowDef == null && colDef == null;
|
|
|
|
|
|
|
|
XSSFName name = getWorkbook().getBuiltInName(
|
|
|
|
XSSFName.BUILTIN_PRINT_TITLE, sheetIndex);
|
|
|
|
if (removeAll) {
|
|
|
|
if (name != null) {
|
|
|
|
getWorkbook().removeName(name);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (name == null) {
|
|
|
|
name = getWorkbook().createBuiltInName(
|
|
|
|
XSSFName.BUILTIN_PRINT_TITLE, sheetIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
String reference = getReferenceBuiltInRecord(
|
|
|
|
name.getSheetName(), col1, col2, row1, row2);
|
|
|
|
name.setRefersToFormula(reference);
|
|
|
|
|
|
|
|
// If the print setup isn't currently defined, then add it
|
|
|
|
// in but without printer defaults
|
|
|
|
// If it's already there, leave it as-is!
|
|
|
|
if (worksheet.isSetPageSetup() && worksheet.isSetPageMargins()) {
|
|
|
|
// Everything we need is already there
|
|
|
|
} else {
|
|
|
|
// Have initial ones put in place
|
|
|
|
getPrintSetup().setValidSettings(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String getReferenceBuiltInRecord(
|
|
|
|
String sheetName, int startC, int endC, int startR, int endR) {
|
2013-08-14 10:57:44 -04:00
|
|
|
// Excel example for built-in title:
|
2012-08-04 04:51:49 -04:00
|
|
|
// 'second sheet'!$E:$F,'second sheet'!$2:$3
|
2013-08-14 10:57:44 -04:00
|
|
|
|
|
|
|
CellReference colRef =
|
2012-08-04 04:51:49 -04:00
|
|
|
new CellReference(sheetName, 0, startC, true, true);
|
2013-08-14 10:57:44 -04:00
|
|
|
CellReference colRef2 =
|
2012-08-04 04:51:49 -04:00
|
|
|
new CellReference(sheetName, 0, endC, true, true);
|
2013-08-14 10:57:44 -04:00
|
|
|
CellReference rowRef =
|
2012-08-04 04:51:49 -04:00
|
|
|
new CellReference(sheetName, startR, 0, true, true);
|
2013-08-14 10:57:44 -04:00
|
|
|
CellReference rowRef2 =
|
2012-08-04 04:51:49 -04:00
|
|
|
new CellReference(sheetName, endR, 0, true, true);
|
|
|
|
|
|
|
|
String escapedName = SheetNameFormatter.format(sheetName);
|
|
|
|
|
|
|
|
String c = "";
|
|
|
|
String r = "";
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
if (startC != -1 || endC != -1) {
|
2016-04-12 18:44:28 -04:00
|
|
|
String col1 = colRef.getCellRefParts()[2];
|
|
|
|
String col2 = colRef2.getCellRefParts()[2];
|
|
|
|
c = escapedName + "!$" + col1 + ":$" + col2;
|
2012-08-04 04:51:49 -04:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:57:38 -04:00
|
|
|
if (startR != -1 || endR != -1) {
|
2016-04-12 18:44:28 -04:00
|
|
|
String row1 = rowRef.getCellRefParts()[1];
|
|
|
|
String row2 = rowRef2.getCellRefParts()[1];
|
|
|
|
if (!row1.equals("0") && !row2.equals("0")) {
|
|
|
|
r = escapedName + "!$" + row1 + ":$" + row2;
|
2014-09-14 18:57:38 -04:00
|
|
|
}
|
2012-08-04 04:51:49 -04:00
|
|
|
}
|
|
|
|
|
2014-08-27 20:08:41 -04:00
|
|
|
StringBuilder rng = new StringBuilder();
|
2012-08-04 04:51:49 -04:00
|
|
|
rng.append(c);
|
|
|
|
if(rng.length() > 0 && r.length() > 0) {
|
|
|
|
rng.append(',');
|
|
|
|
}
|
|
|
|
rng.append(r);
|
|
|
|
return rng.toString();
|
|
|
|
}
|
|
|
|
|
2012-07-21 06:33:00 -04:00
|
|
|
|
|
|
|
private CellRangeAddress getRepeatingRowsOrColums(boolean rows) {
|
|
|
|
int sheetIndex = getWorkbook().getSheetIndex(this);
|
|
|
|
XSSFName name = getWorkbook().getBuiltInName(
|
|
|
|
XSSFName.BUILTIN_PRINT_TITLE, sheetIndex);
|
|
|
|
if (name == null ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
String refStr = name.getRefersToFormula();
|
|
|
|
if (refStr == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
String[] parts = refStr.split(",");
|
|
|
|
int maxRowIndex = SpreadsheetVersion.EXCEL2007.getLastRowIndex();
|
|
|
|
int maxColIndex = SpreadsheetVersion.EXCEL2007.getLastColumnIndex();
|
|
|
|
for (String part : parts) {
|
|
|
|
CellRangeAddress range = CellRangeAddress.valueOf(part);
|
2013-08-14 10:57:44 -04:00
|
|
|
if ((range.getFirstColumn() == 0
|
2012-07-21 06:33:00 -04:00
|
|
|
&& range.getLastColumn() == maxColIndex)
|
2013-08-14 10:57:44 -04:00
|
|
|
|| (range.getFirstColumn() == -1
|
2012-07-21 06:33:00 -04:00
|
|
|
&& range.getLastColumn() == -1)) {
|
|
|
|
if (rows) {
|
|
|
|
return range;
|
|
|
|
}
|
2013-08-14 10:57:44 -04:00
|
|
|
} else if (range.getFirstRow() == 0
|
2012-07-21 06:33:00 -04:00
|
|
|
&& range.getLastRow() == maxRowIndex
|
2013-08-14 10:57:44 -04:00
|
|
|
|| (range.getFirstRow() == -1
|
2012-07-21 06:33:00 -04:00
|
|
|
&& range.getLastRow() == -1)) {
|
|
|
|
if (!rows) {
|
|
|
|
return range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2014-07-30 11:27:09 -04:00
|
|
|
/**
|
|
|
|
* Creates an empty XSSFPivotTable and sets up all its relationships
|
|
|
|
* including: pivotCacheDefinition, pivotCacheRecords
|
|
|
|
* @return returns a pivotTable
|
|
|
|
*/
|
2016-01-10 15:44:17 -05:00
|
|
|
@SuppressWarnings("resource")
|
2014-07-30 11:27:09 -04:00
|
|
|
@Beta
|
|
|
|
private XSSFPivotTable createPivotTable() {
|
|
|
|
XSSFWorkbook wb = getWorkbook();
|
|
|
|
List<XSSFPivotTable> pivotTables = wb.getPivotTables();
|
|
|
|
int tableId = getWorkbook().getPivotTables().size()+1;
|
|
|
|
//Create relationship between pivotTable and the worksheet
|
|
|
|
XSSFPivotTable pivotTable = (XSSFPivotTable) createRelationship(XSSFRelation.PIVOT_TABLE,
|
|
|
|
XSSFFactory.getInstance(), tableId);
|
|
|
|
pivotTable.setParentSheet(this);
|
|
|
|
pivotTables.add(pivotTable);
|
|
|
|
XSSFWorkbook workbook = getWorkbook();
|
|
|
|
|
|
|
|
//Create relationship between the pivot cache defintion and the workbook
|
|
|
|
XSSFPivotCacheDefinition pivotCacheDefinition = (XSSFPivotCacheDefinition) workbook.
|
|
|
|
createRelationship(XSSFRelation.PIVOT_CACHE_DEFINITION, XSSFFactory.getInstance(), tableId);
|
|
|
|
String rId = workbook.getRelationId(pivotCacheDefinition);
|
|
|
|
//Create relationship between pivotTable and pivotCacheDefinition without creating a new instance
|
|
|
|
PackagePart pivotPackagePart = pivotTable.getPackagePart();
|
|
|
|
pivotPackagePart.addRelationship(pivotCacheDefinition.getPackagePart().getPartName(),
|
|
|
|
TargetMode.INTERNAL, XSSFRelation.PIVOT_CACHE_DEFINITION.getRelation());
|
|
|
|
|
|
|
|
pivotTable.setPivotCacheDefinition(pivotCacheDefinition);
|
|
|
|
|
|
|
|
//Create pivotCache and sets up it's relationship with the workbook
|
|
|
|
pivotTable.setPivotCache(new XSSFPivotCache(workbook.addPivotCache(rId)));
|
|
|
|
|
|
|
|
//Create relationship between pivotcacherecord and pivotcachedefinition
|
|
|
|
XSSFPivotCacheRecords pivotCacheRecords = (XSSFPivotCacheRecords) pivotCacheDefinition.
|
|
|
|
createRelationship(XSSFRelation.PIVOT_CACHE_RECORDS, XSSFFactory.getInstance(), tableId);
|
|
|
|
|
|
|
|
//Set relationships id for pivotCacheDefinition to pivotCacheRecords
|
|
|
|
pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().setId(pivotCacheDefinition.getRelationId(pivotCacheRecords));
|
|
|
|
|
|
|
|
wb.setPivotTables(pivotTables);
|
|
|
|
|
|
|
|
return pivotTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-09-20 03:55:13 -04:00
|
|
|
* Create a pivot table using the AreaReference range on sourceSheet, at the given position.
|
|
|
|
* If the source reference contains a sheet name, it must match the sourceSheet
|
|
|
|
* @param source location of pivot data
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
|
|
|
* @param sourceSheet The sheet containing the source data, if the source reference doesn't contain a sheet name
|
|
|
|
* @throws IllegalArgumentException if source references a sheet different than sourceSheet
|
2014-07-30 11:27:09 -04:00
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
@Beta
|
2016-09-20 03:55:13 -04:00
|
|
|
public XSSFPivotTable createPivotTable(final AreaReference source, CellReference position, Sheet sourceSheet) {
|
2016-09-14 18:43:08 -04:00
|
|
|
final String sourceSheetName = source.getFirstCell().getSheetName();
|
|
|
|
if(sourceSheetName != null && !sourceSheetName.equalsIgnoreCase(sourceSheet.getSheetName())) {
|
2014-07-30 11:27:09 -04:00
|
|
|
throw new IllegalArgumentException("The area is referenced in another sheet than the "
|
|
|
|
+ "defined source sheet " + sourceSheet.getSheetName() + ".");
|
|
|
|
}
|
2016-09-20 03:55:13 -04:00
|
|
|
|
|
|
|
return createPivotTable(position, sourceSheet, new PivotTableReferenceConfigurator() {
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
2016-09-20 03:55:13 -04:00
|
|
|
public void configureReference(CTWorksheetSource wsSource) {
|
|
|
|
final String[] firstCell = source.getFirstCell().getCellRefParts();
|
|
|
|
final String firstRow = firstCell[1];
|
|
|
|
final String firstCol = firstCell[2];
|
|
|
|
final String[] lastCell = source.getLastCell().getCellRefParts();
|
|
|
|
final String lastRow = lastCell[1];
|
|
|
|
final String lastCol = lastCell[2];
|
|
|
|
final String ref = firstCol+firstRow+':'+lastCol+lastRow; //or just source.formatAsString()
|
|
|
|
wsSource.setRef(ref);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a pivot table using the AreaReference or named/table range on sourceSheet, at the given position.
|
|
|
|
* If the source reference contains a sheet name, it must match the sourceSheet.
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
|
|
|
* @param sourceSheet The sheet containing the source data, if the source reference doesn't contain a sheet name
|
2016-11-07 16:32:12 -05:00
|
|
|
* @param refConfig A reference to the pivot table configurator
|
2016-09-20 03:55:13 -04:00
|
|
|
* @throws IllegalArgumentException if source references a sheet different than sourceSheet
|
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
private XSSFPivotTable createPivotTable(CellReference position, Sheet sourceSheet, PivotTableReferenceConfigurator refConfig) {
|
|
|
|
|
2014-07-30 11:27:09 -04:00
|
|
|
XSSFPivotTable pivotTable = createPivotTable();
|
|
|
|
//Creates default settings for the pivot table
|
|
|
|
pivotTable.setDefaultPivotTableDefinition();
|
|
|
|
|
|
|
|
//Set sources and references
|
2016-09-20 03:55:13 -04:00
|
|
|
pivotTable.createSourceReferences(position, sourceSheet, refConfig);
|
2014-07-30 11:27:09 -04:00
|
|
|
|
2016-09-20 03:55:13 -04:00
|
|
|
//Create cachefield/s and empty SharedItems - must be after creating references
|
2014-07-30 11:27:09 -04:00
|
|
|
pivotTable.getPivotCacheDefinition().createCacheFields(sourceSheet);
|
|
|
|
pivotTable.createDefaultDataColumns();
|
|
|
|
|
|
|
|
return pivotTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-09-20 03:55:13 -04:00
|
|
|
* Create a pivot table using the AreaReference range, at the given position.
|
|
|
|
* If the source reference contains a sheet name, that sheet is used, otherwise this sheet is assumed as the source sheet.
|
|
|
|
* @param source location of pivot data
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
2014-07-30 11:27:09 -04:00
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public XSSFPivotTable createPivotTable(AreaReference source, CellReference position){
|
2016-09-14 18:43:08 -04:00
|
|
|
final String sourceSheetName = source.getFirstCell().getSheetName();
|
|
|
|
if(sourceSheetName != null && !sourceSheetName.equalsIgnoreCase(this.getSheetName())) {
|
|
|
|
final XSSFSheet sourceSheet = getWorkbook().getSheet(sourceSheetName);
|
|
|
|
return createPivotTable(source, position, sourceSheet);
|
2014-07-30 11:27:09 -04:00
|
|
|
}
|
|
|
|
return createPivotTable(source, position, this);
|
|
|
|
}
|
|
|
|
|
2016-09-20 03:55:13 -04:00
|
|
|
/**
|
|
|
|
* Create a pivot table using the Name range reference on sourceSheet, at the given position.
|
|
|
|
* If the source reference contains a sheet name, it must match the sourceSheet
|
|
|
|
* @param source location of pivot data
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
|
|
|
* @param sourceSheet The sheet containing the source data, if the source reference doesn't contain a sheet name
|
|
|
|
* @throws IllegalArgumentException if source references a sheet different than sourceSheet
|
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public XSSFPivotTable createPivotTable(final Name source, CellReference position, Sheet sourceSheet) {
|
|
|
|
if(source.getSheetName() != null && !source.getSheetName().equals(sourceSheet.getSheetName())) {
|
|
|
|
throw new IllegalArgumentException("The named range references another sheet than the "
|
|
|
|
+ "defined source sheet " + sourceSheet.getSheetName() + ".");
|
|
|
|
}
|
|
|
|
|
|
|
|
return createPivotTable(position, sourceSheet, new PivotTableReferenceConfigurator() {
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
2016-09-20 03:55:13 -04:00
|
|
|
public void configureReference(CTWorksheetSource wsSource) {
|
|
|
|
wsSource.setName(source.getNameName());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a pivot table using the Name range, at the given position.
|
|
|
|
* If the source reference contains a sheet name, that sheet is used, otherwise this sheet is assumed as the source sheet.
|
|
|
|
* @param source location of pivot data
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public XSSFPivotTable createPivotTable(Name source, CellReference position) {
|
|
|
|
return createPivotTable(source, position, getWorkbook().getSheet(source.getSheetName()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a pivot table using the Table, at the given position.
|
|
|
|
* Tables are required to have a sheet reference, so no additional logic around reference sheet is needed.
|
|
|
|
* @param source location of pivot data
|
|
|
|
* @param position A reference to the top left cell where the pivot table will start
|
|
|
|
* @return The pivot table
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public XSSFPivotTable createPivotTable(final Table source, CellReference position) {
|
|
|
|
return createPivotTable(position, getWorkbook().getSheet(source.getSheetName()), new PivotTableReferenceConfigurator() {
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
|
|
|
public void configureReference(CTWorksheetSource wsSource) {
|
2016-09-20 03:55:13 -04:00
|
|
|
wsSource.setName(source.getName());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-07-30 11:27:09 -04:00
|
|
|
/**
|
|
|
|
* Returns all the pivot tables for this Sheet
|
|
|
|
*/
|
|
|
|
@Beta
|
|
|
|
public List<XSSFPivotTable> getPivotTables() {
|
|
|
|
List<XSSFPivotTable> tables = new ArrayList<XSSFPivotTable>();
|
|
|
|
for (XSSFPivotTable table : getWorkbook().getPivotTables()) {
|
|
|
|
if (table.getParent() == this) {
|
|
|
|
tables.add(table);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tables;
|
|
|
|
}
|
2015-01-02 15:03:28 -05:00
|
|
|
|
2017-01-07 19:38:41 -05:00
|
|
|
@Override
|
2015-01-02 15:03:28 -05:00
|
|
|
public int getColumnOutlineLevel(int columnIndex) {
|
|
|
|
CTCol col = columnHelper.getColumn(columnIndex, false);
|
|
|
|
if (col == null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return col.getOutlineLevel();
|
|
|
|
}
|
2016-01-13 12:54:24 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add ignored errors (usually to suppress them in the UI of a consuming
|
|
|
|
* application).
|
|
|
|
*
|
|
|
|
* @param cell Cell.
|
|
|
|
* @param ignoredErrorTypes Types of error to ignore there.
|
|
|
|
*/
|
|
|
|
public void addIgnoredErrors(CellReference cell, IgnoredErrorType... ignoredErrorTypes) {
|
|
|
|
addIgnoredErrors(cell.formatAsString(), ignoredErrorTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ignore errors across a range of cells.
|
|
|
|
*
|
|
|
|
* @param region Range of cells.
|
|
|
|
* @param ignoredErrorTypes Types of error to ignore there.
|
|
|
|
*/
|
|
|
|
public void addIgnoredErrors(CellRangeAddress region, IgnoredErrorType... ignoredErrorTypes) {
|
|
|
|
region.validate(SpreadsheetVersion.EXCEL2007);
|
|
|
|
addIgnoredErrors(region.formatAsString(), ignoredErrorTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the errors currently being ignored and the ranges
|
|
|
|
* where they are ignored.
|
|
|
|
*
|
|
|
|
* @return Map of error type to the range(s) where they are ignored.
|
|
|
|
*/
|
|
|
|
public Map<IgnoredErrorType, Set<CellRangeAddress>> getIgnoredErrors() {
|
|
|
|
Map<IgnoredErrorType, Set<CellRangeAddress>> result = new LinkedHashMap<IgnoredErrorType, Set<CellRangeAddress>>();
|
|
|
|
if (worksheet.isSetIgnoredErrors()) {
|
|
|
|
for (CTIgnoredError err : worksheet.getIgnoredErrors().getIgnoredErrorList()) {
|
2016-02-15 09:58:16 -05:00
|
|
|
for (IgnoredErrorType errType : XSSFIgnoredErrorHelper.getErrorTypes(err)) {
|
2016-01-13 12:54:24 -05:00
|
|
|
if (!result.containsKey(errType)) {
|
|
|
|
result.put(errType, new LinkedHashSet<CellRangeAddress>());
|
|
|
|
}
|
|
|
|
for (Object ref : err.getSqref()) {
|
|
|
|
result.get(errType).add(CellRangeAddress.valueOf(ref.toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void addIgnoredErrors(String ref, IgnoredErrorType... ignoredErrorTypes) {
|
|
|
|
CTIgnoredErrors ctIgnoredErrors = worksheet.isSetIgnoredErrors() ? worksheet.getIgnoredErrors() : worksheet.addNewIgnoredErrors();
|
|
|
|
CTIgnoredError ctIgnoredError = ctIgnoredErrors.addNewIgnoredError();
|
2016-02-15 09:58:16 -05:00
|
|
|
XSSFIgnoredErrorHelper.addIgnoredErrors(ctIgnoredError, ref, ignoredErrorTypes);
|
2016-01-13 12:54:24 -05:00
|
|
|
}
|
2016-12-31 16:50:47 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine the OleObject which links shapes with embedded resources
|
|
|
|
*
|
|
|
|
* @param shapeId the shape id
|
|
|
|
* @return the CTOleObject of the shape
|
|
|
|
*/
|
|
|
|
protected CTOleObject readOleObject(long shapeId) {
|
|
|
|
if (!getCTWorksheet().isSetOleObjects()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we use a XmlCursor here to handle oleObject with-/out AlternateContent wrappers
|
|
|
|
String xquery = "declare namespace p='"+XSSFRelation.NS_SPREADSHEETML+"' .//p:oleObject";
|
|
|
|
XmlCursor cur = getCTWorksheet().getOleObjects().newCursor();
|
|
|
|
try {
|
|
|
|
cur.selectPath(xquery);
|
|
|
|
CTOleObject coo = null;
|
|
|
|
while (cur.toNextSelection()) {
|
|
|
|
String sId = cur.getAttributeText(new QName(null, "shapeId"));
|
|
|
|
if (sId == null || Long.parseLong(sId) != shapeId) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlObject xObj = cur.getObject();
|
|
|
|
if (xObj instanceof CTOleObject) {
|
|
|
|
// the unusual case ...
|
|
|
|
coo = (CTOleObject)xObj;
|
|
|
|
} else {
|
|
|
|
XMLStreamReader reader = cur.newXMLStreamReader();
|
|
|
|
try {
|
|
|
|
CTOleObjects coos = CTOleObjects.Factory.parse(reader);
|
|
|
|
if (coos.sizeOfOleObjectArray() == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
coo = coos.getOleObjectArray(0);
|
|
|
|
} catch (XmlException e) {
|
|
|
|
logger.log(POILogger.INFO, "can't parse CTOleObjects", e);
|
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
reader.close();
|
|
|
|
} catch (XMLStreamException e) {
|
|
|
|
logger.log(POILogger.INFO, "can't close reader", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// there are choice and fallback OleObject ... we prefer the one having the objectPr element,
|
|
|
|
// which is in the choice element
|
|
|
|
if (cur.toChild(XSSFRelation.NS_SPREADSHEETML, "objectPr")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (coo == null) ? null : coo;
|
|
|
|
} finally {
|
|
|
|
cur.dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-01-16 11:08:22 -05:00
|
|
|
}
|