Add PropertyMapper utility class
Add a utility class that can help when mapping values from `@ConfigurationProperties` to a third-party class. See gh-9018pull/11364/head
parent
befdbaaaa9
commit
241a7086c0
@ -0,0 +1,350 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.context.properties;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility that can be used to map values from a supplied source to a destination.
|
||||||
|
* Primarily intended to be help when mapping from
|
||||||
|
* {@link ConfigurationProperties @ConfigrationProperties} to third-party classes.
|
||||||
|
* <p>
|
||||||
|
* Can filter values based on predicates and adapt values if needed. For example:
|
||||||
|
* <pre class="code">
|
||||||
|
* PropertyMapper map = PropertyMapper.get();
|
||||||
|
* map.from(source::getName)
|
||||||
|
* .to(destination::setName);
|
||||||
|
* map.from(source::getTimeout)
|
||||||
|
* .whenNonNull()
|
||||||
|
* .asInt(Duration::getSeconds)
|
||||||
|
* .to(destination::setTimeoutSecs);
|
||||||
|
* map.from(source::isEnabled)
|
||||||
|
* .whenFalse().
|
||||||
|
* .toCall(destination::disable);
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Mappings can ultimately be applied to a {@link Source#to(Consumer) setter}, trigger a
|
||||||
|
* {@link Source#toCall(Runnable) method call} or create a
|
||||||
|
* {@link Source#toInstance(Function) new instance}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public final class PropertyMapper {
|
||||||
|
|
||||||
|
private static final Predicate<?> ALWAYS = (t) -> true;
|
||||||
|
|
||||||
|
private static final PropertyMapper INSTANCE = new PropertyMapper(null, null);
|
||||||
|
|
||||||
|
private final PropertyMapper parent;
|
||||||
|
|
||||||
|
private final SourceOperator sourceOperator;
|
||||||
|
|
||||||
|
private PropertyMapper(PropertyMapper parent, SourceOperator sourceOperator) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.sourceOperator = sourceOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link PropertyMapper} instance that applies
|
||||||
|
* {@link Source#whenNonNull() whenNonNull} to every source.
|
||||||
|
* @return a new property mapper instance
|
||||||
|
*/
|
||||||
|
public PropertyMapper alwaysApplyingWhenNonNull() {
|
||||||
|
return alwaysApplying(this::whenNonNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Source<T> whenNonNull(Source<T> source) {
|
||||||
|
return source.whenNonNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link PropertyMapper} instance that applies the given
|
||||||
|
* {@link SourceOperator} to every source.
|
||||||
|
* @param operator the source operator to apply
|
||||||
|
* @return a new property mapper instance
|
||||||
|
*/
|
||||||
|
public PropertyMapper alwaysApplying(SourceOperator operator) {
|
||||||
|
Assert.notNull(operator, "Operator must not be null");
|
||||||
|
return new PropertyMapper(this, operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link Source} from the specified value supplier that can be used to
|
||||||
|
* perform the mapping.
|
||||||
|
* @param <T> the source type
|
||||||
|
* @param supplier the value supplier
|
||||||
|
* @return a {@link Source} that can be used to complete the mapping
|
||||||
|
*/
|
||||||
|
public <T> Source<T> from(Supplier<T> supplier) {
|
||||||
|
Assert.notNull(supplier, "Supplier must not be null");
|
||||||
|
Source<T> source = getSource(supplier);
|
||||||
|
if (this.sourceOperator != null) {
|
||||||
|
source = this.sourceOperator.apply(source);
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> Source<T> getSource(Supplier<T> supplier) {
|
||||||
|
if (this.parent != null) {
|
||||||
|
return this.parent.from(supplier);
|
||||||
|
}
|
||||||
|
return new Source<T>(new CachingSupplier<>(supplier), (Predicate<T>) ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the property mapper.
|
||||||
|
* @return the property mapper
|
||||||
|
*/
|
||||||
|
public static PropertyMapper get() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplier that caches the value to prevent multiple calls.
|
||||||
|
*/
|
||||||
|
private static class CachingSupplier<T> implements Supplier<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
|
||||||
|
private boolean hasResult;
|
||||||
|
|
||||||
|
private T result;
|
||||||
|
|
||||||
|
CachingSupplier(Supplier<T> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
if (!this.hasResult) {
|
||||||
|
this.result = this.supplier.get();
|
||||||
|
this.hasResult = true;
|
||||||
|
}
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An operation that can be applied to a {@link Source}.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SourceOperator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the operation to the given source.
|
||||||
|
* @param <T> the source type
|
||||||
|
* @param source the source to operate on
|
||||||
|
* @return the updated source
|
||||||
|
*/
|
||||||
|
<T> Source<T> apply(Source<T> source);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source that is in the process of being mapped.
|
||||||
|
* @param <T> the source type
|
||||||
|
*/
|
||||||
|
public final static class Source<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
|
||||||
|
private final Predicate<T> predicate;
|
||||||
|
|
||||||
|
private Source(Supplier<T> supplier, Predicate<T> predicate) {
|
||||||
|
Assert.notNull(predicate, "Arse");
|
||||||
|
this.supplier = supplier;
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an adapted version of the source with {@link Integer} type.
|
||||||
|
* @param <R> the resulting type
|
||||||
|
* @param adapter an adapter to convert the current value to a number.
|
||||||
|
* @return a new adapted source instance
|
||||||
|
*/
|
||||||
|
public <R extends Number> Source<Integer> asInt(Function<T, R> adapter) {
|
||||||
|
return as(adapter).as(Number::intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an adapted version of the source changed via the given adapter function.
|
||||||
|
* @param <R> the resulting type
|
||||||
|
* @param adapter the adapter to apply
|
||||||
|
* @return a new adapted source instance
|
||||||
|
*/
|
||||||
|
public <R> Source<R> as(Function<T, R> adapter) {
|
||||||
|
Assert.notNull(adapter, "Adapter must not be null");
|
||||||
|
Supplier<Boolean> test = () -> this.predicate.test(this.supplier.get());
|
||||||
|
Predicate<R> predicate = (t) -> test.get();
|
||||||
|
Supplier<R> supplier = () -> {
|
||||||
|
if (test.get()) {
|
||||||
|
return adapter.apply(this.supplier.get());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
return new Source<R>(supplier, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that won't map non-null values or
|
||||||
|
* suppliers that throw a {@link NullPointerException}.
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenNonNull() {
|
||||||
|
return new Source<>(new NullPointerExceptionSafeSupplier<>(this.supplier),
|
||||||
|
Objects::nonNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that will only map values that
|
||||||
|
* {@code true}.
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenTrue() {
|
||||||
|
return when(Boolean.TRUE::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that will only map values that
|
||||||
|
* {@code false}.
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenFalse() {
|
||||||
|
return when(Boolean.FALSE::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that will only map values that have a
|
||||||
|
* {@code toString()} containing actual text.
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenHasText() {
|
||||||
|
return when((value) -> StringUtils.hasText(Objects.toString(value, null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that will only map values equal to the
|
||||||
|
* specified {@code object}.
|
||||||
|
* @param object the object to match
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenEqualTo(Object object) {
|
||||||
|
return when(object::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that won't map values that match the
|
||||||
|
* given predicate.
|
||||||
|
* @param predicate the predicate used to filter values
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> whenNot(Predicate<T> predicate) {
|
||||||
|
Assert.notNull(predicate, "Predicate must not be null");
|
||||||
|
return new Source<>(this.supplier, predicate.negate());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a filtered version of the source that won't map values that don't match
|
||||||
|
* the given predicate.
|
||||||
|
* @param predicate the predicate used to filter values
|
||||||
|
* @return a new filtered source instance
|
||||||
|
*/
|
||||||
|
public Source<T> when(Predicate<T> predicate) {
|
||||||
|
Assert.notNull(predicate, "Predicate must not be null");
|
||||||
|
return new Source<>(this.supplier, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete the mapping by passing any non-filtered value to the specified
|
||||||
|
* consumer.
|
||||||
|
* @param consumer the consumer that should accept the value if it's not been
|
||||||
|
* filtered
|
||||||
|
*/
|
||||||
|
public void to(Consumer<T> consumer) {
|
||||||
|
Assert.notNull(consumer, "Consumer must not be null");
|
||||||
|
T value = this.supplier.get();
|
||||||
|
if (this.predicate.test(value)) {
|
||||||
|
consumer.accept(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete the mapping by creating a new instance from the non-filtered value.
|
||||||
|
* @param <R> the resulting type
|
||||||
|
* @param factory the factory used to create the instance
|
||||||
|
* @return the instance
|
||||||
|
* @throws NoSuchElementException if the value has been filtered
|
||||||
|
*/
|
||||||
|
public <R> R toInstance(Function<T, R> factory) {
|
||||||
|
Assert.notNull(factory, "Factory must not be null");
|
||||||
|
T value = this.supplier.get();
|
||||||
|
if (!this.predicate.test(value)) {
|
||||||
|
throw new NoSuchElementException("No value present");
|
||||||
|
}
|
||||||
|
return factory.apply(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete the mapping by calling the specified method when the value has not
|
||||||
|
* been filtered.
|
||||||
|
* @param runnable the method to call if the value has not been filtered
|
||||||
|
*/
|
||||||
|
public void toCall(Runnable runnable) {
|
||||||
|
Assert.notNull(runnable, "Runnable must not be null");
|
||||||
|
T value = this.supplier.get();
|
||||||
|
if (this.predicate.test(value)) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplier that will catch and ignore any {@link NullPointerException}.
|
||||||
|
*/
|
||||||
|
private static class NullPointerExceptionSafeSupplier<T> implements Supplier<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
|
||||||
|
NullPointerExceptionSafeSupplier(Supplier<T> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
try {
|
||||||
|
return this.supplier.get();
|
||||||
|
}
|
||||||
|
catch (NullPointerException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2017 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.context.properties;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link PropertyMapper}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class PropertyMapperTests {
|
||||||
|
|
||||||
|
private PropertyMapper map = PropertyMapper.get();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromWhenSupplierIsNullShouldThrowException() {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("Supplier must not be null");
|
||||||
|
this.map.from(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toWhenConsumerIsNullShouldThrowException() {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("Consumer must not be null");
|
||||||
|
this.map.from(() -> "").to(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toShouldMapFromSupplier() {
|
||||||
|
ExampleSource source = new ExampleSource("test");
|
||||||
|
ExampleDest dest = new ExampleDest();
|
||||||
|
this.map.from(source::getName).to(dest::setName);
|
||||||
|
assertThat(dest.getName()).isEqualTo("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asIntShouldAdaptSupplier() {
|
||||||
|
Integer result = this.map.from(() -> "123").asInt(Long::valueOf)
|
||||||
|
.toInstance(Integer::new);
|
||||||
|
assertThat(result).isEqualTo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asWhenAdapterIsNullShouldThrowException() {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("Adapter must not be null");
|
||||||
|
this.map.from(() -> "").as(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asShouldAdaptSupplier() {
|
||||||
|
ExampleDest dest = new ExampleDest();
|
||||||
|
this.map.from(() -> 123).as(String::valueOf).to(dest::setName);
|
||||||
|
assertThat(dest.getName()).isEqualTo("123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNonNullWhenSuppliedNullShouldNotMap() {
|
||||||
|
this.map.from(() -> null).whenNonNull().as(String::valueOf).toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenNonNullWhenSuppliedThrowsNullPointerExceptionShouldNotMap() {
|
||||||
|
this.map.from(() -> {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}).whenNonNull().as(String::valueOf).toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenTrueWhenValueIsTrueShouldMap() {
|
||||||
|
Boolean result = this.map.from(() -> true).whenTrue().toInstance(Boolean::new);
|
||||||
|
assertThat(result).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenTrueWhenValueIsFalseShouldNotMap() {
|
||||||
|
this.map.from(() -> false).whenTrue().toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenFalseWhenValueIsFalseShouldMap() {
|
||||||
|
Boolean result = this.map.from(() -> false).whenFalse().toInstance(Boolean::new);
|
||||||
|
assertThat(result).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenFalseWhenValueIsTrueShouldNotMap() {
|
||||||
|
this.map.from(() -> true).whenFalse().toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenHasTextWhenValueIsNullShouldNotMap() {
|
||||||
|
this.map.from(() -> null).whenHasText().toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenHasTextWhenValueIsEmptyShouldNotMap() {
|
||||||
|
this.map.from(() -> "").whenHasText().toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenHasTextWhenValueHasTextShouldMap() {
|
||||||
|
Integer result = this.map.from(() -> 123).whenHasText().toInstance(Integer::new);
|
||||||
|
assertThat(result).isEqualTo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenEqualToWhenValueIsEqualShouldMatch() {
|
||||||
|
String result = this.map.from(() -> "123").whenEqualTo("123")
|
||||||
|
.toInstance(String::new);
|
||||||
|
assertThat(result).isEqualTo("123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenEqualToWhenValueIsNotEqualShouldNotMatch() {
|
||||||
|
this.map.from(() -> "123").whenEqualTo("321").toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWhenValueMatchesShouldMap() {
|
||||||
|
String result = this.map.from(() -> "123").when("123"::equals)
|
||||||
|
.toInstance(String::new);
|
||||||
|
assertThat(result).isEqualTo("123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWhenValueDoesNotMatchShouldNotMap() {
|
||||||
|
this.map.from(() -> "123").when("321"::equals).toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWhenCombinedWithAsUsesSourceValue() {
|
||||||
|
Count<String> source = new Count<>(() -> "123");
|
||||||
|
Long result = this.map.from(source).when("123"::equals).as(Integer::valueOf)
|
||||||
|
.when((v) -> v == 123).as(Integer::longValue).toInstance(Long::new);
|
||||||
|
assertThat(result).isEqualTo(123);
|
||||||
|
assertThat(source.getCount()).isOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void alwaysApplyingWhenNonNullShouldAlwaysApplyNonNullToSource() {
|
||||||
|
this.map.alwaysApplyingWhenNonNull().from(() -> null).toCall(Assert::fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Count<T> implements Supplier<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> source;
|
||||||
|
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
Count(Supplier<T> source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
this.count++;
|
||||||
|
return this.source.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return this.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleSource {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
ExampleSource(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleDest {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue