001/** 002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.talend.sdk.component.runtime.manager.xbean.registry; 017 018import static java.util.Optional.ofNullable; 019import static org.talend.sdk.component.runtime.manager.util.Lazy.lazy; 020 021import java.lang.reflect.Type; 022import java.util.Date; 023import java.util.Map; 024import java.util.Objects; 025import java.util.Optional; 026import java.util.ServiceLoader; 027import java.util.function.BiFunction; 028import java.util.function.Function; 029import java.util.function.Supplier; 030import java.util.stream.Stream; 031 032import org.apache.xbean.propertyeditor.AbstractConverter; 033import org.apache.xbean.propertyeditor.BigDecimalEditor; 034import org.apache.xbean.propertyeditor.BigIntegerEditor; 035import org.apache.xbean.propertyeditor.BooleanEditor; 036import org.apache.xbean.propertyeditor.CharacterEditor; 037import org.apache.xbean.propertyeditor.ClassEditor; 038import org.apache.xbean.propertyeditor.Converter; 039import org.apache.xbean.propertyeditor.DateEditor; 040import org.apache.xbean.propertyeditor.DoubleEditor; 041import org.apache.xbean.propertyeditor.FileEditor; 042import org.apache.xbean.propertyeditor.HashMapEditor; 043import org.apache.xbean.propertyeditor.HashtableEditor; 044import org.apache.xbean.propertyeditor.Inet4AddressEditor; 045import org.apache.xbean.propertyeditor.Inet6AddressEditor; 046import org.apache.xbean.propertyeditor.InetAddressEditor; 047import org.apache.xbean.propertyeditor.ListEditor; 048import org.apache.xbean.propertyeditor.MapEditor; 049import org.apache.xbean.propertyeditor.ObjectNameEditor; 050import org.apache.xbean.propertyeditor.PatternConverter; 051import org.apache.xbean.propertyeditor.PropertiesEditor; 052import org.apache.xbean.propertyeditor.PropertyEditorRegistry; 053import org.apache.xbean.propertyeditor.SetEditor; 054import org.apache.xbean.propertyeditor.SortedMapEditor; 055import org.apache.xbean.propertyeditor.SortedSetEditor; 056import org.apache.xbean.propertyeditor.StringEditor; 057import org.apache.xbean.propertyeditor.URIEditor; 058import org.apache.xbean.propertyeditor.URLEditor; 059import org.talend.sdk.component.runtime.manager.xbean.converter.LocalDateConverter; 060import org.talend.sdk.component.runtime.manager.xbean.converter.LocalDateTimeConverter; 061import org.talend.sdk.component.runtime.manager.xbean.converter.LocalTimeConverter; 062import org.talend.sdk.component.runtime.manager.xbean.converter.SchemaConverter; 063import org.talend.sdk.component.runtime.manager.xbean.converter.ZonedDateTimeConverter; 064 065public class EnrichedPropertyEditorRegistry extends PropertyEditorRegistry { 066 067 private final ThreadLocal<Map<Type, Optional<Converter>>> converterCache = new ThreadLocal<>(); 068 069 public EnrichedPropertyEditorRegistry() { 070 final DoubleEditor doubleEditor = new DoubleEditor(); 071 // the front always sends us doubles so 072 // if we don't map double to the native number we get number format exceptions 073 final BiFunction<Class<?>, Function<Double, Object>, Converter> numberConverter = 074 (type, mapper) -> new AbstractConverter(type) { 075 076 @Override 077 protected Object toObjectImpl(final String text) { 078 if (text.isEmpty()) { 079 return null; 080 } 081 final Object o = doubleEditor.toObject(text); 082 return mapper.apply(Double.class.cast(o)); 083 } 084 }; 085 086 // built-in (was provided by xbean originally) 087 super.register(new BooleanEditor()); 088 super.register(numberConverter.apply(Byte.class, Double::byteValue)); 089 super.register(numberConverter.apply(Short.class, Double::shortValue)); 090 super.register(numberConverter.apply(Integer.class, Double::intValue)); 091 super.register(numberConverter.apply(Long.class, Double::longValue)); 092 super.register(numberConverter.apply(Float.class, Double::floatValue)); 093 super.register(doubleEditor); 094 super.register(new BigDecimalEditor()); 095 super.register(new BigIntegerEditor()); 096 super.register(new StringEditor()); 097 super.register(new CharacterEditor()); 098 super.register(new ClassEditor()); 099 super.register(new LazyDateEditor()); 100 super.register(new FileEditor()); 101 super.register(new HashMapEditor()); 102 super.register(new HashtableEditor()); 103 super.register(new Inet4AddressEditor()); 104 super.register(new Inet6AddressEditor()); 105 super.register(new InetAddressEditor()); 106 super.register(new ListEditor()); 107 super.register(new SetEditor()); 108 super.register(new MapEditor()); 109 super.register(new SortedMapEditor()); 110 super.register(new SortedSetEditor()); 111 super.register(new ObjectNameEditor()); 112 super.register(new PropertiesEditor()); 113 super.register(new URIEditor()); 114 super.register(new URLEditor()); 115 super.register(new PatternConverter()); 116 117 // customs 118 super.register(new ZonedDateTimeConverter()); 119 super.register(new LocalDateTimeConverter()); 120 super.register(new LocalDateConverter()); 121 super.register(new LocalTimeConverter()); 122 123 // schema and record 124 super.register(new SchemaConverter()); 125 // extensions 126 ServiceLoader.load(Converter.class).forEach(this::register); 127 } 128 129 @Override 130 public Converter findConverter(final Type type) { 131 final Map<Type, Optional<Converter>> cache = converterCache.get(); 132 if (cache == null) { 133 converterCache.remove(); 134 return doFindConverter(type); 135 } 136 return ofNullable(cache.get(type)).flatMap(c -> c).orElseGet(() -> { 137 final Converter converter = doFindConverter(type); 138 cache.put(type, ofNullable(converter)); 139 return converter; 140 }); 141 } 142 143 public <T> T withCache(final Map<Type, Optional<Converter>> cache, final Supplier<T> task) { 144 converterCache.set(cache); 145 try { 146 return task.get(); 147 } finally { 148 converterCache.remove(); 149 } 150 } 151 152 private Converter doFindConverter(final Type type) { 153 return Stream 154 .<Supplier<Converter>> of(() -> findInternalConverter(type), () -> findStructuralConverter(type)) 155 .map(Supplier::get) 156 .filter(Objects::nonNull) 157 .findFirst() 158 .orElse(null); 159 } 160 161 @Override 162 public Converter register(final Converter converter) { 163 final Map<Type, Optional<Converter>> cache = converterCache.get(); 164 if (cache != null) { 165 cache.putIfAbsent(converter.getType(), ofNullable(converter)); 166 } else { 167 converterCache.remove(); 168 } 169 return converter; // avoid unexpected caching (would leak) 170 } 171 172 // used when default init is slow 173 private static class LazyEditor<T extends AbstractConverter> extends AbstractConverter { 174 175 private final Supplier<T> delegate; 176 177 private LazyEditor(final Supplier<T> delegate, final Class<?> type) { 178 super(type); 179 this.delegate = delegate; 180 } 181 182 @Override 183 protected String toStringImpl(final Object value) { 184 return delegate.get().toString(value); 185 } 186 187 @Override 188 protected Object toObjectImpl(final String text) { 189 return delegate.get().toObject(text); 190 } 191 } 192 193 private static class LazyDateEditor extends LazyEditor<DateEditor> { 194 195 private LazyDateEditor() { 196 super(lazy(DateEditor::new), Date.class); 197 } 198 } 199 200}