
org.leialearns.bridge.BridgeFactory Maven / Gradle / Ivy
package org.leialearns.bridge;
import org.leialearns.common.ExceptionWrapper;
import org.leialearns.common.HasWrappedIterable;
import org.leialearns.common.Setting;
import org.leialearns.common.TransformingIterable;
import org.leialearns.common.TypedIterable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import static org.leialearns.bridge.Static.getFarObject;
import static org.leialearns.common.Display.asDisplayWithTypes;
import static org.leialearns.common.Display.display;
import static org.leialearns.common.Display.displayWithTypes;
import static org.leialearns.common.Display.displayParts;
import static org.leialearns.common.Static.getLoggingClass;
import static org.leialearns.common.Static.offer;
import static org.leialearns.common.Static.offerArray;
/**
* Provides a factory with supporting classes that generates implementations of a logical interface whose methods are
* delegated to various helper objects on both sides of an abstraction separation. See {@link org.leialearns.bridge}.
*
* This class is designed for use in Spring to define several beans, one for each logical interface
* (near type). A bridge factory bean is defined by its far type, i.e., the type of
* its concrete delegation index. The logical interface is determined by reflection (by looking up the return
* type of the {@link DeclaresNearType#declareNearType() declareNearType} method.
*
* Additional helpers can be specified as desired. Bindings for the methods specified in the logical interface
* are looked up when the factory is instantiated in the order in which the helpers are given. If a method is not found
* in one of the helpers it is looked up in the far type. If it is desired to search the far type before some of the
* helpers, include a null pointer in the array of helpers.
*
* There are five types of helpers:
*
* - Helper object that uses near types exclusively
* - Class object
* - Helper object that uses far types exclusively
* - Helper object that mixes near and far types
* null
*
* For helpers of type 2 a new instance is created using the default no-arg constructor for each bridge object
* that is created by the factory. These instances are not configured by the spring bean factory. The helper
* null
represents the far object. For the other three types of helpers, either the near object or the
* far object is prepended to the parameter list, as appropriate. The use of helpers of type 4 is strongly
* discouraged.
*
* In the context of an ORM framework it is common to use not only the DAO of that corresponds the far type as a
* helper, but also DAOs of other entities. For example, the SymbolDAO can provide a method that finds all
* symbols that belong to a particular alphabet. This method can be exposed very naturally on the Alphabet bridged
* object.
*
* Spring configuration example
*
* <bean id="alphabetFactory" class="org.leialearns.bridge.BridgeFactory">
* <constructor-arg value="org.leialearns.jpa.interaction.AlphabetDTO"/>
* <constructor-arg>
* <array>
* <ref bean="alphabetDAO"/>
* <ref bean="symbolDAO"/>
* <null/> <!-- AlphabetDTO -->
* <value type="java.lang.Class">org.leialearns.logic.interaction.AlphabetAugmenter</value>
* </array>
* </constructor-arg>
* </bean>
*
*
* @see org.leialearns.bridge
*/
public class BridgeFactory {
private final Logger logger = LoggerFactory.getLogger(getLoggingClass(this));
private final Class> nearType;
private final Class extends FarObject>> farType;
private final Object[] helpers;
private final Map methodMap = new HashMap<>();
private final Method facetsGetter;
private final Method facetsChecker;
private BridgeHeadTypeRegistry registry;
/**
* Creates a new BridgeFactory instance.
*
* @param farTypeName The name of the far type class
* @param helpers The helpers that implement additional methods on the near type
*/
public BridgeFactory(String farTypeName, Object... helpers) {
this(helpers, getFarType(farTypeName));
}
/**
* Creates a new BridgeFactory instance.
*
* @param farType The far type class
* @param helpers The helpers that implement additional methods on the near type
*/
public BridgeFactory(Class extends FarObject>> farType, Object... helpers) {
this(helpers, farType);
}
protected BridgeFactory(Object[] helpers, Class extends FarObject>> farType) {
this.helpers = (helpers == null ? new Object[0] : helpers);
this.farType = farType;
nearType = getNearType(farType);
if (logger.isTraceEnabled()) {
logger.trace(display(nearType) + ": " + display(farType));
}
try {
facetsGetter = BridgeFacet.class.getMethod("getBridgeFacets");
facetsChecker = BridgeFacet.class.getMethod("hasBridgeFacets");
} catch (Throwable throwable) {
throw ExceptionWrapper.wrap(throwable);
}
}
/**
* Returns the near type of the bridge.
* @return The near type
*/
public Class> getNearType() {
return nearType;
}
/**
* Returns the far type of the bridge.
* @return The far type
*/
public Class> getFarType() {
return farType;
}
protected static Class> getNearType(Class> farType) {
Class> result;
try {
Method typeGetter = farType.getMethod("declareNearType");
result = typeGetter.getReturnType();
} catch (Throwable throwable) {
throw ExceptionWrapper.wrap(throwable);
}
return result;
}
/**
* Sets the {@link org.leialearns.bridge.BridgeHeadTypeRegistry BridgeHeadTypeRegistry}, registers this factory
* instance with it, and looks up bindings for the methods in the near type.
* @param registry The BridgeHeadTypeRegistry instance to use
*/
@Autowired
public void setRegistry(BridgeHeadTypeRegistry registry) {
this.registry = registry;
registry.register(this);
Method[] methods = offerArray(Object.class.getMethods(), nearType.getMethods());
for (Method method : methods) {
if (logger.isTraceEnabled()) {
logger.trace("Method: " + display(method) + ": in: " + display(nearType));
}
Binding binding = getBinding(method.getReturnType(), method.getName(), method.getParameterTypes());
if (binding == null) {
String message = "No binding found: " + display(nearType) + ": " + display(method);
logger.warn(message);
throw new UnsupportedOperationException(message);
}
if (logger.isTraceEnabled()) {
logger.trace("Binding: " + display(binding));
}
methodMap.put(method, binding);
}
}
/**
* Finds or creates a near object for the given far object.
* @param farObject The far object that is to be wrapped by the near object
* @return The near object that corresponds to the given far object
*/
public Object getNearObject(Object farObject) {
Object nearObject = null;
if (farObject instanceof BridgeFacet) {
BridgeFacet facet = (BridgeFacet) farObject;
if (facet.hasBridgeFacets()) {
BridgeFacets facets = facet.getBridgeFacets();
if (facets.hasNearObject()) {
nearObject = nearType.cast(facets.getNearObject());
}
}
}
if (nearObject == null) {
if (!farType.isInstance(farObject)) {
RuntimeException exception = new ClassCastException("Class: " + display(farType) + ": object: " + display(farObject));
if (logger.isTraceEnabled()) {
logger.trace("Far type: " + display(farType), exception);
}
throw exception;
}
Class>[] interfaces = new Class[] { BridgeFacet.class };
final BridgeFacets facets;
if (farObject instanceof BaseBridgeFacet && ((BaseBridgeFacet) farObject).hasBridgeFacets()) {
facets = ((BaseBridgeFacet) farObject).getBridgeFacets();
} else {
Map helperInstances = new HashMap<>();
for (int i = 0; i < helpers.length; i++) {
Object helper = helpers[i];
if (helper instanceof Class) {
Class> helperType = (Class>) helper;
try {
Object helperInstance = helperType.newInstance();
helperInstances.put(i, helperInstance);
} catch (Throwable throwable) {
throw ExceptionWrapper.wrap(throwable);
}
}
}
facets = new BridgeFacets(farType.cast(farObject), this, helperInstances);
for (Object helperInstance : helperInstances.values()) {
if (helperInstance instanceof BaseBridgeFacet) {
((BaseBridgeFacet) helperInstance).setBridgeFacets(facets);
}
}
}
if (farObject instanceof BaseBridgeFacet && !((BaseBridgeFacet) farObject).hasBridgeFacets()) {
((BaseBridgeFacet) farObject).setBridgeFacets(facets);
}
nearObject = newProxyInstance(getClass().getClassLoader(), nearType, interfaces, (object, method, parameters) -> {
Object result;
if (logger.isTraceEnabled()) {
logger.trace("Facets getter: " + display(facetsGetter));
}
if (method.equals(facetsGetter)) {
result = facets;
} else if (method.equals(facetsChecker)) {
result = Boolean.TRUE;
} else {
Binding binding = methodMap.get(method);
if (binding == null) {
String message = "No binding found: " + display(nearType) + ": " + display(method);
logger.warn(message);
throw new UnsupportedOperationException(message);
}
result = binding.invoke(facets, parameters);
Class> returnType = method.getReturnType();
if (logger.isTraceEnabled()) {
logger.trace("Return type: " + displayWithTypes(returnType));
}
if (result instanceof TypedIterable) {
TypedIterable> typedIterable = (TypedIterable>) result;
Class> baseType = typedIterable.getType();
if (logger.isTraceEnabled()) {
logger.trace("Iterable base type: " + display(baseType));
}
if (registry.hasFarType(baseType)) {
Class> type = getNearType(baseType);
result = getAdaptedIterable(returnType, typedIterable, type);
} else if (registry.hasNearType(baseType)) {
result = getAdaptedIterable(returnType, typedIterable, baseType);
} else if (logger.isTraceEnabled()) {
logger.trace("Iterable base type not found in registry");
}
} else if (registry.hasNearType(returnType)) {
Class> farType1 = registry.getFarType(method.getReturnType());
if (logger.isTraceEnabled()) {
logger.trace("Far type of return type: " + display(farType1));
}
if (farType1.isInstance(result)) {
BridgeFactory factory = registry.getBridgeFactory(farType1);
if (logger.isTraceEnabled()) {
logger.trace("Far type of return type factory: " + display(factory.getFarType()));
logger.trace("Result: " + display(result));
logger.trace("Return type: " + display(returnType) + ": factory PO type: " + display(factory.getNearType()));
}
try {
result = factory.getNearObject(result);
} catch (ClassCastException exception) {
if (logger.isTraceEnabled()) {
logger.trace("Stack trace", exception);
}
throw exception;
}
}
} else if (logger.isTraceEnabled()) {
logger.trace("Not adapted");
}
}
return result;
});
facets.setNearObject(nearObject);
}
return nearObject;
}
protected Object getAdaptedIterable(Class> returnType, TypedIterable> typedIterable, Class> type) {
Class> baseType = typedIterable.getType();
if (DeclaresNearType.class.isAssignableFrom(returnType)) {
Method declaration;
try {
declaration = returnType.getDeclaredMethod("declareNearType");
} catch (NoSuchMethodException exception) {
throw ExceptionWrapper.wrap(exception);
}
Class> declaredType = declaration.getReturnType();
if (!declaredType.isAssignableFrom(type)) {
throw new ClassCastException("Near object list: " + display(baseType) + " # " + display(declaredType));
} else if (logger.isTraceEnabled()) {
logger.trace("Mapped list: " + display(baseType) + " -> " + display(declaredType));
}
} else if (logger.isTraceEnabled()) {
logger.trace("Return type does not declare near type: " + display(returnType));
}
Object result = null;
if (returnType.isInterface() && NearIterable.class.isAssignableFrom(returnType)) {
if (logger.isTraceEnabled()) {
logger.trace("Return near object iterable");
}
result = getNearObjectList(typedIterable, returnType, type, baseType);
} else if (TypedIterable.class.isAssignableFrom(returnType)) {
if (logger.isTraceEnabled()) {
logger.trace("Return typed iterable that casts items on-the-fly");
}
result = getTypedIterable(typedIterable, type, baseType);
}
if (result == null) {
result = typedIterable;
}
return result;
}
protected Object getNearObjectList(final TypedIterable> typedIterable, Class> returnType, final Class type, Class> baseType) {
Class> interfaces[] = new Class>[] { returnType, HasWrappedIterable.class };
final TypedIterable delegate = getTypedIterable(typedIterable, type, baseType);
Method method;
try {
method = HasWrappedIterable.class.getDeclaredMethod("getWrappedIterable");
} catch (NoSuchMethodException exception) {
logger.warn("Method 'getWrappedIterable' not found", exception);
method = null;
}
final Method wrappedGetter = method;
return Proxy.newProxyInstance(getClass().getClassLoader(), interfaces, (object, invokedMethod, objects) -> {
Method delegateMethod = null;
if (objects == null || objects.length < 1) {
try {
delegateMethod = delegate.getClass().getMethod(invokedMethod.getName());
} catch (NoSuchMethodException exception) {
// Ignore
}
}
if (delegateMethod == wrappedGetter) {
return typedIterable;
}
if (delegateMethod == null) {
throw new UnsupportedOperationException(display(invokedMethod));
}
return delegateMethod.invoke(delegate);
});
}
protected TypedIterable> getTypedIterable(TypedIterable> typedIterable, final Class> type, Class> baseType) {
TypedIterable> result;
if (registry.hasNearType(baseType)) {
result = typedIterable;
} else {
result = getBridgedTypedIterable(typedIterable, type, baseType);
}
return result;
}
protected TypedIterable getBridgedTypedIterable(TypedIterable> typedIterable, final Class type, Class> baseType) {
final BridgeFactory factory = registry.getBridgeFactory(baseType);
return new BaseNearIterable<>(typedIterable, type, x -> type.cast(factory.getNearObject(x)));
}
/**
* Returns the array of helpers used by this factory instance
* @return The array of helpers used by this factory instance
*/
@SuppressWarnings("unused")
public Object[] getHelpers() {
return helpers.clone();
}
/**
* Returns a helper of the given type. If more than one helper is an instance of the type, then the one that
* occurs first in the helper array is returned. An {@link java.lang.IllegalStateException IllegalStateException}
* is thrown when no helper is found.
*
* @param type The class that represents the type of the requested helper
* @param The type of the requested helper
* @return The requested helper
*/
@SuppressWarnings("unused")
public T getHelper(Class type) {
T result = null;
for (Object candidate : helpers) {
if (type.isInstance(candidate)) {
result = type.cast(candidate);
break;
}
}
if (result == null) {
if (logger.isTraceEnabled()) {
logger.trace("Near type: " + display(nearType));
for (Object helper : helpers) {
logger.trace("Helper type: " + display(helper.getClass()));
}
}
logger.warn("No helper found: Requested type: " + display(type));
throw new IllegalStateException("No helper found of type: " + type.getSimpleName());
}
if (logger.isTraceEnabled()) {
logger.trace("Helper found: " + display(result));
}
return result;
}
@SuppressWarnings("unchecked")
protected I newProxyInstance(ClassLoader classLoader, Class type, Class>[] otherTypes, InvocationHandler invocationHandler) {
Class>[] interfaces = offer(type, otherTypes);
if (logger.isTraceEnabled()) {
logger.trace(display(interfaces));
}
return (I) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
protected Binding getBinding(Class> returnType, String name, Class>[] parameterTypes) {
Signature signature = new Signature(returnType, name, parameterTypes);
Binding result;
result = null;
for (int i = 0; i < helpers.length; i++) {
Object helper = helpers[i];
if (helper == null) {
result = findBinding(farType, -1, signature, null);
} else if (helper instanceof Class) {
result = findBinding((Class>) helper, i, signature, null);
} else {
result = findBinding(helper.getClass(), i, signature, nearType);
if (result != null) {
break;
}
result = findBinding(helper.getClass(), i, signature, farType);
}
if (result != null) {
break;
}
}
if (result == null) {
result = findBinding(farType, -1, signature, null);
}
if (result == null) {
result = findBinding(Object.class, -1, signature, null);
}
return result;
}
protected Binding findBinding(Class> type, int delegationIndex, Signature signature, Class> targetType) {
if (logger.isTraceEnabled()) {
logger.trace("Helper class: " + displayWithTypes(type) + ": " + display(targetType));
}
Class>[] actualParameterTypes;
if (targetType == null) {
actualParameterTypes = signature.parameterTypes;
} else {
actualParameterTypes = signature.getBoundParameterTypes(targetType);
}
if (logger.isTraceEnabled()) {
logger.trace(signature.name + "(" + display(actualParameterTypes) + ")");
}
Method result = null;
List adapters = null;
for (Method method : type.getMethods()) {
if (!method.getName().equals(signature.name)) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace("Candidate: " + display(method.getDeclaringClass()) + "." + method.getName() + "(" + display(method.getParameterTypes()) + ") -> " + display(method.getReturnType()));
}
Class>[] methodParameterTypes = method.getParameterTypes();
List candidateAdapters = canBeAdaptedTo(methodParameterTypes, actualParameterTypes);
if (candidateAdapters != null) {
if (result == null || canBeAdaptedTo(result.getParameterTypes(), methodParameterTypes) != null) {
result = method;
adapters = candidateAdapters;
}
}
}
boolean useDTO = targetType != null && FarObject.class.isAssignableFrom(targetType);
if (logger.isTraceEnabled()) {
logger.trace(display(targetType) + ": useDTO: " + useDTO);
}
return (result == null ? null : new Binding(delegationIndex, useDTO, adapters, result));
}
protected List canBeAdaptedTo(Class>[] formalParameterTypes, Class>[] actualParameterTypes) {
if (logger.isTraceEnabled()) {
logger.trace(display(actualParameterTypes) + " -> " + display(formalParameterTypes));
}
List adapters;
if (actualParameterTypes.length == formalParameterTypes.length) {
adapters = new ArrayList<>();
for (int i = 0; i < formalParameterTypes.length; i++) {
Class> formalParameterType = formalParameterTypes[i];
Class> actualParameterType = actualParameterTypes[i];
if (logger.isTraceEnabled()) {
logger.trace(display(formalParameterType) + " <=? " + display(actualParameterType));
}
if (!canBeAdaptedTo(formalParameterType, actualParameterType, adapters, i)) {
adapters = null;
break;
}
}
} else {
adapters = null;
}
if (logger.isTraceEnabled()) {
logger.trace("Adapter List: " + adapters);
}
return adapters;
}
protected boolean canBeAdaptedTo(Class> formalParameterType, Class> actualParameterType, Collection adapters, int i) {
boolean result = false;
if (actualParameterType == null) {
result = true;
} else if (formalParameterType.isAssignableFrom(actualParameterType)) {
if (TypedIterable.class.isAssignableFrom(actualParameterType)) {
adapters.add(new TypedIterableAdapter(i));
}
result = true;
} else if (Iterable.class.isAssignableFrom(formalParameterType) && NearIterable.class.isAssignableFrom(actualParameterType)) {
adapters.add(new BridgeIterableAdapter(i, actualParameterType));
result = true;
} else if (FarObject.class.isAssignableFrom(formalParameterType)) {
Class> nearType = getNearType(formalParameterType);
if (nearType.isAssignableFrom(actualParameterType)) {
adapters.add(new BridgeScalableAdapter(i, formalParameterType));
result = true;
}
}
return result;
}
protected interface BridgeAdapter {
Object adapt(Object nearObject);
int getIndex();
}
protected class BridgeScalableAdapter implements BridgeAdapter {
private final int index;
private final Class> adapterFarType;
protected BridgeScalableAdapter(int index, Class> adapterFarType) {
this.index = index;
this.adapterFarType = adapterFarType;
}
@Override
public Object adapt(Object nearObject) {
return getFarObject(nearObject);
}
@Override
public int getIndex() {
return index;
}
@Override
public String toString() {
return displayParts(getClass().getSimpleName(), adapterFarType.getSimpleName(), index);
}
}
protected class TypedIterableAdapter implements BridgeAdapter {
private final int index;
protected TypedIterableAdapter(int index) {
this.index = index;
}
public Object adapt(Object nearObject) {
Object result;
TypedIterable> iterable = (TypedIterable>) nearObject;
if (registry.hasNearType(iterable.getType())) {
Class> farType = registry.getFarType(iterable.getType());
result = adaptIterable(iterable, farType);
} else {
result = nearObject;
}
return result;
}
protected TypedIterable adaptIterable(TypedIterable iterable, final Class farType) {
return new TransformingIterable<>(
iterable,
farType,
x -> farType.cast(getFarObject(x))
);
}
@Override
public int getIndex() {
return index;
}
}
protected class BridgeIterableAdapter implements BridgeAdapter {
private final int index;
private final Class> adapterNearType;
private final Setting> adapterFarType = new Setting<>("Iterable adapter far type", new Supplier>() {
@Override
public Class> get() {
return registry.getFarType(adapterNearType);
}
});
protected BridgeIterableAdapter(int index, Class> iteratorType) {
if (!NearIterable.class.isAssignableFrom(iteratorType)) {
throw new IllegalArgumentException("Should implement NearIterable: " + display(iteratorType));
}
Method typeDeclaration;
try {
typeDeclaration = iteratorType.getDeclaredMethod("declareNearType");
} catch (NoSuchMethodException exception) {
throw ExceptionWrapper.wrap(exception);
}
this.adapterNearType = typeDeclaration.getReturnType();
this.index = index;
}
@Override
public Object adapt(Object nearObject) {
Object result = null;
if (nearObject instanceof HasWrappedIterable) {
Iterable> wrappedIterable = ((HasWrappedIterable) nearObject).getWrappedIterable();
if (wrappedIterable instanceof TypedIterable>) {
TypedIterable> typedIterable = (TypedIterable>) wrappedIterable;
if (adapterFarType.get().isAssignableFrom(typedIterable.getType())) {
result = wrappedIterable;
}
}
}
if (result == null) {
Iterable> iterable = (Iterable>) nearObject;
result = getTransformingIterable(iterable, adapterFarType.get());
}
logger.debug("Adapted: {} -> {}", asDisplayWithTypes(getType(nearObject)), asDisplayWithTypes(getType(result)));
return result;
}
protected TransformingIterable getTransformingIterable(Iterable> iterable, final Class farType) {
return new TransformingIterable<>(iterable, farType, x -> farType.cast(getFarObject(x)));
}
@Override
public int getIndex() {
return index;
}
@Override
public String toString() {
return displayParts(getClass().getSimpleName(), adapterNearType.getSimpleName(), index);
}
}
@SuppressWarnings("unchecked")
protected static Class extends FarObject>> getFarType(String farTypeName) {
Class> result;
try {
result = Class.forName(farTypeName);
} catch (Throwable throwable) {
throw ExceptionWrapper.wrap(throwable);
}
if (!FarObject.class.isAssignableFrom(result)) {
throw new IllegalArgumentException("Does not extend FarObject: [" + farTypeName + "]");
}
return (Class extends FarObject>>) result;
}
protected static Class> getType(Object object) {
return object == null ? null : object.getClass();
}
protected static class Signature {
private final Class> returnType;
private final String name;
private final Class>[] parameterTypes;
public Signature(Class> returnType, String name, Class>[] parameterTypes) {
this.returnType = returnType;
this.name = name;
for (Class> parameterType : parameterTypes) {
if (parameterType == null) {
throw new IllegalArgumentException("Parameter type may not be null: " + display(parameterTypes));
}
}
this.parameterTypes = parameterTypes;
}
protected Class>[] getBoundParameterTypes(Class> type) {
return offer(type, parameterTypes);
}
@Override
public int hashCode() {
int result = returnType.hashCode() + name.hashCode();
for (Class> parameterType : parameterTypes) {
result += parameterType.hashCode();
}
return result;
}
@Override
public boolean equals(Object other) {
boolean result;
if (other instanceof Signature) {
Signature otherSignature = Signature.class.cast(other);
result = otherSignature.returnType.equals(returnType);
result = result && otherSignature.name.equals(name) && otherSignature.parameterTypes.length == parameterTypes.length;
for (int i = 0; i < parameterTypes.length; i++) {
if (!result) {
break;
}
result = otherSignature.parameterTypes[i] == parameterTypes[i];
}
} else {
result = false;
}
return result;
}
}
protected class Binding {
private final int delegationIndex;
private final boolean useDTO;
private final List adapters;
private final Method method;
public Binding(int delegationIndex, boolean useDTO, List adapters, Method method) {
this.delegationIndex = delegationIndex;
this.useDTO = useDTO;
this.adapters = adapters;
this.method = method;
if (logger.isTraceEnabled()) {
logger.trace(delegationIndex + ": " + useDTO + ": " + display(method));
}
}
public Object invoke(BridgeFacets facets, Object[] parameters) {
Object[] adapted = (parameters == null ? new Object[0] : parameters.clone());
Object targetObject;
if (delegationIndex < 0) {
targetObject = facets.getFarObject();
} else if (helpers[delegationIndex] instanceof Class) {
targetObject = facets.getHelper(delegationIndex);
} else {
targetObject = helpers[delegationIndex];
Object facet;
if (useDTO) {
facet = facets.getFarObject();
} else {
facet = facets.getNearObject();
}
if (logger.isTraceEnabled()) {
logger.trace(display(targetObject) + "." + method.getName() + "(" + display(facet) + "...");
}
adapted = offer(facet, adapted);
}
adapted = adapt(adapted);
if (logger.isTraceEnabled()) {
// logger.trace("Declaring class of method: " + display(method.getDeclaringClass()));
List messages = new ArrayList<>();
messages.add(displayWithTypes(targetObject) + "." + method.getName() + (adapted.length > 0 ? "(" : "()"));
if (adapted.length > 0) {
for (Object object : adapted) {
messages.add(" " + displayWithTypes(object));
}
messages.add(")");
}
messages.forEach(logger::trace);
logger.trace("Method: " + display(method));
}
Object result;
try {
result = method.invoke(targetObject, adapted);
} catch (InvocationTargetException exception) {
throw ExceptionWrapper.wrap(exception.getTargetException());
} catch (Throwable exception) {
if (logger.isTraceEnabled()) {
logger.trace("Exception", exception);
}
throw ExceptionWrapper.wrap(exception);
}
return result;
}
protected Object[] adapt(Object[] parameters) {
Object[] result;
if (parameters == null) {
result = new Object[0];
} else if (adapters.isEmpty()) {
result = parameters;
} else {
result = parameters; // .clone()
for (BridgeAdapter adapter : adapters) {
int index = adapter.getIndex();
if (result.length > index) {
result[index] = adapter.adapt(result[index]);
}
}
}
return result;
}
}
}