private Set addStep()

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;
    }