/*
* 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
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/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 packager/legal/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> data type transformable by the stage.
* @return identity stage.
*/
public static <DATA> ChainableStage<DATA> 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> 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 {@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.
*
* @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.
*
* @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.
*
* @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 {@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.
*
* @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.
*
* @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 .
*
* @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);
}
}
}