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}