/*******************************************************************************
*
* Copyright 2010 Alexandru Craciun, and individual contributors as indicated
* by the @authors tag.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
******************************************************************************/
package org.netxilia.api.reference;
import java.util.Iterator;
import org.netxilia.api.model.SheetFullName;
import org.netxilia.api.utils.CollectionUtils;
public final class AreaReference implements Iterable<CellReference> {
public static final AreaReference ALL = new AreaReference(new CellReference(null, 0, 0), new CellReference(null,
CellReference.MAX_ROW_INDEX - 1, CellReference.MAX_COLUMN_INDEX - 1));
private final CellReference topLeft;
private final CellReference bottomRight;
public AreaReference(CellReference oneCell) {
this(oneCell, oneCell);
}
public AreaReference(CellReference topLeft, CellReference bottomRight) {
this.topLeft = topLeft;
this.bottomRight = bottomRight;
checkInfinite();
normalizeCoords();
}
public AreaReference(String reference) {
String[] refs = reference.split(":");
if (refs.length == 1) {// in fact a cell reference (XXX: POI does not allow it. Should we?)
topLeft = new CellReference(refs[0]);
bottomRight = topLeft;
} else if (refs.length == 2) {
topLeft = new CellReference(refs[0]);
// XXX - for 3D reference this is not OK
bottomRight = new CellReference(topLeft.getSheetName(), refs[1]);
} else {
throw new IllegalArgumentException("Cannot parse reference " + reference);
}
checkInfinite();
}
public AreaReference(String sheetName, int firstRow, int firstColumn, int lastRow, int lastColumn) {
this(new CellReference(sheetName, firstRow, firstColumn), new CellReference(sheetName, lastRow, lastColumn));
}
private void checkInfinite() {
if (topLeft.isInfiniteColumn() != bottomRight.isInfiniteColumn()) {
throw new IllegalArgumentException("Cannot mix infinite and not infinite cell references");
}
if (topLeft.isInfiniteRow() != bottomRight.isInfiniteRow()) {
throw new IllegalArgumentException("Cannot mix infinite and not infinite cell references");
}
}
public String getSheetName() {
return topLeft.getSheetName();
}
public String formatAsString() {
StringBuilder sb = new StringBuilder();
sb.append(topLeft.formatAsString());
sb.append(":");
sb.append(bottomRight.formatAsString(false));
return sb.toString();
}
// normalize reference, if necessary (make topLeft be topLeft, and
// bottomRight be bottomRight)
private void normalizeCoords() {
// TODO - fix this using immutable references
// TODO - check the sheetName is the same
// if (topLeft.getColumnIndex() > bottomRight.getColumnIndex()) {
// int tmp = topLeft.getColumnIndex();
// topLeft.setColumnIndex(bottomRight.getColumnIndex());
// bottomRight.setColumnIndex(tmp);
// }
// if (topLeft.getRowIndex() > bottomRight.getRowIndex()) {
// int tmp = topLeft.getRowIndex();
// topLeft.setRowIndex(bottomRight.getRowIndex());
// bottomRight.setRowIndex(tmp);
// }
// deletedTarget = false;
}
// position getters & setters
public CellReference getTopLeft() {
return topLeft;
}
public CellReference getBottomRight() {
return bottomRight;
}
public int getFirstColumnIndex() {
return topLeft.isInfiniteColumn() ? 0 : topLeft.getColumnIndex();
}
public int getLastColumnIndex() {
return bottomRight.getColumnIndex();
}
public int getFirstRowIndex() {
return topLeft.isInfiniteRow() ? 0 : topLeft.getRowIndex();
}
public int getLastRowIndex() {
return bottomRight.getRowIndex();
}
public boolean isOneCell() {
return !isFullRow() && !isFullColumn() && topLeft.getRowIndex() == bottomRight.getRowIndex()
&& topLeft.getColumnIndex() == bottomRight.getColumnIndex();
}
/**
* ex: "3:4"
*
* @return
*/
public boolean isFullRow() {
return topLeft.isInfiniteColumn();
}
/**
* ex: C:D
*
* @return
*/
public boolean isFullColumn() {
return topLeft.isInfiniteRow();
}
public AreaReference withSheetName(String sheetName) {
return new AreaReference(topLeft.withSheetName(sheetName), bottomRight.withSheetName(sheetName));
}
public AreaReference withRelativeSheetName(String baseSheetName) {
if (SheetFullName.ALIAS_MAIN_SHEET.equals(topLeft.getSheetName())) {
String relativeName = SheetFullName.relativeName(baseSheetName, topLeft.getSheetName(), null);
return new AreaReference(topLeft.withSheetName(relativeName), bottomRight.withSheetName(relativeName));
}
return this;
}
public Iterator<CellReference> iterator() {
return iterator(CellReference.MAX_ROW_INDEX, CellReference.MAX_COLUMN_INDEX);
}
public int getRowCount() {
return getLastRowIndex() - getFirstRowIndex() + 1;
}
public int getColumnCount() {
return getLastColumnIndex() - getFirstColumnIndex() + 1;
}
public Range getColumns() {
return Range.range(getFirstColumnIndex(), getLastColumnIndex() + 1);
}
public Range getRows() {
return Range.range(getFirstRowIndex(), getLastRowIndex() + 1);
}
/**
* @param maxRow
* - max row - non inclusive
* @param maxColumn
* - max column - non inclusive
*/
public AreaReference bind(int maxRow, int maxColumn) {
if (getLastColumnIndex() < maxColumn && getLastRowIndex() < maxRow) {
return this;
}
return new AreaReference(getSheetName(), Math.min(getFirstRowIndex(), maxRow - 1), Math.min(
getFirstColumnIndex(), maxColumn - 1), Math.min(getLastRowIndex(), maxRow - 1), Math.min(
getLastColumnIndex(), maxColumn - 1));
}
/**
* Return an iterator on all the cells represented by this area reference that are present in this Sheet. That is,
* if there are references outside the boundaries of the sheet (e.g. areaReference.lastColumnIndex >=
* this.columnCount) they will not appear in the Iterator. The iterator cannot be used to change the sheet. The
* {@link Iterator#remove()} will throw a {@link UnsupportedOperationException}.
*
* The cells are iterated by rows, so first will be returned the cells from the first row of the area, the those of
* second row, and so on.
*
* @param areaReference
* @param maxRow
* - max row - non inclusive
* @param maxColumn
* - max column - non inclusive
* @return the area's cells iterator
*/
public Iterator<CellReference> iterator(int maxRow, int maxColumn) {
return new ReferenceIterator(this, maxRow, maxColumn);
}
public Iterable<CellReference> iterable(int maxRow, int maxColumn) {
return CollectionUtils.iterable(iterator(maxRow, maxColumn));
}
public boolean contains(AreaReference area) {
assert area != null;
return area.getFirstRowIndex() >= getFirstRowIndex() && //
area.getLastRowIndex() <= getLastRowIndex() && //
area.getFirstColumnIndex() >= getFirstColumnIndex() && //
area.getLastColumnIndex() <= getLastColumnIndex();
}
@Override
public String toString() {
return formatAsString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bottomRight == null) ? 0 : bottomRight.hashCode());
result = prime * result + ((topLeft == null) ? 0 : topLeft.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AreaReference other = (AreaReference) obj;
if (bottomRight == null) {
if (other.bottomRight != null) {
return false;
}
} else if (!bottomRight.equals(other.bottomRight)) {
return false;
}
if (topLeft == null) {
if (other.topLeft != null) {
return false;
}
} else if (!topLeft.equals(other.topLeft)) {
return false;
}
return true;
}
public static AreaReference lastRow(int firstColumn, int lastColumn) {
return new AreaReference(new CellReference(CellReference.LAST_ROW_INDEX, firstColumn), new CellReference(
CellReference.LAST_ROW_INDEX, lastColumn));
}
}