]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/function/All.java
Adopt spreadsheet changes made in Balas development
[simantics/platform.git] / bundles / org.simantics.spreadsheet.graph / src / org / simantics / spreadsheet / graph / function / All.java
1 package org.simantics.spreadsheet.graph.function;
2
3 import java.io.StringReader;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.function.Consumer;
13
14 import org.simantics.databoard.Bindings;
15 import org.simantics.databoard.Databoard;
16 import org.simantics.databoard.adapter.AdaptException;
17 import org.simantics.databoard.binding.Binding;
18 import org.simantics.databoard.binding.mutable.MutableVariant;
19 import org.simantics.databoard.binding.mutable.Variant;
20 import org.simantics.databoard.type.Datatype;
21 import org.simantics.datatypes.literal.Font;
22 import org.simantics.datatypes.literal.RGB;
23 import org.simantics.datatypes.utils.BTreeContentBean;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.WriteGraph;
27 import org.simantics.db.common.request.ObjectsWithType;
28 import org.simantics.db.common.request.WriteRequest;
29 import org.simantics.db.common.utils.Logger;
30 import org.simantics.db.common.utils.NameUtils;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.layer0.exception.MissingVariableException;
33 import org.simantics.db.layer0.function.StandardChildDomainChildren;
34 import org.simantics.db.layer0.request.PossibleActiveRun;
35 import org.simantics.db.layer0.util.Layer0Utils;
36 import org.simantics.db.layer0.variable.ConstantChildVariable;
37 import org.simantics.db.layer0.variable.ConstantPropertyVariableBuilder;
38 import org.simantics.db.layer0.variable.ProxyChildVariable;
39 import org.simantics.db.layer0.variable.ProxyVariables;
40 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
41 import org.simantics.db.layer0.variable.ValueAccessor;
42 import org.simantics.db.layer0.variable.Variable;
43 import org.simantics.db.layer0.variable.VariableMap;
44 import org.simantics.db.layer0.variable.VariableMapImpl;
45 import org.simantics.db.layer0.variable.Variables;
46 import org.simantics.db.request.Write;
47 import org.simantics.document.server.io.IColor;
48 import org.simantics.document.server.io.IFont;
49 import org.simantics.document.server.io.ITableCell;
50 import org.simantics.layer0.Layer0;
51 import org.simantics.scl.reflection.annotations.SCLValue;
52 import org.simantics.simulator.toolkit.StandardRealm;
53 import org.simantics.simulator.variable.exceptions.NodeManagerException;
54 import org.simantics.spreadsheet.CellEditor;
55 import org.simantics.spreadsheet.ClientModel;
56 import org.simantics.spreadsheet.Range;
57 import org.simantics.spreadsheet.Transaction;
58 import org.simantics.spreadsheet.graph.SpreadsheetGraphUtils;
59 import org.simantics.spreadsheet.graph.SpreadsheetSessionManager;
60 import org.simantics.spreadsheet.graph.celleditor.GraphCellEditorAdapter;
61 import org.simantics.spreadsheet.resource.SpreadsheetResource;
62 import org.simantics.spreadsheet.solver.SheetNode;
63 import org.simantics.spreadsheet.solver.SpreadsheetBook;
64 import org.simantics.spreadsheet.solver.SpreadsheetCell;
65 import org.simantics.spreadsheet.solver.SpreadsheetCellContent;
66 import org.simantics.spreadsheet.solver.SpreadsheetFormula;
67 import org.simantics.spreadsheet.solver.SpreadsheetSCLConstant;
68 import org.simantics.spreadsheet.solver.SpreadsheetStyle;
69 import org.simantics.spreadsheet.solver.SpreadsheetStyle.SpreadsheetStyleBuilder;
70 import org.simantics.spreadsheet.solver.formula.parser.ParseException;
71 import org.simantics.spreadsheet.solver.formula.parser.SheetFormulaParser;
72 import org.simantics.spreadsheet.solver.formula.parser.ast.AstValue;
73 import org.simantics.spreadsheet.synchronization.ExcelFormula;
74 import org.simantics.spreadsheet.synchronization.LineContentBean;
75 import org.simantics.spreadsheet.synchronization.LineContentBeanCell;
76 import org.simantics.spreadsheet.util.SpreadsheetUtils;
77
78 import gnu.trove.map.TMap;
79 import gnu.trove.map.hash.THashMap;
80 import it.unimi.dsi.fastutil.objects.ObjectArrayList;
81
82 public class All {
83
84     @SCLValue(type = "ReadGraph -> Resource -> a -> String")
85     public static String cellLabel(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
86         if(context instanceof Resource) {
87                 return NameUtils.getSafeLabel(graph, ((Resource)context));      
88         } else if (context instanceof Variable) {
89                 Variable parent = ((Variable)context).getParent(graph);
90                 Variable content =  parent.getPossibleProperty(graph, ClientModel.CONTENT);
91                 if(content != null) {
92                 Databoard db = graph.getService(Databoard.class);
93                         Variant variant = content.getValue(graph, db.VARIANT);
94                         return variant.getValue().toString();
95                 } else {
96                         return parent.getName(graph);
97                 }
98         } else {
99                 throw new DatabaseException("Unknown context " + context);
100         }
101     }
102
103         private static Set<String> CLASSIFICATIONS = new HashSet<String>();
104         private static ConstantPropertyVariableBuilder immutableBuilder = new ConstantPropertyVariableBuilder("immutable", true, Bindings.BOOLEAN); 
105         
106         static {
107                 CLASSIFICATIONS.add(SpreadsheetResource.URIs.Attribute);
108         }
109
110     @SCLValue(type = "ValueAccessor")
111         public static ValueAccessor contentValueAccessor = new ValueAccessor() {
112                 
113                 @Override
114                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
115                     System.out.println("contentValueAccessor.context=" + context.getURI(graph));
116                         if(value instanceof String) {
117                                 
118                                 // Expressions are given as string
119                                 String text = (String)value;
120                                 SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
121                                 if (text.startsWith("==")) {
122                         if(!Layer0Utils.setOrClearExpression(graph, context, text.substring(1), SHEET.SCLValue)) {
123                             org.simantics.db.layer0.function.All.standardSetValue3(graph, context, Variant.ofInstance(value), Bindings.VARIANT);
124                             //StandardValueAccessor.setValue(graph, context, Variant.ofInstance(value), Bindings.VARIANT);
125                             //org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, Variant.ofInstance(value), Bindings.VARIANT);
126                         }
127                         return;
128                                 } else {
129                                     
130                                     Variable cell = context.getParent(graph);
131                                     System.out.println("setValue : " + cell.getURI(graph));
132                                     
133                     String formula = text.substring(1);
134                     
135                     if (ProxyVariables.isProxy(graph, context)) {
136                         try {
137                             SheetFormulaParser p = new SheetFormulaParser(new StringReader(formula));
138                             AstValue v = p.relation();
139                             SpreadsheetFormula sformula = new SpreadsheetFormula(v, formula);
140                             setValueToEngine(graph, cell, context, sformula, binding);
141                         } catch (ParseException e) {
142                             e.printStackTrace();
143                         }
144                         return;
145                     }
146                     
147                     Variant v = new Variant(ExcelFormula.BINDING, new ExcelFormula(formula));
148                     graph.claimLiteral(cell.getRepresents(graph), SHEET.Cell_content, SHEET.Cell_content_Inverse, Layer0.getInstance(graph).Variant, v, Bindings.VARIANT);
149                                 }
150                         } else {
151                                 
152                                 if(ProxyVariables.isProxy(graph, context)) {
153                                         
154                                         Variable cell = context.getParent(graph);
155                                         setValueToEngine(graph, cell, context, value, binding);
156                                         return;
157                                 }
158                                 
159                                 // Values are given as Variant
160                                 String expression = context.getPossiblePropertyValue(graph, "expression");
161                                 if(expression != null) {
162                                         Object current_ = context.getPossibleValue(graph);
163                                         if(current_ instanceof Variable) {
164                                                 Variable current = (Variable)current_;
165                                                 Variant variant = (Variant)value;
166                                                 Datatype dt = current.getDatatype(graph);
167                                                 if (dt == null) {
168                                                         throw new DatabaseException();
169                                                 }
170                                                 Binding variableBinding = Bindings.getBinding(dt);
171                                                 try {
172                                                         Object adapted = variant.getValue(variableBinding);
173                                                         current.setValue(graph, adapted, variableBinding);
174                                                 } catch (AdaptException e) {
175                                                         Logger.defaultLogError(e);
176                                                 }
177                                                 return;
178                                         }
179                                         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
180                                         Layer0Utils.clearExpression(graph, context, SHEET.SCLValue);
181                                 }
182                                 org.simantics.db.layer0.function.All.standardSetValue3(graph, context, value, binding);
183                                 //org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, value, binding);
184                         }
185                 }
186                 
187                 private void setValueToEngine(WriteGraph graph, Variable cell, Variable context, Object value, Binding binding) throws DatabaseException {
188             Variable sheet = cell.getParent(graph);
189
190             SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
191
192             while(!sheet.getType(graph).equals(SHEET.Spreadsheet)) {
193                 sheet = sheet.getParent(graph);
194             }
195             
196             Range r = SpreadsheetUtils.decodeCellAbsolute(cell.getName(graph));
197             
198             Variable root = ProxyVariables.proxyVariableRoot(graph, context);
199             
200             String sessionName = root.getParent(graph).getURI(graph);
201             StandardRealm<SheetNode, SpreadsheetBook> realm = SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
202             SpreadsheetBook book = realm.getEngine();
203             SpreadsheetCell sc = book.get(sheet.getName(graph), r.startRow, r.startColumn);
204             sc.setContent(value);
205 //          book.accept(new InvalidateAll());
206 //                      List<SpreadsheetCell> changed = book.invalidate(sc); //Invalidation handled by SpreadsheetNodeManager
207             realm.asyncExec(new Runnable() {
208
209                 @Override
210                 public void run() {
211                     try {
212                         SpreadsheetCellContent content = (SpreadsheetCellContent)sc.getProperties().get("content");
213                         realm.getNodeManager().setValue(content, value, binding);
214 //                      for(SpreadsheetCell cell : changed) {
215 //                          content = (SpreadsheetCellContent)cell.getProperties().get("content");
216 //                          realm.getNodeManager().setValue(content, value, binding);
217 //                      }
218                     } catch (NodeManagerException e) {
219                         Logger.defaultLogError(e);
220                     }
221                 }
222             });
223                 }
224                 
225                 @Override
226                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
227                         if(value instanceof String) setValue(graph, context, value, Bindings.STRING);
228                         else if(value instanceof Variant) setValue(graph, context, value, Bindings.VARIANT);
229                         else throw new DatabaseException("Unsupported value type " + value);
230                 }
231                 
232                 @Override
233                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
234                         return org.simantics.db.layer0.function.All.standardGetValue2(graph, context, binding);
235                         //return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context, binding);
236                 }
237                 
238                 @Override
239                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
240                         return org.simantics.db.layer0.function.All.standardGetValue1(graph, ((StandardGraphPropertyVariable)context));
241 //                      return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context);
242                 }
243
244                 @Override
245                 public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
246                         return org.simantics.db.layer0.function.All.standardGetDatatype(graph, context);
247 //                      return org.simantics.db.layer0.function.All.standardValueAccessor.getDatatype(graph, context);
248                 }
249                 
250         };
251
252     @SCLValue(type = "ValueAccessor")
253         public static ValueAccessor contentDisplayValueAccessor = new ValueAccessor() {
254                 
255                 @Override
256                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
257                         if (!Bindings.STRING.equals(binding)) throw new IllegalArgumentException();
258                         if (!(value instanceof String)) throw new IllegalArgumentException();
259                         
260                         if (((String)value).startsWith("=")) {
261                                 context.getParent(graph).setValue(graph, value, Bindings.STRING);
262                         } else {
263                                 context.getParent(graph).setValue(graph, new Variant(Bindings.STRING, value), Bindings.VARIANT);
264                         }
265                 }
266                 
267                 @Override
268                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
269                         if (!(value instanceof String)) throw new IllegalArgumentException();
270                         
271                         if (((String)value).startsWith("=")) {
272                                 context.getParent(graph).setValue(graph, value, Bindings.STRING);
273                         } else {
274                                 context.getParent(graph).setValue(graph, new Variant(Bindings.STRING, value), Bindings.VARIANT);
275                         }
276                 }
277                 
278                 @Override
279                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
280                         return context.getParent(graph).getValue(graph, binding);
281                 }
282                 
283                 @Override
284                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
285                         return context.getParent(graph).getValue(graph);
286                 }
287
288                 @Override
289                 public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
290                         return context.getParent(graph).getDatatype(graph);
291                 }
292                 
293         };
294
295         @SCLValue(type = "VariableMap")
296         public static VariableMap stringArrayChildren = new VariableMapImpl() {
297         
298                 @Override
299                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
300                         
301                         TMap<String, Variable> map = new THashMap<String, Variable>();
302                         getVariables(graph, context, map);
303                         return map.get(name);                                   
304                         
305                 }
306
307                 @Override
308                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
309                         
310                         Resource resource = context.getRepresents(graph);
311                         
312                 SpreadsheetResource sr = SpreadsheetResource.getInstance(graph);
313                 
314                 String location = graph.getPossibleRelatedValue(resource, sr.Range_location, Bindings.STRING);
315                 if(location == null) return map;
316                 Integer width = graph.getPossibleRelatedValue(resource, sr.Range_widthBound, Bindings.INTEGER);
317                 if(width == null) return map;
318                 String[] array = graph.getPossibleRelatedValue(resource, sr.StringArrayRange_array, Bindings.STRING_ARRAY);
319                 if(array == null) return map;
320                 
321                 int rows = array.length / width;
322                 
323                 if(map == null) map = new HashMap<String,Variable>();
324                 
325                 for(int offset=0,i=0;i<rows;i++) {
326                         for(int j=0;j<width;j++) {
327                                 
328                                 String value = array[offset++];
329                                 String valueLocation = SpreadsheetUtils.offset(location, i, j);
330
331                                 ConstantPropertyVariableBuilder labelBuilder = new ConstantPropertyVariableBuilder(ClientModel.LABEL, value, Bindings.STRING, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS);
332                                 ConstantPropertyVariableBuilder typeBuilder = new ConstantPropertyVariableBuilder(Variables.TYPE, sr.Cell, null, Collections.<ConstantPropertyVariableBuilder>emptyList(), Collections.<String>emptySet());
333                                 
334                                 map.put(valueLocation, new ConstantChildVariable(context, valueLocation, labelBuilder, typeBuilder, immutableBuilder));
335                                 
336                         }
337                 }
338                 
339                 return map;
340                 
341                 }
342                 
343     };
344
345         @SCLValue(type = "VariableMap")
346         public static VariableMap queryRangeChildren = new VariableMapImpl() {
347         
348                 @Override
349                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
350                         
351                         TMap<String, Variable> map = new THashMap<String, Variable>();
352                         getVariables(graph, context, map);
353                         return map.get(name);                                   
354                         
355                 }
356
357                 @Override
358                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
359                         
360                 SpreadsheetResource sr = SpreadsheetResource.getInstance(graph);
361     
362                 String location = "A1";
363                 
364                 try {
365                         
366                         Object object = context.getPropertyValue(graph, sr.ExpressionRange_cells);
367                         
368                         List<?> data = (List<?>)object;
369                         
370                         if(map == null) map = new HashMap<String,Variable>();
371                         
372                         for(Object o : data) {
373                                 if(o instanceof ITableCell) {
374
375                                         ITableCell cell = (ITableCell)o;
376                                         
377                                         String valueLocation = SpreadsheetUtils.offset(location, cell.getRow(), cell.getColumn());
378
379                                         ArrayList<ConstantPropertyVariableBuilder> builders = new ArrayList<ConstantPropertyVariableBuilder>();
380                                         
381                                         builders.add(new ConstantPropertyVariableBuilder(ClientModel.CONTENT, Variant.ofInstance(cell.getText()), Bindings.VARIANT, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS));
382                                         builders.add(new ConstantPropertyVariableBuilder(Variables.TYPE, sr.Cell, null, Collections.<ConstantPropertyVariableBuilder>emptyList(), Collections.<String>emptySet()));
383                                         
384                                         IFont font = cell.getFont();
385                                         if(font != null) {
386                                                 builders.add(new ConstantPropertyVariableBuilder(ClientModel.FONT, new Font(font.getFamily(), font.getHeight(), font.getStyle()), Font.BINDING, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS));
387                                         }
388
389                                         int align = cell.getAlign();
390                                         builders.add(new ConstantPropertyVariableBuilder(ClientModel.ALIGN, align, Bindings.INTEGER, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS));
391                                         
392                                         IColor foreground = cell.getFGColor();
393                                         if(foreground != null) {
394                                                 builders.add(new ConstantPropertyVariableBuilder(ClientModel.FOREGROUND, new RGB.Integer(foreground.red(), foreground.green(), foreground.blue()), RGB.Integer.BINDING, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS));
395                                         }
396
397                                         IColor background = cell.getBGColor();
398                                         if(background != null) {
399                                                 builders.add(new ConstantPropertyVariableBuilder(ClientModel.BACKGROUND, new RGB.Integer(background.red(), background.green(), background.blue()), RGB.Integer.BINDING, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS));
400                                         }
401
402                                         map.put(valueLocation, new ConstantChildVariable(context, valueLocation, builders));
403
404                                 }
405                         }
406                         
407                 } catch (DatabaseException e) {
408                         throw (DatabaseException)e;
409                 } catch (Throwable t) {
410                         throw new DatabaseException(t);
411                 } finally {
412                 }
413
414                 return map;
415                 
416                 }
417                 
418     };
419
420         @SCLValue(type = "VariableMap")
421         public static VariableMap spreadsheetLinesChildren = new StandardChildDomainChildren() {
422         
423                 @Override
424                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
425                         
426                         if(ProxyVariables.isProxy(graph, context))
427                                 return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, null, name);
428                         
429                         return super.getVariable(graph, context, name);
430                         
431 //                      TMap<String, Variable> map = new THashMap<String, Variable>();
432 //                      getVariables(graph, context, map);
433 //                      return map.get(name);
434
435                 }
436
437                 @Override
438                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
439
440                         if(ProxyVariables.isProxy(graph, context))
441                                 return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, Collections.emptyMap(), map);
442
443 //              Resource lines = context.getRepresents(graph);
444 //              
445 //              BTree bt = new BTree(graph, lines, true);
446 //              List<Tuple2> entries = bt.entriesOfBTree(graph);
447 //              for(Tuple2 tuple : entries) {
448 //                      Variant v = (Variant)tuple.get(0);
449 //                      Resource line = (Resource)tuple.get(1);
450 //                      String name = v.getValue().toString();
451 //                      Variable child = org.simantics.db.layer0.function.All.getStandardChildDomainChildVariable(graph, context, line, name);
452 //                      if(map == null) map = new THashMap<String,Variable>();
453 //                      map.put(name, child);
454 //              }
455 //    
456 //              return map;
457
458 //                      return org.simantics.db.layer0.function.All.standardChildDomainChildren.getVariables(graph, context, map);
459
460                         return super.getVariables(graph, context, map);
461                         
462                 }
463                 
464     };
465     
466     @SCLValue(type = "VariableMap")
467         public static VariableMap doubleArrayChildren = new VariableMapImpl() {
468         
469                 @Override
470                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
471                         
472                         TMap<String, Variable> map = new THashMap<String, Variable>();
473                         getVariables(graph, context, map);
474                         return map.get(name);                                   
475                         
476                 }
477
478                 @Override
479                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
480                         
481                         Resource resource = context.getRepresents(graph);
482                         
483                 SpreadsheetResource sr = SpreadsheetResource.getInstance(graph);
484                 
485                 String location = graph.getPossibleRelatedValue(resource, sr.Range_location, Bindings.STRING);
486                 if(location == null) return map;
487                 Integer width = graph.getPossibleRelatedValue(resource, sr.Range_widthBound, Bindings.INTEGER);
488                 if(width == null) return map;
489                 double[] array = graph.getPossibleRelatedValue(resource, sr.DoubleArrayRange_array, Bindings.DOUBLE_ARRAY);
490                 if(array == null) return map;
491                 
492                 if(map == null) map = new HashMap<String,Variable>();
493                 
494                 int rows = array.length / width;
495                 
496                 for(int offset=0,i=0;i<rows;i++) {
497                         for(int j=0;j<width;j++) {
498                                 
499                                 double value = array[offset++];
500                                 String valueLocation = SpreadsheetUtils.offset(location, i, j);
501
502                                 ConstantPropertyVariableBuilder labelBuilder = new ConstantPropertyVariableBuilder(ClientModel.LABEL, String.valueOf(value), Bindings.STRING, Collections.<ConstantPropertyVariableBuilder>emptyList(), CLASSIFICATIONS);
503                                 ConstantPropertyVariableBuilder typeBuilder = new ConstantPropertyVariableBuilder(Variables.TYPE, sr.Cell, null, Collections.<ConstantPropertyVariableBuilder>emptyList(), Collections.<String>emptySet());
504                                 
505                                 map.put(valueLocation, new ConstantChildVariable(context, valueLocation, labelBuilder, typeBuilder, immutableBuilder));
506                                 
507                         }
508                 }
509
510                 return map;
511                 
512                 }
513                 
514     };
515
516     static class SpreadsheetProxyChildVariable extends ProxyChildVariable {
517
518         public SpreadsheetProxyChildVariable(Variable base, Variable parent, Variable other, String name) {
519             super(base, parent, other, name);
520         }
521     
522         @Override
523         public Variable create(Variable base, Variable parent, Variable other, String name) {
524             return new SpreadsheetProxyChildVariable(base, parent, other, name);
525         }
526         
527         public Variable getPossibleChild(ReadGraph graph, String name) throws DatabaseException {
528             
529                 if(CONTEXT_END.equals(name)) {
530                 if(other instanceof ProxyChildVariable) {
531                         // The context is also a proxy - let it do the job
532                     return super.getPossibleChild(graph, name);
533                 } else {
534                         return org.simantics.db.layer0.function.All.buildChildVariable(graph, this, base.getRepresents(graph), null);
535                 }
536                 }
537             
538             return super.getPossibleChild(graph, name);
539             
540         }
541         
542         public Collection<Variable> getChildren(ReadGraph graph) throws DatabaseException {
543
544             Collection<Variable> result = super.getChildren(graph);
545             if(!(base instanceof ProxyChildVariable)) {
546                 Variable root = org.simantics.db.layer0.function.All.buildChildVariable(graph, this, base.getRepresents(graph), null);
547                 result.add(root);
548             }
549             return result;
550             
551         }       
552              
553     }
554
555     @SCLValue(type = "VariableMap")
556     public static VariableMap spreadsheetChildren = new VariableMapImpl() {
557
558         @Override
559         public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
560                 
561                 if(ProxyChildVariable.CONTEXT_BEGIN.equals(name)) return getProxy(graph, context);
562             return org.simantics.db.layer0.function.All.standardChildDomainChildren.getVariable(graph, context, name);
563             
564         }
565
566         private Variable getProxy(ReadGraph graph, Variable context) throws DatabaseException {
567             Variable root = Variables.getRootVariable(graph);
568             return new SpreadsheetProxyChildVariable(context, context, root, ProxyChildVariable.CONTEXT_BEGIN);
569         }
570
571
572         @Override
573         public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map)
574                 throws DatabaseException {
575                 
576             map = org.simantics.db.layer0.function.All.standardChildDomainChildren.getVariables(graph, context, map);
577             
578             if(map == null) map = new HashMap<String,Variable>();
579             map.put(ProxyChildVariable.CONTEXT_BEGIN, getProxy(graph, context));
580             return map;
581         }
582
583     };
584
585    
586     @SCLValue(type = "ReadGraph -> Resource -> Variable -> CellEditor")
587     public static CellEditor<Write> defaultSheetCellEditor(ReadGraph graph, Resource resource, final Variable context_) throws DatabaseException {
588
589         final Variable sheet = context_.getParent(graph);
590         
591         return new GraphCellEditorAdapter(null) {
592
593                 @Override
594                 public <T> void edit(final Transaction<Write> transaction, final String location, final String property, final T value, final Binding binding, Consumer<?> callback) {
595
596                         SpreadsheetUtils.schedule(transaction, new WriteRequest() {
597                                 
598                                 @Override
599                                 public void perform(WriteGraph graph) throws DatabaseException {
600                                         CellEditor<Write> editor = getPossibleCellEditor(graph, location, value == null ? null : new Variant(binding, value));
601                                         if (editor == null)
602                                             return;
603                                         editor.edit(transaction, location, property, value, binding, callback);
604                                 }
605                         });
606                         
607                 }
608                 
609                 @Override
610                 public void edit(final Transaction<Write> transaction, final String location, final Variant value, Consumer<?> callback) {
611                         SpreadsheetUtils.schedule(transaction, new WriteRequest() {
612                                 
613                                 @Override
614                                 public void perform(WriteGraph graph) throws DatabaseException {
615                                     CellEditor<Write> editor = getPossibleCellEditor(graph, location, value);
616                                     if (editor == null)
617                                         return;
618                                         editor.edit(transaction, location, value, callback);
619                                 }
620                         });
621                         
622                 }
623                 
624             private CellEditor<Write> getPossibleCellEditor(WriteGraph graph, String location, Variant value) throws DatabaseException {
625                 SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
626                     Range range = SpreadsheetUtils.decodePossibleCellAbsolute(location);
627                     if(range == null) return null; //No editor found
628                     
629                     List<Variable> cells = SpreadsheetGraphUtils.possibleConfigurationCellVariables(graph, sheet, range);
630                     if (cells.isEmpty()) {
631                         if (value == null) {
632                             return null;
633                         } else {
634                             cells = SpreadsheetGraphUtils.getOrCreateConfigurationCellVariables(graph, sheet, range);
635                         }
636                     }
637                     if (cells.size() != 1)
638                         throw new DatabaseException("Can edit only one cell at a time!");
639                         
640                     return cells.iterator().next().getPropertyValue(graph, SHEET.cellEditor);
641             }
642
643                 @Override
644                 public void copy(final Transaction<Write> transaction, final String location, final MutableVariant variant, Consumer<?> callback) {
645
646                         SpreadsheetUtils.schedule(transaction, new WriteRequest() {
647
648                                 @Override
649                                 public void perform(WriteGraph graph) throws DatabaseException {
650
651                                         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
652                                         Variable variable = sheet.getPossibleChild(graph, location);
653                                         if(variable != null) {
654                                                         CellEditor<Write> editor = variable.getPossiblePropertyValue(graph, SHEET.cellEditor);
655                                                         if(editor != null) {
656                                                                 editor.copy(transaction, location, variant, null);
657                                                         }
658                                         }
659                                 }
660
661                         });
662
663                 }
664                 
665         };
666         
667     }
668
669     @SCLValue(type = "ReadGraph -> Resource -> Variable -> CellEditor")
670     public static CellEditor<Write> variableCellEditor(ReadGraph graph, Resource resource, final Variable context_) throws DatabaseException {
671
672         final Variable cell = context_.getParent(graph);
673         
674         return new GraphCellEditorAdapter(cell) {
675
676                 @Override
677                 public <T> void edit(WriteGraph graph, Transaction<Write> transaction, String location, String property, T value, Binding binding) throws DatabaseException {
678                         cell.setPropertyValue(graph, property, value, binding);
679                 }
680         };
681     }
682     
683     private static int encodeLineOrNode(ReadGraph graph, Resource r) throws DatabaseException {
684         if(r == null) return 0;
685         Layer0 L0 = Layer0.getInstance(graph);
686                 String name = graph.getRelatedValue(r, L0.HasName, Bindings.STRING);
687                 if(name.charAt(0) == 'R') {
688                 return -Integer.parseInt(name.substring(3));
689                 } else {
690                         return Integer.parseInt(name);
691                 }
692     }
693
694     @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
695     public static int[] lineNodeKeys(ReadGraph graph, Resource resource, final Variable context_) throws DatabaseException {
696
697         Resource node = context_.getParent(graph).getRepresents(graph);
698         BTreeContentBean bean = BTreeContentBean.readPossible(graph, node);
699         if(bean == null) return new int[0];
700         // There are n keys and n+1 resources
701         int[] result = new int[2*bean.n+1];
702         for(int i=0;i<bean.n;i++) {
703                 result[2*i] = encodeLineOrNode(graph, bean.getChild(i));
704                 result[2*i+1] = (int)bean.getKey(i).getValue();
705         }
706         result[2*bean.n] = encodeLineOrNode(graph, bean.getChild(bean.n));
707         return result;
708         
709     }
710
711     @SCLValue(type = "ReadGraph -> Resource -> Variable -> LineContentBean")
712     public static LineContentBean defaultLineCells(ReadGraph graph, Resource resource, final Variable context_) throws DatabaseException {
713         
714         String contextUri = context_.getURI(graph);
715         
716         Variable line = context_.getParent(graph);
717         
718         Collection<Variable> children = line.getChildren(graph);
719         ObjectArrayList<LineContentBeanCell> result = new ObjectArrayList<>();
720         SpreadsheetResource SR = SpreadsheetResource.getInstance(graph);
721         for (Variable child : children) {
722             Resource repr = child.getRepresents(graph);
723             
724             Resource style = graph.getPossibleObject(repr, SR.Cell_HasStyle);
725             Integer styleId = null;
726             if (style != null)
727                 styleId = graph.getPossibleRelatedValue(style, SR.Style_id, Bindings.INTEGER);
728             if (styleId == null) {
729                 System.err.println("Style " + style + " has no ID or either cell "+ repr + " has no style attached to it !!");
730                 styleId = SpreadsheetStyle.empty().getStyleId();
731             }
732
733                 LineContentBeanCell cell = new LineContentBeanCell(styleId);
734                 
735                 Variant variant = child.getPossiblePropertyValue(graph, SR.Cell_content);
736                 
737             if(variant != null) {
738                 
739                 Variable var = child.getPossibleProperty(graph, SR.Cell_content);
740                 String expression = var.getPossiblePropertyValue(graph, "expression");
741                 if (expression != null) {
742                     cell.setContent(new Variant(SpreadsheetSCLConstant.BINDING, new SpreadsheetSCLConstant(expression, variant.getValue())));
743                 } else {
744                     cell.setContent(variant);
745                 }
746                 Range r = SpreadsheetUtils.decodeCellAbsolute(child.getName(graph));
747 //                result.add(cell);
748                 while(result.size() < r.startColumn + 1)
749                     result.add(new LineContentBeanCell());
750                 result.set(r.startColumn, cell);
751                     
752             }
753
754         }
755         LineContentBean bean = new LineContentBean();
756         bean.cells = result.toArray(new LineContentBeanCell[result.size()]);
757         return bean;
758         
759     }
760     
761     @SCLValue(type = "ReadGraph -> Resource -> Variable -> CellEditor")
762     public static CellEditor<Write> textCellEditor(ReadGraph graph, Resource resource, final Variable context_) throws DatabaseException {
763
764         System.out.println("Context URI : " + context_.getURI(graph));
765         Variable cells = context_.getParent(graph);
766         System.out.println("Cell URI : " + cells.getURI(graph));
767         
768         return new GraphCellEditorAdapter(cells) {
769
770                 public <T> void edit(WriteGraph graph, Transaction<Write> transaction, String location, String property, T value, Binding binding) throws DatabaseException {
771                     
772                         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
773                         if(ClientModel.CONTENT.equals(property)) {
774                                 cell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.VARIANT);
775                     Variable runCell = null;
776                     Object transactionContext = transaction.getContext();
777                     if (transactionContext != null && transactionContext instanceof Variable) {
778                         Variable varContext = (Variable) transactionContext;
779                         Variable context = Variables.getContext(graph, varContext);
780                         try {
781                             runCell = Variables.switchRealization(graph, cell, context.getRepresents(graph), context);
782                         } catch (MissingVariableException e) {
783                             // Creating new cell, need synchronizing
784                             transaction.needSynchronization(cell.getParent(graph));
785                         }
786                     }
787                                 if (runCell != null)
788                                     runCell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.VARIANT);
789                         } else if(ClientModel.CONTENT_EXPRESSION.equals(property)) {
790                                 cell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.STRING);
791                     Variable runCell = null;
792                     Object transactionContext = transaction.getContext();
793                     if (transactionContext != null && transactionContext instanceof Variable) {
794                         Variable varContext = (Variable) transactionContext;
795                         Variable context = Variables.getContext(graph, varContext);
796                         try {
797                             runCell = Variables.switchRealization(graph, cell, context.getRepresents(graph), context);
798                         } catch (MissingVariableException e) {
799                             //Creating new cell, need synchronizing
800                             transaction.needSynchronization(cell.getParent(graph));
801                         }
802                     }
803                                 if (runCell != null)
804                                     runCell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.STRING);
805                         } else if(ClientModel.BORDER.equals(property)) {
806                     Resource textCell = cell.getRepresents(graph);
807                     Resource styleContainer = graph.getSingleObject(textCell, SHEET.Cell_HasStyle);
808                     SpreadsheetStyleBuilder builder = computeStyleBuilder(SHEET, graph, styleContainer);
809                     builder.border((Integer)value);
810                     finishStyleUpdate(graph, SHEET, styleContainer, textCell, builder, transaction);
811                         } else if(ClientModel.ALIGN.equals(property)) {
812                     Resource textCell = cell.getRepresents(graph);
813                     Resource styleContainer = graph.getSingleObject(textCell, SHEET.Cell_HasStyle);
814                     SpreadsheetStyleBuilder builder = computeStyleBuilder(SHEET, graph, styleContainer);
815                     builder.align((Integer)value);
816                     finishStyleUpdate(graph, SHEET, styleContainer, textCell, builder, transaction);
817                         } else if(ClientModel.LOCKED.equals(property)) {
818                                 cell.setPropertyValue(graph, SHEET.Cell_locked, value, Bindings.BOOLEAN);       
819                         } else if(ClientModel.ROW_SPAN.equals(property)) {
820                                 cell.setPropertyValue(graph, SHEET.Cell_rowSpan, value, Bindings.INTEGER);
821                         } else if(ClientModel.COLUMN_SPAN.equals(property)) {
822                                 cell.setPropertyValue(graph, SHEET.Cell_columnSpan, value, Bindings.INTEGER);
823                         } else if(ClientModel.FONT.equals(property)) {
824                     Resource textCell = cell.getRepresents(graph);
825                     Resource styleContainer = graph.getSingleObject(textCell, SHEET.Cell_HasStyle);
826                     SpreadsheetStyleBuilder builder = computeStyleBuilder(SHEET, graph, styleContainer);
827                     builder.font((Font)value);
828                     finishStyleUpdate(graph, SHEET, styleContainer, textCell, builder, transaction);
829                         } else if(ClientModel.FOREGROUND.equals(property)) {
830                             Resource textCell = cell.getRepresents(graph);
831                             Resource styleContainer = graph.getSingleObject(textCell, SHEET.Cell_HasStyle);
832                             SpreadsheetStyleBuilder builder = computeStyleBuilder(SHEET, graph, styleContainer);
833                             builder.foreground((RGB.Integer)value);
834                             finishStyleUpdate(graph, SHEET, styleContainer, textCell, builder, transaction);
835                         } else if(ClientModel.BACKGROUND.equals(property)) {
836                     Resource textCell = cell.getRepresents(graph);
837                     Resource styleContainer = graph.getSingleObject(textCell, SHEET.Cell_HasStyle);
838                     SpreadsheetStyleBuilder builder = computeStyleBuilder(SHEET, graph, styleContainer);
839                     builder.background((RGB.Integer)value);
840                     finishStyleUpdate(graph, SHEET, styleContainer, textCell, builder, transaction);
841                         }
842                 }
843                 
844                 private void finishStyleUpdate(WriteGraph graph, SpreadsheetResource SHEET, Resource styleContainer, Resource textCell, SpreadsheetStyleBuilder builder, Transaction<?> transaction) throws DatabaseException {
845                 
846                 Variable bookVariable = Variables.getContext(graph, cell);
847                 Resource book = bookVariable.getRepresents(graph);
848                 Resource createdStyle = null;
849                 
850                 SpreadsheetStyle style = builder.build();
851                 int styleId = style.getStyleId();
852
853                 Collection<Resource> existingStyles = graph.syncRequest(new ObjectsWithType(book, Layer0.getInstance(graph).ConsistsOf, SHEET.Style));
854                 for (Resource eStyle : existingStyles) {
855                     int eStyleId = graph.getRelatedValue2(eStyle, SHEET.Style_id, Bindings.INTEGER);
856                     if (eStyleId == styleId) {
857                         createdStyle = eStyle;
858                         break;
859                     }
860                 }
861                 if (createdStyle == null) {
862                         style = builder.name("Style_" + existingStyles.size()).build();
863                     createdStyle = SpreadsheetGraphUtils.createStyle(graph, book, style);
864                 }
865                 
866                 graph.deny(textCell, SHEET.Cell_HasStyle);
867                 Collection<Resource> cellsOfStyle = graph.getObjects(styleContainer, SHEET.Cell_StyleOf);
868                 if (cellsOfStyle.isEmpty()) {
869                     graph.deny(styleContainer);
870                 }
871                 graph.claim(textCell, SHEET.Cell_HasStyle, createdStyle);
872                 
873 //                Variable runCell = null;
874 //                Object transactionContext = transaction.getContext();
875 //                if (transactionContext != null && transactionContext instanceof Variable) {
876 //                    Variable varContext = (Variable) transactionContext;
877 //                    Variable context = Variables.getContext(graph, varContext);
878 //                    try {
879 //                        runCell = Variables.switchRealization(graph, cell, context.getRepresents(graph), context);
880 //                    } catch (MissingVariableException e) {
881 //                        //Creating new cell, need synchronizing
882 //                        transaction.needSynchronization(cell.getParent(graph));
883 //                    }
884 //                }
885 //                if (runCell != null) {
886 //                    Datatype type = new RecordType();
887 //                    Binding b = Bindings.getBinding(type);
888 //                    runCell.setPropertyValue(graph, SHEET.Cell_style, style, b);
889 //                }
890             }
891
892             private SpreadsheetStyleBuilder computeStyleBuilder(SpreadsheetResource SHEET, WriteGraph graph, Resource styleContainer) throws DatabaseException {
893                 RGB.Integer foreground = graph.getPossibleRelatedValue2(styleContainer, SHEET.Cell_foreground, RGB.Integer.BINDING);
894                 RGB.Integer background = graph.getPossibleRelatedValue2(styleContainer, SHEET.Cell_background, RGB.Integer.BINDING);
895                 Font font = graph.getPossibleRelatedValue2(styleContainer, SHEET.Cell_font, Font.BINDING);
896                 Integer align = graph.getPossibleRelatedValue2(styleContainer, SHEET.Cell_align, Bindings.INTEGER);
897                 Integer border = graph.getPossibleRelatedValue2(styleContainer, SHEET.Cell_border, Bindings.INTEGER);
898                 
899                 return SpreadsheetStyle.newInstace().foreground(foreground).background(background).font(font).align(align).border(border);
900             }
901
902             @Override
903                 public <T> void edit(WriteGraph graph, Transaction<Write> transaction, String location, Variant value) throws DatabaseException {
904                         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
905                         
906                         // Handle possible deletes
907                         if (value == null)
908                             value = Variant.ofInstance("");
909                         
910                 if (!transaction.isOperationMode()) {
911                     cell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.VARIANT);
912 //                    return;
913                 }
914                 Variable runCell = null;
915                 Object transactionContext = transaction.getContext();
916                 if (transactionContext != null && transactionContext instanceof Variable) {
917                     Variable varContext = (Variable) transactionContext;
918                     Variable context = Variables.getContext(graph, varContext);
919                     try {
920                         runCell = Variables.switchRealization(graph, cell, context.getRepresents(graph), context);
921                     } catch (MissingVariableException e) {
922                         // Creating cell for the first time so no runCell available at this time, needs synchronization
923                         transaction.needSynchronization(cell.getParent(graph));
924                     }
925                     
926                 }
927
928                         if (runCell != null) {
929                             System.out.println("All.edit " + runCell.getURI(graph));
930                             runCell.setPropertyValue(graph, SHEET.Cell_content, value, Bindings.VARIANT);
931                         }
932                 }
933                 
934                 @Override
935                 public <T> void copy(WriteGraph graph, Transaction<Write> transaction, String location, MutableVariant variant) throws DatabaseException {
936                         SpreadsheetResource SHEET = SpreadsheetResource.getInstance(graph);
937                         
938                 Variable runCell = null;
939                 Object transactionContext = transaction.getContext();
940                 if (transactionContext != null && transactionContext instanceof Variable) {
941                     Variable varContext = (Variable) transactionContext;
942                     Variable context = Variables.getContext(graph, varContext);
943                     runCell = Variables.switchRealization(graph, cell, context.getRepresents(graph), context);
944                 }
945                         
946                         //Variant content = cell.getPropertyValue(graph, SHEET.Cell_content, Bindings.VARIANT);
947                         Object object = cell.getPropertyValue(graph, SHEET.Cell_content);
948                         Variant content = null;
949                         if (object instanceof Variant) {
950                             content = (Variant)object;
951                         } else if (object instanceof Double) {
952                             content = Variant.ofInstance((Double)object);
953                         } else if (object instanceof Float) {
954                                 content = Variant.ofInstance((Float)object);
955                         } else if (object instanceof Integer) {
956                             content = Variant.ofInstance((Integer)object);
957                         } else if (object instanceof Long) {
958                             content = Variant.ofInstance((Long)object);
959                         } else if (object instanceof String) {
960                             content = Variant.ofInstance((String)object);
961                         } else if (object instanceof Variable) {
962                             content = Variant.ofInstance((Variable)object);
963                         } else {
964                             throw new DatabaseException("");
965                         }
966                         variant.setValue(content);
967                 }
968                 
969         };
970         
971     }
972     
973     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
974     public static Variable spreadsheetInput(ReadGraph graph, Resource converter, Variable sheet) throws DatabaseException {
975                 return ProxyVariables.inputVariable(graph, sheet);
976     }
977
978     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
979     public static Variable spreadsheetSession(ReadGraph graph, Resource converter, Variable sheet) throws DatabaseException {
980                 return ProxyVariables.proxySessionVariable(graph, sheet);
981     }
982
983     @SCLValue(type = "ReadGraph -> Resource -> Variable -> Variable")
984     public static Variable spreadsheetRunInput(ReadGraph graph, Resource converter, Variable property) throws DatabaseException {
985         Resource model = Variables.getModel(graph, property);
986         Variable activeRun = graph.syncRequest(new PossibleActiveRun(model));
987         if(activeRun != null) return activeRun;
988         return Variables.getConfigurationContext(graph, model);
989     }
990     
991         @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
992     public static Object sclValue(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
993             return CompileSCLValueRequest.compileAndEvaluate(graph, context);
994     }
995
996 }