Dépôt officiel du code source de l'ERP OpenConcerto
Rev 25 | Rev 156 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
/*
* Row created on 10 décembre 2005
*/
package org.openconcerto.openoffice.spreadsheet;
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.openoffice.StyleStyleDesc;
import org.openconcerto.openoffice.XMLVersion;
import java.util.Arrays;
import java.util.List;
import org.jdom.Element;
/**
* A row in a Calc document. This class will only break "repeated" attributes on demand (eg for
* setting a value).
*
* @author Sylvain
* @param <D> type of document
*/
public class Row<D extends ODDocument> extends TableCalcNode<RowStyle, D> {
static Element createEmpty(XMLVersion ns) {
return new Element("table-row", ns.getTABLE());
}
private final Table<D> parent;
private final int index;
private int repeated;
// the same immutable cell instance is repeated, but each MutableCell is only once
// ATTN MutableCell have their index as attribute
// array is faster than List
private Cell<D>[] cells;
private int cellCount;
Row(Table<D> parent, Element tableRowElem, int index, StyleDesc<RowStyle> styleDesc, StyleDesc<CellStyle> cellStyleDesc) {
super(parent.getODDocument(), tableRowElem, styleDesc);
this.parent = parent;
this.index = index;
this.repeated = Axis.ROW.getRepeated(getElement());
@SuppressWarnings("unchecked")
final Cell<D>[] unsafe = new Cell[parent.getColumnCount()];
this.cells = unsafe;
this.cellCount = 0;
for (final Element cellElem : this.getCellElements()) {
addCellElem(cellElem, cellStyleDesc);
}
}
private final void ensureRoom(int additionalItems) {
final int requiredSize = this.getCellCount() + additionalItems;
if (requiredSize > this.cells.length) {
this.cells = Arrays.copyOf(this.cells, requiredSize);
}
}
protected final Table<D> getSheet() {
return this.parent;
}
// ATTN index of the first row
final int getY() {
return this.index;
}
// inclusive
final int getLastY() {
return this.getY() + this.getRepeated() - 1;
}
final int getRepeated() {
return this.repeated;
}
final void setRepeated(int newRepeated) {
Axis.ROW.setRepeated(getElement(), newRepeated);
this.repeated = newRepeated;
}
// plain Cell instances have multiple indexes (if repeated) but MutableCell are unique
final int getX(MutableCell<D> c) {
final int stop = this.getCellCount();
for (int i = 0; i < stop; i++) {
final Cell<D> item = this.cells[i];
if (c.equals(item))
return i;
}
return -1;
}
private void addCellElem(final Element cellElem, StyleDesc<CellStyle> cellStyleDesc) {
final Cell<D> cell = new Cell<D>(this, cellElem, cellStyleDesc);
final String repeatedS = cellElem.getAttributeValue("number-columns-repeated", this.getTABLE());
final int toRepeat = StyleProperties.parseInt(repeatedS, 1);
final int stop = this.cellCount + toRepeat;
for (int i = this.cellCount; i < stop; i++) {
this.cells[i] = cell;
}
this.cellCount = stop;
}
/**
* All cells of this row.
*
* @return cells of this row, only "table-cell" and "covered-table-cell".
*/
@SuppressWarnings("unchecked")
private List<Element> getCellElements() {
// seuls table-cell et covered-table-cell sont légaux
return this.getElement().getChildren();
}
protected final int getCellCount() {
return this.cellCount;
}
private final List<Cell<D>> getCellsAsList() {
return Arrays.asList(this.cells);
}
protected final Cell<D> getCellAt(int col) {
return this.cells[col];
}
protected final Cell<D> getValidCellAt(int col) {
final Cell<D> c = this.getCellAt(col);
if (!c.isValid())
throw new IllegalArgumentException("invalid cell " + c);
return c;
}
public final MutableCell<D> getMutableCellAt(final int col) {
final Cell<D> c = this.getValidCellAt(col);
if (!(c instanceof MutableCell)) {
RepeatedBreaker.<D> getCellBreaker().breakRepeated(this, getCellsAsList(), col);
}
return (MutableCell<D>) this.getValidCellAt(col);
}
// rempli cette ligne avec autant de cellules vides qu'il faut
void columnCountChanged(StyleStyleDesc<CellStyle> cellStyleDesc) {
final int diff = this.getSheet().getColumnCount() - getCellCount();
if (diff < 0) {
throw new IllegalStateException("should have used Table.removeColumn()");
} else if (diff > 0) {
final Element e = Cell.createEmpty(this.getSheet().getODDocument().getVersion(), diff);
this.getElement().addContent(e);
this.ensureRoom(diff);
addCellElem(e, cellStyleDesc);
}
assert this.getCellCount() == this.getSheet().getColumnCount();
}
void checkRemove(int firstIndex, int lastIndexExcl) {
if (lastIndexExcl > getCellCount()) {
throw new IndexOutOfBoundsException(lastIndexExcl + " > " + getCellCount());
}
if (!this.getCellAt(firstIndex).isValid())
throw new IllegalArgumentException("unable to remove covered cell at " + firstIndex);
}
// ATTN unsafe, must call checkRemove() first
void removeCells(int firstIndex, int lastIndexExcl) {
this.getMutableCellAt(firstIndex).unmerge();
// if lastIndex == size, nothing to do
if (lastIndexExcl < getCellCount()) {
if (!this.getCellAt(lastIndexExcl - 1).isValid()) {
int currentCol = lastIndexExcl - 2;
// the covering cell is on this row since last cells of previous rows have been
// unmerged (see *)
// we've just unmerged firstIndex so there must be a non covered cell before it
while (!this.getCellAt(currentCol).isValid())
currentCol--;
this.getMutableCellAt(currentCol).unmerge();
}
// * lastIndex-1 is now uncovered, we can now unmerge it to assure our following rows
// that any covered cell they encounter is on them and not above
// plus of course if the last cell removed is covering following columns
this.getMutableCellAt(lastIndexExcl - 1).unmerge();
}
for (int i = firstIndex; i < lastIndexExcl; i++) {
// ok to detach multiple times the same element (since repeated cells share the same XML
// element)
this.cells[i].getElement().detach();
}
final int movedCount = getCellCount() - lastIndexExcl;
System.arraycopy(this.cells, lastIndexExcl, this.cells, firstIndex, movedCount);
this.cells = Arrays.copyOfRange(this.cells, 0, firstIndex + movedCount);
this.cellCount = this.cells.length;
assert this.getCellCount() == this.getSheet().getColumnCount();
}
}