package models.dataConstraintModel;
import models.algebra.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class JsonAccessor extends Term {
public JsonAccessor(Symbol symbol) {
super(symbol);
}
@Override
public Expression reduce() {
Expression reducedTerm = super.reduce();
if (reducedTerm instanceof Term) {
if (symbol == 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);
}
} else if (symbol == DataConstraintModel.dotParam && getChildren().size() >= 2) {
// this term is `json.{param}`.
Expression expJson = getChild(0);
Expression expKey = getChild(1);
if (expKey instanceof Variable && expJson instanceof Term) {
reducedTerm = getValue((Term) expJson, (Variable) expKey);
}
}
}
return reducedTerm;
}
private Expression getValue(Term json, Expression key) {
if (json instanceof JsonTerm) {
if (key instanceof Constant) {
return ((JsonTerm) json).get((Constant) key);
} else if (key instanceof Variable) {
return ((JsonTerm) json).get((Variable) key);
}
}
if (key instanceof Constant || key instanceof Variable) {
if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) {
if (json.getChild(1).equals(key)) {
Expression value = json.getChild(2);
if (value == null) {
return new Constant(DataConstraintModel.null_);
}
return value;
}
if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil)))
return null;
return getValue((Term) json.getChild(0), key);
}
}
return new Constant(DataConstraintModel.null_);
}
private Expression getValue(Term json, Variable key) {
if (json instanceof JsonTerm) {
return ((JsonTerm) json).get(key);
}
if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) {
if (json.getChild(1).equals(key)) {
Expression value = json.getChild(2);
if (value == null) {
return new Constant(DataConstraintModel.null_);
}
return value;
}
if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil)))
return null;
return getValue((Term) json.getChild(0), key);
}
if (json.getSymbol().equals(DataConstraintModel.insert)) {
if (json.getChild(1).equals(key)) {
Expression value = json.getChild(2);
if (value == null) {
return new Constant(DataConstraintModel.null_);
}
return value;
}
if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil)))
return null;
return getValue((Term) json.getChild(0), key);
}
return new Constant(DataConstraintModel.null_);
}
@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 == DataConstraintModel.dot && getChildren().size() >= 2) {
// this term is `json.key`.
Expression expJson = getChild(0);
Expression expKey = getChild(1);
Type jsonType = null;
if (expJson instanceof Variable) {
jsonType = ((Variable) expJson).getType();
} else if (expJson instanceof Term) {
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 if (jsonType instanceof JsonType) {
keySet.addAll(((JsonType) jsonType).getKeys());
if (keySet.size() == 0) {
keySet.add(keyName);
}
}
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 == 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 = ((Variable) expKey).getType();
} else if (expKey instanceof Term) {
keyType = ((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;
}
}