/**
 * Copyright 2010 CosmoCode GmbH
 *
 * Licensed 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 de.cosmocode.palava.ipc.xml.rpc.adapters;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;

/**
 * Utility class for {@link Adapter}s.
 *
 * @since 1.0
 * @author Willi Schoenborn
 */
public final class Adapters {
    
    private Adapters() {
        
    }
    
    /**
     * Adapts the specified adapter to the {@link Function} interface using {@link Adapter#decode(Object)}.
     * 
     * @since 1.0
     * @param <F> source type
     * @param <T> target type
     * @param adapter the backing adapter
     * @return a function using the decode method of the given adapter
     * @throws NullPointerException if adapter is null
     */
    public static <F, T> Function<F, T> asDecoder(final Adapter<? super F, ? extends T> adapter) {
        Preconditions.checkNotNull(adapter, "Adapter");
        return new Function<F, T>() {

            @Override
            public T apply(F from) {
                return adapter.decode(from);
            };
            
            @Override
            public String toString() {
                return String.format("Adapters.asDecoder(%s)", adapter);
            }
            
        };
    }
    
    /**
     * Adapts the specified adapter to the {@link Function} interface using {@link Adapter#encode(Object)}.
     * 
     * @since 1.0
     * @param <F> target type
     * @param <T> source type
     * @param adapter the backing adapter
     * @return a function using the encode method of the given adapter
     * @throws NullPointerException if adapter is null
     */
    public static <F, T> Function<T, F> asEncoder(final Adapter<? extends F, ? super T> adapter) {
        Preconditions.checkNotNull(adapter, "Adapter");
        return new Function<T, F>() {
            
            @Override
            public F apply(T from) {
                return adapter.encode(from);
            };
            
            @Override
            public String toString() {
                return String.format("Adapters.asEncoder(%s)", adapter);
            }
            
        };
    }
    
    /**
     * Creates an inverse version of the specified {@link Adapter} which uses
     * {@link Adapter#decode(Object)} when asked to encode and vice versa.
     * 
     * @since 1.0
     * @param <F> target type
     * @param <T> source type
     * @param adapter the backing adapter
     * @return an adapter which is a flipped version of the given one
     * @throws NullPointerException if adapter is null
     */
    public static <F, T> Adapter<T, F> flip(final Adapter<F, T> adapter) {
        Preconditions.checkNotNull(adapter, "Adapter");
        return new Adapter<T, F>() {
            
            @Override
            public F decode(T input) {
                return adapter.encode(input);
            };
            
            @Override
            public T encode(F input) {
                return adapter.decode(input);
            };
            
            @Override
            public String toString() {
                return String.format("Adapters.flip(%s)", adapter);
            }
            
        };
    }
    
    /**
     * Composes two {@link Adapter}s into one.
     * 
     * @since 1.0
     * @param <F> source type
     * @param <T> intermediate type
     * @param <S> target type
     * @param left left adapter
     * @param right right adapter
     * @return an adapter chaining left and right
     * @throws NullPointerException if left or right is null
     */
    public static <F, T, S> Adapter<F, S> compose(final Adapter<F, T> left, final Adapter<T, S> right) {
        Preconditions.checkNotNull(left, "Left");
        Preconditions.checkNotNull(right, "Right");
        return new Adapter<F, S>() {
            
            @Override
            public S decode(F input) {
                return right.decode(left.decode(input));
            };

            @Override
            public F encode(S input) {
                return left.encode(right.encode(input));
            };

            @Override
            public String toString() {
                return String.format("Adapters.compose(%s, %s)", left, right);
            }
            
        };
    }
    
    /**
     * Composes an {@link Adapter} and a {@link Function} into a one-way encoder, an adapter which
     * supports {@link Adapter#encode(Object)} but not {@link Adapter#decode(Object)}.
     * 
     * @since 1.0
     * @param <F> the source type
     * @param <T> the intermediate type
     * @param <S> the target type
     * @param adapter the backing adapter
     * @param function the encoding function
     * @return a composed adapter using the specified adapter and function for encoding 
     */
    public static <F, T, S> Adapter<F, S> composeEncoder(final Adapter<F, T> adapter, 
        final Function<? super S, ? extends T> function) {
        Preconditions.checkNotNull(adapter, "Adapter");
        Preconditions.checkNotNull(function, "Function");
        return new Adapter<F, S>() {
            
            @Override
            public S decode(F input) {
                throw new UnsupportedOperationException();
            };
            
            @Override
            public F encode(S input) {
                return adapter.encode(function.apply(input));
            };
            
            @Override
            public String toString() {
                return String.format("Adapters.compose(%s, %s)", adapter, function);
            }
            
        };
    }
    
}

@@ -1583,34 +1583,33 @@
 );%0A            %7D
-;
 %0A
+
             %0A   
@@ -2481,33 +2481,32 @@
 );%0A            %7D
-;
 %0A            %0A  
@@ -3401,33 +3401,32 @@
 );%0A            %7D
-;
 %0A            %0A  
@@ -3535,33 +3535,32 @@
 );%0A            %7D
-;
 %0A            %0A  
@@ -4477,33 +4477,32 @@
 );%0A            %7D
-;
 %0A%0A            @O
@@ -4618,17 +4618,16 @@
        %7D
-;
 %0A%0A      
@@ -5756,33 +5756,32 @@
 );%0A            %7D
-;
 %0A            %0A  
@@ -5893,32 +5893,32 @@
 .apply(input));%0A
+
             %7D;%0A 
@@ -5910,25 +5910,24 @@
            %7D
-;
 %0A           

removed empty statements