001// Copyright 2014 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. 014package org.apache.tapestry5.ioc.internal; 015 016import java.io.File; 017import java.lang.reflect.Array; 018import java.math.BigDecimal; 019import java.math.BigInteger; 020import java.time.DayOfWeek; 021import java.time.Duration; 022import java.time.Instant; 023import java.time.LocalDate; 024import java.time.LocalDateTime; 025import java.time.LocalTime; 026import java.time.Month; 027import java.time.MonthDay; 028import java.time.OffsetDateTime; 029import java.time.OffsetTime; 030import java.time.Period; 031import java.time.Year; 032import java.time.YearMonth; 033import java.time.ZoneId; 034import java.time.ZoneOffset; 035import java.time.ZonedDateTime; 036import java.util.Arrays; 037import java.util.Collection; 038import java.util.Collections; 039import java.util.Date; 040import java.util.List; 041 042import org.apache.tapestry5.func.Flow; 043import org.apache.tapestry5.ioc.Configuration; 044import org.apache.tapestry5.ioc.services.Coercion; 045import org.apache.tapestry5.ioc.services.CoercionTuple; 046import org.apache.tapestry5.ioc.services.TypeCoercer; 047import org.apache.tapestry5.ioc.util.TimeInterval; 048import org.apache.tapestry5.util.StringToEnumCoercion; 049 050/** 051 * Class that provides Tapestry-IoC's basic type coercions. 052 * @see TypeCoercer 053 * @see Coercion 054 */ 055public class BasicTypeCoercions 056{ 057 /** 058 * Provides the basic type coercions to a {@link Configuration} instance. 059 */ 060 public static void provideBasicTypeCoercions(Configuration<CoercionTuple> configuration) 061 { 062 add(configuration, Object.class, String.class, new Coercion<Object, String>() 063 { 064 @Override 065 public String coerce(Object input) 066 { 067 return input.toString(); 068 } 069 }); 070 071 add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>() 072 { 073 @Override 074 public Boolean coerce(Object input) 075 { 076 return input != null; 077 } 078 }); 079 080 add(configuration, String.class, Double.class, new Coercion<String, Double>() 081 { 082 @Override 083 public Double coerce(String input) 084 { 085 return Double.valueOf(input); 086 } 087 }); 088 089 // String to BigDecimal is important, as String->Double->BigDecimal would lose 090 // precision. 091 092 add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>() 093 { 094 @Override 095 public BigDecimal coerce(String input) 096 { 097 return new BigDecimal(input); 098 } 099 }); 100 101 add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>() 102 { 103 @Override 104 public Double coerce(BigDecimal input) 105 { 106 return input.doubleValue(); 107 } 108 }); 109 110 add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>() 111 { 112 @Override 113 public BigInteger coerce(String input) 114 { 115 return new BigInteger(input); 116 } 117 }); 118 119 add(configuration, String.class, Long.class, new Coercion<String, Long>() 120 { 121 @Override 122 public Long coerce(String input) 123 { 124 return Long.valueOf(input); 125 } 126 }); 127 128 add(configuration, String.class, Integer.class, Integer::valueOf); 129 130 add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>() 131 { 132 @Override 133 public Byte coerce(Long input) 134 { 135 return input.byteValue(); 136 } 137 }); 138 139 add(configuration, Long.class, Short.class, new Coercion<Long, Short>() 140 { 141 @Override 142 public Short coerce(Long input) 143 { 144 return input.shortValue(); 145 } 146 }); 147 148 add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>() 149 { 150 @Override 151 public Integer coerce(Long input) 152 { 153 return input.intValue(); 154 } 155 }); 156 157 add(configuration, Number.class, Long.class, new Coercion<Number, Long>() 158 { 159 @Override 160 public Long coerce(Number input) 161 { 162 return input.longValue(); 163 } 164 }); 165 166 add(configuration, Double.class, Float.class, new Coercion<Double, Float>() 167 { 168 @Override 169 public Float coerce(Double input) 170 { 171 return input.floatValue(); 172 } 173 }); 174 175 add(configuration, Long.class, Double.class, new Coercion<Long, Double>() 176 { 177 @Override 178 public Double coerce(Long input) 179 { 180 return input.doubleValue(); 181 } 182 }); 183 184 add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>() 185 { 186 @Override 187 public Boolean coerce(String input) 188 { 189 String trimmed = input == null ? "" : input.trim(); 190 191 if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0) 192 return false; 193 194 // Any non-blank string but "false" 195 196 return true; 197 } 198 }); 199 200 add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>() 201 { 202 @Override 203 public Boolean coerce(Number input) 204 { 205 return input.longValue() != 0; 206 } 207 }); 208 209 add(configuration, Void.class, Boolean.class, new Coercion<Void, Boolean>() 210 { 211 @Override 212 public Boolean coerce(Void input) 213 { 214 return false; 215 } 216 }); 217 218 add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>() 219 { 220 @Override 221 public Boolean coerce(Collection input) 222 { 223 return !input.isEmpty(); 224 } 225 }); 226 227 add(configuration, Object.class, List.class, new Coercion<Object, List>() 228 { 229 @Override 230 public List coerce(Object input) 231 { 232 return Collections.singletonList(input); 233 } 234 }); 235 236 add(configuration, Object[].class, List.class, new Coercion<Object[], List>() 237 { 238 @Override 239 public List coerce(Object[] input) 240 { 241 return Arrays.asList(input); 242 } 243 }); 244 245 add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>() 246 { 247 @Override 248 public Boolean coerce(Object[] input) 249 { 250 return input != null && input.length > 0; 251 } 252 }); 253 254 add(configuration, Float.class, Double.class, new Coercion<Float, Double>() 255 { 256 @Override 257 public Double coerce(Float input) 258 { 259 return input.doubleValue(); 260 } 261 }); 262 263 Coercion primitiveArrayCoercion = new Coercion<Object, List>() 264 { 265 @Override 266 public List<Object> coerce(Object input) 267 { 268 int length = Array.getLength(input); 269 Object[] array = new Object[length]; 270 for (int i = 0; i < length; i++) 271 { 272 array[i] = Array.get(input, i); 273 } 274 return Arrays.asList(array); 275 } 276 }; 277 278 add(configuration, byte[].class, List.class, primitiveArrayCoercion); 279 add(configuration, short[].class, List.class, primitiveArrayCoercion); 280 add(configuration, int[].class, List.class, primitiveArrayCoercion); 281 add(configuration, long[].class, List.class, primitiveArrayCoercion); 282 add(configuration, float[].class, List.class, primitiveArrayCoercion); 283 add(configuration, double[].class, List.class, primitiveArrayCoercion); 284 add(configuration, char[].class, List.class, primitiveArrayCoercion); 285 add(configuration, boolean[].class, List.class, primitiveArrayCoercion); 286 287 add(configuration, String.class, File.class, new Coercion<String, File>() 288 { 289 @Override 290 public File coerce(String input) 291 { 292 return new File(input); 293 } 294 }); 295 296 add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>() 297 { 298 @Override 299 public TimeInterval coerce(String input) 300 { 301 return new TimeInterval(input); 302 } 303 }); 304 305 add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>() 306 { 307 @Override 308 public Long coerce(TimeInterval input) 309 { 310 return input.milliseconds(); 311 } 312 }); 313 314 add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>() 315 { 316 @Override 317 public Object[] coerce(Object input) 318 { 319 return new Object[] 320 {input}; 321 } 322 }); 323 324 add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>() 325 { 326 @Override 327 public Object[] coerce(Collection input) 328 { 329 return input.toArray(); 330 } 331 }); 332 333 configuration.add(CoercionTuple.create(Flow.class, List.class, new Coercion<Flow, List>() 334 { 335 @Override 336 public List coerce(Flow input) 337 { 338 return input.toList(); 339 } 340 })); 341 342 configuration.add(CoercionTuple.create(Flow.class, Boolean.class, new Coercion<Flow, Boolean>() 343 { 344 @Override 345 public Boolean coerce(Flow input) 346 { 347 return !input.isEmpty(); 348 } 349 })); 350 351 352 } 353 354 /** 355 * Provides the basic type coercions for JSR310 (java.time.*) to a {@link Configuration} 356 * instance. 357 * TAP5-2645 358 */ 359 public static void provideJSR310TypeCoercions( 360 Configuration<CoercionTuple> configuration) 361 { 362 { 363 add(configuration, Year.class, Integer.class, Year::getValue); 364 add(configuration, Integer.class, Year.class, Year::of); 365 } 366 367 { 368 add(configuration, Month.class, Integer.class, Month::getValue); 369 add(configuration, Integer.class, Month.class, Month::of); 370 371 add(configuration, String.class, Month.class, StringToEnumCoercion.create(Month.class)); 372 } 373 374 { 375 add(configuration, String.class, YearMonth.class, YearMonth::parse); 376 377 add(configuration, YearMonth.class, Year.class, input -> Year.of(input.getYear())); 378 add(configuration, YearMonth.class, Month.class, YearMonth::getMonth); 379 } 380 381 { 382 add(configuration, String.class, MonthDay.class, MonthDay::parse); 383 384 add(configuration, MonthDay.class, Month.class, MonthDay::getMonth); 385 } 386 387 { 388 add(configuration, DayOfWeek.class, Integer.class, DayOfWeek::getValue); 389 add(configuration, Integer.class, DayOfWeek.class, DayOfWeek::of); 390 391 add(configuration, String.class, DayOfWeek.class, 392 StringToEnumCoercion.create(DayOfWeek.class)); 393 } 394 395 { 396 add(configuration, LocalDate.class, Instant.class, input -> { 397 return input.atStartOfDay(ZoneId.systemDefault()).toInstant(); 398 }); 399 add(configuration, Instant.class, LocalDate.class, input -> { 400 return input.atZone(ZoneId.systemDefault()).toLocalDate(); 401 }); 402 403 add(configuration, String.class, LocalDate.class, LocalDate::parse); 404 405 add(configuration, LocalDate.class, YearMonth.class, input -> { 406 return YearMonth.of(input.getYear(), input.getMonth()); 407 }); 408 409 add(configuration, LocalDate.class, MonthDay.class, input -> { 410 return MonthDay.of(input.getMonth(), input.getDayOfMonth()); 411 }); 412 } 413 414 { 415 add(configuration, LocalTime.class, Long.class, LocalTime::toNanoOfDay); 416 add(configuration, Long.class, LocalTime.class, LocalTime::ofNanoOfDay); 417 418 add(configuration, String.class, LocalTime.class, LocalTime::parse); 419 } 420 421 { 422 add(configuration, String.class, LocalDateTime.class, LocalDateTime::parse); 423 424 add(configuration, LocalDateTime.class, Instant.class, input -> { 425 return input.atZone(ZoneId.systemDefault()).toInstant(); 426 }); 427 add(configuration, Instant.class, LocalDateTime.class, input -> { 428 return LocalDateTime.ofInstant(input, ZoneId.systemDefault()); 429 }); 430 431 add(configuration, LocalDateTime.class, LocalDate.class, LocalDateTime::toLocalDate); 432 } 433 434 { 435 add(configuration, String.class, OffsetDateTime.class, OffsetDateTime::parse); 436 437 add(configuration, OffsetDateTime.class, Instant.class, OffsetDateTime::toInstant); 438 439 add(configuration, OffsetDateTime.class, OffsetTime.class, 440 OffsetDateTime::toOffsetTime); 441 } 442 443 { 444 add(configuration, String.class, ZoneId.class, ZoneId::of); 445 } 446 447 { 448 add(configuration, String.class, ZoneOffset.class, ZoneOffset::of); 449 } 450 451 { 452 add(configuration, String.class, ZonedDateTime.class, ZonedDateTime::parse); 453 454 add(configuration, ZonedDateTime.class, Instant.class, ZonedDateTime::toInstant); 455 456 add(configuration, ZonedDateTime.class, ZoneId.class, ZonedDateTime::getZone); 457 } 458 459 { 460 add(configuration, Instant.class, Long.class, Instant::toEpochMilli); 461 add(configuration, Long.class, Instant.class, Instant::ofEpochMilli); 462 463 add(configuration, Instant.class, Date.class, Date::from); 464 add(configuration, Date.class, Instant.class, Date::toInstant); 465 } 466 467 { 468 add(configuration, Duration.class, Long.class, Duration::toNanos); 469 add(configuration, Long.class, Duration.class, Duration::ofNanos); 470 } 471 472 { 473 add(configuration, String.class, Period.class, Period::parse); 474 } 475 } 476 477 private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType, 478 Class<T> targetType, Coercion<S, T> coercion) 479 { 480 configuration.add(CoercionTuple.create(sourceType, targetType, coercion)); 481 } 482 483 484 485}