private NameState addEndOfMatch()

in src/main/software/amazon/event/ruler/ByteMachine.java [1440:1581]


    private NameState addEndOfMatch(ByteState state,
                                    ByteState prevState,
                                    final InputCharacter[] characters,
                                    final int charIndex,
                                    final Patterns pattern,
                                    final NameState nameStateCandidate) {
        final int length = characters.length;
        NameState nameState = (nameStateCandidate == null) ? new NameState() : nameStateCandidate;

        if (length == 1 && isWildcard(characters[0])) {
            // Only character is '*'. Make the start state a match so empty input is matched.
            startStateMatch = new ByteMatch(pattern, nameState);
            return nameState;
        }

        ByteTransition trans = getTransition(state, characters[charIndex]);

        // If it is shortcut transition, we need do adjustment first.
        if (!trans.isEmpty() && trans.isShortcutTrans()) {
            ShortcutTransition shortcut = (ShortcutTransition) trans;
            ByteMatch match = shortcut.getMatch();
            // In add/delete rule path, match must not be null and must not have other match
            assert match != null && SHORTCUT_MATCH_TYPES.contains(match.getPattern().type());
            // If it is the same pattern, just return.
            if (pattern.equals(match.getPattern())) {
                return match.getNextNameState();
            }
            // Have asserted current match pattern must be value patterns
            String valueInCurrentPos = ((ValuePatterns) match.getPattern()).pattern();
            final InputCharacter[] charactersInCurrentPos = getParser().parse(match.getPattern().type(),
                    valueInCurrentPos);
            // find the position <m> where the common prefix ends.
            int m = charIndex;
            for (; m < charactersInCurrentPos.length && m < length; m++) {
                if (!charactersInCurrentPos[m].equals(characters[m])) {
                    break;
                }
            }
            // Extend the prefix part in value to byte transitions, to avoid impact on concurrent read we need firstly
            // make the new byte chain ready for using and leave the old transition removing to the last step.
            // firstNewState will be head of new byte chain and, to avoid impact on concurrent match traffic in read
            // path, it need be linked to current state chain after adjustment done.
            ByteState firstNewState = null;
            ByteState currentState = state;
            for (int k = charIndex; k < m; k++) {
                // we need keep the current state always pointed to last character.
                if (k != charactersInCurrentPos.length -1) {
                    final ByteState newByteState = new ByteState();
                    newByteState.setIndeterminatePrefix(currentState.hasIndeterminatePrefix());
                    if (k != charIndex) {
                        putTransitionNextState(currentState, charactersInCurrentPos[k], shortcut, newByteState);
                    } else {
                        firstNewState = newByteState;
                    }
                    currentState = newByteState;
                }
            }

            // If it reached to last character, link the previous read transition in this character, else create
            // shortcut transition. Note: at this time, the previous transition can still keep working.
            boolean isShortcutNeeded = m < charactersInCurrentPos.length - 1;
            int indexToBeChange = isShortcutNeeded ? m : charactersInCurrentPos.length - 1;
            putTransitionMatch(currentState, charactersInCurrentPos[indexToBeChange], isShortcutNeeded ?
                            new ShortcutTransition() : EmptyByteTransition.INSTANCE, match);
            removeTransition(currentState, charactersInCurrentPos[indexToBeChange], shortcut);

            // At last, we link the new created chain to the byte state path, so no uncompleted change can be felt by
            // reading thread. Note: we already confirmed there is only old shortcut transition at charIndex position,
            // now we have move it to new position, so we can directly replace previous transition with new transition
            // pointed to new byte state chain.
            putTransitionNextState(state, characters[charIndex], shortcut, firstNewState);
        }

        // If there is a exact match transition on tail of path, after adjustment target transitions, we start
        // looking at current remaining characters.
        // If this is tail transition, go directly analyse the remaining characters, traverse to tail of chain:
        boolean isEligibleForShortcut = true;
        int j = charIndex;
        for (; j < (length - 1); j++) {
            // We do not want to re-use an existing state for the second last character in the case of a final-character
            // wildcard pattern. In this case, we will have a self-referencing composite match state, which allows zero
            // or many character to satisfy the wildcard. The self-reference would lead to unintended matches for the
            // existing patterns.
            if (j == length - 2 && isWildcard(characters[j + 1])) {
                break;
            }

            trans = getTransition(state, characters[j]);
            if (trans.isEmpty()) {
                break;
            }
            ByteState nextByteState = trans.getNextByteState();
            if (nextByteState != null) {
                // We cannot re-use a state with an indeterminate prefix without creating unintended matches.
                if (nextByteState.hasIndeterminatePrefix()) {
                    // Since there is more path we are unable to traverse, this means we cannot insert shortcut without
                    // potentially ignoring matches further down path.
                    isEligibleForShortcut = false;
                    break;
                }
                prevState = state;
                state = nextByteState;
            } else {
                // trans has match but no next state, we need prepare a next next state to add trans for either last
                // character or shortcut byte.
                final ByteState newByteState = new ByteState();
                newByteState.setIndeterminatePrefix(state.hasIndeterminatePrefix());
                // Stream will not be empty since trans has been verified as non-empty
                SingleByteTransition single = trans.expand().iterator().next();
                putTransitionNextState(state, characters[j], single, newByteState);
                prevState = state;
                state = newByteState;
            }
        }

        // look for a chance to put in a shortcut transition.
        // However, for the moment, we only do this for a JSON string match i.e beginning with ", not literals
        //  like true or false or numbers, because if we do this for numbers produced by
        //  ComparableNumber.generate(), they can be messed up by addRangePattern.
        if (SHORTCUT_MATCH_TYPES.contains(pattern.type())) {
            // For exactly match, if it is last character already, we just put the real transition with match there.
            if (j == length - 1) {
                return insertMatch(characters, j, state, nameState, pattern, prevState);
            } else if (isEligibleForShortcut) {
                // If current character is not last character, create the shortcut transition with the next
                ByteMatch byteMatch = new ByteMatch(pattern, nameState);
                addTransition(state, characters[j], new ShortcutTransition().setMatch(byteMatch));
                addMatchReferences(byteMatch);
                return nameState;
            }
        }

        // For other match type, keep the old logic to extend all characters to byte state path and put the match in the
        // tail state.
        for (; j < (length - 1); j++) {
            ByteState nextByteState = findOrMakeNextByteState(state, prevState, characters, j, pattern, nameState);
            prevState = state;
            state = nextByteState;
        }

        return insertMatch(characters, length - 1, state, nameState, pattern, prevState);
    }