001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hssf.record.aggregates;
019:
020: import org.apache.poi.hssf.record.*;
021:
022: import java.util.Iterator;
023: import java.util.List;
024:
025: /**
026: *
027: * Aggregate value records together. Things are easier to handle that way.
028: *
029: * @author andy
030: * @author Glen Stampoultzis (glens at apache.org)
031: * @author Jason Height (jheight at chariot dot net dot au)
032: */
033:
034: public class ValueRecordsAggregate extends Record {
035: public final static short sid = -1000;
036: int firstcell = -1;
037: int lastcell = -1;
038: CellValueRecordInterface[][] records;
039:
040: /** Creates a new instance of ValueRecordsAggregate */
041:
042: public ValueRecordsAggregate() {
043: records = new CellValueRecordInterface[30][]; // We start with 30 Rows.
044: }
045:
046: public void insertCell(CellValueRecordInterface cell) {
047: short column = cell.getColumn();
048: int row = cell.getRow();
049: if (row >= records.length) {
050: CellValueRecordInterface[][] oldRecords = records;
051: int newSize = oldRecords.length * 2;
052: if (newSize < row + 1)
053: newSize = row + 1;
054: records = new CellValueRecordInterface[newSize][];
055: System.arraycopy(oldRecords, 0, records, 0,
056: oldRecords.length);
057: }
058: CellValueRecordInterface[] rowCells = records[row];
059: if (rowCells == null) {
060: int newSize = column + 1;
061: if (newSize < 10)
062: newSize = 10;
063: rowCells = new CellValueRecordInterface[newSize];
064: records[row] = rowCells;
065: }
066: if (column >= rowCells.length) {
067: CellValueRecordInterface[] oldRowCells = rowCells;
068: int newSize = oldRowCells.length * 2;
069: if (newSize < column + 1)
070: newSize = column + 1;
071: // if(newSize>257) newSize=257; // activate?
072: rowCells = new CellValueRecordInterface[newSize];
073: System.arraycopy(oldRowCells, 0, rowCells, 0,
074: oldRowCells.length);
075: records[row] = rowCells;
076: }
077: rowCells[column] = cell;
078:
079: if ((column < firstcell) || (firstcell == -1)) {
080: firstcell = column;
081: }
082: if ((column > lastcell) || (lastcell == -1)) {
083: lastcell = column;
084: }
085: }
086:
087: public void removeCell(CellValueRecordInterface cell) {
088: if (cell != null) {
089: short column = cell.getColumn();
090: int row = cell.getRow();
091: if (row >= records.length)
092: return;
093: CellValueRecordInterface[] rowCells = records[row];
094: if (rowCells == null)
095: return;
096: if (column >= rowCells.length)
097: return;
098: rowCells[column] = null;
099: }
100: }
101:
102: public int getPhysicalNumberOfCells() {
103: int count = 0;
104: for (int r = 0; r < records.length; r++) {
105: CellValueRecordInterface[] rowCells = records[r];
106: if (rowCells != null)
107: for (short c = 0; c < rowCells.length; c++) {
108: if (rowCells[c] != null)
109: count++;
110: }
111: }
112: return count;
113: }
114:
115: public int getFirstCellNum() {
116: return firstcell;
117: }
118:
119: public int getLastCellNum() {
120: return lastcell;
121: }
122:
123: public int construct(int offset, List records) {
124: int k = 0;
125:
126: FormulaRecordAggregate lastFormulaAggregate = null;
127:
128: List sharedFormulas = new java.util.ArrayList();
129:
130: for (k = offset; k < records.size(); k++) {
131: Record rec = (Record) records.get(k);
132:
133: if (rec instanceof StringRecord == false
134: && !rec.isInValueSection()
135: && !(rec instanceof UnknownRecord)) {
136: break;
137: } else if (rec instanceof SharedFormulaRecord) {
138: sharedFormulas.add(rec);
139: } else if (rec instanceof FormulaRecord) {
140: FormulaRecord formula = (FormulaRecord) rec;
141: if (formula.isSharedFormula()) {
142: Record nextRecord = (Record) records.get(k + 1);
143: if (nextRecord instanceof SharedFormulaRecord) {
144: sharedFormulas.add(nextRecord);
145: k++;
146: }
147: //traverse the list of shared formulas in reverse order, and try to find the correct one
148: //for us
149: boolean found = false;
150: for (int i = sharedFormulas.size() - 1; i >= 0; i--) {
151: SharedFormulaRecord shrd = (SharedFormulaRecord) sharedFormulas
152: .get(i);
153: if (shrd.isFormulaInShared(formula)) {
154: shrd.convertSharedFormulaRecord(formula);
155: found = true;
156: break;
157: }
158: }
159: if (!found) {
160: //Sometimes the shared formula flag "seems" to be errornously set,
161: //cant really do much about that.
162: //throw new RecordFormatException("Could not find appropriate shared formula");
163: }
164: }
165:
166: lastFormulaAggregate = new FormulaRecordAggregate(
167: (FormulaRecord) rec, null);
168: insertCell(lastFormulaAggregate);
169: } else if (rec instanceof StringRecord) {
170: lastFormulaAggregate
171: .setStringRecord((StringRecord) rec);
172: } else if (rec.isValue()) {
173: insertCell((CellValueRecordInterface) rec);
174: }
175: }
176: return k;
177: }
178:
179: /**
180: * called by the class that is responsible for writing this sucker.
181: * Subclasses should implement this so that their data is passed back in a
182: * byte array.
183: *
184: * @param offset to begin writing at
185: * @param data byte array containing instance data
186: * @return number of bytes written
187: */
188:
189: public int serialize(int offset, byte[] data) {
190: throw new RuntimeException(
191: "This method shouldnt be called. ValueRecordsAggregate.serializeCellRow() should be called from RowRecordsAggregate.");
192: }
193:
194: /** Tallies a count of the size of the cell records
195: * that are attached to the rows in the range specified.
196: */
197: public int getRowCellBlockSize(int startRow, int endRow) {
198: MyIterator itr = new MyIterator(startRow, endRow);
199: int size = 0;
200: while (itr.hasNext()) {
201: CellValueRecordInterface cell = (CellValueRecordInterface) itr
202: .next();
203: int row = cell.getRow();
204: if (row > endRow)
205: break;
206: if ((row >= startRow) && (row <= endRow))
207: size += ((Record) cell).getRecordSize();
208: }
209: return size;
210: }
211:
212: /** Returns true if the row has cells attached to it */
213: public boolean rowHasCells(int row) {
214: if (row > records.length - 1) //previously this said row > records.length which means if
215: return false; // if records.length == 60 and I pass "60" here I get array out of bounds
216: CellValueRecordInterface[] rowCells = records[row]; //because a 60 length array has the last index = 59
217: if (rowCells == null)
218: return false;
219: for (int col = 0; col < rowCells.length; col++) {
220: if (rowCells[col] != null)
221: return true;
222: }
223: return false;
224: }
225:
226: /** Serializes the cells that are allocated to a certain row range*/
227: public int serializeCellRow(final int row, int offset, byte[] data) {
228: MyIterator itr = new MyIterator(row, row);
229: int pos = offset;
230:
231: while (itr.hasNext()) {
232: CellValueRecordInterface cell = (CellValueRecordInterface) itr
233: .next();
234: if (cell.getRow() != row)
235: break;
236: pos += ((Record) cell).serialize(pos, data);
237: }
238: return pos - offset;
239: }
240:
241: /**
242: * You never fill an aggregate
243: */
244: protected void fillFields(RecordInputStream in) {
245: }
246:
247: /**
248: * called by constructor, should throw runtime exception in the event of a
249: * record passed with a differing ID.
250: *
251: * @param id alleged id for this record
252: */
253:
254: protected void validateSid(short id) {
255: }
256:
257: /**
258: * return the non static version of the id for this record.
259: */
260:
261: public short getSid() {
262: return sid;
263: }
264:
265: public int getRecordSize() {
266:
267: int size = 0;
268: Iterator irecs = this .getIterator();
269:
270: while (irecs.hasNext()) {
271: size += ((Record) irecs.next()).getRecordSize();
272: }
273:
274: return size;
275: }
276:
277: public Iterator getIterator() {
278: return new MyIterator();
279: }
280:
281: /** Performs a deep clone of the record*/
282: public Object clone() {
283: ValueRecordsAggregate rec = new ValueRecordsAggregate();
284: for (Iterator valIter = getIterator(); valIter.hasNext();) {
285: CellValueRecordInterface val = (CellValueRecordInterface) ((CellValueRecordInterface) valIter
286: .next()).clone();
287: rec.insertCell(val);
288: }
289: return rec;
290: }
291:
292: public class MyIterator implements Iterator {
293: short nextColumn = -1;
294: int nextRow, lastRow;
295:
296: public MyIterator() {
297: this .nextRow = 0;
298: this .lastRow = records.length - 1;
299: findNext();
300: }
301:
302: public MyIterator(int firstRow, int lastRow) {
303: this .nextRow = firstRow;
304: this .lastRow = lastRow;
305: findNext();
306: }
307:
308: public boolean hasNext() {
309: return nextRow <= lastRow;
310: }
311:
312: public Object next() {
313: Object o = records[nextRow][nextColumn];
314: findNext();
315: return o;
316: }
317:
318: public void remove() {
319: throw new UnsupportedOperationException("gibt's noch nicht");
320: }
321:
322: private void findNext() {
323: nextColumn++;
324: for (; nextRow <= lastRow; nextRow++) {
325: //previously this threw array out of bounds...
326: CellValueRecordInterface[] rowCells = (nextRow < records.length) ? records[nextRow]
327: : null;
328: if (rowCells == null) { // This row is empty
329: nextColumn = 0;
330: continue;
331: }
332: for (; nextColumn < rowCells.length; nextColumn++) {
333: if (rowCells[nextColumn] != null)
334: return;
335: }
336: nextColumn = 0;
337: }
338: }
339:
340: }
341: }
|