Lines 18-25
Link Here
|
18 |
package org.apache.poi.hssf.record.aggregates; |
18 |
package org.apache.poi.hssf.record.aggregates; |
19 |
|
19 |
|
20 |
import java.util.ArrayList; |
20 |
import java.util.ArrayList; |
21 |
import java.util.Arrays; |
|
|
22 |
import java.util.Comparator; |
23 |
import java.util.HashMap; |
21 |
import java.util.HashMap; |
24 |
import java.util.List; |
22 |
import java.util.List; |
25 |
import java.util.Map; |
23 |
import java.util.Map; |
Lines 74-80
Link Here
|
74 |
public void add(FormulaRecordAggregate agg) { |
72 |
public void add(FormulaRecordAggregate agg) { |
75 |
if (_numberOfFormulas == 0) { |
73 |
if (_numberOfFormulas == 0) { |
76 |
if (_firstCell.getRow() != agg.getRow() || _firstCell.getCol() != agg.getColumn()) { |
74 |
if (_firstCell.getRow() != agg.getRow() || _firstCell.getCol() != agg.getColumn()) { |
77 |
throw new IllegalStateException("shared formula coding error"); |
75 |
throw new IllegalStateException("shared formula coding error: "+_firstCell.getCol()+'/'+_firstCell.getRow()+" != "+agg.getColumn()+'/'+agg.getRow()); |
78 |
} |
76 |
} |
79 |
} |
77 |
} |
80 |
if (_numberOfFormulas >= _frAggs.length) { |
78 |
if (_numberOfFormulas >= _frAggs.length) { |
Lines 100-115
Link Here
|
100 |
sb.append("]"); |
98 |
sb.append("]"); |
101 |
return sb.toString(); |
99 |
return sb.toString(); |
102 |
} |
100 |
} |
103 |
|
|
|
104 |
/** |
105 |
* Note - the 'first cell' of a shared formula group is not always the top-left cell |
106 |
* of the enclosing range. |
107 |
* @return <code>true</code> if the specified coordinates correspond to the 'first cell' |
108 |
* of this shared formula group. |
109 |
*/ |
110 |
public boolean isFirstCell(int row, int column) { |
111 |
return _firstCell.getRow() == row && _firstCell.getCol() == column; |
112 |
} |
113 |
} |
101 |
} |
114 |
|
102 |
|
115 |
/** |
103 |
/** |
Lines 124-130
Link Here
|
124 |
private final TableRecord[] _tableRecords; |
112 |
private final TableRecord[] _tableRecords; |
125 |
private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord; |
113 |
private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord; |
126 |
/** cached for optimization purposes */ |
114 |
/** cached for optimization purposes */ |
127 |
private SharedFormulaGroup[] _groups; |
115 |
private Map<Integer,SharedFormulaGroup> _groupsCache; |
128 |
|
116 |
|
129 |
private SharedValueManager(SharedFormulaRecord[] sharedFormulaRecords, |
117 |
private SharedValueManager(SharedFormulaRecord[] sharedFormulaRecords, |
130 |
CellReference[] firstCells, ArrayRecord[] arrayRecords, TableRecord[] tableRecords) { |
118 |
CellReference[] firstCells, ArrayRecord[] arrayRecords, TableRecord[] tableRecords) { |
Lines 169-225
Link Here
|
169 |
* @return never <code>null</code> |
157 |
* @return never <code>null</code> |
170 |
*/ |
158 |
*/ |
171 |
public SharedFormulaRecord linkSharedFormulaRecord(CellReference firstCell, FormulaRecordAggregate agg) { |
159 |
public SharedFormulaRecord linkSharedFormulaRecord(CellReference firstCell, FormulaRecordAggregate agg) { |
172 |
|
160 |
SharedFormulaGroup result = findFormulaGroupForCell(firstCell); |
173 |
SharedFormulaGroup result = findFormulaGroup(getGroups(), firstCell); |
|
|
174 |
result.add(agg); |
161 |
result.add(agg); |
175 |
return result.getSFR(); |
162 |
return result.getSFR(); |
176 |
} |
163 |
} |
177 |
|
164 |
|
178 |
private static SharedFormulaGroup findFormulaGroup(SharedFormulaGroup[] groups, CellReference firstCell) { |
165 |
private SharedFormulaGroup findFormulaGroupForCell(final CellReference cellRef) { |
179 |
int row = firstCell.getRow(); |
166 |
if(null == _groupsCache) { |
180 |
int column = firstCell.getCol(); |
167 |
_groupsCache = new HashMap<Integer,SharedFormulaGroup>(_groupsBySharedFormulaRecord.size()); |
181 |
// Traverse the list of shared formulas and try to find the correct one for us |
168 |
for(SharedFormulaGroup group: _groupsBySharedFormulaRecord.values()) { |
|
|
169 |
_groupsCache.put(getKeyForCache(group._firstCell),group); |
170 |
} |
171 |
} |
172 |
SharedFormulaGroup sfg = _groupsCache.get(getKeyForCache(cellRef)); |
173 |
if(null == sfg) { |
174 |
// TODO - fix file "15228.xls" so it opens in Excel after rewriting with POI |
175 |
throw new RuntimeException("Failed to find a matching shared formula record"); |
176 |
} |
177 |
return sfg; |
178 |
} |
182 |
|
179 |
|
183 |
// perhaps this could be optimised to some kind of binary search |
180 |
private Integer getKeyForCache(final CellReference cellRef) { |
184 |
for (int i = 0; i < groups.length; i++) { |
181 |
// The HSSF has a max of 2^16 rows and 2^8 cols |
185 |
SharedFormulaGroup svg = groups[i]; |
182 |
return new Integer((cellRef.getCol()+1)<<16 | cellRef.getRow()); |
186 |
if (svg.isFirstCell(row, column)) { |
183 |
} |
187 |
return svg; |
|
|
188 |
} |
189 |
} |
190 |
// TODO - fix file "15228.xls" so it opens in Excel after rewriting with POI |
191 |
throw new RuntimeException("Failed to find a matching shared formula record"); |
192 |
} |
193 |
|
184 |
|
194 |
private SharedFormulaGroup[] getGroups() { |
|
|
195 |
if (_groups == null) { |
196 |
SharedFormulaGroup[] groups = new SharedFormulaGroup[_groupsBySharedFormulaRecord.size()]; |
197 |
_groupsBySharedFormulaRecord.values().toArray(groups); |
198 |
Arrays.sort(groups, SVGComparator); // make search behaviour more deterministic |
199 |
_groups = groups; |
200 |
} |
201 |
return _groups; |
202 |
} |
203 |
|
204 |
private static final Comparator<SharedFormulaGroup> SVGComparator = new Comparator<SharedFormulaGroup>() { |
205 |
|
206 |
public int compare(SharedFormulaGroup a, SharedFormulaGroup b) { |
207 |
CellRangeAddress8Bit rangeA = a.getSFR().getRange(); |
208 |
CellRangeAddress8Bit rangeB = b.getSFR().getRange(); |
209 |
|
210 |
int cmp; |
211 |
cmp = rangeA.getFirstRow() - rangeB.getFirstRow(); |
212 |
if (cmp != 0) { |
213 |
return cmp; |
214 |
} |
215 |
cmp = rangeA.getFirstColumn() - rangeB.getFirstColumn(); |
216 |
if (cmp != 0) { |
217 |
return cmp; |
218 |
} |
219 |
return 0; |
220 |
} |
221 |
}; |
222 |
|
223 |
/** |
185 |
/** |
224 |
* Gets the {@link SharedValueRecordBase} record if it should be encoded immediately after the |
186 |
* Gets the {@link SharedValueRecordBase} record if it should be encoded immediately after the |
225 |
* formula record contained in the specified {@link FormulaRecordAggregate} agg. Note - the |
187 |
* formula record contained in the specified {@link FormulaRecordAggregate} agg. Note - the |
Lines 248-263
Link Here
|
248 |
// not the first formula cell in the group |
210 |
// not the first formula cell in the group |
249 |
return null; |
211 |
return null; |
250 |
} |
212 |
} |
251 |
SharedFormulaGroup[] groups = getGroups(); |
|
|
252 |
for (int i = 0; i < groups.length; i++) { |
253 |
// note - logic for finding correct shared formula group is slightly |
254 |
// more complicated since the first cell |
255 |
SharedFormulaGroup sfg = groups[i]; |
256 |
if (sfg.isFirstCell(row, column)) { |
257 |
return sfg.getSFR(); |
258 |
} |
259 |
} |
260 |
|
213 |
|
|
|
214 |
if(!_groupsBySharedFormulaRecord.isEmpty()) { |
215 |
SharedFormulaGroup sfg = findFormulaGroupForCell(firstCell); |
216 |
if(null != sfg) { |
217 |
return sfg.getSFR(); |
218 |
} |
219 |
} |
220 |
|
261 |
// Since arrays and tables cannot be sparse (all cells in range participate) |
221 |
// Since arrays and tables cannot be sparse (all cells in range participate) |
262 |
// The first cell will be the top left in the range. So we can match the |
222 |
// The first cell will be the top left in the range. So we can match the |
263 |
// ARRAY/TABLE record directly. |
223 |
// ARRAY/TABLE record directly. |
Lines 284-290
Link Here
|
284 |
if (svg == null) { |
244 |
if (svg == null) { |
285 |
throw new IllegalStateException("Failed to find formulas for shared formula"); |
245 |
throw new IllegalStateException("Failed to find formulas for shared formula"); |
286 |
} |
246 |
} |
287 |
_groups = null; // be sure to reset cached value |
247 |
_groupsCache = null; // be sure to reset cached value |
288 |
svg.unlinkSharedFormulas(); |
248 |
svg.unlinkSharedFormulas(); |
289 |
} |
249 |
} |
290 |
|
250 |
|