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.ioc.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.ioc.services.TypeCoercer} service. Wraps a
022 * {@link org.apache.tapestry5.ioc.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    /**
041     * Wraps an arbitrary coercion with an implementation of toString() that identifies the source and target types.
042     */
043    private class CoercionWrapper<WS, WT> implements Coercion<WS, WT>
044    {
045        private final Coercion<WS, WT> coercion;
046
047        public CoercionWrapper(Coercion<WS, WT> coercion)
048        {
049            this.coercion = coercion;
050        }
051
052        @Override
053        public WT coerce(WS input)
054        {
055            return coercion.coerce(input);
056        }
057
058        @Override
059        public String toString()
060        {
061            return String.format("%s --> %s", convert(sourceType), convert(targetType));
062        }
063    }
064
065    private String convert(Class type)
066    {
067        if (Void.class.equals(type))
068            return "null";
069
070        String name = PlasticUtils.toTypeName(type);
071
072        int dotx = name.lastIndexOf('.');
073
074        // Strip off a package name of "java.lang"
075
076        if (dotx > 0 && name.substring(0, dotx).equals("java.lang"))
077            return name.substring(dotx + 1);
078
079        return name;
080    }
081
082    /**
083     * Standard constructor, which defaults wrap to true.
084     */
085    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
086    {
087        this(sourceType, targetType, coercion, true);
088    }
089
090    /**
091     * Convenience constructor to help with generics.
092     *
093     * @since 5.2.0
094     */
095    public static <S, T> CoercionTuple<S, T> create(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
096    {
097        return new CoercionTuple<S, T>(sourceType, targetType, coercion);
098    }
099
100    /**
101     * Internal-use constructor.
102     *
103     * @param sourceType
104     *         the source (or input) type of the coercion, may be Void.class to indicate a coercion from null
105     * @param targetType
106     *         the target (or output) type of the coercion
107     * @param coercion
108     *         the object that performs the coercion
109     * @param wrap
110     *         if true, the coercion is wrapped to provide a useful toString()
111     */
112    @SuppressWarnings("unchecked")
113    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion, boolean wrap)
114    {
115        assert sourceType != null;
116        assert targetType != null;
117        assert coercion != null;
118
119        this.sourceType = PlasticUtils.toWrapperType(sourceType);
120        this.targetType = PlasticUtils.toWrapperType(targetType);
121        this.coercion = wrap ? new CoercionWrapper<S, T>(coercion) : coercion;
122    }
123
124    @Override
125    public String toString()
126    {
127        return coercion.toString();
128    }
129
130    public Coercion<S, T> getCoercion()
131    {
132        return coercion;
133    }
134
135    public Class<S> getSourceType()
136    {
137        return sourceType;
138    }
139
140    public Class<T> getTargetType()
141    {
142        return targetType;
143    }
144
145}