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