    001// Copyright 2006, 2007, 2008, 2010, 2011, 2012 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.commons.services;
    016
    017import org.apache.tapestry5.plastic.PlasticUtils;
    018
    019/**
    020 * An immutable object that represents a mapping from one type to another. This is also the contribution type when
    021 * building the {@link org.apache.tapestry5.commons.services.TypeCoercer} service. Wraps a
    022 * {@link org.apache.tapestry5.commons.services.Coercion} object that performs the work with additional properties that
    023 * describe
    024 * the input and output types of the coercion, needed when searching for an appropriate coercion (or sequence of
    025 * coercions).
    026 *
    027 * @param <S>
    028 *         source (input) type
    029 * @param <T>
    030 *         target (output) type
    031 */
    032public final class CoercionTuple<S, T>
    033{
    034    private final Class<S> sourceType;
    035
    036    private final Class<T> targetType;
    037
    038    private final Coercion<S, T> coercion;
    039
    040    private final Key key;
    041
    042    /**
    043     * Wraps an arbitrary coercion with an implementation of toString() that identifies the source and target types.
    044     */
    045    private class CoercionWrapper<WS, WT> implements Coercion<WS, WT>
    046    {
    047        private final Coercion<WS, WT> coercion;
    048
    049        public CoercionWrapper(Coercion<WS, WT> coercion)
    050        {
    051            this.coercion = coercion;
    052        }
    053
    054        @Override
    055        public WT coerce(WS input)
    056        {
    057            return coercion.coerce(input);
    058        }
    059
    060        @Override
    061        public String toString()
    062        {
    063            return String.format("%s --> %s", convert(sourceType), convert(targetType));
    064        }
    065    }
    066
    067    private String convert(Class type)
    068    {
    069        if (Void.class.equals(type))
    070            return "null";
    071
    072        String name = PlasticUtils.toTypeName(type);
    073
    074        int dotx = name.lastIndexOf('.');
    075
    076        // Strip off a package name of "java.lang"
    077
    078        if (dotx > 0 && name.substring(0, dotx).equals("java.lang"))
    079            return name.substring(dotx + 1);
    080
    081        return name;
    082    }
    083
    084    /**
    085     * Standard constructor, which defaults wrap to true.
    086     */
    087    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
    088    {
    089        this(sourceType, targetType, coercion, true);
    090    }
    091
    092    /**
    093     * Convenience constructor to help with generics.
    094     *
    095     * @since 5.2.0
    096     */
    097    public static <S, T> CoercionTuple<S, T> create(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
    098    {
    099        return new CoercionTuple<S, T>(sourceType, targetType, coercion);
    100    }
    101
    102    /**
    103     * Internal-use constructor.
    104     *
    105     * @param sourceType
    106     *         the source (or input) type of the coercion, may be Void.class to indicate a coercion from null
    107     * @param targetType
    108     *         the target (or output) type of the coercion
    109     * @param coercion
    110     *         the object that performs the coercion
    111     * @param wrap
    112     *         if true, the coercion is wrapped to provide a useful toString()
    113     */
    114    @SuppressWarnings("unchecked")
    115    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion, boolean wrap)
    116    {
    117        assert sourceType != null;
    118        assert targetType != null;
    119        assert coercion != null;
    120
    121        this.sourceType = PlasticUtils.toWrapperType(sourceType);
    122        this.targetType = PlasticUtils.toWrapperType(targetType);
    123        this.coercion = wrap ? new CoercionWrapper<S, T>(coercion) : coercion;
    124        this.key = new Key();
    125    }
    126
    127    @Override
    128    public String toString()
    129    {
    130        return coercion.toString();
    131    }
    132
    133    public Coercion<S, T> getCoercion()
    134    {
    135        return coercion;
    136    }
    137
    138    public Class<S> getSourceType()
    139    {
    140        return sourceType;
    141    }
    142
    143    public Class<T> getTargetType()
    144    {
    145        return targetType;
    146    }
    147
    148    public Key getKey()
    149    {
    150        return key;
    151    }
    152
    153    /**
    154     * Class that represents the key to be used to the mapped configuration of the
    155     * {@link TypeCoercer} service.
    156     */
    157    public final class Key
    158    {
    159
    160        protected Class<S> getSourceType()
    161        {
    162            return sourceType;
    163        }
    164
    165        protected Class<T> getTargetType()
    166        {
    167            return targetType;
    168        }
    169
    170        @Override
    171        public String toString() {
    172            return String.format("%s -> %s", sourceType.getName(), targetType.getName());
    173        }
    174
    175        @Override
    176        public int hashCode()
    177        {
    178            final int prime = 31;
    179            int result = 1;
    180            result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode());
    181            result = prime * result + ((targetType == null) ? 0 : targetType.hashCode());
    182            return result;
    183        }
    184
    185        @Override
    186        public boolean equals(Object obj)
    187        {
    188            if (this == obj)
    189                return true;
    190            if (obj == null)
    191                return false;
    192            if (getClass() != obj.getClass())
    193                return false;
    194
    195            Key other = (Key) obj;
    196            if (sourceType == null)
    197            {
    198                if (other.getSourceType() != null)
    199                    return false;
    200            } else if (!sourceType.equals(other.getSourceType()))
    201                return false;
    202            if (targetType == null)
    203            {
    204                if (other.getTargetType() != null)
    205                    return false;
    206            } else if (!targetType.equals(other.getTargetType()))
    207                return false;
    208            return true;
    209        }
    210
    211    }
    212
    213}