001    // Copyright 2006, 2007, 2008, 2010, 2011 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    /**
018     * An immutable object that represents a mapping from one type to another. This is also the contribution type when
019     * building the {@link org.apache.tapestry5.ioc.services.TypeCoercer} service. Wraps a
020     * {@link org.apache.tapestry5.ioc.services.Coercion} object that performs the work with additional properties that
021     * describe
022     * the input and output types of the coercion, needed when searching for an appropriate coercion (or sequence of
023     * coercions).
024     * 
025     * @param <S>
026     *            source (input) type
027     * @param <T>
028     *            target (output) type
029     */
030    public final class CoercionTuple<S, T>
031    {
032        private final Class<S> sourceType;
033    
034        private final Class<T> targetType;
035    
036        private final Coercion<S, T> coercion;
037    
038        /**
039         * Wraps an arbitrary coercion with an implementation of toString() that identifies the source and target types.
040         */
041        private class CoercionWrapper<WS, WT> implements Coercion<WS, WT>
042        {
043            private final Coercion<WS, WT> coercion;
044    
045            public CoercionWrapper(Coercion<WS, WT> coercion)
046            {
047                this.coercion = coercion;
048            }
049    
050            public WT coerce(WS input)
051            {
052                return coercion.coerce(input);
053            }
054    
055            @Override
056            public String toString()
057            {
058                return String.format("%s --> %s", convert(sourceType), convert(targetType));
059            }
060        }
061    
062        private String convert(Class type)
063        {
064            if (void.class.equals(type))
065                return "null";
066    
067            String name = ClassFabUtils.toJavaClassName(type);
068    
069            int dotx = name.lastIndexOf('.');
070    
071            // Strip off a package name of "java.lang"
072    
073            if (dotx > 0 && name.substring(0, dotx).equals("java.lang"))
074                return name.substring(dotx + 1);
075    
076            return name;
077        }
078    
079        /**
080         * Standard constructor, which defaults wrap to true.
081         */
082        public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
083        {
084            this(sourceType, targetType, coercion, true);
085        }
086    
087        /**
088         * Convenience constructor to help with generics.
089         * 
090         * @since 5.2.0
091         */
092        public static <S, T> CoercionTuple<S, T> create(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
093        {
094            return new CoercionTuple<S, T>(sourceType, targetType, coercion);
095        }
096    
097        /**
098         * Internal-use constructor.
099         * 
100         * @param sourceType
101         *            the source (or input) type of the coercion
102         * @param targetType
103         *            the target (or output) type of the coercion
104         * @param coercion
105         *            the object that performs the coercion
106         * @param wrap
107         *            if true, the coercion is wrapped to provide a useful toString()
108         */
109        @SuppressWarnings("unchecked")
110        public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion, boolean wrap)
111        {
112            assert sourceType != null;
113            assert targetType != null;
114            assert coercion != null;
115    
116            this.sourceType = ClassFabUtils.getWrapperType(sourceType);
117            this.targetType = ClassFabUtils.getWrapperType(targetType);
118            this.coercion = wrap ? new CoercionWrapper<S, T>(coercion) : coercion;
119        }
120    
121        @Override
122        public String toString()
123        {
124            return coercion.toString();
125        }
126    
127        public Coercion<S, T> getCoercion()
128        {
129            return coercion;
130        }
131    
132        public Class<S> getSourceType()
133        {
134            return sourceType;
135        }
136    
137        public Class<T> getTargetType()
138        {
139            return targetType;
140        }
141    
142    }