123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /* ====================================================================
- 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.hssf.record.aggregates;
-
- import java.util.Iterator;
- import java.util.NoSuchElementException;
- import java.util.Spliterator;
- import java.util.Spliterators;
-
- import org.apache.poi.hssf.model.RecordStream;
- import org.apache.poi.hssf.record.BlankRecord;
- import org.apache.poi.hssf.record.CellValueRecordInterface;
- import org.apache.poi.hssf.record.FormulaRecord;
- import org.apache.poi.hssf.record.MulBlankRecord;
- import org.apache.poi.hssf.record.Record;
- import org.apache.poi.hssf.record.RecordBase;
- import org.apache.poi.hssf.record.StringRecord;
- import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
- import org.apache.poi.ss.formula.FormulaShifter;
- import org.apache.poi.ss.formula.ptg.Ptg;
-
- /**
- *
- * Aggregate value records together. Things are easier to handle that way.
- */
- public final class ValueRecordsAggregate implements Iterable<CellValueRecordInterface> {
- private static final int MAX_ROW_INDEX = 0XFFFF;
- private static final int INDEX_NOT_SET = -1;
- private int firstcell = INDEX_NOT_SET;
- private int lastcell = INDEX_NOT_SET;
- private CellValueRecordInterface[][] records;
-
- /** Creates a new instance of ValueRecordsAggregate */
-
- public ValueRecordsAggregate() {
- this(INDEX_NOT_SET, INDEX_NOT_SET, new CellValueRecordInterface[30][]); // We start with 30 Rows.
- }
- private ValueRecordsAggregate(int firstCellIx, int lastCellIx, CellValueRecordInterface[][] pRecords) {
- firstcell = firstCellIx;
- lastcell = lastCellIx;
- records = pRecords;
- }
-
- public void insertCell(CellValueRecordInterface cell) {
- short column = cell.getColumn();
- int row = cell.getRow();
- if (row >= records.length) {
- CellValueRecordInterface[][] oldRecords = records;
- int newSize = oldRecords.length * 2;
- if (newSize < row + 1)
- newSize = row + 1;
- records = new CellValueRecordInterface[newSize][];
- System.arraycopy(oldRecords, 0, records, 0, oldRecords.length);
- }
- CellValueRecordInterface[] rowCells = records[row];
- if (rowCells == null) {
- int newSize = column + 1;
- if (newSize < 10)
- newSize = 10;
- rowCells = new CellValueRecordInterface[newSize];
- records[row] = rowCells;
- }
- if (column >= rowCells.length) {
- CellValueRecordInterface[] oldRowCells = rowCells;
- int newSize = oldRowCells.length * 2;
- if (newSize < column + 1)
- newSize = column + 1;
- // if(newSize>257) newSize=257; // activate?
- rowCells = new CellValueRecordInterface[newSize];
- System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length);
- records[row] = rowCells;
- }
- rowCells[column] = cell;
-
- if (column < firstcell || firstcell == INDEX_NOT_SET) {
- firstcell = column;
- }
- if (column > lastcell || lastcell == INDEX_NOT_SET) {
- lastcell = column;
- }
- }
-
- public void removeCell(CellValueRecordInterface cell) {
- if (cell == null) {
- throw new IllegalArgumentException("cell must not be null");
- }
- int row = cell.getRow();
- if (row >= records.length) {
- throw new RuntimeException("cell row is out of range");
- }
- CellValueRecordInterface[] rowCells = records[row];
- if (rowCells == null) {
- throw new RuntimeException("cell row is already empty");
- }
- short column = cell.getColumn();
- if (column >= rowCells.length) {
- throw new RuntimeException("cell column is out of range");
- }
- rowCells[column] = null;
- }
-
- public void removeAllCellsValuesForRow(int rowIndex) {
- if (rowIndex < 0 || rowIndex > MAX_ROW_INDEX) {
- throw new IllegalArgumentException("Specified rowIndex " + rowIndex
- + " is outside the allowable range (0.." +MAX_ROW_INDEX + ")");
- }
- if (rowIndex >= records.length) {
- // this can happen when the client code has created a row,
- // and then removes/replaces it before adding any cells. (see bug 46312)
- return;
- }
-
- records[rowIndex] = null;
- }
-
-
- public int getPhysicalNumberOfCells() {
- int count = 0;
- for (int r = 0; r < records.length; r++) {
- CellValueRecordInterface[] rowCells = records[r];
- if (rowCells != null) {
- for (int c = 0; c < rowCells.length; c++) {
- if (rowCells[c] != null)
- count++;
- }
- }
- }
- return count;
- }
-
- public int getFirstCellNum() {
- return firstcell;
- }
-
- public int getLastCellNum() {
- return lastcell;
- }
-
- public void addMultipleBlanks(MulBlankRecord mbr) {
- for (int j = 0; j < mbr.getNumColumns(); j++) {
- BlankRecord br = new BlankRecord();
-
- br.setColumn(( short ) (j + mbr.getFirstColumn()));
- br.setRow(mbr.getRow());
- br.setXFIndex(mbr.getXFAt(j));
- insertCell(br);
- }
- }
-
- /**
- * Processes a single cell value record
- * @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet
- */
- public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) {
- if (rec instanceof FormulaRecord) {
- FormulaRecord formulaRec = (FormulaRecord)rec;
- // read optional cached text value
- StringRecord cachedText;
- Class<? extends Record> nextClass = rs.peekNextClass();
- if (nextClass == StringRecord.class) {
- cachedText = (StringRecord) rs.getNext();
- } else {
- cachedText = null;
- }
- insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh));
- } else {
- insertCell(rec);
- }
- }
-
- /** Tallies a count of the size of the cell records
- * that are attached to the rows in the range specified.
- */
- public int getRowCellBlockSize(int startRow, int endRow) {
- int result = 0;
- for(int rowIx=startRow; rowIx<=endRow && rowIx<records.length; rowIx++) {
- result += getRowSerializedSize(records[rowIx]);
- }
- return result;
- }
-
- /** Returns true if the row has cells attached to it */
- public boolean rowHasCells(int row) {
- if (row >= records.length) {
- return false;
- }
- CellValueRecordInterface[] rowCells=records[row];
- if(rowCells==null) return false;
- for(int col=0;col<rowCells.length;col++) {
- if(rowCells[col]!=null) return true;
- }
- return false;
- }
-
- private static int getRowSerializedSize(CellValueRecordInterface[] rowCells) {
- if(rowCells == null) {
- return 0;
- }
- int result = 0;
- for (int i = 0; i < rowCells.length; i++) {
- RecordBase cvr = (RecordBase) rowCells[i];
- if(cvr == null) {
- continue;
- }
- int nBlank = countBlanks(rowCells, i);
- if (nBlank > 1) {
- result += (10 + 2*nBlank);
- i+=nBlank-1;
- } else {
- result += cvr.getRecordSize();
- }
- }
- return result;
- }
-
- public void visitCellsForRow(int rowIndex, RecordVisitor rv) {
-
- CellValueRecordInterface[] rowCells = records[rowIndex];
- if(rowCells == null) {
- throw new IllegalArgumentException("Row [" + rowIndex + "] is empty");
- }
-
-
- for (int i = 0; i < rowCells.length; i++) {
- RecordBase cvr = (RecordBase) rowCells[i];
- if(cvr == null) {
- continue;
- }
- int nBlank = countBlanks(rowCells, i);
- if (nBlank > 1) {
- rv.visitRecord(createMBR(rowCells, i, nBlank));
- i+=nBlank-1;
- } else if (cvr instanceof RecordAggregate) {
- RecordAggregate agg = (RecordAggregate) cvr;
- agg.visitContainedRecords(rv);
- } else {
- rv.visitRecord((org.apache.poi.hssf.record.Record) cvr);
- }
- }
- }
-
- /**
- * @return the number of <em>consecutive</em> {@link BlankRecord}s in the specified row
- * starting from startIx.
- */
- private static int countBlanks(CellValueRecordInterface[] rowCellValues, int startIx) {
- int i = startIx;
- while(i < rowCellValues.length) {
- CellValueRecordInterface cvr = rowCellValues[i];
- if (!(cvr instanceof BlankRecord)) {
- break;
- }
- i++;
- }
- return i - startIx;
- }
-
- private MulBlankRecord createMBR(CellValueRecordInterface[] cellValues, int startIx, int nBlank) {
-
- short[] xfs = new short[nBlank];
- for (int i = 0; i < xfs.length; i++) {
- xfs[i] = cellValues[startIx + i].getXFIndex();
- }
- int rowIx = cellValues[startIx].getRow();
- return new MulBlankRecord(rowIx, startIx, xfs);
- }
-
- public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
- for (int i = 0; i < records.length; i++) {
- CellValueRecordInterface[] rowCells = records[i];
- if (rowCells == null) {
- continue;
- }
- for (int j = 0; j < rowCells.length; j++) {
- CellValueRecordInterface cell = rowCells[j];
- if (cell instanceof FormulaRecordAggregate) {
- FormulaRecordAggregate fra = (FormulaRecordAggregate)cell;
- Ptg[] ptgs = fra.getFormulaTokens(); // needs clone() inside this getter?
- Ptg[] ptgs2 = ((FormulaRecordAggregate)cell).getFormulaRecord().getParsedExpression(); // needs clone() inside this getter?
-
- if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
- fra.setParsedExpression(ptgs);
- }
- }
- }
- }
- }
-
- /**
- * iterator for CellValueRecordInterface
- */
- class ValueIterator implements Iterator<CellValueRecordInterface> {
-
- int curRowIndex, curColIndex = -1;
- int nextRowIndex, nextColIndex = -1;
-
- public ValueIterator() {
- getNextPos();
- }
-
- void getNextPos() {
- if (nextRowIndex >= records.length)
- return; // no next already
-
- while (nextRowIndex < records.length) {
- ++nextColIndex;
- if (records[nextRowIndex] == null || nextColIndex >= records[nextRowIndex].length) {
- ++nextRowIndex;
- nextColIndex = -1;
- continue;
- }
-
- if (records[nextRowIndex][nextColIndex] != null)
- return; // next cell found
- }
- // no next found
- }
-
- public boolean hasNext() {
- return nextRowIndex < records.length;
- }
-
- public CellValueRecordInterface next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- curRowIndex = nextRowIndex;
- curColIndex = nextColIndex;
- final CellValueRecordInterface ret = records[curRowIndex][curColIndex];
- getNextPos();
- return ret;
- }
-
- public void remove() {
- records[curRowIndex][curColIndex] = null;
- }
- }
-
- /** value iterator */
- public Iterator<CellValueRecordInterface> iterator() {
- return new ValueIterator();
- }
-
- /**
- * value spliterator
- *
- * @since POI 5.2.0
- */
- @Override
- public Spliterator<CellValueRecordInterface> spliterator() {
- return Spliterators.spliterator(iterator(), getPhysicalNumberOfCells(), 0);
- }
- }
|