org.glassfish.jersey.process.internal.Stages Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jersey-common Show documentation
Show all versions of jersey-common Show documentation
Jersey core common packages
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
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 (marek.potociar at oracle.com)
*/
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.
*/
private Stages() {
}
/**
* Get a chainable "identity" stage.
*
* This stage, when applied returns the unmodified input data object
* as part of it's continuation.
*
* @param data type transformable by the stage.
* @return identity stage.
*/
public static ChainableStage identity() {
//noinspection unchecked
return IDENTITY;
}
/**
* 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 type transformable by the stage and returned inflector.
* @param 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 Stage asStage(final Inflector inflector) {
return new InflectingStage(inflector);
}
private static class InflectingStage implements Stage, Inflecting {
private final Inflector inflector;
public InflectingStage(Inflector inflector) {
this.inflector = inflector;
}
@Override
public Inflector inflector() {
return inflector;
}
@Override
public Stage.Continuation apply(DATA request) {
return Continuation.of(request);
}
}
/**
* (Optionally) extracts an {@link Inflector inflector} from a processing stage,
* provided the stage implements {@link Inflecting} interface. Otherwise method
* returns {@code null}.
*
* @param data type transformable by the stage and returned inflector.
* @param 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 > T extractInflector(Object stage) {
if (stage instanceof Inflecting) {
return (T) ((Inflecting) stage).inflector();
}
return null;
}
/**
* Start building a stage chain.
*
* @param transformation root transformation function.
* @return linear accepting chain builder.
*/
public static Stage.Builder chain(Function transformation) {
return new StageChainBuilder(transformation);
}
/**
* Start building a stage chain.
*
* @param rootStage root {@link ChainableStage chainable linear stage}.
* @return linear accepting chain builder.
*/
public static Stage.Builder chain(ChainableStage rootStage) {
return new StageChainBuilder(rootStage);
}
/**
* Run the data through a chain of stages identified by the root stage.
*
* @param processed data type.
* @param data data to be processed.
* @param rootStage root stage of the stage chain.
* @return processing result.
*/
public static DATA process(DATA data, Stage rootStage) {
Stage.Continuation continuation = Stage.Continuation.of(data, rootStage);
Stage 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 {@code inflectorRef}
* parameter.
*
* @param 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 process(
DATA data,
Stage rootStage,
Ref inflectorRef) {
Stage lastStage = rootStage;
Stage.Continuation continuation = Stage.Continuation.of(data, lastStage);
while (continuation.next() != null) {
lastStage = continuation.next();
continuation = lastStage.apply(continuation.result());
}
inflectorRef.set(Stages.extractInflector(lastStage));
return continuation.result();
}
private static class StageChainBuilder implements Stage.Builder {
private final Deque> transformations = new LinkedList>();
private Stage rootStage;
private ChainableStage lastStage;
private StageChainBuilder(Function transformation) {
transformations.push(transformation);
}
private StageChainBuilder(ChainableStage rootStage) {
this.rootStage = rootStage;
this.lastStage = rootStage;
}
@Override
public Stage.Builder to(Function transformation) {
transformations.push(transformation);
return this;
}
@Override
public Stage.Builder to(final ChainableStage stage) {
addTailStage(stage);
lastStage = stage;
return this;
}
private void addTailStage(Stage lastStage) {
Stage tail = lastStage;
if (!transformations.isEmpty()) {
tail = convertTransformations(lastStage);
}
if (rootStage != null) {
this.lastStage.setDefaultNext(tail);
} else {
rootStage = tail;
}
}
@Override
public Stage build(Stage stage) {
addTailStage(stage);
return rootStage;
}
@Override
public Stage build() {
return build(null);
}
private Stage convertTransformations(Stage successor) {
Stage stage;
if (successor == null) {
stage = new LinkedStage(transformations.poll());
} else {
stage = new LinkedStage(transformations.poll(), successor);
}
Function t;
while ((t = transformations.poll()) != null) {
stage = new LinkedStage(t, stage);
}
return stage;
}
}
/**
* Linked linear stage implementation.
*
* @param processed data type.
*/
public static class LinkedStage implements Stage {
private final Stage nextStage;
private final Function transformation;
/**
* 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 transformation, Stage nextStage) {
this.nextStage = nextStage;
this.transformation = transformation;
}
/**
* Create a new terminal stage .
*
* @param transformation Request transformation function to be applied in the stage.
*/
public LinkedStage(Function transformation) {
this(transformation, null);
}
@Override
public Stage.Continuation apply(DATA data) {
return Continuation.of(transformation.apply(data), nextStage);
}
}
}