001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.util;
014
015import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
016import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
017import org.apache.tapestry5.ioc.services.Coercion;
018import org.apache.tapestry5.ioc.util.AvailableValues;
019import org.apache.tapestry5.ioc.util.UnknownValueException;
020
021import java.util.Map;
022
023/**
024 * A {@link org.apache.tapestry5.ioc.services.Coercion} for converting strings into an instance of a particular
025 * enumerated type. The {@link Enum#name() name} is used as the key to identify the enum instance, in a case-insensitive
026 * fashion.
027 *
028 * Moved from tapestry-core to tapestry-ioc in release 5.3, but kept in same package for compatibility.
029 * Moved tapestry-ioc to commons in release 5.4, but kept in same package for compatibility.
030 * 
031 * @param <T>
032 *            the type of enumeration
033 */
034public final class StringToEnumCoercion<T extends Enum> implements Coercion<String, T>
035{
036    private final Class<T> enumClass;
037
038    private final Map<String, T> stringToEnum = CollectionFactory.newCaseInsensitiveMap();
039
040    public StringToEnumCoercion(Class<T> enumClass)
041    {
042        this(enumClass, enumClass.getEnumConstants());
043    }
044
045    public StringToEnumCoercion(Class<T> enumClass, T... values)
046    {
047        this.enumClass = enumClass;
048
049        for (T value : values)
050            stringToEnum.put(value.name(), value);
051    }
052
053    @Override
054    public T coerce(String input)
055    {
056        if (InternalCommonsUtils.isBlank(input))
057            return null;
058
059        T result = stringToEnum.get(input);
060
061        if (result == null)
062        {
063            String message = String.format("Input '%s' does not identify a value from enumerated type %s.", input,
064                    enumClass.getName());
065
066            throw new UnknownValueException(message, new AvailableValues(enumClass.getName() + " enum constants",
067                    stringToEnum));
068        }
069
070        return result;
071    }
072
073    /**
074     * Allows an alias value (alternate) string to reference a value.
075     * 
076     * @since 5.2.2
077     */
078    public StringToEnumCoercion<T> addAlias(String alias, T value)
079    {
080        stringToEnum.put(alias, value);
081
082        return this;
083    }
084
085    public static <T extends Enum> StringToEnumCoercion<T> create(Class<T> enumClass)
086    {
087        return new StringToEnumCoercion<T>(enumClass);
088    }
089
090}