in src/main/software/amazon/event/ruler/GenericMachine.java [544:654]
private Set<SubRuleContext> addStep(final NameState state,
final List<String> keys,
final int keyIndex,
final Map<String, List<Patterns>> patterns,
final T ruleName,
List<String> addedKeys,
final Set<NameState>[] nameStatesForEachKey) {
final String key = keys.get(keyIndex);
ByteMachine byteMachine = state.getTransitionOn(key);
NameMatcher<NameState> nameMatcher = state.getKeyTransitionOn(key);
if (byteMachine == null && hasValuePatterns(patterns.get(key))) {
byteMachine = new ByteMachine();
state.addTransition(key, byteMachine);
addedKeys.add(key);
}
if (nameMatcher == null && hasKeyPatterns(patterns.get(key))) {
nameMatcher = createNameMatcher();
state.addKeyTransition(key, nameMatcher);
addedKeys.add(key);
}
// for each pattern, we'll provisionally add it to the BMC, which may already have it. Pass the states
// list in in case the BMC doesn't already have a next-step for this pattern and needs to make a new one
NameState lastNextState = null;
if (configuration.isAdditionalNameStateReuse()) {
lastNextState = state.getNextNameState(key);
if (lastNextState == null) {
lastNextState = new NameState();
state.addNextNameState(key, lastNextState);
}
}
Set<NameState> nameStates = new HashSet<>();
if (nameStatesForEachKey[keyIndex] == null) {
nameStatesForEachKey[keyIndex] = new HashSet<>();
}
for (Patterns pattern : patterns.get(key)) {
if (isNamePattern(pattern)) {
lastNextState = nameMatcher.addPattern(pattern, lastNextState == null ? new NameState() : lastNextState);
} else {
lastNextState = byteMachine.addPattern(pattern, lastNextState);
}
nameStates.add(lastNextState);
nameStatesForEachKey[keyIndex].add(lastNextState);
}
// Determine if we are adding a new rule or not. If we are not yet at the terminal key, go deeper recursively.
// If we are at the terminal key, unwind recursion stack. Any sub-rule IDs that have previously added the
// rule+pattern for the given key are returned up the recursion stack as our "candidates". At each level of the
// stack, we remove any candidates if they did not have the same rule+pattern for that level's key. If our
// candidate set becomes empty, we know we are adding a new rule.
Set<SubRuleContext> candidateSubRuleIds = null;
Set<SubRuleContext> candidateSubRuleIdsForThisKey = null;
final int nextKeyIndex = keyIndex + 1;
boolean isTerminal = nextKeyIndex == keys.size();
for (NameState nameState : nameStates) {
// At the terminal key, we initialize the candidate IDs to the previously-added sub-rules with the same rule
// name and pattern for this key.
if (isTerminal) {
candidateSubRuleIds = new HashSet<>();
for (Patterns pattern : patterns.get(key)) {
Set<SubRuleContext> subRuleIdsForPattern = nameState.getTerminalSubRuleIdsForPattern(pattern);
Set<SubRuleContext> subRuleIdsForName = subRuleContextGenerator.getIdsGeneratedForName(ruleName);
if (subRuleIdsForPattern != null && subRuleIdsForName != null) {
intersection(subRuleIdsForPattern, subRuleIdsForName, candidateSubRuleIds);
}
}
// At all non-terminal keys, we gather the sub-rule IDs for the key that use the same pattern.
} else {
candidateSubRuleIds = addStep(nameState, keys, nextKeyIndex, patterns, ruleName,
addedKeys, nameStatesForEachKey);
if (candidateSubRuleIds.isEmpty()) {
continue;
}
// This is an optimization for the single pattern, single NameState case. This appears to be the
// majority of cases so it's worth the extra code.
List<Patterns> patternsForThisKey = patterns.get(key);
if (patternsForThisKey.size() == 1 && nameStates.size() == 1) {
candidateSubRuleIdsForThisKey = nameState.getNonTerminalSubRuleIdsForPattern(
patternsForThisKey.get(0));
break;
}
candidateSubRuleIdsForThisKey = new HashSet<>();
for (Patterns pattern : patternsForThisKey) {
Set<SubRuleContext> nonTerminalSubRuleIds = nameState.getNonTerminalSubRuleIdsForPattern(pattern);
if (nonTerminalSubRuleIds != null) {
candidateSubRuleIdsForThisKey.addAll(nonTerminalSubRuleIds);
}
}
}
}
// At all non-terminal keys, remove candidates that are not present for this key.
if (!isTerminal) {
if (candidateSubRuleIdsForThisKey == null) {
candidateSubRuleIds.clear();
} else {
candidateSubRuleIds.retainAll(candidateSubRuleIdsForThisKey);
}
}
// Return remaining candidates up the stack.
return candidateSubRuleIds;
}