1 package org.simantics.spreadsheet.graph;
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;
12 import java.util.Optional;
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.utils.ComponentFactory;
28 import org.simantics.structural.synchronization.utils.MappingBase;
29 import org.simantics.structural.synchronization.utils.ModuleUpdaterBase;
30 import org.simantics.structural.synchronization.utils.ModuleUpdaterFactoryBase;
31 import org.simantics.structural.synchronization.utils.Solver;
32 import org.simantics.structural.synchronization.utils.SolverNameUtil;
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;
41 public class SpreadsheetBook implements StandardNodeManagerSupport<SheetNode>, SpreadsheetElement<SpreadsheetElement, SpreadsheetElement>, Serializable, SheetNode<SpreadsheetEngine, SheetNode>, Solver, SolverNameUtil, ComponentFactory<SheetLineComponent>, ModuleUpdaterFactoryBase<SheetLineComponent> {
43 private static final long serialVersionUID = 7417208688311691396L;
45 public Serializable NotAvailableError = new Serializable() {
46 private static final long serialVersionUID = 2535371785498129460L;
49 public Long2ObjectOpenHashMap<AbstractLongSet> referenceMap = new Long2ObjectOpenHashMap<>();
51 private Int2ObjectArrayMap<SpreadsheetStyle> styles = new Int2ObjectArrayMap<>();
52 // public ObjectArrayList<SpreadsheetStyle> styles = new ObjectArrayList<SpreadsheetStyle>();
53 public ArrayList<SpreadsheetEngine> sheets = new ArrayList<SpreadsheetEngine>();
55 private SpreadsheetMapping mapping;
58 private int idCounter = 1;
60 public SpreadsheetBook() {
62 mapping = new SpreadsheetMapping(new SheetLineComponent(""));
65 public Map<Integer, SpreadsheetElement> children = new HashMap<>();
67 private boolean iterationEnabled;
69 public int getNewId(SpreadsheetElement element) {
70 int result = idCounter++;
71 children.put(result, element);
75 public long getEngineIndex(SpreadsheetEngine engine) {
76 for(int i=0;i<sheets.size();i++)
77 if(sheets.get(i) == engine) return i;
78 throw new IllegalStateException("Did not find sheet " + engine.getName());
81 public void registerReferences(long cell, AbstractLongList refs) {
82 for(int i=0;i<refs.size();i++) {
83 long key = refs.getLong(i);
84 AbstractLongSet set = referenceMap.get(key);
86 set = new LongArraySet();
87 referenceMap.put(key, set);
90 AbstractLongSet newSet = new LongLinkedOpenHashSet();
93 referenceMap.put(key, set);
100 public Binding getEngineBinding(SheetNode node) throws NodeManagerException {
101 Object value = getEngineValue(node);
102 if(value instanceof Variant) return Bindings.VARIANT;
103 if(value instanceof String) return Bindings.STRING;
104 if(value instanceof Boolean) return Bindings.BOOLEAN;
105 else return Bindings.VOID;
110 public Object getEngineValue(SheetNode node) {
111 if(node instanceof SpreadsheetCellContent) {
113 SpreadsheetCellContent scc = (SpreadsheetCellContent)node;
114 Object content = scc.cell.evaluate(SpreadsheetEvaluationEnvironment.getInstance(this));
115 if(content == null) return Variant.ofInstance("");
116 if(content instanceof Variant) return content;
117 else return Variant.ofInstance(content);
118 } catch (Throwable t) {
120 return Variant.ofInstance(t.toString());
122 } else if (node instanceof SpreadsheetCellContentExpression) {
123 SpreadsheetCellContentExpression scce = (SpreadsheetCellContentExpression)node;
124 if (scce.cell.content instanceof SpreadsheetFormula) {
125 SpreadsheetFormula formula = (SpreadsheetFormula)scce.cell.content;
126 return formula.expression;
127 } else if (scce.cell.content instanceof SpreadsheetSCLConstant) {
128 SpreadsheetSCLConstant sclConstant = (SpreadsheetSCLConstant) scce.cell.content;
129 return "=" + sclConstant.expression;
131 System.out.println("Broken SpreadsheetCellContentExpression possibly due to overwriting an existing expression with a constant or something else (current content is " + scce.cell.content + ")");
132 if (scce.cell.content instanceof Variant) {
133 return scce.cell.content;
135 return Variant.ofInstance(scce.cell.content);
138 } else if (node instanceof SpreadsheetTypeNode) {
139 SpreadsheetTypeNode stn = (SpreadsheetTypeNode)node;
141 } else if (node instanceof SpreadsheetCellStyle) {
142 int styleId = ((SpreadsheetCellStyle) node).cell.style;
143 SpreadsheetStyle style = getStyle(styleId);
145 style = SpreadsheetStyle.empty();
146 if (styleId != style.getStyleId())
147 new Exception("different style ids!" + styleId + " " + style.getStyleId()).printStackTrace();
151 } else if (node instanceof SpreadsheetCellEditable) {
152 boolean editable = ((SpreadsheetCellEditable) node).editable();
159 public void setEngineValue(SheetNode node, Object value) {
163 public String getName(SheetNode node) {
164 return node.getName();
168 public Map<String, SheetNode> getChildren(SheetNode node) {
169 return node.getChildren();
173 public Map<String, SheetNode> getProperties(SheetNode node) {
174 return node.getProperties();
178 public String getName() {
183 public Map<String, SpreadsheetEngine> getChildren() {
184 Map<String,SpreadsheetEngine> result = new HashMap<String,SpreadsheetEngine>();
185 for(SpreadsheetEngine engine : sheets)
186 result.put(engine.getName(), engine);
191 public Map<String, SheetNode> getProperties() {
192 return Collections.emptyMap();
195 public SpreadsheetCell get(String sheet, int row, int column) {
196 SpreadsheetEngine engine = getEngine(sheet);
197 if(engine == null) return null;
198 SpreadsheetLine line = engine.getLine(row);
199 if(line == null) return null;
200 if(line.cells.size() <= column) return null;
201 return line.cells.get(column);
204 public SpreadsheetCell get(SpreadsheetEngine engine, int row, int column) {
205 SpreadsheetLine line = engine.getLine(row);
206 if(line == null) return null;
207 if(line.cells.size() <= column) return null;
208 return line.cells.get(column);
211 public SpreadsheetEngine getEngine(String sheet) {
212 for(SpreadsheetEngine engine : sheets)
213 if(sheet.equals(engine.getName())) return engine;
218 public ModuleUpdaterBase<SheetLineComponent> createUpdater(String id) throws DatabaseException {
219 if("http://www.simantics.org/Spreadsheet-1.2/Line".equals(id))
220 return new LineUpdater(id);
221 else if("http://www.simantics.org/Spreadsheet-1.2/LineNode".equals(id))
222 return new LineNodeUpdater(id);
223 else if ("http://www.simantics.org/Spreadsheet-1.2/Style".equals(id))
224 return new StyleUpdater(id);
225 else if("http://www.simantics.org/Spreadsheet-1.2/Lines".equals(id))
226 return new NullUpdater(id);
227 else if("http://www.simantics.org/Spreadsheet-1.2/Spreadsheet".equals(id))
228 return new NullUpdater(id);
229 else if("http://www.simantics.org/Spreadsheet-1.2/Book".equals(id))
230 return new NullUpdater(id);
232 throw new IllegalStateException("createUpdater " + id);
236 public SheetLineComponent create(String uid) {
237 return new SheetLineComponent(uid);
241 public String getFreshName(String parentName, String name) {
242 return parentName + "/" + name;
246 public String ensureNameIsVariationOf(String parentName, int id, String name) {
247 if (parentName.isEmpty())
249 return parentName + "/" + name;
252 final static int COMP_ROOT_POS = "COMP_ROOT/".length();
255 public int getId(String name) {
257 if("COMP_ROOT".equals(name)) return 1;
259 String path = name.substring(COMP_ROOT_POS);
260 String[] parts = path.split("/");
261 Object o = resolve(parts, 0);
262 if(o instanceof SpreadsheetLines) {
263 return ((SpreadsheetLines)o).getId();
264 } else if(o instanceof SpreadsheetLine) {
265 return ((SpreadsheetLine)o).getId();
266 } else if(o instanceof SpreadsheetEngine) {
267 return ((SpreadsheetEngine)o).getId();
268 } else if (o instanceof SpreadsheetStyle) {
269 return ((SpreadsheetStyle) o).getId();
272 throw new IllegalStateException("Resolved object for parts " + Arrays.toString(parts) + " is not the right type! It is " + o);
276 Object resolve(String[] parts, int index) {
277 String part = parts[index];
278 if (part.startsWith("Style")) {
279 for (SpreadsheetStyle style : styles.values()) {
280 if (style.name.equals(part)) {
285 SpreadsheetEngine engine = getEngine(part);
286 if(engine == null) return 0;
287 if(index == parts.length-1) return engine;
288 else return engine.resolve(parts, index+1);
292 public String getName(int id) {
293 if(id == -2) return "http://www.simantics.org/Spreadsheet-1.2/Book";
294 else if(id == -3) return "http://www.simantics.org/Spreadsheet-1.2/Spreadsheet";
295 else if(id == -4) return "http://www.simantics.org/Spreadsheet-1.2/Lines";
297 return "http://www.simantics.org/Spreadsheet-1.2/LineNode";
299 return "http://www.simantics.org/Spreadsheet-1.2/Line";
301 return "http://www.simantics.org/Spreadsheet-1.2/Style";
306 public int getModuleType(int id) {
307 Serializable s = children.get(id);
308 if(s instanceof SpreadsheetBook) return -2;
309 else if(s instanceof SpreadsheetEngine) return -3;
310 else if(s instanceof SpreadsheetLines) {
311 if("Lines".equals(((SpreadsheetLines) s).getName()))
316 else if(s instanceof SpreadsheetLine)
318 else if (s instanceof SpreadsheetStyle)
320 else throw new IllegalStateException();
324 public void remove(int id) {
326 SpreadsheetElement child = children.get(id);
327 Optional<SpreadsheetElement> parent = child.getParent();
329 if (parent.isPresent()) {
330 parent.get().remove(child);
336 public void addSubprocess(String name, String subprocessType) {
337 ensureSubprocess(name);
340 public <T> T ensureSubprocess(String name) {
341 String[] parts = name.split("/");
342 if(parts.length == 2) {
343 SpreadsheetEngine engine = getEngine(parts[1]);
345 engine = new SpreadsheetEngine(this, parts[1]);
349 } else if (parts.length > 2) {
350 SpreadsheetEngine engine = getEngine(parts[1]);
351 return (T)engine.ensureSubprocess(parts, 2);
353 throw new IllegalStateException();
358 public void includeSubprocess(String parentName, String subprocessName) {
363 public <T> T getConcreteSolver() {
368 public void accept(SpreadsheetVisitor v) {
372 //Recursively find all SpreadsheetCells, invalidate them and return them all as a set
373 public Set<SpreadsheetCell> invalidate(SpreadsheetCell cell) {
374 Set<SpreadsheetCell> result = new HashSet<>();
377 long refKey = cell.makeReferenceKey();
378 AbstractLongSet refs = referenceMap.remove(refKey);
379 if(refs == null) return result;
380 for(long ref : refs) {
381 long sheet = ref >> 40;
382 long row = (ref >> 20) & 0xFFFFF;
383 long col = (ref) & 0xFFFFF;
384 SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col);
385 result.addAll(invalidate(referer));
391 public List<SpreadsheetCell> invalidateShallow(SpreadsheetCell cell) {
392 ArrayList<SpreadsheetCell> result = new ArrayList<>();
395 long refKey = cell.makeReferenceKey();
396 AbstractLongSet refs = referenceMap.remove(refKey);
397 if(refs == null) return result;
398 for(long ref : refs) {
399 long sheet = ref >> 40;
400 long row = (ref >> 20) & 0xFFFFF;
401 long col = (ref) & 0xFFFFF;
402 SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col);
409 public void addStyle(SpreadsheetStyle style) {
410 if (style.name == null) {
411 new Exception("Trying to add style to book without name!!").printStackTrace();
414 style.setSynchronizationId(getNewId(style));
415 styles.put(style.getStyleId(), style);
418 public SpreadsheetStyle getStyle(int styleId) {
419 return styles.get(styleId);
422 public MappingBase<SheetLineComponent> getMapping() {
427 public Optional<SpreadsheetElement> getParent() {
428 return Optional.empty();
432 public Collection<SpreadsheetElement> getSpreadsheetChildren() {
433 return children.values();
437 public void remove(SpreadsheetElement child) {
438 // TODO Auto-generated method stub
442 public void setIterationEnabled(boolean value) {
443 this.iterationEnabled = value;
446 public boolean isIterationEnabled() {
447 return iterationEnabled;