]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.spreadsheet/src/org/simantics/spreadsheet/solver/SpreadsheetBook.java
Adopt spreadsheet changes made in Balas development
[simantics/platform.git] / bundles / org.simantics.spreadsheet / src / org / simantics / spreadsheet / solver / SpreadsheetBook.java
diff --git a/bundles/org.simantics.spreadsheet/src/org/simantics/spreadsheet/solver/SpreadsheetBook.java b/bundles/org.simantics.spreadsheet/src/org/simantics/spreadsheet/solver/SpreadsheetBook.java
new file mode 100644 (file)
index 0000000..a5cbfea
--- /dev/null
@@ -0,0 +1,519 @@
+package org.simantics.spreadsheet.solver;
+
+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.simulator.toolkit.StandardNodeManagerSupport;
+import org.simantics.simulator.variable.exceptions.NodeManagerException;
+import org.simantics.spreadsheet.ExternalRef;
+import org.simantics.spreadsheet.SpreadsheetCellStyle;
+import org.simantics.spreadsheet.SpreadsheetVisitor;
+import org.simantics.spreadsheet.solver.formula.SpreadsheetEvaluationEnvironment;
+import org.simantics.spreadsheet.synchronization.LineNodeUpdater;
+import org.simantics.spreadsheet.synchronization.LineUpdater;
+import org.simantics.spreadsheet.synchronization.NullUpdater;
+import org.simantics.spreadsheet.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;
+
+@SuppressWarnings("rawtypes")
+public class SpreadsheetBook implements StandardNodeManagerSupport<SheetNode>, SpreadsheetElement<SpreadsheetElement, SpreadsheetElement>, Serializable, SheetNode<SpreadsheetEngine, SheetNode>, Solver, SolverNameUtil, ComponentFactory<SheetLineComponent>, ModuleUpdaterFactoryBase<SheetLineComponent> {
+    
+    private static final long serialVersionUID = 7417208688311691396L;
+
+    public Serializable NotAvailableError = new Serializable() {
+        private static final long serialVersionUID = 2535371785498129460L;
+    };
+
+    public Long2ObjectOpenHashMap<AbstractLongSet> referenceMap = new Long2ObjectOpenHashMap<>();
+
+    private Int2ObjectArrayMap<SpreadsheetStyle> styles = new Int2ObjectArrayMap<>();
+    public ArrayList<SpreadsheetEngine> sheets = new ArrayList<SpreadsheetEngine>();
+
+    private SpreadsheetMapping mapping;
+    
+    String context;
+
+    private int idCounter = 1;
+    
+    private transient boolean disposed = false;
+
+    public SpreadsheetBook(String context) {
+        getNewId(this);
+        mapping = new SpreadsheetMapping(new SheetLineComponent(""));
+        this.context = context;
+    }
+
+    public Map<Integer, SpreadsheetElement> 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<sheets.size();i++)
+            if(sheets.get(i) == engine) return i;
+        throw new IllegalStateException("Did not find sheet " + engine.getName());
+    }
+
+    public void registerReferences(long cell, AbstractLongList refs) {
+        for(int i=0;i<refs.size();i++) {
+            long key = refs.getLong(i);
+            AbstractLongSet set = referenceMap.get(key);
+            if(set == null) {
+                set = new LongArraySet();
+                referenceMap.put(key, set);
+            }
+            if(set.size() == 5) {
+                AbstractLongSet newSet = new LongLinkedOpenHashSet();
+                newSet.addAll(set);
+                set = newSet;
+                referenceMap.put(key, set);
+            }
+            set.add(cell);
+        }
+    }
+
+    @Override
+    public Binding getEngineBinding(SheetNode node) throws NodeManagerException {
+        Object value = getEngineValue(node);
+        if(value instanceof Variant) return Bindings.VARIANT;
+        if(value instanceof String) return Bindings.STRING;
+        if(value instanceof Boolean) return Bindings.BOOLEAN;
+        else return Bindings.getBindingUnchecked(value.getClass());
+    }
+
+    @Override
+    public Object getEngineValue(SheetNode node) {
+        if(node instanceof SpreadsheetCellContent) {
+            try {
+                SpreadsheetCellContent scc = (SpreadsheetCellContent)node;
+                Object content = scc.cell.evaluate(SpreadsheetEvaluationEnvironment.getInstance(this));
+                if(content == null) return Variant.ofInstance("");
+                if(content instanceof Variant) return content;
+                if(content instanceof ExternalRefData) return ((ExternalRefData)content).getContent();
+                else return Variant.ofInstance(content);
+            } catch (Throwable t) {
+                t.printStackTrace();
+                return Variant.ofInstance(t.toString());
+            }
+        } else if (node instanceof SpreadsheetCellContentExpression) {
+            SpreadsheetCellContentExpression scce = (SpreadsheetCellContentExpression)node;
+            if (scce.cell.getContent() instanceof SpreadsheetFormula) {
+                SpreadsheetFormula formula = (SpreadsheetFormula)scce.cell.getContent(); 
+                return formula.expression;
+            } else if (scce.cell.getContent() instanceof SpreadsheetSCLConstant) {
+                SpreadsheetSCLConstant sclConstant = (SpreadsheetSCLConstant) scce.cell.getContent();
+                return "=" + sclConstant.getExpression();
+            } else {
+                System.out.println("Broken SpreadsheetCellContentExpression possibly due to overwriting an existing expression with a constant or something else (current content is " + scce.cell.getContent() + ")");
+                if (scce.cell.getContent() instanceof Variant) {
+                    return scce.cell.getContent();
+                } else {
+                    return Variant.ofInstance(scce.cell.getContent());
+                }
+            }
+        } else if (node instanceof SpreadsheetTypeNode) {
+            SpreadsheetTypeNode stn = (SpreadsheetTypeNode)node;
+            return stn.uri;
+        } else if (node instanceof SpreadsheetCellStyle) {
+            int styleId = ((SpreadsheetCellStyle) node).cell.style;
+            SpreadsheetStyle style = getStyle(styleId);
+            if (style == null) {
+                style = SpreadsheetStyle.empty();
+                if (styleId != style.getStyleId())
+                    new Exception("different style ids!" + styleId + "  " + style.getStyleId()).printStackTrace();
+                addStyle(style);
+            }
+            return style;
+        } else if (node instanceof SpreadsheetCellEditable) {
+            boolean editable = ((SpreadsheetCellEditable) node).editable();
+            return editable;
+        }
+        return null;
+    }
+
+    @Override
+    public void setEngineValue(SheetNode node, Object value) {
+        
+        SpreadsheetCellContent scc = (SpreadsheetCellContent)node;
+        
+        Object content = scc.cell.evaluate(SpreadsheetEvaluationEnvironment.getInstance(this));
+        System.err.println("content2: " + content);
+        if (content instanceof Variant) {
+            scc.cell.setContent(value);
+        } else if (content instanceof ExternalRefData) {
+            ExternalRefData erd = (ExternalRefData)content;
+            erd.getRef().modify(context, (Variant)value);
+        } else {
+            throw new IllegalStateException("Unable to set cell value");
+        }
+        
+    }
+
+    @Override
+    public String getName(SheetNode node) {
+        return node.getName();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Map<String, SheetNode> getChildren(SheetNode node) {
+        return node.getChildren();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Map<String, SheetNode> getProperties(SheetNode node) {
+        return node.getProperties();
+    }
+
+    @Override
+    public String getName() {
+        return "";
+    }
+
+    @Override
+    public Map<String, SpreadsheetEngine> getChildren() {
+        Map<String,SpreadsheetEngine> result = new HashMap<String,SpreadsheetEngine>();
+        for(SpreadsheetEngine engine : sheets)
+            result.put(engine.getName(), engine);
+        return result;
+    }
+
+    @Override
+    public Map<String, SheetNode> 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<SheetLineComponent> createUpdater(String id) throws Exception {
+        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();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void remove(int id) {
+
+        SpreadsheetElement child = children.get(id);
+        Optional<SpreadsheetElement> parent = child.getParent();
+
+        if (parent.isPresent()) {
+            parent.get().remove(child);
+            children.remove(id);
+        }
+    }
+
+    @Override
+    public void addSubprocess(String name, String subprocessType) {
+        ensureSubprocess(name);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> 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
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getConcreteSolver() {
+        return (T)this;
+    }
+
+    @Override
+    public void accept(SpreadsheetVisitor v) {
+        v.visit(this);
+    }
+
+    SpreadsheetCell cellByReferenceKey(long ref) {
+        long sheet = ref >> 40;
+        long row = (ref >> 20) & 0xFFFFF;
+        long col = (ref) & 0xFFFFF;
+        return  get(sheets.get((int)sheet), (int)row, (int)col);
+    }
+    
+    //Recursively find all SpreadsheetCells, invalidate them and return them all as a set
+    public Set<SpreadsheetCell> invalidate(SpreadsheetCell cell) {
+        Set<SpreadsheetCell> 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) {
+            SpreadsheetCell referer = cellByReferenceKey(ref);
+            result.addAll(invalidate(referer));
+        }
+        return result;
+    }
+
+    @Deprecated
+    public List<SpreadsheetCell> invalidateShallow(SpreadsheetCell cell) {
+        ArrayList<SpreadsheetCell> 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) {
+            SpreadsheetCell referer = cellByReferenceKey(ref);
+            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<SheetLineComponent> getMapping() {
+        return mapping;
+    }
+
+    @Override
+    public Optional<SpreadsheetElement> getParent() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Collection<SpreadsheetElement> 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;
+    }
+
+    static Variant DEFAULT_VALUE = Variant.ofInstance("Pending external reference");
+    
+    private Map<ExternalRef,ExternalRefData> externalRefMap = new HashMap<>();
+
+    void registerListening(long referenceKey, ExternalRef ref) {
+        ExternalRefData data = externalRefMap.get(ref);
+        if(data == null) {
+            data = new ExternalRefData(this, referenceKey, ref);
+            externalRefMap.put(ref, data);
+        } else {
+            // Already registered
+        }
+    }
+
+    ExternalRefData getExternalRefValue(long referenceKey, ExternalRef ref) {
+        ExternalRefData data = externalRefMap.get(ref);
+        if(data == null) {
+            registerListening(referenceKey, ref);
+            return new ExternalRefData(this, referenceKey, new ExternalRefConstant(DEFAULT_VALUE));
+        }
+        return data;
+    }
+    
+    public boolean isDisposed() {
+        return disposed;
+    }
+
+    public static interface SpreadsheetBookListener {
+        void cellsChanged(Collection<SpreadsheetCell> cells);
+    }
+    
+    public void registerListener(SpreadsheetBookListener listener) {
+        if(listeners == null)
+            listeners = new ArrayList<>();
+        listeners.add(listener);
+    }
+    
+    private transient ArrayList<SpreadsheetBookListener> listeners = new ArrayList<>();
+    
+    public void fireChanges(Collection<SpreadsheetCell> cells) {
+        for(SpreadsheetBookListener listener : listeners)
+            listener.cellsChanged(cells);
+    }
+    
+}