SpreadsheetCells with Circular References support iterations.
[simantics/platform.git] / bundles / org.simantics.spreadsheet.graph / src / org / simantics / spreadsheet / graph / SpreadsheetCell.java
1 package org.simantics.spreadsheet.graph;\r
2 \r
3 import java.util.Collections;\r
4 import java.util.HashMap;\r
5 import java.util.List;\r
6 import java.util.Map;\r
7 import java.util.Optional;\r
8 \r
9 import org.simantics.spreadsheet.graph.formula.FormulaError2;\r
10 import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;\r
11 import org.simantics.spreadsheet.graph.parser.ast.AstValue;\r
12 import org.simantics.spreadsheet.resource.SpreadsheetResource;\r
13 import org.simantics.spreadsheet.util.SpreadsheetUtils;\r
14 \r
15 public class SpreadsheetCell implements SpreadsheetElement, SheetNode {\r
16 \r
17         private static final long serialVersionUID = 6616793596542239339L;\r
18 \r
19         public static SpreadsheetCell EMPTY;\r
20         \r
21         static {\r
22             EMPTY = new SpreadsheetCell(null, -1);\r
23             EMPTY.setContent("");\r
24             EMPTY.setStyle(SpreadsheetStyle.empty().getStyleId());\r
25         }\r
26         \r
27         private boolean inProgress = false;\r
28         private int iterations = 0;\r
29         \r
30         final private SpreadsheetLine line;\r
31         final private int column;\r
32         int style;\r
33         Object content;\r
34         \r
35         public SpreadsheetCell(SpreadsheetLine line, int column) {\r
36                 this.line = line;\r
37                 this.column = column;\r
38         }\r
39 \r
40     public boolean hasExpression() {\r
41                 return content instanceof SpreadsheetFormula || content instanceof SpreadsheetSCLConstant; \r
42         }\r
43         \r
44         public void setContent(Object newContent) {\r
45 //       if(newContent != null) {\r
46 //            if (!(newContent instanceof Serializable)) {\r
47 //                throw new AssertionError("content not instanceof Serializable but it is " + newContent.getClass().getSimpleName());\r
48 //            }\r
49 //        }\r
50             if (GraphUI.DEBUG)\r
51                 System.out.println("SpreadsheetCell.setContent "+ this + " " + newContent);\r
52                 this.content = newContent;\r
53         }\r
54 \r
55         @Override\r
56         public String getName() {\r
57                 return SpreadsheetUtils.cellName(line.row, column);\r
58         }\r
59 \r
60         @Override\r
61         public Map<?, ?> getChildren() {\r
62                 return Collections.emptyMap();\r
63         }\r
64         \r
65         private static String[] keys = { "typeURI", "content" };\r
66         \r
67         @Override\r
68         public Map<String, SheetNode> getProperties() {\r
69             Map<String, SheetNode> properties = new HashMap<>();\r
70             \r
71             if (GraphUI.DEBUG)\r
72                 System.out.println("SpreadsheetCell.getProperties: " + this + " " + content + " " + style);\r
73             \r
74             properties.put("typeURI", new SpreadsheetTypeNode(SpreadsheetResource.URIs.Cell));\r
75             properties.put("content", new SpreadsheetCellContent(this));\r
76         properties.put("style", new SpreadsheetCellStyle(this));\r
77         properties.put("editable", new SpreadsheetCellEditable(this));\r
78             return properties;\r
79         }\r
80         \r
81         public SpreadsheetBook getBook() {\r
82                 return line.getEngine().getBook();\r
83         }\r
84 \r
85         public SpreadsheetEngine getEngine() {\r
86                 return line.getEngine();\r
87         }\r
88 \r
89         public <T> T evaluate(SpreadsheetEvaluationEnvironment env) {\r
90                 return evaluate(env, null);\r
91         }\r
92 \r
93         public <T> T evaluate(SpreadsheetEvaluationEnvironment env, CellValueVisitor caller) {\r
94 //          System.err.println(getEngine().getName() + ":" + getName() + ": evaluate");\r
95                 if(caller != null)\r
96                         caller.addReference(makeReferenceKey());\r
97                 if(content instanceof SpreadsheetFormula) {\r
98                         SpreadsheetFormula f = (SpreadsheetFormula)content;\r
99                         if(f.result == null) {\r
100                                 CellValueVisitor visitor = new CellValueVisitor(env, this);\r
101                                 AstValue value = ((SpreadsheetFormula)content).value;\r
102                                 if(this.inProgress == true) this.iterations++;\r
103                                 \r
104                                 if(!env.getBook().isIterationEnabled()){\r
105                                         if(this.inProgress == false){\r
106                                                 this.inProgress = true;\r
107                                                 f.result = value.accept(visitor);\r
108                                         }\r
109                                         else f.result = FormulaError2.CIRCULAR_REF.getString();\r
110                                 }\r
111                                 else if(this.iterations<env.iterationLimit){\r
112                                         this.inProgress = true;\r
113                                         f.result = value.accept(visitor);\r
114                                 }\r
115                                 else {\r
116                                         if(f.result==null)\r
117                                                 f.result = 0.0;\r
118                                 }\r
119                                 \r
120                                 env.getBook().registerReferences(makeReferenceKey(), visitor.getReferences());\r
121                         }\r
122                         this.inProgress = false;\r
123                         this.iterations = 0;\r
124                         return (T)f.result;\r
125                 } else if (content instanceof SpreadsheetSCLConstant) {\r
126                     SpreadsheetSCLConstant sclConstant = (SpreadsheetSCLConstant) content;\r
127                     return (T) sclConstant.content;\r
128                 } else {\r
129                         this.inProgress = false;\r
130                         return (T)content;\r
131                 }\r
132         }\r
133         \r
134         public long makeReferenceKey() {\r
135                 SpreadsheetBook book = getBook();\r
136                 SpreadsheetEngine engine = getEngine();\r
137                 long engineIndex = book.getEngineIndex(engine);\r
138                 long row = line.row;\r
139                 long col = column;\r
140                 return (engineIndex << 40) + (row << 20) + col; \r
141         }\r
142         \r
143         public void invalidate() {\r
144                 getEngine().rangeCache = null;\r
145                 if(content instanceof SpreadsheetFormula) {\r
146                         SpreadsheetFormula f = (SpreadsheetFormula)content;\r
147                         f.result = null;\r
148                 }\r
149         }\r
150 \r
151         @Override\r
152         public void accept(SpreadsheetVisitor v) {\r
153                 v.visit(this);\r
154         }\r
155 \r
156     @Override\r
157     public Optional<SpreadsheetElement> getParent() {\r
158         return Optional.of(line);\r
159     }\r
160 \r
161     @Override\r
162     public List<SpreadsheetElement> getSpreadsheetChildren() {\r
163         return Collections.emptyList();\r
164     }\r
165 \r
166     @Override\r
167     public void remove(SpreadsheetElement child) {\r
168         // TODO Auto-generated method stub\r
169         \r
170     }\r
171 \r
172     @Override\r
173     public int hashCode() {\r
174         final int prime = 31;\r
175         int result = 1;\r
176         result = prime * result + column;\r
177         result = prime * result + ((line == null) ? 0 : line.hashCode());\r
178         return result;\r
179     }\r
180 \r
181     @Override\r
182     public boolean equals(Object obj) {\r
183         if (this == obj)\r
184             return true;\r
185         if (obj == null)\r
186             return false;\r
187         if (getClass() != obj.getClass())\r
188             return false;\r
189         SpreadsheetCell other = (SpreadsheetCell) obj;\r
190         if (column != other.column)\r
191             return false;\r
192         if (line == null) {\r
193             if (other.line != null)\r
194                 return false;\r
195         } else if (!line.equals(other.line))\r
196             return false;\r
197         return true;\r
198     }\r
199 \r
200     public void setStyle(int styleId) {\r
201         this.style = styleId;\r
202     }\r
203 \r
204     public int getStyle() {\r
205         return style;\r
206     }\r
207 \r
208     public Object getContent() {\r
209         return content;\r
210     }\r
211     \r
212     public static SpreadsheetCell empty(SpreadsheetLine line, int column) {\r
213         SpreadsheetCell cell =  new SpreadsheetCell(line, column);\r
214         cell.setContent("");\r
215         cell.setStyle(SpreadsheetStyle.empty().getStyleId());\r
216         return cell;\r
217     }\r
218 \r
219 }\r