diff --git a/AlgebraicDataflowArchitectureModel/models/Twitter.model b/AlgebraicDataflowArchitectureModel/models/Twitter.model index 596ad7b..fe64776 100644 --- a/AlgebraicDataflowArchitectureModel/models/Twitter.model +++ b/AlgebraicDataflowArchitectureModel/models/Twitter.model @@ -1,16 +1,20 @@ -channel CIO1(myId:Str) { - out accounts(ac:List, signup(name:Str)) = cons(tuple(name, nil, nil), t1) +channel SignUp { + out accounts(acDB:Map, signup(myId:Str, name:Str)) = insert(acDB, myId, {"name": name, "tweets": nil, "followees": nil, "timeline": nil}) } -channel CIO2(id:Str) { - out accounts.{id}.tweets(t1:List, tweet(text:Str, time:Long)) = cons(tuple(time, text), t1) +channel Tweet(id:Str) { + out accounts.{id}.tweets(twList:List, tweet(text:Str, time:Long)) = append(twList, {"time": time, "text": text}) } -channel C(myId:Str) { +channel AddFollowee(id:Str) { + out accounts.{id}.followees(fwList:List, addFollowee(flwId:Str)) = append(fwList, flwId) +} + +channel Timeline(myId:Str) { in accounts.{myId}.tweets(t1:List, m) = m.myTweets sub C2(no:Int) { in accounts.{myId}.followees.{no}(id:Str, m) = m.flw.{no}.id - in accounts.{flw.id}.tweets(t2:List, m) = m.flw.{no}.tweets + in accounts.{m.flw.{no}.id}.tweets(t2:List, m) = m.flw.{no}.tweets } out accounts.{myId}.timeline(l:List, m) = merge(m.myTweets, m.flw) } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java index e7cadb5..26a4069 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -54,11 +54,17 @@ if (json instanceof JsonTerm) { return ((JsonTerm) json).get(key); } - if (!json.getSymbol().equals(DataConstraintModel.addMember)) return null; - if (json.getChild(1).equals(key)) { - return json.getChild(2); + if (!json.getSymbol().equals(DataConstraintModel.addMember)) { + return new Constant(DataConstraintModel.null_); } - if (json.getChild(0) == null || json.getChild(0).equals(DataConstraintModel.nil)) return null; + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null || (json.getChild(0) instanceof Term && ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_))) return null; return getValue((Term) json.getChild(0), key); } @@ -73,11 +79,11 @@ // this term is `json.key`. Expression expJson = getChild(0); Expression expKey = getChild(1); - JsonType jsonType = null; + Type jsonType = null; if (expJson instanceof Variable) { - jsonType = (JsonType) ((Variable) expJson).getType(); + jsonType = ((Variable) expJson).getType(); } else if (expJson instanceof Term) { - jsonType = (JsonType) ((Term) expJson).getType(); + jsonType = ((Term) expJson).getType(); } String keyName = null; if (expKey instanceof Constant) { @@ -89,8 +95,8 @@ Set keySet = new HashSet<>(); if (jsonType == null || jsonType == DataConstraintModel.typeJson) { keySet.add(keyName); - } else { - keySet.addAll(jsonType.getKeys()); + } else if (jsonType instanceof JsonType) { + keySet.addAll(((JsonType) jsonType).getKeys()); } for (String key: keySet) { Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java index 5762940..8ccdfd5 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -1,6 +1,7 @@ package models.dataConstraintModel; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -42,6 +43,30 @@ } @Override + public Expression unify(Expression another) { + if (another instanceof JsonTerm) { + JsonTerm anotherTerm = (JsonTerm) another; + JsonTerm unifiedTerm = new JsonTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key: keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.addMember(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.addMember(key, this.get(key)); + } + } else { + unifiedTerm.addMember(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } + + @Override public Object clone() { JsonTerm newTerm = new JsonTerm(); for (Expression e: children) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java index 91dd47d..afc4212 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java @@ -1,7 +1,5 @@ package models.dataConstraintModel; -import java.util.HashMap; - import models.algebra.Expression; import models.algebra.Symbol; import models.algebra.Term; @@ -26,6 +24,20 @@ } @Override + public Expression unify(Expression another) { + if (another instanceof ListTerm) { + ListTerm anotherTerm = (ListTerm) another; + if (children.size() != anotherTerm.children.size()) return null; + ListTerm unifiedTerm = new ListTerm(); + for (int i = 0; i < children.size(); i++) { + unifiedTerm.addChild(children.get(i).unify(anotherTerm.children.get(i))); + } + return unifiedTerm; + } + return this; + } + + @Override public Object clone() { ListTerm newTerm = new ListTerm(); for (Expression e: children) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java index 0badbd1..18f2c9b 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java @@ -1,6 +1,7 @@ package models.dataConstraintModel; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -41,6 +42,30 @@ } @Override + public Expression unify(Expression another) { + if (another instanceof MapTerm) { + MapTerm anotherTerm = (MapTerm) another; + MapTerm unifiedTerm = new MapTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key: keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.insert(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.insert(key, this.get(key)); + } + } else { + unifiedTerm.insert(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } + + @Override public Object clone() { MapTerm newTerm = new MapTerm(); for (Expression e: children) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index 9f68e45..caef1b0 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -532,7 +532,7 @@ return channelMembersMessageDependsOn; } - public ResourcePath fillOutsideResourcePath(ResourcePath resource, Term unifiedMessage, Expression messageTerm, Set dependingVarPosInMessage) + public ResourcePath fillOutsideResourcePath(ResourcePath resource, Expression unifiedMessage, Expression messageTerm, Set dependingVarPosInMessage) throws ResolvingMultipleDefinitionIsFutureWork { ResourcePath filledResourcePath = new ResourcePath(resource); diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java index 33f01ff..4178d73 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -30,7 +30,7 @@ public Map getDependingParamAndValues(List channelValues) { if (referenceStructure == null) { - return null; + return new HashMap<>(); } return referenceStructure.getDependingParamAndValues(channelValues); } @@ -85,7 +85,7 @@ if (subStructure.getDependentParamAndValues() == null) { subStructure = subStructure.getReferenceStructure(chVal); } else { - return null; + return new HashMap<>(); } } return subStructure.getDependentParamAndValues(); diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java index e039805..264c6fc 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -215,7 +215,7 @@ * @param resourceStateValueProvider a resourceStateValueProvider to provide current and next resource states * @return calculated message constraint by this event */ - public Term constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) { + public Expression constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) { try { Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); @@ -226,10 +226,10 @@ return null; } - private Term constructMessageAndDesdendantEvents(IResourceStateValueProvider resourceStateValueProvider, + private Expression constructMessageAndDesdendantEvents(IResourceStateValueProvider resourceStateValueProvider, Map> substitutedPositionsInMessageFromChannels, boolean doesUpdateDependingParameters) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed { - Term unifiedMessage = null; + Expression unifiedMessage = null; Expression messageConstraint = null; IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() { @Override @@ -259,9 +259,9 @@ // Calculate message constraint from an input state transition messageConstraint = channel.calcMessageConstraintForInputMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -271,9 +271,9 @@ // Calculate message constraint from a reference state transition messageConstraint = channel.calcMessageConstraintForReferenceMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -289,7 +289,7 @@ toResolve.remove(depending); } if ((messageConstraint = getMessage()) instanceof Term) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } for (ChannelMember leafMember: toResolve) { if (channel.getInputChannelMembers().contains(leafMember)) { @@ -300,9 +300,9 @@ messageConstraint = channel.calcMessageConstraintForReferenceMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); } if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -352,9 +352,9 @@ messageConstraint = channel.calcMessageConstraintForReferenceMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); } if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -378,9 +378,9 @@ } if (messageConstraint != null) { if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -397,7 +397,7 @@ if (baseResource.getParent() == null) break; baseResource = baseResource.getParent(); } - Term parentEventMessage = (Term) unifiedMessage.clone(); + Expression parentEventMessage = (Expression) unifiedMessage.clone(); for (Channel childChannel: channel.getChildren()) { // Search the deepest input side resource path in each child channel that matches the channel parameters. ChannelMember in = null; @@ -419,9 +419,9 @@ if (messageConstraint != null) { childEvent.setMessage(messageConstraint); if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java index 7209331..2a93a6e 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -267,9 +267,13 @@ private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) { IResourceStateValueProvider resourceStateValueProvider = new ResourceStateValueProvider(curSystemState, nextSystemState); Set nextEvents = new HashSet<>(); - for (Map.Entry chEntry: nextSystemState.getChannelStates().entrySet()) { - DataTransferChannel channel = chEntry.getKey(); + for (Channel ch: model.getChannels()) { + DataTransferChannel channel = (DataTransferChannel) ch; ChannelState nextChannelState = nextSystemState.getChannelState(channel); + if (nextChannelState == null) { + nextChannelState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChannelState); + } Map> dependency = channel.getMemberDependency(); Map> invDependency = new HashMap<>(); for (ChannelMember dependingMem: dependency.keySet()) { @@ -294,10 +298,7 @@ Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); if (message != null) { nextEvent.setMessage(message); - if (nextChannelState == null) { - nextChannelState = new ChannelState(channel); - nextSystemState.updateChannelState(channel, nextChannelState); - } + nextSystemState.updateChannelState(channel, nextChannelState); List channelSelValues = nextEvent.getChannelSelectorValues(); for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { nextChannelState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); @@ -324,54 +325,52 @@ } } } - if (nextChannelState != null) { - for (ChannelMember dependingMem: dependency.keySet()) { - if (inResPath == dependingMem.getResource()) { - // 2) If a 'depending' resource is directly updated. - ResourcePath filledResPath = inResId; - ResourcePath unfilledResPath = inResPath; - // Extract the values of path parameters from the updated resource identifier. - Map selectorVarToVal = new HashMap<>(); - Map dependingVarToVal = new HashMap<>(); - boolean doesSatisfyConstraint = true; - for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { - Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); - if (constraint != null && !constraint.equals(val)) { - // The value of the path parameter does not satisfy the constraint defined for the parameter. - doesSatisfyConstraint = false; - break; - } - if (channel.getAllSelectorVariables().contains(var)) { - selectorVarToVal.put(var, val); - } else { - dependingVarToVal.put(var, val); - } + for (ChannelMember dependingMem: dependency.keySet()) { + if (inResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = inResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; } - if (doesSatisfyConstraint) { - List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); - if (dependedChannelSelectorValues != null) { - for (List channelSelectorValues: dependedChannelSelectorValues) { - // Guess every tuple of channel selector values that may affects the updated resource. - boolean doesMatch = true; - for (Expression var: selectorVarToVal.keySet()) { - for (int i = 0; i < channel.getAllSelectors().size(); i++) { - if (channel.getAllSelectors().get(i).getExpression().equals(var)) { - if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { - // If the value of a selector in the updated resource path does not matches a guessed channel selector value. - doesMatch = false; - } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List channelSelectorValues: dependedChannelSelectorValues) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; } } } - if (doesMatch) { - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); - Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); - if (message != null) { - nextEvent.setMessage(message); - nextEvents.add(nextEvent); - } + } + if (doesMatch) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + nextEvent.setMessage(message); + nextEvents.add(nextEvent); } } } @@ -381,26 +380,17 @@ } } } - if (nextChannelState != null) { - // 3) If a resource in a descendant channel is directly updated. - ResourcePath inResPath = null; - for (ResourcePath path: channel.getInputResources()) { - if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { - inResPath = path; - } + // 3) If a resource in a descendant channel is directly updated. + ResourcePath inResPath = null; + for (ResourcePath path: channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; } - for (Channel childCh: channel.getChildren()) { - DataTransferChannel childChannel = (DataTransferChannel) childCh; - if (inResPath != null) { - for (Map.Entry, Event> childEventEnt: collectDescendantEvents(channel, childChannel, inResId, nextSystemState, resourceStateValueProvider)) { - Event childEvent = childEventEnt.getValue(); - List channelSelectorValues = childEventEnt.getKey(); - Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); - nextEvent.addChild(childEvent); - nextEvents.add(nextEvent); - } - } + } + if (inResPath != null) { + for (Map.Entry, Event> childEventEnt: collectDescendantEvents(channel, channel, inResId, nextSystemState, resourceStateValueProvider)) { + Event childEvent = childEventEnt.getValue(); + nextEvents.add(childEvent); } } } @@ -427,6 +417,7 @@ } for (ResourcePath childInResPath: childChannel.getInputResources()) { if (inResId.isInstanceOf(childInResPath)) { + boolean isInputResourceDepended = false; for (ChannelMember dependedMem: invDependency.keySet()) { if (childInResPath == dependedMem.getResource()) { // 1) If some depending resources are to be updated by the update of an 'depended' input resource. @@ -435,14 +426,39 @@ Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); if (message != null) { childEvent.setMessage(message); - if (nextChannelState == null) { - nextChannelState = new ChannelState(channel); - nextSystemState.updateChannelState(channel, nextChannelState); - } List childChannelSelValues = childEvent.getChannelSelectorValues(); for (Map.Entry paramEnt: childEvent.getDependingParameters().entrySet()) { nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + isInputResourceDepended = true; + } + } + boolean isInputResourceDepending = false; + for (ChannelMember dependingMem: dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + isInputResourceDepending = true; + } + } + if (!isInputResourceDepended && !isInputResourceDepending) { + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { List channelSelectorValues = new ArrayList<>(childChannelSelValues); for (int i = 0; i < childChannel.getSelectors().size(); i++) { channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); @@ -452,52 +468,52 @@ } } } - if (nextChannelState != null) { - for (ChannelMember dependingMem: dependency.keySet()) { - if (childInResPath == dependingMem.getResource()) { - // 2) If a 'depending' resource is directly updated. - ResourcePath filledResPath = inResId; - ResourcePath unfilledResPath = childInResPath; - // Extract the values of path parameters from the updated resource identifier. - Map selectorVarToVal = new HashMap<>(); - Map dependingVarToVal = new HashMap<>(); - boolean doesSatisfyConstraint = true; - for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { - Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); - if (constraint != null && !constraint.equals(val)) { - // The value of the path parameter does not satisfy the constraint defined for the parameter. - doesSatisfyConstraint = false; - break; - } - if (channel.getAllSelectorVariables().contains(var)) { - selectorVarToVal.put(var, val); - } else { - dependingVarToVal.put(var, val); - } + for (ChannelMember dependingMem: dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = childInResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; } - if (doesSatisfyConstraint) { - List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); - if (dependedChannelSelectorValues != null) { - for (List childChannelSelectorValues: dependedChannelSelectorValues) { - // Guess every tuple of channel selector values that may affects the updated resource. - boolean doesMatch = true; - for (Expression var: selectorVarToVal.keySet()) { - for (int i = 0; i < channel.getAllSelectors().size(); i++) { - if (channel.getAllSelectors().get(i).getExpression().equals(var)) { - if (!childChannelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { - // If the value of a selector in the updated resource path does not matches a guessed channel selector value. - doesMatch = false; - } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List childChannelSelectorValues: dependedChannelSelectorValues) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!childChannelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; } } } - if (doesMatch) { - Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal); - Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); - if (message != null) { - childEvent.setMessage(message); + } + if (doesMatch) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + childEvent.setMessage(message); + if (childChannelSelectorValues.size() >= childChannel.getSelectors().size()) { List channelSelectorValues = new ArrayList<>(childChannelSelectorValues); for (int i = 0; i < childChannel.getSelectors().size(); i++) { channelSelectorValues.remove(childChannelSelectorValues.size() - 1 - i); @@ -511,7 +527,6 @@ } } } - } } // 3) If a resource in a descendant channel is directly updated. @@ -530,11 +545,13 @@ Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); Event event = new Event((DataTransferChannel) channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); event.addChild(childEvent); - List parentChannelSelectorValues = new ArrayList<>(channelSelectorValues); - for (int i = 0; i < channel.getSelectors().size(); i++) { - parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i); + if (channelSelectorValues.size() >= channel.getSelectors().size()) { + List parentChannelSelectorValues = new ArrayList<>(channelSelectorValues); + for (int i = 0; i < channel.getSelectors().size(); i++) { + parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i); + } + events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event)); } - events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event)); } return events; }