/** * DataCleaner (community edition) * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.datacleaner.util.convert; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.datacleaner.api.Convertable; import org.datacleaner.api.Converter; import org.datacleaner.configuration.InjectionManager; import org.datacleaner.configuration.InjectionPoint; import org.datacleaner.lifecycle.MemberInjectionPoint; import org.datacleaner.util.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Main converter used by {@link StringConverter}. This converter will delegate * and compose conversions based on relevant converter implementations, such as * {@link NullConverter}, {@link ArrayConverter}, {@link StandardTypeConverter} * and {@link ConfigurationItemConverter}. */ public class DelegatingConverter implements Converter<Object> { private static final Logger logger = LoggerFactory.getLogger(DelegatingConverter.class); private final List<Converter<?>> _converters; private final NullConverter _nullConverter; private final ArrayConverter _arrayConverter; private final MapStringToStringConverter _mapStringToStringConverter; public DelegatingConverter() { this(null); } public DelegatingConverter(final Collection<Converter<?>> converters) { _converters = new ArrayList<>(); _nullConverter = new NullConverter(); _arrayConverter = new ArrayConverter(this); _mapStringToStringConverter = new MapStringToStringConverter(); if (converters != null) { _converters.addAll(converters); } } public List<Converter<?>> getConverters() { return _converters; } public void addConverter(final Converter<?> converter) { _converters.add(converter); } @Override public Object fromString(final Class<?> type, String serializedForm) { if (type == null || serializedForm == null || _nullConverter.isNull(serializedForm)) { return _nullConverter.fromString(type, serializedForm); } if (type.isArray()) { return _arrayConverter.fromString(type, serializedForm); } serializedForm = SerializationStringEscaper.unescape(serializedForm); for (final Converter<?> converter : _converters) { if (converter.isConvertable(type)) { return converter.fromString(type, serializedForm); } } if (ReflectionUtils.is(type, List.class)) { return _arrayConverter.fromString(type, serializedForm); } if (ReflectionUtils.is(type, Map.class)) { return _mapStringToStringConverter.fromString(type, serializedForm); } final Convertable convertable = ReflectionUtils.getAnnotation(type, Convertable.class); if (convertable != null) { try { final Class<? extends Converter<?>> converterClass = convertable.value(); @SuppressWarnings("unchecked") final Converter<Object> converter = (Converter<Object>) ReflectionUtils.newInstance(converterClass); return converter.fromString(type, serializedForm); } catch (final Exception e) { logger.warn("Failed to convert fromString(" + serializedForm + ") using Convertable annotated converter class", e); } } throw new IllegalStateException("Could not find matching converter for type: " + type); } @Override public String toString(final Object instance) { if (null == instance) { return _nullConverter.toString(instance); } final Class<? extends Object> type = instance.getClass(); if (_arrayConverter.isConvertable(type)) { return _arrayConverter.toString(instance); } if (_mapStringToStringConverter.isConvertable(type)) { return _mapStringToStringConverter.toString((Map<?, ?>) instance); } for (final Converter<?> converter : _converters) { if (converter.isConvertable(type)) { @SuppressWarnings("unchecked") final Converter<Object> castedConverter = (Converter<Object>) converter; final String serializedForm = castedConverter.toString(instance); return SerializationStringEscaper.escape(serializedForm); } } final Convertable convertable = ReflectionUtils.getAnnotation(instance.getClass(), Convertable.class); if (convertable != null) { try { final Class<? extends Converter<?>> converterClass = convertable.value(); @SuppressWarnings("unchecked") final Converter<Object> converter = (Converter<Object>) ReflectionUtils.newInstance(converterClass); return SerializationStringEscaper.escape(converter.toString(instance)); } catch (final Exception e) { logger.warn("Failed to convert toString(" + instance + ") using Convertable annotated converter class", e); } } throw new IllegalStateException("Could not find matching converter for instance: " + instance); } @Override public boolean isConvertable(final Class<?> instance) { return true; } /** * Initializes all converters contained with injections * * @param injectionManager */ public void initializeAll(final InjectionManager injectionManager) { for (final Converter<?> converter : _converters) { initialize(converter, injectionManager); } } public void initialize(final Converter<?> converter, final InjectionManager injectionManager) { if (injectionManager != null) { final Field[] fields = ReflectionUtils.getAllFields(converter.getClass(), Inject.class); for (final Field field : fields) { final Object value; if (field.getType() == Converter.class) { // Injected converters are used as callbacks. They // should be assigned to the outer converter, which is // this. value = this; } else { final InjectionPoint<Object> injectionPoint = new MemberInjectionPoint<>(field, converter); value = injectionManager.getInstance(injectionPoint); } field.setAccessible(true); try { field.set(converter, value); } catch (final Exception e) { throw new IllegalStateException("Could not initialize converter: " + converter, e); } } } } }