package models.dataConstraintModel; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import models.algebra.Constant; import models.algebra.Expression; import models.algebra.LambdaAbstraction; import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.Variable; public class JsonAccessor extends Term { public JsonAccessor(Symbol symbol) { super(symbol); } public Type getType() { if (symbol.equals(DataConstraintModel.dotParam)) { Type valueType = null; if (getChild(1) instanceof Term) { valueType = ((Term) getChild(1)).getType(); } else if (getChild(1) instanceof Variable) { valueType = ((Variable) getChild(1)).getType(); } if (valueType != null) return valueType; } return super.getType(); } @Override public Expression reduce() { Expression reducedTerm = super.reduce(); if (reducedTerm instanceof Term) { if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { // this term is `json.key`. Expression expJson = getChild(0); Expression expKey = getChild(1); if (expKey instanceof Constant && expJson instanceof Term) { reducedTerm = getValue((Term) expJson, (Constant) expKey); } } } return reducedTerm; } private Expression getValue(Term json, Constant key) { if (json instanceof JsonTerm) { return ((JsonTerm) json).get(key); } if (!json.getSymbol().equals(DataConstraintModel.addMember)) return null; if (json.getChild(1).equals(key)) { return json.getChild(2); } if (json.getChild(0) == null || json.getChild(0).equals(DataConstraintModel.nil)) return null; return getValue((Term) json.getChild(0), key); } @Override public Expression getInverseMap(Expression outputValue, Position targetPos) { if (targetPos.isEmpty()) return outputValue; targetPos = (Position) targetPos.clone(); int i = targetPos.removeHeadOrder(); Symbol[] inverseSymbols = symbol.getInverses(); if (i == 0) { if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { // this term is `json.key`. Expression expJson = getChild(0); Expression expKey = getChild(1); JsonType jsonType = null; if (expJson instanceof Variable) { jsonType = (JsonType) ((Variable) expJson).getType(); } else if (expJson instanceof Term) { jsonType = (JsonType) ((Term) expJson).getType(); } String keyName = null; if (expKey instanceof Constant) { keyName = (String) ((Constant) expKey).getValue(); Term jsonTerm = new Constant(DataConstraintModel.nil); jsonTerm.setType(DataConstraintModel.typeJson); int v = 1; Map<String, Variable> vars = new HashMap<>(); Set<String> keySet = new HashSet<>(); if (jsonType == null || jsonType == DataConstraintModel.typeJson) { keySet.add(keyName); } else { keySet.addAll(jsonType.getKeys()); } for (String key: keySet) { Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) addMemberTerm.addChild(jsonTerm); addMemberTerm.addChild(new Constant(key, DataConstraintModel.typeString)); Variable var = new Variable("v" + v); addMemberTerm.addChild(var); vars.put(key, var); jsonTerm = addMemberTerm; v++; } Variable var = vars.get(keyName); LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, jsonTerm); // v -> addMember(jsonTerm, key, v) inverseSymbols = new Symbol[] { lambdaAbstraction }; } } else if (symbol.equals(DataConstraintModel.dotParam) && getChildren().size() >= 2) { // this term is `json.{param}`. Expression expListOrMap = getChild(0); Expression expKey = getChild(1); JsonType jsonType = null; if (expListOrMap instanceof Variable) { jsonType = (JsonType) ((Variable) expListOrMap).getType(); } else if (expListOrMap instanceof Term) { jsonType = (JsonType) ((Term) expListOrMap).getType(); } Type keyType = null; if (expKey instanceof Variable) { keyType = (JsonType) ((Variable) expKey).getType(); } else if (expKey instanceof Term) { keyType = (JsonType) ((Term) expKey).getType(); } if (jsonType != null && keyType != null) { if (DataConstraintModel.typeList.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeInt)) { Term setElementTerm = new Term(DataConstraintModel.set); // set(list, idx, v) setElementTerm.addChild(new Constant(DataConstraintModel.nil)); setElementTerm.addChild(expKey); Variable var = new Variable("v"); setElementTerm.addChild(var); LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, setElementTerm); // v -> set(list, idx, v) inverseSymbols = new Symbol[] { lambdaAbstraction }; } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeString)) { Term insertEntryTerm = new Term(DataConstraintModel.insert); // insert(map, key, v) insertEntryTerm.addChild(new Constant(DataConstraintModel.nil)); insertEntryTerm.addChild(expKey); Variable var = new Variable("v"); insertEntryTerm.addChild(var); LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, insertEntryTerm); // v -> insert(map, key, v) inverseSymbols = new Symbol[] { lambdaAbstraction }; } } } } if (inverseSymbols == null || i >= inverseSymbols.length || inverseSymbols[i] == null) return null; Term inverseMap = new Term(inverseSymbols[i]); inverseMap.addChild(outputValue); for (int n = 0; n < inverseSymbols[i].getArity(); n++) { if (n != i) { inverseMap.addChild(children.get(n)); } } return children.get(i).getInverseMap(inverseMap, targetPos); } public String toString() { if (symbol.equals(DataConstraintModel.dotParam)) { if (children.get(1) instanceof Constant) { return children.get(0).toString() + symbol.toString() + (String) ((Constant) children.get(1)).getValue(); } return children.get(0).toString() + symbol.toString() + children.get(1).toString(); } return super.toString(); } public String toImplementation(String[] sideEffects) { if (symbol.equals(DataConstraintModel.dotParam)) { return children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "(" + children.get(1).toImplementation(sideEffects) + ")"; } return super.toImplementation(sideEffects); } @Override public Object clone() { JsonAccessor newTerm = new JsonAccessor(symbol); for (Expression e: children) { newTerm.addChild((Expression) e.clone()); } newTerm.type = type; return newTerm; } }