]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetBook.java
Spreadsheet updates cell values properly
[simantics/platform.git] / bundles / org.simantics.spreadsheet.graph / src / org / simantics / spreadsheet / graph / SpreadsheetBook.java
1 package org.simantics.spreadsheet.graph;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Optional;
13 import java.util.Set;
14
15 import org.simantics.databoard.Bindings;
16 import org.simantics.databoard.binding.Binding;
17 import org.simantics.databoard.binding.mutable.Variant;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.simulator.toolkit.StandardNodeManagerSupport;
20 import org.simantics.simulator.variable.exceptions.NodeManagerException;
21 import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;
22 import org.simantics.spreadsheet.graph.synchronization.LineNodeUpdater;
23 import org.simantics.spreadsheet.graph.synchronization.LineUpdater;
24 import org.simantics.spreadsheet.graph.synchronization.NullUpdater;
25 import org.simantics.spreadsheet.graph.synchronization.SheetLineComponent;
26 import org.simantics.spreadsheet.graph.synchronization.StyleUpdater;
27 import org.simantics.structural.synchronization.base.ComponentFactory;
28 import org.simantics.structural.synchronization.base.MappingBase;
29 import org.simantics.structural.synchronization.base.ModuleUpdaterBase;
30 import org.simantics.structural.synchronization.base.ModuleUpdaterFactoryBase;
31 import org.simantics.structural.synchronization.base.Solver;
32 import org.simantics.structural.synchronization.base.SolverNameUtil;
33
34 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
35 import it.unimi.dsi.fastutil.longs.AbstractLongList;
36 import it.unimi.dsi.fastutil.longs.AbstractLongSet;
37 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
38 import it.unimi.dsi.fastutil.longs.LongArraySet;
39 import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
40
41 public class SpreadsheetBook implements StandardNodeManagerSupport<SheetNode>, SpreadsheetElement<SpreadsheetElement, SpreadsheetElement>, Serializable, SheetNode<SpreadsheetEngine, SheetNode>, Solver, SolverNameUtil, ComponentFactory<SheetLineComponent>, ModuleUpdaterFactoryBase<SheetLineComponent> {
42
43         private static final long serialVersionUID = 7417208688311691396L;
44         
45         public Serializable NotAvailableError = new Serializable() {};
46         
47         public Long2ObjectOpenHashMap<AbstractLongSet> referenceMap = new Long2ObjectOpenHashMap<>();
48         
49         private Int2ObjectArrayMap<SpreadsheetStyle> styles = new Int2ObjectArrayMap<>();
50 //      public ObjectArrayList<SpreadsheetStyle> styles = new ObjectArrayList<SpreadsheetStyle>();
51         public ArrayList<SpreadsheetEngine> sheets = new ArrayList<SpreadsheetEngine>();
52         
53         private SpreadsheetMapping mapping;
54         
55         
56         private int idCounter = 1;
57         
58         public SpreadsheetBook() {
59                 getNewId(this);
60                 mapping = new SpreadsheetMapping(new SheetLineComponent(""));
61         }
62         
63         public Map<Integer, SpreadsheetElement> children = new HashMap<>();
64
65         private boolean iterationEnabled;
66
67         public int getNewId(SpreadsheetElement element) {
68                 int result = idCounter++;
69                 children.put(result, element);
70                 return result; 
71         }
72         
73         public long getEngineIndex(SpreadsheetEngine engine) {
74                 for(int i=0;i<sheets.size();i++)
75                         if(sheets.get(i) == engine) return i;
76                 throw new IllegalStateException("Did not find sheet " + engine.getName());
77         }
78         
79         public void registerReferences(long cell, AbstractLongList refs) {
80                 for(int i=0;i<refs.size();i++) {
81                         long key = refs.getLong(i);
82                         AbstractLongSet set = referenceMap.get(key);
83                         if(set == null) {
84                                 set = new LongArraySet();
85                                 referenceMap.put(key, set);
86                         }
87                         if(set.size() == 5) {
88                                 AbstractLongSet newSet = new LongLinkedOpenHashSet();
89                                 newSet.addAll(set);
90                                 set = newSet;
91                                 referenceMap.put(key, set);
92                         }
93                         set.add(cell);
94                 }
95         }
96         
97         @Override
98         public Binding getEngineBinding(SheetNode node) throws NodeManagerException {
99                 Object value = getEngineValue(node);
100                 if(value instanceof Variant) return Bindings.VARIANT;
101                 if(value instanceof String) return Bindings.STRING;
102                 if(value instanceof Boolean) return Bindings.BOOLEAN;
103                 else return Bindings.VOID;
104                 
105         }
106         
107         @Override
108         public Object getEngineValue(SheetNode node) {
109                 if(node instanceof SpreadsheetCellContent) {
110                         try {
111                                 SpreadsheetCellContent scc = (SpreadsheetCellContent)node;
112                                 Object content = scc.cell.evaluate(SpreadsheetEvaluationEnvironment.getInstance(this));
113                                 if(content == null) return Variant.ofInstance("");
114                                 if(content instanceof Variant) return content;
115                                 else return Variant.ofInstance(content);
116                         } catch (Throwable t) {
117                                 t.printStackTrace();
118                                 return Variant.ofInstance(t.toString());
119                         }
120                 } else if (node instanceof SpreadsheetCellContentExpression) {
121                         SpreadsheetCellContentExpression scce = (SpreadsheetCellContentExpression)node;
122                         if (scce.cell.content instanceof SpreadsheetFormula) {
123                         SpreadsheetFormula formula = (SpreadsheetFormula)scce.cell.content; 
124                         return formula.expression;
125                         } else if (scce.cell.content instanceof SpreadsheetSCLConstant) {
126                             SpreadsheetSCLConstant sclConstant = (SpreadsheetSCLConstant) scce.cell.content;
127                             return "=" + sclConstant.expression;
128                         } else {
129                             System.out.println("Broken SpreadsheetCellContentExpression possibly due to overwriting an existing expression with a constant or something else (current content is " + scce.cell.content + ")");
130                             if (scce.cell.content instanceof Variant) {
131                                 return scce.cell.content;
132                             } else {
133                                 return Variant.ofInstance(scce.cell.content);
134                             }
135                         }
136                 } else if (node instanceof SpreadsheetTypeNode) {
137                         SpreadsheetTypeNode stn = (SpreadsheetTypeNode)node;
138                         return stn.uri;
139                 } else if (node instanceof SpreadsheetCellStyle) {
140                     int styleId = ((SpreadsheetCellStyle) node).cell.style;
141                     SpreadsheetStyle style = getStyle(styleId);
142                     if (style == null) {
143                         style = SpreadsheetStyle.empty();
144                         if (styleId != style.getStyleId())
145                             new Exception("different style ids!" + styleId + "  " + style.getStyleId()).printStackTrace();
146                         addStyle(style);
147                     }
148                     return style;
149                 } else if (node instanceof SpreadsheetCellEditable) {
150                     boolean editable = ((SpreadsheetCellEditable) node).editable();
151                     return editable;
152                 }
153                 return null;
154         }
155
156         @Override
157         public void setEngineValue(SheetNode node, Object value) {
158         }
159
160         @Override
161         public String getName(SheetNode node) {
162                 return node.getName();
163         }
164
165         @Override
166         public Map<String, SheetNode> getChildren(SheetNode node) {
167                 return node.getChildren();
168         }
169
170         @Override
171         public Map<String, SheetNode> getProperties(SheetNode node) {
172                 return node.getProperties();
173         }
174
175         @Override
176         public String getName() {
177                 return "";
178         }
179
180         @Override
181         public Map<String, SpreadsheetEngine> getChildren() {
182                 Map<String,SpreadsheetEngine> result = new HashMap<String,SpreadsheetEngine>();
183                 for(SpreadsheetEngine engine : sheets)
184                         result.put(engine.getName(), engine);
185                 return result;
186         }
187
188         @Override
189         public Map<String, SheetNode> getProperties() {
190                 return Collections.emptyMap();
191         }
192         
193         public SpreadsheetCell get(String sheet, int row, int column) {
194                 SpreadsheetEngine engine = getEngine(sheet);
195                 if(engine == null) return null;
196                 SpreadsheetLine line = engine.getLine(row);
197                 if(line == null) return null;
198                 if(line.cells.size() <= column) return null;
199                 return line.cells.get(column);
200         }
201
202         public SpreadsheetCell get(SpreadsheetEngine engine, int row, int column) {
203                 SpreadsheetLine line = engine.getLine(row);
204                 if(line == null) return null;
205                 if(line.cells.size() <= column) return null;
206                 return line.cells.get(column);
207         }
208
209         public SpreadsheetEngine getEngine(String sheet) {
210                 for(SpreadsheetEngine engine : sheets)
211                         if(sheet.equals(engine.getName())) return engine;
212                 return null;
213         }
214
215         @Override
216         public ModuleUpdaterBase<SheetLineComponent> createUpdater(String id) throws DatabaseException {
217                 if("http://www.simantics.org/Spreadsheet-1.2/Line".equals(id))
218                         return new LineUpdater(id);
219                 else if("http://www.simantics.org/Spreadsheet-1.2/LineNode".equals(id))
220                         return new LineNodeUpdater(id);
221                 else if ("http://www.simantics.org/Spreadsheet-1.2/Style".equals(id))
222                     return new StyleUpdater(id);
223                 else if("http://www.simantics.org/Spreadsheet-1.2/Lines".equals(id))
224                         return new NullUpdater(id);
225                 else if("http://www.simantics.org/Spreadsheet-1.2/Spreadsheet".equals(id))
226                         return new NullUpdater(id);
227                 else if("http://www.simantics.org/Spreadsheet-1.2/Book".equals(id))
228                         return new NullUpdater(id);
229                 else
230                         throw new IllegalStateException("createUpdater " + id);
231         }
232
233         @Override
234         public SheetLineComponent create(String uid) {
235                 return new SheetLineComponent(uid);
236         }
237
238         @Override
239         public String getFreshName(String parentName, String name) {
240                 return parentName + "/" + name;
241         }
242
243         @Override
244         public String ensureNameIsVariationOf(String parentName, int id, String name) {
245             if (parentName.isEmpty())
246                 return name;
247                 return parentName + "/" + name;
248         }
249
250         final static int COMP_ROOT_POS = "COMP_ROOT/".length();
251         
252         @Override
253         public int getId(String name) {
254                 
255                 if("COMP_ROOT".equals(name)) return 1;
256
257                 String path = name.substring(COMP_ROOT_POS);
258                 String[] parts = path.split("/");
259                 Object o = resolve(parts, 0);
260                 if(o instanceof SpreadsheetLines) {
261                         return ((SpreadsheetLines)o).getId();
262                 } else if(o instanceof SpreadsheetLine) {
263                         return ((SpreadsheetLine)o).getId();
264                 } else if(o instanceof SpreadsheetEngine) {
265                         return ((SpreadsheetEngine)o).getId();
266                 } else if (o instanceof SpreadsheetStyle) {
267                     return ((SpreadsheetStyle) o).getId();
268                 }
269                 
270                 throw new IllegalStateException("Resolved object for parts " + Arrays.toString(parts) + " is not the right type! It is " + o);
271                 
272         }
273         
274         Object resolve(String[] parts, int index) {
275                 String part = parts[index];
276                 if (part.startsWith("Style")) {
277                     for (SpreadsheetStyle style : styles.values()) {
278                         if (style.name.equals(part)) {
279                             return style;
280                         }
281                     }
282                 }
283                 SpreadsheetEngine engine = getEngine(part);
284                 if(engine == null) return 0;
285                 if(index == parts.length-1) return engine;
286                 else return engine.resolve(parts, index+1);
287         }
288
289         @Override
290         public String getName(int id) {
291                 if(id == -2) return "http://www.simantics.org/Spreadsheet-1.2/Book";
292                 else if(id == -3) return "http://www.simantics.org/Spreadsheet-1.2/Spreadsheet";
293                 else if(id == -4) return "http://www.simantics.org/Spreadsheet-1.2/Lines";
294                 else if(id == -5)
295                     return "http://www.simantics.org/Spreadsheet-1.2/LineNode";
296                 else if (id == -6)
297                     return "http://www.simantics.org/Spreadsheet-1.2/Line";
298                 else if(id == -7)
299                     return "http://www.simantics.org/Spreadsheet-1.2/Style";
300                 else return "" + id;
301         }
302
303         @Override
304         public int getModuleType(int id) {
305                 Serializable s = children.get(id);
306                 if(s instanceof SpreadsheetBook) return -2;
307                 else if(s instanceof SpreadsheetEngine) return -3;
308                 else if(s instanceof SpreadsheetLines) {
309                         if("Lines".equals(((SpreadsheetLines) s).getName()))
310                                 return -4;
311                         else
312                                 return -5;
313                 }
314                 else if(s instanceof SpreadsheetLine)
315                     return -6;
316                 else if (s instanceof SpreadsheetStyle)
317                     return -7;
318                 else throw new IllegalStateException();
319         }
320
321         @Override
322         public void remove(int id) {
323             
324             SpreadsheetElement child = children.get(id);
325             Optional<SpreadsheetElement> parent = child.getParent();
326             
327             if (parent.isPresent()) {
328                 parent.get().remove(child);
329                 children.remove(id);
330             }
331         }
332
333         @Override
334         public void addSubprocess(String name, String subprocessType) {
335                 ensureSubprocess(name);
336         }
337         
338         public <T> T ensureSubprocess(String name) {
339                 String[] parts = name.split("/");
340                 if(parts.length == 2) {
341                         SpreadsheetEngine engine = getEngine(parts[1]);
342                         if(engine == null) {
343                                 engine = new SpreadsheetEngine(this, parts[1]);
344                                 sheets.add(engine);
345                         }
346                         return (T)engine;
347                 } else if (parts.length > 2) {
348                         SpreadsheetEngine engine = getEngine(parts[1]);
349                         return (T)engine.ensureSubprocess(parts, 2);
350                 }
351                 throw new IllegalStateException();
352         }
353         
354
355         @Override
356         public void includeSubprocess(String parentName, String subprocessName) {
357                 // Nop
358         }
359
360         @Override
361         public <T> T getConcreteSolver() {
362                 return (T)this;
363         }
364         
365         @Override
366         public void accept(SpreadsheetVisitor v) {
367                 v.visit(this);
368         }
369         
370         //Recursively find all SpreadsheetCells, invalidate them and return them all as a set
371         public Set<SpreadsheetCell> invalidate(SpreadsheetCell cell) {
372                 Set<SpreadsheetCell> result = new HashSet<>();
373                 result.add(cell);
374                 cell.invalidate();
375                 long refKey = cell.makeReferenceKey();
376                 AbstractLongSet refs = referenceMap.remove(refKey);
377                 if(refs == null) return result;
378                 for(long ref : refs) {
379                         long sheet = ref >> 40;
380                         long row = (ref >> 20) & 0xFFFFF;
381                         long col = (ref) & 0xFFFFF;
382                         SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col);
383                         result.addAll(invalidate(referer));
384                 }
385                 return result;
386         }
387         
388         @Deprecated
389         public List<SpreadsheetCell> invalidateShallow(SpreadsheetCell cell) {
390                 ArrayList<SpreadsheetCell> result = new ArrayList<>();
391                 result.add(cell);
392                 cell.invalidate();
393                 long refKey = cell.makeReferenceKey();
394                 AbstractLongSet refs = referenceMap.remove(refKey);
395                 if(refs == null) return result;
396                 for(long ref : refs) {
397                         long sheet = ref >> 40;
398                         long row = (ref >> 20) & 0xFFFFF;
399                         long col = (ref) & 0xFFFFF;
400                         SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col);
401                         invalidate(referer);
402                         result.add(referer);
403                 }
404                 return result;
405         }
406
407     public void addStyle(SpreadsheetStyle style) {
408         if (style.name == null) {
409             new Exception("Trying to add style to book without name!!").printStackTrace();
410             return;
411         }
412         style.setSynchronizationId(getNewId(style));
413         styles.put(style.getStyleId(), style);
414     }
415
416     public SpreadsheetStyle getStyle(int styleId) {
417         return styles.get(styleId);
418     }
419
420     public MappingBase<SheetLineComponent> getMapping() {
421         return mapping;
422     }
423
424     @Override
425     public Optional<SpreadsheetElement> getParent() {
426         return Optional.empty();
427     }
428
429     @Override
430     public Collection<SpreadsheetElement> getSpreadsheetChildren() {
431         return children.values();
432     }
433
434     @Override
435     public void remove(SpreadsheetElement child) {
436         // TODO Auto-generated method stub
437         
438     }
439
440         public void setIterationEnabled(boolean value) {
441                 this.iterationEnabled = value;
442         }
443         
444         public boolean isIterationEnabled() {
445                 return iterationEnabled;
446         }
447
448 }