diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model index 6c989ee..f54dd60 100644 --- a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model +++ b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model @@ -1,3 +1,7 @@ +channel CIO_AddCustomer { + out customers(db:Map, addCustomer(uid:Str, off:Str)) == insert(db, uid, addMember(nil, "off", off)) +} + channel CIO_SetCustomerOff(uid:Str) { out customers.{uid}.off(cid:Str, setOff(cid2)) == cid2 } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index c81cdaf..21220f2 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -99,7 +99,6 @@ static public void infer(DataTransferModel model) { Map> resources = new HashMap<>(); - Map> resourceOwnership = new HashMap<>(); Map> resourcePathParams = new HashMap<>(); Map variables = new HashMap<>(); Map, Type>>> messages = new HashMap<>(); @@ -123,7 +122,6 @@ // Maps from the objectId of each group to the set of updated expressions. Map> updateFromResource = new HashMap<>(); Set updateFromResourceOwnership = new HashSet<>(); - Map> updateFromPathParams = new HashMap<>(); Map> updateFromVariable = new HashMap<>(); Map> updateFromMessage = new HashMap<>(); Map> updateFromConsOrSet = new HashMap<>(); @@ -1164,14 +1162,6 @@ // 1.6 Extract constraints on resource hierarchies. for (ResourceHierarchy res: model.getResourceHierarchies()) { - if (res.getParent() != null) { - Set children = resourceOwnership.get(res.getParent()); - if (children == null) { - children = new HashSet<>(); - resourceOwnership.put(res.getParent(), children); - } - children.add(res); - } if (res.getResourceStateType() != null) { updateFromResourceOwnership.add(res); } @@ -1321,7 +1311,7 @@ if (updateFromResourceOwnership.size() > 0) { ResourceHierarchy res = updateFromResourceOwnership.iterator().next(); updateFromResourceOwnership.remove(res); - updateResourceOwnershipTypes(res, resources, resourceOwnership, expToResource, updateFromResource, updateFromResourceOwnership); + updateResourceOwnershipTypes(res, resources, expToResource, updateFromResource, updateFromResourceOwnership); } } } @@ -1395,11 +1385,10 @@ } private static void updateResourceOwnershipTypes(ResourceHierarchy res, Map> resources, - Map> resourceOwnership, Map> expToResource, - Map> updateFromResource, Set updateFromResourceOwnership) { - for (ResourceHierarchy parent: resourceOwnership.keySet()) { + Map> expToResource, Map> updateFromResource, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resources.keySet()) { Type resType = res.getResourceStateType(); - Set children = resourceOwnership.get(parent); + Set children = parent.getChildren(); if (res.equals(parent)) { // Propagate an update of a parent resource type to its children' types. if (DataConstraintModel.typeList.isAncestorOf(resType)) { @@ -2028,16 +2017,18 @@ if (jsonMemberGroup.get(1) instanceof Constant) { keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); } - Type memberType = memberTypes.get(keyName); - Type newMemberType = getExpTypeIfUpdatable(memberType, exp); - if (newMemberType != null && keyName != null) { - // Propagate an update of a member's type to its container's (json's) type. - Map newMemberTypes = new HashMap<>(memberTypes); - newMemberTypes.put(keyName, newMemberType); - newJsonType = jsonTypes.get(newMemberTypes); - if (newJsonType == null) { - // Create new json type. - newJsonType = createNewJsonType(newMemberTypes, jsonType); + if (memberTypes != null) { + Type memberType = memberTypes.get(keyName); + Type newMemberType = getExpTypeIfUpdatable(memberType, exp); + if (newMemberType != null && keyName != null) { + // Propagate an update of a member's type to its container's (json's) type. + Map newMemberTypes = new HashMap<>(memberTypes); + newMemberTypes.put(keyName, newMemberType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(newMemberTypes, jsonType); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java index f4e94dd..ae548ee 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import code.ast.Block; import code.ast.CompilationUnit; @@ -17,6 +18,7 @@ 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; @@ -27,6 +29,7 @@ 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; @@ -47,9 +50,11 @@ */ 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 updateMethodName = "update"; private static String mainTypeName = null; + private static ILanguageSpecific langSpec = null; public static String getMainTypeName() { return mainTypeName; @@ -63,6 +68,44 @@ 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 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. + return langSpec.newListType(getComponentName(child, langSpec)); + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + return langSpec.newMapType(DataConstraintModel.typeString, getComponentName(child, langSpec)); + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + /** * Generate source codes in specified language from data-flow/control-flow graph. * @@ -72,6 +115,7 @@ * @return source codes */ public ArrayList generateCode(DataTransferModel model, DataFlowGraph flowGraph, ILanguageSpecific langSpec) { + CodeGenerator.langSpec = langSpec; ArrayList codes = new ArrayList<>(); // Sort the all components. @@ -213,66 +257,115 @@ constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter()); } - protected void fillGetterMethodToReturnStateField(MethodDeclaration getter, Type resStateType, ILanguageSpecific langSpec) { + 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)) { - getter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; } else { - // copy the current state to be returned as a 'value' - String implTypeName = resStateType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -//// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<>"; -// } else { -// concreteTypeName = implTypeName; -// } - List parameters = new ArrayList<>(); - parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); - getter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); + 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 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 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 Field(fieldName, getImplStateType(child, langSpec)); + } 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]); + stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } } } - protected void fillGetterMethodToReturnComponentOfStateField(MethodDeclaration getter, Type containerStateType, ILanguageSpecific langSpec) { - if (DataConstraintModel.typeList.isAncestorOf(containerStateType)) { + protected void fillChildGetterMethod(MethodDeclaration childGetter, ResourceHierarchy child, Type parentResourceType, ILanguageSpecific langSpec) { + if (DataConstraintModel.typeList.isAncestorOf(parentResourceType)) { Term selector = new Term(DataConstraintModel.get); selector.addChild(new Variable(langSpec.getFieldAccessor(fieldOfResourceState))); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement(langSpec.getReturnStatement(selector.toImplementation(new String[] {})) + langSpec.getStatementDelimiter()); - } else if (DataConstraintModel.typeMap.isAncestorOf(containerStateType)) { + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else if (DataConstraintModel.typeMap.isAncestorOf(parentResourceType)) { Term selector = new Term(DataConstraintModel.lookup); selector.addChild(new Variable(langSpec.getFieldAccessor(fieldOfResourceState))); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement(langSpec.getReturnStatement(selector.toImplementation(new String[] {})) + langSpec.getStatementDelimiter()); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else { + String fieldName = langSpec.toVariableName(getComponentName(child, langSpec)); + childGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldName)) + langSpec.getStatementDelimiter()); } } - protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration getter, ILanguageSpecific langSpec) { - List mainParams = new ArrayList<>(); + protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, ILanguageSpecific langSpec) { + List mainParams = new ArrayList<>(); + int v = 1; for (Selector selector: accessRes.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - mainParams.add(var.getName()); + mainParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } MethodDeclaration accessor = null; if (mainParams.size() == 0) { - accessor = langSpec.newMethodDeclaration("get" + langSpec.toComponentName(accessRes.getResourceName()), accessRes.getResourceStateType()); + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec)); } else { - accessor = langSpec.newMethodDeclaration("get" + langSpec.toComponentName(accessRes.getResourceName()), false, accessRes.getResourceStateType(), getter.getParameters()); + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainParams); } Block block = new Block(); - if (getter.getParameters() == null || getter.getParameters().size() == 0) { - block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), getter.getName())) + langSpec.getStatementDelimiter()); - } else { - List resParams = new ArrayList<>(); - for (VariableDeclaration var: getter.getParameters()) { - resParams.add(var.getName()); - } - block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), getter.getName(), resParams)) + langSpec.getStatementDelimiter()); - } + Expression getState = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(accessRes.getOutSideResource(), 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 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); } @@ -358,15 +451,63 @@ return new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field(fieldOfResourceState, - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); + if (from != null) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = target; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + 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 { + 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; + } + } + + if (target.getResourceHierarchy().getNumParameters() > 0 + || (target.getResourceHierarchy().getChildren() != null && target.getResourceHierarchy().getChildren().size() > 0)) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; } - // for reference channel member - Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; } @Override diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index 9a59356..3f05ff0 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -28,6 +28,7 @@ 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.DataFlowEdge; @@ -46,11 +47,16 @@ public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { - // For each of other components. + // For each components. for (Node componentNode: components) { // Declare this resource. ResourceNode resourceNode = (ResourceNode) componentNode; - String resourceName = langSpec.toComponentName(resourceNode.getResourceName()); + if (resourceNode.getResourceHierarchy().getChildren().size() == 0 && resourceNode.getResourceHierarchy().getNumParameters() == 0) { + // No component is created for a simple leaf resource. + continue; + } + + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); TypeDeclaration component = getComponent(resourceName, codes); List depends = new ArrayList<>(); if (component == null) { @@ -59,33 +65,38 @@ CompilationUnit cu = langSpec.newCompilationUnit(component); codes.add(cu); - // Declare the constructor and the fields to refer to other resources. - MethodDeclaration constructor = declareConstructorAndFieldsToReferToResources(resourceNode, component, depends, langSpec); - - // Update the main component for this component. - updateMainComponent(model, mainComponent, mainConstructor, componentNode, depends, langSpec); - - // Declare the fields to refer to reference resources. - declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); - - if (constructor.getParameters() == null) { - component.removeMethod(constructor); + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // Declare the constructor and the fields to refer to other resources. + MethodDeclaration constructor = declareConstructorAndFieldsToReferToResources(resourceNode, component, depends, langSpec); + + // Update the main component for this component. + updateMainComponent(model, mainComponent, mainConstructor, componentNode, depends, langSpec); + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + if (constructor.getParameters() == null) { + component.removeMethod(constructor); + } } } - Type resStateType = resourceNode.getResourceStateType(); + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); // Declare the field in this resource to store the state. - if (((StoreAttribute) resourceNode.getAttribute()).isStored() && resourceNode.getNumberOfParameters() == 0) { - FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); - component.addField(stateField); + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + declareStateField(resourceNode, component, resStateType, langSpec); } // Declare the getter method in this resource to obtain the state. - MethodDeclaration getter = declareGetterMethod(resourceNode, component, resStateType, langSpec); + MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, langSpec); + + // Declare the getter methods in this resource to obtain children of the resource. + declareChildGetterMethod(resourceNode, component, langSpec); // Declare the accessor method in the main component to call the getter method. - declareAccessorInMainComponent(mainComponent, resourceNode, getter, langSpec); + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); // Declare cache fields and update methods in this resource. List updates = declareCacheFieldsAndUpdateMethods(resourceNode, component, langSpec); @@ -139,30 +150,47 @@ } return constructor; } - - private MethodDeclaration declareGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { - // Declare the getter method of the resource state. - ArrayList params = new ArrayList<>(); - for (Selector selector: resourceNode.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - params.add(new VariableDeclaration(var.getType(), var.getName())); + + private void declareStateField(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, + ILanguageSpecific langSpec) { + Set children = resourceNode.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c, langSpec); + Type childType = null; + if ((c.getChildren() == null || c.getChildren().size() == 0) && c.getNumParameters() == 0) { + // The child does not have a component. + childType = c.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + String fieldName = langSpec.toVariableName(childTypeName); + FieldDeclaration stateField = langSpec.newFieldDeclaration(childType, fieldName, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } } } - MethodDeclaration getter = null; - if (params.size() > 0) { - getter = langSpec.newMethodDeclaration(getterOfResourceState, false, resStateType, params); - } else { - getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); - } - component.addMethod(getter); + } + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(stateGetter); if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { - if (resourceNode.getResourceHierarchy().getNumParameters() == 0) { - fillGetterMethodToReturnStateField(getter, resStateType, langSpec); - } else { - fillGetterMethodToReturnComponentOfStateField(getter, resourceNode.getParent().getResourceStateType(), langSpec); - } + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. boolean isContainedPush = false; @@ -195,11 +223,11 @@ if (!isContainedPush) { // All incoming edges are in PULL-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } else { // At least one incoming edge is in PUSH-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } break; } @@ -210,7 +238,44 @@ } } - return getter; + return stateGetter; + } + + private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { + // Declare the getter methods in this resource to obtain children of the resource. + for (ResourceNode child: resourceNode.getChildren()) { + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: child.getSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + pathParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + pathParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childTypeName = getComponentName(child.getResourceHierarchy(), langSpec); + Type childType = null; + if ((child.getResourceHierarchy().getChildren() == null || child.getResourceHierarchy().getChildren().size() == 0) + && child.getResourceHierarchy().getNumParameters() == 0) { + // The child does not have a component. + childType = child.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + MethodDeclaration childGetter = null; + if (pathParams.size() == 0) { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childTypeName, childType); + } else { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childTypeName, false, childType, pathParams); + } + + fillChildGetterMethod(childGetter, child.getResourceHierarchy(), resourceNode.getResourceStateType(), langSpec); + component.addMethod(childGetter); + } } private List declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { @@ -343,12 +408,6 @@ // Declare an input method in this component. ArrayList resParams = new ArrayList<>(); ArrayList mainParams = new ArrayList<>(); - for (Selector selector: resourceNode.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } for (Selector selector: resourceNode.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); @@ -387,12 +446,6 @@ // Declare an input method in this component. ArrayList resParams = new ArrayList<>(); ArrayList mainParams = new ArrayList<>(); - for (Selector selector: resourceNode.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } for (Selector selector: resourceNode.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); @@ -422,19 +475,19 @@ // Add an invocation to the accessor method. if (mainInput != null) { - List args = new ArrayList<>(); - for (Selector selector: resourceNode.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - args.add(var.getName()); - } + Expression resExp = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(out.getResource(), null); + if (resExp instanceof Term) { + // remove '.getValue()' + resExp = ((Term) resExp).getChild(0); } + String resourceAccess = resExp.toImplementation(new String[] {null}); + List args = new ArrayList<>(); if (message instanceof Term) { for (Variable var: message.getVariables().values()) { args.add(var.getName()); } } - mainInput.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(resName), input.getName(), args) + langSpec.getStatementDelimiter()); + mainInput.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { @@ -444,7 +497,8 @@ Expression updateExp; updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getPullAccessor()); String newState = updateExp.toImplementation(sideEffects); - if (resourceNode.getResourceHierarchy().getNumParameters() == 0) { + ResourceHierarchy resource = resourceNode.getResourceHierarchy(); + if (resource.getNumParameters() == 0 || resource.getChildren() == null || resource.getChildren().size() == 0) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java index 8f8da21..fedcbba 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -20,6 +20,8 @@ MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters); FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName); FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer); + Type newListType(String compTypeName); + Type newMapType(Type keyType, String compTypeName); Type newTupleType(List compTypes); String getVariableDeclaration(String typeName, String varName); String getFieldInitializer(Type type, Expression initialValue); @@ -31,6 +33,7 @@ String getConstructorInvocation(String componentName, List parameters); String getReturnStatement(String returnValue); String toComponentName(String name); + String toVariableName(String name); String getMainComponentName(); String getTupleGet(String tupleExp, int idx, int length); String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index b39db0e..16510b7 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -5,7 +5,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; - +import java.util.Stack; import code.ast.Block; import code.ast.CompilationUnit; @@ -26,6 +26,7 @@ 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.DataTransferModel; @@ -62,6 +63,44 @@ JavaCodeGenerator.mainTypeName = defaultMainTypeName; } + public static String getComponentName(ResourceHierarchy res) { + 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 name.substring(0, 1).toUpperCase() + name.substring(1); + } + + public static Type getImplStateType(ResourceHierarchy res) { + Set 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. + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); ArrayList resources = determineResourceOrder(graph); @@ -75,11 +114,14 @@ MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); mainType.addMethod(mainConstructor); - // For each resource path. + // For each resource node. for (ResourceNode rn: resources) { + if (rn.getResourceHierarchy().getChildren().size() == 0 && rn.getResourceHierarchy().getNumParameters() == 0) { + // No component is created for a simple leaf resource. + continue; + } boolean f = false; - String resourceName = rn.getResourceName().substring(0, 1).toUpperCase() - + rn.getResourceName().substring(1); + String resourceName = getComponentName(rn.getResourceHierarchy()); TypeDeclaration type = null; for (CompilationUnit cu: codes) { for (TypeDeclaration t: cu.types()) { @@ -98,63 +140,66 @@ codes.add(cu); // Declare the field to refer to each resource in the main type. - String fieldInitializer = "new " + resourceName + "("; Set depends = new HashSet<>(); - for (Edge resToCh: rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) resToCh; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - for (Edge chToRes: re.getDestination().getOutEdges()) { - ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); - String resName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); - depends.add(dstRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } - } - } - for (Edge chToRes : rn.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { - DataFlowEdge re = (DataFlowEdge) resToCh; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); - String resName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - depends.add(srcRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } else { - if (re.getDestination().getIndegree() > 1) { - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); - type.addField(new FieldDeclaration( - cashRes.getResourceStateType(), ((ResourceNode) re.getSource()).getOutSideResource().getResourceName(), getInitializer(cashRes))); - } - } - } - } Set refs = new HashSet<>(); - for (Channel ch : model.getChannels()) { - DataTransferChannel c = (DataTransferChannel) ch; - if (c.getInputResources().contains(rn.getOutSideResource())) { - for (ResourcePath res: c.getReferenceResources()) { - if (!refs.contains(res) && !depends.contains(res)) { - refs.add(res); - String refResName = res.getResourceName(); - fieldInitializer += refResName.toLowerCase() + ","; + if (rn.getResourceHierarchy().getParent() == null) { + // For a root resource + String fieldInitializer = "new " + resourceName + "("; + for (Edge resToCh: rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + String resName = getComponentName(dstRes.getResourceHierarchy()); + depends.add(dstRes); + fieldInitializer += resName.toLowerCase() + ","; f = true; } } } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + String resName = getComponentName(srcRes.getResourceHierarchy()); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + depends.add(srcRes); + fieldInitializer += resName.toLowerCase() + ","; + f = true; + } else { + if (re.getDestination().getIndegree() > 1) { + // Declare a field to cash the state of the source resource in the type of the destination resource. + ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); + type.addField(new FieldDeclaration( + cashRes.getResourceStateType(), ((ResourceNode) re.getSource()).getOutSideResource().getResourceName(), getInitializer(cashRes))); + } + } + } + } + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(rn.getOutSideResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res)) { + refs.add(res); + String refResName = res.getResourceName(); + fieldInitializer += refResName.toLowerCase() + ","; + f = true; + } + } + } + } + if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); + fieldInitializer += ")"; + FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName()); + mainType.addField(field); + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(rn.getResourceName() + " = " + fieldInitializer + ";"); } - if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); - fieldInitializer += ")"; - FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName()); - mainType.addField(field); - Block mainConstructorBody = mainConstructor.getBody(); - if (mainConstructorBody == null) { - mainConstructorBody = new Block(); - mainConstructor.setBody(mainConstructorBody); - } - mainConstructorBody.addStatement(rn.getResourceName() + " = " + fieldInitializer + ";"); // Declare a constructor, fields and update methods in the type of each resource. MethodDeclaration constructor = new MethodDeclaration(resourceName, true); @@ -166,7 +211,7 @@ // Declare a field to refer to the destination resource of push transfer. for (Edge chToRes: re.getDestination().getOutEdges()) { ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); - String dstResName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); + String dstResName = getComponentName(dstRes.getResourceHierarchy()); depends.add(dstRes); type.addField(new FieldDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); @@ -178,7 +223,7 @@ for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); - String srcResName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); + String srcResName = getComponentName(srcRes.getResourceHierarchy()); if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { // Declare a field to refer to the source resource of pull transfer. depends.add(srcRes); @@ -207,8 +252,7 @@ for (ResourcePath res: c.getReferenceResources()) { if (!refs.contains(res) && !depends.contains(res)) { refs.add(res); - String refResName = res.getResourceName(); - refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); + String refResName = getComponentName(res.getResourceHierarchy()); type.addField(new FieldDeclaration(new Type(refResName, refResName), res.getResourceName())); constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), res.getResourceName())); block.addStatement("this." + res.getResourceName() + " = " + res.getResourceName() + ";"); @@ -230,17 +274,16 @@ // In each resource. ArrayList resParams = new ArrayList<>(); ArrayList mainParams = new ArrayList<>(); - for (Selector selector: rn.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } + int v = 1; for (Selector selector: rn.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } for (Variable var: message.getVariables().values()) { resParams.add(new VariableDeclaration(var.getType(), var.getName())); @@ -273,17 +316,16 @@ // In each resource. ArrayList resParams = new ArrayList<>(); ArrayList mainParams = new ArrayList<>(); - for (Selector selector: rn.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } + int v = 1; for (Selector selector: rn.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } MethodDeclaration input = null; if (resParams.size() > 0) { @@ -314,58 +356,100 @@ } // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored() && rn.getNumberOfParameters() == 0) { + if (((StoreAttribute) rn.getAttribute()).isStored()) { ResourcePath res = rn.getOutSideResource(); - type.addField(new FieldDeclaration(res.getResourceStateType(), "value", getInitializer(res))); - } - - // Declare the getter methods to obtain the state in the resource and the main type. - List resParams = new ArrayList<>(); - List mainParams = new ArrayList<>(); - for (Selector pathParam: rn.getSelectors()) { - if (pathParam.getExpression() instanceof Variable) { - Variable var = (Variable) pathParam.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + type.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + type.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if ((c.getChildren() == null || c.getChildren().size() == 0) && c.getNumParameters() == 0) { + // The child does not have a component. + childType = c.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1); + type.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } + } } } + + // Declare the getter method to obtain the state in the resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); + type.addMethod(stateGetter); + + // Declare the getter methods to obtain a child of the resource. + for (ResourceNode child: rn.getChildren()) { + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: child.getSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childTypeName = getComponentName(child.getResourceHierarchy()); + Type childType = null; + if ((child.getResourceHierarchy().getChildren() == null || child.getResourceHierarchy().getChildren().size() == 0) + && child.getResourceHierarchy().getNumParameters() == 0) { + // The child does not have a component. + childType = child.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + MethodDeclaration childGetter = null; + if (pathParams.size() == 0) { + childGetter = new MethodDeclaration("get" + childTypeName, childType); + } else { + childGetter = new MethodDeclaration("get" + childTypeName, false, childType, pathParams); + } + type.addMethod(childGetter); + } + + // Declare the getter accessor to obtain the resource state in the main type. + MethodDeclaration accessor = null; + List mainParams = new ArrayList<>(); + int v = 1; for (Selector pathParam: rn.getAllSelectors()) { if (pathParam.getExpression() instanceof Variable) { Variable var = (Variable) pathParam.getExpression(); mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + mainParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } - if (resParams.size() > 0) { - type.addMethod(new MethodDeclaration("getValue", false, rn.getResourceStateType(), resParams)); - } else { - type.addMethod(new MethodDeclaration("getValue", rn.getResourceStateType())); - } - - // In the main type. - MethodDeclaration getter = null; if (mainParams.size() > 0) { - getter = new MethodDeclaration("get" + rn.getResourceName().substring(0, 1).toUpperCase() - + rn.getResourceName().substring(1), - false, - rn.getResourceStateType(), - mainParams); + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + false, + getImplStateType(rn.getResourceHierarchy()), + mainParams); } else { - getter = new MethodDeclaration("get" + rn.getResourceName().substring(0, 1).toUpperCase() - + rn.getResourceName().substring(1), - rn.getResourceStateType()); + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + getImplStateType(rn.getResourceHierarchy())); } - getter.setBody(new Block()); - if (mainParams.size() > 0) { - String sParams = ""; - String delimiter = ""; - for (VariableDeclaration var: mainParams) { - sParams += delimiter + var.getName(); - delimiter = ", "; - } - getter.getBody().addStatement("return " + rn.getResourceName() + ".getValue(" + sParams + ");"); - } else { - getter.getBody().addStatement("return " + rn.getResourceName() + ".getValue();"); - } - mainType.addMethod(getter); + accessor.setBody(new Block()); + Expression getState = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rn.getOutSideResource(), null); + accessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + mainType.addMethod(accessor); } // Declare the Pair class. @@ -566,15 +650,63 @@ static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); + if (from != null) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = target; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null) { + // root resource + String fieldName = typeName.substring(0, 1).toLowerCase() + typeName.substring(1); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + Term newGetter = new Term(new Symbol("get" + 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; + } + } + + if (target.getResourceHierarchy().getNumParameters() > 0 + || (target.getResourceHierarchy().getChildren() != null && target.getResourceHierarchy().getChildren().size() > 0)) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; } - // for reference channel member - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; } @Override diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 9087e2e..d44abfa 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -19,6 +19,7 @@ import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -48,7 +49,7 @@ Map typeMap = new HashMap<>(); for (CompilationUnit code: codes) { for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + typeMap.put(type.getTypeName(), type); } } @@ -62,8 +63,8 @@ ResourceNode src = (ResourceNode) resToCh.getSource(); for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); - String srcResourceName = src.getResourceName(); - String dstResourceName = dst.getResourceName(); + String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); TypeDeclaration srcType = typeMap.get(srcResourceName); TypeDeclaration dstType = typeMap.get(dstResourceName); for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { @@ -95,7 +96,7 @@ } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the current state stored in a field. - MethodDeclaration getter = getGetterMethod(dstType, null); + MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { Type resourceType = dst.getResourceStateType(); if (dst.getResourceHierarchy().getNumParameters() == 0) { @@ -104,15 +105,6 @@ } else { // copy the current state to be returned as a 'value' String implTypeName = resourceType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -//// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<>"; -// } else { -// concreteTypeName = implTypeName; -// } getter.addStatement("return new " + implTypeName + "(value);"); } } else { @@ -184,7 +176,7 @@ } else { // for pull (or push/pull) data transfer if (dst.getNumberOfParameters() == 0) { - MethodDeclaration getter = getGetterMethod(dstType, null); + MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { boolean isContainedPush = false; HashMap inputResourceToStateAccessor = new HashMap<>(); @@ -223,51 +215,96 @@ } } // for source nodes - String mainTypeName = JavaCodeGenerator.mainTypeName.substring(0,1).toLowerCase() + JavaCodeGenerator.mainTypeName.substring(1); - TypeDeclaration mainType = typeMap.get(mainTypeName); + TypeDeclaration mainType = typeMap.get(JavaCodeGenerator.mainTypeName); for (ResourceHierarchy resource: model.getResourceHierarchies()) { // ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResourceName(); + String resourceName = JavaCodeGenerator.getComponentName(resource); TypeDeclaration type = typeMap.get(resourceName); if (type != null) { - // getter method - if (resource.getNumParameters() == 0) { - MethodDeclaration getter = getGetterMethod(type, null); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = resource.getResourceStateType(); - if (model.isPrimitiveType(resourceType)) { - getter.addStatement("return value;"); - } else { - // copy the current state to be returned as a 'value' + // state getter method + Type resourceType = JavaCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getGetterMethod(type, resourceType); + if (stateGetter != null && (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0)) { + if (model.isPrimitiveType(resourceType)) { + // primitive type + stateGetter.addStatement("return value;"); + } else { + if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) { + // list or map String implTypeName = resourceType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<" + generics + ">"; -// } else { -// concreteTypeName = implTypeName; -// } - getter.addStatement("return new " + implTypeName + "(value);"); - } - } - } else { - MethodDeclaration getter = getGetterMethod(type, resource.getNumParameters()); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.get); - selector.addChild(new Field("value")); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); - } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.lookup); - selector.addChild(new Field("value")); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + // copy the current state to be returned as a 'value' + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + if (resource.getChildren() == null || resource.getChildren().size() == 0) { + // a leaf resource + String implTypeName = resourceType.getImplementationTypeName(); + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + Term composer = null; + Term composerSub = new Constant(DataConstraintModel.nil); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resource.getChildren()) { + String childTypeName = JavaCodeGenerator.getComponentName(child); + String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Field(fieldName, JavaCodeGenerator.getImplStateType(child)); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JavaCodeGenerator.getImplStateType(child))); + } + 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]); + stateGetter.addStatement("return " + returnValue+ ";"); + } } } } + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JavaCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(type, methodName); + if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) { + if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue+ ";"); + } else { + String fieldName = JavaCodeGenerator.getComponentName(child); + String returnValue = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + // methods for input events Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { @@ -279,7 +316,7 @@ String[] sideEffects = new String[] {""}; Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); String newState = updateExp.toImplementation(sideEffects); - if (resource.getNumParameters() == 0) { + if (resource.getNumParameters() == 0 || resource.getChildren() == null || resource.getChildren().size() == 0) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; @@ -320,17 +357,16 @@ // In the main type if (mainType != null) { - MethodDeclaration mainInput = getMethod(mainType, input.getName()); - if (mainInput != null) { + MethodDeclaration inputAccessor = getMethod(mainType, input.getName()); + if (inputAccessor != null) { + Expression resExp = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(out.getResource(), null); + if (resExp instanceof Term) { + // remove '.getValue()' + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); String args = ""; String delimiter = ""; - for (Selector selector: entry.getKey().getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - args += delimiter + var.getName(); - delimiter = ", "; - } - } if (out.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) out.getStateTransition().getMessageExpression(); for (Variable var: message.getVariables().values()) { @@ -338,7 +374,7 @@ delimiter = ", "; } } - mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); } } } @@ -369,31 +405,11 @@ } return updates; } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type, int numParams) { + + private static MethodDeclaration getGetterMethod(TypeDeclaration type, Type returnType) { for (MethodDeclaration m: type.getMethods()) { if (m.getName().startsWith("get")) { - if (m.getParameters() == null && numParams == 0) return m; - if (m.getParameters() != null && m.getParameters().size() == numParams) return m; - } - } - return null; - } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type, List params) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) { - 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; - } + if (m.getReturnType().getInterfaceTypeName().equals(returnType.getInterfaceTypeName())) return m; } } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java index 5fbab32..0aa7955 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -64,6 +64,16 @@ } @Override + public Type newListType(String compTypeName) { + return new Type("List", "ArrayList<>", "List<" + compTypeName + ">", DataConstraintModel.typeList); + } + + @Override + public Type newMapType(Type keyType, String valueTypeName) { + return new Type("Map", "HashMap<>", "Map<" + keyType.getImplementationTypeName() + ", " + valueTypeName + ">", DataConstraintModel.typeMap); + } + + @Override public Type newTupleType(List componentTypes) { String implTypeName = "AbstractMap.SimpleEntry<>"; String interfaceTypeName = "Map.Entry<$x>"; @@ -169,6 +179,11 @@ } @Override + public String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + @Override public String getMainComponentName() { return "Main"; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index 0cfbbce..3a6fea5 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -2,8 +2,12 @@ import java.util.ArrayList; 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.Annotation; import code.ast.Block; @@ -25,6 +29,7 @@ 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.DataTransferModel; @@ -62,97 +67,144 @@ JerseyCodeGenerator.mainTypeName = defaultMainTypeName; } + public static String getComponentName(ResourceHierarchy res) { + 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 name.substring(0, 1).toUpperCase() + name.substring(1); + } + + public static Type getImplStateType(ResourceHierarchy res) { + Set 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. + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); // ArrayList resources = StoreResourceCheck(graph); Collection resources = graph.getResourceNodes(); + Map resourceTypes = new HashMap<>(); + Map getterAccessors = new HashMap<>(); + Map inputAccessors = new HashMap<>(); + // For each resource node. for (Node n : resources) { ResourceNode rn = (ResourceNode) n; - String resourceName = rn.getResourceName().substring(0, 1).toUpperCase() - + rn.getResourceName().substring(1); - - TypeDeclaration type = null; - for (CompilationUnit cu: codes) { - for (TypeDeclaration t: cu.types()) { - if (resourceName.equals(t.getTypeName())) { - type = t; - break; - } - } - if (type != null) break; + if (rn.getResourceHierarchy().getChildren().size() == 0 && rn.getResourceHierarchy().getNumParameters() == 0) { + // Don't create new component for a simple leaf resource. + continue; } + String resourceName = getComponentName(rn.getResourceHierarchy()); + + TypeDeclaration type = resourceTypes.get(rn.getResourceHierarchy()); if (type == null) { // Add compilation unit for each resource. type = new TypeDeclaration(resourceName); - type.addAnnotation(new Annotation("Component")); - type.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\"")); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root node. + type.addAnnotation(new Annotation("Component")); + type.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\"")); + } + resourceTypes.put(rn.getResourceHierarchy(), type); CompilationUnit cu = new CompilationUnit(type); cu.addImport(new ImportDeclaration("java.util.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); - cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); - cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); - cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root node. + cu.addImport(new ImportDeclaration("javax.ws.rs.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + } codes.add(cu); } // Declare a client field and update methods from other resources. - boolean bDeclareClientField = false; - for (Edge resToCh: rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) resToCh; - if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - // Declare a client field to connect to the destination resource of push transfer. - type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); - bDeclareClientField = true; - } - } - for (Edge chToRes : rn.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { + if (rn.getResourceHierarchy().getParent() == null) { + // For a root resource + boolean bDeclareClientField = false; + for (Edge resToCh: rn.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); - String srcResName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - if (!bDeclareClientField) { - // Declare a client field to connect to the source resource of pull transfer. - type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); - bDeclareClientField = true; - } - } else { - // Declare an update method in the type of the destination resource. - ArrayList vars = new ArrayList<>(); - String srcName = srcRes.getResourceName(); - Type srcType = srcRes.getResourceStateType(); - VariableDeclaration param = new VariableDeclaration(srcType, srcName); - param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); - vars.add(param); - for (ResourcePath refRes: ((ChannelNode) re.getDestination()).getChannel().getReferenceResources()) { - if (!refRes.equals(rn.getOutSideResource())) { - param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName()); - param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\"")); - vars.add(param); + if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // Declare a client field to connect to the destination resource of push transfer. + type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; } - } - MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); - for (ChannelMember cm: ((ChannelNode) re.getDestination()).getChannel().getOutputChannelMembers()) { - if (rn.getInSideResources().contains(cm.getResource())) { - if (cm.getStateTransition().isRightUnary()) { - update.addAnnotation(new Annotation("PUT")); - } else { - update.addAnnotation(new Annotation("POST")); + } else { + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + String srcName = srcRes.getResourceName(); + Type srcType = srcRes.getResourceStateType(); + VariableDeclaration param = new VariableDeclaration(srcType, srcName); + param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + vars.add(param); + for (ResourcePath refRes: ((ChannelNode) re.getDestination()).getChannel().getReferenceResources()) { + if (!refRes.equals(rn.getOutSideResource())) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName()); + param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\"")); + vars.add(param); } } + MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); + for (ChannelMember cm: ((ChannelNode) re.getDestination()).getChannel().getOutputChannelMembers()) { + if (rn.getInSideResources().contains(cm.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + update.addAnnotation(new Annotation("PUT")); + } else { + update.addAnnotation(new Annotation("POST")); + } + } + } + if (re.getDestination().getIndegree() > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); + // Declare a field to cash the state of the source resource in the type of the destination resource. + ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); + type.addField(new FieldDeclaration(cashRes.getResourceStateType(), srcName, getInitializer(cashRes))); + } + type.addMethod(update); } - if (re.getDestination().getIndegree() > 1) { - // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. - update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); - type.addField(new FieldDeclaration(cashRes.getResourceStateType(), srcName, getInitializer(cashRes))); - } - type.addMethod(update); } } } @@ -181,107 +233,242 @@ if (rn.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { - ArrayList params = new ArrayList<>(); + // In each resource. + ArrayList rootParams = new ArrayList<>(); String resourcePath = "\""; - for (Selector selector: rn.getSelectors()) { + int v = 1; + for (Selector selector: rn.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); - params.add(param); + rootParams.add(param); + resourcePath += "/{" + paramName + "}"; + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootParams.add(param); resourcePath += "/{" + paramName + "}"; } + v++; } resourcePath += "\""; + ArrayList resParams = new ArrayList<>(); for (Variable var: message.getVariables().values()) { String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); - params.add(param); + rootParams.add(param); } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); + + if (rn.getResourceHierarchy().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), + false, typeVoid, resParams); + type.addMethod(input); + } + + // For the root resource. + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootParams); if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + inputAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + inputAccessor.addAnnotation(new Annotation("POST")); } if (ch.getAllSelectors().size() > 0) { - input.addAnnotation(new Annotation("Path", resourcePath)); + inputAccessor.addAnnotation(new Annotation("Path", resourcePath)); } - type.addMethod(input); + inputAccessors.put(rn.getResourceHierarchy(), inputAccessor); } else if (message instanceof Variable) { - ArrayList params = new ArrayList<>(); + // In each resource. + if (rn.getResourceHierarchy().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Variable) cm.getStateTransition().getMessageExpression()).getName(), + false, typeVoid, null); + type.addMethod(input); + } + + // For the root resource. + ArrayList rootParams = new ArrayList<>(); String resourcePath = "\""; - for (Selector selector: rn.getSelectors()) { + int v = 1; + for (Selector selector: rn.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); - params.add(param); + rootParams.add(param); + resourcePath += "/{" + paramName + "}"; + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootParams.add(param); resourcePath += "/{" + paramName + "}"; } + v++; } resourcePath += "\""; - MethodDeclaration input = null; - if (params.size() > 0) { - input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, params); - } else { - input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - } + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootParams); if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + inputAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + inputAccessor.addAnnotation(new Annotation("POST")); } if (ch.getAllSelectors().size() > 0) { - input.addAnnotation(new Annotation("Path", resourcePath)); + inputAccessor.addAnnotation(new Annotation("Path", resourcePath)); } - type.addMethod(input); + inputAccessors.put(rn.getResourceHierarchy(), inputAccessor); } } } } // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored() && rn.getNumberOfParameters() == 0) { + if (((StoreAttribute) rn.getAttribute()).isStored()) { ResourcePath res = rn.getOutSideResource(); - type.addField(new FieldDeclaration(res.getResourceStateType(), "value", getInitializer(res))); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + type.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + type.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if ((c.getChildren() == null || c.getChildren().size() == 0) && c.getNumParameters() == 0) { + // The child does not have a component. + childType = c.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1); + type.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } + } + } } // Declare the getter method to obtain the state in the type of each resource. - ArrayList params = new ArrayList<>(); - String resourcePath = "\""; - for (Selector selector: rn.getSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - String paramName = var.getName(); - VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); - param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); - params.add(param); - resourcePath += "/{" + paramName + "}"; + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); + if (rn.getResourceHierarchy().getParent() == null) { + // Since this getter is also an accessor. + stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + stateGetter.addAnnotation(new Annotation("GET")); + } + type.addMethod(stateGetter); + + // Declare the getter methods to obtain a child of the resource. + for (ResourceNode child: rn.getChildren()) { + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: child.getSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; } - } - resourcePath += "\""; - MethodDeclaration getter = null; - if (params.size() > 0) { - getter = new MethodDeclaration("getValue", false, rn.getResourceStateType(), params); - } else { - getter = new MethodDeclaration("getValue", rn.getResourceStateType()); + String childTypeName = getComponentName(child.getResourceHierarchy()); + Type childType = null; + if ((child.getResourceHierarchy().getChildren() == null || child.getResourceHierarchy().getChildren().size() == 0) + && child.getResourceHierarchy().getNumParameters() == 0) { + // The child does not have a component. + childType = child.getResourceStateType(); + } else { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + } + MethodDeclaration childGetter = null; + if (pathParams.size() == 0) { + childGetter = new MethodDeclaration("get" + childTypeName, childType); + } else { + childGetter = new MethodDeclaration("get" + childTypeName, false, childType, pathParams); + } + type.addMethod(childGetter); } - getter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); - getter.addAnnotation(new Annotation("GET")); - if (rn.getSelectors().size() > 0) { - getter.addAnnotation(new Annotation("Path", resourcePath)); + + // Declare the getter accessor to obtain the resource state in the root resource. + if (rn.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List params = new ArrayList<>(); + String resourcePath = "\""; + int v = 1; + for (Selector pathParam: rn.getAllSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + String paramName = var.getName(); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + params.add(param); + resourcePath += "/{" + paramName + "}"; + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + String paramName = "v" + v; + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + params.add(param); + resourcePath += "/{" + paramName + "}"; + } + v++; + } + resourcePath += "\""; + if (params.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + false, + getImplStateType(rn.getResourceHierarchy()), + params); + } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + getImplStateType(rn.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + Expression getState = JerseyCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rn.getOutSideResource(), rn.getOutSideResource().getRoot()); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + + getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getterAccessor.addAnnotation(new Annotation("GET")); + if (rn.getSelectors().size() > 0) { + getterAccessor.addAnnotation(new Annotation("Path", resourcePath)); + } + getterAccessors.put(rn.getResourceHierarchy(), getterAccessor); } - type.addMethod(getter); + } + + for (ResourceHierarchy rootRes: model.getResourceHierarchies()) { + if (rootRes.getParent() == null) { + // root resource + TypeDeclaration rootType = resourceTypes.get(rootRes); + // Add getter accessors. + for (ResourceHierarchy res: getterAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootType.addMethod(getterAccessors.get(res)); + } + } + // Add inout accessors. + for (ResourceHierarchy res: inputAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootType.addMethod(inputAccessors.get(res)); + } + } + } } // Declare the Pair class. @@ -410,15 +597,64 @@ static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", + if (from != null && !from.getResourceHierarchy().isAncestorOf(target.getResourceHierarchy())) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); + } else { + // access from an ancestor or outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = target; + do { + if (from != null && curPath.equals(from)) break; + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the `from' resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null && from == null) { + // root resource + String fieldName = typeName.substring(0, 1).toLowerCase() + typeName.substring(1); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + Term newGetter = new Term(new Symbol("get" + 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; + } + } + + if (target.getResourceHierarchy().getNumParameters() > 0 + || (target.getResourceHierarchy().getChildren() != null && target.getResourceHierarchy().getChildren().size() > 0)) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; } - // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); } @Override diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index 37d6fb6..11c07fa 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -21,6 +21,7 @@ import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -51,7 +52,7 @@ Map typeMap = new HashMap<>(); for (CompilationUnit code: codes) { for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + typeMap.put(type.getTypeName(), type); } } @@ -66,8 +67,8 @@ ResourceNode src = (ResourceNode) resToCh.getSource(); for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); - String srcResourceName = src.getResourceName(); - String dstResourceName = dst.getResourceName(); + String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); TypeDeclaration srcType = typeMap.get(srcResourceName); TypeDeclaration dstType = typeMap.get(dstResourceName); for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { @@ -180,7 +181,7 @@ } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the state stored in a field. - MethodDeclaration getter = getGetterMethod(dstType, null); + MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { if (dst.getResourceHierarchy().getNumParameters() == 0) { getter.addStatement("return value;"); @@ -295,7 +296,7 @@ } else { // for pull (or push/pull) data transfer if (dst.getNumberOfParameters() == 0) { - MethodDeclaration getter = getGetterMethod(dstType, null); + MethodDeclaration getter = getGetterMethod(dstType, dst.getResourceStateType()); if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { // generate a return statement. String[] sideEffects = new String[] {""}; @@ -320,31 +321,93 @@ } // for source nodes for (ResourceHierarchy resource: model.getResourceHierarchies()) { - String resourceName = resource.getResourceName(); + String resourceName = JerseyCodeGenerator.getComponentName(resource); TypeDeclaration type = typeMap.get(resourceName); if (type != null) { - // getter method - if (resource.getNumParameters() == 0) { - MethodDeclaration getter = getGetterMethod(type, null); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - getter.addStatement("return value;"); - } - } else { - MethodDeclaration getter = getGetterMethod(type, resource.getNumParameters()); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.get); - selector.addChild(new Field("value")); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); - } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.lookup); - selector.addChild(new Field("value")); - selector.addChild(new Variable(getter.getParameters().get(getter.getParameters().size() - 1).getName())); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + // state getter method + Type resourceType = JerseyCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getGetterMethod(type, resourceType); + if (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0) { + if (model.isPrimitiveType(resourceType)) { + // primitive type + stateGetter.addStatement("return value;"); + } else { + if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) { + // list or map + String implTypeName = resourceType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + if (resource.getChildren() == null || resource.getChildren().size() == 0) { + // a leaf resource + String implTypeName = resourceType.getImplementationTypeName(); + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + Term composer = null; + Term composerSub = new Constant(DataConstraintModel.nil); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resource.getChildren()) { + String childTypeName = JerseyCodeGenerator.getComponentName(child); + String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Field(fieldName, JerseyCodeGenerator.getImplStateType(child)); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JerseyCodeGenerator.getImplStateType(child))); + } + 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]); + stateGetter.addStatement("return " + returnValue+ ";"); + } } } } + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JerseyCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(type, methodName); + if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) { + if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue+ ";"); + } else { + String fieldName = JerseyCodeGenerator.getComponentName(child); + String returnValue = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + // methods for input events Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { @@ -352,10 +415,11 @@ for (ChannelMember out: outs) { MethodDeclaration input = getInputMethod(type, out); if (input != null) { + // In each resource Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); - if (resource.getNumParameters() == 0) { + if (resource.getNumParameters() == 0 || resource.getChildren() == null || resource.getChildren().size() == 0) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; @@ -393,6 +457,29 @@ } } } + + if (out.getResource().getParent() != null) { + // In the root resource + MethodDeclaration inputAccessor = getMethod(typeMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), input.getName()); + if (inputAccessor != null) { + Expression resExp = JerseyCodeGenerator.pullAccessor.getCurrentStateAccessorFor(out.getResource(), out.getResource().getRoot()); + if (resExp instanceof Term) { + // remove '.getValue()' + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); + String args = ""; + String delimiter = ""; + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + for (Variable var: message.getVariables().values()) { + args += delimiter + var.getName(); + delimiter = ", "; + } + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); + } + } } } } @@ -594,6 +681,15 @@ return null; } + private static MethodDeclaration getGetterMethod(TypeDeclaration type, Type returnType) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().startsWith("get")) { + if (m.getReturnType().getInterfaceTypeName().equals(returnType.getInterfaceTypeName())) return m; + } + } + return null; + } + private static List getUpdateMethods(TypeDeclaration type) { List updates = new ArrayList<>(); for (MethodDeclaration m: type.getMethods()) { @@ -604,35 +700,6 @@ return updates; } - private static MethodDeclaration getGetterMethod(TypeDeclaration type, int numParams) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) { - if (m.getParameters() == null && numParams == 0) return m; - if (m.getParameters() != null && m.getParameters().size() == numParams) return m; - } - } - return null; - } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type, List params) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) { - 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; - } - private static Map> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) { Map> ioChannelsAndMembers = new HashMap<>(); for (Channel c: model.getIOChannels()) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index a16fac5..81b24ed 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -327,7 +327,12 @@ } if (getArity() >= 1 && symbol.isImplMethod()) { if (implParamOrder == null) { - String exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + String exp = null; + if (children.get(0) != null) { + exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + } else { + exp = symbol.toImplementation() + "("; + } String delimiter = ""; for (int i = 1; i < children.size(); i++) { Expression e = children.get(i); @@ -337,7 +342,11 @@ exp += ")"; if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - exp = children.get(0).toImplementation(new String[] {""}); + if (children.get(0) != null) { + exp = children.get(0).toImplementation(new String[] {""}); + } else { + exp = ""; + } } return exp; } else { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 5c8d782..e685078 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -34,7 +34,7 @@ public static final Type typePairInt = new Type("Pair", "Pair", "Pair", typePair); public static final Type typePairStr = new Type("Pair", "Pair", "Pair", typePair); public static final Type typePairDouble = new Type("Pair", "Pair", "Pair", typePair); - public static final Type typeMap = new Type("Map", "HashMap", "Map"); + public static final Type typeMap = new Type("Map", "HashMap<>", "Map"); public static final JsonType typeJson = new JsonType("Json", "HashMap<>", "Map"); public static final Symbol add = new Symbol(Parser.ADD, 2, Symbol.Type.INFIX); public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX); diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java index a033e28..e591e07 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -29,13 +29,7 @@ } else if (getChild(1) instanceof Variable) { valueType = ((Variable) getChild(1)).getType(); } - if (valueType != null) { - if (valueType.equals(DataConstraintModel.typeInt)) { - return DataConstraintModel.typeList; - } else if (valueType.equals(DataConstraintModel.typeString)) { - return DataConstraintModel.typeMap; - } - } + if (valueType != null) return valueType; } return super.getType(); } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java index f3a89ce..0994c0a 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -1,6 +1,8 @@ package models.dataConstraintModel; +import java.util.HashSet; import java.util.List; +import java.util.Set; import models.algebra.Expression; import models.algebra.Term; @@ -9,6 +11,7 @@ public class ResourceHierarchy { private ResourceHierarchy parent = null; + private Set children = null; private String resourceName = null; private Type resourceStateType = null; private int numParameters = 0; @@ -17,12 +20,14 @@ public ResourceHierarchy(String resourceName) { this.parent = null; + this.children = new HashSet<>(); this.resourceName = resourceName; this.numParameters = 0; } public ResourceHierarchy(String resourceName, Type resourceStateType) { this.parent = null; + this.children = new HashSet<>(); this.resourceName = resourceName; this.numParameters = 0; this.resourceStateType = resourceStateType; @@ -30,45 +35,61 @@ public ResourceHierarchy(ResourceHierarchy parent, String resourceName) { this.parent = parent; + this.children = new HashSet<>(); this.resourceName = resourceName; this.numParameters = 0; - if (parent != null && parent.getResourceStateType() == null) { - parent.setResourceStateType(DataConstraintModel.typeJson); + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + parent.setResourceStateType(DataConstraintModel.typeJson); + } } } public ResourceHierarchy(ResourceHierarchy parent, String resourceName, Type resourceStateType) { this.parent = parent; + this.children = new HashSet<>(); this.resourceName = resourceName; this.numParameters = 0; this.resourceStateType = resourceStateType; - if (parent != null && parent.getResourceStateType() == null) { - parent.setResourceStateType(DataConstraintModel.typeJson); + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + parent.setResourceStateType(DataConstraintModel.typeJson); + } } } public ResourceHierarchy(ResourceHierarchy parent, int numParameters) { this.parent = parent; + this.children = new HashSet<>(); this.resourceName = null; this.numParameters = numParameters; + if (parent != null) { + parent.addChild(this); + } } public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp) { this.parent = parent; + this.children = new HashSet<>(); this.resourceName = null; this.numParameters = 1; - if (parent != null && parent.getResourceStateType() == null) { - Type paramType = null; - if (parameterExp instanceof Variable) { - paramType = ((Variable) parameterExp).getType(); - } else if (parameterExp instanceof Term) { - paramType = ((Term) parameterExp).getType(); - } - if (paramType != null) { - if (paramType.equals(DataConstraintModel.typeInt)) { - parent.setResourceStateType(DataConstraintModel.typeList); - } else if (paramType.equals(DataConstraintModel.typeString)) { - parent.setResourceStateType(DataConstraintModel.typeMap); + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + Type paramType = null; + if (parameterExp instanceof Variable) { + paramType = ((Variable) parameterExp).getType(); + } else if (parameterExp instanceof Term) { + paramType = ((Term) parameterExp).getType(); + } + if (paramType != null) { + if (paramType.equals(DataConstraintModel.typeInt)) { + parent.setResourceStateType(DataConstraintModel.typeList); + } else if (paramType.equals(DataConstraintModel.typeString)) { + parent.setResourceStateType(DataConstraintModel.typeMap); + } } } } @@ -79,17 +100,20 @@ this.resourceName = null; this.numParameters = 1; this.resourceStateType = resourceStateType; - if (parent != null && parent.getResourceStateType() == null) { - Type paramType = null; - if (parameterExp instanceof Variable) { - paramType = ((Variable) parameterExp).getType(); - } else if (parameterExp instanceof Term) { - paramType = ((Term) parameterExp).getType(); - } - if (paramType.equals(DataConstraintModel.typeInt)) { - parent.setResourceStateType(DataConstraintModel.typeList); - } else if (paramType.equals(DataConstraintModel.typeString)) { - parent.setResourceStateType(DataConstraintModel.typeMap); + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + Type paramType = null; + if (parameterExp instanceof Variable) { + paramType = ((Variable) parameterExp).getType(); + } else if (parameterExp instanceof Term) { + paramType = ((Term) parameterExp).getType(); + } + if (paramType.equals(DataConstraintModel.typeInt)) { + parent.setResourceStateType(DataConstraintModel.typeList); + } else if (paramType.equals(DataConstraintModel.typeString)) { + parent.setResourceStateType(DataConstraintModel.typeMap); + } } } } @@ -99,6 +123,9 @@ this.resourceName = null; this.numParameters = numParameters; this.resourceStateType = resourceStateType; + if (parent != null) { + parent.addChild(this); + } } public ResourceHierarchy getParent() { @@ -107,6 +134,28 @@ public void setParent(ResourceHierarchy parent) { this.parent = parent; + if (parent != null) { + parent.addChild(this); + } + } + + public Set getChildren() { + return children; + } + + protected void addChild(ResourceHierarchy child) { + children.add(child); + } + + public ResourceHierarchy getRoot() { + if (parent == null) return this; + return parent.getRoot(); + } + + public boolean isAncestorOf(ResourceHierarchy another) { + if (another == null) return false; + if (this == another) return true; + return isAncestorOf(another.getParent()); } public String getResourceName() { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index 09ab11f..d7c2195 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -88,6 +88,11 @@ public ResourcePath getParent() { return parent; } + + public ResourcePath getRoot() { + if (parent == null) return this; + return parent.getRoot(); + } public ResourcePath getCommonPrefix(ResourcePath another) { Set ancestors = new HashSet<>();