All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.smithy.model.selector.RecursiveSelector Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.model.selector;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;

final class RecursiveSelector implements InternalSelector {

    private final InternalSelector selector;

    RecursiveSelector(InternalSelector selector) {
        this.selector = selector;
    }

    @Override
    public Response push(Context context, Shape shape, Receiver next) {
        // This queue contains the shapes that have yet to have the selector applied to them.
        QueueReceiver queueReceiver = new QueueReceiver(next);
        queueReceiver.queue.add(shape);

        while (!queueReceiver.queue.isEmpty()) {
            Shape match = queueReceiver.queue.pop();
            // Apply the selector to the queue, it will send results downstream immediately, and can ask to stop early.
            if (selector.push(context, match, queueReceiver) == Response.STOP) {
                return Response.STOP;
            }
        }

        return Response.CONTINUE;
    }

    private static final class QueueReceiver implements Receiver {

        final Deque queue = new ArrayDeque<>();
        private final Set visited = new HashSet<>();
        private final Receiver next;

        QueueReceiver(Receiver next) {
            this.next = next;
        }

        @Override
        public Response apply(Context context, Shape matchedShapeFromSelector) {
            // This method receives each shape matched by the selector of RecursiveSelector.
            if (visited.add(matchedShapeFromSelector.getId())) {
                // Send the match downstream right away to do as little work as possible.
                // For example, in `:recursive(-[mixin]->) :test([id=foo#Bar])`, when a match is found, the recursive
                // function can stop finding more mixins.
                if (next.apply(context, matchedShapeFromSelector) == Response.STOP) {
                    return Response.STOP;
                }
                // Enqueue the shape so that the outer loop can send this match back into the selector.
                queue.add(matchedShapeFromSelector);
            }

            return Response.CONTINUE;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy