String formatter property using SCL function instead of adapter
[simantics/platform.git] / bundles / org.simantics.selectionview / src / org / simantics / selectionview / function / All.java
1 package org.simantics.selectionview.function;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Map;
6 import java.util.function.Consumer;
7
8 import org.eclipse.swt.graphics.FontData;
9 import org.eclipse.swt.graphics.RGB;
10 import org.eclipse.swt.widgets.ColorDialog;
11 import org.eclipse.swt.widgets.Control;
12 import org.eclipse.swt.widgets.FontDialog;
13 import org.simantics.Simantics;
14 import org.simantics.browsing.ui.NodeContext;
15 import org.simantics.browsing.ui.content.Labeler.DialogModifier;
16 import org.simantics.common.format.Formatter;
17 import org.simantics.databoard.Bindings;
18 import org.simantics.databoard.Datatypes;
19 import org.simantics.databoard.adapter.AdaptException;
20 import org.simantics.databoard.binding.Binding;
21 import org.simantics.databoard.binding.NumberBinding;
22 import org.simantics.databoard.binding.StringBinding;
23 import org.simantics.databoard.binding.error.BindingConstructionException;
24 import org.simantics.databoard.binding.error.BindingException;
25 import org.simantics.databoard.binding.mutable.MutableStringBinding;
26 import org.simantics.databoard.binding.mutable.Variant;
27 import org.simantics.databoard.parser.DataValuePrinter;
28 import org.simantics.databoard.parser.repository.DataTypeSyntaxError;
29 import org.simantics.databoard.parser.repository.DataValueRepository;
30 import org.simantics.databoard.primitives.MutableString;
31 import org.simantics.databoard.type.Datatype;
32 import org.simantics.databoard.util.ObjectUtils;
33 import org.simantics.datatypes.literal.Font;
34 import org.simantics.db.ReadGraph;
35 import org.simantics.db.Resource;
36 import org.simantics.db.Statement;
37 import org.simantics.db.WriteGraph;
38 import org.simantics.db.common.CommentMetadata;
39 import org.simantics.db.common.request.EnumerationMap;
40 import org.simantics.db.common.request.InstanceEnumerationMap;
41 import org.simantics.db.common.request.IsEnumeratedValue;
42 import org.simantics.db.common.request.UniqueRead;
43 import org.simantics.db.common.request.WriteRequest;
44 import org.simantics.db.common.utils.NameUtils;
45 import org.simantics.db.exception.DatabaseException;
46 import org.simantics.db.layer0.util.Layer0Utils;
47 import org.simantics.db.layer0.variable.ValueAccessor;
48 import org.simantics.db.layer0.variable.Variable;
49 import org.simantics.db.layer0.variable.Variables;
50 import org.simantics.layer0.Layer0;
51 import org.simantics.modeling.ModelingResources;
52 import org.simantics.scl.compiler.types.TVar;
53 import org.simantics.scl.compiler.types.Type;
54 import org.simantics.scl.compiler.types.Types;
55 import org.simantics.scl.reflection.annotations.SCLValue;
56 import org.simantics.scl.runtime.function.Function1;
57 import org.simantics.selectionview.SelectionInput;
58 import org.simantics.selectionview.SelectionViewResources;
59 import org.simantics.selectionview.StandardSelectionInput;
60 import org.simantics.ui.colors.Colors;
61 import org.simantics.ui.fonts.Fonts;
62 import org.simantics.ui.selection.WorkbenchSelectionElement;
63 import org.simantics.ui.selection.WorkbenchSelectionUtils;
64 import org.simantics.utils.datastructures.collections.CollectionUtils;
65 import org.simantics.utils.ui.AdaptionUtils;
66 import org.simantics.utils.ui.ErrorLogger;
67 import org.simantics.utils.ui.ISelectionUtils;
68
69 public class All {
70
71         final private static Binding datatype_binging = Bindings.getBindingUnchecked(Datatype.class);
72
73         @SCLValue(type = "ReadGraph -> Resource -> Variable -> b")
74         public static Object colorModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
75                 return new DialogModifier() {
76
77                         @Override
78                         public String getValue() {
79                                 return null;
80                         }
81
82                         @Override
83                         public String isValid(String label) {
84                                 return null;
85                         }
86
87                         @Override
88                         public void modify(final String label) {
89                                 Simantics.getSession().async(new WriteRequest() {
90
91                                         @Override
92                                         public void perform(WriteGraph graph) throws DatabaseException {
93                                                 Variable displayValue = context.getParent(graph);
94                                                 displayValue.setValue(graph, label, org.simantics.datatypes.literal.RGB.Integer.BINDING);
95                                         }
96
97                                 });
98                         }
99
100                         public String query(Object parentControl, Object controlItem, int columnIndex, NodeContext context, Consumer<String> applyCallback) {
101                                 Control ctrl = (Control) parentControl;
102
103                                 RGB initialValue = null;
104                                 final Variable v = AdaptionUtils.adaptToSingle(context, Variable.class);
105                                 if (v != null) {
106                                         try {
107                                                 org.simantics.datatypes.literal.RGB.Integer rgb = Simantics.getSession().syncRequest(new UniqueRead<org.simantics.datatypes.literal.RGB.Integer>() {
108                                                         @Override
109                                                         public org.simantics.datatypes.literal.RGB.Integer perform(ReadGraph graph) throws DatabaseException {
110                                                                 return v.getPossibleValue(graph, org.simantics.datatypes.literal.RGB.Integer.BINDING);
111                                                         }
112                                                 });
113                                                 if (rgb != null) {
114                                                         initialValue = Colors.rgb(rgb);
115                                                 }
116                                         } catch (DatabaseException e) {
117                                                 ErrorLogger.defaultLogError(e);
118                                         }
119                                 }
120
121                                 ColorDialog dialog = new ColorDialog(ctrl.getShell());
122                                 if (initialValue != null)
123                                         dialog.setRGB(initialValue);
124                                 RGB rgb = dialog.open();
125                                 if (rgb != null)
126                                         applyCallback.accept("(" + rgb.red + "," + rgb.green + "," + rgb.blue + ")");
127                                 return null;
128                         }
129
130                 };
131         }
132
133         @SCLValue(type = "ReadGraph -> Resource -> Variable -> b")
134         public static Object fontModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
135                 return new DialogModifier() {
136
137                         @Override
138                         public String getValue() {
139                                 return null;
140                         }
141
142                         @Override
143                         public String isValid(String label) {
144                                 return null;
145                         }
146
147                         @Override
148                         public void modify(final String label) {
149                                 Simantics.getSession().async(new WriteRequest() {
150
151                                         @Override
152                                         public void perform(WriteGraph graph) throws DatabaseException {
153                                                 Variable displayValue = context.getParent(graph);
154                                                 displayValue.setValue(graph, label, Font.BINDING);
155                                         }
156
157                                 });
158                         }
159
160                         public String query(Object parentControl, Object controlItem, int columnIndex, NodeContext context, Consumer<String> applyCallback) {
161                                 Control ctrl = (Control) parentControl;
162
163                                 FontData[] initialValue = null;
164                                 final Variable v = AdaptionUtils.adaptToSingle(context, Variable.class);
165                                 if (v != null) {
166                                         try {
167                                                 Font font = Simantics.getSession().syncRequest(new UniqueRead<Font>() {
168                                                         @Override
169                                                         public Font perform(ReadGraph graph) throws DatabaseException {
170                                                                 return v.getPossibleValue(graph, Font.BINDING);
171                                                         }
172                                                 });
173                                                 if (font != null) {
174                                                         initialValue = new FontData[] { Fonts.swtFontData(font) };
175                                                 }
176                                         } catch (DatabaseException e) {
177                                                 ErrorLogger.defaultLogError(e);
178                                         }
179                                 }
180
181                                 FontDialog dialog = new FontDialog(ctrl.getShell());
182                                 if (initialValue != null)
183                                         dialog.setFontList(initialValue);
184                                 FontData font = dialog.open();
185                                 if (font != null)
186                                         applyCallback.accept("(\"" + font.getName() + "\"," + font.getHeight() + ",\"" + Fonts.fromSwtStyle(font.getStyle()) + "\")");
187                                 return null;
188                         }
189
190                 };
191         }
192
193         @SCLValue(type = "ReadGraph -> Resource -> a -> b")
194         public static Object getEnumerationValues(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
195                 if(context instanceof Variable) {
196                         Layer0 L0 = Layer0.getInstance(graph);
197                         Variable parameter = ((Variable)context).browse(graph, "..");
198                         Resource parameterResource = parameter.getRepresents(graph);
199                         if(graph.sync(new IsEnumeratedValue(parameterResource))) {
200                                 Map<String, Resource> map = graph.sync(new InstanceEnumerationMap(parameterResource));
201                                 return new ArrayList<String>(map.keySet());
202                         } else if(graph.isInstanceOf(parameterResource, L0.Boolean)) {
203                                 return CollectionUtils.toList("true", "false");
204                         }
205                 }
206                 return null;
207         }
208
209         @SCLValue(type = "ReadGraph -> Resource -> a -> b")
210         public static Object getPropertyChildName(ReadGraph graph, Resource resource, Object context) throws DatabaseException {
211                 if(context instanceof Variable) {
212                         Variable variable = (Variable)context;
213                         String label = variable.getParent(graph).getPossiblePropertyValue(graph, "HasLabel", Bindings.STRING);
214                         if(label != null)
215                                 return label;
216                         return variable.getParent(graph).getName(graph);
217                 }
218                 throw new DatabaseException("Unknown context " + context);
219         }
220         
221         @SCLValue(type = "WriteGraph -> Variable -> a -> b -> String")
222         public static String inputModifier(WriteGraph graph, Variable variable, Object value, Object _binding) throws DatabaseException {
223
224                 //      System.err.println("inputModifier " + variable.getURI(graph));
225                 Layer0 L0 = Layer0.getInstance(graph);
226
227                 Variable parent = variable.getParent(graph);
228                 Resource property = variable.getPredicateResource(graph);
229
230                 Resource container = parent.getRepresents(graph);
231                 if(container == null) return null;
232                 if(property == null) return null;
233
234                 Statement object = graph.getPossibleStatement(container, property);
235                 if(object == null) return null;
236
237                 Resource objectResource = object.getObject();
238                 if(graph.sync(new IsEnumeratedValue(objectResource))) {
239
240                         Resource type = graph.getSingleObject(objectResource, L0.PartOf);
241
242                         Map<String, Resource> enumMap = graph.syncRequest(new EnumerationMap(type));
243                         Resource newLiteral = enumMap.get(value);
244                         graph.deny(container, property, objectResource);
245                         graph.claim(container, property, newLiteral);
246
247                         return null;
248
249                 }
250
251                 Resource newType = Layer0Utils.getPossibleLiteralType(graph, variable);
252                 if(newType == null) {
253                         Type type = Layer0Utils.getSCLType(graph, variable);
254                         // This means that type is a wildcard e.g. "a"
255                         if(Types.canonical(type) instanceof TVar) {
256                                 newType = Layer0Utils.inferLiteralTypeFromString(graph, value.toString());
257                         } else {
258                                 throw new DatabaseException("Failed to find type for property " + NameUtils.getSafeName(graph, property));
259                         }
260                 }
261
262                 boolean correctType = graph.getPossibleType(objectResource, newType) != null;
263                 boolean asserted = object.isAsserted(container);
264                 if(asserted || !correctType) {
265
266                         if(correctType) {
267
268                                 Statement dt = graph.getPossibleStatement(objectResource, L0.HasDataType);
269                                 Datatype custom = dt.isAsserted(objectResource) ? null : (Datatype)graph.getValue(dt.getObject(), datatype_binging);
270
271                                 objectResource = graph.newResource();
272                                 graph.claim(objectResource, L0.InstanceOf, null, newType);
273                                 graph.claim(container, property, objectResource);
274                                 if(custom != null) {
275                                         // Only set HasValueType if the calculated new SCL type differs from the asserted value type
276                                         String newValueType = Layer0Utils.getSCLType(custom);
277                                         String currentValueType = graph.getPossibleRelatedValue(objectResource, L0.HasValueType, Bindings.STRING);
278                                         if (!newValueType.equals(currentValueType)) {
279                                                 graph.addLiteral(objectResource, L0.HasValueType, L0.HasValueType_Inverse, L0.String, newValueType, Bindings.STRING);
280                                         }
281                                         graph.addLiteral(objectResource, L0.HasDataType, L0.HasDataType_Inverse, L0.DataType, custom, datatype_binging);
282                                 }
283
284                         } else {
285
286                                 if(newType != null) {
287
288                                         if(!correctType && !asserted) // if not correct type and not asserted, remove the old value
289                                                 graph.deny(container, property, objectResource);
290
291                                         objectResource = graph.newResource();
292                                         graph.claim(objectResource, L0.InstanceOf, newType);
293                                         graph.claim(container, property, objectResource);
294
295                                 }
296
297                         }
298
299                 }
300
301                 Datatype datatype = variable.getDatatype(graph);
302                 Binding binding = (Binding)_binding;
303                 Layer0Utils.claimAdaptedValue(graph, objectResource, value, binding, datatype);
304
305                 return null;
306
307         }
308
309         @SCLValue(type = "ReadGraph -> a -> Resource")
310         public static Resource singleResourceTransformation(ReadGraph graph, Object input) throws DatabaseException {
311                 return WorkbenchSelectionUtils.getPossibleResource(graph, input);
312         }
313
314
315         @SCLValue(type = "ReadGraph -> a -> Variable")
316         public static Variable singleVariableTransformation(ReadGraph graph, Object input) throws DatabaseException {
317                 Variable single = WorkbenchSelectionUtils.getPossibleVariable(graph, input);
318                 if(single != null) return single;
319                 return ISelectionUtils.filterSingleSelection(input, Variable.class);
320         }
321
322         @SCLValue(type = "ReadGraph -> a -> Variable")
323         public static Variable singleResourceToVariableTransformation(ReadGraph graph, Object input) throws DatabaseException {
324                 Resource r = WorkbenchSelectionUtils.getPossibleResource(graph, input);
325                 if (r == null)
326                         return null;
327                 return Variables.getPossibleVariable(graph, r);
328         }
329
330         @SCLValue(type = "ReadGraph -> a -> SelectionInput")
331         public static SelectionInput standardSelectionInputTransformation(ReadGraph graph, Object input) throws DatabaseException {
332                 WorkbenchSelectionElement wse = WorkbenchSelectionUtils.getPossibleSelectionElement(input);
333                 if (wse == null)
334                         return null;
335                 return new StandardSelectionInput(wse);
336         }
337
338         @SCLValue(type = "ValueAccessor")
339         public static ValueAccessor displayUnitValueAccessor = new ValueAccessor() {
340
341                 @Override
342                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
343                         return Variables.getPossibleUnit(graph, context.getParent(graph));
344                 }
345
346                 @Override
347                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
348                         try {
349                                 Object value = Variables.getPossibleUnit(graph, context.getParent(graph));
350                                 if(value == null) return null;
351                                 Binding srcBinding = Bindings.OBJECT.getContentBinding(value);
352                                 return Bindings.adapt(value, srcBinding, binding);
353                         } catch (AdaptException e) {
354                                 throw new DatabaseException(e);
355                         } catch (BindingException e) {
356                                 throw new DatabaseException(e);
357                         }
358                 }
359
360                 @Override
361                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
362                         throw new UnsupportedOperationException();
363                 }
364
365                 @Override
366                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
367                         throw new UnsupportedOperationException();
368                 }
369
370                 @Override
371                 public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
372                         return org.simantics.db.layer0.function.All.getDatatypeFromValue(graph, context);
373                 }
374
375         };
376
377         @SCLValue(type = "ValueAccessor")
378         public static ValueAccessor displayPropertyValueAccessor = new ValueAccessor() {
379
380                 @Override
381                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
382                         return getValue(graph, context, Bindings.STRING);
383                 }
384
385                 @Override
386                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
387                         Layer0 L0 = Layer0.getInstance(graph);
388                         Variable property = context.getParent(graph);
389                         Resource predicate = property.getPossiblePredicateResource(graph);
390                         if(predicate == null) return property.getName(graph);
391                         String value = graph.getPossibleRelatedValue2(predicate, L0.HasLabel, Bindings.STRING);
392                         if(value == null)
393                             value = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
394                         try {
395                                 return Bindings.adapt(value, binding, Bindings.STRING);
396                         } catch (AdaptException e) {
397                                 throw new DatabaseException(e);
398                         }
399                 }
400
401                 @Override
402                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
403                         throw new UnsupportedOperationException();
404                 }
405
406                 @Override
407                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
408                         throw new UnsupportedOperationException();
409                 }
410
411                 @Override
412                 public Datatype getDatatype(ReadGraph graph, Variable context)
413                                 throws DatabaseException {
414                         return Datatypes.STRING;
415                 }
416
417         };      
418
419         @SCLValue(type = "ValueAccessor")
420         public static ValueAccessor displayValueValueAccessor = new ValueAccessor() {
421
422                 @Override
423                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
424                         return getValue(graph, context, Bindings.STRING);
425                 }
426
427                 public boolean isPrimitive(Datatype dt) {
428                         if(Datatypes.STRING.equals(dt)) return true;
429                         else return false;
430                 }
431
432                 private String possibleExpression(ReadGraph graph, Variable variable) throws DatabaseException {
433
434                         Layer0 L0 = Layer0.getInstance(graph);
435                         Resource object = variable.getPossibleRepresents(graph);
436                         if(object != null && graph.isInstanceOf(object, L0.SCLValue)) {
437                                 String expression = graph.getPossibleRelatedValue(object, L0.SCLValue_expression);
438                                 if (expression != null)
439                                         return "=" + expression;
440                         }
441                         return null;
442
443                 }
444
445                 @Override
446                 public Object getValue(ReadGraph graph, Variable context, Binding _binding) throws DatabaseException {
447
448                         Variable property = context.getParent(graph);
449
450                         String expression = possibleExpression(graph, property);
451                         if(expression != null) return expression;
452
453                         Object value = null;
454                         Resource formatter = property.getPossiblePropertyValue(graph, Variables.FORMATTER);
455                         if(formatter != null) {
456                                 Formatter fmt = graph.adaptContextual(formatter, property, Variable.class, Formatter.class);
457                                 value = fmt.format(property.getValue(graph));
458                         }
459                         if(value == null) {
460                                 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
461                                 Function1<Object,String> formatterFunction = property.getPossiblePropertyValue(graph, SEL.formatter);
462                                 if(formatterFunction != null) {
463                                         value = formatterFunction.apply(property.getValue(graph));
464                                 }
465                         }
466                         if(value == null) {
467
468                                 Variant variant = property.getVariantValue(graph);
469                                 value = variant.getValue();
470                                 Binding binding = variant.getBinding();
471                                 if(binding != null) {
472                                         Datatype dt = binding.type();   
473                                         if(dt != null) {
474                                                 if(!isPrimitive(dt)) {
475                                                         try {
476                                                                 value = DataValuePrinter.writeValueSingleLine(binding, value);
477                                                         } catch (IOException e) {
478                                                                 e.printStackTrace();
479                                                         } catch (BindingException e) {
480                                                                 e.printStackTrace();
481                                                         }
482                                                 }
483                                         }
484                                 }
485
486                         }
487
488                         try {
489                                 return Bindings.adapt(value != null ? value.toString() : "null", _binding, Bindings.STRING);
490                         } catch (AdaptException e) {
491                                 throw new DatabaseException(e);
492                         }
493                 }
494
495                 @Override
496                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
497                         try {
498                                 Binding binding = Bindings.getBinding(value.getClass());
499                                 setValue(graph, context, value, binding);
500                         } catch (BindingConstructionException e) {
501                                 throw new DatabaseException(e);
502                         }
503                 }
504
505                 @Override
506                 public void setValue(WriteGraph graph, Variable context, Object _value, Binding _binding) throws DatabaseException {
507                         try {
508
509                                 if(!(_value instanceof String)) throw new DatabaseException("setValue for HasDisplayValue only accepts String (got " + _value.getClass().getSimpleName() + ")");
510
511                                 String text = (String)_value;
512                                 if(text.startsWith("=")) {
513                                         Variable property = context.getParent(graph);
514                                         Layer0Utils.setExpression(graph, property, text, ModelingResources.getInstance(graph).SCLValue);
515                                         return;
516                                 }
517
518                                 String parsedLabel = (String)_value;
519                                 Object value = parsedLabel;
520
521                                 Datatype type = context.getParent(graph).getPossibleDatatype(graph);
522                                 if (type != null) {
523
524                                         Binding binding = Bindings.getBinding(type);
525
526                                         if (binding instanceof StringBinding) {
527
528                                                 if (binding instanceof MutableStringBinding)
529                                                         value = new MutableString(parsedLabel);
530                                                 else
531                                                         value = parsedLabel;
532
533                                         } else {
534
535                                                 if (binding instanceof NumberBinding) {
536                                                         parsedLabel = parsedLabel.replace(",", ".");
537                                                 }
538
539                                                 value = binding.parseValue(parsedLabel, new DataValueRepository());
540                                         }
541
542                                         //System.out.println("VariableWrite " + ObjectUtils.toString(value));
543                                         context.getParent(graph).setValue(graph, value, binding);
544
545                                 } else {
546
547                                         context.getParent(graph).setValue(graph, value);
548
549                                 }
550
551
552                                 // Add a comment to metadata.
553                                 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
554                                 graph.addMetadata(cm.add("Set value " + ObjectUtils.toString(value)));
555
556                         } catch (DataTypeSyntaxError e) {
557                                 throw new DatabaseException(e);
558                         } catch (BindingException e) {
559                                 throw new DatabaseException(e);
560                         }
561                 }
562
563                 @Override
564                 public Datatype getDatatype(ReadGraph graph, Variable context)
565                                 throws DatabaseException {
566                         return Datatypes.STRING;
567                 }
568
569         };              
570 }