[bug-62055] Fix XSSFImportFromXML table resize. Thanks to Leonard Kappe. This closes #99

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1825315 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2018-02-25 17:06:19 +00:00
parent 02719ac4ba
commit 4ad4d76241
11 changed files with 1053 additions and 302 deletions

View File

@ -75,9 +75,9 @@ public class CreateTable {
} }
} }
// Create the columns // Create the columns
table.addColumn(); table.createColumn("Column 1");
table.addColumn(); table.createColumn("Column 2");
table.addColumn(); table.createColumn("Column 3");
// Set which area the table should be placed in // Set which area the table should be placed in
AreaReference reference = wb.getCreationHelper().createAreaReference( AreaReference reference = wb.getCreationHelper().createAreaReference(

View File

@ -50,9 +50,9 @@ import org.apache.poi.xssf.usermodel.XSSFMap;
import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.XSSFTableColumn;
import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell; import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr; import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
@ -175,35 +175,27 @@ public class XSSFExportToXml implements Comparator<String>{
// Exports elements and attributes mapped with tables // Exports elements and attributes mapped with tables
if (table!=null) { if (table!=null) {
List<CTTableColumn> tableColumns = table.getCTTable().getTableColumns().getTableColumnList(); List<XSSFTableColumn> tableColumns = table.getColumns();
XSSFSheet sheet = table.getXSSFSheet(); XSSFSheet sheet = table.getXSSFSheet();
int startRow = table.getStartCellReference().getRow(); int startRow = table.getStartCellReference().getRow() + table.getHeaderRowCount();
// In mappings created with Microsoft Excel the first row contains the table header and must be skipped
startRow +=1;
int endRow = table.getEndCellReference().getRow(); int endRow = table.getEndCellReference().getRow();
for(int i = startRow; i<= endRow; i++) { for(int i = startRow; i<= endRow; i++) {
XSSFRow row = sheet.getRow(i); XSSFRow row = sheet.getRow(i);
Node tableRootNode = getNodeByXPath(table.getCommonXpath(),doc.getFirstChild(),doc,true); Node tableRootNode = getNodeByXPath(table.getCommonXpath(), doc.getFirstChild(), doc, true);
short startColumnIndex = table.getStartCellReference().getCol(); short startColumnIndex = table.getStartCellReference().getCol();
for (int j = startColumnIndex; j <= table.getEndCellReference().getCol(); j++) { for (XSSFTableColumn tableColumn : tableColumns) {
XSSFCell cell = row.getCell(j); XSSFCell cell = row.getCell(startColumnIndex + tableColumn.getColumnIndex());
if (cell != null) { if (cell != null) {
int tableColumnIndex = j - startColumnIndex; XSSFXmlColumnPr xmlColumnPr = tableColumn.getXmlColumnPr();
if (tableColumnIndex < tableColumns.size()) { if (xmlColumnPr != null) {
CTTableColumn ctTableColumn = tableColumns.get(tableColumnIndex); String localXPath = xmlColumnPr.getLocalXPath();
if (ctTableColumn.getXmlColumnPr() != null) { Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false);
XSSFXmlColumnPr pointer = new XSSFXmlColumnPr(table, ctTableColumn, mapCellOnNode(cell, currentNode);
ctTableColumn.getXmlColumnPr());
String localXPath = pointer.getLocalXPath();
Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false);
mapCellOnNode(cell,currentNode);
}
} }
} }
} }

View File

@ -47,6 +47,7 @@ import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFMap; import org.apache.poi.xssf.usermodel.XSSFMap;
import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.XSSFTableColumn;
import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell; import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr; import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType;
@ -124,25 +125,32 @@ public class XSSFImportFromXML {
String commonXPath = table.getCommonXpath(); String commonXPath = table.getCommonXpath();
NodeList result = (NodeList) xpath.evaluate(commonXPath, doc, XPathConstants.NODESET); NodeList result = (NodeList) xpath.evaluate(commonXPath, doc, XPathConstants.NODESET);
int rowOffset = table.getStartCellReference().getRow() + 1;// the first row contains the table header int rowOffset = table.getStartCellReference().getRow() + table.getHeaderRowCount();
int columnOffset = table.getStartCellReference().getCol() - 1; int columnOffset = table.getStartCellReference().getCol();
table.setDataRowCount(result.getLength());
for (int i = 0; i < result.getLength(); i++) { for (int i = 0; i < result.getLength(); i++) {
// TODO: implement support for denormalized XMLs (see // TODO: implement support for denormalized XMLs (see
// OpenOffice part 4: chapter 3.5.1.7) // OpenOffice part 4: chapter 3.5.1.7)
Node singleNode = result.item(i).cloneNode(true); Node singleNode = result.item(i).cloneNode(true);
for (XSSFXmlColumnPr xmlColumnPr : table.getXmlColumnPrs()) {
for (XSSFTableColumn tableColum : table.getColumns()) {
XSSFXmlColumnPr xmlColumnPr = tableColum.getXmlColumnPr();
if(xmlColumnPr == null) {
continue;
}
int localColumnId = (int) xmlColumnPr.getId();
int rowId = rowOffset + i; int rowId = rowOffset + i;
int columnId = columnOffset + localColumnId; int columnId = columnOffset + tableColum.getColumnIndex();
String localXPath = xmlColumnPr.getLocalXPath(); String localXPath = xmlColumnPr.getLocalXPath();
localXPath = localXPath.substring(localXPath.substring(1).indexOf('/') + 2); localXPath = localXPath.substring(localXPath.indexOf('/', 1) + 1);
// TODO: convert the data to the cell format // TODO: convert the data to the cell format
String value = (String) xpath.evaluate(localXPath, singleNode, XPathConstants.STRING); String value = (String) xpath.evaluate(localXPath, singleNode, XPathConstants.STRING);
logger.log(POILogger.DEBUG, "Extracting with xpath " + localXPath + " : value is '" + value + "'"); logger.log(POILogger.DEBUG, "Extracting with xpath " + localXPath + " : value is '" + value + "'");
XSSFRow row = table.getXSSFSheet().getRow(rowId); XSSFRow row = table.getXSSFSheet().getRow(rowId);
if (row == null) { if (row == null) {
@ -161,6 +169,8 @@ public class XSSFImportFromXML {
} }
} }
private static enum DataType { private static enum DataType {
BOOLEAN(STXmlDataType.BOOLEAN), // BOOLEAN(STXmlDataType.BOOLEAN), //
DOUBLE(STXmlDataType.DOUBLE), // DOUBLE(STXmlDataType.DOUBLE), //

View File

@ -80,6 +80,7 @@ import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import org.apache.poi.util.Removal;
import org.apache.poi.util.Units; import org.apache.poi.util.Units;
import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.usermodel.XSSFPivotTable.PivotTableReferenceConfigurator; import org.apache.poi.xssf.usermodel.XSSFPivotTable.PivotTableReferenceConfigurator;
@ -4057,10 +4058,28 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
} }
/** /**
* Creates a new Table, and associates it with this Sheet * Creates a new Table, and associates it with this Sheet. The table does
* not yet have an area defined and needs to be initialized by calling
* {@link XSSFTable#setArea(AreaReference)}.
*
* @deprecated Use {@link #createTable(AreaReference))} instead
*/ */
@Deprecated
@Removal(version = "4.2.0")
public XSSFTable createTable() { public XSSFTable createTable() {
if(! worksheet.isSetTableParts()) { return createTable(null);
}
/**
* Creates a new Table, and associates it with this Sheet.
*
* @param tableArea
* the area that the table should cover, should not be {@null}
* @return the created table
* @since 4.0.0
*/
public XSSFTable createTable(AreaReference tableArea) {
if (!worksheet.isSetTableParts()) {
worksheet.addNewTableParts(); worksheet.addNewTableParts();
} }
@ -4093,6 +4112,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
tables.put(tbl.getId(), table); tables.put(tbl.getId(), table);
if(tableArea != null) {
table.setArea(tableArea);
}
return table; return table;
} }

View File

@ -24,6 +24,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -32,12 +33,14 @@ import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Table; import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.ss.usermodel.TableStyleInfo; import org.apache.poi.ss.usermodel.TableStyleInfo;
import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.Removal;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr; import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
@ -48,12 +51,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument;
/** /**
* *
* This class implements the Table Part (Open Office XML Part 4: * This class implements the Table Part (Open Office XML Part 4: chapter 3.5.1)
* chapter 3.5.1)
* *
* This implementation works under the assumption that a table contains mappings to a subtree of an XML. * Columns of this table may contains mappings to a subtree of an XML. The root
* The root element of this subtree an occur multiple times (one for each row of the table). The child nodes * element of this subtree can occur multiple times (one for each row of the
* of the root element can be only attributes or element with maxOccurs=1 property set * table). The child nodes of the root element can be only attributes or
* elements with maxOccurs=1 property set.
* *
* *
* @author Roberto Manicardi * @author Roberto Manicardi
@ -61,8 +64,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument;
public class XSSFTable extends POIXMLDocumentPart implements Table { public class XSSFTable extends POIXMLDocumentPart implements Table {
private CTTable ctTable; private CTTable ctTable;
private transient List<XSSFXmlColumnPr> xmlColumnPr; private transient List<XSSFXmlColumnPr> xmlColumnPrs;
private transient CTTableColumn[] ctColumns; private transient List<XSSFTableColumn> tableColumns;
private transient HashMap<String, Integer> columnMap; private transient HashMap<String, Integer> columnMap;
private transient CellReference startCellReference; private transient CellReference startCellReference;
private transient CellReference endCellReference; private transient CellReference endCellReference;
@ -155,18 +158,6 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
return false; return false;
} }
/**
* caches table columns for performance.
* Updated via updateHeaders
* @since 3.15 beta 2
*/
private CTTableColumn[] getTableColumns() {
if (ctColumns == null) {
ctColumns = ctTable.getTableColumns().getTableColumnArray();
}
return ctColumns;
}
/** /**
* *
@ -179,9 +170,9 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
public String getCommonXpath() { public String getCommonXpath() {
if (commonXPath == null) { if (commonXPath == null) {
String[] commonTokens = {}; String[] commonTokens = {};
for (CTTableColumn column : getTableColumns()) { for (XSSFTableColumn column : getColumns()) {
if (column.getXmlColumnPr()!=null) { if (column.getXmlColumnPr()!=null) {
String xpath = column.getXmlColumnPr().getXpath(); String xpath = column.getXmlColumnPr().getXPath();
String[] tokens = xpath.split("/"); String[] tokens = xpath.split("/");
if (commonTokens.length==0) { if (commonTokens.length==0) {
commonTokens = tokens; commonTokens = tokens;
@ -210,45 +201,166 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
return commonXPath; return commonXPath;
} }
/**
* Note this list is static - once read, it does not notice later changes to the underlying column structures
* To clear the cache, call {@link #updateHeaders}
* @return List of XSSFTableColumn
* @since 4.0.0
*/
public List<XSSFTableColumn> getColumns() {
if (tableColumns == null) {
List<XSSFTableColumn> columns = new ArrayList<>();
CTTableColumns ctTableColumns = ctTable.getTableColumns();
if (ctTableColumns != null) {
for (CTTableColumn column : ctTableColumns.getTableColumnList()) {
XSSFTableColumn tableColumn = new XSSFTableColumn(this, column);
columns.add(tableColumn);
}
}
tableColumns = Collections.unmodifiableList(columns);
}
return tableColumns;
}
/** /**
* Note this list is static - once read, it does not notice later changes to the underlying column structures * Note this list is static - once read, it does not notice later changes to the underlying column structures
* To clear the cache, call {@link #updateHeaders} * To clear the cache, call {@link #updateHeaders}
*
* @deprecated Use {@link XSSFTableColumn#getXmlColumnPr()} instead.
*
* @return List of XSSFXmlColumnPr * @return List of XSSFXmlColumnPr
*/ */
@Deprecated
@Removal(version="4.2.0")
public List<XSSFXmlColumnPr> getXmlColumnPrs() { public List<XSSFXmlColumnPr> getXmlColumnPrs() {
if (xmlColumnPr==null) { if (xmlColumnPrs == null) {
xmlColumnPr = new ArrayList<>(); xmlColumnPrs = new ArrayList<>();
for (CTTableColumn column: getTableColumns()) { for (XSSFTableColumn column: getColumns()) {
if (column.getXmlColumnPr()!=null) { XSSFXmlColumnPr xmlColumnPr = column.getXmlColumnPr();
XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this,column,column.getXmlColumnPr()); if (xmlColumnPr != null) {
xmlColumnPr.add(columnPr); xmlColumnPrs.add(xmlColumnPr);
} }
} }
} }
return xmlColumnPr; return xmlColumnPrs;
} }
/** /**
* Adds another column to the table. * Add a new column to the right end of the table.
* *
* Warning - Return type likely to change! * @param columnName
* the unique name of the column, must not be {@code null}
* @return the created table column
* @since 4.0.0
*/ */
@Internal("Return type likely to change") public XSSFTableColumn createColumn(String columnName) {
public void addColumn() { return createColumn(columnName, getColumnCount());
}
/**
* Adds a new column to the table.
*
* @param columnName
* the unique name of the column, or {@code null} for a generated name
* @param columnIndex
* the 0-based position of the column in the table
* @return the created table column
* @throws IllegalArgumentException
* if the column name is not unique or missing or if the column
* can't be created at the given index
* @since 4.0.0
*/
public XSSFTableColumn createColumn(String columnName, int columnIndex) {
int columnCount = getColumnCount();
if(columnIndex < 0 || columnIndex > columnCount) {
throw new IllegalArgumentException("Column index out of bounds");
}
// Ensure we have Table Columns // Ensure we have Table Columns
CTTableColumns columns = ctTable.getTableColumns(); CTTableColumns columns = ctTable.getTableColumns();
if (columns == null) { if (columns == null) {
columns = ctTable.addNewTableColumns(); columns = ctTable.addNewTableColumns();
} }
// Add another Column, and give it a sensible ID // check if name is unique and calculate unique column id
CTTableColumn column = columns.addNewTableColumn(); long nextColumnId = 1;
int num = columns.sizeOfTableColumnArray(); for (XSSFTableColumn tableColumn : getColumns()) {
columns.setCount(num); if (columnName != null && columnName.equalsIgnoreCase(tableColumn.getName())) {
column.setId(num); throw new IllegalArgumentException("Column '" + columnName
+ "' already exists. Column names must be unique per table.");
}
nextColumnId = Math.max(nextColumnId, tableColumn.getId());
}
// Have the Headers updated if possible // Add the new Column
CTTableColumn column = columns.insertNewTableColumn(columnIndex);
columns.setCount(columns.sizeOfTableColumnArray());
column.setId(nextColumnId);
if(columnName != null) {
column.setName(columnName);
} else {
column.setName("Column " + nextColumnId);
}
if (ctTable.getRef() != null) {
// calculate new area
int newColumnCount = columnCount + 1;
CellReference tableStart = getStartCellReference();
CellReference tableEnd = getEndCellReference();
SpreadsheetVersion version = getXSSFSheet().getWorkbook().getSpreadsheetVersion();
CellReference newTableEnd = new CellReference(tableEnd.getRow(),
tableStart.getCol() + newColumnCount - 1);
AreaReference newTableArea = new AreaReference(tableStart, newTableEnd, version);
setCellRef(newTableArea);
}
updateHeaders();
return getColumns().get(columnIndex);
}
/**
* Remove a column from the table.
*
* @param column
* the column to remove
* @since 4.0.0
*/
public void removeColumn(XSSFTableColumn column) {
int columnIndex = getColumns().indexOf(column);
if (columnIndex >= 0) {
ctTable.getTableColumns().removeTableColumn(columnIndex);
updateReferences();
updateHeaders();
}
}
/**
* Remove a column from the table.
*
* @param columnIndex
* the 0-based position of the column in the table
* @throws IllegalArgumentException
* if no column at the index exists or if the table has only a
* single column
* @since 4.0.0
*/
public void removeColumn(int columnIndex) {
if (columnIndex < 0 || columnIndex > getColumnCount() - 1) {
throw new IllegalArgumentException("Column index out of bounds");
}
if(getColumnCount() == 1) {
throw new IllegalArgumentException("Table must have at least one column");
}
CTTableColumns tableColumns = ctTable.getTableColumns();
tableColumns.removeTableColumn(columnIndex);
tableColumns.setCount(tableColumns.getTableColumnList().size());
updateReferences();
updateHeaders(); updateHeaders();
} }
@ -323,20 +435,25 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
} }
/** /**
* @deprecated Use {@link #getColumnCount()} instead.
*
* @return the number of mapped table columns (see Open Office XML Part 4: chapter 3.5.1.4) * @return the number of mapped table columns (see Open Office XML Part 4: chapter 3.5.1.4)
*/ */
@Deprecated
@Removal(version = "4.2.0")
public long getNumberOfMappedColumns() { public long getNumberOfMappedColumns() {
return ctTable.getTableColumns().getCount(); return ctTable.getTableColumns().getCount();
} }
/** /**
* @return The reference for the cells of the table * Get the area reference for the cells which this table covers. The area
* (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) * includes header rows and totals rows.
* *
* Does not track updates to underlying changes to CTTable * Does not track updates to underlying changes to CTTable To synchronize
* To synchronize with changes to the underlying CTTable, * with changes to the underlying CTTable, call {@link #updateReferences()}.
* call {@link #updateReferences()}.
* *
* @return the area of the table
* @see "Open Office XML Part 4: chapter 3.5.1.2, attribute ref"
* @since 3.17 beta 1 * @since 3.17 beta 1
*/ */
public AreaReference getCellReferences() { public AreaReference getCellReferences() {
@ -346,16 +463,35 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
SpreadsheetVersion.EXCEL2007 SpreadsheetVersion.EXCEL2007
); );
} }
/** /**
* Updates the reference for the cells of the table * Set the area reference for the cells which this table covers. The area
* (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) * includes includes header rows and totals rows. Automatically synchronizes
* and synchronizes any changes * any changes by calling {@link #updateHeaders()}.
* @param refs table range
* *
* Note: The area's width should be identical to the amount of columns in
* the table or the table may be invalid. All header rows, totals rows and
* at least one data row must fit inside the area. Updating the area with
* this method does not create or remove any columns and does not change any
* cell values.
*
* @deprecated Use {@link #setTableArea} instead, which will ensure that the
* the amount of columns always matches table area always width.
*
* @see "Open Office XML Part 4: chapter 3.5.1.2, attribute ref"
* @since 3.17 beta 1 * @since 3.17 beta 1
*/ */
@Deprecated
@Removal(version="4.2.0")
public void setCellReferences(AreaReference refs) { public void setCellReferences(AreaReference refs) {
// Strip the Sheet name setCellRef(refs);
}
@Internal
protected void setCellRef(AreaReference refs) {
// Strip the sheet name,
// CTWorksheet.getTableParts defines in which sheet the table is
String ref = refs.formatAsString(); String ref = refs.formatAsString();
if (ref.indexOf('!') != -1) { if (ref.indexOf('!') != -1) {
ref = ref.substring(ref.indexOf('!')+1); ref = ref.substring(ref.indexOf('!')+1);
@ -383,6 +519,87 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
updateHeaders(); updateHeaders();
} }
/**
* Set the area reference for the cells which this table covers. The area
* includes includes header rows and totals rows.
*
* Updating the area with this method will create new column as necessary to
* the right side of the table but will not modify any cell values.
*
* @param refs
* the new area of the table
* @throws IllegalArgumentException
* if the area is {@code null} or not
* @since 4.0.0
*/
public void setArea(AreaReference tableArea) {
if (tableArea == null) {
throw new IllegalArgumentException("AreaReference must not be null");
}
String areaSheetName = tableArea.getFirstCell().getSheetName();
if (areaSheetName != null && !areaSheetName.equals(getXSSFSheet().getSheetName())) {
// TODO to move a table from one sheet to another
// CTWorksheet.getTableParts needs to be updated on both sheets
throw new IllegalArgumentException(
"The AreaReference must not reference a different sheet");
}
int rowCount = (tableArea.getLastCell().getRow() - tableArea.getFirstCell().getRow()) + 1;
int minimumRowCount = 1 + getHeaderRowCount() + getTotalsRowCount();
if (rowCount < minimumRowCount) {
throw new IllegalArgumentException("AreaReference needs at least " + minimumRowCount
+ " rows, to cover at least one data row and all header rows and totals rows");
}
// Strip the sheet name,
// CTWorksheet.getTableParts defines in which sheet the table is
String ref = tableArea.formatAsString();
if (ref.indexOf('!') != -1) {
ref = ref.substring(ref.indexOf('!') + 1);
}
// Update
ctTable.setRef(ref);
if (ctTable.isSetAutoFilter()) {
ctTable.getAutoFilter().setRef(ref);
}
updateReferences();
// add or remove columns on the right side of the table
int columnCount = getColumnCount();
int newColumnCount = (tableArea.getLastCell().getCol() - tableArea.getFirstCell().getCol()) + 1;
if (newColumnCount > columnCount) {
for (int i = columnCount; i < newColumnCount; i++) {
createColumn(null, i);
}
} else if (newColumnCount < columnCount) {
for (int i = columnCount; i > newColumnCount; i--) {
removeColumn(i -1);
}
}
updateHeaders();
}
/**
* Get the area that this table covers.
*
* @return the table's area or {@code null} if the area has not been
* initialized
* @since 4.0.0
*/
public AreaReference getArea() {
String ref = ctTable.getRef();
if (ref != null) {
SpreadsheetVersion version = getXSSFSheet().getWorkbook().getSpreadsheetVersion();
return new AreaReference(ctTable.getRef(), version);
} else {
return null;
}
}
/** /**
* @return The reference for the cell in the top-left part of the table * @return The reference for the cell in the top-left part of the table
* (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) * (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref)
@ -445,12 +662,17 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
/** /**
* @return the total number of rows in the selection. (Note: in this version autofiltering is ignored) * Get the total number of rows in this table, including all
* {@linkplain #getHeaderRowCount() header rows} and all
* {@linkplain #getTotalsRowCount() totals rows}. (Note: in this version
* autofiltering is ignored)
*
* Returns <code>0</code> if the start or end cell references are not set. * Returns <code>0</code> if the start or end cell references are not set.
* *
* Does not track updates to underlying changes to CTTable * Does not track updates to underlying changes to CTTable To synchronize
* To synchronize with changes to the underlying CTTable, * with changes to the underlying CTTable, call {@link #updateReferences()}.
* call {@link #updateReferences()}. *
* @return the total number of rows
*/ */
public int getRowCount() { public int getRowCount() {
CellReference from = getStartCellReference(); CellReference from = getStartCellReference();
@ -462,6 +684,117 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
} }
return rowCount; return rowCount;
} }
/**
* Get the number of data rows in this table. This does not include any
* header rows or totals rows.
*
* Returns <code>0</code> if the start or end cell references are not set.
*
* Does not track updates to underlying changes to CTTable To synchronize
* with changes to the underlying CTTable, call {@link #updateReferences()}.
*
* @return the number of data rows
* @since 4.0.0
*/
public int getDataRowCount() {
CellReference from = getStartCellReference();
CellReference to = getEndCellReference();
int rowCount = 0;
if (from != null && to != null) {
rowCount = (to.getRow() - from.getRow() + 1) - getHeaderRowCount()
- getTotalsRowCount();
}
return rowCount;
}
/**
* Set the number of rows in the data area of the table. This does not
* affect any header rows or totals rows.
*
* If the new row count is less than the current row count, superfluous rows
* will be cleared. If the new row count is greater than the current row
* count, cells below the table will be overwritten by the table.
*
* To resize the table without overwriting cells, use
* {@link #setArea(AreaReference)} instead.
*
* @param newDataRowCount
* new row count for the table
* @throws IllegalArgumentException
* if the row count is less than 1
* @since 4.0.0
*/
public void setDataRowCount(int newDataRowCount) {
if (newDataRowCount < 1) {
throw new IllegalArgumentException("Table must have at least one data row");
}
updateReferences();
int dataRowCount = getDataRowCount();
if (dataRowCount == newDataRowCount) {
return;
}
CellReference tableStart = getStartCellReference();
CellReference tableEnd = getEndCellReference();
SpreadsheetVersion version = getXSSFSheet().getWorkbook().getSpreadsheetVersion();
// calculate new area
int newTotalRowCount = getHeaderRowCount() + newDataRowCount + getTotalsRowCount();
CellReference newTableEnd = new CellReference(tableStart.getRow() + newTotalRowCount - 1,
tableEnd.getCol());
AreaReference newTableArea = new AreaReference(tableStart, newTableEnd, version);
// clear cells
CellReference clearAreaStart;
CellReference clearAreaEnd;
if (newDataRowCount < dataRowCount) {
// table size reduced -
// clear all table cells that are outside of the new area
clearAreaStart = new CellReference(newTableArea.getLastCell().getRow() + 1,
newTableArea.getFirstCell().getCol());
clearAreaEnd = tableEnd;
} else {
// table size increased -
// clear all cells below the table that are inside the new area
clearAreaStart = new CellReference(tableEnd.getRow() + 1,
newTableArea.getFirstCell().getCol());
clearAreaEnd = newTableEnd;
}
AreaReference areaToClear = new AreaReference(clearAreaStart, clearAreaEnd, version);
for (CellReference cellRef : areaToClear.getAllReferencedCells()) {
XSSFRow row = getXSSFSheet().getRow(cellRef.getRow());
if (row != null) {
XSSFCell cell = row.getCell(cellRef.getCol());
if (cell != null) {
cell.setCellType(CellType.BLANK);
cell.setCellStyle(null);
}
}
}
// update table area
setCellRef(newTableArea);
}
/**
* Get the total number of columns in this table.
*
* @return the column count
* @since 4.0.0
*/
public int getColumnCount() {
CTTableColumns tableColumns = ctTable.getTableColumns();
if(tableColumns == null) {
return 0;
}
// Casting to int should be safe here - tables larger than the
// sheet (which holds the actual data of the table) can't exists.
return (int) tableColumns.getCount();
}
/** /**
* Synchronize table headers with cell values in the parent sheet. * Synchronize table headers with cell values in the parent sheet.
@ -479,7 +812,7 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
public void updateHeaders() { public void updateHeaders() {
XSSFSheet sheet = (XSSFSheet)getParent(); XSSFSheet sheet = (XSSFSheet)getParent();
CellReference ref = getStartCellReference(); CellReference ref = getStartCellReference();
if(ref == null) return; if (ref == null) return;
int headerRow = ref.getRow(); int headerRow = ref.getRow();
int firstHeaderColumn = ref.getCol(); int firstHeaderColumn = ref.getCol();
@ -488,18 +821,21 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
if (row != null && row.getCTRow().validate()) { if (row != null && row.getCTRow().validate()) {
int cellnum = firstHeaderColumn; int cellnum = firstHeaderColumn;
for (CTTableColumn col : getCTTable().getTableColumns().getTableColumnList()) { CTTableColumns ctTableColumns = getCTTable().getTableColumns();
XSSFCell cell = row.getCell(cellnum); if(ctTableColumns != null) {
if (cell != null) { for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
col.setName(formatter.formatCellValue(cell)); XSSFCell cell = row.getCell(cellnum);
if (cell != null) {
col.setName(formatter.formatCellValue(cell));
}
cellnum++;
} }
cellnum++;
} }
ctColumns = null;
columnMap = null;
xmlColumnPr = null;
commonXPath = null;
} }
tableColumns = null;
columnMap = null;
xmlColumnPrs = null;
commonXPath = null;
} }
private static String caseInsensitive(String s) { private static String caseInsensitive(String s) {
@ -522,11 +858,11 @@ public class XSSFTable extends POIXMLDocumentPart implements Table {
if (columnHeader == null) return -1; if (columnHeader == null) return -1;
if (columnMap == null) { if (columnMap == null) {
// FIXME: replace with org.apache.commons.collections.map.CaseInsensitiveMap // FIXME: replace with org.apache.commons.collections.map.CaseInsensitiveMap
final int count = getTableColumns().length; final int count = getColumnCount();
columnMap = new HashMap<>(count * 3 / 2); columnMap = new HashMap<>(count * 3 / 2);
int i = 0; int i = 0;
for (CTTableColumn column : getTableColumns()) { for (XSSFTableColumn column : getColumns()) {
String columnName = column.getName(); String columnName = column.getName();
columnMap.put(caseInsensitive(columnName), i); columnMap.put(caseInsensitive(columnName), i);
i++; i++;

View File

@ -0,0 +1,134 @@
/* ====================================================================
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;
import org.apache.poi.util.Internal;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlColumnPr;
/**
* A table column of an {@link XSSFTable}. Use {@link XSSFTable#createColumn} to
* create new table columns.
*
* @author Leonard Kappe
* @since 4.0.0
*/
public class XSSFTableColumn {
private final XSSFTable table;
private final CTTableColumn ctTableColumn;
private XSSFXmlColumnPr xmlColumnPr;
/**
* Create a new table column.
*
* @param table
* the table which contains the column
* @param ctTableColumn
* the table column xmlbean to wrap
* @since 4.0.0
*/
@Internal
protected XSSFTableColumn(XSSFTable table, CTTableColumn ctTableColumn) {
this.table = table;
this.ctTableColumn = ctTableColumn;
}
/**
* Get the table which contains this column
*
* @return the table containing this column
* @since 4.0.0
*/
public XSSFTable getTable() {
return table;
}
/**
* Get the identifier of this column, which is is unique per table.
*
* @return the column id
* @since 4.0.0
*/
public long getId() {
return ctTableColumn.getId();
}
/**
* Set the identifier of this column, which must be unique per table.
*
* It is up to the caller to enforce the uniqueness of the id.
*
* @return the column id
* @since 4.0.0
*/
public void setId(long columnId) {
ctTableColumn.setId(columnId);
}
/**
* Get the name of the column, which is is unique per table.
*
* @return the column name
* @since 4.0.0
*/
public String getName() {
return ctTableColumn.getName();
}
/**
* Get the name of the column, which is is unique per table.
*
* @return the column name
* @since 4.0.0
*/
public void setName(String columnName) {
ctTableColumn.setName(columnName);
}
/**
* Get the XmlColumnPr (XML column properties) if this column has an XML
* mapping.
*
* @return the XmlColumnPr or <code>null</code> if this column has no XML
* mapping
* @since 4.0.0
*/
public XSSFXmlColumnPr getXmlColumnPr() {
if (xmlColumnPr == null) {
CTXmlColumnPr ctXmlColumnPr = ctTableColumn.getXmlColumnPr();
if (ctXmlColumnPr != null) {
xmlColumnPr = new XSSFXmlColumnPr(this, ctXmlColumnPr);
}
}
return xmlColumnPr;
}
/**
* Get the column's position in its table, staring with zero from left to
* right.
*
* @return the column index
* @since 4.0.0
*/
public int getColumnIndex() {
return table.findColumnIndex(getName());
}
}

View File

@ -17,12 +17,14 @@
package org.apache.poi.xssf.usermodel.helpers; package org.apache.poi.xssf.usermodel.helpers;
import org.apache.poi.util.Internal;
import org.apache.poi.util.Removal;
import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFTable;
import org.apache.poi.xssf.usermodel.XSSFTableColumn;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlColumnPr; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlColumnPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType.Enum; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType.Enum;
/** /**
* *
* This class is a wrapper around the CTXmlColumnPr (Open Office XML Part 4: * This class is a wrapper around the CTXmlColumnPr (Open Office XML Part 4:
@ -32,56 +34,85 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType.Enum;
* @author Roberto Manicardi * @author Roberto Manicardi
*/ */
public class XSSFXmlColumnPr { public class XSSFXmlColumnPr {
private XSSFTable table;
private CTTableColumn ctTableColumn;
private CTXmlColumnPr ctXmlColumnPr;
public XSSFXmlColumnPr(XSSFTable table ,CTTableColumn ctTableColum,CTXmlColumnPr ctXmlColumnPr){
this.table = table;
this.ctTableColumn = ctTableColum;
this.ctXmlColumnPr = ctXmlColumnPr;
}
public long getMapId(){
return ctXmlColumnPr.getMapId();
}
public String getXPath(){
return ctXmlColumnPr.getXpath();
}
/**
* (see Open Office XML Part 4: chapter 3.5.1.3)
* @return An integer representing the unique identifier of this column.
*/
public long getId(){
return ctTableColumn.getId();
}
/**
* If the XPath is, for example, /Node1/Node2/Node3 and /Node1/Node2 is the common XPath for the table, the local XPath is /Node3
*
* @return the local XPath
*/
public String getLocalXPath(){
StringBuilder localXPath = new StringBuilder();
int numberOfCommonXPathAxis = table.getCommonXpath().split("/").length-1;
String[] xPathTokens = ctXmlColumnPr.getXpath().split("/");
for(int i=numberOfCommonXPathAxis; i<xPathTokens.length;i++){
localXPath.append("/" +xPathTokens[i]);
}
return localXPath.toString();
}
public Enum getXmlDataType() { private XSSFTable table;
private XSSFTableColumn tableColumn;
return ctXmlColumnPr.getXmlDataType(); private CTXmlColumnPr ctXmlColumnPr;
}
/**
* Create a new XSSFXmlColumnPr (XML column properties) wrapper around a
* CTXmlColumnPr.
*
* @param tableColumn
* table column for which the XML column properties are set
* @param ctXmlColumnPr
* the XML column properties xmlbean to wrap
*/
@Internal
public XSSFXmlColumnPr(XSSFTableColumn tableColumn, CTXmlColumnPr ctXmlColumnPr) {
this.table = tableColumn.getTable();
this.tableColumn = tableColumn;
this.ctXmlColumnPr = ctXmlColumnPr;
}
@Deprecated
@Removal(version="4.2")
public XSSFXmlColumnPr(XSSFTable table, CTTableColumn ctTableColum, CTXmlColumnPr ctXmlColumnPr) {
this.table = table;
this.tableColumn = table.getColumns().get(table.findColumnIndex(ctTableColum.getName()));
this.ctXmlColumnPr = ctXmlColumnPr;
}
/**
* Get the column for which these XML column properties are set.
*
* @return the table column
* @since 4.0.0
*/
public XSSFTableColumn getTableColumn() {
return tableColumn;
}
public long getMapId() {
return ctXmlColumnPr.getMapId();
}
public String getXPath() {
return ctXmlColumnPr.getXpath();
}
/**
* (see Open Office XML Part 4: chapter 3.5.1.3)
*
* @deprecated Use {@link XSSFTableColumn#getId()} instead.
*
* @return An integer representing the unique identifier of this column.
*/
@Deprecated
@Removal(version="4.2")
public long getId() {
return tableColumn.getId();
}
/**
* If the XPath is, for example, /Node1/Node2/Node3 and /Node1/Node2 is the common XPath for the table, the local XPath is /Node3
*
* @return the local XPath
*/
public String getLocalXPath() {
StringBuilder localXPath = new StringBuilder();
int numberOfCommonXPathAxis = table.getCommonXpath().split("/").length-1;
String[] xPathTokens = ctXmlColumnPr.getXpath().split("/");
for (int i = numberOfCommonXPathAxis; i < xPathTokens.length; i++) {
localXPath.append("/" + xPathTokens[i]);
}
return localXPath.toString();
}
public Enum getXmlDataType() {
return ctXmlColumnPr.getXmlDataType();
}
} }

View File

@ -32,6 +32,7 @@ import java.util.Locale;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFMap; import org.apache.poi.xssf.usermodel.XSSFMap;
import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFRow;
@ -41,188 +42,200 @@ import org.junit.Test;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
public class TestXSSFImportFromXML { public class TestXSSFImportFromXML {
@Test
public void testImportFromXML() throws IOException, XPathExpressionException, SAXException{
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings.xlsx")) {
String name = "name";
String teacher = "teacher";
String tutor = "tutor";
String cdl = "cdl";
String duration = "duration";
String topic = "topic";
String project = "project";
String credits = "credits";
String testXML = "<CORSO>" + @Test
"<NOME>" + name + "</NOME>" + public void testImportFromXML() throws IOException, XPathExpressionException, SAXException{
"<DOCENTE>" + teacher + "</DOCENTE>" + try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings.xlsx")) {
"<TUTOR>" + tutor + "</TUTOR>" + String name = "name";
"<CDL>" + cdl + "</CDL>" + String teacher = "teacher";
"<DURATA>" + duration + "</DURATA>" + String tutor = "tutor";
"<ARGOMENTO>" + topic + "</ARGOMENTO>" + String cdl = "cdl";
"<PROGETTO>" + project + "</PROGETTO>" + String duration = "duration";
"<CREDITI>" + credits + "</CREDITI>" + String topic = "topic";
"</CORSO>\u0000"; String project = "project";
String credits = "credits";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("CORSO_mapping"); String testXML = "<CORSO>" +
assertNotNull(map); "<NOME>" + name + "</NOME>" +
XSSFImportFromXML importer = new XSSFImportFromXML(map); "<DOCENTE>" + teacher + "</DOCENTE>" +
"<TUTOR>" + tutor + "</TUTOR>" +
"<CDL>" + cdl + "</CDL>" +
"<DURATA>" + duration + "</DURATA>" +
"<ARGOMENTO>" + topic + "</ARGOMENTO>" +
"<PROGETTO>" + project + "</PROGETTO>" +
"<CREDITI>" + credits + "</CREDITI>" +
"</CORSO>\u0000";
importer.importFromXML(testXML); XSSFMap map = wb.getMapInfo().getXSSFMapByName("CORSO_mapping");
assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
XSSFSheet sheet = wb.getSheetAt(0); importer.importFromXML(testXML);
XSSFRow row = sheet.getRow(0); XSSFSheet sheet = wb.getSheetAt(0);
assertTrue(row.getCell(0).getStringCellValue().equals(name));
assertTrue(row.getCell(1).getStringCellValue().equals(teacher));
assertTrue(row.getCell(2).getStringCellValue().equals(tutor));
assertTrue(row.getCell(3).getStringCellValue().equals(cdl));
assertTrue(row.getCell(4).getStringCellValue().equals(duration));
assertTrue(row.getCell(5).getStringCellValue().equals(topic));
assertTrue(row.getCell(6).getStringCellValue().equals(project));
assertTrue(row.getCell(7).getStringCellValue().equals(credits));
}
}
@Test(timeout=60000)
public void testMultiTable() throws IOException, XPathExpressionException, SAXException{
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx")) {
String cellC6 = "c6";
String cellC7 = "c7";
String cellC8 = "c8";
String cellC9 = "c9";
StringBuilder testXML = new StringBuilder("<ns1:MapInfo xmlns:ns1=\"" + NS_SPREADSHEETML + "\" SelectionNamespaces=\"\">" + XSSFRow row = sheet.getRow(0);
"<ns1:Schema ID=\"" + cellC6 + "\" SchemaRef=\"a\" />" + assertTrue(row.getCell(0).getStringCellValue().equals(name));
"<ns1:Schema ID=\"" + cellC7 + "\" SchemaRef=\"b\" />" + assertTrue(row.getCell(1).getStringCellValue().equals(teacher));
"<ns1:Schema ID=\"" + cellC8 + "\" SchemaRef=\"c\" />" + assertTrue(row.getCell(2).getStringCellValue().equals(tutor));
"<ns1:Schema ID=\"" + cellC9 + "\" SchemaRef=\"d\" />"); assertTrue(row.getCell(3).getStringCellValue().equals(cdl));
assertTrue(row.getCell(4).getStringCellValue().equals(duration));
assertTrue(row.getCell(5).getStringCellValue().equals(topic));
assertTrue(row.getCell(6).getStringCellValue().equals(project));
assertTrue(row.getCell(7).getStringCellValue().equals(credits));
}
}
for (int i = 10; i < 10010; i++) { @Test(timeout=60000)
testXML.append("<ns1:Schema ID=\"c").append(i).append("\" SchemaRef=\"d\" />"); public void testMultiTable() throws IOException, XPathExpressionException, SAXException{
} try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx")) {
String cellC6 = "c6";
String cellC7 = "c7";
String cellC8 = "c8";
String cellC9 = "c9";
testXML.append("<ns1:Map ID=\"1\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "<ns1:Map ID=\"2\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "<ns1:Map ID=\"3\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "</ns1:MapInfo>\u0000"); StringBuilder testXML = new StringBuilder("<ns1:MapInfo xmlns:ns1=\"" + NS_SPREADSHEETML + "\" SelectionNamespaces=\"\">" +
"<ns1:Schema ID=\"" + cellC6 + "\" SchemaRef=\"a\" />" +
"<ns1:Schema ID=\"" + cellC7 + "\" SchemaRef=\"b\" />" +
"<ns1:Schema ID=\"" + cellC8 + "\" SchemaRef=\"c\" />" +
"<ns1:Schema ID=\"" + cellC9 + "\" SchemaRef=\"d\" />");
XSSFMap map = wb.getMapInfo().getXSSFMapByName("MapInfo_mapping"); int cellOffset = 10; // cell C10
assertNotNull(map); for (int i = 0; i < 10000; i++) {
XSSFImportFromXML importer = new XSSFImportFromXML(map); testXML.append("<ns1:Schema ID=\"c").append(i + cellOffset).append("\" SchemaRef=\"d\" />");
}
importer.importFromXML(testXML.toString()); testXML.append("<ns1:Map ID=\"1\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "<ns1:Map ID=\"2\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "<ns1:Map ID=\"3\" Name=\"\" RootElement=\"\" SchemaID=\"\" ShowImportExportValidationErrors=\"\" AutoFit=\"\" Append=\"\" PreserveSortAFLayout=\"\" PreserveFormat=\"\">" + "<ns1:DataBinding DataBindingLoadMode=\"\" />" + "</ns1:Map>" + "</ns1:MapInfo>\u0000");
//Check for Schema element XSSFMap map = wb.getMapInfo().getXSSFMapByName("MapInfo_mapping");
XSSFSheet sheet = wb.getSheetAt(1); assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
assertEquals(cellC6, sheet.getRow(5).getCell(2).getStringCellValue()); importer.importFromXML(testXML.toString());
assertEquals(cellC7, sheet.getRow(6).getCell(2).getStringCellValue());
assertEquals(cellC8, sheet.getRow(7).getCell(2).getStringCellValue());
assertEquals(cellC9, sheet.getRow(8).getCell(2).getStringCellValue());
assertEquals("c5001", sheet.getRow(5000).getCell(2).getStringCellValue());
}
}
//Check for Schema element
@Test XSSFSheet sheet = wb.getSheetAt(1);
public void testSingleAttributeCellWithNamespace() throws IOException, XPathExpressionException, SAXException{
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx")) {
int id = 1;
String displayName = "dispName";
String ref = "19";
int count = 21;
String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>" +
"<ns1:table xmlns:ns1=\"" + NS_SPREADSHEETML + "\" id=\"" + id + "\" displayName=\"" + displayName + "\" ref=\"" + ref + "\">" +
"<ns1:tableColumns count=\"" + count + "\" />" +
"</ns1:table>\u0000";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("table_mapping");
assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
importer.importFromXML(testXML);
//Check for Schema element // check table size (+1 for the header row)
XSSFSheet sheet = wb.getSheetAt(0); assertEquals(3 + 1, wb.getTable("Tabella1").getRowCount());
assertEquals(10004 + 1, wb.getTable("Tabella2").getRowCount());
assertEquals(new Double(id), sheet.getRow(28).getCell(1).getNumericCellValue(), 0); // table1 size was reduced, check that former table cells have been cleared
assertEquals(displayName, sheet.getRow(11).getCell(5).getStringCellValue()); assertEquals(CellType.BLANK, wb.getSheetAt(0).getRow(8).getCell(5).getCellType());
assertEquals(ref, sheet.getRow(14).getCell(7).getStringCellValue());
assertEquals(new Double(count), sheet.getRow(18).getCell(3).getNumericCellValue(), 0);
}
}
@Test
public void testOptionalFields_Bugzilla_55864() throws IOException, XPathExpressionException, SAXException {
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55864.xlsx")) {
String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<PersonInfoRoot>" +
"<PersonData>" +
"<FirstName>Albert</FirstName>" +
"<LastName>Einstein</LastName>" +
"<BirthDate>1879-03-14</BirthDate>" +
"</PersonData>" +
"</PersonInfoRoot>";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("PersonInfoRoot_Map"); // table2 size was increased, check that new table cells have been cleared
assertNotNull(map); assertEquals(CellType.BLANK, sheet.getRow(10).getCell(3).getCellType());
XSSFImportFromXML importer = new XSSFImportFromXML(map);
importer.importFromXML(testXML); assertEquals(cellC6, sheet.getRow(5).getCell(2).getStringCellValue());
assertEquals(cellC7, sheet.getRow(6).getCell(2).getStringCellValue());
assertEquals(cellC8, sheet.getRow(7).getCell(2).getStringCellValue());
assertEquals(cellC9, sheet.getRow(8).getCell(2).getStringCellValue());
assertEquals("c5001", sheet.getRow(5000).getCell(2).getStringCellValue());
}
}
XSSFSheet sheet = wb.getSheetAt(0);
XSSFRow rowHeadings = sheet.getRow(0); @Test
XSSFRow rowData = sheet.getRow(1); public void testSingleAttributeCellWithNamespace() throws IOException, XPathExpressionException, SAXException{
try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMapping-singleattributenamespace.xlsx")) {
int id = 1;
String displayName = "dispName";
String ref = "19";
int count = 21;
assertEquals("FirstName", rowHeadings.getCell(0).getStringCellValue()); String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>" +
assertEquals("Albert", rowData.getCell(0).getStringCellValue()); "<ns1:table xmlns:ns1=\"" + NS_SPREADSHEETML + "\" id=\"" + id + "\" displayName=\"" + displayName + "\" ref=\"" + ref + "\">" +
"<ns1:tableColumns count=\"" + count + "\" />" +
"</ns1:table>\u0000";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("table_mapping");
assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
importer.importFromXML(testXML);
assertEquals("LastName", rowHeadings.getCell(1).getStringCellValue()); //Check for Schema element
assertEquals("Einstein", rowData.getCell(1).getStringCellValue()); XSSFSheet sheet = wb.getSheetAt(0);
assertEquals("BirthDate", rowHeadings.getCell(2).getStringCellValue()); assertEquals(new Double(id), sheet.getRow(28).getCell(1).getNumericCellValue(), 0);
assertEquals("1879-03-14", rowData.getCell(2).getStringCellValue()); assertEquals(displayName, sheet.getRow(11).getCell(5).getStringCellValue());
assertEquals(ref, sheet.getRow(14).getCell(7).getStringCellValue());
assertEquals(new Double(count), sheet.getRow(18).getCell(3).getNumericCellValue(), 0);
}
}
// Value for OptionalRating is declared optional (minOccurs=0) in 55864.xlsx @Test
assertEquals("OptionalRating", rowHeadings.getCell(3).getStringCellValue()); public void testOptionalFields_Bugzilla_55864() throws IOException, XPathExpressionException, SAXException {
assertNull("", rowData.getCell(3)); try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("55864.xlsx")) {
} String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
} "<PersonInfoRoot>" +
"<PersonData>" +
"<FirstName>Albert</FirstName>" +
"<LastName>Einstein</LastName>" +
"<BirthDate>1879-03-14</BirthDate>" +
"</PersonData>" +
"</PersonInfoRoot>";
@Test XSSFMap map = wb.getMapInfo().getXSSFMapByName("PersonInfoRoot_Map");
public void testOptionalFields_Bugzilla_57890() throws IOException, ParseException, XPathExpressionException, SAXException { assertNotNull(map);
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("57890.xlsx"); XSSFImportFromXML importer = new XSSFImportFromXML(map);
String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + "<TestInfoRoot>" importer.importFromXML(testXML);
+ "<TestData>" + "<Int>" + Integer.MIN_VALUE + "</Int>" + "<UnsignedInt>12345</UnsignedInt>"
+ "<double>1.0000123</double>" + "<Date>1991-03-14</Date>" + "</TestData>" + "</TestInfoRoot>";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("TestInfoRoot_Map"); XSSFSheet sheet = wb.getSheetAt(0);
assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
importer.importFromXML(testXML); XSSFRow rowHeadings = sheet.getRow(0);
XSSFRow rowData = sheet.getRow(1);
XSSFSheet sheet = wb.getSheetAt(0); assertEquals("FirstName", rowHeadings.getCell(0).getStringCellValue());
assertEquals("Albert", rowData.getCell(0).getStringCellValue());
XSSFRow rowHeadings = sheet.getRow(0); assertEquals("LastName", rowHeadings.getCell(1).getStringCellValue());
XSSFRow rowData = sheet.getRow(1); assertEquals("Einstein", rowData.getCell(1).getStringCellValue());
assertEquals("Date", rowHeadings.getCell(0).getStringCellValue()); assertEquals("BirthDate", rowHeadings.getCell(2).getStringCellValue());
Date date = new SimpleDateFormat("yyyy-MM-dd", DateFormatSymbols.getInstance(Locale.ROOT)).parse("1991-3-14"); assertEquals("1879-03-14", rowData.getCell(2).getStringCellValue());
assertEquals(date, rowData.getCell(0).getDateCellValue());
// Value for OptionalRating is declared optional (minOccurs=0) in 55864.xlsx
assertEquals("OptionalRating", rowHeadings.getCell(3).getStringCellValue());
assertNull("", rowData.getCell(3));
}
}
@Test
public void testOptionalFields_Bugzilla_57890() throws IOException, ParseException, XPathExpressionException, SAXException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("57890.xlsx");
String testXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + "<TestInfoRoot>"
+ "<TestData>" + "<Int>" + Integer.MIN_VALUE + "</Int>" + "<UnsignedInt>12345</UnsignedInt>"
+ "<double>1.0000123</double>" + "<Date>1991-03-14</Date>" + "</TestData>" + "</TestInfoRoot>";
XSSFMap map = wb.getMapInfo().getXSSFMapByName("TestInfoRoot_Map");
assertNotNull(map);
XSSFImportFromXML importer = new XSSFImportFromXML(map);
importer.importFromXML(testXML);
XSSFSheet sheet = wb.getSheetAt(0);
XSSFRow rowHeadings = sheet.getRow(0);
XSSFRow rowData = sheet.getRow(1);
assertEquals("Date", rowHeadings.getCell(0).getStringCellValue());
Date date = new SimpleDateFormat("yyyy-MM-dd", DateFormatSymbols.getInstance(Locale.ROOT)).parse("1991-3-14");
assertEquals(date, rowData.getCell(0).getDateCellValue());
assertEquals("Amount Int", rowHeadings.getCell(1).getStringCellValue());
assertEquals(new Double(Integer.MIN_VALUE), rowData.getCell(1).getNumericCellValue(), 0);
assertEquals("Amount Double", rowHeadings.getCell(2).getStringCellValue());
assertEquals(1.0000123, rowData.getCell(2).getNumericCellValue(), 0);
assertEquals("Amount UnsignedInt", rowHeadings.getCell(3).getStringCellValue());
assertEquals(new Double(12345), rowData.getCell(3).getNumericCellValue(), 0);
wb.close();
}
assertEquals("Amount Int", rowHeadings.getCell(1).getStringCellValue());
assertEquals(new Double(Integer.MIN_VALUE), rowData.getCell(1).getNumericCellValue(), 0);
assertEquals("Amount Double", rowHeadings.getCell(2).getStringCellValue());
assertEquals(1.0000123, rowData.getCell(2).getNumericCellValue(), 0);
assertEquals("Amount UnsignedInt", rowHeadings.getCell(3).getStringCellValue());
assertEquals(new Double(12345), rowData.getCell(3).getNumericCellValue(), 0);
wb.close();
}
} }

View File

@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
@ -223,6 +224,14 @@ public final class TestXSSFTable {
wb.close(); wb.close();
} }
@Test
public void getColumnCount() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
XSSFTable table = wb.getTable("\\_Prime.1");
assertEquals(3, table.getColumnCount());
wb.close();
}
@Test @Test
public void getAndSetDisplayName() throws IOException { public void getAndSetDisplayName() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx"); XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
@ -291,7 +300,131 @@ public final class TestXSSFTable {
IOUtils.closeQuietly(wb); IOUtils.closeQuietly(wb);
} }
@Test
public void testGetDataRowCount() {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sh = wb.createSheet();
AreaReference tableArea = new AreaReference("B2:B6", wb.getSpreadsheetVersion());
XSSFTable table = sh.createTable(tableArea);
assertEquals(5, table.getRowCount()); // includes column header
assertEquals(4, table.getDataRowCount());
table.setArea(new AreaReference("B2:B7", wb.getSpreadsheetVersion()));
assertEquals(6, table.getRowCount());
assertEquals(5, table.getDataRowCount());
IOUtils.closeQuietly(wb);
}
@Test
public void testSetDataRowCount() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sh = wb.createSheet();
// 1 header row + 1 data row
AreaReference tableArea = new AreaReference("C10:C11", wb.getSpreadsheetVersion());
XSSFTable table = sh.createTable(tableArea);
assertEquals(2, table.getRowCount()); // includes all data and header/footer rows
assertEquals(1, table.getHeaderRowCount());
assertEquals(1, table.getDataRowCount());
assertEquals(0, table.getTotalsRowCount());
table.setDataRowCount(5);
assertEquals(6, table.getRowCount());
assertEquals(1, table.getHeaderRowCount());
assertEquals(5, table.getDataRowCount());
assertEquals(0, table.getTotalsRowCount());
assertEquals("C10:C15", table.getArea().formatAsString());
IOUtils.closeQuietly(wb);
}
@Test
public void testSetArea() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sh = wb.createSheet();
AreaReference tableArea = new AreaReference("B10:D12", wb.getSpreadsheetVersion());
XSSFTable table = sh.createTable(tableArea);
assertEquals(3, table.getColumnCount());
assertEquals(3, table.getRowCount());
// move table without resizing, shouldn't change row or column count
AreaReference tableArea2 = new AreaReference("B11:D13", wb.getSpreadsheetVersion());
table.setArea(tableArea2);
assertEquals(3, table.getColumnCount());
assertEquals(3, table.getRowCount());
// increase size by 1 row and 1 column
AreaReference tableArea3 = new AreaReference("B11:E14", wb.getSpreadsheetVersion());
table.setArea(tableArea3);
assertEquals(4, table.getColumnCount());
assertEquals(4, table.getRowCount());
// reduce size by 2 rows and 2 columns
AreaReference tableArea4 = new AreaReference("C12:D13", wb.getSpreadsheetVersion());
table.setArea(tableArea4);
assertEquals(2, table.getColumnCount());
assertEquals(2, table.getRowCount());
IOUtils.closeQuietly(wb);
}
@Test
public void testCreateColumn() {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sh = wb.createSheet();
AreaReference tableArea = new AreaReference("A2:A3", wb.getSpreadsheetVersion());
XSSFTable table = sh.createTable(tableArea);
assertEquals(1, table.getColumnCount());
assertEquals(2, table.getRowCount());
// add columns
table.createColumn("Column B");
table.createColumn("Column D");
table.createColumn("Column C", 2); // add between B and D
table.updateReferences();
table.updateHeaders();
assertEquals(4, table.getColumnCount());
assertEquals(2, table.getRowCount());
assertEquals("Column 1", table.getColumns().get(0).getName()); // generated name
assertEquals("Column B", table.getColumns().get(1).getName());
assertEquals("Column C", table.getColumns().get(2).getName());
assertEquals("Column D", table.getColumns().get(3).getName());
IOUtils.closeQuietly(wb);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateColumnInvalidIndex() throws IOException {
try (XSSFWorkbook wb = new XSSFWorkbook();) {
XSSFSheet sh = wb.createSheet();
AreaReference tableArea = new AreaReference("D2:D3", wb.getSpreadsheetVersion());
XSSFTable table = sh.createTable(tableArea);
// add columns
table.createColumn("Column 2", 1);
table.createColumn("Column 3", 3); // out of bounds
}
}
@Test @Test
public void testDifferentHeaderTypes() throws IOException { public void testDifferentHeaderTypes() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("TablesWithDifferentHeaders.xlsx"); XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("TablesWithDifferentHeaders.xlsx");
@ -345,13 +478,13 @@ public final class TestXSSFTable {
c5.setCellValue("CD"); c5.setCellValue("CD");
c6.setCellValue("EF"); c6.setCellValue("EF");
// Setting up the CTTable // Setting up the table
XSSFTable t = s.createTable(); XSSFTable t = s.createTable(new AreaReference("A1:C3", wb.getSpreadsheetVersion()));
t.setName("TableTest"); t.setName("TableTest");
t.setDisplayName("CT_Table_Test"); t.setDisplayName("CT_Table_Test");
t.addColumn(); t.createColumn("Column 1");
t.addColumn(); t.createColumn("Column 2");
t.addColumn(); t.createColumn("Column 3");
t.setCellReferences(wb.getCreationHelper().createAreaReference( t.setCellReferences(wb.getCreationHelper().createAreaReference(
new CellReference(c1), new CellReference(c6) new CellReference(c1), new CellReference(c6)
)); ));

View File

@ -0,0 +1,79 @@
/* ====================================================================
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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.util.List;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.junit.Test;
public final class TestXSSFTableColumn {
@Test
public void testGetColumnName() throws IOException {
try (XSSFWorkbook wb = XSSFTestDataSamples
.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx")) {
XSSFTable table = wb.getTable("Tabella2");
List<XSSFTableColumn> tableColumns = table.getColumns();
assertEquals("ID", tableColumns.get(0).getName());
assertEquals("Unmapped Column", tableColumns.get(1).getName());
assertEquals("SchemaRef", tableColumns.get(2).getName());
assertEquals("Namespace", tableColumns.get(3).getName());
}
}
@Test
public void testGetColumnIndex() throws IOException {
try (XSSFWorkbook wb = XSSFTestDataSamples
.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx")) {
XSSFTable table = wb.getTable("Tabella2");
List<XSSFTableColumn> tableColumns = table.getColumns();
assertEquals(0, tableColumns.get(0).getColumnIndex());
assertEquals(1, tableColumns.get(1).getColumnIndex());
assertEquals(2, tableColumns.get(2).getColumnIndex());
assertEquals(3, tableColumns.get(3).getColumnIndex());
}
}
@Test
public void testGetXmlColumnPrs() throws IOException {
try (XSSFWorkbook wb = XSSFTestDataSamples
.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx")) {
XSSFTable table = wb.getTable("Tabella2");
List<XSSFTableColumn> tableColumns = table.getColumns();
assertNotNull(tableColumns.get(0).getXmlColumnPr());
assertNull(tableColumns.get(1).getXmlColumnPr()); // unmapped column
assertNotNull(tableColumns.get(2).getColumnIndex());
assertNotNull(tableColumns.get(3).getColumnIndex());
}
}
}