Skip to content
Permalink
Browse files

overloaded factories for composed implementations

  • Loading branch information...
maciejmikosik committed Jul 10, 2017
2 parents 8055cdf + 294ef5e commit 42f20cb75b6880e403e6e2d4e638e09694b017ed
@@ -0,0 +1,59 @@
package org.logbuddy.common;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public interface Varargs<E> {
List<E> toList();

static <E> Varargs<E> varargs() {
return () -> emptyList();
}

static <E> Varargs<E> varargs(E element) {
return () -> singletonList(element);
}

static <E> Varargs<E> varargs(E... elements) {
return () -> asList(elements);
}

static <E> Varargs<E> varargs(List<E> elements) {
return () -> elements;
}

default Varargs<E> forbidNullElements() {
return () -> {
List<E> list = Varargs.this.toList();
if (list.contains(null)) {
throw new NullPointerException();
}
return list;
};
}

default Varargs<E> defensiveCopy() {
return () -> new ArrayList<>(Varargs.this.toList());
}

default Varargs<E> unmodifiable() {
return () -> unmodifiableList(Varargs.this.toList());
}

default Varargs<E> onErrorThrow(
Function<? super RuntimeException, ? extends RuntimeException> factory) {
return () -> {
try {
return Varargs.this.toList();
} catch (RuntimeException e) {
throw factory.apply(e);
}
};
}
}
@@ -1,43 +1,47 @@
package org.logbuddy.decorator;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static org.logbuddy.LogBuddyException.check;
import static org.logbuddy.common.Varargs.varargs;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import org.logbuddy.Decorator;
import org.logbuddy.LogBuddyException;

public class ComposedDecorator implements Decorator {
private final List<Decorator> decorators;
private final List<? extends Decorator> decorators;

private ComposedDecorator(List<Decorator> decorators) {
private ComposedDecorator(List<? extends Decorator> decorators) {
this.decorators = decorators;
}

public static Decorator compose(Decorator... decorators) {
return new ComposedDecorator(validate(decorators));
return new ComposedDecorator(varargs(decorators)
.defensiveCopy()
.forbidNullElements()
.onErrorThrow(LogBuddyException::new)
.toList());
}

public static Decorator compose(List<? extends Decorator> decorators) {
return new ComposedDecorator(varargs(decorators)
.defensiveCopy()
.forbidNullElements()
.onErrorThrow(LogBuddyException::new)
.toList());
}

public <T> T decorate(T decorable) {
T decorated = decorable;
ListIterator<Decorator> iterator = decorators.listIterator(decorators.size());
ListIterator<? extends Decorator> iterator = decorators.listIterator(decorators.size());
while (iterator.hasPrevious()) {
Decorator decorator = iterator.previous();
decorated = decorator.decorate(decorated);
}
return decorated;
}

private static List<Decorator> validate(Decorator... decorators) {
check(decorators != null);
List<Decorator> decoratorList = new ArrayList<>(asList(decorators));
check(!decoratorList.contains(null));
return decoratorList;
}

public String toString() {
return decorators.stream()
.map(Decorator::toString)
@@ -1,31 +1,35 @@
package org.logbuddy.logger;

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static org.logbuddy.LogBuddyException.check;
import static org.logbuddy.common.Varargs.varargs;

import java.util.ArrayList;
import java.util.List;

import org.logbuddy.LogBuddyException;
import org.logbuddy.Logger;
import org.logbuddy.Message;

public class ComposedLogger implements Logger {
private final List<Logger> loggers;
private final List<? extends Logger> loggers;

private ComposedLogger(List<Logger> loggers) {
private ComposedLogger(List<? extends Logger> loggers) {
this.loggers = loggers;
}

public static Logger compose(Logger... loggers) {
return new ComposedLogger(validate(loggers));
return new ComposedLogger(varargs(loggers)
.defensiveCopy()
.forbidNullElements()
.onErrorThrow(LogBuddyException::new)
.toList());
}

private static List<Logger> validate(Logger[] unvalidatedLoggers) {
check(unvalidatedLoggers != null);
List<Logger> loggers = new ArrayList<>(asList(unvalidatedLoggers));
check(!loggers.contains(null));
return loggers;
public static Logger compose(List<? extends Logger> loggers) {
return new ComposedLogger(varargs(loggers)
.defensiveCopy()
.forbidNullElements()
.onErrorThrow(LogBuddyException::new)
.toList());
}

public void log(Message message) {
@@ -0,0 +1,119 @@
package org.logbuddy.common;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.logbuddy.common.Varargs.varargs;
import static org.testory.Testory.given;
import static org.testory.Testory.givenTest;
import static org.testory.Testory.thenEqual;
import static org.testory.Testory.thenReturned;
import static org.testory.Testory.thenThrown;
import static org.testory.Testory.when;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

public class TestVarargs {
private Varargs<Foo> varargs;
private Foo a, b, c, d;
private List<Foo> list, elements;
private Foo[] array;

@Before
public void before() {
givenTest(this);
}

@Test
public void creates_empty_list() {
when(varargs()
.toList());
thenReturned(emptyList());
}

@Test
public void creates_singleton_list() {
when(varargs(a)
.toList());
thenReturned(asList(a));
}

@Test
public void creates_list_from_array() {
when(varargs(a, b, c)
.toList());
thenReturned(asList(a, b, c));
}

@Test
public void creates_list_from_list() {
when(varargs(asList(a, b, c))
.toList());
thenReturned(asList(a, b, c));
}

@Test
public void forbids_null_elements_accepts_correct_list() {
when(varargs(a, b, c)
.forbidNullElements()
.toList());
thenReturned(asList(a, b, c));
}

@Test
public void forbids_null_elements_rejects_wrong_list() {
given(varargs = varargs(a, null, c)
.forbidNullElements());
when(() -> varargs.toList());
thenThrown(NullPointerException.class);
}

@Test
public void unmodifiable_prevents_modification() {
given(elements = varargs(new ArrayList<>(asList(a, b, c)))
.unmodifiable()
.toList());
when(() -> elements.add(d));
thenThrown(UnsupportedOperationException.class);
}

@Test
public void defensive_copies_array() {
given(array = new Foo[] { a, b, c });
given(elements = varargs(array)
.defensiveCopy()
.toList());
when(array[1] = d);
thenEqual(elements, asList(a, b, c));
}

@Test
public void defensive_copies_list() {
given(list = new ArrayList<>(asList(a, b, c)));
given(elements = varargs(list)
.defensiveCopy()
.toList());
when(list.add(d));
thenEqual(elements, asList(a, b, c));
}

@Test
public void on_error_wraps_exception() {
given(varargs = varargs(a, null, b)
.forbidNullElements()
.onErrorThrow(FooException::new));
when(() -> varargs.toList());
thenThrown(FooException.class);
}

private static class Foo {}

private static class FooException extends RuntimeException {
public FooException(Throwable cause) {
super(cause);
}
}
}
@@ -1,6 +1,7 @@
package org.logbuddy.decorator;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.logbuddy.decorator.ComposedDecorator.compose;
import static org.testory.Testory.given;
import static org.testory.Testory.givenTest;
@@ -25,7 +26,7 @@ public void before() {
}

@Test
public void composes_many_decorators() {
public void composes_many_decorators_in_array() {
given(willReturn(serviceA), decoratorA).decorate(service);
given(willReturn(serviceB), decoratorB).decorate(serviceA);
given(willReturn(serviceC), decoratorC).decorate(serviceB);
@@ -34,6 +35,16 @@ public void composes_many_decorators() {
thenReturned(serviceC);
}

@Test
public void composes_many_decorators_in_list() {
given(willReturn(serviceA), decoratorA).decorate(service);
given(willReturn(serviceB), decoratorB).decorate(serviceA);
given(willReturn(serviceC), decoratorC).decorate(serviceB);
given(composed = compose(asList(decoratorC, decoratorB, decoratorA)));
when(composed.decorate(service));
thenReturned(serviceC);
}

@Test
public void no_decorators_return_original_service() {
given(composed = compose());
@@ -1,6 +1,7 @@
package org.logbuddy.logger;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.logbuddy.logger.ComposedLogger.compose;
import static org.testory.Testory.given;
import static org.testory.Testory.givenTest;
@@ -26,7 +27,7 @@ public void before() {
}

@Test
public void logs_using_all_loggers() {
public void logs_using_all_loggers_in_array() {
given(composed = compose(loggerA, loggerB, loggerC));
when(() -> composed.log(message));
thenReturned();
@@ -35,6 +36,16 @@ public void logs_using_all_loggers() {
thenCalled(loggerC).log(message);
}

@Test
public void logs_using_all_loggers_in_list() {
given(composed = compose(asList(loggerA, loggerB, loggerC)));
when(() -> composed.log(message));
thenReturned();
thenCalled(loggerA).log(message);
thenCalled(loggerB).log(message);
thenCalled(loggerC).log(message);
}

@Test
public void implements_to_string() {
given(composed = compose(loggerA, loggerB, loggerC));

0 comments on commit 42f20cb

Please sign in to comment.
You can’t perform that action at this time.