001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.conversion; 018 019import java.util.*; 020 021/** 022 * A {@link BasicConverter} subclass that supports pluggable type conversion discovery via {@link ConversionFinder}. 023 * 024 * <p> 025 * Pass one or more {@link ConversionFinder} instances to the constructor. Finders are consulted in order before 026 * falling back to the built-in {@link BasicConverter} reflection logic. 027 * 028 * <h5 class='section'>Example:</h5> 029 * <p class='bjava'> 030 * ConfigurableConverter <jv>converter</jv> = <jk>new</jk> ConfigurableConverter( 031 * (<jv>in</jv>, <jv>out</jv>) -> <jv>in</jv> == String.<jk>class</jk> && <jv>out</jv> == MyBean.<jk>class</jk> 032 * ? (<jv>s</jv>, <jv>memberOf</jv>, <jv>session</jv>, <jv>args</jv>) -> MyBean.fromString((<jk>String</jk>) <jv>s</jv>) 033 * : <jk>null</jk> 034 * ); 035 * 036 * MyBean <jv>bean</jv> = <jv>converter</jv>.to(<js>"value"</js>, MyBean.<jk>class</jk>); 037 * </p> 038 * 039 * <h5 class='section'>See Also:</h5><ul> 040 * <li class='jc'>{@link BasicConverter} 041 * <li class='jc'>{@link ConversionFinder} 042 * <li class='jc'>{@link Conversion} 043 * </ul> 044 */ 045public class ConfigurableConverter extends BasicConverter { 046 047 private final List<ConversionFinder> finders; 048 049 /** 050 * Constructor. 051 * 052 * @param finders Optional {@link ConversionFinder} instances consulted in order before registered 053 * type-pair conversions and the built-in {@link BasicConverter} reflection logic. 054 */ 055 public ConfigurableConverter(ConversionFinder... finders) { 056 this.finders = List.of(finders); 057 } 058 059 /** 060 * Returns <jk>true</jk> if any registered {@link ConversionFinder} can convert the specified type pair. 061 * 062 * <p> 063 * This only checks the registered finders, not the built-in {@link BasicConverter} reflection logic. 064 * 065 * @param inType The input type class. 066 * @param outType The output type class. 067 * @return <jk>true</jk> if a finder-provided conversion exists for the specified type pair. 068 */ 069 public boolean hasCustomConversion(Class<?> inType, Class<?> outType) { 070 for (var finder : finders) 071 if (finder.findConversion(inType, outType) != null) 072 return true; 073 return false; 074 } 075 076 @Override 077 @SuppressWarnings({ 078 "unchecked" // Type safety guaranteed by ConversionFinder contract 079 }) 080 protected <I,O> Conversion<I,O> findConversion(Class<I> inType, Class<O> outType) { 081 for (var finder : finders) { 082 var fn = (Conversion<I,O>) finder.findConversion(inType, outType); 083 if (fn != null) 084 return fn; 085 } 086 return super.findConversion(inType, outType); 087 } 088}