/* * Copyright 2016 LINE Corporation * * LINE Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.linecorp.armeria.client; import java.util.Collections; import java.util.List; import java.util.function.Function; import com.linecorp.armeria.common.Request; import com.linecorp.armeria.common.Response; /** * A set of {@link Function}s that transforms a {@link Client} into another. */ public final class ClientDecoration { /** * A {@link ClientDecoration} that decorates no {@link Client}. */ public static final ClientDecoration NONE = new ClientDecoration(Collections.emptyList()); /** * Creates a new instance from a single decorator {@link Function}. * * @param requestType the type of the {@link Request} that the {@code decorator} is interested in * @param responseType the type of the {@link Response} that the {@code decorator} is interested in * @param decorator the {@link Function} that transforms a {@link Client} to another * @param <T> the type of the {@link Client} being decorated * @param <R> the type of the {@link Client} produced by the {@code decorator} */ public static <T extends Client<? super I, ? extends O>, R extends Client<I, O>, I extends Request, O extends Response> ClientDecoration of(Class<I> requestType, Class<O> responseType, Function<T, R> decorator) { return new ClientDecorationBuilder().add(requestType, responseType, decorator).build(); } private final List<Entry<?, ?>> entries; ClientDecoration(List<Entry<?, ?>> entries) { this.entries = Collections.unmodifiableList(entries); } List<Entry<?, ?>> entries() { return entries; } /** * Decorates the specified {@link Client} using the decorator with matching {@code requestType} and * {@code responseType}. * * @param requestType the type of the {@link Request} the specified {@link Client} accepts * @param responseType the type of the {@link Response} the specified {@link Client} produces * @param client the {@link Client} being decorated * @param <I> {@code requestType} * @param <O> {@code responseType} */ public <I extends Request, O extends Response> Client<I, O> decorate( Class<I> requestType, Class<O> responseType, Client<I, O> client) { for (Entry<?, ?> e : entries) { if (!requestType.isAssignableFrom(e.requestType()) || !responseType.isAssignableFrom(e.responseType())) { continue; } @SuppressWarnings("unchecked") final Function<Client<? super I, ? extends O>, Client<I, O>> decorator = ((Entry<I, O>) e).decorator(); client = decorator.apply(client); } return client; } static final class Entry<I extends Request, O extends Response> { private final Class<I> requestType; private final Class<O> responseType; private final Function<Client<? super I, ? extends O>, Client<I, O>> decorator; Entry(Class<I> requestType, Class<O> responseType, Function<? extends Client<? super I, ? extends O>, ? extends Client<I, O>> decorator) { this.requestType = requestType; this.responseType = responseType; @SuppressWarnings("unchecked") Function<Client<? super I, ? extends O>, Client<I, O>> castDecorator = (Function<Client<? super I, ? extends O>, Client<I, O>>) decorator; this.decorator = castDecorator; } Class<I> requestType() { return requestType; } Class<O> responseType() { return responseType; } Function<Client<? super I, ? extends O>, Client<I, O>> decorator() { return decorator; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final Entry<?, ?> entry = (Entry<?, ?>) o; if (!requestType.equals(entry.requestType)) { return false; } if (!responseType.equals(entry.responseType)) { return false; } final Function<?, ?> decorator = this.decorator; return decorator == entry.decorator; } @Override public int hashCode() { int result = requestType.hashCode(); result = 31 * result + responseType.hashCode(); result = 31 * result + System.identityHashCode(decorator); return result; } @Override public String toString() { return '(' + requestType.getSimpleName() + ", " + responseType.getSimpleName() + ", " + decorator + ')'; } } }