| /* ==================================================================== |
| 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.xssf.usermodel; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.poi.ss.formula.EvaluationCell; |
| import org.apache.poi.ss.formula.EvaluationSheet; |
| import org.apache.poi.ss.usermodel.Cell; |
| import org.apache.poi.ss.usermodel.Row; |
| import org.apache.poi.util.Internal; |
| |
| /** |
| * XSSF wrapper for a sheet under evaluation |
| */ |
| @Internal |
| final class XSSFEvaluationSheet implements EvaluationSheet { |
| |
| private final XSSFSheet _xs; |
| private Map<CellKey, EvaluationCell> _cellCache; |
| |
| public XSSFEvaluationSheet(XSSFSheet sheet) { |
| _xs = sheet; |
| } |
| |
| public XSSFSheet getXSSFSheet() { |
| return _xs; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.poi.ss.formula.EvaluationSheet#getlastRowNum() |
| * @since POI 4.0.0 |
| */ |
| @Override |
| public int getLastRowNum() { |
| return _xs.getLastRowNum(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.poi.ss.formula.EvaluationSheet#isRowHidden(int) |
| * @since POI 4.1.0 |
| */ |
| public boolean isRowHidden(int rowIndex) { |
| final XSSFRow row = _xs.getRow(rowIndex); |
| if (row == null) return false; |
| return row.getZeroHeight(); |
| } |
| |
| /* (non-JavaDoc), inherit JavaDoc from EvaluationWorkbook |
| * @since POI 3.15 beta 3 |
| */ |
| @Override |
| public void clearAllCachedResultValues() { |
| _cellCache = null; |
| } |
| |
| @Override |
| public EvaluationCell getCell(int rowIndex, int columnIndex) { |
| // shortcut evaluation if reference is outside the bounds of existing data |
| // see issue #61841 for impact on VLOOKUP in particular |
| if (rowIndex > getLastRowNum()) { |
| return null; |
| } |
| |
| // cache for performance: ~30% speedup due to caching |
| if (_cellCache == null) { |
| _cellCache = new HashMap<>(_xs.getLastRowNum() * 3); |
| for (final Row row : _xs) { |
| final int rowNum = row.getRowNum(); |
| for (final Cell cell : row) { |
| // cast is safe, the iterator is just defined using the interface |
| final CellKey key = new CellKey(rowNum, cell.getColumnIndex()); |
| final EvaluationCell evalcell = new XSSFEvaluationCell((XSSFCell) cell, this); |
| _cellCache.put(key, evalcell); |
| } |
| } |
| } |
| |
| final CellKey key = new CellKey(rowIndex, columnIndex); |
| EvaluationCell evalcell = _cellCache.get(key); |
| |
| // If cache is stale, update cache with this one cell |
| // This is a compromise between rebuilding the entire cache |
| // (which would quickly defeat the benefit of the cache) |
| // and not caching at all. |
| // See bug 59958: Add cells on the fly to the evaluation sheet cache on cache miss |
| if (evalcell == null) { |
| XSSFRow row = _xs.getRow(rowIndex); |
| if (row == null) { |
| return null; |
| } |
| XSSFCell cell = row.getCell(columnIndex); |
| if (cell == null) { |
| return null; |
| } |
| evalcell = new XSSFEvaluationCell(cell, this); |
| _cellCache.put(key, evalcell); |
| } |
| |
| return evalcell; |
| } |
| |
| private static class CellKey { |
| private final int _row; |
| private final int _col; |
| private int _hash = -1; //lazily computed |
| |
| protected CellKey(int row, int col) { |
| _row = row; |
| _col = col; |
| } |
| |
| @Override |
| public int hashCode() { |
| if ( _hash == -1 ) { |
| _hash = (17 * 37 + _row) * 37 + _col; |
| } |
| return _hash; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof CellKey)) { |
| return false; |
| } |
| // assumes other object is one of us, otherwise ClassCastException is thrown |
| final CellKey oKey = (CellKey) obj; |
| return _row == oKey._row && _col == oKey._col; |
| } |
| } |
| } |