/*
* Copyright (c) 2013-2017 Cinchapi Inc.
*
* 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 com.cinchapi.concourse.util;
import java.lang.reflect.Array;
import java.util.AbstractSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.cinchapi.concourse.annotate.UtilityClass;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* A collection of tools used to transform objects in a collection using a
* transformation {@link Function}.
*
* @author Jeff Nelson
*/
@UtilityClass
public final class Transformers {
/**
* Return an array whose content is equal to the content of {@code original}
* after it has been transformed by {@code function}.
*
* @param original
* @param function
* @param vClass - the class object for the type to which the items in
* {@code original} will be transformed
* @return the transformed array
*/
@SuppressWarnings("unchecked")
public static <F, V> V[] transformArray(F[] original,
Function<? super F, ? extends V> function, Class<V> vClass) {
V[] transformed = (V[]) Array.newInstance(vClass, original.length);
for (int i = 0; i < original.length; ++i) {
F item = original[i];
transformed[i] = function.apply(item);
}
return transformed;
}
/**
* Return a Map whose keys are equal to the those in {@code original} after
* it has been transformed by {@code function}.
* <p>
* <strong>WARNING:</strong> There is the potential for data loss in the
* event that {@code function} returns duplicate transformed results for
* items in {@code original}.
* </p>
*
* @param original
* @param function
* @return the transformed Map
*/
public static <K, K2, V> Map<K2, V> transformMap(Map<K, V> original,
Function<? super K, ? extends K2> function) {
Map<K2, V> transformed = PrettyLinkedHashMap.newPrettyLinkedHashMap();
for (Map.Entry<K, V> entry : original.entrySet()) {
transformed.put(function.apply(entry.getKey()), entry.getValue());
}
return transformed;
}
/**
* Transform the keys in {@code original} with the {@code keys} function
* and each of the values with the {@code values} function and return the
* result.
* <p>
* <strong>WARNING:</strong> There is the potential for data loss in the
* event that {@code function} returns duplicate transformed results for
* items in {@code original}.
* </p>
*
* @param original
* @param keys
* @param values
* @return the transformed Map
*/
public static <K, K2, V, V2> Map<K2, Set<V2>> transformMapSet(
Map<K, Set<V>> original, Function<? super K, ? extends K2> keys,
Function<? super V, ? extends V2> values) {
Map<K2, Set<V2>> transformed = Maps.newLinkedHashMap();
for (Map.Entry<K, Set<V>> entry : original.entrySet()) {
transformed.put(keys.apply(entry.getKey()),
transformSet(entry.getValue(), values));
}
return transformed;
}
/**
* Transform each of the values in the {@code original} with the
* {@code function}.
*
* @param original
* @param function
* @return the transformed Map
*/
public static <K, V, V2> Map<K, V2> transformMapValues(Map<K, V> original,
Function<V, V2> function) {
Map<K, V2> transformed = Maps.newLinkedHashMap();
for (Map.Entry<K, V> entry : original.entrySet()) {
transformed.put(entry.getKey(), function.apply(entry.getValue()));
}
return transformed;
}
/**
* Return a new {@link Map} where the key and value of each entry is
* transformed by the {@code keyFunction} and {@code valueFunction}
* respectively.
*
* @param original the original map in which the transformations are
* performed
* @param keyFunction the function to transform each entry key
* @param valueFunction the function to transform each entry value
* @return the transformed {@link Map}
*/
public static <K1, V1, K2, V2> Map<K2, V2> transformMapEntries(
Map<K1, V1> original, Function<K1, K2> keyFunction,
Function<V1, V2> valueFunction) {
Map<K2, V2> transformed = Maps.newLinkedHashMap();
for (Map.Entry<K1, V1> entry : original.entrySet()) {
transformed.put(keyFunction.apply(entry.getKey()),
valueFunction.apply(entry.getValue()));
}
return transformed;
}
/**
* Populate a {@link Set} with the items in {@code original} after applying
* {@code function}.
*
* <p>
* <strong>WARNING:</strong> There is the potential for data loss in the
* event that {@code function} returns duplicate transformed results for
* items in {@code original}.
* </p>
*
* @param original the {@link Set} to transform
* @param function the transformation {@link Function}
* @return the transformed Set
*/
public static <F, V> Set<V> transformSet(Set<F> original,
Function<? super F, ? extends V> function) {
Set<V> transformed = Sets.newLinkedHashSetWithExpectedSize(original
.size());
for (F item : original) {
transformed.add(function.apply(item));
}
return transformed;
}
/**
* Return a {@link Set} that lazily populates a new set with items from the
* {@code original} after applying the transformation {@code function}.
*
* @param original the {@link Set} to transform
* @param function the transformation {@link Function}
* @return the transformed Set
*/
public static <V1, V2> Set<V2> transformSetLazily(Set<V1> original,
Function<V1, V2> function) {
return new LazyTransformSet<V1, V2>(original, function);
}
/**
* Transform the keys in {@code original} with the {@code keys} function
* and each of the values with the {@code values} function and return the
* map result that is sorted according to the {@code sorter}.
* <p>
* <strong>WARNING:</strong> There is the potential for data loss in the
* event that {@code function} returns duplicate transformed results for
* items in {@code original}.
* </p>
*
* @param original
* @param keys
* @param values
* @param sorter
* @return the transformed TreeMap
*/
public static <K, K2, V, V2> Map<K2, Set<V2>> transformTreeMapSet(
Map<K, Set<V>> original, Function<? super K, ? extends K2> keys,
Function<? super V, ? extends V2> values,
final Comparator<K2> sorter) {
Map<K2, Set<V2>> transformed = Maps.newTreeMap(sorter);
for (Map.Entry<K, Set<V>> entry : original.entrySet()) {
transformed.put(keys.apply(entry.getKey()),
transformSet(entry.getValue(), values));
}
return transformed;
}
/**
* A {@link Set} that transform values from an original set of type
* {@code V1} into values of type {@code V2} on-the-fly using a
* transformation {@link Function}.
*
* @author chandresh.pancholi
*
*/
public static class LazyTransformSet<V1, V2> extends AbstractSet<V2> {
/**
* A {@link Function} to transform values of type {@code V1} in the
* {@code original} set to values of type {@code V2}.
*/
private final Function<V1, V2> function;
/**
* The original set, whose values will be transformed lazily.
*/
private final Set<V1> original;
/**
* Construct a new instance.
*
* @param original
* @param function
*/
public LazyTransformSet(Set<V1> original, Function<V1, V2> function) {
this.original = original;
this.function = function;
}
@Override
public Iterator<V2> iterator() {
return new ReadOnlyIterator<V2>() {
Iterator<V1> backing = original.iterator();
@Override
public boolean hasNext() {
return backing.hasNext();
}
@Override
public V2 next() {
return function.apply(backing.next());
}
};
}
@Override
public int size() {
return original.size();
}
}
}