/*
 * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.process.internal;

import java.util.Deque;
import java.util.LinkedList;
import java.util.function.Function;

import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.process.Inflector;

A stage-related collection of utility methods.
Author:Marek Potociar
/** * A stage-related collection of utility methods. * * @author Marek Potociar */
public final class Stages { private static final ChainableStage IDENTITY = new AbstractChainableStage() { @Override public Continuation apply(Object o) { //noinspection unchecked return Continuation.of(o, getDefaultNext()); } };
Prevents instantiation.
/** * Prevents instantiation. */
private Stages() { }
Get a chainable "identity" stage. This stage, when applied returns the unmodified input data object as part of it's continuation.
Type parameters:
  • <DATA> – data type transformable by the stage.
Returns:identity stage.
/** * Get a chainable "identity" stage. * * This stage, when applied returns the unmodified input data object * as part of it's continuation. * * @param <DATA> data type transformable by the stage. * @return identity stage. */
public static <DATA> ChainableStage<DATA> identity() { //noinspection unchecked return IDENTITY; }
Creates a terminal Stage that implements Inflecting interface and returns the provided Inflector instance when the Inflecting.inflector() method is called.
Params:
  • inflector – a request to response transformation to be wrapped in a stage.
Type parameters:
  • <DATA> – data type transformable by the stage and returned inflector.
  • <RESULT> – type of result produced by a successful inflector data transformation.
Returns:a stage that wraps the supplied Inflector.
/** * Creates a terminal {@link Stage} that implements {@link Inflecting} * interface and returns the provided {@link Inflector} instance * when the {@link Inflecting#inflector()} method is called. * * @param <DATA> data type transformable by the stage and returned inflector. * @param <RESULT> type of result produced by a successful inflector data transformation. * @param inflector a request to response transformation to be wrapped in * a stage. * @return a stage that wraps the supplied {@code Inflector}. */
@SuppressWarnings("unchecked") public static <DATA, RESULT> Stage<DATA> asStage(final Inflector<DATA, RESULT> inflector) { return new InflectingStage<DATA, RESULT>(inflector); } private static class InflectingStage<DATA, RESULT> implements Stage<DATA>, Inflecting<DATA, RESULT> { private final Inflector<DATA, RESULT> inflector; public InflectingStage(Inflector<DATA, RESULT> inflector) { this.inflector = inflector; } @Override public Inflector<DATA, RESULT> inflector() { return inflector; } @Override public Stage.Continuation<DATA> apply(DATA request) { return Continuation.of(request); } }
(Optionally) extracts an inflector from a processing stage, provided the stage implements Inflecting interface. Otherwise method returns null.
Params:
  • stage – a stage to extract the inflector from.
Type parameters:
  • <DATA> – data type transformable by the stage and returned inflector.
  • <RESULT> – type of result produced by a successful inflector data transformation.
Returns:extracted inflector if present, null otherwise.
/** * (Optionally) extracts an {@link Inflector inflector} from a processing stage, * provided the stage implements {@link Inflecting} interface. Otherwise method * returns {@code null}. * * @param <DATA> data type transformable by the stage and returned inflector. * @param <RESULT> type of result produced by a successful inflector data transformation. * @param stage a stage to extract the inflector from. * @return extracted inflector if present, {@code null} otherwise. */
@SuppressWarnings("unchecked") public static <DATA, RESULT, T extends Inflector<DATA, RESULT>> T extractInflector(Object stage) { if (stage instanceof Inflecting) { return (T) ((Inflecting<DATA, RESULT>) stage).inflector(); } return null; }
Start building a stage chain.
Params:
  • transformation – root transformation function.
Returns:linear accepting chain builder.
/** * Start building a stage chain. * * @param transformation root transformation function. * @return linear accepting chain builder. */
public static <DATA> Stage.Builder<DATA> chain(Function<DATA, DATA> transformation) { return new StageChainBuilder<DATA>(transformation); }
Start building a stage chain.
Params:
Returns:linear accepting chain builder.
/** * Start building a stage chain. * * @param rootStage root {@link ChainableStage chainable linear stage}. * @return linear accepting chain builder. */
public static <DATA> Stage.Builder<DATA> chain(ChainableStage<DATA> rootStage) { return new StageChainBuilder<DATA>(rootStage); }
Run the data through a chain of stages identified by the root stage.
Params:
  • data – data to be processed.
  • rootStage – root stage of the stage chain.
Type parameters:
  • <DATA> – processed data type.
Returns:processing result.
/** * Run the data through a chain of stages identified by the root stage. * * @param <DATA> processed data type. * @param data data to be processed. * @param rootStage root stage of the stage chain. * @return processing result. */
public static <DATA> DATA process(DATA data, Stage<DATA> rootStage) { Stage.Continuation<DATA> continuation = Stage.Continuation.of(data, rootStage); Stage<DATA> currentStage; while ((currentStage = continuation.next()) != null) { continuation = currentStage.apply(continuation.result()); } return continuation.result(); }
Run the data through a chain of stages identified by the root stage. If an inflector is found in the leaf stage, it's reference is set into the inflectorRef parameter.
Params:
  • data – data to be processed.
  • rootStage – root stage of the stage chain.
  • inflectorRef – a mutable reference to an inflector.
Type parameters:
  • <DATA> – processed data type.
Returns:processing result.
/** * Run the data through a chain of stages identified by the root stage. * * If an inflector is found in the leaf stage, it's reference is set into the {@code inflectorRef} * parameter. * * @param <DATA> processed data type. * @param data data to be processed. * @param rootStage root stage of the stage chain. * @param inflectorRef a mutable reference to an inflector. * @return processing result. */
public static <DATA, RESULT, T extends Inflector<DATA, RESULT>> DATA process( DATA data, Stage<DATA> rootStage, Ref<T> inflectorRef) { Stage<DATA> lastStage = rootStage; Stage.Continuation<DATA> continuation = Stage.Continuation.of(data, lastStage); while (continuation.next() != null) { lastStage = continuation.next(); continuation = lastStage.apply(continuation.result()); } inflectorRef.set(Stages.<DATA, RESULT, T>extractInflector(lastStage)); return continuation.result(); } private static class StageChainBuilder<DATA> implements Stage.Builder<DATA> { private final Deque<Function<DATA, DATA>> transformations = new LinkedList<Function<DATA, DATA>>(); private Stage<DATA> rootStage; private ChainableStage<DATA> lastStage; private StageChainBuilder(Function<DATA, DATA> transformation) { transformations.push(transformation); } private StageChainBuilder(ChainableStage<DATA> rootStage) { this.rootStage = rootStage; this.lastStage = rootStage; } @Override public Stage.Builder<DATA> to(Function<DATA, DATA> transformation) { transformations.push(transformation); return this; } @Override public Stage.Builder<DATA> to(final ChainableStage<DATA> stage) { addTailStage(stage); lastStage = stage; return this; } private void addTailStage(Stage<DATA> lastStage) { Stage<DATA> tail = lastStage; if (!transformations.isEmpty()) { tail = convertTransformations(lastStage); } if (rootStage != null) { this.lastStage.setDefaultNext(tail); } else { rootStage = tail; } } @Override public Stage<DATA> build(Stage<DATA> stage) { addTailStage(stage); return rootStage; } @Override public Stage<DATA> build() { return build(null); } private Stage<DATA> convertTransformations(Stage<DATA> successor) { Stage<DATA> stage; if (successor == null) { stage = new LinkedStage<DATA>(transformations.poll()); } else { stage = new LinkedStage<DATA>(transformations.poll(), successor); } Function<DATA, DATA> t; while ((t = transformations.poll()) != null) { stage = new LinkedStage<DATA>(t, stage); } return stage; } }
Linked linear stage implementation.
Type parameters:
  • <DATA> – processed data type.
/** * Linked linear stage implementation. * * @param <DATA> processed data type. */
public static class LinkedStage<DATA> implements Stage<DATA> { private final Stage<DATA> nextStage; private final Function<DATA, DATA> transformation;
Create a new stage that will return the supplied stage in the continuation.
Params:
  • transformation – Request transformation function to be applied in the stage.
  • nextStage – next stage returned in the continuation.
/** * Create a new stage that will return the supplied stage in the * continuation. * * @param transformation Request transformation function to be applied in the stage. * @param nextStage next stage returned in the continuation. */
public LinkedStage(Function<DATA, DATA> transformation, Stage<DATA> nextStage) { this.nextStage = nextStage; this.transformation = transformation; }
Create a new terminal stage .
Params:
  • transformation – Request transformation function to be applied in the stage.
/** * Create a new terminal stage . * * @param transformation Request transformation function to be applied in the stage. */
public LinkedStage(Function<DATA, DATA> transformation) { this(transformation, null); } @Override public Stage.Continuation<DATA> apply(DATA data) { return Continuation.of(transformation.apply(data), nextStage); } } }