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