]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetGraphUtils.java
SCL API for direct access to SpreadsheetBooks
[simantics/platform.git] / bundles / org.simantics.spreadsheet.graph / src / org / simantics / spreadsheet / graph / SpreadsheetGraphUtils.java
index 43f3515d622fb4ed8bfdfda08dcdc6827e4388fc..3938e478b923d768220952d44301eedb449388f1 100644 (file)
-package org.simantics.spreadsheet.graph;\r
-\r
-import java.io.File;\r
-import java.io.FileNotFoundException;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.ObjectOutputStream;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.Simantics;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.util.binary.RandomAccessBinary;\r
-import org.simantics.datatypes.DatatypeResource;\r
-import org.simantics.datatypes.literal.Font;\r
-import org.simantics.datatypes.literal.RGB;\r
-import org.simantics.datatypes.utils.BTree;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.ObjectsWithType;\r
-import org.simantics.db.common.utils.LiteralFileUtil;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.ServiceException;\r
-import org.simantics.db.layer0.StandardRealm;\r
-import org.simantics.db.layer0.util.Layer0Utils;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.layer0.variable.Variables;\r
-import org.simantics.db.service.ClusteringSupport;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.scl.runtime.tuple.Tuple2;\r
-import org.simantics.spreadsheet.Range;\r
-import org.simantics.spreadsheet.graph.synchronization.SpreadsheetSynchronizationEventHandler;\r
-import org.simantics.spreadsheet.resource.SpreadsheetResource;\r
-import org.simantics.spreadsheet.util.SpreadsheetUtils;\r
-import org.simantics.structural.synchronization.Synchronizer;\r
-\r
-import gnu.trove.iterator.TObjectIntIterator;\r
-import gnu.trove.map.hash.TObjectIntHashMap;\r
-\r
-public class SpreadsheetGraphUtils {\r
-\r
-    public static File extractInitialCondition(ReadGraph graph, Resource ic) throws DatabaseException, IOException {\r
-\r
-       SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-\r
-        File temp = Simantics.getTempfile("excel","ic");\r
-\r
-        LiteralFileUtil.copyRandomAccessBinaryToFile(graph, ic, SR.InitialCondition_bytes, temp);\r
-        if (temp.length() == 0)\r
-            throw new FileNotFoundException("Snapshot file does not exist.\nThis seems to be a database bug that manifests as total loss of state file data.\nThis error prevents the program from crashing.");\r
-\r
-        return temp;\r
-        \r
-    }\r
-       \r
-    public static RandomAccessBinary getOrCreateRandomAccessBinary(WriteGraph graph, Resource initialCondition) throws DatabaseException, IOException {\r
-\r
-       SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-       \r
-        // We put snapshot literals in their own clusters for now just to be safe\r
-        Resource literal = graph.getPossibleObject(initialCondition, SR.InitialCondition_bytes);\r
-        if (literal != null) {\r
-            RandomAccessBinary rab = graph.getRandomAccessBinary(literal);\r
-            rab.position(0);\r
-            rab.removeBytes(rab.length(), RandomAccessBinary.ByteSide.Right);\r
-            return rab;\r
-        } else {\r
-            Layer0 L0 = Layer0.getInstance(graph);\r
-            ClusteringSupport cs = graph.getService(ClusteringSupport.class);\r
-            literal = graph.newResource(cs.createCluster());\r
-            graph.claim(literal, L0.InstanceOf, null, L0.ByteArray);\r
-            graph.claim(initialCondition, SR.InitialCondition_bytes, SR.InitialCondition_bytes_Inverse, literal);\r
-            return graph.createRandomAccessBinary(literal, Bindings.BYTE_ARRAY.type(), null);\r
-        }\r
-    }\r
-\r
-    public static Resource saveInitialCondition(WriteGraph graph, Variable run, Resource container, String name) throws DatabaseException {\r
-\r
-               String sessionName = run.getParent(graph).getURI(graph);\r
-\r
-               Resource bookResource = run.getRepresents(graph);\r
-               \r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-\r
-       try {\r
-       \r
-               File temp = Simantics.getTempfile("excel", "ic");\r
-               System.err.println("Saving initial condition to " + temp.getAbsolutePath());\r
-               \r
-                       FileOutputStream fileOut = new FileOutputStream(temp);\r
-                       ObjectOutputStream out = new ObjectOutputStream(fileOut);\r
-                       out.writeObject(book);\r
-                       out.close();\r
-                       fileOut.close();\r
-                       \r
-                       Layer0 L0 = Layer0.getInstance(graph);\r
-               SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-                       Resource ic = graph.newResource();\r
-                       graph.claim(ic, L0.InstanceOf, SR.InitialCondition);\r
-                       graph.addLiteral(ic, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);\r
-               \r
-               RandomAccessBinary rab = getOrCreateRandomAccessBinary(graph, ic);\r
-               LiteralFileUtil.copyRandomAccessBinaryFromFile(temp, rab);\r
-               \r
-                       graph.claim(container, L0.ConsistsOf, L0.PartOf, ic);\r
-                       \r
-                       graph.deny(bookResource, SR.HasInitialCondition);\r
-                       graph.claim(bookResource, SR.HasInitialCondition, ic);\r
-                       graph.claim(ic, SR.InitialCondition_ConditionOf, bookResource);\r
-                       \r
-                       setDefaultInitialConditionForBook(graph, bookResource, ic);\r
-\r
-               return ic;\r
-               \r
-       } catch (IOException e) {\r
-               \r
-               throw new DatabaseException(e);\r
-               \r
-       }\r
-    }\r
-    \r
-    public static void setDefaultInitialConditionForBook(WriteGraph graph, Resource book, Resource ic) throws ServiceException {\r
-        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-        graph.deny(book, SR.Book_HasDefaultInitialCondition);\r
-        graph.claim(ic, SR.InitialCondition_DefaultConditionOf, book);\r
-    }\r
-\r
-    public static void evaluateAll(ReadGraph graph, Variable run) throws DatabaseException {\r
-\r
-               String sessionName = run.getParent(graph).getURI(graph);\r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-       book.accept(new EvaluateAll(book));\r
-       \r
-    }\r
-\r
-    public static void invalidateAll(ReadGraph graph, Variable run) throws DatabaseException {\r
-\r
-               String sessionName = run.getParent(graph).getURI(graph);\r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-       book.accept(new InvalidateAll());\r
-       realm.getNodeManager().refreshVariables();\r
-       \r
-    }\r
-    \r
-    public static boolean fullSynchronization(ReadGraph graph, Variable run) throws DatabaseException {\r
-        return partialSynchronization(graph, run, null);\r
-    }\r
-\r
-    public static boolean partialSynchronization(ReadGraph graph, Variable run, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {\r
-\r
-       Synchronizer synchronizer = new Synchronizer(graph);\r
-               String sessionName = run.getParent(graph).getURI(graph);\r
-               \r
-       Resource bookResource = run.getRepresents(graph);\r
-       Variable configuration = Variables.getVariable(graph, bookResource);\r
-       \r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-                       \r
-        SpreadsheetSynchronizationEventHandler handler = new SpreadsheetSynchronizationEventHandler(graph, book);\r
-        \r
-//        System.err.println("sessionName : " + sessionName);\r
-//        System.err.println("bookResource : " + graph.getURI(bookResource));\r
-//        System.err.println("configuration : " + configuration.getURI(graph));\r
-//        System.err.println("realm : " + realm);\r
-//        System.err.println("book : " + book);\r
-        \r
-        if (changeFlags == null) {\r
-            synchronizer.fullSynchronization(configuration, handler);\r
-        } else {\r
-            \r
-            TObjectIntIterator<Variable> iter = changeFlags.iterator();\r
-            iter.advance();\r
-            Variable row = iter.key();\r
-            \r
-            Variable rowParent = row.getParent(graph);\r
-            while (!rowParent.equals(configuration)) {\r
-                changeFlags.put(rowParent, 1);\r
-                rowParent = rowParent.getParent(graph);\r
-            }\r
-            \r
-            changeFlags.put(configuration, 1);\r
-            \r
-            synchronizer.partialSynchronization(configuration, handler, changeFlags);\r
-        }\r
-        \r
-//        book.accept(new InvalidateAll());\r
-//        realm.getNodeManager().refreshVariables();\r
-//        mapping.currentRevision = synchronizer.getHeadRevisionId();\r
-//        mapping.setTrustUids(true);\r
-        // Clean up queries\r
-//        QueryControl qc = g.getService(QueryControl.class);\r
-//        qc.flush(g);\r
-//        TimeLogger.log("Finished full synchronization");\r
-        realm.getNodeManager().fireNodeListeners();\r
-        return handler.getDidChanges();\r
-        \r
-    }\r
-\r
-    public static Variable findCell(ReadGraph graph, Variable run, String reference) throws DatabaseException {\r
-\r
-       int pos = reference.indexOf("!");\r
-       String sheetName = reference.substring(0, pos);\r
-       String cellName = reference.substring(pos+1);\r
-\r
-               String sessionName = run.getParent(graph).getURI(graph);\r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-       SpreadsheetEngine engine = book.getEngine(sheetName);\r
-       if(engine == null) return null;\r
-       \r
-       Range r = SpreadsheetUtils.decodeCellAbsolute(cellName);\r
-       SpreadsheetLine line = engine.getLine(r.startRow);\r
-       if(line == null) return null;\r
-       \r
-       String path = line.getPath();\r
-       if(path == null) return null;\r
-       \r
-       Variable lineVariable = run.browse(graph, path);\r
-       if(lineVariable==null) return null;\r
-       \r
-       return lineVariable.getChild(graph, cellName);\r
-       \r
-    }\r
-\r
-    \r
-    public static boolean asBoolean(Object object) {\r
-       if(object instanceof Boolean) return (Boolean)object;\r
-       else if(object instanceof Number) return ((Number)object).doubleValue() != 0;\r
-       else if(object instanceof Variant) return asBoolean(((Variant)object).getValue());\r
-       else if(object instanceof String) {\r
-               Double d = asDoubleWhereEmptyStringIsZero((String)object);\r
-               if(d==null) return false;\r
-               else return d != 0;\r
-       }\r
-       return false;\r
-    }\r
-    \r
-    public static String asString(Object object) {\r
-       if(object == null) return "";\r
-       if(object instanceof String) return (String)object;\r
-       if(object instanceof Number) {\r
-               double dVal = ((Number)object).doubleValue();\r
-               if(dVal == Math.floor(dVal)){\r
-                       return ""+((Number)object).intValue();\r
-               } else {\r
-                       return object.toString();\r
-               }\r
-       }\r
-       else if(object instanceof Variant) {\r
-               Object o = ((Variant) object).getValue();\r
-               if(o instanceof String) return (String)o;\r
-               else if(o instanceof Number) asString((Number)o);\r
-               else return o.toString();\r
-       }\r
-       return object.toString();\r
-    }\r
-    \r
-    public static Double asDoubleWhereEmptyStringIsZero(Object object){\r
-       if(object instanceof Number)\r
-               return ((Number)object).doubleValue();\r
-       else if(object instanceof String) {\r
-               try {\r
-                       if(((String)object).isEmpty())\r
-                               return 0.0;\r
-                       return Double.parseDouble((String)object);\r
-                       } catch (NumberFormatException e) {\r
-                               return null;\r
-                       }\r
-       } else if(object instanceof Variant) {\r
-               Object o = ((Variant) object).getValue();\r
-               return asDoubleWhereEmptyStringIsZero(o);\r
-       } else if (SpreadsheetCell.EMPTY == object) {\r
-               return null;\r
-       }\r
-       return null;\r
-    }\r
-    \r
-    public static double asNumber(Object object) {\r
-       if(object instanceof Number) {\r
-               return ((Number)object).doubleValue();\r
-       } else if(object instanceof String) {\r
-               try {\r
-                       String str = (String)object;\r
-                       if(str.isEmpty()) return 0;\r
-                       return Double.parseDouble((String)object);\r
-                       } catch (NumberFormatException e) {\r
-                       return 0;\r
-                       }\r
-       } else if(object instanceof Variant) {\r
-               Object o = ((Variant) object).getValue();\r
-               return asNumber(o);\r
-       } else if (SpreadsheetCell.EMPTY == object) {\r
-               return 0.0;\r
-       }\r
-               \r
-       return 0.0;\r
-       \r
-    }\r
-    \r
-    public static Number asValidNumber(Object object) {\r
-       if(object instanceof Number) {\r
-               return (Number)object;\r
-       } else if(object instanceof String) {\r
-               try {\r
-                       return Double.parseDouble((String)object);\r
-                       } catch (NumberFormatException e) {\r
-                       return null;\r
-                       }\r
-       } else if(object instanceof Variant) {\r
-               Object o = ((Variant) object).getValue();\r
-               return asNumber(o);\r
-       } else if (SpreadsheetCell.EMPTY == object) {\r
-               return null;\r
-       }\r
-               \r
-       return null;\r
-       \r
-    }\r
-    \r
-    public static boolean matchCriteria(Object value, Object criteria) {\r
-       if(value==null || criteria==null) return false;\r
-       \r
-       if(value instanceof Variant){\r
-               Double dVal = asDoubleWhereEmptyStringIsZero(value);\r
-               if(dVal==null) value = ((Variant)value).getValue();\r
-               else value = dVal;\r
-       }\r
-       if(criteria instanceof Variant){\r
-               Double dVal = asDoubleWhereEmptyStringIsZero(criteria);\r
-               if(dVal==null) criteria = ((Variant)criteria).getValue();\r
-               else criteria = dVal;\r
-       }\r
-       \r
-       if(criteria instanceof Number && value instanceof Number) {\r
-               Number nc = (asNumber(criteria));\r
-               Number nv = (asNumber(value));\r
-               return nc.equals(nv);\r
-       }\r
-       if(criteria instanceof String){\r
-               boolean nums = false;\r
-               Object valueObj = null;\r
-                       if(value instanceof Number){\r
-                               valueObj = ((Number)value).doubleValue();\r
-                               nums = true;\r
-                       }\r
-                       else valueObj = value.toString();\r
-                       \r
-               String sc = criteria.toString();\r
-                       if(sc.length() >= 3){\r
-                               String oper = sc.substring(0, 2);\r
-                               String criteriaStr = sc.substring(2);\r
-                               Double criteriaNum = null;\r
-                               try {\r
-                                       criteriaNum = Double.parseDouble(criteriaStr);\r
-                                       if(oper.equals("<>")){\r
-                                               if(!nums) return true;\r
-                                       }\r
-                                       else if(!nums) return false;\r
-                                       nums = true;\r
-                               } catch (NumberFormatException e){\r
-                                       if(oper.equals("<>")){\r
-                                               if(nums) return true;\r
-                                       }\r
-                                       else if(nums) return false;\r
-                                       nums = false;\r
-                               }\r
-                               \r
-                               if(oper.equals(">=")){\r
-                                       if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) >= 0 ;\r
-                                       else return ((Number)valueObj).doubleValue() >= criteriaNum;\r
-                               } else if(oper.equals("<=")){\r
-                                       if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) <= 0 ;\r
-                                       else return ((Number)valueObj).doubleValue() <= criteriaNum;\r
-                               } else if(oper.equals("<>")){\r
-                                       if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) != 0 ;\r
-                                       else return ((Number)valueObj).doubleValue() != criteriaNum;\r
-                               }\r
-                       }\r
-               if(sc.length() >= 2){\r
-                       String oper = sc.substring(0, 1);\r
-                       String criteriaStr = sc.substring(1);\r
-                       Double criteriaNum = null;\r
-                       \r
-                       try {\r
-                                       criteriaNum = Double.parseDouble(criteriaStr);\r
-                                       if(!nums) return false;\r
-                                       nums = true;\r
-                               } catch (NumberFormatException e){\r
-                                       if(nums) return false;\r
-                                       nums = false;\r
-                               }\r
-                       if(oper.equals("<")){\r
-                               if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) < 0;\r
-                               else return ((Number)valueObj).doubleValue() < criteriaNum;\r
-                       } else if(oper.equals(">")){\r
-                               if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) > 0;\r
-                               else return ((Number)valueObj).doubleValue() > criteriaNum;\r
-                       } else if(oper.equals("=")){\r
-                               if(!nums) return (valueObj.toString().toLowerCase()).compareTo(criteriaStr.toLowerCase()) == 0;\r
-                               else return ((Number)valueObj).doubleValue() == criteriaNum;\r
-                       }\r
-               }\r
-               return sc.equals(valueObj);\r
-       }\r
-       else if (criteria instanceof Number){\r
-               return false;\r
-       }\r
-       throw new IllegalStateException();\r
-    }\r
-\r
-    public static boolean excelEquals(Object left, Object right) {\r
-       if(left instanceof String) {\r
-               if(right instanceof String) {\r
-                       return ((String) left).toLowerCase().equals(((String) right).toLowerCase());\r
-               }\r
-       }\r
-       return left.equals(right);\r
-    }\r
-    \r
-\r
-    public static List<Variable> possibleConfigurationCellVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {\r
-        List<Variable> rowVariables = possibleConfigurationLineVariables(graph, sheet, range);\r
-        List<Variable> result = new ArrayList<>();\r
-        for (Variable variable : rowVariables) {\r
-            Collection<Variable> children = variable.getChildren(graph);\r
-            for (Variable child : children) {\r
-                if (variableInRange(graph, child, range)) {\r
-                    result.add(child);\r
-                }\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    public static Map<Integer, Resource> possibleConfigurationLineResources(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {\r
-        Variable lines = sheet.getPossibleChild(graph, "Lines");\r
-        if (lines == null)\r
-            throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));\r
-        Resource linesR = lines.getRepresents(graph);\r
-        BTree bt = new BTree(graph, linesR);\r
-        List<Tuple2> tuples = bt.searchRangeBTree(graph, Variant.ofInstance(range.startRow), Variant.ofInstance(range.endRow));\r
-        Map<Integer, Resource> result = new HashMap<>(tuples.size());\r
-        for (Tuple2 tuple : tuples) {\r
-            Integer lineNumber = (Integer)((Variant)tuple.c0).getValue();\r
-            Resource resource = (Resource)tuple.c1;\r
-            result.put(lineNumber, resource);\r
-        }\r
-        return result; \r
-    }\r
-    \r
-    public static List<Variable> possibleConfigurationLineVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {\r
-        Map<Integer, Resource> rows = possibleConfigurationLineResources(graph, sheet, range);\r
-        List<Variable> result = new ArrayList<>(rows.size());\r
-        for (Resource row: rows.values()) {\r
-            Variable lineVar = Variables.getPossibleVariable(graph, row);\r
-            if (lineVar != null)\r
-                result.add(lineVar);\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    public static List<Variable> possibleRunLineVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {\r
-       \r
-       Variable run = sheetRun.getParent(graph);\r
-       \r
-       String sheetName = sheetRun.getName(graph);\r
-       String sessionName = run.getParent(graph).getURI(graph);\r
-       \r
-       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);\r
-       SpreadsheetBook book = realm.getEngine();\r
-       \r
-       SpreadsheetEngine engine = book.getEngine(sheetName);\r
-       if(engine == null) return null;\r
-       \r
-       List<Variable> result = new ArrayList<>();\r
-       \r
-       int end = range.endRow < engine.lines.getMaxRow() ? range.endRow : engine.lines.getMaxRow();\r
-       for (int i = range.startRow; i <= end; i++) {\r
-               SpreadsheetLine line = engine.getLine(i);\r
-               if(line == null)\r
-                       continue;\r
-               \r
-               String path = line.getPath();\r
-               path = line.getPath();\r
-               if(path == null)\r
-                       continue;\r
-               \r
-               Variable lineVariable = run.browse(graph, path);\r
-               if(lineVariable==null)\r
-                       continue;\r
-               result.add(lineVariable);\r
-       }\r
-    \r
-        return result;\r
-    }\r
-    \r
-    public static List<Variable> possibleRunCellVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {\r
-        List<Variable> runLineVariable = possibleRunLineVariables(graph, sheetRun, range);\r
-        List<Variable> result = new ArrayList<>();\r
-        for (Variable variable : runLineVariable) {\r
-//             System.out.println("line: " + variable.getURI(graph));\r
-            for (Variable child : variable.getChildren(graph)) {\r
-//             System.out.print("cell : " + child.getURI(graph));\r
-                if (variableInRange(graph, child, range)) {\r
-                    result.add(child);\r
-                }\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-    \r
-    private static boolean variableInRange(ReadGraph graph, Variable child, Range range) throws DatabaseException {\r
-        String name = child.getName(graph);\r
-        Range childRange = SpreadsheetUtils.decodeCellAbsolute(name);\r
-//        System.out.print(" and range " + childRange);\r
-        if (childRange != null && range.contains(childRange)) {\r
-//             System.out.println(" => range.contains(childRange) = true");\r
-            return true;\r
-        }\r
-//        System.out.println();\r
-        return false;\r
-    }\r
-    \r
-    public static Map<Integer, Resource> createConfigurationLineResources(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);\r
-        \r
-        Variable lines = sheet.getPossibleChild(graph, "Lines");\r
-        if (lines == null)\r
-            throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));\r
-        Resource linesR = lines.getRepresents(graph);\r
-        BTree bt = new BTree(graph, linesR);\r
-        \r
-        Map<Integer, Resource> result = new HashMap<>();\r
-        for (int lineNumber = range.startRow; lineNumber <= range.endRow; lineNumber++) {\r
-            Resource line = graph.newResource();\r
-            graph.claim(line, L0.InstanceOf, null, SHEET.Line);\r
-            graph.claimLiteral(line, L0.HasName, L0.NameOf, L0.String, "Row" + lineNumber, Bindings.STRING);\r
-            bt.insertBTree(graph, Variant.ofInstance(lineNumber), line);\r
-            result.put(lineNumber, line);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    public static List<Variable> getOrCreateConfigurationCellVariables(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {\r
-        \r
-        List<Variable> rows = possibleConfigurationLineVariables(graph, sheet, range);\r
-        if (rows.isEmpty()) {\r
-            createConfigurationLineResources(graph, sheet, range);\r
-            rows = possibleConfigurationLineVariables(graph, sheet, range);\r
-        }\r
-        \r
-        List<Variable> cells = possibleConfigurationCellVariables(graph, sheet, range);\r
-        if (cells.isEmpty()) {\r
-            Iterator<Variable> rowIterator = rows.iterator();\r
-            for (int rowNumber = range.startRow; rowNumber <= range.endRow; rowNumber++) {\r
-                Variable row = rowIterator.next();\r
-                for (int colNumber = range.startColumn; colNumber <= range.endColumn; colNumber++) {\r
-                    String location = SpreadsheetUtils.cellName(rowNumber, colNumber);\r
-                    defaultCreateCell(graph, row, location, new Variant(Bindings.STRING, ""));\r
-                }\r
-            }\r
-        }\r
-        \r
-        cells = possibleConfigurationCellVariables(graph, sheet, range);\r
-        if(cells.isEmpty())\r
-            throw new DatabaseException("Unexpected problem while creating spreadsheet cell at '" + range + "'");\r
-        \r
-        return cells;\r
-    }\r
-    \r
-    private static void defaultCreateCell(WriteGraph graph, Variable parent, String location, Variant value) throws DatabaseException {\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);\r
-        Resource container = parent.getRepresents(graph);\r
-        \r
-        Resource cell = graph.newResource();\r
-        graph.claim(cell, L0.InstanceOf, null, SHEET.TextCell);\r
-        graph.addLiteral(cell, L0.HasName, L0.NameOf, L0.String, location, Bindings.STRING);\r
-        graph.addLiteral(cell, SHEET.Cell_content, SHEET.Cell_content_Inverse, L0.Variant, value, Bindings.VARIANT);\r
-        graph.claim(cell, L0.PartOf, container);\r
-        \r
-        Resource book = Variables.getContext(graph, parent).getRepresents(graph);\r
-        \r
-        \r
-        Collection<Resource> objects = graph.sync(new ObjectsWithType(book, L0.ConsistsOf, SHEET.Style));\r
-        \r
-        int styleId = SpreadsheetStyle.empty().getStyleId();\r
-        Resource style = null;\r
-        for (Resource possibleStyle : objects) {\r
-            int possibleStyleId = graph.getRelatedValue2(possibleStyle, SHEET.Style_id, Bindings.INTEGER);\r
-            if (possibleStyleId == styleId) {\r
-                style = possibleStyle;\r
-                break;\r
-            }\r
-        }\r
-        \r
-        if (style == null) {\r
-            style = graph.newResource();\r
-            graph.claim(style, L0.InstanceOf, null, SHEET.Style);\r
-            graph.claim(style, L0.PartOf, book);\r
-            \r
-            int id = objects.size();\r
-            graph.claimLiteral(style, L0.HasName, "Style_" + id);\r
-            graph.claimLiteral(style, SHEET.Style_id, styleId, Bindings.INTEGER);\r
-        }\r
-        graph.claim(cell, SHEET.Cell_HasStyle, style);\r
-        Layer0Utils.addCommentMetadata(graph, "Created cell on location " + location + " with value " + value.toString());\r
-    }\r
-\r
-    public static Resource createStyle(WriteGraph graph, Resource book, SpreadsheetStyle sstyle) throws DatabaseException {\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-        Resource style = graph.newResource();\r
-        graph.claim(style, L0.InstanceOf, null, SR.Style);\r
-        graph.claim(style, L0.PartOf, book);\r
-        \r
-        int styleId = sstyle.getStyleId();\r
-        String styleName = sstyle.name;\r
-        \r
-        graph.claimLiteral(style, L0.HasName, styleName);\r
-        //System.err.println("CREATING STYLE " + styleName + " WITH ID: " + styleId);\r
-        graph.claimLiteral(style, SR.Style_id, styleId, Bindings.INTEGER);\r
-        \r
-        DatatypeResource DATATYPES = DatatypeResource.getInstance(graph);\r
-        if (sstyle.foreground != null)\r
-            graph.claimLiteral(style, SR.Cell_foreground, DATATYPES.RGB_Integer, sstyle.foreground, RGB.Integer.BINDING);\r
-        if (sstyle.background != null)\r
-            graph.claimLiteral(style, SR.Cell_background, DATATYPES.RGB_Integer, sstyle.background, RGB.Integer.BINDING);\r
-        if (sstyle.align != -1)\r
-            graph.claimLiteral(style, SR.Cell_align, sstyle.align, Bindings.INTEGER);\r
-        if (sstyle.font != null)\r
-            graph.claimLiteral(style, SR.Cell_font, DATATYPES.Font, sstyle.font, Font.BINDING);\r
-        if (sstyle.border != -1)\r
-            graph.claimLiteral(style, SR.Cell_border, sstyle.border);\r
-        if (sstyle.formatString != null && !sstyle.formatString.isEmpty())\r
-            graph.claimLiteral(style, SR.Cell_formatString, sstyle.formatString, Bindings.STRING);\r
-        if (sstyle.formatIndex != -1)\r
-            graph.claimLiteral(style, SR.Cell_formatIndex, sstyle.formatIndex, Bindings.INTEGER);\r
-        \r
-        return style;\r
-    }\r
-    \r
-    public static Resource createBook(WriteGraph graph, Resource parent, String name) throws DatabaseException {\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);\r
-        Resource book = graph.newResource();\r
-        graph.claim(book, L0.InstanceOf, SR.Book);\r
-        graph.claimLiteral(book, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);\r
-        graph.claim(parent, L0.ConsistsOf, book);\r
-        \r
-        return book;\r
-    }\r
-    \r
-    public static Variable constructAndInitializeRunVariable(WriteGraph graph, Resource root) throws DatabaseException {\r
-        Variable run = SpreadsheetUtils.getBookVariable(graph, root);\r
-        SpreadsheetGraphUtils.fullSynchronization(graph, run);\r
-        SpreadsheetGraphUtils.evaluateAll(graph, run);\r
-        SpreadsheetGraphUtils.saveInitialCondition(graph, run, root, "Initial");\r
-        return run;\r
-    }\r
-\r
-}\r
+package org.simantics.spreadsheet.graph;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.soap.Node;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.util.binary.RandomAccessBinary;
+import org.simantics.datatypes.DatatypeResource;
+import org.simantics.datatypes.literal.Font;
+import org.simantics.datatypes.literal.RGB;
+import org.simantics.datatypes.utils.BTree;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.BinaryRead;
+import org.simantics.db.common.request.ObjectsWithType;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.LiteralFileUtil;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ServiceException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.variable.StandardGraphChildVariable;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.VariableNode;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.db.procedure.Listener;
+import org.simantics.db.service.ClusteringSupport;
+import org.simantics.layer0.Layer0;
+import org.simantics.scl.runtime.SCLContext;
+import org.simantics.scl.runtime.function.Function;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.scl.runtime.tuple.Tuple;
+import org.simantics.scl.runtime.tuple.Tuple0;
+import org.simantics.scl.runtime.tuple.Tuple2;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.simantics.spreadsheet.CellEditor;
+import org.simantics.spreadsheet.ExternalRef;
+import org.simantics.spreadsheet.OperationMode;
+import org.simantics.spreadsheet.Range;
+import org.simantics.spreadsheet.Spreadsheets;
+import org.simantics.spreadsheet.Transaction;
+import org.simantics.spreadsheet.graph.synchronization.SpreadsheetSynchronizationEventHandler;
+import org.simantics.spreadsheet.resource.SpreadsheetResource;
+import org.simantics.spreadsheet.solver.SheetNode;
+import org.simantics.spreadsheet.solver.SpreadsheetBook;
+import org.simantics.spreadsheet.solver.SpreadsheetCell;
+import org.simantics.spreadsheet.solver.SpreadsheetEngine;
+import org.simantics.spreadsheet.solver.SpreadsheetLine;
+import org.simantics.spreadsheet.solver.SpreadsheetStyle;
+import org.simantics.spreadsheet.util.SpreadsheetUtils;
+import org.simantics.structural.synchronization.client.Synchronizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.iterator.TObjectIntIterator;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
+public class SpreadsheetGraphUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetGraphUtils.class);
+
+    public static File extractInitialCondition(ReadGraph graph, Resource ic) throws DatabaseException, IOException {
+
+       SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+
+        File temp = Simantics.getTempfile("excel","ic");
+
+        LiteralFileUtil.copyRandomAccessBinaryToFile(graph, ic, SR.InitialCondition_bytes, temp);
+        if (temp.length() == 0)
+            throw new FileNotFoundException("Snapshot file does not exist.\nThis seems to be a database bug that manifests as total loss of state file data.\nThis error prevents the program from crashing.");
+
+        return temp;
+        
+    }
+       
+    public static RandomAccessBinary getOrCreateRandomAccessBinary(WriteGraph graph, Resource initialCondition) throws DatabaseException, IOException {
+
+       SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+       
+        // We put snapshot literals in their own clusters for now just to be safe
+        Resource literal = graph.getPossibleObject(initialCondition, SR.InitialCondition_bytes);
+        if (literal != null) {
+            RandomAccessBinary rab = graph.getRandomAccessBinary(literal);
+            rab.position(0);
+            rab.removeBytes(rab.length(), RandomAccessBinary.ByteSide.Right);
+            return rab;
+        } else {
+            Layer0 L0 = Layer0.getInstance(graph);
+            ClusteringSupport cs = graph.getService(ClusteringSupport.class);
+            literal = graph.newResource(cs.createCluster());
+            graph.claim(literal, L0.InstanceOf, null, L0.ByteArray);
+            graph.claim(initialCondition, SR.InitialCondition_bytes, SR.InitialCondition_bytes_Inverse, literal);
+            return graph.createRandomAccessBinary(literal, Bindings.BYTE_ARRAY.type(), null);
+        }
+    }
+
+    public static Resource saveInitialCondition(WriteGraph graph, Variable run, Resource container, String name) throws DatabaseException {
+
+               String sessionName = run.getParent(graph).getURI(graph);
+
+               Resource bookResource = run.getRepresents(graph);
+               
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+
+       try {
+       
+               File temp = Simantics.getTempfile("excel", "ic");
+               System.err.println("Saving initial condition to " + temp.getAbsolutePath());
+               
+                       FileOutputStream fileOut = new FileOutputStream(temp);
+                       ObjectOutputStream out = new ObjectOutputStream(fileOut);
+                       out.writeObject(book);
+                       out.close();
+                       fileOut.close();
+                       
+                       Layer0 L0 = Layer0.getInstance(graph);
+               SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+                       Resource ic = graph.newResource();
+                       graph.claim(ic, L0.InstanceOf, SR.InitialCondition);
+                       graph.addLiteral(ic, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
+               
+               RandomAccessBinary rab = getOrCreateRandomAccessBinary(graph, ic);
+               LiteralFileUtil.copyRandomAccessBinaryFromFile(temp, rab);
+               
+                       graph.claim(container, L0.ConsistsOf, L0.PartOf, ic);
+                       
+                       graph.deny(bookResource, SR.HasInitialCondition);
+                       graph.claim(bookResource, SR.HasInitialCondition, ic);
+                       graph.claim(ic, SR.InitialCondition_ConditionOf, bookResource);
+                       
+                       setDefaultInitialConditionForBook(graph, bookResource, ic);
+
+               return ic;
+               
+       } catch (IOException e) {
+               
+               throw new DatabaseException(e);
+               
+       }
+    }
+    
+    public static void setDefaultInitialConditionForBook(WriteGraph graph, Resource book, Resource ic) throws ServiceException {
+        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+        graph.deny(book, SR.Book_HasDefaultInitialCondition);
+        graph.claim(ic, SR.InitialCondition_DefaultConditionOf, book);
+    }
+
+    public static void evaluateAll(ReadGraph graph, Variable run) throws DatabaseException {
+
+               String sessionName = run.getParent(graph).getURI(graph);
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+       book.accept(new EvaluateAll(book));
+       
+    }
+
+    public static void invalidateAll(ReadGraph graph, Variable run) throws DatabaseException {
+
+               String sessionName = run.getParent(graph).getURI(graph);
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+       book.accept(new InvalidateAll());
+       realm.getNodeManager().refreshVariables();
+       
+    }
+    
+    public static boolean fullSynchronization(ReadGraph graph, Variable run) throws DatabaseException {
+        return partialSynchronization(graph, run, null);
+    }
+
+    public static boolean partialSynchronization(ReadGraph graph, Variable run, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
+
+       Synchronizer synchronizer = new Synchronizer(graph);
+               String sessionName = run.getParent(graph).getURI(graph);
+               
+       Resource bookResource = run.getRepresents(graph);
+       Variable configuration = Variables.getVariable(graph, bookResource);
+       
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+                       
+        SpreadsheetSynchronizationEventHandler handler = new SpreadsheetSynchronizationEventHandler(graph, book);
+        
+        if (changeFlags == null) {
+            synchronizer.fullSynchronization(configuration, handler);
+        } else {
+            
+            TObjectIntIterator<Variable> iter = changeFlags.iterator();
+            iter.advance();
+            Variable row = iter.key();
+            
+            Variable rowParent = row.getParent(graph);
+            while (!rowParent.equals(configuration)) {
+                changeFlags.put(rowParent, 1);
+                rowParent = rowParent.getParent(graph);
+            }
+            
+            changeFlags.put(configuration, 1);
+            
+            synchronizer.partialSynchronization(configuration, handler, changeFlags);
+        }
+        
+        realm.getNodeManager().fireNodeListeners();
+        return handler.getDidChanges();
+        
+    }
+
+    public static Variable findCell(ReadGraph graph, Variable run, String reference) throws DatabaseException {
+
+       int pos = reference.indexOf("!");
+       String sheetName = reference.substring(0, pos);
+       String cellName = reference.substring(pos+1);
+
+               String sessionName = run.getParent(graph).getURI(graph);
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+       SpreadsheetEngine engine = book.getEngine(sheetName);
+       if(engine == null) return null;
+       
+       Range r = Spreadsheets.decodeCellAbsolute(cellName);
+       SpreadsheetLine line = engine.getLine(r.startRow);
+       if(line == null) return null;
+       
+       String path = line.getPath();
+       if(path == null) return null;
+       
+       Variable lineVariable = run.browse(graph, path);
+       if(lineVariable==null) return null;
+       
+       return lineVariable.getChild(graph, cellName);
+       
+    }
+
+    public static void forRows(ReadGraph graph, Variable run, String sheetName, int min, int max, Function1<Variable, Tuple> fn) throws DatabaseException {
+
+        String sessionName = run.getParent(graph).getURI(graph);
+        StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+        SpreadsheetBook book = realm.getEngine();
+        SpreadsheetEngine engine = book.getEngine(sheetName);
+        if(engine == null) return;
+        
+        engine.forLines(line -> {
+
+            String path = line.getPath();
+            if(path == null) return;
+
+            try {
+                Variable lineVariable = run.browse(graph, path);
+                if(lineVariable != null)
+                    fn.apply(lineVariable);
+            } catch (DatabaseException e) {
+                // This is not reported here
+            }
+
+        } , min, max);
+        
+    }
+    
+    public static List<Variable> possibleConfigurationCellVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
+        List<Variable> rowVariables = possibleConfigurationLineVariables(graph, sheet, range);
+        List<Variable> result = new ArrayList<>();
+        for (Variable variable : rowVariables) {
+            Collection<Variable> children = variable.getChildren(graph);
+            for (Variable child : children) {
+                if (variableInRange(graph, child, range)) {
+                    result.add(child);
+                }
+            }
+        }
+        return result;
+    }
+    
+    public static Map<Integer, Resource> possibleConfigurationLineResources(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
+        Variable lines = sheet.getPossibleChild(graph, "Lines");
+        if (lines == null)
+            throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));
+        Resource linesR = lines.getRepresents(graph);
+        BTree bt = new BTree(graph, linesR);
+        List<Tuple2> tuples = bt.searchRangeBTree(graph, Variant.ofInstance(range.startRow), Variant.ofInstance(range.endRow));
+        Map<Integer, Resource> result = new HashMap<>(tuples.size());
+        for (Tuple2 tuple : tuples) {
+            Integer lineNumber = (Integer)((Variant)tuple.c0).getValue();
+            Resource resource = (Resource)tuple.c1;
+            result.put(lineNumber, resource);
+        }
+        return result; 
+    }
+    
+    public static List<Variable> possibleConfigurationLineVariables(ReadGraph graph, Variable sheet, Range range) throws DatabaseException {
+        Map<Integer, Resource> rows = possibleConfigurationLineResources(graph, sheet, range);
+        List<Variable> result = new ArrayList<>(rows.size());
+        for (Resource row: rows.values()) {
+            Variable lineVar = Variables.getPossibleVariable(graph, row);
+            if (lineVar != null)
+                result.add(lineVar);
+        }
+        return result;
+    }
+    
+    public static List<Variable> possibleRunLineVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {
+       
+       Variable run = sheetRun.getParent(graph);
+       
+       String sheetName = sheetRun.getName(graph);
+       String sessionName = run.getParent(graph).getURI(graph);
+       
+       StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
+       SpreadsheetBook book = realm.getEngine();
+       
+       SpreadsheetEngine engine = book.getEngine(sheetName);
+       if(engine == null) return null;
+       
+       List<Variable> result = new ArrayList<>();
+       
+       int end = range.endRow < engine.lines.getMaxRow() ? range.endRow : engine.lines.getMaxRow();
+       for (int i = range.startRow; i <= end; i++) {
+               SpreadsheetLine line = engine.getLine(i);
+               if(line == null)
+                       continue;
+               
+               String path = line.getPath();
+               path = line.getPath();
+               if(path == null)
+                       continue;
+               
+               Variable lineVariable = run.browse(graph, path);
+               if(lineVariable==null)
+                       continue;
+               result.add(lineVariable);
+       }
+    
+        return result;
+    }
+    
+    public static List<Variable> possibleRunCellVariables(ReadGraph graph, Variable sheetRun, Range range) throws DatabaseException {
+        List<Variable> runLineVariable = possibleRunLineVariables(graph, sheetRun, range);
+        List<Variable> result = new ArrayList<>();
+        for (Variable variable : runLineVariable) {
+//             System.out.println("line: " + variable.getURI(graph));
+            for (Variable child : variable.getChildren(graph)) {
+//             System.out.print("cell : " + child.getURI(graph));
+                if (variableInRange(graph, child, range)) {
+                    result.add(child);
+                }
+            }
+        }
+        return result;
+    }
+    
+    private static boolean variableInRange(ReadGraph graph, Variable child, Range range) throws DatabaseException {
+        String name = child.getName(graph);
+        Range childRange = Spreadsheets.decodeCellAbsolute(name);
+//        System.out.print(" and range " + childRange);
+        if (childRange != null && range.contains(childRange)) {
+//             System.out.println(" => range.contains(childRange) = true");
+            return true;
+        }
+//        System.out.println();
+        return false;
+    }
+    
+    public static Map<Integer, Resource> createConfigurationLineResources(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
+        
+        Variable lines = sheet.getPossibleChild(graph, "Lines");
+        if (lines == null)
+            throw new DatabaseException("Invalid input variable " + sheet.getURI(graph));
+        Resource linesR = lines.getRepresents(graph);
+        BTree bt = new BTree(graph, linesR);
+        
+        Map<Integer, Resource> result = new HashMap<>();
+        for (int lineNumber = range.startRow; lineNumber <= range.endRow; lineNumber++) {
+            Resource line = graph.newResource();
+            graph.claim(line, L0.InstanceOf, null, SHEET.Line);
+            graph.claimLiteral(line, L0.HasName, L0.NameOf, L0.String, "Row" + lineNumber, Bindings.STRING);
+            bt.insertBTree(graph, Variant.ofInstance(lineNumber), line);
+            result.put(lineNumber, line);
+        }
+        return result;
+    }
+
+    public static List<Variable> getOrCreateConfigurationCellVariables(WriteGraph graph, Variable sheet, Range range) throws DatabaseException {
+        
+        List<Variable> rows = possibleConfigurationLineVariables(graph, sheet, range);
+        if (rows.isEmpty()) {
+            createConfigurationLineResources(graph, sheet, range);
+            rows = possibleConfigurationLineVariables(graph, sheet, range);
+        }
+        
+        List<Variable> cells = possibleConfigurationCellVariables(graph, sheet, range);
+        if (cells.isEmpty()) {
+            Iterator<Variable> rowIterator = rows.iterator();
+            for (int rowNumber = range.startRow; rowNumber <= range.endRow; rowNumber++) {
+                Variable row = rowIterator.next();
+                for (int colNumber = range.startColumn; colNumber <= range.endColumn; colNumber++) {
+                    String location = Spreadsheets.cellName(rowNumber, colNumber);
+                    defaultCreateCell(graph, row, location, new Variant(Bindings.STRING, ""));
+                }
+            }
+        }
+        
+        cells = possibleConfigurationCellVariables(graph, sheet, range);
+        if(cells.isEmpty())
+            throw new DatabaseException("Unexpected problem while creating spreadsheet cell at '" + range + "'");
+        
+        return cells;
+    }
+    
+    private static void defaultCreateCell(WriteGraph graph, Variable parent, String location, Variant value) throws DatabaseException {
+
+        Layer0 L0 = Layer0.getInstance(graph);
+        SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
+        Resource container = parent.getRepresents(graph);
+        
+        Resource cell = graph.newResource();
+        graph.claim(cell, L0.InstanceOf, null, SHEET.TextCell);
+        graph.addLiteral(cell, L0.HasName, L0.NameOf, L0.String, location, Bindings.STRING);
+        graph.addLiteral(cell, SHEET.Cell_content, SHEET.Cell_content_Inverse, L0.Variant, value, Bindings.VARIANT);
+        graph.claim(cell, L0.PartOf, container);
+        
+        Resource book = Variables.getContext(graph, parent).getRepresents(graph);
+        
+        
+        Collection<Resource> objects = graph.sync(new ObjectsWithType(book, L0.ConsistsOf, SHEET.Style));
+        
+        int styleId = SpreadsheetStyle.empty().getStyleId();
+        Resource style = null;
+        for (Resource possibleStyle : objects) {
+            int possibleStyleId = graph.getRelatedValue2(possibleStyle, SHEET.Style_id, Bindings.INTEGER);
+            if (possibleStyleId == styleId) {
+                style = possibleStyle;
+                break;
+            }
+        }
+        
+        if (style == null) {
+            style = graph.newResource();
+            graph.claim(style, L0.InstanceOf, null, SHEET.Style);
+            graph.claim(style, L0.PartOf, book);
+            
+            int id = objects.size();
+            graph.claimLiteral(style, L0.HasName, "Style_" + id);
+            graph.claimLiteral(style, SHEET.Style_id, styleId, Bindings.INTEGER);
+        }
+        graph.claim(cell, SHEET.Cell_HasStyle, style);
+        Layer0Utils.addCommentMetadata(graph, "Created cell on location " + location + " with value " + value.toString());
+    }
+
+    public static Resource createStyle(WriteGraph graph, Resource book, SpreadsheetStyle sstyle) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+        Resource style = graph.newResource();
+        graph.claim(style, L0.InstanceOf, null, SR.Style);
+        graph.claim(style, L0.PartOf, book);
+        
+        int styleId = sstyle.getStyleId();
+        String styleName = sstyle.name;
+        
+        graph.claimLiteral(style, L0.HasName, styleName);
+        //System.err.println("CREATING STYLE " + styleName + " WITH ID: " + styleId);
+        graph.claimLiteral(style, SR.Style_id, styleId, Bindings.INTEGER);
+        
+        DatatypeResource DATATYPES = DatatypeResource.getInstance(graph);
+        if (sstyle.foreground != null)
+            graph.claimLiteral(style, SR.Cell_foreground, DATATYPES.RGB_Integer, sstyle.foreground, RGB.Integer.BINDING);
+        if (sstyle.background != null)
+            graph.claimLiteral(style, SR.Cell_background, DATATYPES.RGB_Integer, sstyle.background, RGB.Integer.BINDING);
+        if (sstyle.align != -1)
+            graph.claimLiteral(style, SR.Cell_align, sstyle.align, Bindings.INTEGER);
+        if (sstyle.font != null)
+            graph.claimLiteral(style, SR.Cell_font, DATATYPES.Font, sstyle.font, Font.BINDING);
+        if (sstyle.border != -1)
+            graph.claimLiteral(style, SR.Cell_border, sstyle.border);
+        if (sstyle.formatString != null && !sstyle.formatString.isEmpty())
+            graph.claimLiteral(style, SR.Cell_formatString, sstyle.formatString, Bindings.STRING);
+        if (sstyle.formatIndex != -1)
+            graph.claimLiteral(style, SR.Cell_formatIndex, sstyle.formatIndex, Bindings.INTEGER);
+        
+        return style;
+    }
+    
+    public static Resource createBook(WriteGraph graph, Resource parent, String name) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
+        Resource book = graph.newResource();
+        graph.claim(book, L0.InstanceOf, SR.Book);
+        graph.claimLiteral(book, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
+        graph.claim(parent, L0.ConsistsOf, book);
+        
+        return book;
+    }
+    
+    public static Variable constructAndInitializeRunVariable(WriteGraph graph, Resource root) throws DatabaseException {
+        Variable run = SpreadsheetUtils.getBookVariable(graph, root);
+        SpreadsheetGraphUtils.fullSynchronization(graph, run);
+        SpreadsheetGraphUtils.evaluateAll(graph, run);
+        SpreadsheetGraphUtils.saveInitialCondition(graph, run, root, "Initial");
+        return run;
+    }
+    
+    public static Variant extRefVariable(ReadGraph graph, Variable var) throws DatabaseException {
+        return new Variant(Bindings.VOID, new ExternalRefVariable(graph, var));
+    }
+    
+    static class ExternalRefVariable implements ExternalRef {
+
+        final private String uri;
+        
+        public ExternalRefVariable(ReadGraph graph, Variable variable) throws DatabaseException {
+            this.uri = variable.getURI(graph);
+        }
+        
+        @Override
+        public void listen(Object context, ExternalRefListener listener) {
+            Simantics.getSession().asyncRequest(new UnaryRead<String, Variant>(uri) {
+
+                @Override
+                public Variant perform(ReadGraph graph) throws DatabaseException {
+                    Variable variable = Variables.getVariable(graph, parameter);
+                    return variable.getVariantValue(graph);
+                }
+                
+            }, new Listener<Variant>() {
+
+                @Override
+                public void execute(Variant result) {
+                    listener.newValue(result);
+                }
+
+                @Override
+                public void exception(Throwable t) {
+                    LOGGER.error("Error while evaluating variable value", t);
+                }
+
+                @Override
+                public boolean isDisposed() {
+                    return listener.isDisposed();
+                }
+                
+            });
+        }
+        
+        @Override
+        public void modify(Object context, Variant newValue) {
+            
+            Simantics.getSession().asyncRequest(new WriteRequest() {
+
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    Variable variable = Variables.getVariable(graph, uri);
+                    variable.setValue(graph, newValue);
+                }
+            });
+            
+        }
+        
+    }
+
+    public static Variant extRefActiveVariable(ReadGraph graph, Variable var) throws DatabaseException {
+        return new Variant(Bindings.VOID, new ExternalRefActiveVariable(graph, var));
+    }
+    
+    static class ExternalRefActiveVariable implements ExternalRef {
+
+        final private String uri;
+        
+        public ExternalRefActiveVariable(ReadGraph graph, Variable variable) throws DatabaseException {
+            this.uri = variable.getURI(graph);
+        }
+        
+        @Override
+        public void listen(Object context, ExternalRefListener listener) {
+            Simantics.getSession().asyncRequest(new BinaryRead<String, String, Variant>((String)context, uri) {
+
+                @Override
+                public Variant perform(ReadGraph graph) throws DatabaseException {
+                    Variable contextVariable = Variables.getVariable(graph, parameter);
+                    Variable configVariable = Variables.getVariable(graph, parameter2);
+                    Variable activeVariable = Variables.switchPossibleContext(graph, configVariable, contextVariable.getRepresents(graph));
+                    if(activeVariable == null) return Variant.ofInstance("Could not resolve " + configVariable.getURI(graph) + " for " + contextVariable.getURI(graph));
+                    return activeVariable.getVariantValue(graph);
+                }
+            }, new Listener<Variant>() {
+
+                @Override
+                public void execute(Variant result) {
+                    listener.newValue(result);
+                }
+
+                @Override
+                public void exception(Throwable t) {
+                    LOGGER.error("Error while evaluating variable value, context = " + context + " uri=" + uri, t);
+                }
+
+                @Override
+                public boolean isDisposed() {
+                    return listener.isDisposed();
+                }
+                
+            });
+        }
+        
+        @Override
+        public void modify(Object context, Variant newValue) {
+            
+            Simantics.getSession().asyncRequest(new WriteRequest() {
+
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    Variable contextVariable = Variables.getVariable(graph, (String)context);
+                    Variable configVariable = Variables.getVariable(graph,uri);
+                    Variable activeVariable = Variables.switchPossibleContext(graph, configVariable, contextVariable.getRepresents(graph));
+                    if(activeVariable == null) return;
+                    activeVariable.setValue(graph, newValue.getValue(), newValue.getBinding());
+                }
+            });
+            
+        }
+        
+    }
+    
+    public static CellEditor cellEditor(ReadGraph graph, Resource sheet) throws DatabaseException {
+        SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
+        Variable sheetVariable = Variables.getVariable(graph, sheet);
+        return sheetVariable.getPropertyValue(graph, SHEET.cellEditor);
+    }
+    
+    public static final String SPREADSHEET_TRANSACTION = "spreadsheetTransaction";
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static Object syncExec(CellEditor editor, OperationMode mode, Function fun) throws InterruptedException {
+        
+        Transaction tr = editor.startTransaction(mode);
+        
+        SCLContext context = SCLContext.getCurrent();
+        Transaction oldTransaction = (Transaction)context.put(SPREADSHEET_TRANSACTION, tr);
+        
+        Object result = null;
+        
+        try {
+
+            result = fun.apply(Tuple0.INSTANCE);
+            
+        } finally {
+            
+            tr.commit();
+            
+            context.put(SPREADSHEET_TRANSACTION, oldTransaction);
+            
+        }
+        
+        return result;
+        
+    }
+    
+    public static int cellColumn(ReadGraph graph, Variable cell) {
+        if(cell instanceof StandardGraphChildVariable) {
+            StandardGraphChildVariable sgcv = (StandardGraphChildVariable)cell;
+            SpreadsheetCell sc = (SpreadsheetCell)sgcv.node.node;
+            return sc.getColumn();
+        }
+        throw new IllegalStateException("Expected StandardGraphChildVariable, got " + cell.getClass().getName());
+    }
+    
+    private static SpreadsheetCell getCellFromVariable(Variable cell) {
+        StandardGraphChildVariable std =  (StandardGraphChildVariable)cell;
+        return (SpreadsheetCell)std.node.node;
+    }
+    
+    private static SpreadsheetLine getLineFromVariable(Variable cell) {
+        StandardGraphChildVariable std =  (StandardGraphChildVariable)cell;
+        return (SpreadsheetLine)std.node.node;
+    }
+
+    public static Variable linesVariable(ReadGraph graph, Variable sheetVariable) throws DatabaseException {
+        while(!"Lines".equals(sheetVariable.getName(graph)))
+            sheetVariable = sheetVariable.getParent(graph);
+        return sheetVariable;
+    }
+    
+    public static Variable offsetCell(ReadGraph graph, Variable cellVariable, int x, int y) throws DatabaseException {
+        
+        Variable lineVariable = cellVariable.getParent(graph);
+        Variable offsetLine = offsetRow(graph, lineVariable, y);
+        if(offsetLine == null) return null;
+        SpreadsheetCell cell = getCellFromVariable(cellVariable);
+        return rowCell(graph, offsetLine, cell.column + x);
+        
+    }
+
+    public static Variable offsetRow(ReadGraph graph, Variable lineVariable, int offset) throws DatabaseException {
+        
+        if(offset == 0) return lineVariable;
+
+        SpreadsheetLine line = getLineFromVariable(lineVariable);
+        SpreadsheetLine offsetLine = line.possibleOffset(offset);
+        if(offsetLine == null) return null;
+        
+        Variable linesVariable = linesVariable(graph, lineVariable);
+        String path = offsetLine.getLinesPath();
+        return linesVariable.browsePossible(graph, path);
+
+    }
+    
+    public static Variable rowCell(ReadGraph graph, Variable lineVariable, int column) throws DatabaseException {
+        
+        SpreadsheetLine line = getLineFromVariable(lineVariable);
+        
+        return lineVariable.getPossibleChild(graph, Spreadsheets.cellName(line.row, column));
+        
+    }
+    
+    public static SpreadsheetBook spreadsheetBook(Variable variable) {
+        if(variable instanceof StandardGraphChildVariable) {
+            VariableNode<Node> node = ((StandardGraphChildVariable)variable).node;
+            if(node != null) {
+                if(node.node instanceof SpreadsheetBook)
+                    return (SpreadsheetBook) node.node;
+            }
+        }
+        return null;
+    }
+    
+}