package generators;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import code.ast.CompilationUnit;
import code.ast.Block;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
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.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ListType;
import models.dataConstraintModel.MapType;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.IFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
public class CodeGenerationContext {
protected DataTransferModel model = null;
protected IFlowGraph flowGraph = null;
protected ArrayList<CompilationUnit> AST;
protected HashMap<String, HashMap<ResourceHierarchy, String>> componentNames = new HashMap<>();
protected Map<ResourceHierarchy, TypeDeclaration> resourceHierarchyToComponent;
protected Map<String, Type> componentNameToType;
protected Map<ResourceHierarchy, Map<ResourceHierarchy, Map<DataTransferChannel, String>>> updateMethods;
protected ILanguageSpecific langSpec = null;
protected IPlatformSpecific platformSpec = null;
public CodeGenerationContext(ILanguageSpecific langSpec, IPlatformSpecific platformSpec) {
this.resourceHierarchyToComponent = new HashMap<>();
this.componentNameToType = new HashMap<>();
this.updateMethods = new HashMap<>();
this.langSpec = langSpec;
this.platformSpec = platformSpec;
}
public void init(DataTransferModel model, IFlowGraph flowGraph) {
this.setModel(model);
this.setFlowGraph(flowGraph);
this.AST = new ArrayList<>();
}
public void setModel(DataTransferModel model) {
this.model = model;
}
public DataTransferModel getModel() {
return this.model;
}
public void setFlowGraph(IFlowGraph flowGraph) {
this.flowGraph = flowGraph;
}
public IFlowGraph getFlowGraph() {
return this.flowGraph;
}
public CompilationUnit createCompilationUnit(ResourceHierarchy resourceHierarchy, TypeDeclaration component) {
CompilationUnit cu = langSpec.newCompilationUnit(component);
if (!platformSpec.isMonolithic() && resourceHierarchy.getParent() == null) {
// For each root node, add platform specific imports.
((RestApiSpecific) platformSpec).addPlatformSpecificImports(cu);
}
addCompilationUnit(cu);
resourceHierarchyToComponent.put(resourceHierarchy, component);
return cu;
}
public void addCompilationUnit(CompilationUnit compilationUnit) {
this.AST.add(compilationUnit);
}
public void putComponent(ResourceHierarchy res, TypeDeclaration component) {
resourceHierarchyToComponent.put(res, component);
}
public TypeDeclaration getComponent(ResourceHierarchy res) {
return resourceHierarchyToComponent.get(res);
}
public ArrayList<CompilationUnit> getAST() {
return this.AST;
}
public 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";
}
}
String componentName = langSpec.toComponentName(name);
if (!CodeGenerator.generatesComponent(res)) return componentName;
// To avoid generating multiple components with the same name.
HashMap<ResourceHierarchy, String> resToName = componentNames.get(componentName);
if (resToName == null) {
resToName = new HashMap<>();
resToName.put(res, componentName);
componentNames.put(componentName, resToName);
return componentName;
}
if (resToName.get(res) == null) {
componentName += resToName.size();
resToName.put(res, componentName);
return componentName;
}
return resToName.get(res);
}
public Type getOrCreateComponentType(ResourceHierarchy res) {
return getOrCreateComponentType(getComponentName(res, langSpec));
}
public Type getOrCreateComponentType(String componentName) {
Type componentType = componentNameToType.get(componentName);
if (componentType != null) return componentType;
componentType = new Type(componentName, new code.ast.SimpleType(componentName));
componentNameToType.put(componentName, componentType);
return componentType;
}
public 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()) || res.getResourceStateType() instanceof ListType) {
// list.
if (CodeGenerator.generatesComponent(child)) {
return langSpec.newListType(getOrCreateComponentType(child));
} else {
return langSpec.newListType(getImplStateType(child, langSpec));
}
} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType()) || res.getResourceStateType() instanceof MapType) {
// map.
if (CodeGenerator.generatesComponent(child)) {
return langSpec.newMapType(DataConstraintModel.typeString, getOrCreateComponentType(child).getInterfaceTypeName());
} else {
return langSpec.newMapType(DataConstraintModel.typeString, getImplStateType(child, langSpec).getInterfaceTypeName());
}
}
return null;
} else {
// class
return res.getResourceStateType();
}
}
}
public MethodDeclaration getConstructor(TypeDeclaration component) {
for (MethodDeclaration m: component.getMethods()) {
if (m.isConstructor()) return m;
}
return null;
}
public 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;
}
public MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) return m;
}
return null;
}
public String getOrPutUpdateMethodName(ResourceHierarchy srcRes, DataTransferChannel ch, ResourceHierarchy dstRes) {
Map<ResourceHierarchy, Map<DataTransferChannel, String>> dstResUpdatesMethods = updateMethods.getOrDefault(dstRes, new HashMap<>());
updateMethods.put(dstRes, dstResUpdatesMethods);
Map<DataTransferChannel, String> dstResFromSrcResUpdatesMethods = dstResUpdatesMethods.getOrDefault(srcRes, new HashMap<>());
dstResUpdatesMethods.put(srcRes, dstResFromSrcResUpdatesMethods);
String updateMethodName = dstResFromSrcResUpdatesMethods.get(ch);
if (updateMethodName == null) {
String srcResComponentName = getComponentName(srcRes, langSpec);
String dstResComponentName = getComponentName(dstRes, langSpec);
if (CodeGenerator.generatesComponent(dstRes)) {
updateMethodName = CodeGenerator.updateMethodPrefix + CodeGenerator.from + srcResComponentName;
} else if (dstRes.getParent() != null) {
updateMethodName = CodeGenerator.updateMethodPrefix + dstResComponentName + CodeGenerator.from + srcResComponentName;
}
if (dstResFromSrcResUpdatesMethods.size() > 0) {
updateMethodName += dstResFromSrcResUpdatesMethods.size() + 1; // To avoid declaring the method multiply.
}
dstResFromSrcResUpdatesMethods.put(ch, updateMethodName);
}
return updateMethodName;
}
public MethodDeclaration getOrDeclareUpdateMethod(ResourceHierarchy srcRes, ResourceHierarchy dstRes, DataTransferChannel ch, ArrayList<VariableDeclaration> parameters) {
MethodDeclaration update = null;
String updateMethodName = getOrPutUpdateMethodName(srcRes, ch, dstRes);
if (CodeGenerator.generatesComponent(dstRes)) {
// A component is created for this resource.
TypeDeclaration dstComponent = getComponent(dstRes);
for (MethodDeclaration method: dstComponent.getMethods()) {
if (method.getName().equals(updateMethodName)) {
update = method;
break;
}
}
if (update == null) {
update = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters);
dstComponent.addMethod(update);
}
} else if (dstRes.getParent() != null) {
// No component is created for this resource.
TypeDeclaration dstParentComponent = getComponent(dstRes.getParent());
for (MethodDeclaration method: dstParentComponent.getMethods()) {
if (method.getName().equals(updateMethodName)) {
update = method;
break;
}
}
if (update == null) {
update = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters);
dstParentComponent.addMethod(update);
}
}
return update;
}
public void declareGetterAccessorInTheRootResource(ResourceNode resourceNode, TypeDeclaration rootComponent) {
if (resourceNode.getResourceHierarchy().getParent() != null) {
// For a non-root resource
MethodDeclaration getterAccessor = null;
List<VariableDeclaration> mainGetterParams = new ArrayList<>();
String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams, platformSpec, langSpec);
if (resourcePath.indexOf('/') > 0) {
// Remove the root resource from the path
resourcePath = resourcePath.substring(resourcePath.indexOf('/'));
} else {
resourcePath = "";
}
if (mainGetterParams.size() > 0) {
getterAccessor = langSpec.newMethodDeclaration(CodeGeneratorFromDataFlowGraph.getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + CodeGeneratorFromDataFlowGraph.methoNameOfResourceState,
false,
getImplStateType(resourceNode.getResourceHierarchy(), langSpec),
mainGetterParams);
} else {
getterAccessor = langSpec.newMethodDeclaration(CodeGeneratorFromDataFlowGraph.getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + CodeGeneratorFromDataFlowGraph.methoNameOfResourceState,
getImplStateType(resourceNode.getResourceHierarchy(), langSpec));
}
getterAccessor.setBody(new Block());
ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath());
for (int i = 0; i < mainGetterParams.size(); i++) {
Parameter pathParam = new Parameter(mainGetterParams.get(i).getName());
resPath.replacePathParam(i, pathParam, null);
}
Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot());
getterAccessor.getBody().addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})));
if (!platformSpec.isMonolithic()) {
((RestApiSpecific) platformSpec).addGetAnnotations(getterAccessor);
if (resourcePath.length() > 0) {
((RestApiSpecific) platformSpec).addPathAnnotation(getterAccessor, resourcePath);
}
}
rootComponent.addMethod(getterAccessor);
}
}
public 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);
}
public void declareUpdateAccessorInTheRootResource(ResourceNode resourceNode, String updateMethodName, DataTransferChannel ch, ChannelMember cm, ResourcePath srcResPath, ResourcePath dstResPath, TypeDeclaration rootComponent, int inDegree, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
ArrayList<VariableDeclaration> parameters;
VariableDeclaration param;
parameters = new ArrayList<>();
String resourcePath = getUpdateResourcePathAndPathParams(dstResPath, parameters, true, platformSpec, langSpec); // Path parameters to identify the self resource.
if (resourcePath.indexOf('/') > 0) {
// Remove the root resource from the path
resourcePath = resourcePath.substring(resourcePath.indexOf('/'));
} else {
resourcePath = "";
}
ResourcePath resPath = new ResourcePath(dstResPath);
for (int i = 0; i < parameters.size(); i++) {
Parameter pathParam = new Parameter(parameters.get(i).getName());
resPath.replacePathParam(i, pathParam, null);
}
for (Selector selector: ch.getAllSelectors()) {
if (selector.getExpression() instanceof Variable) {
Variable selVar = (Variable) selector.getExpression();
VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName());
if (!platformSpec.isMonolithic()) {
((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName());
}
parameters.add(chParam); // A channel parameter to specify the context of the collaboration.
}
}
Type srcType = srcResPath.getResourceStateType();
String srcResName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec));
param = langSpec.newVariableDeclaration(srcType, srcResName);
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName);
parameters.add(param); // The state of the source resource to carry the data-flow.
for (ResourcePath refRes: ch.getReferenceResources()) {
if (!refRes.equals(resourceNode.getInSideResource(ch))) {
String refName = langSpec.toVariableName(getComponentName(refRes.getResourceHierarchy(), langSpec));
param = langSpec.newVariableDeclaration(refRes.getResourceStateType(), refName);
if (!platformSpec.isMonolithic()) {
((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName);
}
parameters.add(param);
}
}
MethodDeclaration updateAccessor = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters);
if (!platformSpec.isMonolithic()) {
if (isPut(cm)) {
((RestApiSpecific) platformSpec).addPutAnnotations(updateAccessor);
} else {
if (!isDelete(cm)) {
((RestApiSpecific) platformSpec).addPostAnnotations(updateAccessor);
} else {
((RestApiSpecific) platformSpec).addDeleteAnnotations(updateAccessor);
}
}
}
if (inDegree > 1) {
// For each source resource, a child resource is defined in the destination resource so that its state can be updated separately.
resourcePath += "/" + langSpec.toVariableName(srcResName);
}
if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathAnnotation(updateAccessor, resourcePath);
// To make the accessor call the update method.
Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot());
List<String> args = new ArrayList<>();
if (resExp instanceof Term) {
// To access the parent resource if the leaf resource is primitive and cannot declare the update method, or to remove getValue().
if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) {
args.add(((Variable)((Term) resExp).getChild(1)).getName());
}
resExp = ((Term) resExp).getChild(0);
}
for (VariableDeclaration var: updateAccessor.getParameters()) {
args.add(var.getName());
}
if (resExp != null) {
String resourceAccess = resExp.toImplementation(new String[] {""});
updateAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, updateMethodName, args) + langSpec.getStatementDelimiter());
} else {
updateAccessor.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + langSpec.getStatementDelimiter());
}
rootComponent.addMethod(updateAccessor);
}
public 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);
}
public MethodDeclaration declareInputAccessorInTheRootResource(String inputMethodName,
ArrayList<VariableDeclaration> rootInputParams, ChannelMember cm, String resourcePath,
TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
MethodDeclaration rootInputAccessor;
rootInputAccessor = langSpec.newMethodDeclaration(inputMethodName, false, null, rootInputParams);
if (!platformSpec.isMonolithic()) {
if (isPut(cm)) {
((RestApiSpecific) platformSpec).addPutAnnotations(rootInputAccessor);
} else {
if (!isDelete(cm)) {
((RestApiSpecific) platformSpec).addPostAnnotations(rootInputAccessor);
} else {
((RestApiSpecific) platformSpec).addDeleteAnnotations(rootInputAccessor);
}
}
if (resourcePath.length() > 0) {
((RestApiSpecific) platformSpec).addPathAnnotation(rootInputAccessor, resourcePath);
}
}
rootComponent.addMethod(rootInputAccessor);
return rootInputAccessor;
}
public 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);
}
public boolean isPut(ChannelMember cm) {
return cm.getStateTransition().isRightUnary();
}
public 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;
}
public 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(CodeGenerator.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(CodeGenerator.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(CodeGenerator.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(CodeGenerator.fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
}
}
public 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 (CodeGenerator.generatesComponent(targetRes.getResourceHierarchy())) {
getter = new Term(new Symbol(CodeGenerator.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(CodeGenerator.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 (CodeGenerator.generatesComponent(targetRes.getResourceHierarchy())) {
getter = new Term(new Symbol(CodeGenerator.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(CodeGenerator.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(CodeGenerator.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 (CodeGenerator.generatesComponent(targetRes.getResourceHierarchy())) {
Term getter = new Term(new Symbol(CodeGenerator.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 && CodeGenerator.generatesComponent(curPath.getResourceHierarchy())) {
// root resource
String fieldName = langSpec.toVariableName(typeName);
getter = new Field(fieldName, getOrCreateComponentType(typeName));
} else {
if (CodeGenerator.generatesComponent(curPath.getResourceHierarchy())) {
if (arity == 2) {
Term newGetter = new Term(new Symbol(CodeGenerator.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(CodeGenerator.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(CodeGenerator.getterPrefix + typeName);
// add a path parameter.
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
arity++;
}
}
}
}
}
if (CodeGenerator.generatesComponent(targetRes.getResourceHierarchy())) {
Term newGetter = new Term(new Symbol(CodeGenerator.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(CodeGenerator.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, getOrCreateComponentType(typeName));
} else {
if (CodeGenerator.generatesComponent(curPath.getResourceHierarchy())) {
if (arity == 2) {
Term newGetter = new Term(new Symbol(CodeGenerator.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(CodeGenerator.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(CodeGenerator.getterPrefix + typeName);
// add a path parameter.
Expression param = curPath.getLastParam();
if (param != null) {
getter.getSymbol().setArity(arity);
getter.addChild(param);
arity++;
}
}
}
}
}
if (CodeGenerator.generatesComponent(targetRes.getResourceHierarchy())) {
Term newGetter = new Term(new Symbol(CodeGenerator.getterOfResourceState, 1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
}
return getter;
}
}
};
}
}
public 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(CodeGenerator.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(CodeGenerator.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(CodeGenerator.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(CodeGenerator.fieldOfResourceState,
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
}
}
}