Fixed InvertBasicExpressionVisitor bugs with subtractions and divisions
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / InvertBasicExpressionVisitor.java
1 package org.simantics.modeling;
2
3 import org.simantics.basicexpression.Expressions;
4 import org.simantics.db.ReadGraph;
5 import org.simantics.db.Resource;
6 import org.simantics.db.WriteGraph;
7 import org.simantics.db.exception.DatabaseException;
8 import org.simantics.db.layer0.variable.Variable;
9 import org.simantics.scl.compiler.types.Type;
10 import org.simantics.structural.stubs.StructuralResource2;
11 import org.simantics.utils.datastructures.Pair;
12 import org.simantics.utils.datastructures.Triple;
13
14 public class InvertBasicExpressionVisitor extends InvertBasicExpressionVisitorBase {
15
16         private static final String MAGIC = "_111_";
17
18         private static String replaced(String expression) {
19             return expression.replaceAll("\\.([A-Za-z])", MAGIC + "$1");
20         }
21
22     public static void invert(WriteGraph graph, Variable base, String expression, Object value) throws DatabaseException {
23         InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();
24         Expressions.evaluate(replaced(expression), visitor);
25         Object result = visitor.getResult();
26         if(result == null) return;
27         if(result instanceof Triple) {
28             @SuppressWarnings("unchecked")
29             Triple<Double, Double, String> data = (Triple<Double, Double, String>)result;
30             String key = data.third.replace(MAGIC, ".");
31             String path = getVariablePath(graph, base, key);
32             Variable targetVariable = base.browse(graph, path);
33             if(value instanceof Number) {
34                 if(Math.abs(data.first) > 1e-9) {
35                     double inverted = (double) (((Number)value).doubleValue() - data.second) / data.first;
36                     Object invertedValue = numericValueInType(inverted, value.getClass());
37                     targetVariable.setValue(graph, invertedValue);
38                 }
39             } else if (value instanceof Boolean) {
40                 // TODO: support 'not'
41                 targetVariable.setValue(graph, value);
42             }
43         }
44         
45     }
46
47     private static Object numericValueInType(double value, Class<?> type) {
48         if (type == Integer.class) {
49             return (int) value;
50         } else if (type == Long.class) {
51             return (long) value;
52         } else if (type == Byte.class) {
53             return (byte) value;
54         } else if (type == Float.class) {
55             return (float) value;
56         }
57         return value;
58     }
59
60     private static String getVariablePath(ReadGraph graph, Variable base, String key) throws DatabaseException {
61         StructuralResource2 STR = StructuralResource2.getInstance(graph);
62         Resource type = base.getPossibleType(graph);
63         if(type == null)
64             return null;
65         boolean procedural = graph.isInstanceOf(type, STR.ProceduralComponentType);
66         Pair<String, Type> pair;
67         if(procedural)
68             pair = graph.sync(new ProceduralSubstructureMapRequest(base)).get(key);
69         else
70             pair = ComponentTypeSubstructure.forType(graph, type).possibleTypedRVI(key);
71         if(pair == null)
72             return null;
73         return pair.first;
74     }
75
76     /**
77      * @param graph
78      * @param base
79      * @param expression the expression to check for invertibility. An empty is expression is not invertible.
80      * @return
81      * @throws DatabaseException
82      * @throws NullPointerException for null expression
83      */
84     public static boolean isInvertible(ReadGraph graph, Variable base, String expression) throws DatabaseException {
85         if (expression == null)
86             throw new NullPointerException("null expression for variable " + base.getURI(graph));
87         if (expression.isEmpty())
88             return false;
89
90         Resource type = base.getPossibleType(graph);
91         if(type == null) return false;
92
93         InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();
94         Expressions.evaluate(replaced(expression), visitor);
95         Object pair = visitor.getResult();
96         if(pair == null) return false;
97         if(pair instanceof Triple) {
98             @SuppressWarnings("unchecked")
99             Triple<Double, Double, String> data = (Triple<Double, Double, String>)pair;
100             String key = data.third.replace(MAGIC,".");
101             return getVariablePath(graph, base, key) != null;
102         }
103         return false;
104         
105     }
106
107     @SuppressWarnings("unchecked")
108     private static Triple<Double, Double, String> possibleInvertibleExpression(ReadGraph graph, Variable base, String expression) throws DatabaseException {
109         if (base == null || expression == null || expression.isEmpty())
110             return null;
111         InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();
112         //System.out.println("invert : " + expression + " -> " + replaced(expression) + " for " + base.getURI(graph));
113         Expressions.evaluate(replaced(expression), visitor);
114         Object result = visitor.getResult();
115         if (result instanceof Triple)
116             return (Triple<Double, Double, String>) result;
117         return null;
118     }
119
120     public static Variable possibleInvertibleExpressionReferencedProperty(ReadGraph graph, Variable base, String expression) throws DatabaseException {
121         Triple<Double, Double, String> data = possibleInvertibleExpression(graph, base, expression);
122         if (data == null)
123             return null;
124         String path = getVariablePath(graph, base, data.third.replace(MAGIC, "."));
125         return path != null ? base.browsePossible(graph, path) : null;
126     }
127
128     public static Triple<Double, Double, Variable> possibleInvertibleExpressionReferencedTransformedProperty(ReadGraph graph, Variable base, String expression) throws DatabaseException {
129         Triple<Double, Double, String> data = possibleInvertibleExpression(graph, base, expression);
130         if (data == null)
131             return null;
132         String path = getVariablePath(graph, base, data.third.replace(MAGIC, "."));
133         if (path == null)
134             return null;
135         Variable targetVariable = base.browsePossible(graph, path);
136         return targetVariable != null ? Triple.make(data.first, data.second, targetVariable) : null;
137     }
138
139 }