org.jtransfo.internal.JTransfoImpl Maven / Gradle / Ivy
/*
* This file is part of jTransfo, a library for converting to and from transfer objects.
* Copyright (c) PROGS bvba, Belgium
*
* The program is available in open source according to the Apache License, Version 2.0.
* For full licensing details, see LICENSE.txt in the project root.
*/
package org.jtransfo.internal;
import org.jtransfo.AutomaticListTypeConverter;
import org.jtransfo.AutomaticSetTypeConverter;
import org.jtransfo.ClassReplacer;
import org.jtransfo.ConfigurableJTransfo;
import org.jtransfo.ConvertInterceptor;
import org.jtransfo.ConvertSourceTarget;
import org.jtransfo.Converter;
import org.jtransfo.JTransfo;
import org.jtransfo.JTransfoException;
import org.jtransfo.NeedsJTransfo;
import org.jtransfo.NoConversionTypeConverter;
import org.jtransfo.ObjectFinder;
import org.jtransfo.ObjectReplacer;
import org.jtransfo.PostConverter;
import org.jtransfo.PreConverter;
import org.jtransfo.ReadOnlyDomainAutomaticTypeConverter;
import org.jtransfo.ToConverter;
import org.jtransfo.ToDomainTypeConverter;
import org.jtransfo.TypeConverter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* jTransfo main access point standard implementation.
*/
public class JTransfoImpl implements JTransfo, ConfigurableJTransfo, ConvertSourceTarget {
private static final String[] DEFAULT_TAGS_WHEN_NO_TAGS = {JTransfo.DEFAULT_TAG_WHEN_NO_TAGS};
private ToHelper toHelper = new ToHelper();
private ConverterHelper converterHelper = new ConverterHelper();
private Map converters = new ConcurrentHashMap<>();
private List modifyableObjectFinders = new ArrayList<>();
private List internalObjectFinders = new ArrayList<>();
private LockableList objectFinders = new LockableList<>();
private List modifyableTypeConverters = new ArrayList<>();
private List internalTypeConverters = new ArrayList<>();
private List modifyablePreConverters = new ArrayList<>();
private List modifyablePostConverters = new ArrayList<>();
private List modifyableConvertInterceptors = new ArrayList<>();
private ConvertSourceTarget convertInterceptorChain;
private List modifyableObjectReplacers = new ArrayList<>();
private List modifyableClassReplacers = new ArrayList<>();
private LockableList objectReplacers = new LockableList<>();
private LockableList classReplacers = new LockableList<>();
/**
* Constructor.
*/
public JTransfoImpl() {
internalObjectFinders.add(new NewInstanceObjectFinder());
updateObjectFinders();
internalTypeConverters.add(new NoConversionTypeConverter());
internalTypeConverters.add(new ToDomainTypeConverter(this));
ReadOnlyDomainAutomaticTypeConverter readOnlyDomainAutomaticTypeConverter =
new ReadOnlyDomainAutomaticTypeConverter();
readOnlyDomainAutomaticTypeConverter.setSortList(true);
internalTypeConverters.add(readOnlyDomainAutomaticTypeConverter);
internalTypeConverters.add(new AutomaticSetTypeConverter());
AutomaticListTypeConverter automaticListTypeConverter = new AutomaticListTypeConverter();
automaticListTypeConverter.setSortList(true);
internalTypeConverters.add(automaticListTypeConverter);
updateTypeConverters();
updateConvertInterceptors();
updateObjectReplacers();
updateClassReplacers();
toHelper.setClassReplacer(this::replaceClass);
// CHECKSTYLE EMPTY_BLOCK: OFF
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class> plugin = cl.loadClass("org.jtransfo.internal.JTransfoJrebelPlugin");
Method setInstance = plugin.getMethod("setInstance", JTransfoImpl.class);
Method preInit = plugin.getMethod("preinit");
if (null != setInstance && null != preInit) {
Object instance = plugin.newInstance();
setInstance.invoke(instance, this);
preInit.invoke(instance);
}
System.out.println("jRebel reload support for jTransfo loaded.");
} catch (Throwable ex) {
// JRebel not found - will not reload directory service automatically.
}
// CHECKSTYLE EMPTY_BLOCK: ON
}
@Override
public List getTypeConverters() {
return modifyableTypeConverters;
}
@Override
public void updateTypeConverters() {
updateTypeConverters(null);
}
@Override
public void updateTypeConverters(List newConverters) {
if (null != newConverters) {
modifyableTypeConverters.clear();
modifyableTypeConverters.addAll(newConverters);
}
ArrayList allConverters = new ArrayList<>();
allConverters.addAll(modifyableTypeConverters);
allConverters.addAll(internalTypeConverters);
for (TypeConverter tc : allConverters) {
if (tc instanceof NeedsJTransfo) {
((NeedsJTransfo) tc).setJTransfo(this);
}
}
converterHelper.setTypeConvertersInOrder(allConverters);
}
@Override
public List getPreConverters() {
return modifyablePreConverters;
}
@Override
public void updatePreConverters() {
updatePreConverters(null);
}
@Override
public void updatePreConverters(List newConverters) {
if (null != newConverters) {
modifyablePreConverters.clear();
modifyablePreConverters.addAll(newConverters);
}
ArrayList allConverters = new ArrayList<>();
allConverters.addAll(modifyablePreConverters);
for (PreConverter pc : allConverters) {
if (pc instanceof NeedsJTransfo) {
((NeedsJTransfo) pc).setJTransfo(this);
}
}
converterHelper.setPreConverters(allConverters);
}
@Override
public List getPostConverters() {
return modifyablePostConverters;
}
@Override
public void updatePostConverters() {
updatePostConverters(null);
}
@Override
public void updatePostConverters(List newConverters) {
if (null != newConverters) {
modifyablePostConverters.clear();
modifyablePostConverters.addAll(newConverters);
}
ArrayList allConverters = new ArrayList<>();
allConverters.addAll(modifyablePostConverters);
for (PostConverter pc : allConverters) {
if (pc instanceof NeedsJTransfo) {
((NeedsJTransfo) pc).setJTransfo(this);
}
}
converterHelper.setPostConverters(allConverters);
}
@Override
public List getObjectFinders() {
return modifyableObjectFinders;
}
@Override
public void updateObjectFinders() {
updateObjectFinders(null);
}
@Override
public void updateObjectFinders(List newObjectFinders) {
if (null != newObjectFinders) {
modifyableObjectFinders.clear();
modifyableObjectFinders.addAll(newObjectFinders);
}
LockableList newList = new LockableList<>();
newList.addAll(internalObjectFinders);
newList.addAll(modifyableObjectFinders);
newList.lock();
objectFinders = newList;
}
@Override
public List getObjectReplacers() {
return modifyableObjectReplacers;
}
@Override
public void updateObjectReplacers() {
updateObjectReplacers(null);
}
@Override
public void updateObjectReplacers(List newObjectReplacers) {
if (null != newObjectReplacers) {
modifyableObjectReplacers.clear();
modifyableObjectReplacers.addAll(newObjectReplacers);
}
LockableList newList = new LockableList<>();
newList.addAll(modifyableObjectReplacers);
newList.lock();
objectReplacers = newList;
}
@Override
public List getClassReplacers() {
return modifyableClassReplacers;
}
@Override
public void updateClassReplacers() {
updateClassReplacers(null);
}
@Override
public void updateClassReplacers(List newClassReplacers) {
if (null != newClassReplacers) {
modifyableClassReplacers.clear();
modifyableClassReplacers.addAll(newClassReplacers);
}
LockableList newList = new LockableList<>();
newList.addAll(modifyableClassReplacers);
newList.lock();
classReplacers = newList;
}
/**
* Get the list of {@link ConvertInterceptor}s to allow customization.
*
* The elements are tried in reverse order (from end to start of list).
*
* You are explicitly allowed to change this list, but beware to do this from one thread only.
*
* Changes in the list are not used until you call {@link #updateConvertInterceptors()}.
*
*
* @return list of object finders
*/
public List getConvertInterceptors() {
return modifyableConvertInterceptors;
}
/**
* Update the list of convert interceptors which is used based on the internal list
* (see {@link #getConvertInterceptors()}.
*/
public void updateConvertInterceptors() {
updateConvertInterceptors(null);
}
/**
* Update the list of convert interceptors which is used.
*
* When null is passed, this updates the changes to the internal list (see {@link #getConvertInterceptors()}.
* Alternatively, you can pass the new list explicitly.
*
*
* @param newConvertInterceptors new list of convert interceptors
*/
public void updateConvertInterceptors(List newConvertInterceptors) {
if (null != newConvertInterceptors) {
modifyableConvertInterceptors.clear();
modifyableConvertInterceptors.addAll(newConvertInterceptors);
}
ConvertSourceTarget chainPart = this; // actual conversion at the end of the list
for (int i = modifyableConvertInterceptors.size() - 1; i >= 0; i--) {
chainPart = new ConvertInterceptorChainPiece(modifyableConvertInterceptors.get(i), chainPart);
}
convertInterceptorChain = chainPart;
}
@Override
public T convert(Object source, T target, String... tags) {
if (null == source || null == target) {
throw new JTransfoException("Source and target are required to be not-null.");
}
source = replaceObject(source);
boolean targetIsTo = false;
if (!toHelper.isTo(source)) {
targetIsTo = true;
if (!toHelper.isTo(target)) {
throw new JTransfoException(String.format("Neither source nor target are annotated with DomainClass " +
"on classes %s and %s.", source.getClass().getName(), target.getClass().getName()));
}
}
if (0 == tags.length) {
tags = DEFAULT_TAGS_WHEN_NO_TAGS;
}
return convertInterceptorChain.convert(source, target, targetIsTo, tags);
}
@Override
public T convert(Object source, T target, boolean targetIsTo, String... tags) {
source = replaceObject(source);
ToConverter toConverter;
List converters;
PreConverter.Result shouldConvert;
if (targetIsTo) {
toConverter = getToConverter(target.getClass());
converters = toConverter.getToTo();
shouldConvert = toConverter.getPreConverter().preConvertToTo(source, target, tags);
} else {
toConverter = getToConverter(source.getClass());
converters = toConverter.getToDomain();
shouldConvert = toConverter.getPreConverter().preConvertToDomain(source, target, tags);
}
if (PreConverter.Result.CONTINUE == shouldConvert) {
for (Converter converter : converters) {
converter.convert(source, target, tags);
}
}
return target;
}
@Override
public Object convert(Object source) {
if (null == source) {
return null;
}
source = replaceObject(source);
Class> domainClass = getDomainClass(source.getClass());
return convertTo(source, domainClass);
}
@Override
public T convertTo(Object source, Class targetClass, String... tags) {
if (null == source) {
return null;
}
source = replaceObject(source);
Class realTargetClass = replaceClass(targetClass);
return (T) convert(source, findTarget(source, realTargetClass, tags), tags);
}
@Override
public List convertList(List> source, Class targetClass, String... tags) {
if (null == source) {
return null;
}
List result = new ArrayList<>();
for (Object object : source) {
result.add(convertTo(object, targetClass, tags));
}
return result;
}
@Override
public T findTarget(Object source, Class targetClass, String... tags) {
if (null == source) {
return null;
}
source = replaceObject(source);
int i = objectFinders.size() - 1;
T target = null;
Class realTargetClass = replaceClass(targetClass);
if (isToClass(realTargetClass)) {
realTargetClass = (Class) getToSubType(realTargetClass, source);
}
while (null == target && i >= 0) {
target = objectFinders.get(i--).getObject(realTargetClass, source, tags);
}
if (null == target) {
throw new JTransfoException("Cannot create instance of target class " + realTargetClass.getName() +
" for source object " + source + ".");
}
return target;
}
@Override
public Class> getDomainClass(Class> toClass) {
return toHelper.getDomainClass(toClass);
}
@Override
public boolean isToClass(Class> toClass) {
return toHelper.isToClass(toClass);
}
@Override
public Class> getToSubType(Class> toType, Object domainObject) {
return toHelper.getToSubType(toType, replaceObject(domainObject));
}
/**
* Clear cache with converters. Needed when classes are reloaded by something like jRebel or spring reloaded.
*/
public void clearCaches() {
converters.clear();
}
private ToConverter getToConverter(Class toClass) {
ToConverter toConverter = converters.get(toClass);
if (null == toConverter) {
Class> domainClass = getDomainClass(toClass);
toConverter = converterHelper.getToConverter(toClass, domainClass);
converters.put(toClass, toConverter);
}
return toConverter;
}
private Object replaceObject(Object object) {
Object res = object;
for (ObjectReplacer replacer : objectReplacers) {
res = replacer.replaceObject(res);
}
return res;
}
private Class replaceClass(Class clazz) {
Class res = clazz;
for (ClassReplacer replacer : classReplacers) {
res = replacer.replaceClass(res);
}
return res;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy