Skip to content

Commit

Permalink
Implemenent future handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonOellerer committed Dec 16, 2021
1 parent 9ea11cf commit 633db6a
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 13 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group 'com.docutools'
version = '1.4.0-alpha'
version = '1.4.0-alpha.1'

sourceCompatibility = 17
targetCompatibility = 17
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.docutools.jocument.impl;

import com.docutools.jocument.CustomPlaceholderRegistry;
import com.docutools.jocument.PlaceholderData;
import com.docutools.jocument.PlaceholderMapper;
import com.docutools.jocument.PlaceholderResolver;
import com.docutools.jocument.annotations.Image;
import com.docutools.jocument.impl.word.placeholders.ImagePlaceholderData;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.time.temporal.Temporal;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* Takes a {@link Object} of any type and resolves placeholder names with reflective access to its type.
*
* @author codecitizen
* @see PlaceholderResolver
* @since 2020-02-19
*/
public class FutureReflectionResolver extends ReflectionResolver {
private static final Logger logger = LogManager.getLogger();
private final PlaceholderMapper placeholderMapper = new PlaceholderMapperImpl();

public FutureReflectionResolver(Object value) {
this(value, new CustomPlaceholderRegistryImpl()); //NoOp CustomPlaceholderRegistry
}

public FutureReflectionResolver(Object value, CustomPlaceholderRegistry customPlaceholderRegistry) {
super(value, customPlaceholderRegistry);
}


@Override
public Optional<PlaceholderData> resolve(String placeholderName, Locale locale) {
logger.debug("Trying to resolve placeholder {}", placeholderName);
placeholderName = placeholderMapper.map(placeholderName);
Optional<PlaceholderData> result = Optional.empty();
for (String property : placeholderName.split("\\.")) {
result = result.isEmpty()
? doResolve(property, locale)
: result
.flatMap(r -> r.stream().findAny())
.flatMap(r -> r.resolve(property, locale));
}
return result;
}

private Optional<PlaceholderData> doResolve(String placeholderName, Locale locale) {
try {
if (customPlaceholderRegistry.governs(placeholderName)) {
return customPlaceholderRegistry.resolve(placeholderName);
}
var property = getBeanProperty(placeholderName);
if (property == null) {
return Optional.empty();
}
if (property instanceof Future<?>) {
property = ((Future<?>) property).get();
}
if (property instanceof Number number) {
var numberFormat = findNumberFormat(placeholderName, locale);
return Optional.of(new ScalarPlaceholderData(numberFormat.format(number)));
} else if (property instanceof Enum || property instanceof String || ReflectionUtils.isWrapperType(property.getClass())) {
return Optional.of(new ScalarPlaceholderData(property.toString()));
} else if (property instanceof Collection<?> collection) {
List<PlaceholderResolver> list = collection.stream()
.map(object -> new FutureReflectionResolver(object, customPlaceholderRegistry))
.collect(Collectors.toList());
return Optional.of(new IterablePlaceholderData(list, list.size()));
} else if (property instanceof Temporal temporal) {
return formatTemporal(placeholderName, temporal, locale);
} else if (property instanceof Path path && isFieldAnnotatedWith(bean.getClass(), placeholderName, Image.class)) {
return ReflectionUtils.findFieldAnnotation(bean.getClass(), placeholderName, Image.class)
.map(image -> new ImagePlaceholderData(path)
.withMaxWidth(image.maxWidth()));
}
if (bean.equals(property)) {
return Optional.of(new IterablePlaceholderData(List.of(new FutureReflectionResolver(bean, customPlaceholderRegistry)), 1));
} else {
var value = getBeanProperty(placeholderName);
if (value instanceof Future<?>) {
value = ((Future<?>) value).get();
}
return Optional.of(new IterablePlaceholderData(List.of(new FutureReflectionResolver(value, customPlaceholderRegistry)), 1));
}
} catch (NoSuchMethodException | IllegalArgumentException e) {
logger.debug("Did not find placeholder {}", placeholderName);
return Optional.empty();
} catch (IllegalAccessException | InvocationTargetException e) {
logger.error("Could not resolve placeholder %s".formatted(placeholderName), e);
throw new IllegalStateException("Could not resolve placeholderName against type.", e);
} catch (InstantiationException e) {
logger.warn("InstantiationException when trying to resolve placeholder %s".formatted(placeholderName), e);
return Optional.empty();
} catch (InterruptedException e) {
logger.warn("InterruptedException when waiting for Future placeholder %s".formatted(placeholderName), e);
Thread.currentThread().interrupt();
return Optional.empty();
} catch (ExecutionException e) {
logger.warn("Execution exception when waiting for Future placeholder %s".formatted(placeholderName), e);
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ public class PlaceholderMapperImpl implements PlaceholderMapper {
var file = path.toFile();
if (file.exists() && file.canRead()) {
try {
logger.info("Parsing mappings");
var reader = new BufferedReader(new FileReader(file));
placeholderMappings = reader.lines()
.filter(line -> !line.isEmpty())
.filter(line -> !line.startsWith("//"))
.map(line -> line.split(":"))
.collect(Collectors.toMap(strings -> strings[0], strings -> strings[1]));
logger.info("Parsed mappings");
} catch (IOException e) {
logger.error(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@
* @since 2020-02-19
*/
public class ReflectionResolver extends PlaceholderResolver {
private final PlaceholderMapper placeholderMapper = new PlaceholderMapperImpl();

private static final String SELF_REFERENCE = "this";

private static final Logger logger = LogManager.getLogger();

private final Object bean;
protected final Object bean;
private final PropertyUtilsBean pub = new PropertyUtilsBean();
private final CustomPlaceholderRegistry customPlaceholderRegistry;
protected final CustomPlaceholderRegistry customPlaceholderRegistry;

public ReflectionResolver(Object value) {
this(value, new CustomPlaceholderRegistryImpl()); //NoOp CustomPlaceholderRegistry
Expand All @@ -61,7 +59,7 @@ public ReflectionResolver(Object value, CustomPlaceholderRegistry customPlacehol
this.customPlaceholderRegistry = customPlaceholderRegistry;
}

private static boolean isFieldAnnotatedWith(Class<?> clazz, String fieldName, Class<? extends Annotation> annotation) {
protected static boolean isFieldAnnotatedWith(Class<?> clazz, String fieldName, Class<? extends Annotation> annotation) {
try {
return clazz.getDeclaredField(fieldName)
.getDeclaredAnnotation(annotation) != null;
Expand Down Expand Up @@ -127,7 +125,6 @@ private static DateTimeFormatter toDateTimeFormatter(Format format) {
@Override
public Optional<PlaceholderData> resolve(String placeholderName, Locale locale) {
logger.debug("Trying to resolve placeholder {}", placeholderName);
placeholderName = placeholderMapper.map(placeholderName);
Optional<PlaceholderData> result = Optional.empty();
for (String property : placeholderName.split("\\.")) {
result = result.isEmpty()
Expand Down Expand Up @@ -183,7 +180,7 @@ private Optional<PlaceholderData> doResolve(String placeholderName, Locale local
}
}

private Object getBeanProperty(String placeholderName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
protected Object getBeanProperty(String placeholderName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
if (SELF_REFERENCE.equals(placeholderName)) {
return bean;
} else if (bean.getClass().isRecord()) {
Expand All @@ -198,7 +195,7 @@ private Object getBeanProperty(String placeholderName) throws InvocationTargetEx
}
}

private Optional<PlaceholderData> formatTemporal(String placeholderName, Temporal time, Locale locale) {
protected Optional<PlaceholderData> formatTemporal(String placeholderName, Temporal time, Locale locale) {
Optional<DateTimeFormatter> formatter;
if (isFieldAnnotatedWith(bean.getClass(), placeholderName, Format.class)) {
formatter = ReflectionUtils.findFieldAnnotation(bean.getClass(), placeholderName, Format.class)
Expand All @@ -219,7 +216,7 @@ private Optional<PlaceholderData> formatTemporal(String placeholderName, Tempora
return formatter.map(dateTimeFormatter -> new ScalarPlaceholderData(dateTimeFormatter.format(time)));
}

private NumberFormat findNumberFormat(String fieldName, Locale locale) {
protected NumberFormat findNumberFormat(String fieldName, Locale locale) {
return ReflectionUtils.findFieldAnnotation(bean.getClass(), fieldName, Percentage.class)
.map(percentage -> toNumberFormat(percentage, locale))
.or(() -> ReflectionUtils.findFieldAnnotation(bean.getClass(), fieldName, Money.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.docutools.jocument.Template;
import com.docutools.jocument.TestUtils;
import com.docutools.jocument.impl.CustomPlaceholderRegistryImpl;
import com.docutools.jocument.impl.FutureReflectionResolver;
import com.docutools.jocument.impl.ReflectionResolver;
import com.docutools.jocument.sample.model.SampleModelData;
import com.docutools.jocument.sample.placeholders.QuotePlaceholder;
Expand Down Expand Up @@ -199,7 +200,7 @@ void shouldResolveLegacy() throws IOException, InterruptedException {
// Assemble
Template template = Template.fromClassPath("/templates/word/LegacyCollectionsTemplate.docx")
.orElseThrow();
PlaceholderResolver resolver = new ReflectionResolver(SampleModelData.PICARD);
PlaceholderResolver resolver = new FutureReflectionResolver(SampleModelData.FUTURE_PICARD);

// Act
Document document = template.startGeneration(resolver);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.docutools.jocument.sample.model;

import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public record FutureCaptain(CompletableFuture<String> name,
CompletableFuture<Integer> rank,
CompletableFuture<Uniform> uniform,
CompletableFuture<FirstOfficer> officer,
CompletableFuture<List<Service>> services,
CompletableFuture<Path> profilePic) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

public class SampleModelData {

public static final Captain PICARD;
public static final FutureCaptain FUTURE_PICARD;
public static final Person PICARD_PERSON = new Person("Jean-Luc", "Picard", LocalDate.of(1948, 9, 23));
public static final List<Captain> CAPTAINS;
public static final Ship ENTERPRISE;
Expand All @@ -29,6 +30,12 @@ public class SampleModelData {
Path.of(SampleModelData.class.getResource("/images/picardProfile.jpg").toURI()));
CAPTAINS = List.of(PICARD);
ENTERPRISE = new Ship("USS Enterprise", PICARD, 5, services, LocalDate.now());
FUTURE_PICARD = new FutureCaptain(CompletableFuture.completedFuture("Jean-Luc Picard"),
CompletableFuture.completedFuture(4),
CompletableFuture.completedFuture(Uniform.Red),
CompletableFuture.completedFuture(new FirstOfficer("Riker", 3, Uniform.Red)),
CompletableFuture.completedFuture(services),
CompletableFuture.completedFuture(Path.of(SampleModelData.class.getResource("/images/picardProfile.jpg").toURI())));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/log4j2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<Root level="DEBUG">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
Expand Down

0 comments on commit 633db6a

Please sign in to comment.