/** * AnalyzerBeans * 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.eobjects.analyzer.util.convert; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.inject.Inject; import org.eobjects.analyzer.beans.api.Convertable; import org.eobjects.analyzer.beans.api.Converter; import org.eobjects.analyzer.configuration.InjectionManager; import org.eobjects.analyzer.configuration.InjectionPoint; import org.eobjects.analyzer.lifecycle.MemberInjectionPoint; import org.eobjects.analyzer.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 static final String[][] ESCAPE_MAPPING = { { "&", "&" }, { "[", "[" }, { "]", "]" }, { ",", "," }, { "<", "<" }, { ">", ">" }, { """, "\"" }, { "©", "\u00a9" }, { "®", "\u00ae" }, { "€", "\u20a0" } }; private final List<Converter<?>> _converters; private final NullConverter _nullConverter; private final ArrayConverter _arrayConverter; public DelegatingConverter() { this(null); } public DelegatingConverter(Collection<Converter<?>> converters) { _converters = new ArrayList<Converter<? extends Object>>(); _nullConverter = new NullConverter(); _arrayConverter = new ArrayConverter(this); if (converters != null) { _converters.addAll(converters); } } public List<Converter<?>> getConverters() { return _converters; } public void addConverter(Converter<?> converter) { _converters.add(converter); } @Override public Object fromString(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 = unescape(serializedForm); for (Converter<?> converter : _converters) { if (converter.isConvertable(type)) { Object result = converter.fromString(type, serializedForm); return result; } } if (ReflectionUtils.is(type, List.class)) { return _arrayConverter.fromString(type, serializedForm); } Convertable convertable = ReflectionUtils.getAnnotation(type, Convertable.class); if (convertable != null) { try { Class<? extends Converter<?>> converterClass = convertable.value(); @SuppressWarnings("unchecked") Converter<Object> converter = (Converter<Object>) ReflectionUtils.newInstance(converterClass); return converter.fromString(type, serializedForm); } catch (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(Object instance) { if (null == instance) { return _nullConverter.toString(instance); } final Class<? extends Object> type = instance.getClass(); if (_arrayConverter.isConvertable(type)) { return _arrayConverter.toString(instance); } for (Converter<?> converter : _converters) { if (converter.isConvertable(type)) { @SuppressWarnings("unchecked") Converter<Object> castedConverter = (Converter<Object>) converter; String serializedForm = castedConverter.toString(instance); serializedForm = escape(serializedForm); return serializedForm; } } Convertable convertable = ReflectionUtils.getAnnotation(instance.getClass(), Convertable.class); if (convertable != null) { try { Class<? extends Converter<?>> converterClass = convertable.value(); @SuppressWarnings("unchecked") Converter<Object> converter = (Converter<Object>) ReflectionUtils.newInstance(converterClass); return converter.toString(instance); } catch (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(Class<?> instance) { return true; } /** * Initializes all converters contained with injections * * @param injectionManager */ public void initializeAll(InjectionManager injectionManager) { if (injectionManager != null) { for (Converter<?> converter : _converters) { Field[] fields = ReflectionUtils.getFields(converter.getClass(), Inject.class); for (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 { InjectionPoint<Object> injectionPoint = new MemberInjectionPoint<Object>(field, converter); value = injectionManager.getInstance(injectionPoint); } field.setAccessible(true); try { field.set(converter, value); } catch (Exception e) { throw new IllegalStateException("Could not initialize converter: " + converter, e); } } } } } private static final String escape(String str) { for (String[] mapping : ESCAPE_MAPPING) { String escapedValue = mapping[1]; if (str.contains(escapedValue)) { str = str.replace(escapedValue, mapping[0]); } } return str; } private static final String unescape(String str) { for (String[] mapping : ESCAPE_MAPPING) { String unescapedValue = mapping[0]; if (str.contains(unescapedValue)) { str = str.replaceAll(unescapedValue, mapping[1]); } } return str; } }