package org.simantics.spreadsheet.graph; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.spreadsheet.Range; import org.simantics.spreadsheet.graph.formula.FormulaError2; import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment; import org.simantics.spreadsheet.graph.parser.ast.AstApply; import org.simantics.spreadsheet.graph.parser.ast.AstArgList; import org.simantics.spreadsheet.graph.parser.ast.AstArithmeticExpression; import org.simantics.spreadsheet.graph.parser.ast.AstArray; import org.simantics.spreadsheet.graph.parser.ast.AstArrayFormulaReference; import org.simantics.spreadsheet.graph.parser.ast.AstBoolean; import org.simantics.spreadsheet.graph.parser.ast.AstDouble; import org.simantics.spreadsheet.graph.parser.ast.AstFactor; import org.simantics.spreadsheet.graph.parser.ast.AstIdentifier; import org.simantics.spreadsheet.graph.parser.ast.AstInteger; import org.simantics.spreadsheet.graph.parser.ast.AstNothing; import org.simantics.spreadsheet.graph.parser.ast.AstNull; import org.simantics.spreadsheet.graph.parser.ast.AstRange; import org.simantics.spreadsheet.graph.parser.ast.AstRelation; import org.simantics.spreadsheet.graph.parser.ast.AstString; import org.simantics.spreadsheet.graph.parser.ast.AstTerm; import org.simantics.spreadsheet.graph.parser.ast.AstValue; import org.simantics.spreadsheet.graph.parser.ast.AstValueVisitor; import org.simantics.spreadsheet.util.SpreadsheetUtils; import it.unimi.dsi.fastutil.longs.AbstractLongList; import it.unimi.dsi.fastutil.longs.LongArrayList; public class CellValueVisitor implements AstValueVisitor { final public SpreadsheetBook book; final private SpreadsheetEvaluationEnvironment env; final private SpreadsheetCell thisCell; final private LongArrayList references = new LongArrayList(); public CellValueVisitor(SpreadsheetEvaluationEnvironment env, SpreadsheetCell thisCell) { this.book = env.getBook(); this.env = env; this.thisCell = thisCell; } public void addReference(long ref) { references.add(ref); } public AbstractLongList getReferences() { return references; } @Override public Object visit(AstBoolean astBoolean) { return astBoolean.value; } @Override public Object visit(AstDouble astFloat) { return astFloat.value; } @Override public Object visit(AstInteger astInteger) { return astInteger.value; } @Override public Object visit(AstNull astNull) { throw new IllegalStateException(); } @Override public Object visit(AstString astString) { return astString.value; } @Override public Object visit(AstRange astRange) { if(astRange.isRef()){ return FormulaError2.REF.getString(); } if(astRange.isCell()) { String ref = astRange.first; Range r = SpreadsheetUtils.decodeCell(ref, 0, 0); String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName(); SpreadsheetCell cell = thisCell.getBook().get(sheetName, r.startRow, r.startColumn); if(cell == null) { SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName); SpreadsheetLine line = eng.getLine(r.startRow); if (line == null) { line = new SpreadsheetLine(eng.lines, r.startRow); eng.lines.lines.put(-r.startRow, line); } cell = SpreadsheetCell.empty(line, r.startColumn); } return cell.evaluate(env, this); } else { Object cached = thisCell.getEngine().getCachedRange(astRange); if(cached != null) { Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second); String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName(); SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName); Range r = eng.actualRange(r_); for(int row=0; row < r.height();row++) { SpreadsheetLine line = eng.getLine(r.startRow + row); if(line != null) { for(int column=0; column < r.width();column++) { int col = r.startColumn + column; if(line.cells.size() > col) { SpreadsheetCell cell = line.cells.get(r.startColumn + column); //Add references, but do not evaluate if there exists a cached range. addReference(cell.makeReferenceKey()); } } } } return cached; } Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second); String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName(); SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName); // Full ranges are resolved here Range r = eng.actualRange(r_); SpreadsheetMatrix result = new SpreadsheetMatrix(r.width(), r.height()); for(int row=0; row < r.height();row++) { SpreadsheetLine line = eng.getLine(r.startRow + row); if(line != null) { for(int column=0; column < r.width();column++) { int col = r.startColumn + column; if(line.cells.size() > col) { SpreadsheetCell cell = line.cells.get(r.startColumn + column); result.values[r.width()*row + column] = cell.evaluate(env, this); } } } } thisCell.getEngine().cacheRange(astRange, result); return result; } } @Override public Object visit(AstArgList astArgList) { throw new IllegalStateException(); } @Override public Object visit(AstApply astApply) { CellFormulaFunction fn = env.getFunction(astApply.value); if(fn != null) { return fn.evaluate(this, astApply.args); } else { return FormulaError2.NAME.getString(); } } @Override public Object visit(AstRelation astRelation) { Object leftResult = astRelation.left.accept(this); Object rightResult = astRelation.right.accept(this); FormulaError2 err = FormulaError2.forObject(leftResult); if(err!=null) return err.getString(); FormulaError2 err2 = FormulaError2.forObject(rightResult); if(err2!=null) return err2.getString(); if(leftResult instanceof Variant){ Object leftTemp = ((Variant)leftResult).getValue(); Double leftVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftTemp); if(leftVal==null) leftResult = leftTemp.toString(); else leftResult = leftVal; } if(rightResult instanceof Variant){ Object rightTemp = ((Variant)rightResult).getValue(); Double rightVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightTemp); if(rightVal==null) rightResult = rightTemp.toString(); else rightResult = rightVal; } if ((leftResult instanceof String) || (rightResult instanceof String)) { String leftString = (leftResult.toString()).toLowerCase(); String rightString = (rightResult.toString()).toLowerCase(); if("<".equals(astRelation.op.trim())) return leftString.compareTo(rightString) < 0; else if(">".equals(astRelation.op.trim())) return leftString.compareTo(rightString) > 0; else if("=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) == 0; else if("<>".equals(astRelation.op.trim())) return leftString.compareTo(rightString) != 0 ; else if("<=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) <= 0 ; else if(">=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) >= 0 ; else throw new IllegalStateException(); } else { Number leftNumber = SpreadsheetGraphUtils.asNumber(leftResult); Number rightNumber = SpreadsheetGraphUtils.asNumber(rightResult); if("<".equals(astRelation.op.trim())) return leftNumber.doubleValue() < rightNumber.doubleValue(); else if(">".equals(astRelation.op.trim())) return leftNumber.doubleValue() > rightNumber.doubleValue(); else if("=".equals(astRelation.op.trim())) return leftNumber.doubleValue() == rightNumber.doubleValue(); else if("<>".equals(astRelation.op.trim())) return leftNumber.doubleValue() != rightNumber.doubleValue(); else if("<=".equals(astRelation.op.trim())) return leftNumber.doubleValue() <= rightNumber.doubleValue(); else if(">=".equals(astRelation.op.trim())) return leftNumber.doubleValue() >= rightNumber.doubleValue(); else throw new IllegalStateException(); } } Object leftValueWithPrefix(Object result, AstValue value, String prefix, boolean forceNumber) { if(result == null) { Object obj = value.accept(this); FormulaError2 err = FormulaError2.forObject(obj); if(err!=null) return err.getString(); if("-".equals(prefix)) { result = SpreadsheetGraphUtils.asNumber(obj); return -((Number)result).doubleValue(); } else { if(forceNumber) return SpreadsheetGraphUtils.asNumber(obj); else return obj; } } try{ return (Number)Double.parseDouble(result.toString()); } catch (NumberFormatException e){ return result; } } @Override public Object visit(AstArithmeticExpression exp) { Object result = null; Object other = null; AstValue value = null; Object acceptedValue = null; for(int i=0;i