/**
* 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.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.datacleaner.api.Converter;
import org.datacleaner.configuration.DataCleanerConfiguration;
import org.datacleaner.configuration.DataCleanerConfigurationImpl;
import org.datacleaner.configuration.InjectionManager;
import org.datacleaner.configuration.InjectionManagerFactory;
import org.datacleaner.configuration.InjectionPoint;
import org.datacleaner.configuration.SimpleInjectionPoint;
import org.datacleaner.job.AnalysisJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class for converting objects to and from string representations as
* used for example in serialized XML jobs.
*
* The string converter currently supports instances and arrays of:
* <ul>
* <li>Boolean</li>
* <li>Byte</li>
* <li>Short</li>
* <li>Integer</li>
* <li>Long</li>
* <li>Float</li>
* <li>Double</li>
* <li>Character</li>
* <li>String</li>
* <li>java.io.File</li>
* <li>java.util.Date</li>
* <li>java.sql.Date</li>
* <li>java.util.Calendar</li>
* <li>java.util.regex.Pattern</li>
* <li>org.datacleaner.reference.Dictionary</li>
* <li>org.datacleaner.reference.SynonymCatalog</li>
* <li>org.datacleaner.reference.StringPattern</li>
* <li>org.datacleaner.connection.Datastore</li>
* <li>org.apache.metamodel.schema.Column</li>
* <li>org.apache.metamodel.schema.Table</li>
* <li>org.apache.metamodel.schema.Schema</li>
* </ul>
*/
public final class StringConverter {
private static final Logger logger = LoggerFactory.getLogger(StringConverter.class);
private static final StringConverter SIMPLE_INSTANCE = new StringConverter(new DataCleanerConfigurationImpl());
private final InjectionManager _injectionManager;
private final DataCleanerConfiguration _configuration;
private final DelegatingConverter _baseConverter;
public StringConverter(final DataCleanerConfiguration configuration, final AnalysisJob job) {
this(getInjectionManager(configuration, job));
}
public StringConverter(final DataCleanerConfiguration configuration) {
if (configuration == null) {
throw new IllegalArgumentException("DataCleanerConfiguration cannot be null");
}
_configuration = configuration;
_injectionManager =
configuration.getEnvironment().getInjectionManagerFactory().getInjectionManager(configuration);
_baseConverter = createBaseConverter();
}
public StringConverter(final InjectionManager injectionManager) {
if (injectionManager == null) {
throw new IllegalArgumentException("InjectionManager cannot be null");
}
final InjectionPoint<DataCleanerConfiguration> injectionPoint =
SimpleInjectionPoint.of(DataCleanerConfiguration.class);
_configuration = injectionManager.getInstance(injectionPoint);
_injectionManager = injectionManager;
_baseConverter = createBaseConverter();
}
/**
* Gets a simple instance of {@link StringConverter}. This instance will not
* be able to convert all types as well as an instance that is bound to a
* specific {@link DataCleanerConfiguration}, a {@link InjectionManager} or
* an {@link AnalysisJob}.
*
* In other words: The instance will work for simple use-cases but is
* discouraged when it is possible to provide a bounded context object.
*
* @return
*/
public static StringConverter simpleInstance() {
return SIMPLE_INSTANCE;
}
private static InjectionManager getInjectionManager(final DataCleanerConfiguration configuration,
final AnalysisJob job) {
final InjectionManagerFactory injectionManagerFactory =
configuration.getEnvironment().getInjectionManagerFactory();
if (job == null) {
return injectionManagerFactory.getInjectionManager(configuration);
} else {
return injectionManagerFactory.getInjectionManager(configuration, job);
}
}
private DelegatingConverter createBaseConverter() {
final DelegatingConverter baseConverter = new DelegatingConverter();
baseConverter.addConverter(new ConfigurationItemConverter());
baseConverter.addConverter(getResourceConverter());
baseConverter.addConverter(new StandardTypeConverter(_configuration, baseConverter));
baseConverter.initializeAll(_injectionManager);
return baseConverter;
}
/**
* Serializes a Java object to a String representation.
*
* @param obj
* the object to serialize
* @return a String representation of the Java object
*/
public String serialize(final Object obj) {
return serialize(obj, new ArrayList<>(0));
}
/**
* @deprecated by {@link #serialize(Object, Converter)}
*/
public String serialize(final Object obj, final Class<? extends Converter<?>> converterClass) {
final Converter<?> converter = converterClass == null ? null : createConverter(converterClass);
return serialize(obj, converter);
}
public String serialize(final Object obj, final Converter<?> converter) {
final Collection<Converter<?>> col = new ArrayList<>();
if (converter != null) {
col.add(converter);
}
return serialize(obj, col);
}
/**
* Serializes a Java object to a String representation.
*
* @param obj
* the object to serialize
* @param converters
* an optional collection of custom converter classes
* @return a String representation of the Java object
*/
public String serialize(final Object obj, final Collection<Converter<?>> converters) {
final DelegatingConverter delegatingConverter = new DelegatingConverter();
if (converters != null) {
for (final Converter<?> converter : converters) {
delegatingConverter.addConverter(converter);
}
}
delegatingConverter.addConverter(new ConfigurationItemConverter());
delegatingConverter.addConverter(getResourceConverter());
delegatingConverter.addConverter(new StandardTypeConverter(_configuration, delegatingConverter));
delegatingConverter.initializeAll(_injectionManager);
return delegatingConverter.toString(obj);
}
private ResourceConverter getResourceConverter() {
if (_injectionManager == null) {
return new ResourceConverter(_configuration);
} else {
final ResourceConverter converter =
_injectionManager.getInstance(SimpleInjectionPoint.of(ResourceConverter.class));
if (converter == null) {
return new ResourceConverter(_configuration);
}
return converter;
}
}
private Converter<?> createConverter(final Class<? extends Converter<?>> converterClass) {
try {
return converterClass.newInstance();
} catch (final Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new IllegalStateException("Error occurred while using instantiating: " + converterClass, e);
}
}
/**
* Deserializes a String into a Java object of the particular type.
*
* @param str
* the serialized string representation
* @param type
* the requested type
* @return a Java object matching the String representation
*/
public <E> E deserialize(final String str, final Class<E> type) {
return deserialize(str, type, new ArrayList<>(0));
}
/**
* @deprecated by {@link #deserialize(String, Class, Converter)}
*/
public <E> E deserialize(final String str, final Class<E> type,
final Class<? extends Converter<?>> converterClass) {
if (converterClass == null) {
return deserialize(str, type);
}
return deserialize(str, type, createConverter(converterClass));
}
public <E> E deserialize(final String str, final Class<E> type, final Converter<?> converter) {
final Collection<Converter<?>> col = new ArrayList<>();
if (converter != null) {
col.add(converter);
}
return deserialize(str, type, col);
}
/**
* Deserializes a String into a Java object of the particular type.
*
* @param str
* the serialized string representation
* @param type
* the requested type
* @param converters
* an optional collection of custom converters to apply when
* deserializing
* @return a Java object matching the String representation
*/
public <E> E deserialize(final String str, final Class<E> type, final Collection<Converter<?>> converters) {
logger.debug("deserialize(\"{}\", {})", str, type);
if (converters == null || converters.isEmpty()) {
// when possible, just reuse the base converter
@SuppressWarnings("unchecked") final E result = (E) _baseConverter.fromString(type, str);
return result;
}
final DelegatingConverter delegatingConverter = new DelegatingConverter();
if (converters != null) {
for (final Converter<?> converter : converters) {
delegatingConverter.addConverter(converter);
delegatingConverter.initialize(converter, _injectionManager);
}
}
final List<Converter<?>> baseconverters = _baseConverter.getConverters();
for (final Converter<?> converter : baseconverters) {
delegatingConverter.addConverter(converter);
}
@SuppressWarnings("unchecked") final E result = (E) delegatingConverter.fromString(type, str);
return result;
}
}