Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow injection of custom bean introspection #120

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>io.beanmapper</groupId>
<artifactId>beanmapper</artifactId>
<version>3.0.1</version>
<version>3.0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>42 Bean Mapper</name>
<description>Easy-to-use bean mapper for conversion from form to object to view</description>
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/io/beanmapper/config/BeanMapperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import io.beanmapper.BeanMapper;
import io.beanmapper.annotations.BeanCollectionUsage;
import io.beanmapper.annotations.LogicSecuredCheck;
import io.beanmapper.core.BeanMatchStore;
import io.beanmapper.core.BeanMatchStoreFactory;
import io.beanmapper.core.collections.CollectionHandler;
import io.beanmapper.core.collections.ListCollectionHandler;
import io.beanmapper.core.collections.MapCollectionHandler;
Expand Down Expand Up @@ -39,7 +41,11 @@ public class BeanMapperBuilder {
private List<CollectionHandler> customCollectionHandlers = new ArrayList<>();

public BeanMapperBuilder() {
this.configuration = new CoreConfiguration();
this(BeanMatchStore::new);
}

public BeanMapperBuilder(BeanMatchStoreFactory beanMatchStoreFactory) {
this.configuration = new CoreConfiguration(beanMatchStoreFactory);
}

public BeanMapperBuilder(Configuration configuration) {
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/io/beanmapper/config/CoreConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.beanmapper.annotations.BeanCollectionUsage;
import io.beanmapper.annotations.LogicSecuredCheck;
import io.beanmapper.core.BeanMatchStore;
import io.beanmapper.core.BeanMatchStoreFactory;
import io.beanmapper.core.collections.CollectionHandler;
import io.beanmapper.core.constructor.BeanInitializer;
import io.beanmapper.core.constructor.DefaultBeanInitializer;
Expand All @@ -19,7 +20,6 @@
import io.beanmapper.exceptions.BeanConfigurationOperationNotAllowedException;

public class CoreConfiguration implements Configuration {

/**
* Initializes the beans.
*/
Expand All @@ -40,13 +40,13 @@ public class CoreConfiguration implements Configuration {
* Contains a store of matches for source and target class pairs. A pair is created only
* once and reused every time thereafter.
*/
private BeanMatchStore beanMatchStore = new BeanMatchStore(collectionHandlerStore, beanUnproxy);
private final BeanMatchStore beanMatchStore;

/**
* Contains a store of classes that are generated by using the MapToDynamicClassStrategy.
* Every generated class is a downsized source class or a downsized target class.
*/
private ClassStore classStore = new ClassStore();
private final ClassStore classStore;

/**
* The list of packages (and subpackages) containing classes which are eligible for mapping.
Expand Down Expand Up @@ -99,6 +99,11 @@ public class CoreConfiguration implements Configuration {
*/
private Boolean useNullValue = false;

public CoreConfiguration(BeanMatchStoreFactory beanMatchStoreFactory) {
this.beanMatchStore = beanMatchStoreFactory.create(collectionHandlerStore, beanUnproxy);
this.classStore = new ClassStore(beanMatchStoreFactory);
}

@Override
public List<String> getDownsizeTarget() { return null; }

Expand Down
15 changes: 12 additions & 3 deletions src/main/java/io/beanmapper/core/BeanMatchStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,18 @@ public class BeanMatchStore {

private final BeanUnproxy beanUnproxy;

private final PropertyAccessors propertyAccessors;

private Map<String, Map<String, BeanMatch>> store = new TreeMap<String, Map<String, BeanMatch>>();

public BeanMatchStore(CollectionHandlerStore collectionHandlerStore, BeanUnproxy beanUnproxy) {
this.collectionHandlerStore = collectionHandlerStore;
this.beanUnproxy = beanUnproxy;
this.propertyAccessors = createPropertyAccessors();
}

protected PropertyAccessors createPropertyAccessors() {
return new PropertyAccessors();
}

public void validateStrictBeanPairs(List<BeanPair> beanPairs) {
Expand Down Expand Up @@ -122,7 +129,7 @@ private BeanMatch determineBeanMatch(BeanPair beanPair,
private Map<String, BeanProperty> getAllFields(Map<String, BeanProperty> ourNodes, Map<String, BeanProperty> otherNodes, Map<String, BeanProperty> aliases, Class<?> ourType, Class<?> otherType, BeanProperty precedingBeanProperty, BeanPropertyMatchupDirection matchupDirection) {

Map<String, BeanProperty> ourCurrentNodes = ourNodes;
List<PropertyAccessor> accessors = PropertyAccessors.getAll(ourType);
List<PropertyAccessor> accessors = propertyAccessors.getAll(ourType);
for (PropertyAccessor accessor : accessors) {

BeanPropertyAccessType accessType = matchupDirection.accessType(accessor);
Expand All @@ -144,7 +151,7 @@ private Map<String, BeanProperty> getAllFields(Map<String, BeanProperty> ourNode
BeanProperty currentBeanProperty = null;
try {
currentBeanProperty = new BeanPropertyCreator(
matchupDirection, ourType, accessor.getName()).determineNodesForPath(precedingBeanProperty);
matchupDirection, ourType, propertyAccessors, accessor.getName()).determineNodesForPath(precedingBeanProperty);
currentBeanProperty.setMustMatch(beanPropertyWrapper.isMustMatch());
} catch (BeanNoSuchPropertyException e) {
throw new BeanMissingPathException(ourType, accessor.getName(), e);
Expand Down Expand Up @@ -187,6 +194,8 @@ private Map<String, BeanProperty> getAllFields(Map<String, BeanProperty> ourNode
return ourCurrentNodes;
}



private void handleBeanLogicSecuredAnnotation(BeanProperty beanProperty, BeanLogicSecured beanLogicSecured) {
if (beanLogicSecured == null) {
return;
Expand Down Expand Up @@ -265,7 +274,7 @@ private BeanPropertyWrapper dealWithBeanProperty(
try {
otherNodes.put(
wrapper.getName(),
new BeanPropertyCreator(matchupDirection.getInverse(), otherType, wrapper.getName())
new BeanPropertyCreator(matchupDirection.getInverse(), otherType, propertyAccessors, wrapper.getName())
.determineNodesForPath());
} catch (BeanNoSuchPropertyException err) {
// Acceptable, might have been tagged as @BeanProperty as well
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/beanmapper/core/BeanMatchStoreFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.beanmapper.core;

import io.beanmapper.config.CollectionHandlerStore;
import io.beanmapper.core.unproxy.BeanUnproxy;

public interface BeanMatchStoreFactory {
BeanMatchStore create(CollectionHandlerStore collectionHandlerStore, BeanUnproxy beanUnproxy);
}
7 changes: 5 additions & 2 deletions src/main/java/io/beanmapper/core/BeanPropertyCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ public class BeanPropertyCreator {

private final Route route;

public BeanPropertyCreator(BeanPropertyMatchupDirection matchupDirection, Class<?> baseClass, String path) {
private final PropertyAccessors propertyAccessors;

public BeanPropertyCreator(BeanPropertyMatchupDirection matchupDirection, Class<?> baseClass, PropertyAccessors propertyAccessors, String path) {
this.matchupDirection = matchupDirection;
this.baseClass = baseClass;
this.propertyAccessors = propertyAccessors;
this.route = new Route(path);
}

Expand Down Expand Up @@ -66,7 +69,7 @@ private BeanProperty getFirstBeanProperty(Stack<BeanProperty> beanProperties) {
private void traversePath(Stack<BeanProperty> beanProperties) {
Class<?> currentBaseClass = baseClass;
for (String node : route.getRoute()) {
final PropertyAccessor property = PropertyAccessors.findProperty(currentBaseClass, node);
final PropertyAccessor property = propertyAccessors.findProperty(currentBaseClass, node);
if (property == null) {
throw new BeanNoSuchPropertyException("Property '" + node + "' does not exist in: " + currentBaseClass.getSimpleName());
}
Expand Down
19 changes: 12 additions & 7 deletions src/main/java/io/beanmapper/core/inspector/PropertyAccessors.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class PropertyAccessors {
* @param beanClass the bean class
* @return the property accessors of that bean
*/
public static List<PropertyAccessor> getAll(Class<?> beanClass) {
public List<PropertyAccessor> getAll(Class<?> beanClass) {
Map<String, PropertyDescriptor> descriptors = findPropertyDescriptors(beanClass);
Map<String, Field> fields = findAllFields(beanClass);

Expand All @@ -47,11 +47,11 @@ public static List<PropertyAccessor> getAll(Class<?> beanClass) {
return accessors;
}

private static Map<String, PropertyDescriptor> findPropertyDescriptors(Class<?> clazz) {
private Map<String, PropertyDescriptor> findPropertyDescriptors(Class<?> clazz) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>();
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
PropertyDescriptor[] propertyDescriptors = extractPropertyDescriptors(clazz);
for (PropertyDescriptor descriptor : propertyDescriptors) {
result.put(descriptor.getName(), descriptor);
}
result.remove(CLASS_PROPERTY);
Expand All @@ -64,7 +64,12 @@ private static Map<String, PropertyDescriptor> findPropertyDescriptors(Class<?>
}
}

private static void addParentInterfaceProperties(Class<?> clazz, Map<String, PropertyDescriptor> result) {
protected PropertyDescriptor[] extractPropertyDescriptors(Class<?> clazz) throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
return beanInfo.getPropertyDescriptors();
}

private void addParentInterfaceProperties(Class<?> clazz, Map<String, PropertyDescriptor> result) {
for (Class<?> parent : clazz.getInterfaces()) {
result.putAll(findPropertyDescriptors(parent));
}
Expand All @@ -87,7 +92,7 @@ private static Map<String, Field> findAllFields(Class<?> clazz) {
* @param propertyName the property name
* @return the accessor that can manage the property
*/
public static PropertyAccessor findProperty(Class<?> beanClass, String propertyName) {
public PropertyAccessor findProperty(Class<?> beanClass, String propertyName) {
PropertyAccessor result = null;
PropertyDescriptor descriptor = findPropertyDescriptor(beanClass, propertyName);
Field field = findField(beanClass, propertyName);
Expand All @@ -97,7 +102,7 @@ public static PropertyAccessor findProperty(Class<?> beanClass, String propertyN
return result;
}

private static PropertyDescriptor findPropertyDescriptor(Class<?> beanClass, String propertyName) {
private PropertyDescriptor findPropertyDescriptor(Class<?> beanClass, String propertyName) {
Map<String, PropertyDescriptor> descriptors = findPropertyDescriptors(beanClass);
return descriptors.get(propertyName);
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/beanmapper/dynclass/ClassGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.beanmapper.annotations.BeanCollection;
import io.beanmapper.config.StrictMappingProperties;
import io.beanmapper.core.BeanMatchStore;
import io.beanmapper.core.BeanMatchStoreFactory;
import io.beanmapper.core.BeanProperty;
import io.beanmapper.core.converter.collections.BeanCollectionInstructions;
import javassist.CannotCompileException;
Expand All @@ -26,8 +27,8 @@ public class ClassGenerator {
private static Integer GENERATED_CLASS_PREFIX = 0;
private BeanMatchStore beanMatchStore;

public ClassGenerator() {
this.beanMatchStore = new BeanMatchStore(null, null);
public ClassGenerator(BeanMatchStoreFactory beanMatchStoreFactory) {
this.beanMatchStore = beanMatchStoreFactory.create(null, null);
this.classPool = ClassPool.getDefault();
}

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/beanmapper/dynclass/ClassStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.TreeMap;

import io.beanmapper.config.StrictMappingProperties;
import io.beanmapper.core.BeanMatchStoreFactory;
import io.beanmapper.exceptions.BeanDynamicClassGenerationException;

public class ClassStore {
Expand All @@ -16,8 +17,8 @@ public class ClassStore {
CACHE = new TreeMap<String, Map<String, Class<?>>>();
}

public ClassStore() {
this.classGenerator = new ClassGenerator();
public ClassStore(BeanMatchStoreFactory beanMatchStoreFactory) {
this.classGenerator = new ClassGenerator(beanMatchStoreFactory);
}

public Class<?> getOrCreateGeneratedClass(
Expand Down
16 changes: 10 additions & 6 deletions src/test/java/io/beanmapper/config/CoreConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,51 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import io.beanmapper.core.BeanMatchStore;
import io.beanmapper.core.BeanMatchStoreFactory;
import io.beanmapper.exceptions.BeanConfigurationOperationNotAllowedException;

import org.junit.Test;

public class CoreConfigurationTest {

private BeanMatchStoreFactory beanMatchStoreFactory = BeanMatchStore::new;

@Test(expected = BeanConfigurationOperationNotAllowedException.class)
public void setParent() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
configuration.setParent(null);
}

@Test(expected = BeanConfigurationOperationNotAllowedException.class)
public void setTarget() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
configuration.setTarget("Hello world");
}

@Test
public void determineTargetClass_noTargetSet() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
assertNull(configuration.determineTargetClass());
}

@Test
public void addNullPackagePrefix() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
configuration.addPackagePrefix((Class<?>)null);
assertEquals(0, configuration.getPackagePrefixes().size());
}

@Test
public void addWithoutDefaultConverters() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
configuration.withoutDefaultConverters();
assertFalse(configuration.isAddDefaultConverters());
}

@Test
public void singleMapRunProperties() {
CoreConfiguration configuration = new CoreConfiguration();
CoreConfiguration configuration = new CoreConfiguration(beanMatchStoreFactory);
assertNull(configuration.getDownsizeSource());
assertNull(configuration.getDownsizeTarget());
assertNull(configuration.getTargetClass());
Expand Down
15 changes: 8 additions & 7 deletions src/test/java/io/beanmapper/core/BeanPropertyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import io.beanmapper.core.inspector.PropertyAccessors;
import io.beanmapper.testmodel.nested_classes.Layer1;
import io.beanmapper.testmodel.nested_classes.Layer1Result;

Expand All @@ -12,7 +13,7 @@ public class BeanPropertyTest {

@Test
public void determineNodes() {
BeanProperty beanProperty = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanProperty = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
assertEquals("layer2", beanProperty.getCurrentAccessor().getName());
assertNotNull("the 'layer2' node must refer to the 'layer3' node", beanProperty.getNext());
assertEquals("layer3", beanProperty.getNext().getCurrentAccessor().getName());
Expand All @@ -23,7 +24,7 @@ public void determineNodes() {
@Test
public void getObject() {
Layer1 layer1 = Layer1.createNestedClassObject();
BeanProperty beanProperty = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanProperty = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
Object object = beanProperty.getObject(layer1);
assertEquals(String.class, object.getClass());
assertEquals("name3", object);
Expand All @@ -32,9 +33,9 @@ public void getObject() {
@Test
public void writeObjectToNewTarget() {
Layer1 layer1 = Layer1.createNestedClassObject();
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
Object source = beanPropertyForSource.getObject(layer1);
BeanProperty beanPropertyForTarget = new BeanPropertyCreator(BeanPropertyMatchupDirection.TARGET_TO_SOURCE, Layer1Result.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanPropertyForTarget = new BeanPropertyCreator(BeanPropertyMatchupDirection.TARGET_TO_SOURCE, Layer1Result.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
Layer1Result target = (Layer1Result) beanPropertyForTarget.writeObject(source, new Layer1Result(), null, null);
assertEquals("name3", target.getLayer2().getLayer3().getName3());
}
Expand All @@ -43,16 +44,16 @@ public void writeObjectToNewTarget() {
public void writeObjectToExistingTarget() {
Layer1 layer1 = Layer1.createNestedClassObject();
Layer1Result existingTarget = Layer1Result.createNestedClassObject();
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
Object source = beanPropertyForSource.getObject(layer1);
BeanProperty beanPropertyForTarget = new BeanPropertyCreator(BeanPropertyMatchupDirection.TARGET_TO_SOURCE, Layer1Result.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanPropertyForTarget = new BeanPropertyCreator(BeanPropertyMatchupDirection.TARGET_TO_SOURCE, Layer1Result.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
Layer1Result target = (Layer1Result) beanPropertyForTarget.writeObject(source, existingTarget, null, null);
assertEquals("name3", target.getLayer2().getLayer3().getName3());
}

@Test
public void getName() {
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, "layer2.layer3.name3").determineNodesForPath();
BeanProperty beanPropertyForSource = new BeanPropertyCreator(BeanPropertyMatchupDirection.SOURCE_TO_TARGET, Layer1.class, new PropertyAccessors(), "layer2.layer3.name3").determineNodesForPath();
assertEquals("layer2.layer3.name3", beanPropertyForSource.getName());
}

Expand Down
Loading