diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/AbstractMultiConstructorInstantiator.java b/src/main/java/pl/pojo/tester/internal/instantiator/AbstractMultiConstructorInstantiator.java index 220e11be..e50eedcd 100644 --- a/src/main/java/pl/pojo/tester/internal/instantiator/AbstractMultiConstructorInstantiator.java +++ b/src/main/java/pl/pojo/tester/internal/instantiator/AbstractMultiConstructorInstantiator.java @@ -97,7 +97,7 @@ private Object[] putEnclosingClassInstanceAsFirstParameter(final Object enclosin .toArray(Object[]::new); } - private Object createObjectFromConstructor(final Constructor constructor) { + protected Object createObjectFromConstructor(final Constructor constructor) { makeAccessible(constructor); if (constructor.getParameterCount() == 0) { return createObjectFromNoArgsConstructor(constructor); diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/BestConstructorInstantiator.java b/src/main/java/pl/pojo/tester/internal/instantiator/BestConstructorInstantiator.java index 142d63e1..aea61ac5 100644 --- a/src/main/java/pl/pojo/tester/internal/instantiator/BestConstructorInstantiator.java +++ b/src/main/java/pl/pojo/tester/internal/instantiator/BestConstructorInstantiator.java @@ -9,12 +9,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -class BestConstructorInstantiator extends AbstractMultiConstructorInstantiator { +public class BestConstructorInstantiator extends AbstractMultiConstructorInstantiator { private static final Logger LOGGER = LoggerFactory.getLogger(BestConstructorInstantiator.class); - BestConstructorInstantiator(final Class clazz, - final MultiValuedMap, ConstructorParameters> constructorParameters) { + public BestConstructorInstantiator(final Class clazz, + final MultiValuedMap, ConstructorParameters> constructorParameters) { super(clazz, constructorParameters); } diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/Instantiable.java b/src/main/java/pl/pojo/tester/internal/instantiator/Instantiable.java index 8049da92..0abe8a0c 100644 --- a/src/main/java/pl/pojo/tester/internal/instantiator/Instantiable.java +++ b/src/main/java/pl/pojo/tester/internal/instantiator/Instantiable.java @@ -7,24 +7,11 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; -public final class Instantiable { - - private static final List> INSTANTIATORS; +import static pl.pojo.tester.internal.instantiator.Instantiator.INSTANTIATORS; - static { - INSTANTIATORS = new LinkedList<>(); - INSTANTIATORS.add(UserDefinedConstructorInstantiator.class); - INSTANTIATORS.add(JavaTypeInstantiator.class); - INSTANTIATORS.add(CollectionInstantiator.class); - INSTANTIATORS.add(DefaultConstructorInstantiator.class); - INSTANTIATORS.add(EnumInstantiator.class); - INSTANTIATORS.add(ArrayInstantiator.class); - INSTANTIATORS.add(ProxyInstantiator.class); - INSTANTIATORS.add(BestConstructorInstantiator.class); - } +public final class Instantiable { private Instantiable() { } @@ -41,8 +28,8 @@ static AbstractObjectInstantiator forClass(final Class clazz, final MultiValuedMap, ConstructorParameters> constructorParameters) { return instantiateInstantiators(clazz, constructorParameters).stream() .filter(AbstractObjectInstantiator::canInstantiate) - .findAny() - .get(); + .findFirst() + .orElseThrow(() -> new RuntimeException("No instantiator found for class " + clazz.getName())); } private static List instantiateInstantiators(final Class clazz, diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/Instantiator.java b/src/main/java/pl/pojo/tester/internal/instantiator/Instantiator.java new file mode 100644 index 00000000..bc4bd73d --- /dev/null +++ b/src/main/java/pl/pojo/tester/internal/instantiator/Instantiator.java @@ -0,0 +1,31 @@ +package pl.pojo.tester.internal.instantiator; + +import java.util.LinkedList; +import java.util.List; + +public final class Instantiator { + + public static final Instantiator INSTANCE = new Instantiator(); + + static final List> INSTANTIATORS; + private final static int INTERNAL_INSTANTIATORS_NUMBER; + + static { + INSTANTIATORS = new LinkedList<>(); + INSTANTIATORS.add(UserDefinedConstructorInstantiator.class); + INSTANTIATORS.add(JavaTypeInstantiator.class); + INSTANTIATORS.add(CollectionInstantiator.class); + INSTANTIATORS.add(DefaultConstructorInstantiator.class); + INSTANTIATORS.add(EnumInstantiator.class); + INSTANTIATORS.add(ArrayInstantiator.class); + INSTANTIATORS.add(ProxyInstantiator.class); + INSTANTIATORS.add(BestConstructorInstantiator.class); + + INTERNAL_INSTANTIATORS_NUMBER = INSTANTIATORS.size(); + } + + public Instantiator attach(final Class clazz) { + INSTANTIATORS.add(INSTANTIATORS.size() - INTERNAL_INSTANTIATORS_NUMBER, clazz); + return this; + } +} diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/ObjectInstantiationException.java b/src/main/java/pl/pojo/tester/internal/instantiator/ObjectInstantiationException.java index c8df0930..420b5e51 100644 --- a/src/main/java/pl/pojo/tester/internal/instantiator/ObjectInstantiationException.java +++ b/src/main/java/pl/pojo/tester/internal/instantiator/ObjectInstantiationException.java @@ -3,7 +3,7 @@ import java.util.Arrays; -class ObjectInstantiationException extends RuntimeException { +public class ObjectInstantiationException extends RuntimeException { ObjectInstantiationException(final Class clazz, final String message, final Throwable cause) { super(createMessage(clazz) + " " + message, cause); diff --git a/src/main/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiator.java b/src/main/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiator.java new file mode 100644 index 00000000..839d0b9c --- /dev/null +++ b/src/main/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiator.java @@ -0,0 +1,28 @@ +package pl.pojo.tester.internal.instantiator; + + +import org.apache.commons.collections4.MultiValuedMap; +import pl.pojo.tester.api.ConstructorParameters; + +import java.util.Optional; + + +public abstract class UserObjectInstantiator extends AbstractObjectInstantiator { + + public UserObjectInstantiator(final Class clazz, + final MultiValuedMap, ConstructorParameters> constructorParameters) { + super(clazz, constructorParameters); + } + + @Override + public Object instantiate() { + return tryToInstantiate() + .orElseThrow(this::createObjectInstantiationException); + } + + public abstract Optional tryToInstantiate(); + + private ObjectInstantiationException createObjectInstantiationException() { + return new ObjectInstantiationException(clazz, "There is no declared object for class " + clazz.getName()); + } +} diff --git a/src/test/java/pl/pojo/tester/internal/instantiator/InstantiatorTest.java b/src/test/java/pl/pojo/tester/internal/instantiator/InstantiatorTest.java new file mode 100644 index 00000000..1cb070ea --- /dev/null +++ b/src/test/java/pl/pojo/tester/internal/instantiator/InstantiatorTest.java @@ -0,0 +1,73 @@ +package pl.pojo.tester.internal.instantiator; + +import org.apache.commons.collections4.MultiValuedMap; +import org.junit.jupiter.api.Test; +import pl.pojo.tester.api.ConstructorParameters; + +import java.util.Optional; + +import static java.util.Optional.empty; +import static org.assertj.core.api.Assertions.assertThat; + +public class InstantiatorTest { + + @Test + public void should_attach_instantiators() { + Instantiator.INSTANCE + .attach(CustomInstantiator.class) + .attach(CustomInstantiator2.class); + + assertThat(Instantiator.INSTANTIATORS) + .containsExactly( + CustomInstantiator.class, + CustomInstantiator2.class, + UserDefinedConstructorInstantiator.class, + JavaTypeInstantiator.class, + CollectionInstantiator.class, + DefaultConstructorInstantiator.class, + EnumInstantiator.class, + ArrayInstantiator.class, + ProxyInstantiator.class, + BestConstructorInstantiator.class + ); + } + + private static class CustomInstantiator extends UserObjectInstantiator { + + CustomInstantiator(final Class clazz, + final MultiValuedMap, ConstructorParameters> constructorParameters) { + super(clazz, constructorParameters); + + } + + @Override + public Optional tryToInstantiate() { + return empty(); + } + + @Override + public boolean canInstantiate() { + return false; + } + } + + private static class CustomInstantiator2 extends UserObjectInstantiator { + + CustomInstantiator2(final Class clazz, + final MultiValuedMap, ConstructorParameters> constructorParameters) { + super(clazz, constructorParameters); + + } + + @Override + public Optional tryToInstantiate() { + return empty(); + } + + @Override + public boolean canInstantiate() { + return false; + } + } + +} \ No newline at end of file diff --git a/src/test/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiatorTest.java b/src/test/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiatorTest.java new file mode 100644 index 00000000..47d588db --- /dev/null +++ b/src/test/java/pl/pojo/tester/internal/instantiator/UserObjectInstantiatorTest.java @@ -0,0 +1,59 @@ +package pl.pojo.tester.internal.instantiator; + +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import java.util.Optional; + +import static java.util.Optional.empty; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class UserObjectInstantiatorTest { + + @Test + public void should_instantiate() { + // Given + UserObjectInstantiator instantiator = new UserObjectInstantiator(String.class, new HashSetValuedHashMap<>()) { + @Override + public Optional tryToInstantiate() { + return Optional.of("a String instance"); + } + + @Override + public boolean canInstantiate() { + return true; + } + }; + + // When + Object object = instantiator.instantiate(); + + // Then + assertThat(object).isEqualTo("a String instance"); + } + + @Test + public void should_not_instantiate_and_throw_exception() { + // Given + UserObjectInstantiator instantiator = new UserObjectInstantiator(String.class, new HashSetValuedHashMap<>()) { + @Override + public Optional tryToInstantiate() { + return empty(); + } + + @Override + public boolean canInstantiate() { + return true; + } + }; + + // When + Executable executable = instantiator::instantiate; + + // Then + assertThrows(ObjectInstantiationException.class, executable); + } + +} \ No newline at end of file