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