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    
015    package org.apache.tapestry5.ioc.services;
016    
017    import 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     */
032    public 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    }