001// Copyright 2026 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.ioc.internal.services;
016
017import java.util.ArrayList;
018import java.util.List;
019
020/**
021 * Describes an assembled pipeline as {@code header: filter -> filter -> terminator}.
022 *
023 * <p>The {@code toString()} of the bridge classes generated by {@link BridgeBuilder} delegates here,
024 * as does {@link org.apache.tapestry5.ioc.services.AssembledPipeline}.
025 * @since 5.9.2
026 */
027public final class PipelineDescriber
028{
029    private static final String SEPARATOR = " -> ";
030
031    private PipelineDescriber()
032    {
033    }
034
035    /**
036     * The header identifying a pipeline, ahead of its steps.
037     */
038    public static String header(Class<?> serviceInterface, Class<?> filterInterface)
039    {
040        return String.format("<PipelineBridge from %s to %s>", serviceInterface.getName(), filterInterface.getName());
041    }
042
043    /**
044     * Walks the pipeline from the given step to its end, describing each filter, including the terminator.
045     *
046     * @param header
047     *         identifies the pipeline, the steps follow it, separated by a colon
048     * @param step
049     *         where to start, typically the outermost bridge, but any step describes the pipeline from itself
050     *         onwards
051     */
052    public static String describe(String header, PipelineStep step)
053    {
054        List<Object> steps = new ArrayList<>();
055
056        Object current = step;
057
058        while (current instanceof PipelineStep)
059        {
060            PipelineStep pipelineStep = (PipelineStep) current;
061
062            steps.add(pipelineStep.getPipelineFilter());
063
064            current = pipelineStep.getPipelineNext();
065        }
066
067        steps.add(current);
068
069        return describe(header, steps);
070    }
071
072    /**
073     * Describes steps that have already been collected: the filters, in the order they are invoked, followed by the
074     * terminator.
075     */
076    public static String describe(String header, List<?> steps)
077    {
078        StringBuilder builder = new StringBuilder(header).append(": ");
079
080        for (int i = 0; i < steps.size(); i++)
081        {
082            if (i > 0)
083            {
084                builder.append(SEPARATOR);
085            }
086
087            builder.append(steps.get(i));
088        }
089
090        return builder.toString();
091    }
092}