Initial add of ForkedEvaluator functionality

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@723194 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-12-04 02:48:24 +00:00
parent 1869eb5186
commit 11cc164f26
4 changed files with 539 additions and 0 deletions

View File

@ -0,0 +1,132 @@
/* ====================================================================
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.ss.formula.eval.forked;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationSheet;
import org.apache.poi.ss.usermodel.Cell;
/**
* Represents a cell being used for forked evaluation that has had a value set different from the
* corresponding cell in the shared master workbook.
*
* @author Josh Micich
*/
final class ForkedEvaluationCell implements EvaluationCell {
private final EvaluationSheet _sheet;
/** corresponding cell from master workbook */
private final EvaluationCell _masterCell;
private boolean _booleanValue;
private int _cellType;
private int _errorValue;
private double _numberValue;
private String _stringValue;
public ForkedEvaluationCell(ForkedEvaluationSheet sheet, EvaluationCell masterCell) {
_sheet = sheet;
_masterCell = masterCell;
// start with value blank, but expect construction to be immediately
setValue(BlankEval.INSTANCE); // followed by a proper call to setValue()
}
public Object getIdentityKey() {
return _masterCell.getIdentityKey();
}
public void setValue(ValueEval value) {
Class<? extends ValueEval> cls = value.getClass();
if (cls == NumberEval.class) {
_cellType = HSSFCell.CELL_TYPE_NUMERIC;
_numberValue = ((NumberEval)value).getNumberValue();
return;
}
if (cls == StringEval.class) {
_cellType = HSSFCell.CELL_TYPE_STRING;
_stringValue = ((StringEval)value).getStringValue();
return;
}
if (cls == BoolEval.class) {
_cellType = HSSFCell.CELL_TYPE_BOOLEAN;
_booleanValue = ((BoolEval)value).getBooleanValue();
return;
}
if (cls == ErrorEval.class) {
_cellType = HSSFCell.CELL_TYPE_ERROR;
_errorValue = ((ErrorEval)value).getErrorCode();
return;
}
if (cls == BlankEval.class) {
_cellType = HSSFCell.CELL_TYPE_BLANK;
return;
}
throw new IllegalArgumentException("Unexpected value class (" + cls.getName() + ")");
}
public void copyValue(Cell destCell) {
switch (_cellType) {
case Cell.CELL_TYPE_BLANK: destCell.setCellType(Cell.CELL_TYPE_BLANK); return;
case Cell.CELL_TYPE_NUMERIC: destCell.setCellValue(_numberValue); return;
case Cell.CELL_TYPE_BOOLEAN: destCell.setCellValue(_booleanValue); return;
case Cell.CELL_TYPE_STRING: destCell.setCellValue(_stringValue); return;
case Cell.CELL_TYPE_ERROR: destCell.setCellErrorValue((byte)_errorValue); return;
}
throw new IllegalStateException("Unexpected data type (" + _cellType + ")");
}
private void checkCellType(int expectedCellType) {
if (_cellType != expectedCellType) {
throw new RuntimeException("Wrong data type (" + _cellType + ")");
}
}
public int getCellType() {
return _cellType;
}
public boolean getBooleanCellValue() {
checkCellType(HSSFCell.CELL_TYPE_BOOLEAN);
return _booleanValue;
}
public int getErrorCellValue() {
checkCellType(HSSFCell.CELL_TYPE_ERROR);
return _errorValue;
}
public double getNumericCellValue() {
checkCellType(HSSFCell.CELL_TYPE_NUMERIC);
return _numberValue;
}
public String getStringCellValue() {
checkCellType(HSSFCell.CELL_TYPE_STRING);
return _stringValue;
}
public EvaluationSheet getSheet() {
return _sheet;
}
public int getRowIndex() {
return _masterCell.getRowIndex();
}
public int getColumnIndex() {
return _masterCell.getColumnIndex();
}
}

View File

@ -0,0 +1,129 @@
/* ====================================================================
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.ss.formula.eval.forked;
import java.util.Arrays;
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.formula.EvaluationWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
/**
* Represents a sheet being used for forked evaluation. Initially, objects of this class contain
* only the cells from the master workbook. By calling {@link #getOrCreateUpdatableCell(int, int)},
* the master cell object is logically replaced with a {@link ForkedEvaluationCell} instance, which
* will be used in all subsequent evaluations.
*
* @author Josh Micich
*/
final class ForkedEvaluationSheet implements EvaluationSheet {
private final EvaluationSheet _masterSheet;
/**
* Only cells which have been split are put in this map. (This has been done to conserve memory).
*/
private final Map<RowColKey, ForkedEvaluationCell> _sharedCellsByRowCol;
public ForkedEvaluationSheet(EvaluationSheet masterSheet) {
_masterSheet = masterSheet;
_sharedCellsByRowCol = new HashMap<RowColKey, ForkedEvaluationCell>();
}
public EvaluationCell getCell(int rowIndex, int columnIndex) {
RowColKey key = new RowColKey(rowIndex, columnIndex);
ForkedEvaluationCell result = _sharedCellsByRowCol.get(key);
if (result == null) {
return _masterSheet.getCell(rowIndex, columnIndex);
}
return result;
}
public ForkedEvaluationCell getOrCreateUpdatableCell(int rowIndex, int columnIndex) {
RowColKey key = new RowColKey(rowIndex, columnIndex);
ForkedEvaluationCell result = _sharedCellsByRowCol.get(key);
if (result == null) {
EvaluationCell mcell = _masterSheet.getCell(rowIndex, columnIndex);
result = new ForkedEvaluationCell(this, mcell);
_sharedCellsByRowCol.put(key, result);
}
return result;
}
public void copyUpdatedCells(Sheet sheet) {
RowColKey[] keys = new RowColKey[_sharedCellsByRowCol.size()];
_sharedCellsByRowCol.keySet().toArray(keys);
Arrays.sort(keys);
for (int i = 0; i < keys.length; i++) {
RowColKey key = keys[i];
Row row = sheet.getRow(key.getRowIndex());
if (row == null) {
row = sheet.createRow(key.getRowIndex());
}
Cell destCell = row.getCell(key.getColumnIndex());
if (destCell == null) {
destCell = row.createCell(key.getColumnIndex());
}
ForkedEvaluationCell srcCell = _sharedCellsByRowCol.get(key);
srcCell.copyValue(destCell);
}
}
public int getSheetIndex(EvaluationWorkbook mewb) {
return mewb.getSheetIndex(_masterSheet);
}
private static final class RowColKey implements Comparable<RowColKey>{
private final int _rowIndex;
private final int _columnIndex;
public RowColKey(int rowIndex, int columnIndex) {
_rowIndex = rowIndex;
_columnIndex = columnIndex;
}
@Override
public boolean equals(Object obj) {
RowColKey other = (RowColKey) obj;
return _rowIndex == other._rowIndex && _columnIndex == other._columnIndex;
}
@Override
public int hashCode() {
return _rowIndex ^ _columnIndex;
}
public int compareTo(RowColKey o) {
int cmp = _rowIndex - o._rowIndex;
if (cmp != 0) {
return cmp;
}
return _columnIndex - o._columnIndex;
}
public int getRowIndex() {
return _rowIndex;
}
public int getColumnIndex() {
return _columnIndex;
}
}
}

View File

@ -0,0 +1,144 @@
/* ====================================================================
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.ss.formula.eval.forked;
import java.util.HashMap;
import java.util.Map;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationName;
import org.apache.poi.ss.formula.EvaluationSheet;
import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Represents a workbook being used for forked evaluation. Most operations are delegated to the
* shared master workbook, except those that potentially involve cell values that may have been
* updated after a call to {@link #getOrCreateUpdatableCell(String, int, int)}.
*
* @author Josh Micich
*/
final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
private final EvaluationWorkbook _masterBook;
private final Map<String, ForkedEvaluationSheet> _sharedSheetsByName;
public ForkedEvaluationWorkbook(EvaluationWorkbook master) {
_masterBook = master;
_sharedSheetsByName = new HashMap<String, ForkedEvaluationSheet>();
}
public ForkedEvaluationCell getOrCreateUpdatableCell(String sheetName, int rowIndex,
int columnIndex) {
ForkedEvaluationSheet sheet = getSharedSheet(sheetName);
return sheet.getOrCreateUpdatableCell(rowIndex, columnIndex);
}
public EvaluationCell getEvaluationCell(String sheetName, int rowIndex, int columnIndex) {
ForkedEvaluationSheet sheet = getSharedSheet(sheetName);
return sheet.getCell(rowIndex, columnIndex);
}
private ForkedEvaluationSheet getSharedSheet(String sheetName) {
ForkedEvaluationSheet result = _sharedSheetsByName.get(sheetName);
if (result == null) {
result = new ForkedEvaluationSheet(_masterBook.getSheet(_masterBook
.getSheetIndex(sheetName)));
_sharedSheetsByName.put(sheetName, result);
}
return result;
}
public void copyUpdatedCells(Workbook workbook) {
String[] sheetNames = new String[_sharedSheetsByName.size()];
_sharedSheetsByName.keySet().toArray(sheetNames);
OrderedSheet[] oss = new OrderedSheet[sheetNames.length];
for (int i = 0; i < sheetNames.length; i++) {
String sheetName = sheetNames[i];
oss[i] = new OrderedSheet(sheetName, _masterBook.getSheetIndex(sheetName));
}
for (int i = 0; i < oss.length; i++) {
String sheetName = oss[i].getSheetName();
ForkedEvaluationSheet sheet = _sharedSheetsByName.get(sheetName);
sheet.copyUpdatedCells(workbook.getSheet(sheetName));
}
}
public int convertFromExternSheetIndex(int externSheetIndex) {
return _masterBook.convertFromExternSheetIndex(externSheetIndex);
}
public ExternalSheet getExternalSheet(int externSheetIndex) {
return _masterBook.getExternalSheet(externSheetIndex);
}
public Ptg[] getFormulaTokens(EvaluationCell cell) {
if (cell instanceof ForkedEvaluationCell) {
// doesn't happen yet because formulas cannot be modified from the master workbook
throw new RuntimeException("Updated formulas not supported yet");
}
return _masterBook.getFormulaTokens(cell);
}
public EvaluationName getName(NamePtg namePtg) {
return _masterBook.getName(namePtg);
}
public EvaluationSheet getSheet(int sheetIndex) {
return getSharedSheet(getSheetName(sheetIndex));
}
public int getSheetIndex(EvaluationSheet sheet) {
if (sheet instanceof ForkedEvaluationSheet) {
ForkedEvaluationSheet mes = (ForkedEvaluationSheet) sheet;
return mes.getSheetIndex(_masterBook);
}
return _masterBook.getSheetIndex(sheet);
}
public int getSheetIndex(String sheetName) {
return _masterBook.getSheetIndex(sheetName);
}
public String getSheetName(int sheetIndex) {
return _masterBook.getSheetName(sheetIndex);
}
public String resolveNameXText(NameXPtg ptg) {
return _masterBook.resolveNameXText(ptg);
}
private static final class OrderedSheet implements Comparable<OrderedSheet> {
private final String _sheetName;
private final int _index;
public OrderedSheet(String sheetName, int index) {
_sheetName = sheetName;
_index = index;
}
public String getSheetName() {
return _sheetName;
}
public int compareTo(OrderedSheet o) {
return _index - o._index;
}
}
}

View File

@ -0,0 +1,134 @@
/* ====================================================================
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.ss.formula.eval.forked;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/**
* An alternative workbook evaluator that saves memory in situations where a single workbook is
* concurrently and independently evaluated many times. With standard formula evaluation, around
* 90% of memory consumption is due to loading of the {@link HSSFWorkbook} or {@link XSSFWorkbook}.
* This class enables a 'master workbook' to be loaded just once and shared between many evaluation
* clients. Each evaluation client creates its own {@link ForkedEvaluator} and can set cell values
* that will be used for local evaluations (and don't disturb evaluations on other evaluators).
*
* @author Josh Micich
*/
public final class ForkedEvaluator {
private WorkbookEvaluator _evaluator;
private ForkedEvaluationWorkbook _sewb;
private ForkedEvaluator(EvaluationWorkbook masterWorkbook, IStabilityClassifier stabilityClassifier) {
_sewb = new ForkedEvaluationWorkbook(masterWorkbook);
_evaluator = new WorkbookEvaluator(_sewb, stabilityClassifier);
}
private static EvaluationWorkbook createEvaluationWorkbook(Workbook wb) {
if (wb instanceof HSSFWorkbook) {
return HSSFEvaluationWorkbook.create((HSSFWorkbook) wb);
}
// TODO rearrange POI build to allow this
// if (wb instanceof XSSFWorkbook) {
// return XSSFEvaluationWorkbook.create((XSSFWorkbook) wb);
// }
throw new IllegalArgumentException("Unexpected workbook type (" + wb.getClass().getName() + ")");
}
public static ForkedEvaluator create(Workbook wb, IStabilityClassifier stabilityClassifier) {
return new ForkedEvaluator(createEvaluationWorkbook(wb), stabilityClassifier);
}
/**
* Sets the specified cell to the supplied <tt>value</tt>
* @param sheetName the name of the sheet containing the cell
* @param rowIndex zero based
* @param columnIndex zero based
*/
public void updateCell(String sheetName, int rowIndex, int columnIndex, ValueEval value) {
ForkedEvaluationCell cell = _sewb.getOrCreateUpdatableCell(sheetName, rowIndex, columnIndex);
cell.setValue(value);
_evaluator.notifyUpdateCell(cell);
}
/**
* Copies the values of all updated cells (modified by calls to {@link
* #updateCell(String, int, int, ValueEval)}) to the supplied <tt>workbook</tt>.<br/>
* Typically, the supplied <tt>workbook</tt> is a writable copy of the 'master workbook',
* but at the very least it must contain sheets with the same names.
*/
public void copyUpdatedCells(Workbook workbook) {
_sewb.copyUpdatedCells(workbook);
}
/**
* If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the
* original cell.
*
* @param cell may be <code>null</code> signifying that the cell is not present (or blank)
* @return <code>null</code> if the supplied cell is <code>null</code> or blank
*/
public ValueEval evaluate(String sheetName, int rowIndex, int columnIndex) {
EvaluationCell cell = _sewb.getEvaluationCell(sheetName, rowIndex, columnIndex);
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_BOOLEAN:
return BoolEval.valueOf(cell.getBooleanCellValue());
case HSSFCell.CELL_TYPE_ERROR:
return ErrorEval.valueOf(cell.getErrorCellValue());
case HSSFCell.CELL_TYPE_FORMULA:
return _evaluator.evaluate(cell);
case HSSFCell.CELL_TYPE_NUMERIC:
return new NumberEval(cell.getNumericCellValue());
case HSSFCell.CELL_TYPE_STRING:
return new StringEval(cell.getStringCellValue());
case HSSFCell.CELL_TYPE_BLANK:
return null;
}
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
}
/**
* Coordinates several formula evaluators together so that formulas that involve external
* references can be evaluated.
* @param workbookNames the simple file names used to identify the workbooks in formulas
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
*/
public static void setupEnvironment(String[] workbookNames, ForkedEvaluator[] evaluators) {
WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
for (int i = 0; i < wbEvals.length; i++) {
wbEvals[i] = evaluators[i]._evaluator;
}
CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
}
}