--- /dev/null 2011-08-08 08:40:49.685541141 -0700 +++ new/src/share/classes/java/util/functions/Mappers.java 2011-08-09 10:55:26.000000000 -0700 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.util.functions; + +import java.lang.reflect.Constructor; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Map; +import java.util.Objects; + +/** + * Static utility methods pertaining to {@code Mapper} instances. + * + *

All of the returned mappers are serializable if provided serializable + * parameters. + */ +public final class Mappers { + + /** + * A mapper which who's {@code map} method returns the provided + * input. + */ + public static final Mapper IDENTITY = #{Object t -> t}; + + /** + * A mapper which performs a mapping from an object to it's + * string representation. + */ + public static final Mapper STRING = #{Object t -> String.valueOf(t)}; + + /** + * singleton utils + */ + private Mappers() { + throw new AssertionError("No instances!"); + } + + /** + * Returns a mapper which who's {@code map} method returns the provided + * input. Useful when a Mapper is required and {@code } and {@code } + * are the same type. + */ + public static Mapper identity() { + return (Mapper) IDENTITY; + } + +// /** +// * Returns a mapper which who's {@code map} method returns a clone of the +// * provided input. +// */ +// public static Mapper cloneOf() { +// return +// +// #{T t -> try { +// return t.clone(); +// } catch (CloneNotSupportedException ex) { +// throw new UndeclaredThrowableException(ex); +// } +// }; +// } +// + /** + * Returns a mapper which performs a mapping from {@code } to it's + * string representation. + * + * @param Type of input values + * @return a mapper which performs a mapping from {@code } to it's + * string representation + */ + public static Mapper string() { + return (Mapper) STRING; + } + + /** + * Returns a mapper which performs a mapping from {@code } to {@code } + * followed by a mapping from {@code } to {@code }. + * + * @param Type for input values + * @param Type for intermediate mapped values. May be the same type as + * {@code }. + * @param Type for final mapped values. May be the same type as + * {@code }. + * @param first Initial mapping from {@code } to {@code }. + * @param second additional mapping from {@code } to {@code }. + */ + public static Mapper chain( + Mapper first, + Mapper second) { + Objects.requireNonNull(first); + Objects.requireNonNull(second); + + return #{T t -> second.map(first.map(t))}; + } + + /** + * Returns a constant output regardless of input. + * + * @param constant The value to be returned by the {@code map} method. + * @return a mapper who's {@code map} method provides a constant result. + */ + public static Mapper constant(U constant) { + return #{ T t -> constant }; + } + + /** + * A mapper that substitutes a single input value with a specified + * replacement. Input values are compared using {@code equals()}. + * + * @param The type of values. + * @param subOut The input value to be substituted out. + * @param subIn The replacement value for matching inputs. + * @return a mapper that substitutes a single input value with a specified + * replacement. + */ + public static Mapper substitute(T subOut, T subIn) { + return #{ T t -> Objects.equals(subOut,t) ? subIn : t}; + } + + /** + * Returns a new instance of {@code } constructed with provided + * {@code }. + * + * @param Type of input values to mapping + * @param Type of output values from mapping + * @param clazzT The {@code Class} which defines objects of type {@code } + * @param clazzU The {@code Class} which defines objects of type {@code } + * @return a mapper which creates instances of {@code } using {@code } + * as the constructor parameter. + * @throws NoSuchMethodException when {@code } has no constructor which + * takes a {@code } as a parameter. + */ + public static Mapper instantiate(final Class clazzT, final Class clazzU) { + Objects.requireNonNull(clazzT); + Objects.requireNonNull(clazzU); + + final Constructor constructor; + try { + constructor = clazzU.getConstructor(clazzT); + } catch(NoSuchMethodException noConstructor) { + throw new IllegalArgumentException("no constructor for ()", noConstructor); + } + + return #{T t -> + try { + return constructor.newInstance(t); + } catch (ReflectiveOperationException ex) { + // XXX mduigou argument for exception transparency? + throw new UndeclaredThrowableException(ex); + } + }; + } + + /** + * Map according to the provided mapping. The map is expected to contain + * all possible values of {@code }. A copy is not made + * of the map and care should be taken to avoid changes to the map during + * operation may produce results which violate the {@code map} method + * contract. + * + * @param input type to mapping operation + * @param output type from mapping operation + * @param map provides the mappings from {@code } to {@code } + * @throws IllegalArgumentException for all values of {@code } not + * present in the map + */ + public static Mapper forMap(final Map map) { + Objects.requireNonNull(map); + + return #{T t -> if (map.containsKey(t)) { + return map.get(t); + } + + throw new IllegalArgumentException("unmappable : " + t); + }; + } + + /** + * Map according to the provided mapping. The provided default value is + * returned for all {@code } keys not found in the map. A copy is + * not made of the map and care should be taken to avoid + * changes to the map during operation may produce results which violate the + * {@coe map} method contract. + * + * @param input type to map + * @param output type from mapping + * @param map provides the mappings from {@code } to {@code } + * @param defaultValue the value returned by {@code map} method for + * {code } values not contained in the provided map + */ + public static Mapper forMap(final Map map, final U defaultValue) { + Objects.requireNonNull(map); + + if(map.isEmpty()) { + return constant(defaultValue); + } + + return #{T t -> map.containsKey(t) ? map.get(t) : defaultValue}; + } + + /** + * Map according to the provided predicate. Two output values are provided + * {@code forTrue} is returned if the predicate returns {@code true} + * otherwise the {@code forFalse} value is returned. + * + * @param input type to map + * @param output type from mapping + * @param predicate decides which value {@code map} method should return + * @param forTrue value to be returned for {@code true} predicate results + * @param forFalse value to be returned for {@code false} predicate results + * @return a Mapper who's {@code map} method provides results according to + * the provided predicate. + */ + public static Mapper forPredicate(Predicate predicate, U forTrue, U forFalse) { + Objects.requireNonNull(predicate); + + return #{T t -> predicate.eval(t) ? forTrue : forFalse}; + } +}