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.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.commons.services.TypeCoercer} service. Wraps a 022 * {@link org.apache.tapestry5.commons.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 */ 032public 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 private final Key key; 041 042 /** 043 * Wraps an arbitrary coercion with an implementation of toString() that identifies the source and target types. 044 */ 045 private class CoercionWrapper<WS, WT> implements Coercion<WS, WT> 046 { 047 private final Coercion<WS, WT> coercion; 048 049 public CoercionWrapper(Coercion<WS, WT> coercion) 050 { 051 this.coercion = coercion; 052 } 053 054 @Override 055 public WT coerce(WS input) 056 { 057 return coercion.coerce(input); 058 } 059 060 @Override 061 public String toString() 062 { 063 return String.format("%s --> %s", convert(sourceType), convert(targetType)); 064 } 065 } 066 067 private String convert(Class type) 068 { 069 if (Void.class.equals(type)) 070 return "null"; 071 072 String name = PlasticUtils.toTypeName(type); 073 074 int dotx = name.lastIndexOf('.'); 075 076 // Strip off a package name of "java.lang" 077 078 if (dotx > 0 && name.substring(0, dotx).equals("java.lang")) 079 return name.substring(dotx + 1); 080 081 return name; 082 } 083 084 /** 085 * Standard constructor, which defaults wrap to true. 086 */ 087 public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion) 088 { 089 this(sourceType, targetType, coercion, true); 090 } 091 092 /** 093 * Convenience constructor to help with generics. 094 * 095 * @since 5.2.0 096 */ 097 public static <S, T> CoercionTuple<S, T> create(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion) 098 { 099 return new CoercionTuple<S, T>(sourceType, targetType, coercion); 100 } 101 102 /** 103 * Internal-use constructor. 104 * 105 * @param sourceType 106 * the source (or input) type of the coercion, may be Void.class to indicate a coercion from null 107 * @param targetType 108 * the target (or output) type of the coercion 109 * @param coercion 110 * the object that performs the coercion 111 * @param wrap 112 * if true, the coercion is wrapped to provide a useful toString() 113 */ 114 @SuppressWarnings("unchecked") 115 public CoercionTuple(Class<S> sourceType, Class<T> targetType, Coercion<S, T> coercion, boolean wrap) 116 { 117 assert sourceType != null; 118 assert targetType != null; 119 assert coercion != null; 120 121 this.sourceType = PlasticUtils.toWrapperType(sourceType); 122 this.targetType = PlasticUtils.toWrapperType(targetType); 123 this.coercion = wrap ? new CoercionWrapper<S, T>(coercion) : coercion; 124 this.key = new Key(); 125 } 126 127 @Override 128 public String toString() 129 { 130 return coercion.toString(); 131 } 132 133 public Coercion<S, T> getCoercion() 134 { 135 return coercion; 136 } 137 138 public Class<S> getSourceType() 139 { 140 return sourceType; 141 } 142 143 public Class<T> getTargetType() 144 { 145 return targetType; 146 } 147 148 public Key getKey() 149 { 150 return key; 151 } 152 153 /** 154 * Class that represents the key to be used to the mapped configuration of the 155 * {@link TypeCoercer} service. 156 */ 157 public final class Key 158 { 159 160 @Override 161 public String toString() { 162 return String.format("%s -> %s", sourceType.getName(), targetType.getName()); 163 } 164 165 @Override 166 public int hashCode() 167 { 168 final int prime = 31; 169 int result = 1; 170 result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode()); 171 result = prime * result + ((targetType == null) ? 0 : targetType.hashCode()); 172 return result; 173 } 174 175 @Override 176 public boolean equals(Object obj) 177 { 178 if (this == obj) 179 return true; 180 if (obj == null) 181 return false; 182 if (getClass() != obj.getClass()) 183 return false; 184 CoercionTuple other = (CoercionTuple) obj; 185 if (sourceType == null) 186 { 187 if (other.sourceType != null) 188 return false; 189 } else if (!sourceType.equals(other.sourceType)) 190 return false; 191 if (targetType == null) 192 { 193 if (other.targetType != null) 194 return false; 195 } else if (!targetType.equals(other.targetType)) 196 return false; 197 return true; 198 } 199 200 } 201 202}