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.commons.services;
016
017import org.apache.tapestry5.commons.MappedConfiguration;
018import org.apache.tapestry5.plastic.PlasticUtils;
019
020/**
021 * An immutable object that represents a mapping from one type to another. This is also the contribution type when
022 * building the {@link org.apache.tapestry5.commons.services.TypeCoercer} service. Wraps a
023 * {@link org.apache.tapestry5.commons.services.Coercion} object that performs the work with additional properties that
024 * describe
025 * the input and output types of the coercion, needed when searching for an appropriate coercion (or sequence of
026 * coercions).
027 *
028 * @param <S>
029 *         source (input) type
030 * @param <T>
031 *         target (output) type
032 */
033public final class CoercionTuple<S, T>
034{
035    private final Class<S> sourceType;
036
037    private final Class<T> targetType;
038
039    private final Coercion<S, T> coercion;
040    
041    private final Key key;
042
043    /**
044     * Wraps an arbitrary coercion with an implementation of toString() that identifies the source and target types.
045     */
046    private class CoercionWrapper<WS, WT> implements Coercion<WS, WT>
047    {
048        private final Coercion<WS, WT> coercion;
049
050        public CoercionWrapper(Coercion<WS, WT> coercion)
051        {
052            this.coercion = coercion;
053        }
054
055        @Override
056        public WT coerce(WS input)
057        {
058            return coercion.coerce(input);
059        }
060
061        @Override
062        public String toString()
063        {
064            return String.format("%s --> %s", convert(sourceType), convert(targetType));
065        }
066    }
067
068    private String convert(Class type)
069    {
070        if (Void.class.equals(type))
071            return "null";
072
073        String name = PlasticUtils.toTypeName(type);
074
075        int dotx = name.lastIndexOf('.');
076
077        // Strip off a package name of "java.lang"
078
079        if (dotx > 0 && name.substring(0, dotx).equals("java.lang"))
080            return name.substring(dotx + 1);
081
082        return name;
083    }
084
085    /**
086     * Standard constructor, which defaults wrap to true.
087     */
088    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
089    {
090        this(sourceType, targetType, coercion, true);
091    }
092
093    /**
094     * Convenience method to create a coercion tuple using {@linkplain #create(Class, Class, Coercion)} 
095     * and add it to a {@linkplain MappedConfiguration} in a single step.
096     *
097     * @since 5.8.0
098     */
099    @SuppressWarnings("rawtypes")
100    public static <S, T> void add(
101            MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration, 
102            Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
103    {
104        CoercionTuple<S, T> coercionTuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
105        configuration.add(coercionTuple.getKey(), coercionTuple);
106    }
107    
108    /**
109     * Convenience method to create a coercion tuple using {@linkplain #create(Class, Class, Coercion)} 
110     * and override a matching one in a {@linkplain MappedConfiguration} in a single step.
111     *
112     * @since 5.8.0
113     */
114    @SuppressWarnings("rawtypes")
115    public static <S, T> void override(
116            MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration, 
117            Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
118    {
119        CoercionTuple<S, T> coercionTuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
120        configuration.override(coercionTuple.getKey(), coercionTuple);
121    }
122    
123    /**
124     * Convenience constructor to help with generics.
125     *
126     * @since 5.2.0
127     */
128    public static <S, T> CoercionTuple<S, T> create(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion)
129    {
130        return new CoercionTuple<S, T>(sourceType, targetType, coercion);
131    }
132
133    /**
134     * Internal-use constructor.
135     *
136     * @param sourceType
137     *         the source (or input) type of the coercion, may be Void.class to indicate a coercion from null
138     * @param targetType
139     *         the target (or output) type of the coercion
140     * @param coercion
141     *         the object that performs the coercion
142     * @param wrap
143     *         if true, the coercion is wrapped to provide a useful toString()
144     */
145    @SuppressWarnings("unchecked")
146    public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion, boolean wrap)
147    {
148        assert sourceType != null;
149        assert targetType != null;
150        assert coercion != null;
151
152        this.sourceType = PlasticUtils.toWrapperType(sourceType);
153        this.targetType = PlasticUtils.toWrapperType(targetType);
154        this.coercion = wrap ? new CoercionWrapper<S, T>(coercion) : coercion;
155        this.key = new Key();
156    }
157
158    @Override
159    public String toString()
160    {
161        return coercion.toString();
162    }
163
164    public Coercion<S, T> getCoercion()
165    {
166        return coercion;
167    }
168
169    public Class<S> getSourceType()
170    {
171        return sourceType;
172    }
173
174    public Class<T> getTargetType()
175    {
176        return targetType;
177    }
178    
179    public Key getKey() 
180    {
181        return key;
182    }
183
184    /**
185     * Class that represents the key to be used to the mapped configuration of the
186     * {@link TypeCoercer} service.
187     */
188    public final class Key 
189    {
190        
191        protected Class<S> getSourceType()
192        {
193            return sourceType;
194        }
195
196        protected Class<T> getTargetType()
197        {
198            return targetType;
199        }
200        
201        @Override
202        public String toString() {
203            return String.format("%s -> %s", sourceType.getName(), targetType.getName());
204        }
205    
206        @Override
207        public int hashCode() 
208        {
209            final int prime = 31;
210            int result = 1;
211            result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode());
212            result = prime * result + ((targetType == null) ? 0 : targetType.hashCode());
213            return result;
214        }
215    
216        @Override
217        public boolean equals(Object obj) 
218        {
219            if (this == obj)
220                return true;
221            if (obj == null)
222                return false;
223            if (getClass() != obj.getClass())
224                return false;
225
226            Key other = (Key) obj;
227            if (sourceType == null) 
228            {
229                if (other.getSourceType() != null)
230                    return false;
231            } else if (!sourceType.equals(other.getSourceType()))
232                return false;
233            if (targetType == null) 
234            {
235                if (other.getTargetType() != null)
236                    return false;
237            } else if (!targetType.equals(other.getTargetType()))
238                return false;
239            return true;
240        }
241        
242    }
243
244}