From cbb82add8ffc3db815b47212fefacfa4de182a45 Mon Sep 17 00:00:00 2001 From: Javen O'Neal Date: Tue, 7 Nov 2017 00:29:09 +0000 Subject: [PATCH] bug 61730: add iterator interface to CellRangeAddressBase for iterating over CellAddresses git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1814448 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/util/CellRangeAddressBase.java | 99 ++++++++++++++++++- .../poi/ss/util/TestCellRangeAddress.java | 37 ++++++- 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java b/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java index 194c793345..fab893e015 100644 --- a/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java +++ b/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java @@ -17,7 +17,12 @@ package org.apache.poi.ss.util; +import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; import java.util.Set; import org.apache.poi.ss.SpreadsheetVersion; @@ -29,7 +34,7 @@ import org.apache.poi.ss.usermodel.Cell; * * Common superclass of 8-bit and 16-bit versions */ -public abstract class CellRangeAddressBase { +public abstract class CellRangeAddressBase implements Iterable { /** * Indicates a cell or range is in the given relative position in a range. @@ -160,6 +165,20 @@ public abstract class CellRangeAddressBase { return isInRange(ref.getRow(), ref.getCol()); } + /** + * Determines if the given {@link CellAddress} lies within the bounds + * of this range. + *

NOTE: It is up to the caller to ensure the reference is + * for the correct sheet, since this instance doesn't have a sheet reference. + * + * @param ref the CellAddress to check + * @return True if the reference lies within the bounds, false otherwise. + * @see #intersects(CellRangeAddressBase) for checking if two ranges overlap + */ + public boolean isInRange(CellAddress ref) { + return isInRange(ref.getRow(), ref.getColumn()); + } + /** * Determines if the given {@link Cell} lies within the bounds * of this range. @@ -263,11 +282,83 @@ public abstract class CellRangeAddressBase { public int getNumberOfCells() { return (_lastRow - _firstRow + 1) * (_lastCol - _firstCol + 1); } + + public List getCellAddresses(boolean rowMajorOrder) { + List addresses = new ArrayList<>(); + if (rowMajorOrder) { + for (int r = _firstRow; r <= _lastRow; r++) { + for (int c = _firstCol; c <= _lastCol; c++) { + addresses.add(new CellAddress(r, c)); + } + } + } + else { + for (int c = _firstCol; c <= _lastCol; c++) { + for (int r = _firstRow; r <= _lastRow; r++) { + addresses.add(new CellAddress(r, c)); + } + } + } + return Collections.unmodifiableList(addresses); + } + + @Override + public Iterator iterator() { + return new RowMajorCellAddressIterator(this); + } + + /** + * Iterates over the cell addresses in a cell range in row major order + * + * The iterator is unaffected by changes to the CellRangeAddressBase instance + * after the iterator is created. + */ + private static class RowMajorCellAddressIterator implements Iterator { + private final int firstRow, firstCol, lastRow, lastCol; + private int r, c; + + public RowMajorCellAddressIterator(CellRangeAddressBase ref) { + r = firstRow = ref.getFirstRow(); + c = firstCol = ref.getFirstColumn(); + lastRow = ref.getLastRow(); + lastCol = ref.getLastColumn(); + + // whole row and whole column ranges currently not supported + if (firstRow < 0) throw new IllegalStateException("First row cannot be negative."); + if (firstCol < 0) throw new IllegalStateException("First column cannot be negative."); + + // avoid infinite iteration + if (firstRow > lastRow) throw new IllegalStateException("First row cannot be greater than last row."); + if (firstCol > lastCol) throw new IllegalStateException("First column cannot be greater than last column."); + } + + @Override + public boolean hasNext() { + return r <= lastRow && c <= lastCol; + } + + @Override + public CellAddress next() { + if (hasNext()) { + final CellAddress addr = new CellAddress(r, c); + // row major order + if (c < lastCol) { + c++; + } + else { //c >= lastCol, end of row reached + c = firstCol; //CR + r++; //LF + } + return addr; + } + throw new NoSuchElementException(); + } + } @Override - public final String toString() { - CellReference crA = new CellReference(_firstRow, _firstCol); - CellReference crB = new CellReference(_lastRow, _lastCol); + public final String toString() { + CellAddress crA = new CellAddress(_firstRow, _firstCol); + CellAddress crB = new CellAddress(_lastRow, _lastCol); return getClass().getName() + " [" + crA.formatAsString() + ":" + crB.formatAsString() +"]"; } diff --git a/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java b/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java index 557a50df13..3bc1e59980 100644 --- a/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java +++ b/src/testcases/org/apache/poi/ss/util/TestCellRangeAddress.java @@ -20,15 +20,17 @@ package org.apache.poi.ss.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Iterator; +import java.util.NoSuchElementException; import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.util.LittleEndianOutputStream; - import org.junit.Test; public final class TestCellRangeAddress { @@ -263,6 +265,39 @@ public final class TestCellRangeAddress { assertFalse(region.containsColumn(6)); } + @Test + public void iterator() { + final CellRangeAddress A1_B2 = new CellRangeAddress(0, 1, 0, 1); + + // the cell address iterator iterates in row major order + final Iterator iter = A1_B2.iterator(); + assertEquals("A1", new CellAddress(0, 0), iter.next()); + assertEquals("B1", new CellAddress(0, 1), iter.next()); + assertEquals("A2", new CellAddress(1, 0), iter.next()); + assertEquals("B2", new CellAddress(1, 1), iter.next()); + assertFalse(iter.hasNext()); + try { + iter.next(); + fail("Expected NoSuchElementException"); + } catch (final NoSuchElementException e) { + //expected + } + try { + iter.remove(); + fail("Expected UnsupportedOperationException"); + } catch (final UnsupportedOperationException e) { + //expected + } + + // for each interface + int count = 0; + for (final CellAddress addr : A1_B2) { + assertNotNull(addr); + count++; + } + assertEquals(4, count); + } + private static void assertIntersects(CellRangeAddress regionA, CellRangeAddress regionB) { if (!(regionA.intersects(regionB) && regionB.intersects(regionA))) { final String A = regionA.formatAsString(); -- 2.39.5