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