1 package org.simantics.spreadsheet.graph;
\r
3 import org.simantics.databoard.binding.mutable.Variant;
\r
4 import org.simantics.spreadsheet.Range;
\r
5 import org.simantics.spreadsheet.graph.formula.FormulaError2;
\r
6 import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;
\r
7 import org.simantics.spreadsheet.graph.parser.ast.AstApply;
\r
8 import org.simantics.spreadsheet.graph.parser.ast.AstArgList;
\r
9 import org.simantics.spreadsheet.graph.parser.ast.AstArithmeticExpression;
\r
10 import org.simantics.spreadsheet.graph.parser.ast.AstArray;
\r
11 import org.simantics.spreadsheet.graph.parser.ast.AstArrayFormulaReference;
\r
12 import org.simantics.spreadsheet.graph.parser.ast.AstBoolean;
\r
13 import org.simantics.spreadsheet.graph.parser.ast.AstDouble;
\r
14 import org.simantics.spreadsheet.graph.parser.ast.AstFactor;
\r
15 import org.simantics.spreadsheet.graph.parser.ast.AstIdentifier;
\r
16 import org.simantics.spreadsheet.graph.parser.ast.AstInteger;
\r
17 import org.simantics.spreadsheet.graph.parser.ast.AstNothing;
\r
18 import org.simantics.spreadsheet.graph.parser.ast.AstNull;
\r
19 import org.simantics.spreadsheet.graph.parser.ast.AstRange;
\r
20 import org.simantics.spreadsheet.graph.parser.ast.AstRelation;
\r
21 import org.simantics.spreadsheet.graph.parser.ast.AstString;
\r
22 import org.simantics.spreadsheet.graph.parser.ast.AstTerm;
\r
23 import org.simantics.spreadsheet.graph.parser.ast.AstValue;
\r
24 import org.simantics.spreadsheet.graph.parser.ast.AstValueVisitor;
\r
25 import org.simantics.spreadsheet.util.SpreadsheetUtils;
\r
27 import it.unimi.dsi.fastutil.longs.AbstractLongList;
\r
28 import it.unimi.dsi.fastutil.longs.LongArrayList;
\r
30 public class CellValueVisitor implements AstValueVisitor<Object> {
\r
32 final public SpreadsheetBook book;
\r
33 final private SpreadsheetEvaluationEnvironment env;
\r
34 final private SpreadsheetCell thisCell;
\r
35 final private LongArrayList references = new LongArrayList();
\r
37 public CellValueVisitor(SpreadsheetEvaluationEnvironment env, SpreadsheetCell thisCell) {
\r
38 this.book = env.getBook();
\r
40 this.thisCell = thisCell;
\r
43 public void addReference(long ref) {
\r
44 references.add(ref);
\r
47 public AbstractLongList getReferences() {
\r
52 public Object visit(AstBoolean astBoolean) {
\r
53 return astBoolean.value;
\r
57 public Object visit(AstDouble astFloat) {
\r
58 return astFloat.value;
\r
62 public Object visit(AstInteger astInteger) {
\r
63 return astInteger.value;
\r
67 public Object visit(AstNull astNull) {
\r
68 throw new IllegalStateException();
\r
72 public Object visit(AstString astString) {
\r
73 return astString.value;
\r
77 public Object visit(AstRange astRange) {
\r
79 if(astRange.isRef()){
\r
80 return FormulaError2.REF.getString();
\r
83 if(astRange.isCell()) {
\r
84 String ref = astRange.first;
\r
85 Range r = SpreadsheetUtils.decodeCell(ref, 0, 0);
\r
86 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
\r
87 SpreadsheetCell cell = thisCell.getBook().get(sheetName, r.startRow, r.startColumn);
\r
89 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
\r
90 SpreadsheetLine line = eng.getLine(r.startRow);
\r
93 line = new SpreadsheetLine(eng.lines, r.startRow);
\r
94 eng.lines.lines.put(-r.startRow, line);
\r
96 cell = SpreadsheetCell.empty(line, r.startColumn);
\r
98 return cell.evaluate(env, this);
\r
102 Object cached = thisCell.getEngine().getCachedRange(astRange);
\r
103 if(cached != null) {
\r
105 Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second);
\r
106 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
\r
107 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
\r
108 Range r = eng.actualRange(r_);
\r
109 for(int row=0; row < r.height();row++) {
\r
110 SpreadsheetLine line = eng.getLine(r.startRow + row);
\r
112 for(int column=0; column < r.width();column++) {
\r
113 int col = r.startColumn + column;
\r
114 if(line.cells.size() > col) {
\r
115 SpreadsheetCell cell = line.cells.get(r.startColumn + column);
\r
116 //Add references, but do not evaluate if there exists a cached range.
\r
117 addReference(cell.makeReferenceKey());
\r
125 Range r_ = SpreadsheetUtils.decodeRange(astRange.first + ":" + astRange.second);
\r
126 String sheetName = astRange.sheetName != null ? astRange.sheetName : thisCell.getEngine().getName();
\r
127 SpreadsheetEngine eng = thisCell.getBook().getEngine(sheetName);
\r
128 // Full ranges are resolved here
\r
129 Range r = eng.actualRange(r_);
\r
130 SpreadsheetMatrix result = new SpreadsheetMatrix(r.width(), r.height());
\r
131 for(int row=0; row < r.height();row++) {
\r
132 SpreadsheetLine line = eng.getLine(r.startRow + row);
\r
134 for(int column=0; column < r.width();column++) {
\r
135 int col = r.startColumn + column;
\r
136 if(line.cells.size() > col) {
\r
137 SpreadsheetCell cell = line.cells.get(r.startColumn + column);
\r
138 result.values[r.width()*row + column] = cell.evaluate(env, this);
\r
143 thisCell.getEngine().cacheRange(astRange, result);
\r
149 public Object visit(AstArgList astArgList) {
\r
150 throw new IllegalStateException();
\r
154 public Object visit(AstApply astApply) {
\r
155 CellFormulaFunction<?> fn = env.getFunction(astApply.value);
\r
157 return fn.evaluate(this, astApply.args);
\r
159 return FormulaError2.NAME.getString();
\r
164 public Object visit(AstRelation astRelation) {
\r
166 Object leftResult = astRelation.left.accept(this);
\r
167 Object rightResult = astRelation.right.accept(this);
\r
169 FormulaError2 err = FormulaError2.forObject(leftResult);
\r
170 if(err!=null) return err.getString();
\r
171 FormulaError2 err2 = FormulaError2.forObject(rightResult);
\r
172 if(err2!=null) return err2.getString();
\r
174 if(leftResult instanceof Variant){
\r
175 Object leftTemp = ((Variant)leftResult).getValue();
\r
176 Double leftVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftTemp);
\r
177 if(leftVal==null) leftResult = leftTemp.toString();
\r
178 else leftResult = leftVal;
\r
180 if(rightResult instanceof Variant){
\r
181 Object rightTemp = ((Variant)rightResult).getValue();
\r
182 Double rightVal = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightTemp);
\r
183 if(rightVal==null) rightResult = rightTemp.toString();
\r
184 else rightResult = rightVal;
\r
187 if ((leftResult instanceof String) || (rightResult instanceof String)) {
\r
188 String leftString = (leftResult.toString()).toLowerCase();
\r
189 String rightString = (rightResult.toString()).toLowerCase();
\r
190 if("<".equals(astRelation.op.trim())) return leftString.compareTo(rightString) < 0;
\r
191 else if(">".equals(astRelation.op.trim())) return leftString.compareTo(rightString) > 0;
\r
192 // empty string should equal zero (TODO: this is a hack, the proper fix should be somewhere earlier so other cases would work as well)
\r
193 else if("=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) == 0 || emptyAndZero(leftString, rightString) || emptyAndZero(rightString, leftString);
\r
194 else if("<>".equals(astRelation.op.trim())) return leftString.compareTo(rightString) != 0 ;
\r
195 else if("<=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) <= 0 ;
\r
196 else if(">=".equals(astRelation.op.trim())) return leftString.compareTo(rightString) >= 0 ;
\r
197 else throw new IllegalStateException();
\r
199 Number leftNumber = SpreadsheetGraphUtils.asNumber(leftResult);
\r
200 Number rightNumber = SpreadsheetGraphUtils.asNumber(rightResult);
\r
201 if("<".equals(astRelation.op.trim())) return leftNumber.doubleValue() < rightNumber.doubleValue();
\r
202 else if(">".equals(astRelation.op.trim())) return leftNumber.doubleValue() > rightNumber.doubleValue();
\r
203 else if("=".equals(astRelation.op.trim())) return leftNumber.doubleValue() == rightNumber.doubleValue();
\r
204 else if("<>".equals(astRelation.op.trim())) return leftNumber.doubleValue() != rightNumber.doubleValue();
\r
205 else if("<=".equals(astRelation.op.trim())) return leftNumber.doubleValue() <= rightNumber.doubleValue();
\r
206 else if(">=".equals(astRelation.op.trim())) return leftNumber.doubleValue() >= rightNumber.doubleValue();
\r
207 else throw new IllegalStateException();
\r
211 private static boolean emptyAndZero(String a, String b) {
\r
213 return a.isEmpty() && (Double.parseDouble(b) == 0);
\r
215 catch (NumberFormatException e) {
\r
220 Object leftValueWithPrefix(Object result, AstValue value, String prefix, boolean forceNumber) {
\r
221 if(result == null) {
\r
222 Object obj = value.accept(this);
\r
223 FormulaError2 err = FormulaError2.forObject(obj);
\r
224 if(err!=null) return err.getString();
\r
226 if("-".equals(prefix)) {
\r
227 result = SpreadsheetGraphUtils.asNumber(obj);
\r
228 return -((Number)result).doubleValue();
\r
230 if(forceNumber) return SpreadsheetGraphUtils.asNumber(obj);
\r
235 return (Number)Double.parseDouble(result.toString());
\r
236 } catch (NumberFormatException e){
\r
242 public Object visit(AstArithmeticExpression exp) {
\r
243 Object result = null;
\r
244 Object other = null;
\r
245 AstValue value = null;
\r
246 Object acceptedValue = null;
\r
248 for(int i=0;i<exp.rightCount();i++) {
\r
249 String op = exp.rightOp(i);
\r
250 value = exp.rightValue(i);
\r
251 acceptedValue = value.accept(this);
\r
252 if("+".equals(op)) {
\r
253 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
\r
255 if(!(result instanceof Number)) {
\r
256 FormulaError2 err = FormulaError2.forObject(result);
\r
257 if(err!=null) return err.getString();
\r
259 if(result instanceof String && !((String) result).isEmpty()){
\r
260 return FormulaError2.VALUE.getString();
\r
262 else if(result instanceof Variant){
\r
263 Object val = ((Variant)result).getValue();
\r
264 if(val instanceof String && (val.toString().isEmpty())){
\r
267 Number resNum = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(val);
\r
268 if(resNum==null) return FormulaError2.VALUE.getString();
\r
269 else result = resNum;
\r
276 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
\r
277 if(err2!=null) return err2.getString();
\r
279 other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(acceptedValue);
\r
281 return FormulaError2.handleErrorCall(acceptedValue);
\r
283 result = ((Number)result).doubleValue() + ((Number)other).doubleValue();
\r
285 } else if("-".equals(op)) {
\r
286 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
\r
288 if(!(result instanceof Number)) {
\r
289 FormulaError2 err = FormulaError2.forObject(result);
\r
290 if(err!=null) return err.getString();
\r
292 if(result instanceof String && !((String) result).isEmpty()){
\r
293 return FormulaError2.VALUE.getString();
\r
295 else if(result instanceof Variant){
\r
296 Object val = ((Variant)result).getValue();
\r
297 if(val instanceof String && (val.toString().isEmpty())){
\r
300 Number resNum = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(val);
\r
302 return FormulaError2.VALUE.getString();
\r
303 else result = resNum;
\r
309 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
\r
310 if(err2!=null) return err2.getString();
\r
312 other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(acceptedValue);
\r
314 return FormulaError2.handleErrorCall(acceptedValue);
\r
316 result = ((Number)result).doubleValue() - ((Number)other).doubleValue();
\r
318 } else if("&".equals(op)) {
\r
319 result = leftValueWithPrefix(result, exp.left, exp.prefix, false);
\r
320 FormulaError2 err = FormulaError2.forObject(result);
\r
321 if(err!=null) return err.getString();
\r
322 FormulaError2 err2 = FormulaError2.forObject(acceptedValue);
\r
323 if(err2!=null) return err2.getString();
\r
325 result = SpreadsheetGraphUtils.asString(result);
\r
326 other = SpreadsheetGraphUtils.asString(acceptedValue);
\r
328 result += (String)other;
\r
331 return leftValueWithPrefix(result, exp.left, exp.prefix, false);
\r
336 public Object visit(AstTerm exp) {
\r
337 Number result = null;
\r
338 for(int i=0;i<exp.rightCount();i++) {
\r
339 String op = exp.rightOp(i);
\r
340 AstValue value = exp.rightValue(i);
\r
341 Object leftValue = exp.left.accept(this);
\r
342 Object rightValue = value.accept(this);
\r
343 if("*".equals(op)) {
\r
344 if(result == null) {
\r
345 result = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftValue);
\r
347 return FormulaError2.handleErrorCall(leftValue);
\r
349 Number other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightValue);
\r
350 if(other==null) return FormulaError2.handleErrorCall(rightValue);
\r
352 result = new Double(result.doubleValue() * other.doubleValue());
\r
354 } else if("/".equals(op)) {
\r
355 if(result == null) {
\r
356 result = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftValue);
\r
358 return FormulaError2.handleErrorCall(leftValue);
\r
360 Number other = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(rightValue);
\r
361 if(other==null) return FormulaError2.handleErrorCall(rightValue);
\r
362 if(other.doubleValue()==0.0) return FormulaError2.DIV0.getString();
\r
364 result = new Double(result.doubleValue() / other.doubleValue());
\r
367 if(result == null) result = SpreadsheetGraphUtils.asNumber(exp.left.accept(this));
\r
372 public Object visit(AstFactor exp) {
\r
373 Object result = null;
\r
374 for(int i=0;i<exp.rightCount();i++) {
\r
375 String op = exp.rightOp(i);
\r
376 AstValue value = exp.rightValue(i);
\r
377 if("^".equals(op)) {
\r
378 if(result == null) {
\r
379 Object leftValue = exp.left.accept(this);
\r
381 FormulaError2 err = FormulaError2.forObject(leftValue);
\r
382 if(err!=null) return err.getString();
\r
384 if(leftValue instanceof Variant){
\r
385 Object leftTemp = ((Variant)leftValue).getValue();
\r
386 Double leftV = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(leftTemp);
\r
387 if(leftV==null) leftValue = leftTemp.toString();
\r
388 else leftValue = leftV;
\r
391 if(leftValue instanceof String){
\r
392 if((leftValue.toString()).isEmpty())
\r
395 return FormulaError2.VALUE.getString();
\r
397 else if(leftValue instanceof SpreadsheetMatrix)
\r
398 result = leftValue;
\r
400 result = SpreadsheetGraphUtils.asNumber(leftValue);
\r
402 Object otherValue = value.accept(this);
\r
404 FormulaError2 err2 = FormulaError2.forObject(otherValue);
\r
405 if(err2!=null) return err2.getString();
\r
407 if(otherValue instanceof Variant){
\r
408 Object otherTemp = ((Variant)otherValue).getValue();
\r
409 Double otherV = SpreadsheetGraphUtils.asDoubleWhereEmptyStringIsZero(otherTemp);
\r
410 if(otherV==null) otherValue = otherTemp.toString();
\r
411 else otherValue = otherV;
\r
414 if(otherValue instanceof String){
\r
415 if((otherValue.toString()).isEmpty())
\r
418 return FormulaError2.VALUE.getString();
\r
421 if(result instanceof SpreadsheetMatrix) {
\r
422 result = ((SpreadsheetMatrix)result).pow(otherValue);
\r
424 if(otherValue instanceof SpreadsheetMatrix) {
\r
425 throw new IllegalStateException();
\r
427 Double base = ((Number)result).doubleValue();
\r
428 Double exponent = SpreadsheetGraphUtils.asNumber(otherValue);
\r
429 if(exponent==0 && base==0)
\r
430 return FormulaError2.NUM.getString();
\r
431 if(exponent<0 && base==0)
\r
432 return FormulaError2.DIV0.getString();
\r
433 result = Math.pow(base, exponent);
\r
434 if(result instanceof Double && Double.isInfinite((Double)result)){
\r
435 return FormulaError2.NUM.getString();
\r
441 if(result == null) result = SpreadsheetGraphUtils.asNumber(exp.left.accept(this));
\r
446 public Object visit(AstIdentifier id) {
\r
447 return FormulaError2.NAME.getString();
\r
448 //throw new IllegalStateException();
\r
452 public Object visit(AstArray array) {
\r
453 SpreadsheetMatrix m = new SpreadsheetMatrix(array.values.size(), 1);
\r
454 for(int i=0;i<array.values.size();i++) {
\r
455 m.values[i] = array.values.get(i).accept(this);
\r
461 public Object visit(AstNothing array) {
\r
462 return AstNothing.NOTHING;
\r
466 public Object visit(AstArrayFormulaReference ref) {
\r
468 Range thisRange = SpreadsheetUtils.decodeRange(thisCell.getName());
\r
469 Range arrayRange = SpreadsheetUtils.decodeRange(ref.range);
\r
470 int x = thisRange.startColumn - arrayRange.startColumn;
\r
471 int y = thisRange.startRow - arrayRange.startRow;
\r
473 SpreadsheetMatrix m = (SpreadsheetMatrix)ref.value.accept(this);
\r
474 return m.get(y, x);
\r