package org.simantics.spreadsheet.graph; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.db.exception.DatabaseException; import org.simantics.simulator.toolkit.StandardNodeManagerSupport; import org.simantics.simulator.variable.exceptions.NodeManagerException; import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment; import org.simantics.spreadsheet.graph.synchronization.LineNodeUpdater; import org.simantics.spreadsheet.graph.synchronization.LineUpdater; import org.simantics.spreadsheet.graph.synchronization.NullUpdater; import org.simantics.spreadsheet.graph.synchronization.SheetLineComponent; import org.simantics.spreadsheet.graph.synchronization.StyleUpdater; import org.simantics.structural.synchronization.base.ModuleUpdaterBase; import org.simantics.structural.synchronization.base.ModuleUpdaterFactoryBase; import org.simantics.structural.synchronization.base.SolverNameUtil; import org.simantics.structural.synchronization.utils.ComponentFactory; import org.simantics.structural.synchronization.utils.MappingBase; import org.simantics.structural.synchronization.utils.Solver; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.longs.AbstractLongList; import it.unimi.dsi.fastutil.longs.AbstractLongSet; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; public class SpreadsheetBook implements StandardNodeManagerSupport, SpreadsheetElement, Serializable, SheetNode, Solver, SolverNameUtil, ComponentFactory, ModuleUpdaterFactoryBase { private static final long serialVersionUID = 7417208688311691396L; public Serializable NotAvailableError = new Serializable() { private static final long serialVersionUID = 2535371785498129460L; }; public Long2ObjectOpenHashMap referenceMap = new Long2ObjectOpenHashMap<>(); private Int2ObjectArrayMap styles = new Int2ObjectArrayMap<>(); // public ObjectArrayList styles = new ObjectArrayList(); public ArrayList sheets = new ArrayList(); private SpreadsheetMapping mapping; private int idCounter = 1; public SpreadsheetBook() { getNewId(this); mapping = new SpreadsheetMapping(new SheetLineComponent("")); } public Map children = new HashMap<>(); private boolean iterationEnabled; public int getNewId(SpreadsheetElement element) { int result = idCounter++; children.put(result, element); return result; } public long getEngineIndex(SpreadsheetEngine engine) { for(int i=0;i getChildren(SheetNode node) { return node.getChildren(); } @Override public Map getProperties(SheetNode node) { return node.getProperties(); } @Override public String getName() { return ""; } @Override public Map getChildren() { Map result = new HashMap(); for(SpreadsheetEngine engine : sheets) result.put(engine.getName(), engine); return result; } @Override public Map getProperties() { return Collections.emptyMap(); } public SpreadsheetCell get(String sheet, int row, int column) { SpreadsheetEngine engine = getEngine(sheet); if(engine == null) return null; SpreadsheetLine line = engine.getLine(row); if(line == null) return null; if(line.cells.size() <= column) return null; return line.cells.get(column); } public SpreadsheetCell get(SpreadsheetEngine engine, int row, int column) { SpreadsheetLine line = engine.getLine(row); if(line == null) return null; if(line.cells.size() <= column) return null; return line.cells.get(column); } public SpreadsheetEngine getEngine(String sheet) { for(SpreadsheetEngine engine : sheets) if(sheet.equals(engine.getName())) return engine; return null; } @Override public ModuleUpdaterBase createUpdater(String id) throws DatabaseException { if("http://www.simantics.org/Spreadsheet-1.2/Line".equals(id)) return new LineUpdater(id); else if("http://www.simantics.org/Spreadsheet-1.2/LineNode".equals(id)) return new LineNodeUpdater(id); else if ("http://www.simantics.org/Spreadsheet-1.2/Style".equals(id)) return new StyleUpdater(id); else if("http://www.simantics.org/Spreadsheet-1.2/Lines".equals(id)) return new NullUpdater(id); else if("http://www.simantics.org/Spreadsheet-1.2/Spreadsheet".equals(id)) return new NullUpdater(id); else if("http://www.simantics.org/Spreadsheet-1.2/Book".equals(id)) return new NullUpdater(id); else throw new IllegalStateException("createUpdater " + id); } @Override public SheetLineComponent create(String uid) { return new SheetLineComponent(uid); } @Override public String getFreshName(String parentName, String name) { return parentName + "/" + name; } @Override public String ensureNameIsVariationOf(String parentName, int id, String name) { if (parentName.isEmpty()) return name; return parentName + "/" + name; } final static int COMP_ROOT_POS = "COMP_ROOT/".length(); @Override public int getId(String name) { if("COMP_ROOT".equals(name)) return 1; String path = name.substring(COMP_ROOT_POS); String[] parts = path.split("/"); Object o = resolve(parts, 0); if(o instanceof SpreadsheetLines) { return ((SpreadsheetLines)o).getId(); } else if(o instanceof SpreadsheetLine) { return ((SpreadsheetLine)o).getId(); } else if(o instanceof SpreadsheetEngine) { return ((SpreadsheetEngine)o).getId(); } else if (o instanceof SpreadsheetStyle) { return ((SpreadsheetStyle) o).getId(); } throw new IllegalStateException("Resolved object for parts " + Arrays.toString(parts) + " is not the right type! It is " + o); } Object resolve(String[] parts, int index) { String part = parts[index]; if (part.startsWith("Style")) { for (SpreadsheetStyle style : styles.values()) { if (style.name.equals(part)) { return style; } } } SpreadsheetEngine engine = getEngine(part); if(engine == null) return 0; if(index == parts.length-1) return engine; else return engine.resolve(parts, index+1); } @Override public String getName(int id) { if(id == -2) return "http://www.simantics.org/Spreadsheet-1.2/Book"; else if(id == -3) return "http://www.simantics.org/Spreadsheet-1.2/Spreadsheet"; else if(id == -4) return "http://www.simantics.org/Spreadsheet-1.2/Lines"; else if(id == -5) return "http://www.simantics.org/Spreadsheet-1.2/LineNode"; else if (id == -6) return "http://www.simantics.org/Spreadsheet-1.2/Line"; else if(id == -7) return "http://www.simantics.org/Spreadsheet-1.2/Style"; else return "" + id; } @Override public int getModuleType(int id) { Serializable s = children.get(id); if(s instanceof SpreadsheetBook) return -2; else if(s instanceof SpreadsheetEngine) return -3; else if(s instanceof SpreadsheetLines) { if("Lines".equals(((SpreadsheetLines) s).getName())) return -4; else return -5; } else if(s instanceof SpreadsheetLine) return -6; else if (s instanceof SpreadsheetStyle) return -7; else throw new IllegalStateException(); } @Override public void remove(int id) { SpreadsheetElement child = children.get(id); Optional parent = child.getParent(); if (parent.isPresent()) { parent.get().remove(child); children.remove(id); } } @Override public void addSubprocess(String name, String subprocessType) { ensureSubprocess(name); } public T ensureSubprocess(String name) { String[] parts = name.split("/"); if(parts.length == 2) { SpreadsheetEngine engine = getEngine(parts[1]); if(engine == null) { engine = new SpreadsheetEngine(this, parts[1]); sheets.add(engine); } return (T)engine; } else if (parts.length > 2) { SpreadsheetEngine engine = getEngine(parts[1]); return (T)engine.ensureSubprocess(parts, 2); } throw new IllegalStateException(); } @Override public void includeSubprocess(String parentName, String subprocessName) { // Nop } @Override public T getConcreteSolver() { return (T)this; } @Override public void accept(SpreadsheetVisitor v) { v.visit(this); } //Recursively find all SpreadsheetCells, invalidate them and return them all as a set public Set invalidate(SpreadsheetCell cell) { Set result = new HashSet<>(); result.add(cell); cell.invalidate(); long refKey = cell.makeReferenceKey(); AbstractLongSet refs = referenceMap.remove(refKey); if(refs == null) return result; for(long ref : refs) { long sheet = ref >> 40; long row = (ref >> 20) & 0xFFFFF; long col = (ref) & 0xFFFFF; SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col); result.addAll(invalidate(referer)); } return result; } @Deprecated public List invalidateShallow(SpreadsheetCell cell) { ArrayList result = new ArrayList<>(); result.add(cell); cell.invalidate(); long refKey = cell.makeReferenceKey(); AbstractLongSet refs = referenceMap.remove(refKey); if(refs == null) return result; for(long ref : refs) { long sheet = ref >> 40; long row = (ref >> 20) & 0xFFFFF; long col = (ref) & 0xFFFFF; SpreadsheetCell referer = get(sheets.get((int)sheet), (int)row, (int)col); invalidate(referer); result.add(referer); } return result; } public void addStyle(SpreadsheetStyle style) { if (style.name == null) { new Exception("Trying to add style to book without name!!").printStackTrace(); return; } style.setSynchronizationId(getNewId(style)); styles.put(style.getStyleId(), style); } public SpreadsheetStyle getStyle(int styleId) { return styles.get(styleId); } public MappingBase getMapping() { return mapping; } @Override public Optional getParent() { return Optional.empty(); } @Override public Collection getSpreadsheetChildren() { return children.values(); } @Override public void remove(SpreadsheetElement child) { // TODO Auto-generated method stub } public void setIterationEnabled(boolean value) { this.iterationEnabled = value; } public boolean isIterationEnabled() { return iterationEnabled; } }