--- /dev/null
+package org.simantics.modeling;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+import java.util.Stack;\r
+\r
+import org.simantics.basicexpression.Expressions;\r
+import org.simantics.basicexpression.analysis.DepthFirstAdapter;\r
+import org.simantics.basicexpression.node.AConstantValue;\r
+import org.simantics.basicexpression.node.ADivMultiplicative;\r
+import org.simantics.basicexpression.node.AMultMultiplicative;\r
+import org.simantics.basicexpression.node.APlusExpression;\r
+import org.simantics.basicexpression.node.AVariablePrimary;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.scl.compiler.types.Type;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.datastructures.Triple;\r
+\r
+public class InvertBasicExpressionVisitor extends DepthFirstAdapter {\r
+\r
+ Stack<Object> stack = new Stack<Object>();\r
+\r
+ public Object getResult() {\r
+ if(stack.size() != 1) return null; \r
+ return stack.pop();\r
+ }\r
+\r
+ public void outAConstantValue(AConstantValue node) {\r
+ stack.push(Double.valueOf(node.toString()));\r
+ }\r
+\r
+ @Override\r
+ public void outAVariablePrimary(AVariablePrimary node) {\r
+ String value = node.toString().trim();\r
+ stack.push(Triple.make(1.0, 0.0, value));\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ public void outAPlusExpression(APlusExpression node) {\r
+ \r
+ final Object o1 = stack.pop();\r
+ final Object o2 = stack.pop();\r
+ \r
+ if(o1 instanceof Double && o2 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o2;\r
+ stack.push(Triple.make(p.first, p.second + (Double)o1, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o1;\r
+ stack.push(Triple.make(p.first, p.second + (Double)o2, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Double) {\r
+ stack.push((Double)o1 + (Double)o2);\r
+ } else {\r
+ stack.push(Double.NaN);\r
+ }\r
+ \r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public void outAMinusExpression(APlusExpression node) {\r
+ \r
+ final Object o1 = stack.pop();\r
+ final Object o2 = stack.pop();\r
+ \r
+ if(o1 instanceof Double && o2 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o2;\r
+ stack.push(Triple.make(-p.first, (Double)o1 - p.second, p.third ));\r
+ } else if (o2 instanceof Double && o1 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o1;\r
+ stack.push(Triple.make(p.first, p.second - (Double)o2, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Double) {\r
+ stack.push((Double)o1 - (Double)o2);\r
+ } else {\r
+ stack.push(Double.NaN);\r
+ }\r
+ \r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public void outAMultMultiplicative(AMultMultiplicative node) {\r
+ \r
+ final Object o1 = stack.pop();\r
+ final Object o2 = stack.pop();\r
+ \r
+ if(o1 instanceof Double && o2 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o2;\r
+ stack.push(Triple.make(p.first * (Double)o1, p.second * (Double)o1, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double, Double, String>)o1;\r
+ stack.push(Triple.make(p.first * (Double)o2, p.second * (Double)o2, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Double) {\r
+ stack.push((Double)o1 * (Double)o2);\r
+ } else {\r
+ stack.push(Double.NaN);\r
+ }\r
+ \r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public void outADivMultiplicative(ADivMultiplicative node) {\r
+ \r
+ final Object o1 = stack.pop();\r
+ final Object o2 = stack.pop();\r
+ \r
+ if(o1 instanceof Double && o2 instanceof Triple) {\r
+ stack.push(Double.NaN);\r
+ } else if (o2 instanceof Double && o1 instanceof Triple) {\r
+ Triple<Double, Double, String> p = (Triple<Double,Double, String>)o1;\r
+ stack.push(Triple.make(p.first / (Double)o2, p.second / (Double)o2, p.third));\r
+ } else if (o2 instanceof Double && o1 instanceof Double) {\r
+ stack.push((Double)o1 / (Double)o2);\r
+ } else {\r
+ stack.push(Double.NaN);\r
+ }\r
+ \r
+ }\r
+\r
+ private static final String MAGIC = "_111_";\r
+ \r
+ private static String replaced(String expression) {\r
+ return expression.replaceAll("\\.([A-Za-z])", MAGIC + "$1");\r
+ }\r
+ \r
+ public static void invert(WriteGraph graph, Variable base, String expression, Object value) throws DatabaseException {\r
+ InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();\r
+ Expressions.evaluate(replaced(expression), visitor);\r
+ Object pair = visitor.getResult();\r
+ if(pair == null) return;\r
+ if(pair instanceof Triple) {\r
+ @SuppressWarnings("unchecked")\r
+ Triple<Double, Double, String> data = (Triple<Double, Double, String>)pair;\r
+ String key = data.third.replace(MAGIC, ".");\r
+ String path = getVariablePath(graph, base, key);\r
+ Variable targetVariable = base.browse(graph, path);\r
+ if(value instanceof Number) {\r
+ if(Math.abs(data.first) > 1e-9) {\r
+ Double inverted = (((Number)value).doubleValue() - data.second) / data.first;\r
+ targetVariable.setValue(graph, inverted);\r
+ }\r
+ } else if (value instanceof Boolean) {\r
+ // TODO: support 'not'\r
+ targetVariable.setValue(graph, value);\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ private static String getVariablePath(ReadGraph graph, Variable base, String key) throws DatabaseException {\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ Resource type = base.getPossibleType(graph);\r
+ if(type == null)\r
+ return null;\r
+ boolean procedural = graph.isInstanceOf(type, STR.ProceduralComponentType);\r
+ Pair<String, Type> pair;\r
+ if(procedural)\r
+ pair = graph.sync(new ProceduralSubstructureMapRequest(base)).get(key);\r
+ else\r
+ pair = ComponentTypeSubstructure.forType(graph, type).possibleTypedRVI(key);\r
+ if(pair == null)\r
+ return null;\r
+ return pair.first;\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param base\r
+ * @param expression the expression to check for invertibility. An empty is expression is not invertible.\r
+ * @return\r
+ * @throws DatabaseException\r
+ * @throws NullPointerException for null expression\r
+ */\r
+ public static boolean isInvertible(ReadGraph graph, Variable base, String expression) throws DatabaseException {\r
+ if (expression == null)\r
+ throw new NullPointerException("null expression for variable " + base.getURI(graph));\r
+ if (expression.isEmpty())\r
+ return false;\r
+\r
+ Resource type = base.getPossibleType(graph);\r
+ if(type == null) return false;\r
+\r
+ InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();\r
+ Expressions.evaluate(replaced(expression), visitor);\r
+ Object pair = visitor.getResult();\r
+ if(pair == null) return false;\r
+ if(pair instanceof Triple) {\r
+ @SuppressWarnings("unchecked")\r
+ Triple<Double, Double, String> data = (Triple<Double, Double, String>)pair;\r
+ String key = data.third.replace(MAGIC,".");\r
+ return getVariablePath(graph, base, key) != null;\r
+ }\r
+ return false;\r
+ \r
+ }\r
+\r
+ public static Variable possibleInvertibleExpressionReferencedProperty(ReadGraph graph, Variable base, String expression) throws DatabaseException {\r
+ if (base == null || expression == null || expression.isEmpty())\r
+ return null;\r
+ InvertBasicExpressionVisitor visitor = new InvertBasicExpressionVisitor();\r
+ //System.out.println("invert : " + expression + " -> " + replaced(expression) + " for " + base.getURI(graph));\r
+ Expressions.evaluate(replaced(expression), visitor);\r
+ Object pair = visitor.getResult();\r
+ if(pair == null)\r
+ return null;\r
+ if(pair instanceof Triple) {\r
+ @SuppressWarnings("unchecked")\r
+ Triple<Double, Double, String> data = (Triple<Double, Double, String>)pair;\r
+ String key = data.third.replace(MAGIC,".");\r
+ String path = getVariablePath(graph, base, key);\r
+ Variable targetVariable = base.browsePossible(graph, path);\r
+ return targetVariable;\r
+ }\r
+ return null;\r
+ }\r
+\r
+}\r