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 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.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.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 getterPrefix = "get";
public static final String getterOfResourceState = "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;
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;
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, DataFlowGraph flowGraph, ILanguageSpecific langSpec) {
CodeGenerator.langSpec = langSpec;
ArrayList<CompilationUnit> codes = new ArrayList<>();
// Get the dependency among root nodes.
Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph = getDependedRootComponentGraph(model);
// Sort the all components.
ArrayList<ResourceNode> components = determineComponentOrder(flowGraph, dependedRootComponentGraph);
// Add the main component.
if (mainTypeName == null) {
mainTypeName = langSpec.getMainComponentName();
}
TypeDeclaration mainComponent = langSpec.newTypeDeclaration(mainTypeName);
MethodDeclaration mainConstructor = mainComponent.createConstructor();
CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent);
codes.add(mainCU);
// Generate the other components.
generateCodeFromFlowGraph(model, flowGraph, components, dependedRootComponentGraph, mainComponent, mainConstructor, codes, langSpec);
return codes;
}
public abstract void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList<ResourceNode> components, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph,
TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec);
private static Map<ResourceHierarchy, Set<ResourceHierarchy>> getDependedRootComponentGraph(DataTransferModel model) {
Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedComponentGraph = new HashMap<>();
for (Channel ch: model.getChannels()) {
DataTransferChannel dtCh = (DataTransferChannel) ch;
Set<ResourceHierarchy> inRes = new HashSet<>();
Set<ResourceHierarchy> outRes = new HashSet<>();
for (ChannelMember cm: dtCh.getChannelMembers()) {
if (cm.isOutside()) {
outRes.add(cm.getResource().getResourceHierarchy());
} else {
inRes.add(cm.getResource().getResourceHierarchy());
}
}
if (outRes.size() > 0 && inRes.size() > 0) {
for (ResourceHierarchy out: outRes) {
for (ResourceHierarchy in: inRes) {
Set<ResourceHierarchy> dependings = dependedComponentGraph.get(out.getRoot());
if (dependings == null) {
dependings = new HashSet<>();
dependedComponentGraph.put(out.getRoot(), dependings);
}
dependings.add(in.getRoot());
}
}
}
}
return dependedComponentGraph;
}
private static ArrayList<ResourceNode> determineComponentOrder(DataFlowGraph graph, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph) {
ArrayList<ResourceNode> objects = new ArrayList<>();
Set<ResourceNode> visited = new HashSet<>();
Collection<ResourceNode> allNodes = graph.getResourceNodes();
for (ResourceNode resNode: allNodes) {
topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, objects);
}
return objects;
}
private static void topologicalSort(ResourceNode curResNode, Collection<ResourceNode> allNodes, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, Set<ResourceNode> visited, List<ResourceNode> orderedList) {
if (visited.contains(curResNode)) return;
visited.add(curResNode);
// A caller is before the callee
// For each incoming PUSH transfer.
for (Edge chToRes: curResNode.getInEdges()) {
for (Edge resToCh: chToRes.getSource().getInEdges()) {
if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
topologicalSort((ResourceNode) resToCh.getSource(), allNodes, dependedRootComponentGraph, visited, orderedList);
}
}
}
// For each outgoing PULL transfer.
if (curResNode instanceof ResourceNode) {
for (Edge resToCh: curResNode.getOutEdges()) {
DataFlowEdge de = (DataFlowEdge) resToCh;
if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
for (Edge chToRes : resToCh.getDestination().getOutEdges()) {
topologicalSort((ResourceNode) chToRes.getDestination(), allNodes, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
// For each depending root node.
if (curResNode instanceof ResourceNode && dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) {
for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) {
for (ResourceNode rootNode: allNodes) {
ResourceHierarchy rootRes = rootNode.getResourceHierarchy();
if (rootRes.getParent() == null && rootRes.equals(dependingRes)) {
topologicalSort(rootNode, allNodes, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
// For each reference resource.
ResourceNode cn = null;
if (curResNode instanceof ResourceNode) {
cn = (ResourceNode) curResNode;
}
if (cn != null) {
for (Node n: allNodes) {
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 (curResNode.getOutSideResources().contains(m.getResource())) {
topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, orderedList);
}
}
}
}
}
}
orderedList.add(0, curResNode);
}
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()) {
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, 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
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
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 ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) {
// the child is not a class
childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD));
childGetter.addChild(new Constant("this"));
} 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 ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) {
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(descendant, 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, 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++;
}
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().getDirectStateAccessorFor(accessRes.getPrimaryResourcePath(), 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) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) return m;
}
return null;
}
protected IResourceStateAccessor getPushAccessor() {
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(targetRes.getLeafResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targerRes= target.getResource();
return new Parameter(targerRes.getLeafResourceName(),
targerRes.getResourceStateType() != null ? targerRes.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(targetRes.getLeafResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
}
protected IResourceStateAccessor getPullAccessor() {
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
Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType()));
return getter;
} else {
// access from the outside of the hierarchy
Stack<ResourcePath> pathStack = new Stack<>();
ResourcePath curPath = targetRes;
do {
pathStack.push(curPath);
curPath = curPath.getParent();
} while (curPath != null);
// iterate from the root resource
Term getter = null;
int v = 1;
int arity = 2;
while (!pathStack.empty()) {
curPath = pathStack.pop();
String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec);
if (getter == 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) {
Variable var = null;
Expression param = curPath.getLastParam();
if (param instanceof Variable) {
var = (Variable) param;
} else if (param instanceof Term) {
var = new Variable("v" + v, ((Term) param).getType());
}
if (var != null) {
newGetter.addChild(var);
newGetter.getSymbol().setArity(2);
}
v++;
}
getter = newGetter;
} else {
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Variable var = null;
Expression param = curPath.getLastParam();
if (param instanceof Variable) {
var = (Variable) param;
} else if (param instanceof Term) {
var = new Variable("v" + v, ((Term) param).getType());
}
if (var != null) {
getter.getSymbol().setArity(arity);
getter.addChild(var);
}
v++;
}
}
arity = 2;
} else {
// to get a descendant resource directly.
if (arity == 2) {
Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
}
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Variable var = null;
Expression param = curPath.getLastParam();
if (param instanceof Variable) {
var = (Variable) param;
} else if (param instanceof Term) {
var = new Variable("v" + v, ((Term) param).getType());
}
if (var != null) {
getter.getSymbol().setArity(arity);
getter.addChild(var);
arity++;
}
v++;
}
}
}
}
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() {
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(targetRes.getLeafResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targerRes= target.getResource();
return new Parameter(targerRes.getLeafResourceName(),
targerRes.getResourceStateType() != null ? targerRes.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(targetRes.getLeafResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
}
}