You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ValueRecordsAggregate.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hssf.record.aggregates;
  16. import java.util.Iterator;
  17. import java.util.NoSuchElementException;
  18. import java.util.Spliterator;
  19. import java.util.Spliterators;
  20. import org.apache.poi.hssf.model.RecordStream;
  21. import org.apache.poi.hssf.record.BlankRecord;
  22. import org.apache.poi.hssf.record.CellValueRecordInterface;
  23. import org.apache.poi.hssf.record.FormulaRecord;
  24. import org.apache.poi.hssf.record.MulBlankRecord;
  25. import org.apache.poi.hssf.record.Record;
  26. import org.apache.poi.hssf.record.RecordBase;
  27. import org.apache.poi.hssf.record.StringRecord;
  28. import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
  29. import org.apache.poi.ss.formula.FormulaShifter;
  30. import org.apache.poi.ss.formula.ptg.Ptg;
  31. /**
  32. *
  33. * Aggregate value records together. Things are easier to handle that way.
  34. */
  35. public final class ValueRecordsAggregate implements Iterable<CellValueRecordInterface> {
  36. private static final int MAX_ROW_INDEX = 0XFFFF;
  37. private static final int INDEX_NOT_SET = -1;
  38. private int firstcell = INDEX_NOT_SET;
  39. private int lastcell = INDEX_NOT_SET;
  40. private CellValueRecordInterface[][] records;
  41. /** Creates a new instance of ValueRecordsAggregate */
  42. public ValueRecordsAggregate() {
  43. this(INDEX_NOT_SET, INDEX_NOT_SET, new CellValueRecordInterface[30][]); // We start with 30 Rows.
  44. }
  45. private ValueRecordsAggregate(int firstCellIx, int lastCellIx, CellValueRecordInterface[][] pRecords) {
  46. firstcell = firstCellIx;
  47. lastcell = lastCellIx;
  48. records = pRecords;
  49. }
  50. public void insertCell(CellValueRecordInterface cell) {
  51. short column = cell.getColumn();
  52. int row = cell.getRow();
  53. if (row >= records.length) {
  54. CellValueRecordInterface[][] oldRecords = records;
  55. int newSize = oldRecords.length * 2;
  56. if (newSize < row + 1)
  57. newSize = row + 1;
  58. records = new CellValueRecordInterface[newSize][];
  59. System.arraycopy(oldRecords, 0, records, 0, oldRecords.length);
  60. }
  61. CellValueRecordInterface[] rowCells = records[row];
  62. if (rowCells == null) {
  63. int newSize = column + 1;
  64. if (newSize < 10)
  65. newSize = 10;
  66. rowCells = new CellValueRecordInterface[newSize];
  67. records[row] = rowCells;
  68. }
  69. if (column >= rowCells.length) {
  70. CellValueRecordInterface[] oldRowCells = rowCells;
  71. int newSize = oldRowCells.length * 2;
  72. if (newSize < column + 1)
  73. newSize = column + 1;
  74. // if(newSize>257) newSize=257; // activate?
  75. rowCells = new CellValueRecordInterface[newSize];
  76. System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length);
  77. records[row] = rowCells;
  78. }
  79. rowCells[column] = cell;
  80. if (column < firstcell || firstcell == INDEX_NOT_SET) {
  81. firstcell = column;
  82. }
  83. if (column > lastcell || lastcell == INDEX_NOT_SET) {
  84. lastcell = column;
  85. }
  86. }
  87. public void removeCell(CellValueRecordInterface cell) {
  88. if (cell == null) {
  89. throw new IllegalArgumentException("cell must not be null");
  90. }
  91. int row = cell.getRow();
  92. if (row >= records.length) {
  93. throw new RuntimeException("cell row is out of range");
  94. }
  95. CellValueRecordInterface[] rowCells = records[row];
  96. if (rowCells == null) {
  97. throw new RuntimeException("cell row is already empty");
  98. }
  99. short column = cell.getColumn();
  100. if (column >= rowCells.length) {
  101. throw new RuntimeException("cell column is out of range");
  102. }
  103. rowCells[column] = null;
  104. }
  105. public void removeAllCellsValuesForRow(int rowIndex) {
  106. if (rowIndex < 0 || rowIndex > MAX_ROW_INDEX) {
  107. throw new IllegalArgumentException("Specified rowIndex " + rowIndex
  108. + " is outside the allowable range (0.." +MAX_ROW_INDEX + ")");
  109. }
  110. if (rowIndex >= records.length) {
  111. // this can happen when the client code has created a row,
  112. // and then removes/replaces it before adding any cells. (see bug 46312)
  113. return;
  114. }
  115. records[rowIndex] = null;
  116. }
  117. public int getPhysicalNumberOfCells() {
  118. int count = 0;
  119. for (int r = 0; r < records.length; r++) {
  120. CellValueRecordInterface[] rowCells = records[r];
  121. if (rowCells != null) {
  122. for (int c = 0; c < rowCells.length; c++) {
  123. if (rowCells[c] != null)
  124. count++;
  125. }
  126. }
  127. }
  128. return count;
  129. }
  130. public int getFirstCellNum() {
  131. return firstcell;
  132. }
  133. public int getLastCellNum() {
  134. return lastcell;
  135. }
  136. public void addMultipleBlanks(MulBlankRecord mbr) {
  137. for (int j = 0; j < mbr.getNumColumns(); j++) {
  138. BlankRecord br = new BlankRecord();
  139. br.setColumn(( short ) (j + mbr.getFirstColumn()));
  140. br.setRow(mbr.getRow());
  141. br.setXFIndex(mbr.getXFAt(j));
  142. insertCell(br);
  143. }
  144. }
  145. /**
  146. * Processes a single cell value record
  147. * @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet
  148. */
  149. public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) {
  150. if (rec instanceof FormulaRecord) {
  151. FormulaRecord formulaRec = (FormulaRecord)rec;
  152. // read optional cached text value
  153. StringRecord cachedText;
  154. Class<? extends Record> nextClass = rs.peekNextClass();
  155. if (nextClass == StringRecord.class) {
  156. cachedText = (StringRecord) rs.getNext();
  157. } else {
  158. cachedText = null;
  159. }
  160. insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh));
  161. } else {
  162. insertCell(rec);
  163. }
  164. }
  165. /** Tallies a count of the size of the cell records
  166. * that are attached to the rows in the range specified.
  167. */
  168. public int getRowCellBlockSize(int startRow, int endRow) {
  169. int result = 0;
  170. for(int rowIx=startRow; rowIx<=endRow && rowIx<records.length; rowIx++) {
  171. result += getRowSerializedSize(records[rowIx]);
  172. }
  173. return result;
  174. }
  175. /** Returns true if the row has cells attached to it */
  176. public boolean rowHasCells(int row) {
  177. if (row >= records.length) {
  178. return false;
  179. }
  180. CellValueRecordInterface[] rowCells=records[row];
  181. if(rowCells==null) return false;
  182. for(int col=0;col<rowCells.length;col++) {
  183. if(rowCells[col]!=null) return true;
  184. }
  185. return false;
  186. }
  187. private static int getRowSerializedSize(CellValueRecordInterface[] rowCells) {
  188. if(rowCells == null) {
  189. return 0;
  190. }
  191. int result = 0;
  192. for (int i = 0; i < rowCells.length; i++) {
  193. RecordBase cvr = (RecordBase) rowCells[i];
  194. if(cvr == null) {
  195. continue;
  196. }
  197. int nBlank = countBlanks(rowCells, i);
  198. if (nBlank > 1) {
  199. result += (10 + 2*nBlank);
  200. i+=nBlank-1;
  201. } else {
  202. result += cvr.getRecordSize();
  203. }
  204. }
  205. return result;
  206. }
  207. public void visitCellsForRow(int rowIndex, RecordVisitor rv) {
  208. CellValueRecordInterface[] rowCells = records[rowIndex];
  209. if(rowCells == null) {
  210. throw new IllegalArgumentException("Row [" + rowIndex + "] is empty");
  211. }
  212. for (int i = 0; i < rowCells.length; i++) {
  213. RecordBase cvr = (RecordBase) rowCells[i];
  214. if(cvr == null) {
  215. continue;
  216. }
  217. int nBlank = countBlanks(rowCells, i);
  218. if (nBlank > 1) {
  219. rv.visitRecord(createMBR(rowCells, i, nBlank));
  220. i+=nBlank-1;
  221. } else if (cvr instanceof RecordAggregate) {
  222. RecordAggregate agg = (RecordAggregate) cvr;
  223. agg.visitContainedRecords(rv);
  224. } else {
  225. rv.visitRecord((org.apache.poi.hssf.record.Record) cvr);
  226. }
  227. }
  228. }
  229. /**
  230. * @return the number of <em>consecutive</em> {@link BlankRecord}s in the specified row
  231. * starting from startIx.
  232. */
  233. private static int countBlanks(CellValueRecordInterface[] rowCellValues, int startIx) {
  234. int i = startIx;
  235. while(i < rowCellValues.length) {
  236. CellValueRecordInterface cvr = rowCellValues[i];
  237. if (!(cvr instanceof BlankRecord)) {
  238. break;
  239. }
  240. i++;
  241. }
  242. return i - startIx;
  243. }
  244. private MulBlankRecord createMBR(CellValueRecordInterface[] cellValues, int startIx, int nBlank) {
  245. short[] xfs = new short[nBlank];
  246. for (int i = 0; i < xfs.length; i++) {
  247. xfs[i] = cellValues[startIx + i].getXFIndex();
  248. }
  249. int rowIx = cellValues[startIx].getRow();
  250. return new MulBlankRecord(rowIx, startIx, xfs);
  251. }
  252. public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
  253. for (int i = 0; i < records.length; i++) {
  254. CellValueRecordInterface[] rowCells = records[i];
  255. if (rowCells == null) {
  256. continue;
  257. }
  258. for (int j = 0; j < rowCells.length; j++) {
  259. CellValueRecordInterface cell = rowCells[j];
  260. if (cell instanceof FormulaRecordAggregate) {
  261. FormulaRecordAggregate fra = (FormulaRecordAggregate)cell;
  262. Ptg[] ptgs = fra.getFormulaTokens(); // needs clone() inside this getter?
  263. Ptg[] ptgs2 = ((FormulaRecordAggregate)cell).getFormulaRecord().getParsedExpression(); // needs clone() inside this getter?
  264. if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
  265. fra.setParsedExpression(ptgs);
  266. }
  267. }
  268. }
  269. }
  270. }
  271. /**
  272. * iterator for CellValueRecordInterface
  273. */
  274. class ValueIterator implements Iterator<CellValueRecordInterface> {
  275. int curRowIndex, curColIndex = -1;
  276. int nextRowIndex, nextColIndex = -1;
  277. public ValueIterator() {
  278. getNextPos();
  279. }
  280. void getNextPos() {
  281. if (nextRowIndex >= records.length)
  282. return; // no next already
  283. while (nextRowIndex < records.length) {
  284. ++nextColIndex;
  285. if (records[nextRowIndex] == null || nextColIndex >= records[nextRowIndex].length) {
  286. ++nextRowIndex;
  287. nextColIndex = -1;
  288. continue;
  289. }
  290. if (records[nextRowIndex][nextColIndex] != null)
  291. return; // next cell found
  292. }
  293. // no next found
  294. }
  295. public boolean hasNext() {
  296. return nextRowIndex < records.length;
  297. }
  298. public CellValueRecordInterface next() {
  299. if (!hasNext()) {
  300. throw new NoSuchElementException();
  301. }
  302. curRowIndex = nextRowIndex;
  303. curColIndex = nextColIndex;
  304. final CellValueRecordInterface ret = records[curRowIndex][curColIndex];
  305. getNextPos();
  306. return ret;
  307. }
  308. public void remove() {
  309. records[curRowIndex][curColIndex] = null;
  310. }
  311. }
  312. /** value iterator */
  313. public Iterator<CellValueRecordInterface> iterator() {
  314. return new ValueIterator();
  315. }
  316. /**
  317. * value spliterator
  318. *
  319. * @since POI 5.2.0
  320. */
  321. @Override
  322. public Spliterator<CellValueRecordInterface> spliterator() {
  323. return Spliterators.spliterator(iterator(), getPhysicalNumberOfCells(), 0);
  324. }
  325. }