org.odftoolkit.simple.table.CellRange Maven / Gradle / Ivy
Show all versions of simple-odf Show documentation
/*
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.odftoolkit.simple.table;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.attribute.office.OfficeValueTypeAttribute;
import org.odftoolkit.odfdom.dom.element.table.TableCoveredTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableNamedExpressionsElement;
import org.odftoolkit.odfdom.dom.element.table.TableNamedRangeElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowElement;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.pkg.OdfName;
import org.odftoolkit.odfdom.pkg.OdfXMLFactory;
import org.odftoolkit.simple.Document;
import org.odftoolkit.simple.SpreadsheetDocument;
/**
* CellRange represent a rang of cells that are adjacent with each other
*
* CellRange provides methods to get/set/modify the properties of cell range.
*/
public class CellRange {
private int mnStartRow;
private int mnStartColumn;
private int mnEndRow;
private int mnEndColumn;
private String msCellRangeName;
private Table maOwnerTable;
private boolean mbSpreadsheet;
/**
* Construct the instance of CellRange.
* @param table
* is the container table of this cell range.
* @param startColumn
* is the column index of the first cell in this cell range.
* @param startRow
* is the row index of the first cell in this cell range.
* @param endColumn
* is the column index of the last cell in this cell range.
* @param endRow
* is the row index of the last cell in this cell range.
*/
CellRange(Table table, int startColumn, int startRow, int endColumn, int endRow) {
maOwnerTable = table;
Document doc = (Document) ((OdfFileDom) maOwnerTable.getOdfElement().getOwnerDocument()).getDocument();
if (doc instanceof SpreadsheetDocument) {
mbSpreadsheet = true;
}
//the first cell is the covered cell, then the cell range should be enlarged
//so that it can contains the complete cell
//get the cell cover info
mnStartColumn = startColumn;
mnStartRow = startRow;
mnEndColumn = endColumn;
mnEndRow = endRow;
List coverList = maOwnerTable.getCellCoverInfos(0, 0, endColumn, endRow);
Cell cell;// = maOwnerTable.getOwnerCellByPosition(coverList, nStartColumn, nStartRow);
for (int i = startColumn; i <= endColumn; i++) {
cell = maOwnerTable.getOwnerCellByPosition(coverList, i, startRow);
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
mnStartColumn = Math.min(mnStartColumn, colIndex);
mnStartRow = Math.min(mnStartRow, rowIndex);
mnEndColumn = Math.max(mnEndColumn, colIndex + cell.getColumnSpannedNumber() - 1);
mnEndRow = Math.max(mnEndRow, rowIndex + cell.getRowSpannedNumber() - 1);
}
for (int i = startColumn; i <= endColumn; i++) {
cell = maOwnerTable.getOwnerCellByPosition(coverList, i, endRow);
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
mnStartColumn = Math.min(mnStartColumn, colIndex);
mnStartRow = Math.min(mnStartRow, rowIndex);
mnEndColumn = Math.max(mnEndColumn, colIndex + cell.getColumnSpannedNumber() - 1);
mnEndRow = Math.max(mnEndRow, rowIndex + cell.getRowSpannedNumber() - 1);
}
for (int i = startRow + 1; i < endRow; i++) {
cell = maOwnerTable.getOwnerCellByPosition(coverList, startColumn, i);
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
mnStartColumn = Math.min(mnStartColumn, colIndex);
mnStartRow = Math.min(mnStartRow, rowIndex);
mnEndColumn = Math.max(mnEndColumn, colIndex + cell.getColumnSpannedNumber() - 1);
mnEndRow = Math.max(mnEndRow, rowIndex + cell.getRowSpannedNumber() - 1);
}
for (int i = startRow + 1; i < endRow; i++) {
cell = maOwnerTable.getOwnerCellByPosition(coverList, endColumn, i);
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
mnStartColumn = Math.min(mnStartColumn, colIndex);
mnStartRow = Math.min(mnStartRow, rowIndex);
mnEndColumn = Math.max(mnEndColumn, colIndex + cell.getColumnSpannedNumber() - 1);
mnEndRow = Math.max(mnEndRow, rowIndex + cell.getRowSpannedNumber() - 1);
}
}
/**
* construct the empty cellRange
*/
CellRange() {
}
/**
* Merge the current cell range to one cell
*/
public void merge() {
Cell firstCell = maOwnerTable.getCellByPosition(mnStartColumn, mnStartRow);
//note: after merge, the cell row/column count might be changed
int rowCount = maOwnerTable.getRowCount();
int colCount = maOwnerTable.getColumnCount();
//if the cell range is the whole table, then merge it to a big cell
//as to the spreadsheet document, it should still keep the original cell count,
//rather than merge to a big cell.
if (rowCount == (mnEndRow - mnStartRow + 1)
&& colCount == (mnEndColumn - mnStartColumn + 1)
&& !mbSpreadsheet) {
if (firstCell.getOdfElement() instanceof TableTableCellElement) {
TableTableCellElement firstCellElement = (TableTableCellElement) (firstCell.getOdfElement());
firstCellElement.removeAttributeNS(OdfDocumentNamespace.TABLE.getUri(), "number-columns-spanned");
firstCellElement.removeAttributeNS(OdfDocumentNamespace.TABLE.getUri(), "number-rows-spanned");
firstCellElement.setOfficeValueTypeAttribute(OfficeValueTypeAttribute.Value.STRING.toString());
}
//just copy the text of the other cells to this first cell
for (int i = mnStartRow; i < mnEndRow + 1; i++) {
for (int j = mnStartColumn; j < mnEndColumn + 1; j++) {
Cell cellBase = maOwnerTable.getCellByPosition(j, i);
if (j != mnStartColumn || i != mnStartRow) {
//copy the content of this cell to the first cell
firstCell.appendContentFrom(cellBase);
}
}
}
maOwnerTable.removeRowsByIndex(1, maOwnerTable.getRowCount() - 1);
maOwnerTable.removeColumnsByIndex(1, maOwnerTable.getColumnCount() - 1);
Column firstColumn = maOwnerTable.getColumnByIndex(0);
firstColumn.setWidth(maOwnerTable.getWidth());
mnEndRow = mnStartRow;
mnEndColumn = mnStartColumn;
return;
} //if the cell range covered all the table row, and the merged column > 1
//the merged column can be removed
else if (rowCount == (mnEndRow - mnStartRow + 1)
&& colCount > (mnEndColumn - mnStartColumn + 1)
&& (mnEndColumn - mnStartColumn) > 0) {
//the first cell, set the span attribute
if (firstCell.getOdfElement() instanceof TableTableCellElement) {
TableTableCellElement firstCellElement = (TableTableCellElement) (firstCell.getOdfElement());
firstCellElement.removeAttributeNS(OdfDocumentNamespace.TABLE.getUri(), "number-columns-spanned");
firstCellElement.setTableNumberRowsSpannedAttribute(Integer.valueOf(mnEndRow - mnStartRow + 1));
firstCellElement.setOfficeValueTypeAttribute(OfficeValueTypeAttribute.Value.STRING.toString());
}
//the other cell, copy the content to first cell
//if it is also in the first column of the cell range, set to the covered cell
//other cell not in the first column will be removed when remove the column
for (int i = mnStartRow; i < mnEndRow + 1; i++) {
for (int j = mnStartColumn; j < mnEndColumn + 1; j++) {
Cell cellBase = maOwnerTable.getCellByPosition(j, i);
if (j != mnStartColumn || i != mnStartRow) {
//append content to first cell
firstCell.appendContentFrom(cellBase);
//change the cell in the first column of cell range to covered cell
if ((j == mnStartColumn) && (cellBase.getOdfElement() instanceof TableTableCellElement)) {
//change the normal cell to be the covered cell
TableTableCellElement firstColumnCell = (TableTableCellElement) cellBase.getOdfElement();
TableCoveredTableCellElement coveredCell = (TableCoveredTableCellElement) OdfXMLFactory.newOdfElement(
(OdfFileDom) firstColumnCell.getOwnerDocument(),
OdfName.newName(OdfDocumentNamespace.TABLE, "covered-table-cell"));
Row parentRow = cellBase.getTableRow();
parentRow.getOdfElement().insertBefore(coveredCell, firstColumnCell);
parentRow.getOdfElement().removeChild(firstColumnCell);
}
}
}
}
List widthList = getCellRangeWidthList();
double nCellRangeWidth = widthList.get(widthList.size() - 1) - widthList.get(0);
maOwnerTable.removeColumnsByIndex(mnStartColumn + 1, mnEndColumn - mnStartColumn);
Column firstColumn = maOwnerTable.getColumnByIndex(mnStartColumn);
firstColumn.setWidth(nCellRangeWidth);
mnEndColumn = mnStartColumn;
return;
} //if the cell range covered all the table column, the merged row can be removed
else if (rowCount > (mnEndRow - mnStartRow + 1)
&& colCount == (mnEndColumn - mnStartColumn + 1)
&& (mnEndRow - mnStartRow) > 0) {
//the first cell, set the span attribute
if (firstCell.getOdfElement() instanceof TableTableCellElement) {
TableTableCellElement firstCellElement = (TableTableCellElement) (firstCell.getOdfElement());
firstCellElement.removeAttributeNS(OdfDocumentNamespace.TABLE.getUri(), "number-rows-spanned");
firstCellElement.setTableNumberColumnsSpannedAttribute(Integer.valueOf(mnEndColumn - mnStartColumn + 1));
firstCellElement.setOfficeValueTypeAttribute(OfficeValueTypeAttribute.Value.STRING.toString());
}
//the other cell, copy the content to first cell
//if it is also in the first row of the cell range, set to the covered cell
//other cell not in the first row will be removed when remove the row
for (int i = mnStartRow; i < mnEndRow + 1; i++) {
for (int j = mnStartColumn; j < mnEndColumn + 1; j++) {
Cell cellBase = maOwnerTable.getCellByPosition(j, i);
if (j != mnStartColumn || i != mnStartRow) {
//append content to first cell
firstCell.appendContentFrom(cellBase);
//change the cell in the first row of cell range to covered cell
if ((i == mnStartRow) && (cellBase.getOdfElement() instanceof TableTableCellElement)) {
//change the normal cell to be the covered cell
TableTableCellElement firstRowCell = (TableTableCellElement) cellBase.getOdfElement();
TableCoveredTableCellElement coveredCell = (TableCoveredTableCellElement) OdfXMLFactory.newOdfElement(
(OdfFileDom) firstRowCell.getOwnerDocument(),
OdfName.newName(OdfDocumentNamespace.TABLE, "covered-table-cell"));
Row parentRow = cellBase.getTableRow();
parentRow.getOdfElement().insertBefore(coveredCell, firstRowCell);
parentRow.getOdfElement().removeChild(firstRowCell);
}
}
}
}
maOwnerTable.removeRowsByIndex(mnStartRow + 1, mnEndRow - mnStartRow);
mnEndRow = mnStartRow;
return;
} //don't remove any row/column
else {
//first keep the column and row count in this cell range
//the first cell, set the span attribute
if (firstCell.getOdfElement() instanceof TableTableCellElement) {
//first cell number columns repeated attribute may > 1.
firstCell.splitRepeatedCells();
TableTableCellElement firstCellElement = (TableTableCellElement) (firstCell.getOdfElement());
firstCellElement.setTableNumberColumnsSpannedAttribute(mnEndColumn - mnStartColumn + 1);
firstCellElement.setTableNumberRowsSpannedAttribute(mnEndRow - mnStartRow + 1);
}
//the other cell, set to the covered cell
for (int i = mnStartRow; i < mnEndRow + 1; i++) {
for (int j = mnStartColumn; j < mnEndColumn + 1; j++) {
Cell cellBase = maOwnerTable.getCellByPosition(j, i);
if (j != mnStartColumn || i != mnStartRow) {
if (cellBase.getOdfElement() instanceof TableTableCellElement) {
//change the normal cell to be the covered cell
TableTableCellElement cell = (TableTableCellElement) cellBase.getOdfElement();
TableCoveredTableCellElement coveredCell = (TableCoveredTableCellElement) OdfXMLFactory.newOdfElement(
(OdfFileDom) cell.getOwnerDocument(),
OdfName.newName(OdfDocumentNamespace.TABLE, "covered-table-cell"));
TableTableRowElement parentRowEle = cellBase.getTableRow().getOdfElement();
parentRowEle.insertBefore(coveredCell, cell);
//copy the content of this cell to the first cell
firstCell.appendContentFrom(cellBase);
cellBase.removeContent();
//set the table column repeated attribute
int repeatedNum = cell.getTableNumberColumnsRepeatedAttribute();
int num = (mnEndColumn - j + 1) - repeatedNum;
if (num >= 0) {
if(repeatedNum > 1){
coveredCell.setTableNumberColumnsRepeatedAttribute(repeatedNum);
}
parentRowEle.removeChild(cell);
} else {
int tableNumberColumnsRepeatedValue = mnEndColumn - j + 1;
if(tableNumberColumnsRepeatedValue > 1){
coveredCell.setTableNumberColumnsRepeatedAttribute(tableNumberColumnsRepeatedValue);
}
cell.setTableNumberColumnsRepeatedAttribute(-num);
}
} else if (cellBase.getOdfElement() instanceof TableCoveredTableCellElement) {
try {
//copy the content of this cell to the first cell
firstCell.appendContentFrom(cellBase);
cellBase.removeContent();
} catch (Exception e) {
Logger.getLogger(CellRange.class.getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
}
}
}
}
}
//vector store the x coordinate of each column which reference to the left start point of owner table
//the returned value is all measured with "mm" unit
private List getCellRangeWidthList() {
List list = new ArrayList();
Double length = Double.valueOf(0.0);
for (int i = 0; i < maOwnerTable.getColumnCount() - 1; i++) {
Column col = maOwnerTable.getColumnByIndex(i);
int repeateNum = col.getColumnsRepeatedNumber();
if (repeateNum == 1) {
if (isColumnInCellRange(i)) {
list.add(length);
}
length = Double.valueOf(length + col.getWidth());
} else {
for (int j = 0; j < repeateNum; j++) {
if (isColumnInCellRange(i + j)) {
list.add(length);
length = Double.valueOf(length + col.getWidth());
}
}
i += repeateNum - 1;
}
}
//x coordinate of last column right point
list.add(length);
return list;
}
//vector store the x coordinate of each will split column start point
List getVeticalSplitCellRangeWidthList(int splitNum) {
//get each cell in the cell range(the cell here means the real cell, not the covered cell)
List coverList = maOwnerTable.getCellCoverInfos(mnStartColumn, mnStartRow, mnEndColumn, mnEndRow);
//then get the real(uncovered) cell x coordinate
List tmpList = new ArrayList();
List widthList = getCellRangeWidthList();
for (int i = mnStartColumn; i < mnEndColumn + 1; i++) {
for (int j = mnStartRow; j < mnEndRow + 1; j++) {
if (maOwnerTable.isCoveredCellInOwnerTable(coverList, i, j)) {
continue;
} else {
//the real cell, record the x coordinate of the left point
Double width = widthList.get(i - mnStartColumn);
if (!tmpList.contains(width)) {
tmpList.add(width);
}
}
}
}
//last, reorder the tmpVector and split it to splitNum between each item
Double[] widthArray = (Double[]) tmpList.toArray();
Arrays.sort(widthArray);
List rtnValues = new ArrayList();
double colWidth;
double unitWidth;
rtnValues.add(widthArray[0]);
for (int i = 1; i < widthArray.length; i++) {
colWidth = Double.valueOf(widthArray[i] - widthArray[i - 1]);
unitWidth = colWidth / splitNum;
for (int j = 1; j < splitNum; j++) {
double eachWidth = unitWidth * j + widthArray[i - 1];
rtnValues.add(Double.valueOf(eachWidth));
}
rtnValues.add(widthArray[i]);
}
return rtnValues;
}
/**
* Get the name of the named cell range.
*
* @return the name of the cell range
*/
public String getCellRangeName() {
return msCellRangeName;
}
/**
* Set the name of the current cell range.
*
* @param cellRangeName the name that need to set
*/
public void setCellRangeName(String cellRangeName) {
try {
OdfElement contentRoot = maOwnerTable.mDocument.getContentRoot();
//create name range element
OdfFileDom contentDom = ((OdfFileDom) maOwnerTable.getOdfElement().getOwnerDocument());
TableNamedExpressionsElement nameExpress = (TableNamedExpressionsElement) OdfXMLFactory.newOdfElement(
contentDom,
OdfName.newName(OdfDocumentNamespace.TABLE, "named-expressions"));
String startCellRange = "$" + maOwnerTable.getTableName() + "." + maOwnerTable.getAbsoluteCellAddress(mnStartColumn, mnStartRow);
String endCellRange = "$" + maOwnerTable.getTableName() + "." + maOwnerTable.getAbsoluteCellAddress(mnEndColumn, mnEndRow);
TableNamedRangeElement nameRange = (TableNamedRangeElement) nameExpress.newTableNamedRangeElement(startCellRange + ":" + endCellRange, cellRangeName);
nameRange.setTableBaseCellAddressAttribute(endCellRange);
contentRoot.appendChild(nameExpress);
msCellRangeName = cellRangeName;
} catch (Exception ex) {
Logger.getLogger(CellRange.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Get the Table
instance who contains this cell range.
* @return the table that contains the cell range.
*/
public Table getTable() {
return maOwnerTable;
}
/**
* Get the number of rows in this cell range.
* @return rows number in the cell range
*/
public int getRowNumber() {
return (mnEndRow - mnStartRow + 1);
}
/**
* Get the number of columns in this cell range.
* @return columns number in the cell range
*/
public int getColumnNumber() {
return (mnEndColumn - mnStartColumn + 1);
}
/**
* Returns a single cell that is positioned at specified column and row.
* @param clmIndex the column index of the cell inside the range.
* @param rowIndex the row index of the cell inside the range.
* @return
* the cell at the specified position relative to the start position of the cell range
* @throws IndexOutOfBoundsException if the column/row index is bigger than the column/row count
*/
public Cell getCellByPosition(int clmIndex, int rowIndex) throws IndexOutOfBoundsException {
return maOwnerTable.getCellByPosition(mnStartColumn + clmIndex, mnStartRow + rowIndex);
}
/**
* Check if the given column in is this cell range.
* @param colIndex
* the given column index
* @return true if the given column index is in the current cell range
*
*/
private boolean isColumnInCellRange(int colIndex) {
if (colIndex < mnStartColumn || colIndex > mnEndColumn) {
return false;
} else {
return true;
}
}
/**
* Returns a single cell that is positioned at specified cell address.
*
* @param address
* the cell address of the cell inside the range.
* @return
* the cell at the specified cell address relative to the start position of the cell range
*/
public Cell getCellByPosition(String address) {
//if the address also contain the table name, but the table is not the maOwnerTable
//what should do? get the table then getcellByPosition?
return getCellByPosition(maOwnerTable.getColIndexFromCellAddress(address), maOwnerTable.getRowIndexFromCellAddress(address));
}
}