1 package org.simantics.spreadsheet.graph;
3 import org.simantics.databoard.binding.mutable.Variant;
4 import org.simantics.spreadsheet.Range;
5 import org.simantics.spreadsheet.graph.formula.FormulaError2;
6 import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;
7 import org.simantics.spreadsheet.graph.parser.ast.AstApply;
8 import org.simantics.spreadsheet.graph.parser.ast.AstArgList;
9 import org.simantics.spreadsheet.graph.parser.ast.AstArithmeticExpression;
10 import org.simantics.spreadsheet.graph.parser.ast.AstArray;
11 import org.simantics.spreadsheet.graph.parser.ast.AstArrayFormulaReference;
12 import org.simantics.spreadsheet.graph.parser.ast.AstBoolean;
13 import org.simantics.spreadsheet.graph.parser.ast.AstDouble;
14 import org.simantics.spreadsheet.graph.parser.ast.AstFactor;
15 import org.simantics.spreadsheet.graph.parser.ast.AstIdentifier;
16 import org.simantics.spreadsheet.graph.parser.ast.AstInteger;
17 import org.simantics.spreadsheet.graph.parser.ast.AstNothing;
18 import org.simantics.spreadsheet.graph.parser.ast.AstNull;
19 import org.simantics.spreadsheet.graph.parser.ast.AstRange;
20 import org.simantics.spreadsheet.graph.parser.ast.AstRelation;
21 import org.simantics.spreadsheet.graph.parser.ast.AstString;
22 import org.simantics.spreadsheet.graph.parser.ast.AstTerm;
23 import org.simantics.spreadsheet.graph.parser.ast.AstValue;
24 import org.simantics.spreadsheet.graph.parser.ast.AstValueVisitor;
25 import org.simantics.spreadsheet.util.SpreadsheetUtils;
27 import it.unimi.dsi.fastutil.longs.AbstractLongList;
28 import it.unimi.dsi.fastutil.longs.LongArrayList;
30 public class CellValueVisitor implements AstValueVisitor<Object> {
32 final public SpreadsheetBook book;
33 final private SpreadsheetEvaluationEnvironment env;
34 final private SpreadsheetCell thisCell;
35 final private LongArrayList references = new LongArrayList();
37 public CellValueVisitor(SpreadsheetEvaluationEnvironment env, SpreadsheetCell thisCell) {
38 this.book = env.getBook();
40 this.thisCell = thisCell;
43 public void addReference(long ref) {
47 public AbstractLongList getReferences() {
52 public Object visit(AstBoolean astBoolean) {
53 return astBoolean.value;
57 public Object visit(AstDouble astFloat) {
58 return astFloat.value;
62 public Object visit(AstInteger astInteger) {
63 return astInteger.value;
67 public Object visit(AstNull astNull) {
68 throw new IllegalStateException();
72 public Object visit(AstString astString) {
73 return astString.value;
77 public Object visit(AstRange astRange) {
80 return FormulaError2.REF.getString();
83 if(astRange.isCell()) {
84 String ref = astRange.first;
85 Range r = SpreadsheetUtils.decodeCell(ref, 0, 0);
86 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
87 SpreadsheetCell cell = thisCell.getBook().get(sheetName, r.startRow, r.startColumn);
89 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
90 SpreadsheetLine line = eng.getLine(r.startRow);
93 line = new SpreadsheetLine(eng.lines, r.startRow);
94 eng.lines.lines.put(-r.startRow, line);
96 cell = SpreadsheetCell.empty(line, r.startColumn);
98 return cell.evaluate(env, this);
102 Object cached = thisCell.getEngine().getCachedRange(astRange);
105 Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second);
106 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
107 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
108 Range r = eng.actualRange(r_);
109 for(int row=0; row < r.height();row++) {
110 SpreadsheetLine line = eng.getLine(r.startRow + row);
112 for(int column=0; column < r.width();column++) {
113 int col = r.startColumn + column;
114 if(line.cells.size() > col) {
115 SpreadsheetCell cell = line.cells.get(r.startColumn + column);
116 //Add references, but do not evaluate if there exists a cached range.
117 addReference(cell.makeReferenceKey());
125 Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second);
126 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
127 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
128 // Full ranges are resolved here
129 Range r = eng.actualRange(r_);
130 SpreadsheetMatrix result = new SpreadsheetMatrix(r.width(), r.height());
131 for(int row=0; row < r.height();row++) {
132 SpreadsheetLine line = eng.getLine(r.startRow + row);
134 for(int column=0; column < r.width();column++) {
135 int col = r.startColumn + column;
136 if(line.cells.size() > col) {
137 SpreadsheetCell cell = line.cells.get(r.startColumn + column);
138 result.values[r.width()*row + column] = cell.evaluate(env, this);
143 thisCell.getEngine().cacheRange(astRange, result);
149 public Object visit(AstArgList astArgList) {
150 throw new IllegalStateException();
154 public Object visit(AstApply astApply) {
155 CellFormulaFunction<?> fn = env.getFunction(astApply.value);
157 return fn.evaluate(this, astApply.args);
159 return FormulaError2.NAME.getString();
164 public Object visit(AstRelation astRelation) {
166 Object leftResult = astRelation.left.accept(this);
167 Object rightResult = astRelation.right.accept(this);
169 FormulaError2 err = FormulaError2.forObject(leftResult);
170 if(err!=null) return err.getString();
171 FormulaError2 err2 = FormulaError2.forObject(rightResult);
172 if(err2!=null) return err2.getString();
174 if(leftResult instanceof Variant){
175 Object leftTemp = ((Variant)leftResult).getValue();
176 Double leftVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftTemp);
177 if(leftVal==null) leftResult = leftTemp.toString();
178 else leftResult = leftVal;
180 if(rightResult instanceof Variant){
181 Object rightTemp = ((Variant)rightResult).getValue();
182 Double rightVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightTemp);
183 if(rightVal==null) rightResult = rightTemp.toString();
184 else rightResult = rightVal;
186 if ((leftResult instanceof String) && (rightResult instanceof String)) {
187 String leftString = (leftResult.toString()).toLowerCase();
188 String rightString = (rightResult.toString()).toLowerCase();
189 if("<".equals(astRelation.op.trim())) return leftString.compareTo(rightString) < 0;
190 else if(">".equals(astRelation.op.trim())) return leftString.compareTo(rightString) > 0;
191 else if("=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) == 0;
192 else if("<>".equals(astRelation.op.trim())) return leftString.compareTo(rightString) != 0 ;
193 else if("<=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) <= 0 ;
194 else if(">=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) >= 0 ;
195 else throw new IllegalStateException();
197 Number leftNumber = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftResult);
198 Number rightNumber = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightResult);
199 if(leftNumber==null || rightNumber==null) return false;
200 if("<".equals(astRelation.op.trim())) return leftNumber.doubleValue() < rightNumber.doubleValue();
201 else if(">".equals(astRelation.op.trim())) return leftNumber.doubleValue() > rightNumber.doubleValue();
202 else if("=".equals(astRelation.op.trim())) return leftNumber.doubleValue() == rightNumber.doubleValue();
203 else if("<>".equals(astRelation.op.trim())) return leftNumber.doubleValue() != rightNumber.doubleValue();
204 else if("<=".equals(astRelation.op.trim())) return leftNumber.doubleValue() <= rightNumber.doubleValue();
205 else if(">=".equals(astRelation.op.trim())) return leftNumber.doubleValue() >= rightNumber.doubleValue();
206 else throw new IllegalStateException();
210 Object leftValueWithPrefix(Object result, AstValue value, String prefix, boolean forceNumber) {
212 Object obj = value.accept(this);
213 FormulaError2 err = FormulaError2.forObject(obj);
214 if(err!=null) return err.getString();
216 if("-".equals(prefix)) {
217 result = SpreadsheetGraphUtils.asNumber(obj);
218 return -((Number)result).doubleValue();
220 if(forceNumber) return SpreadsheetGraphUtils.asNumber(obj);
225 return (Number)Double.parseDouble(result.toString());
226 } catch (NumberFormatException e){
232 public Object visit(AstArithmeticExpression exp) {
233 Object result = null;
235 AstValue value = null;
236 Object acceptedValue = null;
238 for(int i=0;i<exp.rightCount();i++) {
239 String op = exp.rightOp(i);
240 value = exp.rightValue(i);
241 acceptedValue = value.accept(this);
243 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
245 if(!(result instanceof Number)) {
246 FormulaError2 err = FormulaError2.forObject(result);
247 if(err!=null) return err.getString();
249 if(result instanceof String && !((String) result).isEmpty()){
250 return FormulaError2.VALUE.getString();
252 else if(result instanceof Variant){
253 Object val = ((Variant)result).getValue();
254 if(val instanceof String && (val.toString().isEmpty())){
257 Number resNum = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(val);
258 if(resNum==null) return FormulaError2.VALUE.getString();
259 else result = resNum;
266 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
267 if(err2!=null) return err2.getString();
269 other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(acceptedValue);
271 return FormulaError2.handleErrorCall(acceptedValue);
273 result = ((Number)result).doubleValue() + ((Number)other).doubleValue();
275 } else if("-".equals(op)) {
276 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
278 if(!(result instanceof Number)) {
279 FormulaError2 err = FormulaError2.forObject(result);
280 if(err!=null) return err.getString();
282 if(result instanceof String && !((String) result).isEmpty()){
283 return FormulaError2.VALUE.getString();
285 else if(result instanceof Variant){
286 Object val = ((Variant)result).getValue();
287 if(val instanceof String && (val.toString().isEmpty())){
290 Number resNum = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(val);
292 return FormulaError2.VALUE.getString();
293 else result = resNum;
299 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
300 if(err2!=null) return err2.getString();
302 other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(acceptedValue);
304 return FormulaError2.handleErrorCall(acceptedValue);
306 result = ((Number)result).doubleValue() - ((Number)other).doubleValue();
308 } else if("&".equals(op)) {
309 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
310 FormulaError2 err = FormulaError2.forObject(result);
311 if(err!=null) return err.getString();
312 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
313 if(err2!=null) return err2.getString();
315 result = SpreadsheetGraphUtils.asString(result);
316 other = SpreadsheetGraphUtils.asString(acceptedValue);
318 result += (String)other;
321 return leftValueWithPrefix(result, exp.left, exp.prefix, false);
326 public Object visit(AstTerm exp) {
327 Number result = null;
328 for(int i=0;i<exp.rightCount();i++) {
329 String op = exp.rightOp(i);
330 AstValue value = exp.rightValue(i);
331 Object leftValue = exp.left.accept(this);
332 Object rightValue = value.accept(this);
335 result = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftValue);
337 return FormulaError2.handleErrorCall(leftValue);
339 Number other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightValue);
340 if(other==null) return FormulaError2.handleErrorCall(rightValue);
342 result = new Double(result.doubleValue() * other.doubleValue());
344 } else if("/".equals(op)) {
346 result = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftValue);
348 return FormulaError2.handleErrorCall(leftValue);
350 Number other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightValue);
351 if(other==null) return FormulaError2.handleErrorCall(rightValue);
352 if(other.doubleValue()==0.0) return FormulaError2.DIV0.getString();
354 result = new Double(result.doubleValue() / other.doubleValue());
357 if(result == null) result = SpreadsheetGraphUtils.asNumber(exp.left.accept(this));
362 public Object visit(AstFactor exp) {
363 Object result = null;
364 for(int i=0;i<exp.rightCount();i++) {
365 String op = exp.rightOp(i);
366 AstValue value = exp.rightValue(i);
369 Object leftValue = exp.left.accept(this);
371 FormulaError2 err = FormulaError2.forObject(leftValue);
372 if(err!=null) return err.getString();
374 if(leftValue instanceof Variant){
375 Object leftTemp = ((Variant)leftValue).getValue();
376 Double leftV = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftTemp);
377 if(leftV==null) leftValue = leftTemp.toString();
378 else leftValue = leftV;
381 if(leftValue instanceof String){
382 if((leftValue.toString()).isEmpty())
385 return FormulaError2.VALUE.getString();
387 else if(leftValue instanceof SpreadsheetMatrix)
390 result = SpreadsheetGraphUtils.asNumber(leftValue);
392 Object otherValue = value.accept(this);
394 FormulaError2 err2 = FormulaError2.forObject(otherValue);
395 if(err2!=null) return err2.getString();
397 if(otherValue instanceof Variant){
398 Object otherTemp = ((Variant)otherValue).getValue();
399 Double otherV = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(otherTemp);
400 if(otherV==null) otherValue = otherTemp.toString();
401 else otherValue = otherV;
404 if(otherValue instanceof String){
405 if((otherValue.toString()).isEmpty())
408 return FormulaError2.VALUE.getString();
411 if(result instanceof SpreadsheetMatrix) {
412 result = ((SpreadsheetMatrix)result).pow(otherValue);
414 if(otherValue instanceof SpreadsheetMatrix) {
415 throw new IllegalStateException();
417 Double base = ((Number)result).doubleValue();
418 Double exponent = SpreadsheetGraphUtils.asNumber(otherValue);
419 if(exponent==0 && base==0)
420 return FormulaError2.NUM.getString();
421 if(exponent<0 && base==0)
422 return FormulaError2.DIV0.getString();
423 result = Math.pow(base, exponent);
424 if(result instanceof Double && Double.isInfinite((Double)result)){
425 return FormulaError2.NUM.getString();
431 if(result == null) result = SpreadsheetGraphUtils.asNumber(exp.left.accept(this));
436 public Object visit(AstIdentifier id) {
437 return FormulaError2.NAME.getString();
438 //throw new IllegalStateException();
442 public Object visit(AstArray array) {
443 SpreadsheetMatrix m = new SpreadsheetMatrix(array.values.size(), 1);
444 for(int i=0;i<array.values.size();i++) {
445 m.values[i] = array.values.get(i).accept(this);
451 public Object visit(AstNothing array) {
452 return AstNothing.NOTHING;
456 public Object visit(AstArrayFormulaReference ref) {
458 Range thisRange = SpreadsheetUtils.decodeRange(thisCell.getName());
459 Range arrayRange = SpreadsheetUtils.decodeRange(ref.range);
460 int x = thisRange.startColumn - arrayRange.startColumn;
461 int y = thisRange.startRow - arrayRange.startRow;
463 SpreadsheetMatrix m = (SpreadsheetMatrix)ref.value.accept(this);