package generators;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import algorithms.TypeInference;
import code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.Parameter;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;
import models.controlFlowModel.StatefulObjectNode;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.IFlowGraph;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
/**
* Common generator for prototypes
*
* @author Nitta
*
*/
public abstract class CodeGenerator {
public static final String fieldOfResourceState = "value";
public static final String methoNameOfResourceState = "Value";
public static final String getterPrefix = "get";
public static final String getterOfResourceState = getterPrefix + methoNameOfResourceState; // "getValue"
public static final String updateMethodPrefix = "update";
public static final String from = "From";
public static final String _for = "For";
private static String mainTypeName = null;
private static ILanguageSpecific langSpec = null;
private static IPlatformSpecific platformSpec = null;
public static String getMainTypeName() {
return mainTypeName;
}
public static void setMainTypeName(String mainTypeName) {
CodeGenerator.mainTypeName = mainTypeName;
}
public static void resetMainTypeName() {
CodeGenerator.mainTypeName = null;
}
public static String getComponentName(ResourceHierarchy res, ILanguageSpecific langSpec) {
String name = res.getResourceName();
if (res.getNumParameters() > 0) {
if (name.length() > 3 && name.endsWith("ies")) {
name = name.substring(0, name.length() - 3) + "y";
} else if (name.length() > 1 && name.endsWith("s")) {
name = name.substring(0, name.length() - 1);
} else {
name += "Element";
}
}
return langSpec.toComponentName(name);
}
public static Type getImplStateType(ResourceHierarchy res, ILanguageSpecific langSpec) {
Set<ResourceHierarchy> children = res.getChildren();
if (children == null || children.size() == 0) {
// leaf resource.
return res.getResourceStateType();
} else {
ResourceHierarchy child = children.iterator().next();
if (children.size() == 1 && child.getNumParameters() > 0) {
// map or list.
if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) {
// list.
if (generatesComponent(child)) {
return langSpec.newListType(getComponentName(child, langSpec));
} else {
return langSpec.newListType(getImplStateType(child, langSpec).getInterfaceTypeName());
}
} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) {
// map.
if (generatesComponent(child)) {
return langSpec.newMapType(DataConstraintModel.typeString, getComponentName(child, langSpec));
} else {
return langSpec.newMapType(DataConstraintModel.typeString, getImplStateType(child, langSpec).getInterfaceTypeName());
}
}
return null;
} else {
// class
return res.getResourceStateType();
}
}
}
public static boolean generatesComponent(ResourceHierarchy res) {
if (res.getParent() == null) return true;
if (res.getChildren() == null || res.getChildren().size() == 0) return false;
if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) return false;
if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0
&& (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) return false;
return true;
// return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0);
}
/**
* Generate source codes in specified language from data-flow/control-flow graph.
*
* @param model architecture model
* @param flowGraph data-flow or control-flow graph
* @param langSpec specified language
* @return source codes
*/
public ArrayList<CompilationUnit> generateCode(DataTransferModel model, IFlowGraph flowGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
CodeGenerator.langSpec = langSpec;
CodeGenerator.platformSpec = platformSpec;
ArrayList<CompilationUnit> codes = new ArrayList<>();
Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph = null;
Collection<Set<Node>> components = null;
if (platformSpec.isMonolithic()) {
// To build monolithic application, the dependency of the components should be resolved in advance.
// Get the dependency among root nodes.
dependedRootComponentGraph = getDependedRootComponentGraph(model);
// Sort the all components.
components = determineComponentOrder(flowGraph, dependedRootComponentGraph);
} else {
// Get the all components.
components = flowGraph.getAllComponentNodes().values();
}
// Generate the other components.
generateCodeFromFlowGraph(model, flowGraph, components, codes, dependedRootComponentGraph, platformSpec, langSpec);
return codes;
}
public abstract void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, Collection<Set<Node>> components, ArrayList<CompilationUnit> codes,
Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec);
private static Map<ResourceHierarchy, Set<ResourceHierarchy>> getDependedRootComponentGraph(DataTransferModel model) {
Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedComponentGraph = new HashMap<>();
for (Channel ch: model.getChannels()) {
Set<ResourceHierarchy> insideRes = new HashSet<>();
Set<ResourceHierarchy> outsideRes = new HashSet<>();
getDependedRootComponentGraphSub(ch, insideRes, outsideRes, true);
if (outsideRes.size() > 0 && insideRes.size() > 0) {
for (ResourceHierarchy outside: outsideRes) {
for (ResourceHierarchy inside: insideRes) {
Set<ResourceHierarchy> dependings = dependedComponentGraph.get(outside.getRoot());
if (dependings == null) {
dependings = new HashSet<>();
dependedComponentGraph.put(outside.getRoot(), dependings);
}
if (!outside.getRoot().equals(inside.getRoot())) {
dependings.add(inside.getRoot());
}
}
}
}
}
return dependedComponentGraph;
}
private static void getDependedRootComponentGraphSub(Channel ch, Set<ResourceHierarchy> insideRes, Set<ResourceHierarchy> outsideRes, boolean isRoot) {
DataTransferChannel dtCh = (DataTransferChannel) ch;
for (ChannelMember cm: dtCh.getChannelMembers()) {
if (!isRoot && !cm.isOutside()) {
outsideRes.add(cm.getResource().getResourceHierarchy()); // dependency to a descendant channel resource.
}
if (cm.isOutside()) {
outsideRes.add(cm.getResource().getResourceHierarchy()); // dependency to an outside resource.
} else {
insideRes.add(cm.getResource().getResourceHierarchy()); // dependency from an inside resource.
}
}
for (Channel childCh: ch.getChildren()) {
getDependedRootComponentGraphSub(childCh, insideRes, outsideRes, false);
}
}
private static ArrayList<Set<Node>> determineComponentOrder(IFlowGraph graph, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph) {
ArrayList<Set<Node>> objectList = new ArrayList<>();
Set<Set<Node>> visited = new HashSet<>();
Map<Node, Set<Node>> allNodes = graph.getAllComponentNodes();
for (Set<Node> nodeSet: allNodes.values()) {
topologicalSort(nodeSet, allNodes, dependedRootComponentGraph, visited, objectList);
}
return objectList;
}
private static void topologicalSort(Set<Node> curNodeSet, Map<Node, Set<Node>> allNodeSets, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, Set<Set<Node>> visited, List<Set<Node>> orderedList) {
if (visited.contains(curNodeSet)) return;
visited.add(curNodeSet);
// A caller is before the callee
// For each incoming PUSH transfer.
for (Node curNode: curNodeSet) {
for (Edge chToRes: curNode.getInEdges()) {
for (Edge resToCh: chToRes.getSource().getInEdges()) {
if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
topologicalSort(allNodeSets.get(resToCh.getSource()), allNodeSets, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
// For each outgoing PULL transfer.
if (curNodeSet.iterator().next() instanceof ResourceNode) {
for (Node curNode: curNodeSet) {
for (Edge resToCh: curNode.getOutEdges()) {
DataFlowEdge de = (DataFlowEdge) resToCh;
if (((PushPullAttribute) de.getAttribute()).getSelectedOption() != PushPullValue.PUSH) {
for (Edge chToRes : resToCh.getDestination().getOutEdges()) {
topologicalSort(allNodeSets.get(chToRes.getDestination()), allNodeSets, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
}
// For each depending root node.
Node curNode = curNodeSet.iterator().next();
if (curNode instanceof ResourceNode && dependedRootComponentGraph.get(((ResourceNode) curNode).getResourceHierarchy()) != null) {
for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(((ResourceNode) curNode).getResourceHierarchy())) {
for (Node node: allNodeSets.keySet()) {
if (node instanceof ResourceNode) {
ResourceNode rootNode = (ResourceNode) node;
ResourceHierarchy rootRes = rootNode.getResourceHierarchy();
if (rootRes.getParent() == null && rootRes.equals(dependingRes)) {
topologicalSort(allNodeSets.get(rootNode), allNodeSets, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
}
// For each reference resource.
ResourceNode cn = null;
if (curNode instanceof ResourceNode) {
cn = (ResourceNode) curNode;
} else if (curNode instanceof StatefulObjectNode) {
cn = ((StatefulObjectNode) curNode).getResource();
}
if (cn != null) {
for (Node n: allNodeSets.keySet()) {
ResourceNode resNode = null;
if (n instanceof ResourceNode) {
resNode = (ResourceNode) n;
}
if (resNode != null) {
for (Edge resToCh: resNode.getOutEdges()) {
ChannelNode chNode = (ChannelNode) resToCh.getDestination();
for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) {
if (cn.getOutSideResources().contains(m.getResource())) {
topologicalSort(allNodeSets.get(resNode), allNodeSets, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
}
}
orderedList.add(0, curNodeSet);
}
protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode,
MethodDeclaration constructor, final List<ResourceHierarchy> depends, ILanguageSpecific langSpec) {
// Declare the field to refer to each object in the main type.
ResourceNode resNode = null;
String nodeName = null;
if (componentNode instanceof ResourceNode) {
resNode = (ResourceNode) componentNode;
nodeName = resNode.getResourceName();
}
String componentName = langSpec.toComponentName(nodeName);
// Declare a field to refer each object.
if (langSpec.declareField()) {
FieldDeclaration refField = langSpec.newFieldDeclaration(new Type(componentName, componentName), nodeName);
mainType.addField(refField);
}
// Add a statement to instantiate each object to the main constructor.
List<String> parameters = new ArrayList<>();
for (ResourceHierarchy res: depends) {
parameters.add(res.getResourceName());
}
if (constructor.getParameters() != null) {
for (VariableDeclaration var: constructor.getParameters()) {
if (!parameters.contains(var.getName())) {
parameters.add(var.getName());
}
}
}
Block mainConstructorBody = mainConstructor.getBody();
if (mainConstructorBody == null) {
mainConstructorBody = new Block();
mainConstructor.setBody(mainConstructorBody);
}
mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter());
}
protected ResourceHierarchy addReference(TypeDeclaration component, MethodDeclaration constructor, ResourceHierarchy dstRes, ILanguageSpecific langSpec) {
if (!generatesComponent(dstRes)) {
dstRes = dstRes.getParent();
}
String dstComponentName = getComponentName(dstRes, langSpec);
if (dstComponentName != null) {
String dstNodeName = langSpec.toVariableName(dstComponentName);
if (langSpec.declareField()) {
// Declare a field to refer to another component.
component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
}
// Initialize the field to refer to another component.
constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter());
}
return dstRes;
}
protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
// returns the state field when all incoming data-flow edges are PUSH-style.
if (langSpec.isValueType(resStateType)) {
stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value;
} else {
if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) {
// list or map
// if (!platformSpec.isMonolithic()) {
// // For REST API
// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value;
// } else {
String implTypeName = resStateType.getImplementationTypeName();
// copy the current state to be returned as a 'value'
List<String> parameters = new ArrayList<>();
parameters.add(langSpec.getFieldAccessor(fieldOfResourceState));
stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value);
// }
} else {
if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) {
// // a leaf resource
// if (!platformSpec.isMonolithic()) {
// // For REST API
// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value;
// } else {
String implTypeName = resStateType.getImplementationTypeName();
// copy the current state to be returned as a 'value'
List<String> parameters = new ArrayList<>();
parameters.add(langSpec.getFieldAccessor(fieldOfResourceState));
stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value);
// }
} else {
Term composer = null;
Term composerSub = new Constant(DataConstraintModel.nil);
composerSub.setType(DataConstraintModel.typeMap);
for (ResourceHierarchy child: resourceHierarchy.getChildren()) {
String childTypeName = getComponentName(child, langSpec);
String fieldName = langSpec.toVariableName(childTypeName);
Term childGetter = null;
if (!generatesComponent(child)) {
// the child is not a class
childGetter = new Term(new Symbol(getterPrefix + childTypeName, 1, Symbol.Type.METHOD));
childGetter.addChild(new Constant(langSpec.getSelfExp()));
} else {
// the child is a class
childGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
childGetter.addChild(new Field(fieldName, getImplStateType(child, langSpec)));
}
composer = new Term(DataConstraintModel.insert);
composer.addChild(composerSub);
composer.addChild(new Constant(fieldName, DataConstraintModel.typeString)); // key
composer.addChild(childGetter); // value
composer.setType(DataConstraintModel.typeMap);
composerSub = composer;
}
composer.setType(stateGetter.getReturnType());
String[] sideEffects = new String[] {null};
String returnValue = composer.toImplementation(sideEffects);
if (sideEffects[0] != null) {
stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
} else {
stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
}
}
}
}
}
protected void fillDescendantGetterMethod(MethodDeclaration descendantGetter, ResourceHierarchy descendant,
ResourceHierarchy child, ResourceHierarchy ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) {
// (#4) descendant getter method (the implementation must be kept consistent with #3)
Expression selector;
if (DataConstraintModel.typeList.isAncestorOf(ancestor.getResourceStateType())) {
selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState));
} else if (DataConstraintModel.typeMap.isAncestorOf(ancestor.getResourceStateType())) {
selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState));
} else {
String fieldName = langSpec.toVariableName(getComponentName(child, langSpec));
selector = new Variable(langSpec.getFieldAccessor(fieldName));
}
if (descendantGetter.getParameters() != null) {
for (VariableDeclaration param: descendantGetter.getParameters()) {
if (DataConstraintModel.typeInt.isAncestorOf(param.getType())) {
Term newSelector = new Term(DataConstraintModel.get);
newSelector.addChild(selector);
newSelector.addChild(new Variable(param.getName()));
selector = newSelector;
} else if (DataConstraintModel.typeString.isAncestorOf(param.getType())) {
Term newSelector = new Term(DataConstraintModel.lookup);
newSelector.addChild(selector);
newSelector.addChild(new Variable(param.getName()));
selector = newSelector;
}
}
}
if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) {
String[] sideEffects = new String[] {null};
String returnValue = selector.toImplementation(sideEffects);
if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]);
descendantGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
}
}
protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
List<VariableDeclaration> mainGetterParams = new ArrayList<>();
int v = 1;
for (Expression param: accessRes.getPrimaryResourcePath().getPathParams()) {
if (param instanceof Variable) {
Variable var = (Variable) param;
mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
} else if (param instanceof Term) {
Term var = (Term) param;
mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v));
}
v++;
}
ResourcePath accessResPath = new ResourcePath(accessRes.getPrimaryResourcePath());
for (int i = 0; i < mainGetterParams.size(); i++) {
Parameter pathParam = new Parameter(mainGetterParams.get(i).getName());
accessResPath.replacePathParam(i, pathParam, null);
}
MethodDeclaration accessor = null;
if (mainGetterParams.size() == 0) {
accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec));
} else {
accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainGetterParams);
}
Block block = new Block();
Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(accessResPath, null);
block.addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter());
// if (stateGetter.getParameters() == null || stateGetter.getParameters().size() == 0) {
// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName())) + langSpec.getStatementDelimiter());
// } else {
// List<String> resParams = new ArrayList<>();
// for (VariableDeclaration var: stateGetter.getParameters()) {
// resParams.add(var.getName());
// }
// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName(), resParams)) + langSpec.getStatementDelimiter());
// }
accessor.setBody(block);
mainComponent.addMethod(accessor);
}
protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor,
final List<ResourceHierarchy> depends, ILanguageSpecific langSpec) {
Set<ResourcePath> refs = new HashSet<>();
for (Channel ch : model.getChannels()) {
DataTransferChannel c = (DataTransferChannel) ch;
if (resourceNode.getOutSideResource(c) != null) {
for (ResourcePath res: c.getReferenceResources()) {
if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) {
refs.add(res);
String refResName = langSpec.toComponentName(res.getLeafResourceName());
component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName()));
constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName()));
constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getLeafResourceName()) + langSpec.getAssignment() + res.getLeafResourceName() + langSpec.getStatementDelimiter());
}
}
}
}
}
protected MethodDeclaration getConstructor(TypeDeclaration component) {
for (MethodDeclaration m: component.getMethods()) {
if (m.isConstructor()) return m;
}
return null;
}
protected static List<MethodDeclaration> getGetterMethods(TypeDeclaration component, String resourceName) {
List<MethodDeclaration> getters = new ArrayList<>();
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(getterPrefix + resourceName)) {
getters.add(m);
}
}
return getters;
}
protected MethodDeclaration getUpdateMethod(Edge inEdge, TypeDeclaration component,
Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, ILanguageSpecific langSpec) {
List<ResourceNode> passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH);
String methodName = updateMethodPrefix;
for (ResourceNode rn: passedResoueces) {
methodName += langSpec.toComponentName(rn.getResourceName());
}
return getMethod(component, methodName);
}
protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannel ch, TypeDeclaration component) {
MethodDeclaration input = null;
for (ChannelMember out : ch.getOutputChannelMembers()) {
if (resourceNode.getInSideResources().contains(out.getResource())) {
Expression message = out.getStateTransition().getMessageExpression();
if (message instanceof Term) {
input = getMethod(component, ((Term) message).getSymbol().getImplName());
} else if (message instanceof Variable) {
// Declare an input method in this component.
input = getMethod(component, ((Variable) message).getName());
}
break;
}
}
return input;
}
protected MethodDeclaration getMethod(TypeDeclaration component, String methodName, List<VariableDeclaration> params) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) {
if (m.getParameters() == null && (params == null || params.size() == 0)) return m;
if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) {
boolean matchParams = true;
for (int i = 0; i < m.getParameters().size(); i++) {
if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) {
matchParams = false;
break;
}
}
if (matchParams) return m;
}
}
}
return null;
}
protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) return m;
}
return null;
}
protected boolean isPut(ChannelMember cm) {
return cm.getStateTransition().isRightUnary();
}
protected boolean isDelete(ChannelMember cm) {
boolean isDelete = false;
Expression nextExp = cm.getStateTransition().getNextStateExpression();
if (nextExp instanceof Term) {
Symbol rootSymbol = ((Term) nextExp).getSymbol();
if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
isDelete = true;
} else if (rootSymbol.equals(DataConstraintModel.cond)) {
Expression childExp = ((Term) nextExp).getChild(1);
if (childExp instanceof Term) {
rootSymbol = ((Term) childExp).getSymbol();
if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
isDelete = true;
}
}
childExp = ((Term) nextExp).getChild(2);
if (childExp instanceof Term) {
rootSymbol = ((Term) childExp).getSymbol();
if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
isDelete = true;
}
}
}
}
return isDelete;
}
protected String getGetterResourcePathAndPathParams(ResourcePath resPath, List<VariableDeclaration> pathParams,
IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
int v = 1;
List<String> params = new ArrayList<>();
for (Expression pathParam: resPath.getPathParams()) {
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
pathParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
pathParams.add(param);
}
}
return resPath.getResourceHierarchy().toResourcePath(params);
}
protected String getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> pathParams, boolean isRestAPI,
IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
int v = 1;
List<String> params = new ArrayList<>();
for (Expression pathParam: resPath.getPathParams()) {
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = null;
if (isRestAPI) {
paramName = var.getName();
} else {
paramName = "self" + (v > 1 ? v : "");
}
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
pathParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = null;
if (isRestAPI) {
paramName = "v" + v;
} else {
paramName = "self" + (v > 1 ? v : "");
}
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
pathParams.add(param);
}
v++;
}
return resPath.getResourceHierarchy().toResourcePath(params);
}
protected String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> rootInputParams,
IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
int v = 1;
List<String> params = new ArrayList<>();
if (resPath.getLastParam() != null) {
Expression pathParam = resPath.getLastParam();
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
rootInputParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
rootInputParams.add(param);
}
v++;
}
if (resPath.getParent() != null) {
for (Expression pathParam: resPath.getParent().getPathParams()) {
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
rootInputParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
rootInputParams.add(param);
}
v++;
}
}
return resPath.getResourceHierarchy().toResourcePath(params);
}
protected void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType, boolean doesAddFirst,
IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec;
String varName = new String(fromResourceName);
String respTypeName = fromResourceType.getInterfaceTypeName();
String respImplTypeName = fromResourceType.getImplementationTypeName();
String respConverter = "";
if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) {
Type compType = TypeInference.getListComponentType(fromResourceType);
if (DataConstraintModel.typeTuple.isAncestorOf(compType)) {
varName += "_json";
String mapTypeName = convertFromEntryToMapType(compType, langSpec);
respTypeName = langSpec.newListType(mapTypeName).getInterfaceTypeName();
respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
respConverter += langSpec.getForStatementForCollection("i", mapTypeName, varName) + "\n";
respConverter += "\t" + langSpec.getMethodInvocation(fromResourceName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n";
respConverter += langSpec.getEndForStatement("i") ;
restApiSpec.addJsonException(methodBody);
} else if (DataConstraintModel.typeMap.isAncestorOf(compType)) {
// To do.
}
} else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter();
respImplTypeName = "HashMap";
} else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToPair(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter();
respImplTypeName = "HashMap";
} else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName, langSpec);
respImplTypeName = "HashMap";
}
if (doesAddFirst) {
if (respConverter.length() > 0) {
methodBody.addFirstStatement(respConverter);
}
methodBody.addFirstStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName));
} else {
methodBody.addStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName));
if (respConverter.length() > 0) {
methodBody.addStatement(respConverter);
}
}
}
protected String convertFromEntryToMapType(Type type, ILanguageSpecific langSpec) {
String mapTypeName = null;
if (DataConstraintModel.typePair.isAncestorOf(type)) {
Type compType = TypeInference.getPairComponentType(type);
String wrapperType = DataConstraintModel.getWrapperType(compType);
if (wrapperType != null) {
mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName();
} else {
mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compType.getInterfaceTypeName()).getInterfaceTypeName();
}
} else if (DataConstraintModel.typeMap.isAncestorOf(type)) {
List<Type> compTypes = TypeInference.getMapComponentTypes(type);
String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1));
if (wrapperType != null) {
mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName();
} else {
mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compTypes.get(1).getInterfaceTypeName()).getInterfaceTypeName();
}
} else {
mapTypeName = type.getInterfaceTypeName();
mapTypeName = mapTypeName.replace(DataConstraintModel.typeTuple.getInterfaceTypeName(), DataConstraintModel.typeMap.getInterfaceTypeName());
for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) { // Java specific
int to = mapTypeName.indexOf(",", idx); // Java specific
if (to > idx) {
mapTypeName = mapTypeName.substring(0, idx + 1) + DataConstraintModel.typeString.getInterfaceTypeName() + mapTypeName.substring(to); // All elements except for the last one have the string type.
}
}
}
return mapTypeName;
}
protected String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar, ILanguageSpecific langSpec) {
String decoded = "$x";
List<Type> elementsTypes = TypeInference.getTupleComponentTypes(tupleType);
String elementBase = mapVar;
for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) {
elementBase = langSpec.getFirstEntryFromMapExp(elementBase); // elementBase.entrySet().iterator().next()
if (elmType == DataConstraintModel.typeBoolean
|| elmType == DataConstraintModel.typeInt
|| elmType == DataConstraintModel.typeLong
|| elmType == DataConstraintModel.typeFloat
|| elmType == DataConstraintModel.typeDouble) {
String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey()
String elmVal = langSpec.getStringToValueExp(elmType.getImplementationTypeName(), getKey); // Integer.parseInt(elementBase.getKey())
decoded = decoded.replace("$x", langSpec.getPairExp(elmVal, "$x")); // new AbstractMap.SimpleEntry<>(Integer.parseInt(elementBase.getKey()), $x)
} else if (elmType == DataConstraintModel.typeString) {
String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey()
decoded = decoded.replace("$x", langSpec.getPairExp(getKey, "$x")); // new AbstractMap.SimpleEntry<>(elementBase.getKey(), $x)
} else {
// To do.
}
elementBase = langSpec.getMethodInvocation(elementBase, DataConstraintModel.snd.getImplName()); // elementBase.getValue()
}
decoded = decoded.replace("$x", elementBase);
return decoded;
}
protected String getCodeForConversionFromMapToPair(Type pairType, String mapVar, ILanguageSpecific langSpec) {
String decoded = "$x";
decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)");
decoded = decoded.replace("$x", mapVar + ".get(\"right\")");
return decoded;
}
protected String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar, ILanguageSpecific langSpec) {
List<Type> elementsTypes = TypeInference.getMapComponentTypes(mapType);
Type keyType = elementsTypes.get(0);
// Type valType = elementsTypes.get(1);
String decoded = "";
if (keyType == DataConstraintModel.typeBoolean
|| keyType == DataConstraintModel.typeInt
|| keyType == DataConstraintModel.typeLong
|| keyType == DataConstraintModel.typeFloat
|| keyType == DataConstraintModel.typeDouble) {
String keyVal = langSpec.getStringToValueExp(keyType.getImplementationTypeName(), "k");
String getInvocation = langSpec.getMethodInvocation(mapVal, DataConstraintModel.lookup.getImplName(), List.of(keyVal));
decoded += langSpec.getForStatementForMap("k", DataConstraintModel.typeString.getInterfaceTypeName(), mapVal) + "\n";
decoded += "\t" + langSpec.getMethodInvocation(mapVar, DataConstraintModel.insert.getImplName(), List.of(keyVal, getInvocation)) + langSpec.getStatementDelimiter() + "\n";
decoded += langSpec.getEndForStatement("k");
} else if (keyType == DataConstraintModel.typeString) {
decoded += mapVar + langSpec.getAssignment() + mapVal + langSpec.getStatementDelimiter();
}
return decoded;
}
protected IResourceStateAccessor getPushAccessor(IPlatformSpecific platformSpec) {
if (platformSpec.isMonolithic()) {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
ResourcePath fromRes= from.getResource();
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// use the cached value as the current state
return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
} else {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
ResourcePath fromRes = from.getResource();
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// use the cached value as the current state
return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
}
}
protected IResourceStateAccessor getPullAccessor(IPlatformSpecific platformSpec) {
if (platformSpec.isMonolithic()) {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
if (from != null) {
ResourcePath fromRes= from.getResource();
if (!target.isOutside()) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
Term getter = null;
String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec);
if (generatesComponent(targetRes.getResourceHierarchy())) {
getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType()));
} else {
String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec));
Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD));
getter.addChild(new Field(parentName, parentType));
}
return getter;
} else {
return getDirectStateAccessorFor(targetRes, null);
}
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
if (from != null) {
ResourcePath fromRes= from.getResource();
if (!target.isOutside()) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
Term getter = null;
String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec);
if (generatesComponent(targetRes.getResourceHierarchy())) {
getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType()));
} else {
String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec));
Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD));
getter.addChild(new Field(parentName, parentType));
}
return getter;
} else {
return getDirectStateAccessorFor(targetRes, null);
}
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null) {
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
if (fromRes.isAncestorOf(targetRes)) {
Stack<ResourcePath> pathStack = new Stack<>();
ResourcePath curPath = targetRes;
do {
pathStack.push(curPath);
curPath = curPath.getParent();
} while (!curPath.equals(fromRes));
// iterate from the fromRes resource
return getRelativePath(targetRes, pathStack);
}
if (generatesComponent(targetRes.getResourceHierarchy())) {
Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
getter.addChild(new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType()));
return getter;
} else {
return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType());
}
} else {
// (#3) access from the outside of the hierarchy (must be kept consistent with #4)
Stack<ResourcePath> pathStack = new Stack<>();
ResourcePath curPath = targetRes;
do {
pathStack.push(curPath);
curPath = curPath.getParent();
} while (curPath != null);
// iterate from the root resource
return getRelativePath(targetRes, pathStack);
}
}
private Expression getRelativePath(ResourcePath targetRes, Stack<ResourcePath> pathStack) {
ResourcePath curPath;
Term getter = null;
int arity = 2;
boolean doesChainInvocations = true;
while (!pathStack.empty()) {
curPath = pathStack.pop();
String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec);
if (getter == null && generatesComponent(curPath.getResourceHierarchy())) {
// root resource
String fieldName = langSpec.toVariableName(typeName);
getter = new Field(fieldName, new Type(typeName, typeName));
} else {
if (generatesComponent(curPath.getResourceHierarchy())) {
if (arity == 2) {
Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Expression param = curPath.getLastParam();
if (param != null) {
newGetter.addChild(param);
newGetter.getSymbol().setArity(2);
}
}
getter = newGetter;
} else {
// add the last path parameter.
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
}
}
}
arity = 2;
doesChainInvocations = true;
} else {
// to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id))
if (doesChainInvocations) {
Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
doesChainInvocations = false;
}
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
// may change the symbol name
getter.getSymbol().changeName(getterPrefix + typeName);
// add a path parameter.
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
arity++;
}
}
}
}
}
if (generatesComponent(targetRes.getResourceHierarchy())) {
Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
}
return getter;
}
};
} else {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
if (from != null && !target.isOutside()) {
ResourcePath fromRes = from.getResource();
if (targetRes.getCommonPrefix(fromRes) != null) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
}
// for reference channel member
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
if (from != null && !target.isOutside()) {
ResourcePath fromRes = from.getResource();
if (targetRes.getCommonPrefix(fromRes) != null) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
}
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) {
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
} else {
// (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4)
Stack<ResourcePath> pathStack = new Stack<>();
ResourcePath curPath = targetRes;
do {
if (fromRes != null && curPath.equals(fromRes)) break;
pathStack.push(curPath);
curPath = curPath.getParent();
} while (curPath != null);
// iterate from the `from' resource
Term getter = null;
int arity = 2;
boolean doesChainInvocations = true;
while (!pathStack.empty()) {
curPath = pathStack.pop();
String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec);
if (getter == null && fromRes == null) {
// root resource
String fieldName = langSpec.toVariableName(typeName);
getter = new Field(fieldName, new Type(typeName, typeName));
} else {
if (generatesComponent(curPath.getResourceHierarchy())) {
if (arity == 2) {
Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Expression param = curPath.getLastParam();
if (param != null) {
newGetter.addChild(param);
newGetter.getSymbol().setArity(2);
}
}
getter = newGetter;
} else {
// add the last path parameter.
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
}
}
}
arity = 2;
doesChainInvocations = true;
} else {
// to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id))
if (doesChainInvocations) {
Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
doesChainInvocations = false;
}
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
// may change the symbol name
getter.getSymbol().changeName(getterPrefix + typeName);
// add a path parameter.
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
arity++;
}
}
}
}
}
if (generatesComponent(targetRes.getResourceHierarchy())) {
Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
}
return getter;
}
}
};
}
}
protected IResourceStateAccessor getRefAccessor(IPlatformSpecific platformSpec) {
if (platformSpec.isMonolithic()) {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
ResourcePath fromRes= from.getResource();
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// use the cached value as the current state
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes= target.getResource();
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
} else {
return new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
ResourcePath fromRes = from.getResource();
if (targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field(fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
}
}
}