diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model index c587fb7..07e5d80 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, {"off": off}) +} + channel CIO_SetCustomerOff(uid:Str) { out customers.{uid}.off(cid:Str, setOff(cid2)) == cid2 } @@ -7,7 +11,7 @@ } channel C(uid:Str) { - in customers.{uid}.off(cid, sync(cid2, a2)) == cid2 - in companies.{cid2}.add(a1, sync(cid2, a2)) == a2 - out customers.{uid}.add(a3:Str, sync(cid2, a2)) == a2 + in customers.{uid}.off(cid, sync(cid2, add2)) == cid2 + in companies.{cid2}.add(a1, sync(cid2, add2)) == add2 + out customers.{uid}.add(a3:Str, sync(cid2, add2)) == add2 } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index 6cfd4e7..69fe530 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -28,25 +28,26 @@ channels.addAll(model.getChannels()); for (Channel channel: channels) { for (ChannelMember member: ((DataTransferChannel) channel).getOutputChannelMembers()) { - boolean flag = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. + boolean toBeStored = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. for (Node node : graph.getResourceNodes()) { if (((ResourceNode) node).getInSideResources().contains(member.getResource())) { - setStoreAttribute((ResourceNode) node, flag); + setStoreAttribute((ResourceNode) node, toBeStored); } } } } - for (Node node : graph.getResourceNodes()) { + for (Node node: graph.getResourceNodes()) { HashSet inChannels = new HashSet<>(); - for(Edge pre : ((ResourceNode) node).getInEdges()) { - if (pre instanceof DataFlowEdge) { - DataFlowEdge dfEdge = (DataFlowEdge) pre; + for(Edge inEdge: ((ResourceNode) node).getInEdges()) { + if (inEdge instanceof DataFlowEdge) { + DataFlowEdge dfEdge = (DataFlowEdge) inEdge; if (dfEdge.isChannelToResource()) { inChannels.add(((ChannelNode) dfEdge.getSource()).getChannel()); } } } if ((inChannels.size() > 1)) { + // If the resource has multiple input channels, then the state of the resource needs to be stored. setStoreAttribute((ResourceNode) node, true); } else if (((ResourceNode) node).getAttribute() == null) { setStoreAttribute((ResourceNode) node, false); @@ -55,15 +56,15 @@ return graph; } - static private void setStoreAttribute(ResourceNode node, boolean flag) { + static private void setStoreAttribute(ResourceNode node, boolean toBeStored) { NodeAttribute attr = node.getAttribute(); StoreAttribute store; if (attr != null && attr instanceof NodeAttribute) { store = (StoreAttribute) attr; - store.setNeeded(store.isNeeded() || flag); + store.setNeeded(store.isNeeded() || toBeStored); } else { store = new StoreAttribute(); - store.setNeeded(flag); + store.setNeeded(toBeStored); node.setAttribute(store); } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index 4479246..21220f2 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -13,6 +13,7 @@ import javax.xml.crypto.Data; import models.Node; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Position; import models.algebra.Symbol; @@ -22,8 +23,10 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ResourceNode; @@ -43,6 +46,8 @@ static private Map pairComponentTypes = new HashMap<>(); static private Map, Type> mapTypes = new HashMap<>(); static private Map> mapComponentTypes = new HashMap<>(); + static private Map, Type> jsonTypes = new HashMap<>(); + static private Map> jsonMemberTypes = new HashMap<>(); public static Type getListType(Type compType) { return listTypes.get(compType); @@ -84,29 +89,46 @@ return mapComponentTypes.get(mapType); } + public static Type getJsonType(Map memberTypes) { + return jsonTypes.get(memberTypes); + } + + public static Map getJsonMemberTypes(Type jsonType) { + return jsonMemberTypes.get(jsonType); + } + static public void infer(DataTransferModel model) { Map> resources = new HashMap<>(); + Map> resourcePathParams = new HashMap<>(); Map variables = new HashMap<>(); Map, Type>>> messages = new HashMap<>(); Map consOrSet = new HashMap<>(); Map tuple = new HashMap<>(); Map pair = new HashMap<>(); Map map = new HashMap<>(); + Map json = new HashMap<>(); + + // Maps from the objectId of each expression to its belonging group that has the same type as the expression Map> expToResource = new HashMap<>(); + Map> expToPathParams = new HashMap<>(); Map> expToVariable = new HashMap<>(); Map> expToMessage = new HashMap<>(); Map>> expToConsOrSet = new HashMap<>(); Map>> expToTuple = new HashMap<>(); Map>> expToPair = new HashMap<>(); Map>> expToMap = new HashMap<>(); + Map>> expToJson = new HashMap<>(); + // Maps from the objectId of each group to the set of updated expressions. Map> updateFromResource = new HashMap<>(); + Set updateFromResourceOwnership = new HashSet<>(); Map> updateFromVariable = new HashMap<>(); Map> updateFromMessage = new HashMap<>(); Map> updateFromConsOrSet = new HashMap<>(); Map> updateFromTuple = new HashMap<>(); Map> updateFromPair = new HashMap<>(); Map> updateFromMap = new HashMap<>(); + Map> updateFromJson = new HashMap<>(); listComponentTypes.put(DataConstraintModel.typeList, null); listComponentTypes.put(DataConstraintModel.typeListInt, DataConstraintModel.typeInt); @@ -132,22 +154,24 @@ ResourceHierarchy res = cm.getResource().getResourceHierarchy(); // 1.1 Group expressions by resources. - List sameResource = resources.get(res); - if (sameResource == null) { - sameResource = new ArrayList<>(); - resources.put(res, sameResource); + List identicalResources = resources.get(res); + if (identicalResources == null) { + identicalResources = new ArrayList<>(); + resources.put(res, identicalResources); } - sameResource.add(st.getCurStateExpression()); - if (st.getNextStateExpression() != null) sameResource.add(st.getNextStateExpression()); - expToResource.put(System.identityHashCode(st.getCurStateExpression()), sameResource); - if (st.getNextStateExpression() != null) expToResource.put(System.identityHashCode(st.getNextStateExpression()), sameResource); - Map updatedExps = getUpdateSet(updateFromResource, sameResource); + identicalResources.add(st.getCurStateExpression()); + expToResource.put(System.identityHashCode(st.getCurStateExpression()), identicalResources); + if (st.getNextStateExpression() != null) { + identicalResources.add(st.getNextStateExpression()); + expToResource.put(System.identityHashCode(st.getNextStateExpression()), identicalResources); + } + Map updatedExps = getUpdateSet(updateFromResource, identicalResources); Type resType = res.getResourceStateType(); Expression exp = st.getCurStateExpression(); Type expType = getExpTypeIfUpdatable(resType, exp); if (expType != null) { res.setResourceStateType(expType); - for (Expression resExp : sameResource) { + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { ((Variable) resExp).setType(expType); @@ -175,7 +199,7 @@ expType = getExpTypeIfUpdatable(resType, exp); if (expType != null) { res.setResourceStateType(expType); - for (Expression resExp : sameResource) { + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { ((Variable) resExp).setType(expType); @@ -205,8 +229,22 @@ List allVariables = new ArrayList<>(); allVariables.addAll(st.getCurStateExpression().getVariables().values()); allVariables.addAll(st.getMessageExpression().getVariables().values()); - if (st.getNextStateExpression() != null) + if (st.getNextStateExpression() != null) { allVariables.addAll(st.getNextStateExpression().getVariables().values()); + } + for (Selector s: c.getSelectors()) { // add channel selectors + if (s.getExpression() instanceof Variable) { + allVariables.add((Variable) s.getExpression()); + } + } + ResourcePath resPath = cm.getResource(); + for (Expression param: resPath.getPathParams()) { // add path parameters + if (param instanceof Variable) { + allVariables.add((Variable) param); + } else if (param instanceof Term) { + allVariables.addAll(((Term) param).getVariables().values()); + } + } for (Variable var : allVariables) { List sameVariable = locals.get(var.getName()); if (sameVariable == null) { @@ -344,7 +382,7 @@ if (symbol.equals(DataConstraintModel.cons) || symbol.equals(DataConstraintModel.set)) { // If the root symbol of the term is cons or set. List consExps = new ArrayList<>(); - consExps.add(t); + consExps.add(t); // list term updateExpressionBelonging(expToConsOrSet, t, consExps); if (symbol.equals(DataConstraintModel.cons)) { // If the root symbol of the term is cons. @@ -355,10 +393,10 @@ } else { // If the root symbol of the term is set. Expression e = t.getChildren().get(2); - consExps.add(e); + consExps.add(e); // list component updateExpressionBelonging(expToConsOrSet, e, consExps); e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); } Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); @@ -411,9 +449,9 @@ // If the root symbol of the term is head or get. List consExps = new ArrayList<>(); Expression e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); - consExps.add(t); + consExps.add(t); // list's component updateExpressionBelonging(expToConsOrSet, t, consExps); consExps.add(null); Type listType = listTypes.get(t.getType()); @@ -447,11 +485,11 @@ } else if (symbol.equals(DataConstraintModel.tail)) { // If the root symbol of the term is tail. List consExps = new ArrayList<>(); - consExps.add(t); + consExps.add(t); // list term updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); + consExps.add(null); // list's component Expression e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); if (newType != null) { @@ -483,26 +521,27 @@ } else if (symbol.equals(DataConstraintModel.tuple)) { // If the root symbol of the term is tuple. List tupleExps = new ArrayList<>(); - List argsTypeList = new ArrayList<>(); - tupleExps.add(t); + List newArgTypesList = new ArrayList<>(); + tupleExps.add(t); // tuple term updateExpressionBelonging(expToTuple, t, tupleExps); for (Expression e : t.getChildren()) { - tupleExps.add(e); + tupleExps.add(e); // tuple's component updateExpressionBelonging(expToTuple, e, tupleExps); if (e instanceof Variable) { - argsTypeList.add(((Variable) e).getType()); + newArgTypesList.add(((Variable) e).getType()); } else if (e instanceof Term) { - argsTypeList.add(((Term) e).getType()); + newArgTypesList.add(((Term) e).getType()); } else { - argsTypeList.add(null); + newArgTypesList.add(null); } } if (t.getType() == DataConstraintModel.typeTuple) { - Type newTupleType = tupleTypes.get(argsTypeList); + Type newTupleType = tupleTypes.get(newArgTypesList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(argsTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newArgTypesList, DataConstraintModel.typeTuple); } + // Update the type of the tuple term and record the updated expression. t.setType(newTupleType); Map updateExps = getUpdateSet(updateFromTuple, tupleExps); updateExps.put(System.identityHashCode(t), t); @@ -511,22 +550,22 @@ } else if (symbol.equals(DataConstraintModel.pair)) { // If the root symbol of the term is pair. List pairExps = new ArrayList<>(); - pairExps.add(t); + pairExps.add(t); // pair updateExpressionBelonging(expToPair, t, pairExps); if (t.getType() == DataConstraintModel.typePair) { for (Expression e : t.getChildren()) { - pairExps.add(e); + pairExps.add(e); // left/right updateExpressionBelonging(expToPair, e, pairExps); - Type argType = null; + Type newArgType = null; if (e instanceof Variable) { - argType = (((Variable) e).getType()); + newArgType = (((Variable) e).getType()); } else if (e instanceof Term) { - argType = (((Term) e).getType()); + newArgType = (((Term) e).getType()); } - if (argType != null) { - Type newPairType = pairTypes.get(argType); + if (newArgType != null) { + Type newPairType = pairTypes.get(newArgType); if (newPairType != null) { t.setType(newPairType); Map updateExps = getUpdateSet(updateFromPair, pairExps); @@ -541,11 +580,11 @@ // If the root symbol of the term is fst. List tupleExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - tupleExps.add(arg); + tupleExps.add(arg); // tuple argument updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(t); + tupleExps.add(t); // first component updateExpressionBelonging(expToTuple, t, tupleExps); - tupleExps.add(null); + tupleExps.add(null); // second component Type argType = null; if (arg instanceof Variable) { argType = ((Variable) arg).getType(); @@ -554,16 +593,17 @@ } Type newTupleType = DataConstraintModel.typeTuple; if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(t.getType()); - compTypeList.add(null); - newTupleType = tupleTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newTupleType = tupleTypes.get(newCompTypeList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); } } if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. if (arg instanceof Variable) { ((Variable) arg).setType(newTupleType); argType = newTupleType; @@ -579,10 +619,10 @@ // If the root symbol of the term is snd. List tupleExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - tupleExps.add(arg); + tupleExps.add(arg); // tuple argument updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(null); - tupleExps.add(t); + tupleExps.add(null); // first component + tupleExps.add(t); // second component updateExpressionBelonging(expToTuple, t, tupleExps); Type argType = null; if (arg instanceof Variable) { @@ -592,27 +632,28 @@ } Type newTupleType = DataConstraintModel.typeTuple; if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(null); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); if (DataConstraintModel.typeTuple.isAncestorOf(t.getType())) { List sndTypes = tupleComponentTypes.get(t.getType()); if (sndTypes != null) { for (Type t2: sndTypes) { - compTypeList.add(t2); + newCompTypeList.add(t2); } } else { - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); } } else { - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); } - newTupleType = tupleTypes.get(compTypeList); + newTupleType = tupleTypes.get(newCompTypeList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); } } if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. if (arg instanceof Variable) { ((Variable) arg).setType(newTupleType); argType = newTupleType; @@ -628,11 +669,11 @@ // If the root symbol of the term is left. List pairExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - pairExps.add(arg); + pairExps.add(arg); // pair updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(t); + pairExps.add(t); // left updateExpressionBelonging(expToPair, t, pairExps); - pairExps.add(null); + pairExps.add(null); // right Type argType = null; if (arg instanceof Variable) { argType = ((Variable) arg).getType(); @@ -641,13 +682,13 @@ } Type newPairType = DataConstraintModel.typePair; if (argType == DataConstraintModel.typePair && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(t.getType()); - compTypeList.add(null); - newPairType = pairTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newPairType = pairTypes.get(newCompTypeList); if (newPairType == null) { // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); } } if (argType != newPairType && newPairType != null) { @@ -666,10 +707,10 @@ // If the root symbol of the term is right. List pairExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - pairExps.add(arg); + pairExps.add(arg); // pair updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(null); - pairExps.add(t); + pairExps.add(null); // left + pairExps.add(t); // right updateExpressionBelonging(expToPair, t, pairExps); Type argType = null; if (arg instanceof Variable) { @@ -679,13 +720,13 @@ } Type newPairType = DataConstraintModel.typePair; if (argType == DataConstraintModel.typePair && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(null); - compTypeList.add(t.getType()); - newPairType = pairTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); + newCompTypeList.add(t.getType()); + newPairType = pairTypes.get(newCompTypeList); if (newPairType == null) { // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); } } if (argType != newPairType && newPairType != null) { @@ -717,21 +758,22 @@ } else if (arg1 instanceof Term) { arg1Type = ((Term) arg1).getType(); } - List compTypeList = new ArrayList<>(); + List newCompTypeList = new ArrayList<>(); if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); + newCompTypeList.add(((Variable) arg2).getType()); } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); + newCompTypeList.add(((Term) arg2).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { - Type newMapType = mapTypes.get(compTypeList); + Type newMapType = mapTypes.get(newCompTypeList); if (newMapType == null) { // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); } + // Update the type of the map argument and record the updated expression. if (arg1 instanceof Variable) { ((Variable) arg1).setType(newMapType); arg1Type = newMapType; @@ -758,33 +800,186 @@ mapExps.add(arg0); updateExpressionBelonging(expToMap, arg0, mapExps); Type termType = t.getType(); - List compTypeList = new ArrayList<>(); + List newCompTypeList = new ArrayList<>(); if (arg1 instanceof Variable) { - compTypeList.add(((Variable) arg1).getType()); + newCompTypeList.add(((Variable) arg1).getType()); } else if (arg1 instanceof Term) { - compTypeList.add(((Term) arg1).getType()); + newCompTypeList.add(((Term) arg1).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); + newCompTypeList.add(((Variable) arg2).getType()); } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); + newCompTypeList.add(((Term) arg2).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } if (termType == DataConstraintModel.typeMap || termType == null) { - Type newMapType = mapTypes.get(compTypeList); + Type newMapType = mapTypes.get(newCompTypeList); if (newMapType == null) { // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); } + // Update the type of the map term and record the updated expression. t.setType(newMapType); termType = newMapType; Map updateExps = getUpdateSet(updateFromMap, mapExps); updateExps.put(System.identityHashCode(t), t); } map.put(System.identityHashCode(mapExps), termType); + } else if (symbol.equals(DataConstraintModel.addMember)) { + // If the root symbol of the term is addMember (addMember(json, key, value)). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + Expression valueArg = t.getChildren().get(2); + dotExps.add(t); // json + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(valueArg); // value + updateExpressionBelonging(expToJson, valueArg, dotExps); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + Type jsonType = t.getType(); + Type valueType = null; + if (valueArg instanceof Variable) { + valueType = ((Variable) valueArg).getType(); + } else if (valueArg instanceof Term) { + valueType = ((Term) valueArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type jsonArgType = null; + if (jsonArg instanceof Variable) { + jsonArgType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonArgType = ((Term) jsonArg).getType(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && jsonArgType != null && keyName != null) { + Map newMemberTypes = new HashMap<>(((JsonType) jsonArgType).getMemberTypes()); + newMemberTypes.put(keyName, valueType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json term and record the updated expression. + t.setType(newJsonType); + jsonType = newJsonType; + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(t), t); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dot)) { + // If the root symbol of the term is dot (json.property). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && t.getType() != null && keyName != null) { + Map newMemberTypes = new HashMap<>(); + newMemberTypes.put(keyName, t.getType()); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dotParam)) { + // If the root symbol of the term is dot (json.{param}). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json (list/map) + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(null); // key + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + Type keyType = null; + if (keyArg instanceof Variable) { + keyType = ((Variable) keyArg).getType(); + } else if (keyArg instanceof Term) { + keyType = ((Term) keyArg).getType(); + } + Type newJsonType = null; + if (keyType == DataConstraintModel.typeInt) { + newJsonType = DataConstraintModel.typeList; + } else if (keyType == DataConstraintModel.typeString) { + newJsonType = DataConstraintModel.typeMap; + } + if (t.getType() != null) { + if ((jsonType == DataConstraintModel.typeList)) { + newJsonType = listTypes.get(t.getType()); + if (newJsonType == null) { + // Create new list type; + newJsonType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + } else if (jsonType == DataConstraintModel.typeMap) { + List keyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, t.getType()}); + newJsonType = mapTypes.get(keyValueTypes); + if (newJsonType == null) { + // Create new map type; + newJsonType = createNewMapType(keyValueTypes, DataConstraintModel.typeMap); + } + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); } else if (symbol.equals(DataConstraintModel.cond)) { // If the root symbol of the term is if function. Expression c1 = t.getChild(1); @@ -925,12 +1120,57 @@ variables.put(System.identityHashCode(consExps), condType); } } + + // 1.5 Extract constraints on path parameters and resources. + ResourcePath rPath = cm.getResource(); + while (rPath != null) { + Expression param = rPath.getLastParam(); + if (param != null) { + ResourceHierarchy parent = rPath.getResourceHierarchy().getParent(); + if (parent != null) { + List pathParams = resourcePathParams.get(parent); + if (pathParams == null) { + pathParams = new ArrayList<>(); + resourcePathParams.put(parent, pathParams); + } + pathParams.add(param); + expToPathParams.put(System.identityHashCode(param), pathParams); + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (param instanceof Variable) { + paramType = ((Variable) param).getType(); + } else if (param instanceof Term) { + paramType = ((Term) param).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + rPath = rPath.getParent(); + } + } + } + + // 1.6 Extract constraints on resource hierarchies. + for (ResourceHierarchy res: model.getResourceHierarchies()) { + if (res.getResourceStateType() != null) { + updateFromResourceOwnership.add(res); } } // 2. Propagate type information. while (updateFromResource.size() > 0 || updateFromVariable.size() > 0 || updateFromMessage.size() > 0 - || updateFromConsOrSet.size() > 0 || updateFromTuple.size() > 0 || updateFromPair.size() > 0 || updateFromMap.size() > 0) { + || updateFromConsOrSet.size() > 0 || updateFromTuple.size() > 0 || updateFromPair.size() > 0 + || updateFromMap.size() > 0 || updateFromJson.size() > 0 || updateFromResourceOwnership.size() > 0) { if (updateFromResource.size() > 0) { Set resourceKeys = updateFromResource.keySet(); Integer resourceKey = resourceKeys.iterator().next(); @@ -944,6 +1184,8 @@ updateTupleTypes(resExp, tuple, expToTuple, updateFromTuple); updatePairTypes(resExp, pair, expToPair, updateFromPair); updateMapTypes(resExp, map, expToMap, updateFromMap); + updateJsonTypes(resExp, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(resExp, resourcePathParams, updateFromResourceOwnership); } } if (updateFromVariable.size() > 0) { @@ -953,13 +1195,15 @@ updateFromVariable.remove(variableKey); for (int i : variableValue.keySet()) { Expression var = variableValue.get(i); - updateResourceTypes(var, resources, expToResource, updateFromResource); + updateResourceTypes(var, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(var, variables, expToVariable, updateFromVariable); updateMessageTypes(var, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(var, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(var, tuple, expToTuple, updateFromTuple); updatePairTypes(var, pair, expToPair, updateFromPair); updateMapTypes(var, map, expToMap, updateFromMap); + updateJsonTypes(var, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(var, resourcePathParams, updateFromResourceOwnership); } } if (updateFromMessage.size() > 0) { @@ -969,12 +1213,14 @@ updateFromMessage.remove(messageKey); for (int i : messageValue.keySet()) { Expression mesExp = messageValue.get(i); - updateResourceTypes(mesExp, resources, expToResource, updateFromResource); + updateResourceTypes(mesExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(mesExp, variables, expToVariable, updateFromVariable); updateConsOrSetTypes(mesExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(mesExp, tuple, expToTuple, updateFromTuple); updatePairTypes(mesExp, pair, expToPair, updateFromPair); updateMapTypes(mesExp, map, expToMap, updateFromMap); + updateJsonTypes(mesExp, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(mesExp, resourcePathParams, updateFromResourceOwnership); } } if (updateFromConsOrSet.size() > 0) { @@ -984,13 +1230,14 @@ updateFromConsOrSet.remove(consKey); for (int i : consValue.keySet()) { Expression consExp = consValue.get(i); - updateResourceTypes(consExp, resources, expToResource, updateFromResource); + updateResourceTypes(consExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(consExp, variables, expToVariable, updateFromVariable); updateMessageTypes(consExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(consExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(consExp, tuple, expToTuple, updateFromTuple); updatePairTypes(consExp, pair, expToPair, updateFromPair); updateMapTypes(consExp, map, expToMap, updateFromMap); + updateJsonTypes(consExp, json, expToJson, updateFromJson); } } if (updateFromTuple.size() > 0) { @@ -1000,13 +1247,14 @@ updateFromTuple.remove(tupleKey); for (int i : tupleValue.keySet()) { Expression tupleExp = tupleValue.get(i); - updateResourceTypes(tupleExp, resources, expToResource, updateFromResource); + updateResourceTypes(tupleExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(tupleExp, variables, expToVariable, updateFromVariable); updateMessageTypes(tupleExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(tupleExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(tupleExp, tuple, expToTuple, updateFromTuple); updatePairTypes(tupleExp, pair, expToPair, updateFromPair); updateMapTypes(tupleExp, map, expToMap, updateFromMap); + updateJsonTypes(tupleExp, json, expToJson, updateFromJson); } } if (updateFromPair.size() > 0) { @@ -1016,13 +1264,14 @@ updateFromPair.remove(pairKey); for (int i : pairValue.keySet()) { Expression pairExp = pairValue.get(i); - updateResourceTypes(pairExp, resources, expToResource, updateFromResource); + updateResourceTypes(pairExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(pairExp, variables, expToVariable, updateFromVariable); updateMessageTypes(pairExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(pairExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(pairExp, tuple, expToTuple, updateFromTuple); updatePairTypes(pairExp, pair, expToPair, updateFromPair); updateMapTypes(pairExp, map, expToMap, updateFromMap); + updateJsonTypes(pairExp, json, expToJson, updateFromJson); } } if (updateFromMap.size() > 0) { @@ -1032,15 +1281,38 @@ updateFromMap.remove(mapKey); for (int i : mapValue.keySet()) { Expression mapExp = mapValue.get(i); - updateResourceTypes(mapExp, resources, expToResource, updateFromResource); + updateResourceTypes(mapExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(mapExp, variables, expToVariable, updateFromVariable); updateMessageTypes(mapExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(mapExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(mapExp, tuple, expToTuple, updateFromTuple); updatePairTypes(mapExp, pair, expToPair, updateFromPair); updateMapTypes(mapExp, map, expToMap, updateFromMap); + updateJsonTypes(mapExp, json, expToJson, updateFromJson); } } + if (updateFromJson.size() > 0) { + Set jsonKeys = updateFromJson.keySet(); + Integer jsonKey = jsonKeys.iterator().next(); + Map jsonValue = updateFromJson.get(jsonKey); + updateFromJson.remove(jsonKey); + for (int i : jsonValue.keySet()) { + Expression jsonExp = jsonValue.get(i); + updateResourceTypes(jsonExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); + updateVaribleTypes(jsonExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(jsonExp, messages, expToMessage, updateFromMessage); + updateConsOrSetTypes(jsonExp, consOrSet, expToConsOrSet, updateFromConsOrSet); + updateTupleTypes(jsonExp, tuple, expToTuple, updateFromTuple); + updatePairTypes(jsonExp, pair, expToPair, updateFromPair); + updateMapTypes(jsonExp, map, expToMap, updateFromMap); + updateJsonTypes(jsonExp, json, expToJson, updateFromJson); + } + } + if (updateFromResourceOwnership.size() > 0) { + ResourceHierarchy res = updateFromResourceOwnership.iterator().next(); + updateFromResourceOwnership.remove(res); + updateResourceOwnershipTypes(res, resources, expToResource, updateFromResource, updateFromResourceOwnership); + } } } @@ -1057,18 +1329,20 @@ } } - private static void updateResourceTypes(Expression exp, Map> resources, - Map> expToResource, Map> updateFromResource) { - List sameResource = expToResource.get(System.identityHashCode(exp)); - if (sameResource == null) return; - for (ResourceHierarchy res : resources.keySet()) { - if (resources.get(res) == sameResource) { + private static void updateResourceTypes(Expression exp, Map> resources, Map> expToResource, + Map> updateFromResource, Set updateFromResourceOwnership) { + List identicalResources = expToResource.get(System.identityHashCode(exp)); + if (identicalResources == null) return; + for (ResourceHierarchy res: resources.keySet()) { + if (resources.get(res) == identicalResources) { Type resType = res.getResourceStateType(); Type newResType = getExpTypeIfUpdatable(resType, exp); if (newResType != null) { res.setResourceStateType(newResType); - Map updateExps = getUpdateSet(updateFromResource, sameResource); - for (Expression resExp : sameResource) { + updateFromResourceOwnership.add(res); // To update parent and children resources + // Update identical resources + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable) { ((Variable) resExp).setType(newResType); @@ -1083,6 +1357,145 @@ } } } + + private static void updateResourcePathParamsTypes(Expression exp, Map> resourcePathParams, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resourcePathParams.keySet()) { + List pathParams = resourcePathParams.get(parent); + if (pathParams.contains(exp)) { + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (exp instanceof Variable) { + paramType = ((Variable) exp).getType(); + } else if (exp instanceof Term) { + paramType = ((Term) exp).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + + private static void updateResourceOwnershipTypes(ResourceHierarchy res, Map> resources, + Map> expToResource, Map> updateFromResource, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resources.keySet()) { + Type resType = res.getResourceStateType(); + 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)) { + Type newElementType = listComponentTypes.get(resType); + if (newElementType != null && children != null && children.size() == 1) { + ResourceHierarchy element = children.iterator().next(); + Type elementType = element.getResourceStateType(); + if (compareTypes(elementType, newElementType)) { + element.setResourceStateType(newElementType); + updateFromResourceOwnership.add(element); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + List newComponentTypes = mapComponentTypes.get(resType); + if (newComponentTypes != null && newComponentTypes.size() == 2 && children != null && children.size() == 1) { + ResourceHierarchy value = children.iterator().next(); + Type valueType = value.getResourceStateType(); + if (compareTypes(valueType, newComponentTypes.get(1))) { + value.setResourceStateType(newComponentTypes.get(1)); + updateFromResourceOwnership.add(value); + } + } + } else if (DataConstraintModel.typeJson.isAncestorOf(resType)) { + Map newMemberTypes = jsonMemberTypes.get(resType); + if (newMemberTypes != null && newMemberTypes.size() > 0 && children != null && children.size() > 0) { + for (ResourceHierarchy chlid: children) { + String key = chlid.getResourceName(); + Type memberType = chlid.getResourceStateType(); + if (compareTypes(memberType, newMemberTypes.get(key))) { + chlid.setResourceStateType(newMemberTypes.get(key)); + updateFromResourceOwnership.add(chlid); + } + } + } + } + } + if (children.contains(res)) { + // Propagate an update of a child resource type to its parent's type. + Type parentType = parent.getResourceStateType(); + if (parentType != null && DataConstraintModel.typeList.isAncestorOf(parentType)) { + Type oldElementType = listComponentTypes.get(parentType); + if (compareTypes(oldElementType, resType)) { + Type newListType = listTypes.get(resType); + if (newListType == null) { + newListType = createNewListType(resType, parentType); + } + if (newListType != null) { + parent.setResourceStateType(newListType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeMap.isAncestorOf(parentType)) { + List oldComponentTypes = mapComponentTypes.get(parentType); + if (compareTypes(oldComponentTypes.get(1), resType)) { + List newComponentTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, resType}); + Type newMapType = mapTypes.get(newComponentTypes); + if (newMapType == null) { + newMapType = createNewMapType(newComponentTypes, parentType); + } + if (newMapType != null) { + parent.setResourceStateType(newMapType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeJson.isAncestorOf(parentType)) { + Map oldMemberTypes = jsonMemberTypes.get(parentType); + String key= res.getResourceName(); + if (oldMemberTypes == null || compareTypes(oldMemberTypes.get(key), resType)) { + Map newMemberTypes = new HashMap<>(); + if (oldMemberTypes != null) newMemberTypes.putAll(oldMemberTypes); + newMemberTypes.put(key, resType); + Type newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + newJsonType = createNewJsonType(newMemberTypes, parentType); + } + if (newJsonType != null) { + parent.setResourceStateType(newJsonType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + // Propagate an update of the resource to its expressions. + List identicalResources = resources.get(res); + if (identicalResources != null) { + Type newResType = res.getResourceStateType(); + for (Expression resExp: identicalResources) { + Type resType = null; + if (resExp instanceof Variable) { + resType = ((Variable) resExp).getType(); + } else if (resExp instanceof Term) { + resType = ((Term) resExp).getType(); + } + if (resType == null || compareTypes(resType, newResType)) { + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + if (resExp instanceof Variable) { + ((Variable) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term) { + ((Term) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } + } + } + } + } private static void updateVaribleTypes(Expression exp, Map variables, Map> expToVariable, Map> updateFromVariable) { @@ -1168,6 +1581,7 @@ int idx = consOrSetComponentGroup.indexOf(exp); switch (idx) { case 0: + // exp is a list itself. if (!(exp instanceof Term)) break; Type listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type expType = getExpTypeIfUpdatable(listType, exp); @@ -1192,6 +1606,7 @@ } break; case 1: + // exp is a list's component. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type compType = listComponentTypes.get(listType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1217,6 +1632,7 @@ } break; case 2: + // exp is a list itself. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); expType = getExpTypeIfUpdatable(listType, exp); if (expType != null) { @@ -1246,6 +1662,7 @@ for (List tupleComponentGroup: tupleComponentGroups) { int idx = tupleComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a tuple itself. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); Type newTupleType = getExpTypeIfUpdatable(tupleType, exp); if (newTupleType != null) { @@ -1313,6 +1730,7 @@ } } } else { + // exp is a tuple's component. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); List componentTypes = tupleComponentTypes.get(tupleType); boolean updated = false; @@ -1383,6 +1801,7 @@ for (List pairComponentGroup: pairComponentGroups) { int idx = pairComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a pair itself. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type newPairType = getExpTypeIfUpdatable(pairType, exp); if (newPairType != null) { @@ -1406,6 +1825,7 @@ } } } else { + // exp is a pair's component. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type compType = pairComponentTypes.get(pairType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1436,6 +1856,7 @@ for (List mapComponentGroup: mapComponentGroups) { int idx = mapComponentGroup.indexOf(exp); if (idx == 0 || idx == 3) { + // exp is a map itself. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); Type newMapType = getExpTypeIfUpdatable(mapType, exp); if (newMapType != null) { @@ -1479,6 +1900,7 @@ } } } else { + // exp is a map's key or value. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); List componentTypes = mapComponentTypes.get(mapType); Type compType = componentTypes.get(idx - 1); @@ -1517,6 +1939,116 @@ } } + + private static void updateJsonTypes(Expression exp, Map json, + Map>> expToJson, Map> updateFromJson) { + Set> jsonMemberGroups = expToJson.get(System.identityHashCode(exp)); + if (jsonMemberGroups == null) return; + for (List jsonMemberGroup: jsonMemberGroups) { + int idx = jsonMemberGroup.indexOf(exp); + if (idx == 3) { + // exp is a json argument (0:t = addMember(3:json, 1:key, 2:value)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Map memberTypes = new HashMap<>(jsonMemberTypes.get(jsonType)); + Map argMemberTypes = new HashMap<>(memberTypes); + String keyName = null; + Type valueType = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + valueType = ((Constant) jsonMemberGroup.get(1)).getType(); + argMemberTypes.remove(keyName); + } + Type jsonArgType = jsonTypes.get(argMemberTypes); + Type newJsonArgType = getExpTypeIfUpdatable(jsonArgType, exp); + if (newJsonArgType != null && keyName != null) { + // Propagate an update of a json arg's type to its container's (json's) type. + argMemberTypes = ((JsonType) newJsonArgType).getMemberTypes(); + argMemberTypes.put(keyName, valueType); + memberTypes.putAll(argMemberTypes); + Type newJsonType = jsonTypes.get(memberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(memberTypes, jsonType); + } + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } else if (idx == 2) { + // exp is a value (0:t = addMember(3:json, 1:key, 2:value) or 2:value = dot(0:list/map, 1:key)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Type newJsonType = null; + if (exp instanceof Term && ((Term) exp).getSymbol().equals(DataConstraintModel.dotParam)) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType)) { + Type elementType = listComponentTypes.get(jsonType); + Type newElementType = getExpTypeIfUpdatable(elementType, exp); + if (newElementType != null) { + // Propagate an update of a member's type to its container's (json's) type. + newJsonType = listTypes.get(newElementType); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewListType(newElementType, jsonType); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType)) { + List keyValueTypes = mapComponentTypes.get(jsonType); + Type newValueType = getExpTypeIfUpdatable(keyValueTypes.get(1), exp); + if (newValueType != null) { + // Propagate an update of a member's type to its container's (json's) type. + List newKeyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, newValueType}); + newJsonType = mapTypes.get(newKeyValueTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewMapType(newKeyValueTypes, jsonType); + } + } + } + } else { + Map memberTypes = jsonMemberTypes.get(jsonType); + String keyName = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + } + 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); + } + } + } + } + if (newJsonType != null) { + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } + } + } + private static Type createNewListType(Type compType, Type parentType) { String compTypeName = getInterfaceTypeName(compType); List childrenTypes = getChildrenTypes(parentType, listComponentTypes.keySet()); @@ -1599,9 +2131,39 @@ return newMapType; } - private static List getChildrenTypes(Type parentType, Set componentTypes) { + private static JsonType createNewJsonType(Map memberTypes, Type parentJsonType) { + String implTypeName = "HashMap<>"; + String interfaceTypeName = "Map"; + List childrenTypes = getChildrenTypes(parentJsonType, jsonMemberTypes.keySet()); + JsonType newJsonType = new JsonType("Json", implTypeName, interfaceTypeName, parentJsonType); + for (String key: memberTypes.keySet()) { + newJsonType.addMemberType(key, memberTypes.get(key)); + } + jsonTypes.put(memberTypes, newJsonType); + jsonMemberTypes.put(newJsonType, memberTypes); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newJsonType)) { + if (newJsonType.getParentTypes().contains(parentJsonType)) { + newJsonType.replaceParentType(parentJsonType, childType); + } else { + newJsonType.addParentType(childType); + } + } else if (compareTypes(newJsonType, childType)) { + childType.replaceParentType(parentJsonType, newJsonType); + } + } + return newJsonType; + } + + /** + * Get children types of a given type from given set of types. + * @param parentType a type + * @param allTypes set of types + * @return list of the children types + */ + private static List getChildrenTypes(Type parentType, Set allTypes) { List childrenTypes = new ArrayList<>(); - for (Type childType : componentTypes) { + for (Type childType : allTypes) { if (childType.getParentTypes().contains(parentType)) { childrenTypes.add(childType); } @@ -1683,6 +2245,7 @@ for (int i = 0; i < originalCompTypes.size(); i++) { if (originalCompTypes.get(i) != null) { if (DataConstraintModel.typeTuple.isAncestorOf(originalCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = originalCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { originalCompTypes.add(t); @@ -1690,6 +2253,7 @@ } if (newCompTypes.size() - 1 < i) return false; if (newCompTypes.get(i) != null && DataConstraintModel.typeTuple.isAncestorOf(newCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = newCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { newCompTypes.add(t); @@ -1707,6 +2271,19 @@ if (originalCompType != null && (newCompType == null || !originalCompType.isAncestorOf(newCompType))) return false; return true; } + if (DataConstraintModel.typeJson.isAncestorOf(originalType) && DataConstraintModel.typeJson.isAncestorOf(newType)) { + Map originalMemberTypes = jsonMemberTypes.get(originalType); + Map newMemberTypes = jsonMemberTypes.get(newType); + if (originalMemberTypes == null) return true; + if (originalMemberTypes.keySet().size() < newMemberTypes.keySet().size() + && newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return true; + if (originalMemberTypes.keySet().size() > newMemberTypes.keySet().size()) return false; + if (!newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return false; + for (String key: originalMemberTypes.keySet()) { + if (!originalMemberTypes.get(key).isAncestorOf(newMemberTypes.get(key))) return false; + } + return true; + } } return false; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java index b4d81a6..7ded3d9 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java @@ -30,7 +30,9 @@ import models.visualModel.FormulaChannel; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; public class DataTransferModelingCellEditor implements mxICellEditor { public int DEFAULT_MIN_WIDTH = 70; @@ -123,7 +125,7 @@ Expression exp = parser.parseTerm(stream, editor.getModel()); ((FormulaChannel) ch).setFormula(formula); ((FormulaChannel) ch).setFormulaTerm(exp); - } catch (ExpectedRightBracket e) { + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index 933f5ea..25604c4 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -6,6 +6,8 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,10 +35,15 @@ import algorithms.Validation; import application.layouts.*; import code.ast.CompilationUnit; +import generators.JavaCodeGenerator; import models.Edge; import models.EdgeAttribute; import models.Node; import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; @@ -46,6 +53,7 @@ import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; @@ -56,6 +64,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; @@ -68,6 +77,7 @@ import parser.exceptions.ExpectedResource; import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongRHSExpression; import parser.exceptions.ExpectedRightCurlyBracket; @@ -204,7 +214,7 @@ | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel - | ExpectedRightCurlyBracket | WrongPathExpression e) { + | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } @@ -240,7 +250,7 @@ return model; } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { @@ -577,7 +587,7 @@ Expression exp = parser.parseTerm(stream, getModel()); resourcePath = new ResourcePath(parentPath, exp); getModel().addResourcePath(resourcePath); - } catch (ExpectedRightBracket e) { + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); return null; } @@ -767,10 +777,28 @@ resetDataFlowGraph(); } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression - | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } + + public Set> getResourceDependencyForChannel(DataTransferChannel ch, DataTransferModel model, DataFlowGraph dataFlowGraph) { + Set> resourceDpendency = new HashSet<>(); + try { + Map>> dependency = ch.fillOutsideResourcePaths(ch.getOutputChannelMembers().iterator().next(), JavaCodeGenerator.pullAccessor); + for (ChannelMember srcMem: dependency.keySet()) { + ResourceNode srcNode = dataFlowGraph.getResourceNode(srcMem.getResource()); + for (ChannelMember dstMem: dependency.get(srcMem).getValue()) { + ResourceNode dstNode = dataFlowGraph.getResourceNode(dstMem.getResource()); + resourceDpendency.add(new AbstractMap.SimpleEntry<>(srcNode, dstNode)); + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + return resourceDpendency; + } private class SrcDstAttribute extends EdgeAttribute { private Object src; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java index f4e94dd..9984ce0 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,58 @@ 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. + if (generatesComponent(child)) { + return langSpec.newListType(getComponentName(child, langSpec)); + } else { + return langSpec.newListType(getImplStateType(child, langSpec).getImplementationTypeName()); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return langSpec.newMapType(DataConstraintModel.typeString, getComponentName(child, langSpec)); + } else { + return langSpec.newMapType(DataConstraintModel.typeString, getImplStateType(child, langSpec).getImplementationTypeName()); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + /** * Generate source codes in specified language from data-flow/control-flow graph. * @@ -72,6 +129,7 @@ * @return source codes */ public ArrayList generateCode(DataTransferModel model, DataFlowGraph flowGraph, ILanguageSpecific langSpec) { + CodeGenerator.langSpec = langSpec; ArrayList codes = new ArrayList<>(); // Sort the all components. @@ -198,81 +256,136 @@ mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter()); } - protected void addReference(TypeDeclaration component, MethodDeclaration constructor, Node dstNode, ILanguageSpecific langSpec) { - String dstNodeName = null; - if (dstNode instanceof ResourceNode) { - dstNodeName = ((ResourceNode) dstNode).getResourceName(); + protected ResourcePath addReference(TypeDeclaration component, MethodDeclaration constructor, ResourcePath dstRes, ILanguageSpecific langSpec) { + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); } - String dstComponentName = langSpec.toComponentName(dstNodeName); - if (langSpec.declareField()) { - // Declare a field to refer to another component. - component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + String dstComponentName = getComponentName(dstRes.getResourceHierarchy(), langSpec); + if (dstComponentName != null) { + String dstNodeName = langSpec.toVariableName(dstComponentName); + if (langSpec.declareField()) { + // Declare a field to refer to another component. + component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + } + // Initialize the field to refer to another component. + constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter()); } - // Initialize the field to refer to another component. - constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); - constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter()); + return dstRes; } - protected void 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(langSpec.getStringDelimiter() + fieldName + langSpec.getStringDelimiter(), DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else { + stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } + } } } - protected void 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 mainGetterParams = new ArrayList<>(); + int v = 1; for (Selector selector: accessRes.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - mainParams.add(var.getName()); + mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainGetterParams.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()); + if (mainGetterParams.size() == 0) { + 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), mainGetterParams); } 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 = getPullAccessor().getDirectStateAccessorFor(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); } @@ -283,13 +396,13 @@ for (Channel ch : model.getChannels()) { DataTransferChannel c = (DataTransferChannel) ch; if (c.getInputResources().contains(resourceNode.getOutSideResource())) { - for (ResourcePath id: c.getReferenceResources()) { - if (!refs.contains(id) && !depends.contains(id)) { - refs.add(id); - String refResName = langSpec.toComponentName(id.getResourceName()); - component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), id.getResourceName())); - constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), id.getResourceName())); - constructor.getBody().addStatement(langSpec.getFieldAccessor(id.getResourceName()) + langSpec.getAssignment() + id.getResourceName() + langSpec.getStatementDelimiter()); + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res)) { + refs.add(res); + String refResName = langSpec.toComponentName(res.getResourceName()); + component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getResourceName()) + langSpec.getAssignment() + res.getResourceName() + langSpec.getStatementDelimiter()); } } } @@ -333,22 +446,38 @@ protected IResourceStateAccessor getPushAccessor() { return new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); + if (targetRes.equals(fromRes)) { return new Field(fieldOfResourceState, - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getResourceName(), + targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } }; @@ -357,23 +486,113 @@ protected IResourceStateAccessor getPullAccessor() { 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); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); } - // 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 - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + 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 (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index 9a59356..11096e4 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -1,10 +1,12 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import code.ast.Block; @@ -28,6 +30,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,71 +49,100 @@ public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { - // For each of other components. + Map resourceComponents = new HashMap<>(); + List> getters = new ArrayList<>(); + List> inputs = new ArrayList<>(); + List> fields = new ArrayList<>(); + + // For each components. for (Node componentNode: components) { // Declare this resource. ResourceNode resourceNode = (ResourceNode) componentNode; - String resourceName = langSpec.toComponentName(resourceNode.getResourceName()); - TypeDeclaration component = getComponent(resourceName, codes); - List depends = new ArrayList<>(); - if (component == null) { - // Add compilation unit for this component. - component = langSpec.newTypeDeclaration(resourceName); - 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); + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); + TypeDeclaration component = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + // A component will be generated for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + List depends = new ArrayList<>(); + if (component == null) { + // Add compilation unit for this component. + component = langSpec.newTypeDeclaration(resourceName); + resourceComponents.put(resourceNode.getResourceHierarchy(), component); + 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); + + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // 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); + } } + + // Declare the field in this resource to store the state. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + declareStateField(resourceNode, component, resStateType, langSpec); + } + + // Declare the getter methods in this resource to obtain the children resources. + declareChildGetterMethod(resourceNode, component, langSpec); + + // Declare cache fields and update methods in this resource. + List updates = declareCacheFieldsAndUpdateMethods(resourceNode, component, langSpec); } - Type resStateType = resourceNode.getResourceStateType(); - - // 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); + // Declare the state field and reference fields in the parent component. + if (component == null) { + declareFieldsInParentComponent(resourceNode, fields, 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); + if (component == null) { + // stateGetter was not added to any component because no component had been generated. + getters.add(new AbstractMap.SimpleEntry(resourceNode.getResourceHierarchy().getParent(), stateGetter)); + } // Declare the accessor method in the main component to call the getter method. - declareAccessorInMainComponent(mainComponent, resourceNode, getter, langSpec); - - // Declare cache fields and update methods in this resource. - List updates = declareCacheFieldsAndUpdateMethods(resourceNode, component, langSpec); + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); // Declare input methods in this component and the main component. - List inputs = declareInputMethodsInThisAndMainComponents(resourceNode, component, mainComponent, model, langSpec); + List inputMethods = declareInputMethodsInThisAndMainComponents(resourceNode, component, mainComponent, model, langSpec); + if (component == null) { + // inputMethods were not added to any component because no component had been generated. + for (MethodDeclaration in: inputMethods) { + inputs.add(new AbstractMap.SimpleEntry(resourceNode.getResourceHierarchy().getParent(), in)); + } + } + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf input methods to the parent components. + for (Map.Entry entry: inputs) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); } } - private TypeDeclaration getComponent(String resourceName, ArrayList codes) { - TypeDeclaration component = null; - for (CompilationUnit cu: codes) { - for (TypeDeclaration comp: cu.types()) { - if (resourceName.equals(comp.getTypeName())) { - component = comp; - break; - } - } - if (component != null) break; - } - return component; - } - - private MethodDeclaration declareConstructorAndFieldsToReferToResources(ResourceNode resourceNode, TypeDeclaration component, - List depends, ILanguageSpecific langSpec) { + private MethodDeclaration declareConstructorAndFieldsToReferToResources(ResourceNode resourceNode, TypeDeclaration component, List depends, ILanguageSpecific langSpec) { // Declare a constructor in each component. MethodDeclaration constructor = component.createConstructor(); Block block = new Block(); @@ -118,73 +150,246 @@ // Declare fields in each component. (for data-flow graph) for (Edge resToCh: resourceNode.getOutEdges()) { - if (((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { for (Edge chToRes: resToCh.getDestination().getOutEdges()) { // for PUSH transfer - addReference(component, constructor, chToRes.getDestination(), langSpec); - ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); - if (!depends.contains(dstRes)) depends.add(dstRes); + if (chToRes.getDestination() instanceof ResourceNode) { + ResourcePath dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource(), langSpec); + if (outsideOutputResource) { + if (dstRes != null && dstRes.getParent() != null) { + // Reference to root resource. + addReference(component, constructor, dstRes.getRoot(), langSpec); + } + } + if (!depends.contains(dstRes)) depends.add(dstRes); + } } } } for (Edge chToRes: resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { - if (((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + ResourcePath srcRes = ((ResourceNode) resToCh.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for PULL transfer - addReference(component, constructor, resToCh.getSource(), langSpec); - ResourcePath srcRes = ((ResourceNode) resToCh.getSource()).getOutSideResource(); + srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource(), langSpec); + if (outsideInputResource) { + if (srcRes != null & srcRes.getParent() != null) { + // Reference to root resource. + addReference(component, constructor, srcRes.getRoot(), langSpec); + } + } if (!depends.contains(srcRes)) depends.add(srcRes); } } } 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 (generatesComponent(c)) { + // 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); + } + + private void declareFieldsInParentComponent(ResourceNode resourceNode, List> fields, ILanguageSpecific langSpec) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + // Check if the destination resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + String dstResName = null; + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + dstResName = getComponentName(dstRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + fields.add(new AbstractMap.SimpleEntry(resourceNode.getParent().getResourceHierarchy(), refFieldForPush)); + if (dstRes.getParent() != null) { + // Reference to root resource. + String dstRootResName = getComponentName(dstRes.getRoot().getResourceHierarchy(), langSpec); + FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(new Type(dstRootResName, dstRootResName), langSpec.toVariableName(dstRootResName)); + fields.add(new AbstractMap.SimpleEntry(resourceNode.getParent().getResourceHierarchy(), refRootFieldForPush)); + } + } + } } - component.addMethod(getter); + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { + // Declare a field in the parent component to refer to the source resource of pull transfer. + String srcResName = null; + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + fields.add(new AbstractMap.SimpleEntry(resourceNode.getParent().getResourceHierarchy(), refFieldForPull)); + if (srcRes.getParent() != null) { + // Reference to root resource. + String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); + FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(new Type(srcRootResName, srcRootResName), langSpec.toVariableName(srcRootResName)); + fields.add(new AbstractMap.SimpleEntry(resourceNode.getParent().getResourceHierarchy(), refRootFieldForPull)); + } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res, langSpec); + FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), langSpec.toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry(resourceNode.getParent().getResourceHierarchy(), stateField)); + } + } + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = null; + if (component != null) { + // A component is created for this resource. + stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List getterParams = new ArrayList<>(); + int v = 1; + for (Selector param: resourceNode.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String resCompName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration(getterPrefix + resCompName, resStateType); + } else { + stateGetter = new MethodDeclaration(getterPrefix + resCompName, false, resStateType, getterParams); + } + } if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { - if (resourceNode.getResourceHierarchy().getNumParameters() == 0) { - fillGetterMethodToReturnStateField(getter, resStateType, langSpec); + if (component != null) { + // A component is created for this resource. + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); } else { - fillGetterMethodToReturnComponentOfStateField(getter, resourceNode.getParent().getResourceStateType(), langSpec); + // No component is created for this resource. + fillChildGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resourceNode.getResourceHierarchy().getParent().getResourceStateType(), langSpec); } } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. boolean isContainedPush = false; DataTransferChannel ch = null; - HashMap inputResourceToStateAccessor = new HashMap<>(); + HashMap inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { // PUSH transfer isContainedPush = true; - inputResourceToStateAccessor.put(((ResourceNode) resToCh.getSource()).getOutSideResource(), getPushAccessor()); + inputResourceToStateAccessor.put(in, getPushAccessor()); } else { // PULL transfer - inputResourceToStateAccessor.put(((ResourceNode) resToCh.getSource()).getOutSideResource(), getPullAccessor()); - ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + inputResourceToStateAccessor.put(in, getPullAccessor()); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. } } } // for reference channel members. for (ChannelMember c: ch.getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), getPullAccessor()); // by pull data transfer + inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer } // generate a return statement. @@ -195,11 +400,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 +415,39 @@ } } - return getter; + return stateGetter; + } + + private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { + // Declare the getter methods in this resource to obtain the children resources. + for (ResourceNode child: resourceNode.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + List params = new ArrayList<>(); + int v = 1; + for (Selector param: child.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childCompName = getComponentName(child.getResourceHierarchy(), langSpec); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (params.size() == 0) { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, childType); + } else { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, false, childType, params); + } + + fillChildGetterMethod(childGetter, child.getResourceHierarchy(), resourceNode.getResourceStateType(), langSpec); + component.addMethod(childGetter); + } + } } private List declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { @@ -219,18 +456,26 @@ List updateMethods = new ArrayList<>(); for (Edge chToRes: resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); String srcResName = srcRes.getResourceName(); String srcResComponentName = langSpec.toComponentName(srcResName); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { // for push data transfer // Declare an update method in the type of the destination resource. ArrayList vars = new ArrayList<>(); vars.add(langSpec.newVariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); // For the refs. - DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); for (ResourcePath ref: ch.getReferenceResources()) { if (!resourceNode.getInSideResources().contains(ref)) { vars.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); @@ -243,9 +488,9 @@ // Add a statement to update the state field if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { try { - for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { + for (ChannelMember out: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { - Expression updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, getPushAccessor()); + Expression updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()); String[] sideEffects = new String[] {""}; String curState = updateExp.toImplementation(sideEffects); String updateStatement; @@ -279,23 +524,81 @@ } // Update the cache field. - String cashStatement = langSpec.getFieldAccessor(srcResName) + langSpec.getAssignment() + srcResName + langSpec.getStatementDelimiter(); + String cashStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter(); if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { update.addFirstStatement(cashStatement); } } + Set outsideInputMembers = new HashSet<>(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers.add(cm); + } + } + if (outsideInputMembers.size() > 0) { + Map>> resourcePaths = null; + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + try { + resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor()); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + break; + } + } + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } + } + } + // Add an invocation to another update method (for a chain of update method invocations). for (Edge resToCh2: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh2; - if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource2 = false; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy())) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + } + } + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) { for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { // PUSH transfer + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource())) { + in = cm; + break; + } + } Map> referredResources = new HashMap<>(); List params = new ArrayList<>(); params.add(langSpec.getFieldAccessor(fieldOfResourceState)); Set referredSet = referredResources.get(update); - for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -306,7 +609,7 @@ String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) resToCh2.getSource()).getOutSideResource()); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -320,6 +623,46 @@ params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); } } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(); + for (ChannelMember out: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } } } } @@ -338,43 +681,61 @@ if (resourceNode.getInSideResources().contains(out.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; - MethodDeclaration mainInput = null; + MethodDeclaration inputAccessor = null; if (message instanceof Term) { // Declare an input method in this component. - ArrayList resParams = new ArrayList<>(); - ArrayList mainParams = new ArrayList<>(); + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; for (Selector selector: resourceNode.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } + v++; } - for (Selector selector: resourceNode.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + if (resourceNode.getParent() != null) { + for (Selector selector: resourceNode.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; } } for (Variable var: message.getVariables().values()) { - resParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); - mainParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } - input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, resParams); - component.addMethod(input); - inputMethods.add(input); + input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputMethods.add(input); + } // Declare the accessor in the main component to call the input method. String str = ((Term) message).getSymbol().getImplName(); - mainInput = getMethod(mainComponent, str); - if (mainInput == null) { - mainInput = langSpec.newMethodDeclaration(str, false, null, mainParams); - mainComponent.addMethod(mainInput); + inputAccessor = getMethod(mainComponent, str); + if (inputAccessor == null) { + inputAccessor = langSpec.newMethodDeclaration(str, false, null, mainInputParams); + mainComponent.addMethod(inputAccessor); } else { // Add type to a parameter without type. - if (mainInput.getParameters() != null) { - for (VariableDeclaration param: mainInput.getParameters()) { + if (inputAccessor.getParameters() != null) { + for (VariableDeclaration param: inputAccessor.getParameters()) { if (param.getType() == null) { - for (VariableDeclaration p: mainParams) { + for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } @@ -385,56 +746,77 @@ } } else if (message instanceof Variable) { // Declare an input method in this component. - ArrayList resParams = new ArrayList<>(); - ArrayList mainParams = new ArrayList<>(); + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; for (Selector selector: resourceNode.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (resourceNode.getResourceHierarchy().getParent() != null) { + for (Selector selector: resourceNode.getParent().getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; } } - for (Selector selector: resourceNode.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } - if (resParams.size() == 0) { + if (resInputParams.size() == 0) { input = langSpec.newMethodDeclaration(((Variable) message).getName(), null); } else { - input = langSpec.newMethodDeclaration(((Variable) message).getName(), false, null, resParams); + input = langSpec.newMethodDeclaration(((Variable) message).getName(), false, null, resInputParams); } - component.addMethod(input); - inputMethods.add(input); - String str = ((Variable) message).getName(); - + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputMethods.add(input); + } + // Declare the accessor in the main component to call the input method. - mainInput = getMethod(mainComponent, str); - if (mainInput == null) { - if (mainParams.size() == 0) { - mainInput = langSpec.newMethodDeclaration(str, null); + String str = ((Variable) message).getName(); + inputAccessor = getMethod(mainComponent, str); + if (inputAccessor == null) { + if (mainInputParams.size() == 0) { + inputAccessor = langSpec.newMethodDeclaration(str, null); } else { - mainInput = langSpec.newMethodDeclaration(str, false, null, mainParams); + inputAccessor = langSpec.newMethodDeclaration(str, false, null, mainInputParams); } - mainComponent.addMethod(mainInput); + mainComponent.addMethod(inputAccessor); } } // Add an invocation to the accessor method. - if (mainInput != null) { + if (inputAccessor != null) { + Expression resExp = getPullAccessor().getDirectStateAccessorFor(out.getResource(), null); List args = new ArrayList<>(); - for (Selector selector: resourceNode.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - args.add(var.getName()); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); } + resExp = ((Term) resExp).getChild(0); } + String resourceAccess = resExp.toImplementation(new String[] {null}); 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()); + inputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { @@ -444,7 +826,8 @@ Expression updateExp; updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getPullAccessor()); String newState = updateExp.toImplementation(sideEffects); - if (resourceNode.getResourceHierarchy().getNumParameters() == 0) { + ResourceHierarchy resource = resourceNode.getResourceHierarchy(); + if (JavaCodeGenerator.generatesComponent(resource)) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; @@ -467,13 +850,16 @@ String newList = selector.toImplementation(sideEffects2); updateStatement = sideEffects[0] + sideEffects2[0]; } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); + Term selector = new Term(DataConstraintModel.insert); selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); + String newMap = selector.toImplementation(sideEffects2); updateStatement = sideEffects[0] + sideEffects2[0]; + } else { + String resourceName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)); + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } if (updateStatement != null) { input.addFirstStatement(updateStatement); @@ -488,14 +874,32 @@ // Add an invocation to an update method (for a chain of update method invocations). for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh; - if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).getChannel(); + boolean outsideInputResource2 = false; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy())) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + } + } + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) { for (Edge chToRes: resToCh.getDestination().getOutEdges()) { // PUSH transfer + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource())) { + in = cm; + break; + } + } Map> referredResources = new HashMap<>(); List params = new ArrayList<>(); params.add(langSpec.getFieldAccessor(fieldOfResourceState)); Set referredSet = referredResources.get(input); - for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -506,7 +910,7 @@ String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) resToCh.getSource()).getOutSideResource()); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -520,6 +924,46 @@ params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); } } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(); + for (ChannelMember out2: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java index 8f8da21..7d62432 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,11 +33,13 @@ 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); String getAssignment(); String getStatementDelimiter(); + String getStringDelimiter(); boolean isValueType(Type type); boolean isVoidType(Type type); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index b39db0e..3b7f58c 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -1,11 +1,14 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; - +import java.util.Stack; import code.ast.Block; import code.ast.CompilationUnit; @@ -26,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,206 +66,534 @@ 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 String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + 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. + if (generatesComponent(child)) { + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else { + return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } else { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); ArrayList resources = determineResourceOrder(graph); + Map resourceComponents = new HashMap<>(); + List> getters = new ArrayList<>(); + List> inputs = new ArrayList<>(); + List> fields = new ArrayList<>(); - TypeDeclaration mainType = new TypeDeclaration(mainTypeName); - CompilationUnit mainCU = new CompilationUnit(mainType); + TypeDeclaration mainComponent = new TypeDeclaration(mainTypeName); + CompilationUnit mainCU = new CompilationUnit(mainComponent); mainCU.addImport(new ImportDeclaration("java.util.*")); codes.add(mainCU); // Declare the constructor of the main type. MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); - mainType.addMethod(mainConstructor); + mainComponent.addMethod(mainConstructor); - // For each resource path. + // For each resource node. for (ResourceNode rn: resources) { - boolean f = false; - 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 (type == null) { - // Add compilation unit for each resource. - type = new TypeDeclaration(resourceName); - CompilationUnit cu = new CompilationUnit(type); - cu.addImport(new ImportDeclaration("java.util.*")); - 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() + ","; - 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 + ";"); - - // Declare a constructor, fields and update methods in the type of each resource. - MethodDeclaration constructor = new MethodDeclaration(resourceName, true); - Block block = new Block(); - depends = new HashSet<>(); - for (Edge resToCh : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) resToCh; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - // 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); - depends.add(dstRes); - type.addField(new FieldDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); - block.addStatement("this." + dstResName.toLowerCase() + " = " + dstResName.toLowerCase() + ";"); - } - } - } - for (Edge chToRes : rn.getInEdges()) { - 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); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - // Declare a field to refer to the source resource of pull transfer. - depends.add(srcRes); - type.addField(new FieldDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); - block.addStatement("this." + srcResName.toLowerCase() + " = " + srcResName.toLowerCase() + ";"); - } else { - // Declare an update method in the type of the destination resource. - ArrayList vars = new ArrayList<>(); - vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); - DataTransferChannel c = ((ChannelNode) resToCh.getDestination()).getChannel(); - for (ResourcePath ref: c.getReferenceResources()) { - if (!ref.equals(rn.getOutSideResource())) { - vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + TypeDeclaration component = null; + if (generatesComponent(rn.getResourceHierarchy())) { + boolean f = false; + String resourceName = getComponentName(rn.getResourceHierarchy()); + + component = resourceComponents.get(rn.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + resourceComponents.put(rn.getResourceHierarchy(), component); + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + codes.add(cu); + + // Declare the field to refer to each resource in the main type. + Set depends = new HashSet<>(); + Set refs = new HashSet<>(); + 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 += toVariableName(resName) + ","; + f = true; } } - type.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + 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 += toVariableName(resName) + ","; + 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(); + component.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 += toVariableName(refResName) + ","; + f = true; + } + } + } + } + if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); + fieldInitializer += ")"; + FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName()); + mainComponent.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); + Block block = new Block(); + depends = new HashSet<>(); + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + // Declare a field to refer to the destination resource of push transfer. + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + depends.add(dstRes); + component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); + constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); + block.addStatement("this." + toVariableName(dstResName) + " = " + toVariableName(dstResName) + ";"); + if (outsideOutputResource) { + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourcePath dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); + constructor.addParameter(new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); + block.addStatement("this." + toVariableName(dstRootResName) + " = " + toVariableName(dstRootResName) + ";"); + } + } + } } } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + String srcResName = null; + ResourcePath srcRes2 = srcRes; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcRes2 = srcRes.getParent(); + srcResName = getComponentName(srcRes2.getResourceHierarchy()); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // Declare a field to refer to the source resource of pull transfer. + depends.add(srcRes2); + component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + block.addStatement("this." + toVariableName(srcResName) + " = " + toVariableName(srcResName) + ";"); + if (outsideInputResource) { + if (srcRes2.getParent() != null) { + // Reference to root resource. + ResourcePath srcRootRes = srcRes2.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); + constructor.addParameter(new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); + block.addStatement("this." + toVariableName(srcRootResName) + " = " + toVariableName(srcRootResName) + ";"); + } + } + } else { + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); + DataTransferChannel c = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ResourcePath ref: c.getReferenceResources()) { + if (!ref.equals(rn.getOutSideResource())) { + vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + } + } + component.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + } + } + // Declare a field to refer to the reference resource. + 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 = getComponentName(res.getResourceHierarchy()); + component.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() + ";"); + } + } + } + } + constructor.setBody(block); + if (constructor.getParameters() != null) + component.addMethod(constructor); } - // Declare a field to refer to the reference resource. - 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(); - refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); - 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() + ";"); + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getOutSideResource(); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + component.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. + component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } } } } } - constructor.setBody(block); - if (constructor.getParameters() != null) - type.addMethod(constructor); + + // Declare the getter methods to obtain the children resources. + for (ResourceNode child: rn.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + List params = new ArrayList<>(); + int v = 1; + for (Selector param: child.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + params.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + params.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childCompName = getComponentName(child.getResourceHierarchy()); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (params.size() == 0) { + childGetter = new MethodDeclaration("get" + childCompName, childType); + } else { + childGetter = new MethodDeclaration("get" + childCompName, false, childType, params); + } + component.addMethod(childGetter); + } + } } - // Declare input methods in resources and the main type. + // Declare the state field and reference fields in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { // This logic may be incorrect. (ToDo) + // Declare a field in the parent component to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPush)); + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourcePath dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); + FieldDeclaration refRootFieldForPush = new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refRootFieldForPush)); + } + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { // This logic may be incorrect. (ToDo) + // Declare a field in the parent component to refer to the source resource of pull transfer. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPull)); + if (srcRes.getParent() != null) { + // Reference to root resource. + ResourcePath srcRootRes = srcRes.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + FieldDeclaration refRootFieldForPull = new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refRootFieldForPull)); + } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateField)); + } + } + + // Declare the getter method in this component to obtain the resource state. + if (component != null) { + // A component is created for this resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List getterParams = new ArrayList<>(); + int v = 1; + for (Selector param: rn.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String resCompName = getComponentName(rn.getResourceHierarchy()); + Type resType = getImplStateType(rn.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration("get" + resCompName, resType); + } else { + stateGetter = new MethodDeclaration("get" + resCompName, false, resType, getterParams); + } + getters.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateGetter)); + } + + // Declare the accessor method in the main type to call the getter method. + MethodDeclaration accessor = null; + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Selector param: rn.getAllSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (mainGetterParams.size() > 0) { + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + false, + getImplStateType(rn.getResourceHierarchy()), + mainGetterParams); + } else { + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + getImplStateType(rn.getResourceHierarchy())); + } + accessor.setBody(new Block()); + Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), null); + accessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + mainComponent.addMethod(accessor); + + // Declare the input method in this component and the main component. for (Channel ch : model.getIOChannels()) { for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (rn.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { // In each resource. - ArrayList resParams = new ArrayList<>(); - ArrayList mainParams = new ArrayList<>(); + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + v = 1; for (Selector selector: rn.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } - for (Selector selector: rn.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + if (rn.getParent() != null) { + for (Selector selector: rn.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; } } for (Variable var: message.getVariables().values()) { - resParams.add(new VariableDeclaration(var.getType(), var.getName())); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } MethodDeclaration input = new MethodDeclaration( ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, resParams); - type.addMethod(input); + false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); + } // In the main type. String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); - input = getMethod(mainType, str, mainParams); + input = getMethod(mainComponent, str, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, mainParams); - mainType.addMethod(input); + input = new MethodDeclaration(str, false, typeVoid, mainInputParams); + mainComponent.addMethod(input); } else { // Add type to a parameter without type. for (VariableDeclaration param: input.getParameters()) { if (param.getType() == null) { - for (VariableDeclaration p: mainParams) { + for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } @@ -271,101 +603,81 @@ } } else if (message instanceof Variable) { // In each resource. - ArrayList resParams = new ArrayList<>(); - ArrayList mainParams = new ArrayList<>(); + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + v = 1; for (Selector selector: rn.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable var = (Variable) selector.getExpression(); - resParams.add(new VariableDeclaration(var.getType(), var.getName())); + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); } + v++; } - for (Selector selector: rn.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable var = (Variable) selector.getExpression(); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); + if (rn.getResourceHierarchy().getParent() != null) { + for (Selector selector: rn.getParent().getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; } } MethodDeclaration input = null; - if (resParams.size() > 0) { + if (resInputParams.size() > 0) { input = new MethodDeclaration( ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, resParams); + false, typeVoid, resInputParams); } else { input = new MethodDeclaration( ((Variable) cm.getStateTransition().getMessageExpression()).getName(), false, typeVoid, null); } - type.addMethod(input); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); + } // In the main type. String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); - input = getMethod(mainType, str, mainParams); + input = getMethod(mainComponent, str, mainInputParams); if (input == null) { - if (mainParams.size() > 0) { - input = new MethodDeclaration(str, false, typeVoid, mainParams); + if (mainInputParams.size() > 0) { + input = new MethodDeclaration(str, false, typeVoid, mainInputParams); } else { input = new MethodDeclaration(str, false, typeVoid, null); } - mainType.addMethod(input); + mainComponent.addMethod(input); } } } } - } - - // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored() && rn.getNumberOfParameters() == 0) { - 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())); - } - } - for (Selector pathParam: rn.getAllSelectors()) { - if (pathParam.getExpression() instanceof Variable) { - Variable var = (Variable) pathParam.getExpression(); - mainParams.add(new VariableDeclaration(var.getType(), var.getName())); - } - } - 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); - } else { - getter = new MethodDeclaration("get" + rn.getResourceName().substring(0, 1).toUpperCase() - + rn.getResourceName().substring(1), - rn.getResourceStateType()); - } - 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); + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf input methods to the parent components. + for (Map.Entry entry: inputs) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); } // Declare the Pair class. @@ -544,44 +856,150 @@ static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } }; + 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); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); } - // 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 - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null) { + // root resource + String fieldName = toVariableName(typeName); + 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 (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 9087e2e..5ff0fc5 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; import code.ast.CompilationUnit; import code.ast.MethodDeclaration; @@ -19,6 +20,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; @@ -45,10 +47,10 @@ public class JavaMethodBodyGenerator { public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -62,18 +64,45 @@ ResourceNode src = (ResourceNode) resToCh.getSource(); for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); - String srcResourceName = src.getResourceName(); - String dstResourceName = dst.getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { + String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(src.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dst.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); + String srcResName = null; + if (srcComponent == null) { + srcResName = srcResourceName; + } else { + srcResName = srcComponent.getTypeName(); + } + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration update = getUpdateMethod(dstComponent, srcResName); if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) - Expression updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); + Expression updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); String[] sideEffects = new String[] {""}; String curState = updateExp.toImplementation(sideEffects); String updateStatement; @@ -88,14 +117,19 @@ } if (resToCh.getDestination().getIndegree() > 1) { // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; + String cashStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { update.addFirstStatement(cashStatement); } } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the current state stored in a field. - MethodDeclaration getter = getGetterMethod(dstType, null); + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { Type resourceType = dst.getResourceStateType(); if (dst.getResourceHierarchy().getNumParameters() == 0) { @@ -104,37 +138,38 @@ } 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 { + String[] sideEffects = new String[] {""}; if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.get); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.lookup); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } } } } // src side (for a chain of update method invocations) - for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { + ChannelMember in = null; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(src.getOutSideResource())) { + in = cm; + break; + } + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent)) { String refParams = ""; Set referredSet = referredResources.get(srcUpdate); - for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + for (ChannelMember rc: ch.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -145,7 +180,7 @@ String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource()); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -154,12 +189,12 @@ refParams += ", " + refVarName; } } - srcUpdate.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); + srcUpdate.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");"); } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { String refParams = ""; Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + for (ChannelMember rc: ch.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -170,7 +205,7 @@ String refVarName = ref.getResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getOutSideResource()); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -179,167 +214,332 @@ refParams += ", " + refVarName; } } - srcInput.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); + srcInput.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");"); } - } else { + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer - if (dst.getNumberOfParameters() == 0) { - MethodDeclaration getter = getGetterMethod(dstType, null); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - boolean isContainedPush = false; - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge chToRes2: dst.getInEdges()) { - for (Edge resToCh2: chToRes2.getSource().getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) resToCh2; - if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - isContainedPush = true; - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pushAccessor); + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + boolean isContainedPush = false; + Map inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh2; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + isContainedPush = true; + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); + } else { + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pullAccessor); + } + } + } + // for reference channel members + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer + } + String[] sideEffects = new String[] {""}; + // generate a return statement. + // An input resource is outside. + if (!isContainedPush) { + // All incoming edges are in PULL style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + // At least one incoming edge is in PUSH style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + } + if (outsideInputResource) { + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Set dependingMembers = resourcePaths.get(outsideMember).getValue(); + for (ChannelMember dependingMember: dependingMembers) { + ResourcePath dependingRes = dependingMember.getResource(); + ResourceNode dependingNode = null; + PushPullAttribute pushPull2 = null; + for (Edge resToCh2: resToCh.getDestination().getInEdges()) { + if (((ResourceNode) resToCh2.getSource()).getOutSideResource().equals(dependingRes)) { + dependingNode = (ResourceNode) resToCh2.getSource(); + pushPull2 = (PushPullAttribute) resToCh.getAttribute(); + } + } + TypeDeclaration dependingComponent = null; + if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { + String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); + dependingComponent = componentMap.get(dependingResourceName); } else { - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JavaCodeGenerator.pullAccessor); + String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy()); + dependingComponent = componentMap.get(dependingParentResourceName); + } + if (dstComponent == dependingComponent) { + // In the common parent. + if (dependingNode != null) { + // Inspect further dependency. + for (Edge chToRes2: dependingNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + if (ch2.getInputChannelMembers().size() == 0) { + // In an input method. + MethodDeclaration input = getInputMethod(dependingComponent, ch2.getOutputChannelMembers().iterator().next()); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } else { + boolean isPush = true; + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + isPush = false; + break; + } + } + if (isPush) { + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + // In an update method. + ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource(); + MethodDeclaration update = getUpdateMethod(dependingComponent, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + } + } + } + } else { + if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) { + MethodDeclaration update = getUpdateMethod(dstComponent, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } } } } - // for reference channel members - for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.pullAccessor); // by pull data transfer - } - String[] sideEffects = new String[] {""}; - // generate a return statement. - if (!isContainedPush) { - // All incoming edges are in PULL style. - String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } else { - // At least one incoming edge is in PUSH style. - String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } - } + } } - } + } } } } } } // for source nodes - String mainTypeName = JavaCodeGenerator.mainTypeName.substring(0,1).toLowerCase() + JavaCodeGenerator.mainTypeName.substring(1); - TypeDeclaration mainType = typeMap.get(mainTypeName); + TypeDeclaration mainComponent = componentMap.get(JavaCodeGenerator.mainTypeName); for (ResourceHierarchy resource: model.getResourceHierarchies()) { // ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResourceName(); - 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' + String resourceName = JavaCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (component != null) { + // state getter method + Type resourceType = JavaCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getMethod(component, "getValue"); + 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 = JavaCodeGenerator.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, 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] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } } } } - // methods for input events - Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); - for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { - Set outs = entry.getValue(); - for (ChannelMember out: outs) { - MethodDeclaration input = getInputMethod(type, out); - if (input != null) { - // In each resource - String[] sideEffects = new String[] {""}; - Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); - String newState = updateExp.toImplementation(sideEffects); - if (resource.getNumParameters() == 0) { - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JavaCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(component, 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 = JavaCodeGenerator.toVariableName(fieldName); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + } + + // methods for input events + Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); + for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { + Set outs = entry.getValue(); + for (ChannelMember out: outs) { + MethodDeclaration input = null; + if (JavaCodeGenerator.generatesComponent(resource)) { + // A component is generated for this resource. + input = getInputMethod(component, out); + } else { + // No component is generated for this resource. + ResourceHierarchy parent = resource.getParent(); + if (parent != null) { + TypeDeclaration parentType = componentMap.get(JavaCodeGenerator.getComponentName(parent)); + input = getInputMethod(parentType, out); + } + } + if (input != null) { + // In each resource + String[] sideEffects = new String[] {""}; + Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); + String newState = updateExp.toImplementation(sideEffects); + if (JavaCodeGenerator.generatesComponent(resource)) { + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + input.addFirstStatement(updateStatement); + } + } else { + String updateStatement = null; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + // ToDo. + updateStatement = sideEffects[0]; + } else { + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; } else { - updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + updateStatement = sideEffects[0] + "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; } - if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { input.addFirstStatement(updateStatement); } - } else { - String updateStatement = null; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - // ToDo. - updateStatement = sideEffects[0]; - } else { - if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); - selector.addChild(new Field("value")); - selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); - updateStatement = sideEffects[0] + sideEffects2[0]; - } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); - selector.addChild(new Field("value")); - selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); - updateStatement = sideEffects[0] + sideEffects2[0]; - } - if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { - input.addFirstStatement(updateStatement); - } - } } - - // In the main type - if (mainType != null) { - MethodDeclaration mainInput = getMethod(mainType, input.getName()); - if (mainInput != 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 = ", "; - } + } + + // In the main type + if (mainComponent != null) { + MethodDeclaration inputAccessor = getMethod(mainComponent, input.getName()); + if (inputAccessor != null) { + Expression resExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), null); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; } - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - for (Variable var: message.getVariables().values()) { - args += delimiter + var.getName(); - delimiter = ", "; - } - } - mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); + resExp = ((Term) resExp).getChild(0); } + String resourceAccess = resExp.toImplementation(new String[] {null}); + 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 + ");"); } } } @@ -353,48 +553,26 @@ return codes; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals("update" + from.getTypeName())) return m; + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String fromResName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals("update" + fromResName)) return m; } return null; } - private static List getUpdateMethods(TypeDeclaration type) { + private static List getUpdateMethods(TypeDeclaration component) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } } 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; - } - } + + private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; } return null; } @@ -420,14 +598,14 @@ return ioChannelsAndMembers; } - private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + private static List getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List inputs = new ArrayList<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (resource.getInSideResources().contains(out.getResource())) { - MethodDeclaration input = getInputMethod(type, out); + MethodDeclaration input = getInputMethod(component, out); inputs.add(input); } } @@ -435,20 +613,35 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + private static List getInputMethods(TypeDeclaration component, ResourceHierarchy resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (resource.equals(out.getResource().getResourceHierarchy())) { + MethodDeclaration input = getInputMethod(component, out); + inputs.add(input); + } + } + } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm) { MethodDeclaration input = null; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); - } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { - Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + input = getMethod(component, message.getSymbol().getImplName()); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + input = getMethod(component, message.getName()); } return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java index 5fbab32..62743b9 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"; } @@ -184,6 +199,11 @@ } @Override + public String getStringDelimiter() { + return "\""; + } + + @Override public String getTupleGet(String tupleExp, int idx, int length) { Expression t = new Variable(tupleExp, DataConstraintModel.typeTuple); for (int i = 0; i < idx; i++) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index 0cfbbce..fd97870 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -1,9 +1,14 @@ package generators; +import java.util.AbstractMap; 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 +30,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; @@ -47,6 +53,7 @@ public class JerseyCodeGenerator { public static final Type typeVoid = new Type("Void", "void"); public static final Type typeClient = new Type("Client", "Client"); + public static boolean differentTreesAsDifferentServices = true; private static String defaultMainTypeName = "Main"; static String mainTypeName = defaultMainTypeName; @@ -62,226 +69,585 @@ 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 String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + 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. + if (generatesComponent(child)) { + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else { + return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } else { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); // ArrayList resources = StoreResourceCheck(graph); Collection resources = graph.getResourceNodes(); + Map resourceComponents = new HashMap<>(); + List> getters = new ArrayList<>(); + List> inputs = new ArrayList<>(); + List> fields = new ArrayList<>(); + 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; + ResourceNode rn = (ResourceNode) n; + TypeDeclaration component = null; + if (generatesComponent(rn.getResourceHierarchy())) { + String resourceName = getComponentName(rn.getResourceHierarchy()); + + component = resourceComponents.get(rn.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root node. + component.addAnnotation(new Annotation("Component")); + component.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\"")); } - } - if (type != null) break; - } - 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() + "\"")); - - 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")); - 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()) { - 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); + resourceComponents.put(rn.getResourceHierarchy(), component); + + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + 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; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; } } - 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")); + if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + for (ChannelMember cm: ch.getOutputChannelMembers()) { + ResourcePath dstRes = cm.getResource(); + String dstResName = null; + if (generatesComponent(dstRes.getResourceHierarchy())) { + dstResName = getComponentName(dstRes.getResourceHierarchy()); } else { - update.addAnnotation(new Annotation("POST")); + dstResName = getComponentName(dstRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); } } } - 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))); + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input and output resources are outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + String srcResName = null; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcResName = getComponentName(srcRes.getResourceHierarchy().getParent()); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + } + } 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(); + component.addField(new FieldDeclaration(cashRes.getResourceStateType(), srcName, getInitializer(cashRes))); + } + component.addMethod(update); + } } - type.addMethod(update); + } + } + +// // Declare a client field to connect to the source resource of reference transfer. +// if (!bDeclareClientField) { +// for (ChannelGenerator cg : model.getChannelGenerators()) { +// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg); +// for (ChannelMember cm : dcg.getOutputChannelMembers()) { +// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) { +// if (dcg.getReferenceChannelMembers().size() > 0) { +// // If there exists one or more reference channel member. +// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); +// bDeclareClientField = true; +// break; +// } +// } +// } +// if (bDeclareClientField) break; +// } +// } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getOutSideResource(); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + component.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. + component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } + } + } + } + } + + // Declare the getter methods to obtain the children resources. + for (ResourceNode child: rn.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // The child generates a component. + 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 childCompName = getComponentName(child.getResourceHierarchy()); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (pathParams.size() == 0) { + childGetter = new MethodDeclaration("get" + childCompName, childType); + } else { + childGetter = new MethodDeclaration("get" + childCompName, false, childType, pathParams); + } + component.addMethod(childGetter); } } } -// // Declare a client field to connect to the source resource of reference transfer. -// if (!bDeclareClientField) { -// for (ChannelGenerator cg : model.getChannelGenerators()) { -// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg); -// for (ChannelMember cm : dcg.getOutputChannelMembers()) { -// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) { -// if (dcg.getReferenceChannelMembers().size() > 0) { -// // If there exists one or more reference channel member. -// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); -// bDeclareClientField = true; -// break; -// } -// } -// } -// if (bDeclareClientField) break; -// } -// } + // Declare the state field and reference fields in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean bDeclareClientField = false; + boolean noPullTransfer = true; + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + String dstResName = null; + if (generatesComponent(dstRes.getResourceHierarchy())) { + dstResName = getComponentName(dstRes.getResourceHierarchy()); + } else { + dstResName = getComponentName(dstRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPush)); + } + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { + // Declare a field in the parent component to refer to the source resource of pull transfer. + String srcResName = null; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcResName = getComponentName(srcRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), refFieldForPull)); + noPullTransfer = false; + } + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateField)); + } + } - // Declare input methods in resources. + // Declare the getter method to obtain the resource state in the component of each resource. + if (component != null) { + // A component is created for this resource. + 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")); + } + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: rn.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 resCompName = getComponentName(rn.getResourceHierarchy()); + Type resType = getImplStateType(rn.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (pathParams.size() == 0) { + stateGetter = new MethodDeclaration("get" + resCompName, resType); + } else { + stateGetter = new MethodDeclaration("get" + resCompName, false, resType, pathParams); + } + getters.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), stateGetter)); + } + + // Declare the getter accessor in the root resource. + if (rn.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(rn, mainGetterParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; + } else { + resourcePath = "\"" + resourcePath + "\""; + } + if (mainGetterParams.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + false, + getImplStateType(rn.getResourceHierarchy()), + mainGetterParams); + } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + getImplStateType(rn.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(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.getAllSelectors().size() > 0) { + getterAccessor.addAnnotation(new Annotation("Path", resourcePath)); + } + getterAccessors.put(rn.getResourceHierarchy(), getterAccessor); + } + + // Declare the input method in each resource and the root resource. for (Channel ch : model.getIOChannels()) { for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (rn.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { - 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 + "}"; - } + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getInputMethodResourcePathaAndPathParams(rn, resInputParams, rootInputParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; + } else { + resourcePath = "\"" + resourcePath + "\""; } - resourcePath += "\""; for (Variable var: message.getVariables().values()) { String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); - params.add(param); + rootInputParams.add(param); } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); + + if (rn.getResourceHierarchy().getParent() != null && rn.getResourceHierarchy().getParent().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), + false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); + } + } + + // For the root resource. + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams); 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<>(); - String resourcePath = "\""; + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + int v = 1; 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 + "}"; + resInputParams.add(param); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + } + v++; + } + if (rn.getResourceHierarchy().getParent() != null && rn.getResourceHierarchy().getParent().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Variable) cm.getStateTransition().getMessageExpression()).getName(), + false, typeVoid, null); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry(rn.getParent().getResourceHierarchy(), input)); } } - resourcePath += "\""; - MethodDeclaration input = null; - if (params.size() > 0) { - input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, params); + + // For the root resource. + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(rn, rootInputParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; } else { - input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); + resourcePath = "\"" + resourcePath + "\""; } + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams); 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) { - ResourcePath res = rn.getOutSideResource(); - type.addField(new FieldDeclaration(res.getResourceStateType(), "value", 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 + "}"; + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf input methods to the parent components. + for (Map.Entry entry: inputs) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); + } + + // Add accessors. + for (ResourceHierarchy rootRes: model.getResourceHierarchies()) { + if (rootRes.getParent() == null) { + // root resource + TypeDeclaration rootComponent = resourceComponents.get(rootRes); + // Add getter accessors. + for (ResourceHierarchy res: getterAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootComponent.addMethod(getterAccessors.get(res)); + } } - } - resourcePath += "\""; - MethodDeclaration getter = null; - if (params.size() > 0) { - getter = new MethodDeclaration("getValue", false, rn.getResourceStateType(), params); - } else { - getter = new MethodDeclaration("getValue", rn.getResourceStateType()); + // Add input accessors. + for (ResourceHierarchy res: inputAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootComponent.addMethod(inputAccessors.get(res)); + } + } } - getter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); - getter.addAnnotation(new Annotation("GET")); - if (rn.getSelectors().size() > 0) { - getter.addAnnotation(new Annotation("Path", resourcePath)); - } - type.addMethod(getter); } // Declare the Pair class. @@ -329,6 +695,79 @@ return codes; } + private static String getGetterResourcePathAndPathParams(ResourceNode rn, List pathParams) { + int v = 1; + List params = new ArrayList<>(); + for (Selector selector: rn.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + pathParams.add(param); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + pathParams.add(param); + } + v++; + } + return rn.getResourceHierarchy().toResourcePath(params); + } + + private static String getInputMethodResourcePathaAndPathParams(ResourceNode rn, ArrayList resInputParams, + ArrayList rootInputParams) { + int v = 1; + List params = new ArrayList<>(); + for (Selector selector: rn.getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + if (rn.getParent() != null) { + for (Selector selector: rn.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + } + return rn.getResourceHierarchy().toResourcePath(params); + } + private static String getInitializer(ResourcePath resId) { Type stateType = resId.getResourceStateType(); String initializer = null; @@ -391,41 +830,123 @@ static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); - } - }; - 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); - } - // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + return null; + } + }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // access from an ancestor or outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + if (fromRes != null && curPath.equals(fromRes)) break; + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the `from' resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null && fromRes == null) { + // root resource + String fieldName = toVariableName(typeName); + 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 (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index 37d6fb6..ecb3daf 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import algorithms.TypeInference; @@ -21,6 +22,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,10 +50,10 @@ public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -66,33 +68,58 @@ ResourceNode src = (ResourceNode) resToCh.getSource(); for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); - String srcResourceName = src.getResourceName(); - String dstResourceName = dst.getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: ((ChannelNode) resToCh.getDestination()).getChannel().getOutputChannelMembers()) { + String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(src.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dst.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); + MethodDeclaration update = getUpdateMethod(dstComponent, srcComponent); if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) Expression updateExp = null; - if (((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers().size() == 0) { - updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); } else { // if there exists one or more reference channel member. - HashMap inputResourceToStateAccessor = new HashMap<>(); + HashMap inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); for (Edge resToCh2: chToRes2.getSource().getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) resToCh2; - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getOutSideResource(), JerseyCodeGenerator.pushAccessor); + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } + inputResourceToStateAccessor.put(in, JerseyCodeGenerator.pushAccessor); } } - for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JerseyCodeGenerator.pullAccessor); + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.pullAccessor); } - updateExp = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor); + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor); } String[] sideEffects = new String[] {""}; String curState = updateExp.toImplementation(sideEffects); @@ -109,7 +136,7 @@ } if (resToCh.getDestination().getIndegree() > 1) { // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; + String cashStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { update.addFirstStatement(cashStatement); } @@ -180,7 +207,16 @@ } if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the state stored in a field. - MethodDeclaration getter = getGetterMethod(dstType, null); + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { if (dst.getResourceHierarchy().getNumParameters() == 0) { getter.addStatement("return value;"); @@ -206,12 +242,12 @@ } else { httpMethod = "post"; } - for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent)) { if (srcUpdate != null) { List>> params = new ArrayList<>(); Set referredSet = referredResources.get(srcUpdate); - if (((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers().size() > 0) { - for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + if (ch.getReferenceChannelMembers().size() > 0) { + for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -223,7 +259,7 @@ Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + generatePullDataTransfer(srcUpdate, refResourceName, refResourceName, refResourceType); } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); @@ -238,23 +274,23 @@ // The first call to an update method in this method // Value of the source side (input side) resource. params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); chainedCalls.add(srcUpdate); } else { // After the second time of call to update methods in this method // Value of the source side (input side) resource. params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } srcUpdate.addThrow("JsonProcessingException"); } } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { List>> params = new ArrayList<>(); Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { @@ -266,7 +302,7 @@ Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcInput, refResourceName, refResourceType); + generatePullDataTransfer(srcInput, refResourceName, refResourceName, refResourceType); } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); @@ -280,37 +316,62 @@ // First call to an update method in this method // Value of the source side (input side) resource. params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); chainedCalls.add(srcInput); } else { // After the second time of call to update methods in this method // Value of the source side (input side) resource. params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } srcInput.addThrow("JsonProcessingException"); } - } else { + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer - if (dst.getNumberOfParameters() == 0) { - MethodDeclaration getter = getGetterMethod(dstType, null); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - // generate a return statement. - String[] sideEffects = new String[] {""}; - String curState = ((ChannelNode) resToCh.getDestination()).getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); // no pull data transfer is included. - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - // For each reference channel member, get the current state of the reference side resource by pull data transfer. - for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { - String refResourceName = c.getResource().getResourceName(); - Type refResourceType = c.getResource().getResourceStateType(); - generatePullDataTransfer(getter, refResourceName, refResourceType); + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor); // no pull data transfer is included. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + String[] sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + for (ChannelMember c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + String refResourceName = c.getResource().getResourceName(); + Type refResourceType = c.getResource().getResourceStateType(); + generatePullDataTransfer(getter, refResourceName, refResourceName, refResourceType); + } + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath src2 = pathEnt.getValue().getKey(); + // get outside src resource state by pull data transfer. + if (cm.isOutside() || src2.getCommonPrefix(dst.getOutSideResource()) == null) { + Type srcResourceType = src2.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: src2.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, src2.getResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } } - // get src side resource state by pull data transfer. + } + // get src resource state by pull data transfer. + if (src.getNumberOfParameters() == 0 && src.getOutSideResource().getCommonPrefix(dst.getOutSideResource()) == null) { Type srcResourceType = src.getResourceStateType(); - generatePullDataTransfer(getter, srcResourceName, srcResourceType); + List pathParams = new ArrayList<>(); + generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } } } @@ -320,78 +381,184 @@ } // for source nodes for (ResourceHierarchy resource: model.getResourceHierarchies()) { - String resourceName = resource.getResourceName(); - 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[] {}) + ";"); + String resourceName = JerseyCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (component != null) { + // state getter method + Type resourceType = JerseyCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getMethod(component, "getValue"); + 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 = JerseyCodeGenerator.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, 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] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } } } } - // methods for input events - Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); - for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { - Set outs = entry.getValue(); - for (ChannelMember out: outs) { - MethodDeclaration input = getInputMethod(type, out); - if (input != null) { - Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); - String[] sideEffects = new String[] {""}; - String newState = updateExp.toImplementation(sideEffects); - if (resource.getNumParameters() == 0) { - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JerseyCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(component, 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 = JerseyCodeGenerator.toVariableName(fieldName); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + } + + // methods for input events + Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); + for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { + Set outs = entry.getValue(); + for (ChannelMember out: outs) { + MethodDeclaration input = null; + if (JerseyCodeGenerator.generatesComponent(resource)) { + // A component is generated for this resource. + input = getInputMethod(component, out); + } else { + // No component is generated for this resource. + ResourceHierarchy parent = resource.getParent(); + if (parent != null) { + TypeDeclaration parentType = componentMap.get(JerseyCodeGenerator.getComponentName(parent)); + input = getInputMethod(parentType, 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 (JerseyCodeGenerator.generatesComponent(resource)) { + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + input.addFirstStatement(updateStatement); + } + } else { + String updateStatement = null; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + // ToDo. + updateStatement = sideEffects[0]; + } else { + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; } else { - updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + updateStatement = sideEffects[0] + "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; } - if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { input.addFirstStatement(updateStatement); } - } else { - String updateStatement = null; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - // ToDo. - updateStatement = sideEffects[0]; - } else { - if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); - selector.addChild(new Field("value")); - selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); - updateStatement = sideEffects[0] + sideEffects2[0]; - } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); - selector.addChild(new Field("value")); - selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); - updateStatement = sideEffects[0] + sideEffects2[0]; + } + } + + if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) { + // In the root resource + MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), input.getName()); + if (inputAccessor != null) { + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; } - if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { - input.addFirstStatement(updateStatement); + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); + 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 + ");"); } } } @@ -405,7 +572,7 @@ return codes; } - private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, Type fromResourceType) { + private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType) { String varName = new String(fromResourceName); String respTypeName = fromResourceType.getInterfaceTypeName(); String respImplTypeName = fromResourceType.getImplementationTypeName(); @@ -444,7 +611,7 @@ if (respConverter.length() > 0) { methodBody.addFirstStatement(respConverter); } - methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourceName, "get", respImplTypeName)); + methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourcePath, "get", respImplTypeName)); } private static String convertFromEntryToMapType(Type type) { @@ -587,16 +754,23 @@ return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, TypeDeclaration from) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals("update" + from.getTypeName())) return m; } return null; } - private static List getUpdateMethods(TypeDeclaration type) { + private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration component) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } @@ -604,35 +778,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()) { @@ -654,14 +799,14 @@ return ioChannelsAndMembers; } - private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + private static List getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List inputs = new ArrayList<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { if (resource.getInSideResources().contains(out.getResource())) { - MethodDeclaration input = getInputMethod(type, out); + MethodDeclaration input = getInputMethod(component, out); inputs.add(input); } } @@ -669,20 +814,20 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember out) { MethodDeclaration input = null; if (out.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); + input = getMethod(component, message.getSymbol().getImplName()); } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + input = getMethod(component, message.getName()); } return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java index 440145e..cc1ffee 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java @@ -36,7 +36,7 @@ public String toImplementation(String[] sideEffects) { if (symbol.isImplGenerative()) { - String exp = symbol.generate(getType(), new String[] {}, new String[] {}, sideEffects); + String exp = symbol.generate(getType(), new String[] {}, new String[] {}, sideEffects); return exp; } return symbol.getImplName(); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java index 0a7c10b..de90a73 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java @@ -28,6 +28,14 @@ public boolean isEmpty() { return (orders == null || orders.size() == 0); } + + public boolean isAncestorOf(Position another) { + if (another.orders.size() < this.orders.size()) return false; + for (int i = 0; i < orders.size(); i++) { + if (this.orders.get(i) != another.orders.get(i)) return false; + } + return true; + } public Object clone() { return new Position((ArrayList) orders.clone()); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index a16fac5..3c15d21 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -242,7 +242,7 @@ if (getArity() == 2 && symbol.isInfix()) { return "(" + children.get(0) + symbol.toString() + children.get(1) + ")"; } - if (getArity() >= 1 && symbol.isMethod()) { + if ((getArity() >= 1 || getArity() == -1) && symbol.isMethod()) { String exp = children.get(0).toString() + "." + symbol.toString() + "("; String delimiter = ""; for (int i = 1; i < children.size(); i++) { @@ -325,9 +325,14 @@ return "(" + children.get(implParamOrder[0]).toImplementation(sideEffects) + symbol.toImplementation() + children.get(implParamOrder[1]).toImplementation(sideEffects) + ")"; } } - if (getArity() >= 1 && symbol.isImplMethod()) { + if ((getArity() >= 1 || 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/algebra/Type.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java index 50b0285..eb827bf 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java @@ -9,28 +9,28 @@ private String interfaceTypeName; private List parentTypes = new ArrayList<>(); - public Type(String typeName, String implementastionTypeName) { + public Type(String typeName, String implementationTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; } - public Type(String typeName, String implementastionTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; this.parentTypes.add(parentType); } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; this.parentTypes.add(parentType); } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index c7b96ae..a778387 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Set; +import models.algebra.Expression; import models.algebra.Variable; public class Channel { @@ -89,18 +90,19 @@ public void setChannelMembers(Set channelMembers) { this.channelMembers = channelMembers; -// for (ChannelMember channelMember: channelMembers) { -// for (Selector selector: channelMember.getSelectors()) { -// addSelector(selector); -// } -// } } public void addChannelMember(ChannelMember channelMember) { channelMembers.add(channelMember); -// for (Selector selector: channelMember.getSelectors()) { -// addSelector(selector); -// } + if (getAllSelectors().size() > 0) { + Set params = new HashSet<>(); + for (Selector s: getAllSelectors()) { + params.add(s.getExpression()); + } + if (!params.containsAll(channelMember.getResource().getPathParams())) { + channelMember.setOutside(true); + } + } } public void removeChannelMember(ResourcePath res) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java index 6be0bb3..93a3cea 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java @@ -6,10 +6,12 @@ public class ChannelMember { private ResourcePath resourcePath = null; private StateTransition stateTransition = null; + private boolean isOutside = false; public ChannelMember(ResourcePath resourcePath) { this.resourcePath = resourcePath; stateTransition = new StateTransition(); + isOutside = false; } public ResourcePath getResource() { @@ -20,19 +22,6 @@ this.resourcePath = resourcePath; } -// public List getSelectors() { -// return selectors; -// } -// -// public void setSelectors(List selectors) { -// this.selectors = selectors; -// } -// -// public ChannelMember addSelector(Selector selector) { -// selectors.add(selector); -// return this; -// } - public StateTransition getStateTransition() { return stateTransition; } @@ -41,6 +30,14 @@ this.stateTransition = stateTransition; } + public boolean isOutside() { + return this.isOutside; + } + + public void setOutside(boolean isOutside) { + this.isOutside = isOutside; + } + @Override public String toString() { if (stateTransition.getNextStateExpression() == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 79137dc..f117ef6 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -34,9 +34,10 @@ 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);; + public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX); public static final Symbol sub = new Symbol(Parser.SUB, 2, Symbol.Type.INFIX); public static final Symbol div = new Symbol(Parser.DIV, 2, Symbol.Type.INFIX); public static final Symbol minus = new Symbol(Parser.MINUS, 1); @@ -52,6 +53,7 @@ public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; if (type != null) { + String temp = "temp_nil"; String interfaceType = type.getInterfaceTypeName(); if (interfaceType.contains("<")) { compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); @@ -60,7 +62,8 @@ if (implType.indexOf('<') >= 0) { implType = implType.substring(0, implType.indexOf('<')); } - return "new " + implType + "<" + compType + ">()"; + sideEffect[0] = interfaceType + " " + temp + " = " + "new " + implType + "<" + compType + ">();\n"; + return temp; } return "new ArrayList<" + compType + ">()"; } @@ -132,7 +135,6 @@ public static final Symbol left = new Symbol("left", 1, Symbol.Type.PREFIX, "getLeft", Symbol.Type.METHOD); public static final Symbol right = new Symbol("right", 1, Symbol.Type.PREFIX, "getRight", Symbol.Type.METHOD); public static final Symbol insert = new Symbol("insert", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); -// public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD); public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { final int count[] = {0}; @Override @@ -144,12 +146,15 @@ impl += "\t" + temp + " = " + childrenImpl[0] + ".get(" + childrenImpl[1] + ");\n"; impl += "} else {\n"; impl += "\t" + temp + " = " + getDefaultValue(type) + ";\n"; - impl += "}\n"; + impl += "}"; sideEffect[0] = impl; count[0]++; return temp; } }); + public static final Symbol addMember = new Symbol("addMember", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol dot = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); + public static final Symbol dotParam = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); public static final Symbol pi = new Symbol("PI", 0, Symbol.Type.PREFIX, "Math.PI", Symbol.Type.PREFIX); public static final Symbol E = new Symbol("E", 0, Symbol.Type.PREFIX, "Math.E", Symbol.Type.PREFIX); public static final Symbol sqrt = new Symbol("sqrt", 1, Symbol.Type.PREFIX, "Math.sqrt", Symbol.Type.PREFIX); @@ -201,6 +206,9 @@ snd.setInverses(new Symbol[] {new LambdaAbstraction(new Variable("y"), new Term(tuple, new Expression[] {new Variable("x"), new Variable("y")}))}); insert.setSignature(new Type[] {typeMap, typeMap, null, null}); lookup.setSignature(new Type[] {null, typeMap, null}); + addMember.setSignature(new Type[] {typeJson, typeJson, typeString, null}); + dot.setSignature(new Type[] {null, typeJson, typeString}); + dotParam.setSignature(new Type[] {null, null, null}); pi.setSignature(new Type[] {typeDouble}); E.setSignature(new Type[] {typeDouble}); sqrt.setSignature(new Type[] {typeDouble, typeDouble}); @@ -237,6 +245,7 @@ addType(typePair); addType(typeTuple); addType(typeMap); + addType(typeJson); symbols = new HashMap<>(); addSymbol(add); addSymbol(mul); @@ -273,6 +282,9 @@ addSymbol(snd); addSymbol(insert); addSymbol(lookup); + addSymbol(addMember); + addSymbol(dot); + addSymbol(dotParam); addSymbol(pi); addSymbol(E); addSymbol(sqrt); diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..5d9c4fb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,153 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; +import models.algebra.Position; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class JsonAccessor extends Term { + + public JsonAccessor(Symbol symbol) { + super(symbol); + } + + public Type getType() { + if (symbol.equals(DataConstraintModel.dotParam)) { + Type valueType = null; + if (getChild(1) instanceof Term) { + valueType = ((Term) getChild(1)).getType(); + } else if (getChild(1) instanceof Variable) { + valueType = ((Variable) getChild(1)).getType(); + } + if (valueType != null) return valueType; + } + return super.getType(); + } + + + @Override + public Expression getInverseMap(Expression outputValue, Position targetPos) { + if (targetPos.isEmpty()) return outputValue; + targetPos = (Position) targetPos.clone(); + int i = targetPos.removeHeadOrder(); + Symbol[] inverseSymbols = symbol.getInverses(); + if (i == 0) { + if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expJson instanceof Variable) { + jsonType = (JsonType) ((Variable) expJson).getType(); + } else if (expJson instanceof Term) { + jsonType = (JsonType) ((Term) expJson).getType(); + } + String keyName = null; + if (expKey instanceof Constant) { + keyName = ((Constant) expKey).getSymbol().getName(); + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + int v = 1; + Map vars = new HashMap<>(); + Set keySet = new HashSet<>(); + if (jsonType == null || jsonType == DataConstraintModel.typeJson) { + keySet.add(keyName); + } else { + keySet.addAll(jsonType.getKeys()); + } + for (String key: keySet) { + Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) + addMemberTerm.addChild(jsonTerm); + addMemberTerm.addChild(new Constant(key)); + Variable var = new Variable("v" + v); + addMemberTerm.addChild(var); + vars.put(key, var); + jsonTerm = addMemberTerm; + v++; + } + Variable var = vars.get(keyName); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, jsonTerm); // v -> addMember(jsonTerm, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } else if (symbol.equals(DataConstraintModel.dotParam) && getChildren().size() >= 2) { + // this term is `json.{param}`. + Expression expListOrMap = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expListOrMap instanceof Variable) { + jsonType = (JsonType) ((Variable) expListOrMap).getType(); + } else if (expListOrMap instanceof Term) { + jsonType = (JsonType) ((Term) expListOrMap).getType(); + } + Type keyType = null; + if (expKey instanceof Variable) { + keyType = (JsonType) ((Variable) expKey).getType(); + } else if (expKey instanceof Term) { + keyType = (JsonType) ((Term) expKey).getType(); + } + if (jsonType != null && keyType != null) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeInt)) { + Term setElementTerm = new Term(DataConstraintModel.set); // set(list, idx, v) + setElementTerm.addChild(new Constant(DataConstraintModel.nil)); + setElementTerm.addChild(expKey); + Variable var = new Variable("v"); + setElementTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, setElementTerm); // v -> set(list, idx, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeString)) { + Term insertEntryTerm = new Term(DataConstraintModel.insert); // insert(map, key, v) + insertEntryTerm.addChild(new Constant(DataConstraintModel.nil)); + insertEntryTerm.addChild(expKey); + Variable var = new Variable("v"); + insertEntryTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, insertEntryTerm); // v -> insert(map, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } + } + } + if (inverseSymbols == null || i >= inverseSymbols.length || inverseSymbols[i] == null) return null; + Term inverseMap = new Term(inverseSymbols[i]); + inverseMap.addChild(outputValue); + for (int n = 0; n < inverseSymbols[i].getArity(); n++) { + if (n != i) { + inverseMap.addChild(children.get(n)); + } + } + return children.get(i).getInverseMap(inverseMap, targetPos); + } + + public String toString() { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toString() + symbol.toString() + "{" + children.get(1).toString() + "}"; + } + return super.toString(); + } + + public String toImplementation(String[] sideEffects) { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toImplementation(sideEffects) + symbol.toImplementation() + "{" + children.get(1).toImplementation(sideEffects) + "}"; + } + return super.toImplementation(sideEffects); + } + + @Override + public Object clone() { + JsonAccessor newTerm = new JsonAccessor(symbol); + for (Expression e: children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.type = type; + return newTerm; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java new file mode 100644 index 0000000..9f9e835 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java @@ -0,0 +1,65 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Type; + +public class JsonType extends Type { + protected boolean isListOrMap = false; + protected Map memberTypes = null; + protected Type listOrMapElementType = null; + + public JsonType(String typeName, String implementationTypeName) { + super(typeName, implementationTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName) { + super(typeName, implementationTypeName, interfaceTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, Type parentType) { + super(typeName, implementationTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { + super(typeName, implementationTypeName, interfaceTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public Map getMemberTypes() { + return memberTypes; + } + + public Type getMemberType(String key) { + return memberTypes.get(key); + } + + public Set getKeys() { + return memberTypes.keySet(); + } + + public void addMemberType(String key, Type valueType) { + memberTypes.put(key, valueType); + } + +// public boolean isListOrMap() { +// return isListOrMap; +// } +// +// public void setListOrMap(boolean isListOrMap) { +// this.isListOrMap = isListOrMap; +// } + + public Type getElementType() { + return listOrMapElementType; + } + + public void setElementType(Type listElementType) { + this.listOrMapElementType = listElementType; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java index 7e0ca88..7412266 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -1,13 +1,17 @@ package models.dataConstraintModel; +import java.util.HashSet; import java.util.List; +import java.util.Set; import models.algebra.Expression; import models.algebra.Term; import models.algebra.Type; +import models.algebra.Variable; public class ResourceHierarchy { private ResourceHierarchy parent = null; + private Set children = null; private String resourceName = null; private Type resourceStateType = null; private int numParameters = 0; @@ -16,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; @@ -29,21 +35,87 @@ public ResourceHierarchy(ResourceHierarchy parent, String resourceName) { this.parent = parent; + this.children = new HashSet<>(); this.resourceName = resourceName; this.numParameters = 0; + 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.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.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); + } + } + } + } + } + + public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp, Type resourceStateType) { + this.parent = parent; + this.resourceName = null; + this.numParameters = 1; + this.resourceStateType = resourceStateType; + 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); + } + } + } } public ResourceHierarchy(ResourceHierarchy parent, int numParameters, Type resourceStateType) { @@ -51,6 +123,9 @@ this.resourceName = null; this.numParameters = numParameters; this.resourceStateType = resourceStateType; + if (parent != null) { + parent.addChild(this); + } } public ResourceHierarchy getParent() { @@ -59,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() { @@ -131,4 +228,15 @@ return parent.toString(pathParams) + ".{" + lastParam +"}"; } } + + public String toResourcePath(List pathParams) { + if (parent == null) return resourceName; + if (resourceName != null) { + return parent.toResourcePath(pathParams) + "/" + resourceName; + } else { + String lastParam = pathParams.get(pathParams.size() - 1); + pathParams = pathParams.subList(0, pathParams.size() - 1); + return parent.toResourcePath(pathParams) + "/" + lastParam; + } + } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index cc427ef..f4d0e19 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -32,19 +32,17 @@ public ResourcePath(ResourcePath parent, Expression exp) { super(parent.toString() + ".{" + exp + "}"); this.parent = parent; - this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), 1); + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), exp); this.pathParams = new ArrayList<>(parent.getPathParams()); this.pathParams.add(exp); } - public ResourcePath(ResourcePath parent, List pathParams) { - super(parent.toString() + ".{" + pathParams.toString() + "}"); - this.parent = parent; - this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), pathParams.size()); - this.pathParams = new ArrayList<>(parent.getPathParams()); - this.pathParams.addAll(pathParams); + public ResourcePath(ResourcePath another) { + super(another.name); + this.parent = another.parent; + this.resourceHierarchy = another.resourceHierarchy; + this.pathParams = new ArrayList<>(another.getPathParams()); } - public ResourceHierarchy getResourceHierarchy() { return resourceHierarchy; @@ -82,9 +80,26 @@ pathParams.add(pathParam); } + public boolean endsWithParam() { + if (resourceHierarchy.getNumParameters() > 0) return true; + return false; + } + + public Expression getLastParam() { + if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1); + } + return null; + } + 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<>(); @@ -103,13 +118,4 @@ public String toString() { return resourceHierarchy.toString(pathParams); } -// -// public boolean equals(Object another) { -// if (!(another instanceof ResourcePath)) return false; -// return toString().equals(((ResourcePath) another).toString()); -// } -// -// public int hashCode() { -// return toString().hashCode(); -// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java index 065525c..0186360 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; +import java.util.Set; import models.algebra.Constant; import models.algebra.Expression; @@ -49,7 +50,7 @@ return true; } - public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -104,24 +105,10 @@ } Expression messageTerm = getMessageExpression(); - if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) throw new InvalidMessage(); - HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { - if (bindings.get(var) != null) { - if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); - if (messageTerm instanceof Term) { - messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); - } else if (messageTerm instanceof Variable) { - if (messageTerm.equals(var)) { - return bindings.get(var).iterator().next(); - } - } - } - } - return messageTerm; + return substituteValues(messageTerm, bindings, substitutedPositions); } - public Expression deriveMessageConstraintFor(Expression curStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -144,15 +131,23 @@ } Expression messageTerm = getMessageExpression(); + return substituteValues(messageTerm, bindings, substitutedPositions); + } + + private Expression substituteValues(Expression messageTerm, HashMap> bindings, Set substitutedPositions) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) throw new InvalidMessage(); HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { + for (Entry varEnt: messageVars.entrySet()) { + Variable var = varEnt.getValue(); if (bindings.get(var) != null) { if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); if (messageTerm instanceof Term) { + substitutedPositions.add(varEnt.getKey()); messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); } else if (messageTerm instanceof Variable) { if (messageTerm.equals(var)) { + substitutedPositions.add(new Position()); return bindings.get(var).iterator().next(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index ad09c92..ce8c581 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -1,10 +1,15 @@ package models.dataFlowModel; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Map.Entry; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.Parameter; @@ -50,7 +55,7 @@ this.inputChannelMembers = inputChannelMembers; } - public void addInputChannelMember(ChannelMember inputChannelMember) { + private void addInputChannelMember(ChannelMember inputChannelMember) { inputChannelMembers.add(inputChannelMember); } @@ -62,7 +67,7 @@ this.outputChannelMembers = outputChannelMembers; } - public void addOutputChannelMember(ChannelMember outputChannelMember) { + private void addOutputChannelMember(ChannelMember outputChannelMember) { outputChannelMembers.add(outputChannelMember); } @@ -74,7 +79,7 @@ this.referenceChannelMembers = referenceChannelMembers; } - public void addReferenceChannelMember(ChannelMember referenceChannelMember) { + private void addReferenceChannelMember(ChannelMember referenceChannelMember) { referenceChannelMembers.add(referenceChannelMember); } @@ -157,8 +162,8 @@ HashMap nextStateParams = new HashMap<>(); @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getResourceName(); Parameter curStateParam = curStateParams.get(resource); if (curStateParam == null) { curStateParam = new Parameter("cur" + resource); @@ -168,24 +173,35 @@ } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getResourceName(); Parameter nextStateParam = nextStateParams.get(resource); if (nextStateParam == null) { - nextStateParam = new Parameter("next" + resource); + nextStateParam = new Parameter("next" + target); nextStateParams.put(resource, nextStateParam); } return nextStateParam; } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) { + String resource = target.getResourceName(); + Parameter curStateParam = curStateParams.get(resource); + if (curStateParam == null) { + curStateParam = new Parameter("cur" + resource); + curStateParams.put(resource, curStateParam); + } + return curStateParam; + } }; return deriveUpdateExpressionOf(targetMember, defaultStateAccessor); } /** - * Derive the update expression of the state of the target channel member with a given resource state accessor. + * Derive the state update calculation of the target channel member with a given resource push/pull state accessor. * @param targetMember a channel member whose state is to be updated - * @param stateAccessor a resource state accessor - * @return the derived update expression + * @param stateAccessor a push/pull resource state accessor + * @return the derived update calculation * @throws ParameterizedIdentifierIsFutureWork * @throws ResolvingMultipleDefinitionIsFutureWork * @throws InvalidMessage @@ -197,37 +213,146 @@ return deriveUpdateExpressionOf(targetMember, stateAccessor, null); } - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, HashMap inputResourceToStateAccessor) + public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { if (!getOutputChannelMembers().contains(targetMember)) return null; - HashSet messageConstraints = new HashSet<>(); + + // Calculate unified message constraints + Map> substitutedPositionsFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsFromChannels); + + // Calculate the next state of target resource from the unified message and the current resource state + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetMember, targetMember); + if (unifiedMessage == null) { + // for IOChannel + if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { + unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + } + } + return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage); + } + + /** + * Fill outside resource paths with a given resource push/pull state accessor. + * @param stateAccessor a push/pull resource state accessor + * @param resourcePaths a container to be returned with filled outside resource paths + * @return the derived update calculation + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + return fillOutsideResourcePaths(targetMember, stateAccessor, null); + } + + public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + if (!getOutputChannelMembers().contains(targetMember)) return null; + Map>> resourcePaths = new HashMap<>(); + + // Calculate unified message constraints from input and reference state transitions + Map> substitutedPositionsFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsFromChannels); + + // Fill outside resource paths + if (unifiedMessage != null) { + for (ChannelMember cm: getInputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getReferenceChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getOutputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + } + return resourcePaths; + } + + private Term calcUnifiedMessage(ChannelMember targetMember, IResourceStateAccessor stateAccessor, + Map inputResourceToStateAccessor, Map> substitutedPositionsFromChannels) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed { + Set messageConstraints = new HashSet<>(); // Calculate message constraints from input state transitions for (ChannelMember inputMember: getInputChannelMembers()) { - ResourcePath inputResource = inputMember.getResource(); Expression curInputStateAccessor = null; Expression nextInputStateAccessor = null; if (inputResourceToStateAccessor == null) { - curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(inputResource, targetMember.getResource()); - nextInputStateAccessor = stateAccessor.getNextStateAccessorFor(inputResource, targetMember.getResource()); + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = stateAccessor.getNextStateAccessorFor(inputMember, targetMember); } else { - curInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getCurrentStateAccessorFor(inputResource, targetMember.getResource()); - nextInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getNextStateAccessorFor(inputResource, targetMember.getResource()); + curInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getNextStateAccessorFor(inputMember, targetMember); } - Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor); + Set substitutedPositions = new HashSet<>(); + Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor, substitutedPositions); + if (substitutedPositions.size() > 0) substitutedPositionsFromChannels.put(inputMember, substitutedPositions); messageConstraints.add((Term) messageConstraintByInput); } // Calculate message constraints from reference state transitions for (ChannelMember referenceMember: getReferenceChannelMembers()) { - ResourcePath referenceResource = referenceMember.getResource(); Expression curInputStateAccessor = null; if (inputResourceToStateAccessor == null) { - curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceMember, targetMember); } else { - curInputStateAccessor = inputResourceToStateAccessor.get(referenceResource).getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); + curInputStateAccessor = inputResourceToStateAccessor.get(referenceMember).getCurrentStateAccessorFor(referenceMember, targetMember); } - Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor); + Set substitutedPositions = new HashSet<>(); + Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, substitutedPositions); + if (substitutedPositions.size() > 0) substitutedPositionsFromChannels.put(referenceMember, substitutedPositions); messageConstraints.add((Term) messageConstraintByReference); } @@ -243,17 +368,46 @@ } } } + return unifiedMessage; + } + + private ResourcePath fillOutsideResourcePath(ResourcePath resource, Term unifiedMessage, Expression messageTerm, Set dependingVarPos) + throws ResolvingMultipleDefinitionIsFutureWork { + ResourcePath filledResourcePath = new ResourcePath(resource); - // Calculate the next state of target resource from the unified message and the current resource state - ResourcePath targetResource = targetMember.getResource(); - Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetResource, targetResource); - if (unifiedMessage == null) { - // for IOChannel - if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { - unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + Map> bindings = new HashMap<>(); + Map messageVars = messageTerm.getVariables(); + for (Entry messageVarEnt: messageVars.entrySet()) { + Variable var = messageVarEnt.getValue(); + Position varPos = messageVarEnt.getKey(); + Expression valueCalc = unifiedMessage.getSubTerm(varPos); + if (valueCalc != null) { + if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); + bindings.put(var, new AbstractMap.SimpleEntry(varPos, valueCalc)); } } - return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage); + + List dstParams = filledResourcePath.getPathParams(); + for (int i = 0; i < filledResourcePath.getPathParams().size(); i++) { + Expression pathParam = dstParams.get(i); + if (pathParam instanceof Variable) { + dstParams.set(i, bindings.get((Variable) pathParam).getValue()); + dependingVarPos.add(bindings.get((Variable) pathParam).getKey()); + } else { + Map pathParamVars = ((Term) pathParam).getVariables(); + for (Variable var: bindings.keySet()) { + if (pathParamVars.values().contains(var)) { + pathParam = ((Term) pathParam).substitute(var, bindings.get(var).getValue()); + dependingVarPos.add(bindings.get((Variable) var).getKey()); + } + } + if (!(pathParam instanceof Constant)) { + pathParam = ((Term) pathParam).reduce(); + } + dstParams.set(i, pathParam); + } + } + return filledResourcePath; } @Override @@ -273,7 +427,8 @@ } public interface IResourceStateAccessor { - Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from); - Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from); + Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from); + Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from); + Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes); } } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index bf8d96d..022588b 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -1,6 +1,5 @@ package parser; -import java.awt.image.DataBufferDouble; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; @@ -14,6 +13,8 @@ import models.algebra.Type; import models.algebra.Variable; import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.StateTransition; @@ -23,6 +24,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -30,6 +32,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -51,6 +54,10 @@ public static final String RIGHT_BRACKET = ")"; public static final String LEFT_BRACKET_REGX = "\\("; public static final String RIGHT_BRACKET_REGX = "\\)"; + public static final String LEFT_SQUARE_BRACKET = "["; + public static final String RIGHT_SQUARE_BRACKET = "]"; + public static final String LEFT_SQUARE_BRACKET_REGX = "\\["; + public static final String RIGHT_SQUARE_BRACKET_REGX = "\\]"; public static final String ADD = "+"; public static final String MUL = "*"; public static final String SUB = "-"; @@ -87,14 +94,14 @@ public DataTransferModel doParse() throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression { + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon { return parseDataFlowModel(); } public DataTransferModel parseDataFlowModel() throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression { + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon { DataTransferModel model = new DataTransferModel(); DataTransferChannel channel; while ((channel = parseChannel(model)) != null) { @@ -111,7 +118,7 @@ throws ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefOrSubKeyword, - ExpectedStateTransition, ExpectedEquals, WrongPathExpression { + ExpectedStateTransition, ExpectedEquals, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) return null; if (stream.checkNext().equals(RIGHT_CURLY_BRACKET)) return null; @@ -178,7 +185,7 @@ public void parseInit(DataTransferModel model) throws ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression, - ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { + ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { String leftBracket = stream.next(); if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); while (stream.hasNext() && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { @@ -205,7 +212,7 @@ public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef) throws ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedStateTransition, ExpectedEquals, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression { + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) throw new ExpectedStateTransition(stream.getLine()); StateTransitionTerm leftTerm = parseStateTransitionTerm(stream, model); if (leftTerm == null || !(leftTerm instanceof Term)) throw new WrongLHSExpression(stream.getLine()); @@ -252,7 +259,7 @@ return channelMember; } - public Expression parseTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket { + public Expression parseTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { ArrayList expressions = new ArrayList<>(); ArrayList operators = new ArrayList<>(); String operator = null; @@ -263,6 +270,16 @@ String rightBracket = stream.next(); if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_CURLY_BRACKET)) { + Expression exp = parseJsonTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_SQUARE_BRACKET)) { + Expression exp = parseListTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_SQUARE_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); } else { Symbol minus = null; String symbolName = null; @@ -294,18 +311,19 @@ symbol.setArity(arity); exp = term; } else { - // constant or variable - try { - if (stream.checkNext() != null && stream.checkNext().equals(DOT)) { - // Because tokens are separated by a DOT. - stream.next(); - symbolName += DOT + stream.next(); // decimal fraction - } - Symbol symbol = model.getSymbol(symbolName); - if (symbol != null && symbol.getArity() == 0) { - // a constant - exp = new Constant(symbol); - } else { + // constant or variable or json access + Symbol symbol = model.getSymbol(symbolName); + if (symbol != null && symbol.getArity() == 0) { + // a constant + exp = new Constant(symbol); + } else { + if (Character.isDigit(symbolName.charAt(0))) { + // maybe a numerical value + if (stream.checkNext() != null && stream.checkNext().equals(DOT)) { + // Because tokens are separated by a DOT. + stream.next(); + symbolName += DOT + stream.next(); // decimal fraction + } Double d = Double.parseDouble(symbolName); // a numerical value if (symbolName.contains(DOT)) { @@ -313,10 +331,10 @@ } else { exp = new Constant(symbolName, DataTransferModel.typeInt); } + } else { + // a variable + exp = parseVariable(stream, model, symbolName); } - } catch (NumberFormatException e) { - // a variable - exp = parseVariable(stream, model, symbolName); } } if (minus != null) { @@ -332,16 +350,52 @@ break; } else if (operator.equals(ADD)) { operators.add(DataTransferModel.add); + stream.next(); } else if (operator.equals(MUL)) { operators.add(DataTransferModel.mul); + stream.next(); } else if (operator.equals(SUB)) { operators.add(DataTransferModel.sub); // not minus + stream.next(); } else if (operator.equals(DIV)) { operators.add(DataTransferModel.div); + stream.next(); + } else if (operator.equals(DOT)) { + // json accessor + Expression exp = expressions.remove(0); + stream.next(); // DOT + if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine()); + String literalOrLeftCurlyBracket = stream.next(); + Expression paramTerm = null; + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // parameter + paramTerm = parseTerm(stream, model); + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new WrongJsonExpression(stream.getLine()); + } else { + // literal + paramTerm = new Constant(literalOrLeftCurlyBracket, DataTransferModel.typeString); + } + Type paramType = null; + if (paramTerm instanceof Variable) { + paramType = ((Variable) paramTerm).getType(); + } else if (paramTerm instanceof Term) { + paramType = ((Term) paramTerm).getType(); + } + Term term = null; + if (paramType != null && DataConstraintModel.typeInt.isAncestorOf(paramType)) { + term = new JsonAccessor(DataConstraintModel.dotParam); + } else { + term = new JsonAccessor(DataConstraintModel.dot); + } + term.addChild(exp); + term.addChild(paramTerm); + expressions.add(term); + operator = stream.checkNext(); + if (operator == null || !operator.equals(DOT)) break; } else { break; } - stream.next(); // an arithmetic operator } if (expressions.size() == 1) { // no arithmetic operators @@ -380,6 +434,42 @@ return firstMonomial; } + private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { + String key = stream.next(); + Constant keyExp = new Constant(key); + keyExp.setType(DataConstraintModel.typeString); + if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine()); + String colon = stream.next(); + Expression value = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.addMember); + nextTerm.addChild(jsonTerm); + nextTerm.addChild(keyExp); + nextTerm.addChild(value); + jsonTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return jsonTerm; + } + + private Expression parseListTerm(TokenStream stream2, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term listTerm = new Constant(DataConstraintModel.nil); + listTerm.setType(DataConstraintModel.typeList); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { + Expression element = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.cons); + nextTerm.addChild(element); + nextTerm.addChild(listTerm); + listTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return listTerm; + } + private Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { Variable var; if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { @@ -397,7 +487,8 @@ return var; } - public StateTransitionTerm parseStateTransitionTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { + public StateTransitionTerm parseStateTransitionTerm(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { ResourcePath resourcePath = parseResourcePath(stream, model); StateTransitionTerm term = new StateTransitionTerm(resourcePath); int arity = 0; @@ -414,7 +505,8 @@ return term; } - public ResourceHierarchy parseResourceHierarchy(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { + public ResourceHierarchy parseResourceHierarchy(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { ResourceHierarchy hierarchy = null; do { String literalOrLeftCurlyBracket = stream.next(); @@ -433,18 +525,17 @@ return hierarchy; } - public ResourcePath parseResourcePath(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { + public ResourcePath parseResourcePath(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { ResourcePath path = null; - List pathParams = new ArrayList<>(); do { String literalOrLeftCurlyBracket = stream.next(); if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { // Path parameter Expression paramTerm = parseTerm(stream, model); - pathParams.add(paramTerm); String rightCurlyBracket = stream.next(); if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine()); - path = new ResourcePath(path, pathParams); + path = new ResourcePath(path, paramTerm); } else { // Path literal if (path == null) { @@ -493,31 +584,37 @@ splitBy( splitBy( splitBy( - Arrays.asList(line.split("[ \t]")), - ADD, - ADD_REGX), - MUL, - MUL_REGX), - SUB, - SUB_REGX), - DIV, - DIV_REGX), - DOT, - DOT_REGX), - COMMA, - COMMA), - COLON, - COLON), - LEFT_BRACKET, - LEFT_BRACKET_REGX), - RIGHT_BRACKET, - RIGHT_BRACKET_REGX), - EQUALS, - EQUALS), - LEFT_CURLY_BRACKET, - LEFT_CURLY_BRACKET_REGX), - RIGHT_CURLY_BRACKET, - RIGHT_CURLY_BRACKET_REGX)); + splitBy( + splitBy( + Arrays.asList(line.split("[ \t]")), + ADD, + ADD_REGX), + MUL, + MUL_REGX), + SUB, + SUB_REGX), + DIV, + DIV_REGX), + DOT, + DOT_REGX), + COMMA, + COMMA), + COLON, + COLON), + LEFT_BRACKET, + LEFT_BRACKET_REGX), + RIGHT_BRACKET, + RIGHT_BRACKET_REGX), + EQUALS, + EQUALS), + LEFT_CURLY_BRACKET, + LEFT_CURLY_BRACKET_REGX), + RIGHT_CURLY_BRACKET, + RIGHT_CURLY_BRACKET_REGX), + LEFT_SQUARE_BRACKET, + LEFT_SQUARE_BRACKET_REGX), + RIGHT_SQUARE_BRACKET, + RIGHT_SQUARE_BRACKET_REGX)); } private ArrayList splitBy(final List tokens, final String delimiter, final String delimiterRegx) { diff --git a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java index 2c7848c..6ea7dec 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java @@ -14,6 +14,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; @@ -27,6 +28,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -63,9 +65,11 @@ /**-------------------------------------------------------------------------------- * * @param reader + * @throws WrongJsonExpression + * @throws ExpectedColon */ public DataTransferModel doParseModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { DataTransferModel model = getParsedModel(); return model; } @@ -85,9 +89,11 @@ /**-------------------------------------------------------------------------------- * * @param stream + * @throws WrongJsonExpression + * @throws ExpectedColon */ private DataTransferModel getParsedModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) throw new NullPointerException(); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java new file mode 100644 index 0000000..426aaf1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java @@ -0,0 +1,8 @@ +package parser.exceptions; + +public class ExpectedColon extends ParseException { + + public ExpectedColon(int line) { + super(line); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java new file mode 100644 index 0000000..6a84d6f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongJsonExpression extends ParseException { + + public WrongJsonExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java index 8cca610..b318c30 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java @@ -17,6 +17,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -24,6 +25,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -43,7 +45,7 @@ System.out.println(codetree); } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java index 7326b58..b228862 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -13,6 +13,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -20,6 +21,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -41,7 +43,7 @@ } } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java index 5a4eb65..73eff57 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -12,6 +12,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -19,6 +20,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -41,7 +43,7 @@ } } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java index afd9e6d..c3b11ab 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java @@ -12,6 +12,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -19,6 +20,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -40,7 +42,7 @@ } } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java index 85644fc..165013e 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java @@ -16,10 +16,14 @@ import models.algebra.Position; import models.algebra.Term; import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; import models.dataFlowModel.DataTransferModel; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; public class InverseTest { @Test @@ -75,7 +79,7 @@ HashMap rhsVars3 = rhsExp3.getVariables(); for (Position vPos: rhsVars3.keySet()) { Variable v = rhsVars3.get(vPos); - Expression inv = rhsExp3.getInverseMap(z, vPos); // inverse map to get v back from the output value y + Expression inv = rhsExp3.getInverseMap(z, vPos); // inverse map to get v back from the output value z if (inv instanceof Term) { inv = ((Term) inv).reduce(); } @@ -83,7 +87,31 @@ assertFalse(inv.contains(v)); System.out.println(rhsVars3.get(vPos) + " = " + inv); } - } catch (ExpectedRightBracket e) { + + // Solve {z = x.id} for x + TokenStream stream4 = new Parser.TokenStream(); + Parser parser4 = new Parser(stream4); + stream4.addLine("x.id"); + Expression rhsExp4 = parser4.parseTerm(stream4, model); + System.out.println("=== solve{" + z + " = " + rhsExp4 + "} for x ==="); + HashMap rhsVars4 = rhsExp4.getVariables(); + for (Position vPos: rhsVars4.keySet()) { + Variable v = rhsVars4.get(vPos); + if (x.getName().equals("x")) { + JsonType jsonType = new JsonType("Json", "HashMap<>", DataConstraintModel.typeJson); + jsonType.addMemberType("id", DataConstraintModel.typeInt); + jsonType.addMemberType("name", DataConstraintModel.typeString); + v.setType(jsonType); + } + Expression inv = rhsExp4.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + System.out.println(rhsVars4.get(vPos) + " = " + inv); + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + } + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java index bdb21e6..92c754d 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java @@ -46,21 +46,28 @@ final Parameter pTotal = new Parameter("total", DataConstraintModel.typeInt); IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(points)) return fPoints; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) return pPayment; - if (target.equals(points)) return pPoints; - if (target.equals(history)) return pHistory; - if (target.equals(total)) return pTotal; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) return pPayment; + if (targetRes.equals(points)) return pPoints; + if (targetRes.equals(history)) return pHistory; + if (targetRes.equals(total)) return pTotal; + return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { return null; } }; @@ -72,38 +79,45 @@ final Symbol totalGetter = new Symbol("getTotal", 1, Symbol.Type.METHOD); IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(points)) return fPoints; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) { + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) { Term getter = new Term(paymentGetter); getter.addChild(fPayment); return getter; } - if (target.equals(points)) { + if (targetRes.equals(points)) { Term getter = new Term(pointsGetter); getter.addChild(fPoints); return getter; } - if (target.equals(history)) { + if (targetRes.equals(history)) { Term getter = new Term(historyGetter); getter.addChild(fHistory); return getter; } - if (target.equals(total)) { + if (targetRes.equals(total)) { Term getter = new Term(totalGetter); getter.addChild(fTotal); return getter; } return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + return null; } }; diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java index 112b4d6..c507ae9 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -11,6 +11,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -18,6 +19,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -34,7 +36,7 @@ } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedRightCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment - | WrongPathExpression e) { + | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java index 0076e51..93ef4e8 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java @@ -17,6 +17,7 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; @@ -24,6 +25,7 @@ import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; @@ -56,7 +58,7 @@ | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage - | UnificationFailed | ValueUndefined | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression e) { + | UnificationFailed | ValueUndefined | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) {