--- /dev/null
+package org.simantics.spreadsheet.graph;\r
+\r
+import java.io.Serializable;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.StandardEngine;\r
+import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;\r
+import org.simantics.spreadsheet.graph.synchronization.LineNodeUpdater;\r
+import org.simantics.spreadsheet.graph.synchronization.LineUpdater;\r
+import org.simantics.spreadsheet.graph.synchronization.NullUpdater;\r
+import org.simantics.spreadsheet.graph.synchronization.SheetLineComponent;\r
+import org.simantics.spreadsheet.graph.synchronization.StyleUpdater;\r
+import org.simantics.structural.synchronization.base.ComponentFactory;\r
+import org.simantics.structural.synchronization.base.MappingBase;\r
+import org.simantics.structural.synchronization.base.ModuleUpdaterBase;\r
+import org.simantics.structural.synchronization.base.ModuleUpdaterFactoryBase;\r
+import org.simantics.structural.synchronization.base.Solver;\r
+import org.simantics.structural.synchronization.base.SolverNameUtil;\r
+\r
+import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;\r
+import it.unimi.dsi.fastutil.longs.AbstractLongList;\r
+import it.unimi.dsi.fastutil.longs.AbstractLongSet;\r
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;\r
+import it.unimi.dsi.fastutil.longs.LongArraySet;\r
+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;\r
+\r
+public class SpreadsheetBook implements SpreadsheetElement<SpreadsheetElement, SpreadsheetElement>, StandardEngine<SheetNode>, Serializable, SheetNode<SpreadsheetEngine, SheetNode>, Solver, SolverNameUtil, ComponentFactory<SheetLineComponent>, ModuleUpdaterFactoryBase<SheetLineComponent> {\r
+\r
+ private static final long serialVersionUID = 7417208688311691396L;\r
+ \r
+ public Serializable NotAvailableError = new Serializable() {};\r
+ \r
+ public Long2ObjectOpenHashMap<AbstractLongSet> referenceMap = new Long2ObjectOpenHashMap<>();\r
+ \r
+ private Int2ObjectArrayMap<SpreadsheetStyle> styles = new Int2ObjectArrayMap<>();\r
+// public ObjectArrayList<SpreadsheetStyle> styles = new ObjectArrayList<SpreadsheetStyle>();\r
+ public ArrayList<SpreadsheetEngine> sheets = new ArrayList<SpreadsheetEngine>();\r
+ \r
+ private SpreadsheetMapping mapping;\r
+ \r
+ \r
+ private int idCounter = 1;\r
+ \r
+ public SpreadsheetBook() {\r
+ getNewId(this);\r
+ mapping = new SpreadsheetMapping(new SheetLineComponent(""));\r
+ }\r
+ \r
+ public Map<Integer, SpreadsheetElement> children = new HashMap<>();\r
+\r
+ public int getNewId(SpreadsheetElement element) {\r
+ int result = idCounter++;\r
+ children.put(result, element);\r
+ return result; \r
+ }\r
+ \r
+ public long getEngineIndex(SpreadsheetEngine engine) {\r
+ for(int i=0;i<sheets.size();i++)\r
+ if(sheets.get(i) == engine) return i;\r
+ throw new IllegalStateException("Did not find sheet " + engine.getName());\r
+ }\r
+ \r
+ public void registerReferences(long cell, AbstractLongList refs) {\r
+ for(int i=0;i<refs.size();i++) {\r
+ long key = refs.getLong(i);\r
+ AbstractLongSet set = referenceMap.get(key);\r
+ if(set == null) {\r
+ set = new LongArraySet();\r
+ referenceMap.put(key, set);\r
+ }\r
+ if(set.size() == 5) {\r
+ AbstractLongSet newSet = new LongLinkedOpenHashSet();\r
+ newSet.addAll(set);\r
+ set = newSet;\r
+ referenceMap.put(key, set);\r
+ }\r
+ set.add(cell);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public Object getValue(SheetNode node) {\r
+ if(node instanceof SpreadsheetCellContent) {\r
+ try {\r
+ SpreadsheetCellContent scc = (SpreadsheetCellContent)node;\r
+ Object content = scc.cell.evaluate(SpreadsheetEvaluationEnvironment.getInstance(this));\r
+ if(content == null) return Variant.ofInstance("");\r
+ if(content instanceof Variant) return content;\r
+ else return Variant.ofInstance(content);\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ return Variant.ofInstance(t.toString());\r
+ }\r
+ } else if (node instanceof SpreadsheetCellContentExpression) {\r
+ SpreadsheetCellContentExpression scce = (SpreadsheetCellContentExpression)node;\r
+ if (scce.cell.content instanceof SpreadsheetFormula) {\r
+ SpreadsheetFormula formula = (SpreadsheetFormula)scce.cell.content; \r
+ return formula.expression;\r
+ } else if (scce.cell.content instanceof SpreadsheetSCLConstant) {\r
+ SpreadsheetSCLConstant sclConstant = (SpreadsheetSCLConstant) scce.cell.content;\r
+ return "=" + sclConstant.expression;\r
+ } else {\r
+ System.out.println("Broken SpreadsheetCellContentExpression possibly due to overwriting an existing expression with a constant or something else (current content is " + scce.cell.content + ")");\r
+ if (scce.cell.content instanceof Variant) {\r
+ return scce.cell.content;\r
+ } else {\r
+ return Variant.ofInstance(scce.cell.content);\r
+ }\r
+ }\r
+ } else if (node instanceof SpreadsheetTypeNode) {\r
+ SpreadsheetTypeNode stn = (SpreadsheetTypeNode)node;\r
+ return stn.uri;\r
+ } else if (node instanceof SpreadsheetCellStyle) {\r
+ int styleId = ((SpreadsheetCellStyle) node).cell.style;\r
+ SpreadsheetStyle style = getStyle(styleId);\r
+ if (style == null) {\r
+ style = SpreadsheetStyle.empty();\r
+ if (styleId != style.getStyleId())\r
+ new Exception("different style ids!" + styleId + " " + style.getStyleId()).printStackTrace();\r
+ addStyle(style);\r
+ }\r
+ return style;\r
+ } else if (node instanceof SpreadsheetCellEditable) {\r
+ boolean editable = ((SpreadsheetCellEditable) node).editable();\r
+ return editable;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public void setValue(SheetNode node, Object value) {\r
+ }\r
+\r
+ @Override\r
+ public String getName(SheetNode node) {\r
+ return node.getName();\r
+ }\r
+\r
+ @Override\r
+ public Map<String, SheetNode> getChildren(SheetNode node) {\r
+ return node.getChildren();\r
+ }\r
+\r
+ @Override\r
+ public Map<String, SheetNode> getProperties(SheetNode node) {\r
+ return node.getProperties();\r
+ }\r
+\r
+ @Override\r
+ public String getName() {\r
+ return "";\r
+ }\r
+\r
+ @Override\r
+ public Map<String, SpreadsheetEngine> getChildren() {\r
+ Map<String,SpreadsheetEngine> result = new HashMap<String,SpreadsheetEngine>();\r
+ for(SpreadsheetEngine engine : sheets)\r
+ result.put(engine.getName(), engine);\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public Map<String, SheetNode> getProperties() {\r
+ return Collections.emptyMap();\r
+ }\r
+ \r
+ public SpreadsheetCell get(String sheet, int row, int column) {\r
+ SpreadsheetEngine engine = getEngine(sheet);\r
+ if(engine == null) return null;\r
+ SpreadsheetLine line = engine.getLine(row);\r
+ if(line == null) return null;\r
+ if(line.cells.size() <= column) return null;\r
+ return line.cells.get(column);\r
+ }\r
+\r
+ public SpreadsheetCell get(SpreadsheetEngine engine, int row, int column) {\r
+ SpreadsheetLine line = engine.getLine(row);\r
+ if(line == null) return null;\r
+ if(line.cells.size() <= column) return null;\r
+ return line.cells.get(column);\r
+ }\r
+\r
+ public SpreadsheetEngine getEngine(String sheet) {\r
+ for(SpreadsheetEngine engine : sheets)\r
+ if(sheet.equals(engine.getName())) return engine;\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public ModuleUpdaterBase<SheetLineComponent> createUpdater(String id) throws DatabaseException {\r
+ if("http://www.simantics.org/Spreadsheet-1.2/Line".equals(id))\r
+ return new LineUpdater(id);\r
+ else if("http://www.simantics.org/Spreadsheet-1.2/LineNode".equals(id))\r
+ return new LineNodeUpdater(id);\r
+ else if ("http://www.simantics.org/Spreadsheet-1.2/Style".equals(id))\r
+ return new StyleUpdater(id);\r
+ else if("http://www.simantics.org/Spreadsheet-1.2/Lines".equals(id))\r
+ return new NullUpdater(id);\r
+ else if("http://www.simantics.org/Spreadsheet-1.2/Spreadsheet".equals(id))\r
+ return new NullUpdater(id);\r
+ else if("http://www.simantics.org/Spreadsheet-1.2/Book".equals(id))\r
+ return new NullUpdater(id);\r
+ else\r
+ throw new IllegalStateException("createUpdater " + id);\r
+ }\r
+\r
+ @Override\r
+ public SheetLineComponent create(String uid) {\r
+ return new SheetLineComponent(uid);\r
+ }\r
+\r
+ @Override\r
+ public String getFreshName(String parentName, String name) {\r
+ return parentName + "/" + name;\r
+ }\r
+\r
+ @Override\r
+ public String ensureNameIsVariationOf(String parentName, int id, String name) {\r
+ if (parentName.isEmpty())\r
+ return name;\r
+ return parentName + "/" + name;\r
+ }\r
+\r
+ final static int COMP_ROOT_POS = "COMP_ROOT/".length();\r
+ \r
+ @Override\r
+ public int getId(String name) {\r
+ \r
+ if("COMP_ROOT".equals(name)) return 1;\r
+\r
+ String path = name.substring(COMP_ROOT_POS);\r
+ String[] parts = path.split("/");\r
+ Object o = resolve(parts, 0);\r
+ if(o instanceof SpreadsheetLines) {\r
+ return ((SpreadsheetLines)o).getId();\r
+ } else if(o instanceof SpreadsheetLine) {\r
+ return ((SpreadsheetLine)o).getId();\r
+ } else if(o instanceof SpreadsheetEngine) {\r
+ return ((SpreadsheetEngine)o).getId();\r
+ } else if (o instanceof SpreadsheetStyle) {\r
+ return ((SpreadsheetStyle) o).getId();\r
+ }\r
+ \r
+ throw new IllegalStateException("Resolved object for parts " + Arrays.toString(parts) + " is not the right type! It is " + o);\r
+ \r
+ }\r
+ \r
+ Object resolve(String[] parts, int index) {\r
+ String part = parts[index];\r
+ if (part.startsWith("Style")) {\r
+ for (SpreadsheetStyle style : styles.values()) {\r
+ if (style.name.equals(part)) {\r
+ return style;\r
+ }\r
+ }\r
+ }\r
+ SpreadsheetEngine engine = getEngine(part);\r
+ if(engine == null) return 0;\r
+ if(index == parts.length-1) return engine;\r
+ else return engine.resolve(parts, index+1);\r
+ }\r
+\r
+ @Override\r
+ public String getName(int id) {\r
+ if(id == -2) return "http://www.simantics.org/Spreadsheet-1.2/Book";\r
+ else if(id == -3) return "http://www.simantics.org/Spreadsheet-1.2/Spreadsheet";\r
+ else if(id == -4) return "http://www.simantics.org/Spreadsheet-1.2/Lines";\r
+ else if(id == -5)\r
+ return "http://www.simantics.org/Spreadsheet-1.2/LineNode";\r
+ else if (id == -6)\r
+ return "http://www.simantics.org/Spreadsheet-1.2/Line";\r
+ else if(id == -7)\r
+ return "http://www.simantics.org/Spreadsheet-1.2/Style";\r
+ else return "" + id;\r
+ }\r
+\r
+ @Override\r
+ public int getModuleType(int id) {\r
+ Serializable s = children.get(id);\r
+ if(s instanceof SpreadsheetBook) return -2;\r
+ else if(s instanceof SpreadsheetEngine) return -3;\r
+ else if(s instanceof SpreadsheetLines) {\r
+ if("Lines".equals(((SpreadsheetLines) s).getName()))\r
+ return -4;\r
+ else\r
+ return -5;\r
+ }\r
+ else if(s instanceof SpreadsheetLine)\r
+ return -6;\r
+ else if (s instanceof SpreadsheetStyle)\r
+ return -7;\r
+ else throw new IllegalStateException();\r
+ }\r
+\r
+ @Override\r
+ public void remove(int id) {\r
+ \r
+ SpreadsheetElement child = children.get(id);\r
+ Optional<SpreadsheetElement> parent = child.getParent();\r
+ \r
+ if (parent.isPresent()) {\r
+ parent.get().remove(child);\r
+ children.remove(id);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void addSubprocess(String name) {\r
+ ensureSubprocess(name);\r
+ }\r
+ \r
+ public <T> T ensureSubprocess(String name) {\r
+ String[] parts = name.split("/");\r
+ if(parts.length == 2) {\r
+ SpreadsheetEngine engine = getEngine(parts[1]);\r
+ if(engine == null) {\r
+ engine = new SpreadsheetEngine(this, parts[1]);\r
+ sheets.add(engine);\r
+ }\r
+ return (T)engine;\r
+ } else if (parts.length > 2) {\r
+ SpreadsheetEngine engine = getEngine(parts[1]);\r
+ return (T)engine.ensureSubprocess(parts, 2);\r
+ }\r
+ throw new IllegalStateException();\r
+ }\r
+ \r
+\r
+ @Override\r
+ public void includeSubprocess(String parentName, String subprocessName) {\r
+ // Nop\r
+ }\r
+\r
+ @Override\r
+ public <T> T getConcreteSolver() {\r
+ return (T)this;\r
+ }\r
+ \r
+ @Override\r
+ public void accept(SpreadsheetVisitor v) {\r
+ v.visit(this);\r
+ }\r
+ \r
+ public List<SpreadsheetCell> invalidate(SpreadsheetCell cell) {\r
+ ArrayList<SpreadsheetCell> result = new ArrayList<>();\r
+ result.add(cell);\r
+ cell.invalidate();\r
+ long refKey = cell.makeReferenceKey();\r
+ AbstractLongSet refs = referenceMap.remove(refKey);\r
+ if(refs == null) return result;\r
+ for(long ref : refs) {\r
+ long sheet = ref >> 40;\r
+ long row = (ref >> 20) & 0xFFFFF;\r
+ long col = (ref) & 0xFFFFF;\r
+ SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col);\r
+ invalidate(referer);\r
+ result.add(referer);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public void addStyle(SpreadsheetStyle style) {\r
+ if (style.name == null) {\r
+ new Exception("Trying to add style to book without name!!").printStackTrace();\r
+ return;\r
+ }\r
+ style.setSynchronizationId(getNewId(style));\r
+ styles.put(style.getStyleId(), style);\r
+ }\r
+\r
+ public SpreadsheetStyle getStyle(int styleId) {\r
+ return styles.get(styleId);\r
+ }\r
+\r
+ public MappingBase<SheetLineComponent> getMapping() {\r
+ return mapping;\r
+ }\r
+\r
+ @Override\r
+ public Optional<SpreadsheetElement> getParent() {\r
+ return Optional.empty();\r
+ }\r
+\r
+ @Override\r
+ public Collection<SpreadsheetElement> getSpreadsheetChildren() {\r
+ return children.values();\r
+ }\r
+\r
+ @Override\r
+ public void remove(SpreadsheetElement child) {\r
+ // TODO Auto-generated method stub\r
+ \r
+ }\r
+\r
+}\r