se.softhouse.common.guavaextensions.Functions2.java Source code

Java tutorial

Introduction

Here is the source code for se.softhouse.common.guavaextensions.Functions2.java

Source

/* Copyright 2013 Jonatan Jnsson
 *
 *    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 se.softhouse.common.guavaextensions;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;

/**
 * Gives you static access to additional implementations of the {@link Function} interface, as a
 * complement to {@link Functions}
 */
@Immutable
public final class Functions2 {
    private Functions2() {
    }

    /**
     * Runs several {@link Function}s in the same order as they are
     * given as arguments here.<br>
     * If one of the {@link Function}s makes {@code T} {@link Immutable},
     * make sure to pass it in last as it's hard to modify
     * an {@link Immutable} value. This works exactly like
     * {@link Functions#compose(Function, Function)} except that if {@code first} doesn't do
     * anything, {@code second} is returned directly instead.
     * 
     * @param first a {@link Function}
     * @param second another {@link Function}
     * @return a merged {@link Function}
     */
    @Nonnull
    public static <T> Function<T, T> compound(Function<T, T> first, Function<T, T> second) {
        checkNotNull(first);
        checkNotNull(second);
        if (first == Functions.identity())
            return second;

        return new CompoundFunction<T>(first, second);
    }

    private static final class CompoundFunction<T> implements Function<T, T> {
        @Nonnull
        private final Function<T, T> first;
        @Nonnull
        private final Function<T, T> second;

        private CompoundFunction(Function<T, T> first, Function<T, T> second) {
            this.first = first;
            this.second = second;
        }

        @Nullable
        @Override
        public T apply(@Nullable T value) {
            return second.apply(first.apply(value));
        }
    }

    /**
     * Creates a {@link Function} that applies {@code elementTransformer} to each element of the
     * input {@link List} and puts the new elements in a new , immutable, list and returns it.
     */
    @Nonnull
    public static <E> Function<List<E>, List<E>> listTransformer(Function<E, E> elementTransformer) {
        if (elementTransformer == Functions.identity())
            return Functions.identity();
        return new ListTransformer<E>(elementTransformer);
    }

    private static final class ListTransformer<E> implements Function<List<E>, List<E>> {
        private final Function<E, E> elementTransformer;

        private ListTransformer(Function<E, E> elementTransformer) {
            this.elementTransformer = checkNotNull(elementTransformer);
        }

        @Override
        public List<E> apply(List<E> values) {
            if (values == null)
                return null;
            return ImmutableList.copyOf(Lists.transform(values, elementTransformer));
        }
    }

    /**
     * Creates a {@link Function} that applies {@code valueTransformer} to each value of the
     * input {@link Map} and puts them in a new , immutable, map and returns it.
     */
    @Nonnull
    public static <K, V> Function<Map<K, V>, Map<K, V>> mapValueTransformer(Function<V, V> valueTransformer) {
        if (valueTransformer == Functions.identity())
            return Functions.identity();
        return new MapValueTransformer<K, V>(valueTransformer);
    }

    private static final class MapValueTransformer<K, V> implements Function<Map<K, V>, Map<K, V>> {
        private final Function<V, V> valueTransformer;

        private MapValueTransformer(Function<V, V> valueTransformer) {
            this.valueTransformer = checkNotNull(valueTransformer);
        }

        @Override
        public Map<K, V> apply(Map<K, V> map) {
            if (map == null)
                return null;
            return ImmutableMap.copyOf(Maps.transformValues(map, valueTransformer));
        }
    }

    /**
     * Creates a {@link Function} that wraps {@link List}s with
     * {@link Collections#unmodifiableList(List)}
     */
    @Nonnull
    public static <E> Function<List<E>, List<E>> unmodifiableList() {
        return new UnmodifiableListMaker<E>();
    }

    private static final class UnmodifiableListMaker<E> implements Function<List<E>, List<E>> {
        @Override
        public List<E> apply(List<E> value) {
            if (value == null)
                return null;
            return Collections.unmodifiableList(value);
        }
    }

    /**
     * Creates a {@link Function} that wraps {@link Map}s with
     * {@link Collections#unmodifiableMap(Map)}
     */
    @Nonnull
    public static <K, V> Function<Map<K, V>, Map<K, V>> unmodifiableMap() {
        return new UnmodifiableMapMaker<K, V>();
    }

    private static final class UnmodifiableMapMaker<K, V> implements Function<Map<K, V>, Map<K, V>> {
        @Override
        public Map<K, V> apply(Map<K, V> value) {
            if (value == null)
                return null;
            return Collections.unmodifiableMap(value);
        }
    }

    /**
     * Runs {@code function} {@code times} times.
     * The result of the first call is passed to the next apply in the chain and so on.
     * For instance:
     * 
     * <pre class="prettyprint">
     * <code class="language-java">
     * assertThat(Functions2.repeat(ADD_ONE, 2).apply(0)).isEqualTo(2);
     * </code>
     * </pre>
     */
    @Nonnull
    public static <T> Function<T, T> repeat(Function<T, T> function, long times) {
        checkNotNull(function);
        checkArgument(times >= 0, "times (%s) must be positive", times);
        return new FunctionRepeater<T>(function, times);
    }

    private static final class FunctionRepeater<T> implements Function<T, T> {
        private final long times;
        @Nonnull
        private final Function<T, T> function;

        private FunctionRepeater(Function<T, T> function, long times) {
            this.function = function;
            this.times = times;
        }

        @Override
        public T apply(T input) {
            T output = input;
            for (long i = 0; i < times; i++) {
                output = function.apply(output);
            }
            return output;
        }
    }

    /**
     * Returns a {@link Function} that reads whole {@link File}s into {@link String}s using the
     * {@link Charsets#UTF_8 UTF-8} charset.
     */
    public static Function<File, String> fileToString() {
        return FileToString.INSTANCE;
    }

    private static final class FileToString implements Function<File, String> {
        private static final Function<File, String> INSTANCE = new FileToString();

        @Override
        public String apply(@Nonnull File input) {
            if (input.isDirectory())
                throw new IllegalArgumentException(input.getAbsolutePath() + " is a directory, not a file");
            try {
                return Files.toString(input, Charsets.UTF_8);
            } catch (IOException e) {
                throw new IllegalArgumentException("I/O error occured while reading: " + input.getAbsolutePath(),
                        e);
            }
        }
    }
}