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

[Feature] user custom instantiators #243

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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class<?>, ConstructorParameters> constructorParameters) {
public BestConstructorInstantiator(final Class<?> clazz,
final MultiValuedMap<Class<?>, ConstructorParameters> constructorParameters) {
super(clazz, constructorParameters);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class<? extends AbstractObjectInstantiator>> 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() {
}
Expand All @@ -41,8 +28,8 @@ static AbstractObjectInstantiator forClass(final Class<?> clazz,
final MultiValuedMap<Class<?>, 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<AbstractObjectInstantiator> instantiateInstantiators(final Class<?> clazz,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Class<? extends AbstractObjectInstantiator>> 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<? extends AbstractObjectInstantiator> clazz) {
INSTANTIATORS.add(INSTANTIATORS.size() - INTERNAL_INSTANTIATORS_NUMBER, clazz);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Class<?>, ConstructorParameters> constructorParameters) {
super(clazz, constructorParameters);
}

@Override
public Object instantiate() {
return tryToInstantiate()
.orElseThrow(this::createObjectInstantiationException);
}

public abstract Optional<Object> tryToInstantiate();

private ObjectInstantiationException createObjectInstantiationException() {
return new ObjectInstantiationException(clazz, "There is no declared object for class " + clazz.getName());
}
}
Original file line number Diff line number Diff line change
@@ -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<Class<?>, ConstructorParameters> constructorParameters) {
super(clazz, constructorParameters);

}

@Override
public Optional<Object> tryToInstantiate() {
return empty();
}

@Override
public boolean canInstantiate() {
return false;
}
}

private static class CustomInstantiator2 extends UserObjectInstantiator {

CustomInstantiator2(final Class<?> clazz,
final MultiValuedMap<Class<?>, ConstructorParameters> constructorParameters) {
super(clazz, constructorParameters);

}

@Override
public Optional<Object> tryToInstantiate() {
return empty();
}

@Override
public boolean canInstantiate() {
return false;
}
}

}
Original file line number Diff line number Diff line change
@@ -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<Object> 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<Object> tryToInstantiate() {
return empty();
}

@Override
public boolean canInstantiate() {
return true;
}
};

// When
Executable executable = instantiator::instantiate;

// Then
assertThrows(ObjectInstantiationException.class, executable);
}

}