Skip to content

Commit

Permalink
Add custom lambda functions and suppliers
Browse files Browse the repository at this point in the history
Refactor code to support custom lambda functions and suppliers in OfficeStamperConfiguration. Add tests for new feature.
  • Loading branch information
caring-coder committed Oct 31, 2024
1 parent e438ddc commit c7fbf1d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;


/**
Expand All @@ -25,8 +26,7 @@ public interface OfficeStamperConfiguration {
* allows you to define how unresolved expressions are handled in a more flexible and
* comprehensive manner.
*/
@Deprecated(since = "2.5", forRemoval = true)
boolean isFailOnUnresolvedExpression();
@Deprecated(since = "2.5", forRemoval = true) boolean isFailOnUnresolvedExpression();

/**
* Sets the failOnUnresolvedExpression flag to determine whether unresolved expressions should
Expand Down Expand Up @@ -113,8 +113,7 @@ public interface OfficeStamperConfiguration {
* allows you to define how unresolved expressions are handled in a more flexible and
* comprehensive manner.
*/
@Deprecated(since = "2.5", forRemoval = true)
OfficeStamperConfiguration replaceUnresolvedExpressions(
@Deprecated(since = "2.5", forRemoval = true) OfficeStamperConfiguration replaceUnresolvedExpressions(
boolean replaceUnresolvedExpressions
);

Expand Down Expand Up @@ -275,14 +274,13 @@ OfficeStamperConfiguration setSpelParserConfiguration(

List<CustomFunction> customFunctions();

void addCustomFunction(String name, Supplier<?> implementation);

<T> NeedsFunctionImpl<T> addCustomFunction(String name, Class<T> class0);

<T, U> NeedsBiFunctionImpl<T, U> addCustomFunction(String name, Class<T> class0, Class<U> class1);

<T, U, V> NeedsTriFunctionImpl<T, U, V> addCustomFunction(
String name,
Class<T> class0,
Class<U> class1,
Class<V> class2
String name, Class<T> class0, Class<U> class1, Class<V> class2
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

/**
* The {@link DocxStamperConfiguration} class represents the configuration for
Expand Down Expand Up @@ -403,6 +404,11 @@ public void addCustomFunction(CustomFunction function) {
}


@Override public void addCustomFunction(String name, Supplier<?> implementation) {
this.addCustomFunction(new CustomFunction(name, List.of(), args -> implementation.get()));
}


@Override public <T> NeedsFunctionImpl<T> addCustomFunction(String name, Class<T> class0) {
return new FunctionBuilder<>(this, name, class0);
}
Expand Down
24 changes: 5 additions & 19 deletions engine/src/test/java/pro/verron/officestamper/test/Contexts.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pro.verron.officestamper.preset.StampTable;

import java.nio.file.Path;
import java.time.LocalDate;
import java.util.*;

import static org.junit.jupiter.params.provider.Arguments.of;
Expand Down Expand Up @@ -192,18 +193,6 @@ public static Map<String, Object> coupleContext() {
return context;
}

/**
* <p>nowContext.</p>
*
* @return a {@link Contexts.DateContext} object
*
* @since 1.6.6
*/
public static DateContext nowContext() {
var now = new Date();
return new DateContext(now);
}

/**
* <p>mapAndReflectiveContext.</p>
*
Expand Down Expand Up @@ -285,13 +274,6 @@ public record Role(String name, String actor) {}
*/
public record Characters(List<Role> characters) {}

/**
* Represents a Date context.
*
* @param date The Date value to be encapsulated in the context.
*/
public record DateContext(Date date) {}

public record ZonedDateContext(java.time.ZonedDateTime date) {}

/**
Expand Down Expand Up @@ -614,4 +596,8 @@ public record Name(String name) {}
public static class EmptyContext {}

public record TableContext(StampTable characters) {}

public record DateContext(LocalDate date) {

}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
package pro.verron.officestamper.test;

import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import pro.verron.officestamper.test.Functions.UppercaseFunction;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static pro.verron.officestamper.preset.OfficeStamperConfigurations.standard;
import static pro.verron.officestamper.test.TestUtils.getResource;
import static pro.verron.officestamper.test.TestUtils.makeResource;

@DisplayName("Custom functions")
class CustomFunctionTests {
@DisplayName("Custom functions") class CustomFunctionTests {


@DisplayName("Should works with variables, multiline text, in comment content, inside comment, and in repetitions.")
@Test()
void features() {
var config = standard()
.exposeInterfaceToExpressionLanguage(UppercaseFunction.class, Functions.upperCase());
@DisplayName("Should allow to inject full interfaces") @Test() void interfaces() {
var config = standard().exposeInterfaceToExpressionLanguage(UppercaseFunction.class, Functions.upperCase());
var template = getResource(Path.of("CustomExpressionFunction.docx"));
var context = Contexts.show();
var stamper = new TestDocxStamper<>(config);
Expand Down Expand Up @@ -60,4 +67,66 @@ void features() {
var actual = stamper.stampAndLoadAndExtract(template, context);
assertEquals(expected, actual);
}

@DisplayName("Should allow to inject lambda functions") @Test() void functions()
throws IOException, Docx4JException {
var config = standard();
config.addCustomFunction("toUppercase", String.class)
.withImplementation(String::toUpperCase);
var template = makeResource("${toUppercase(name)}");
var context = Contexts.show();
var stamper = new TestDocxStamper<>(config);
var expected = """
THE SIMPSONS
""";
var actual = stamper.stampAndLoadAndExtract(template, context);
assertEquals(expected, actual);
}

@DisplayName("Should allow to inject lambda suppliers.") @Test() void suppliers()
throws IOException, Docx4JException {
var config = standard();
config.addCustomFunction("foo", () -> List.of("a", "b", "c"));
var template = makeResource("${foo()}");
var context = Contexts.empty();
var stamper = new TestDocxStamper<>(config);
var expected = """
[a, b, c]
""";
var actual = stamper.stampAndLoadAndExtract(template, context);
assertEquals(expected, actual);
}

@DisplayName("Should allow to inject lambda suppliers.") @Test() void bifunctions()
throws IOException, Docx4JException {
var config = standard();
config.addCustomFunction("Add", String.class, Integer.class)
.withImplementation((s, i) -> new BigDecimal(s).add(new BigDecimal(i)));
var template = makeResource("${Add('3.22', 4)}");
var context = Contexts.empty();
var stamper = new TestDocxStamper<>(config);
var expected = """
7.22
""";
var actual = stamper.stampAndLoadAndExtract(template, context);
assertEquals(expected, actual);
}

@DisplayName("Should allow to inject lambda trifunctions.")
@CsvSource({"ZH,2024 四月", "FR,2024 avril", "EN,2024 April", "JA,2024 4月", "HE,2024 אפריל", "IT,2024 aprile"})
@ParameterizedTest() void trifunctions(String tag, String expected)
throws IOException, Docx4JException {
var config = standard();
config.addCustomFunction("format", LocalDate.class, String.class, String.class)
.withImplementation((date, pattern, languageTag) -> {
var locale = Locale.forLanguageTag(languageTag);
var formatter = DateTimeFormatter.ofPattern(pattern, locale);
return formatter.format(date);
});
var template = makeResource("${format(date,'yyyy MMMM','%s')}" .formatted(tag));
var context = new Contexts.DateContext(LocalDate.of(2024, Month.APRIL, 1));
var stamper = new TestDocxStamper<>(config);
var actual = stamper.stampAndLoadAndExtract(template, context);
assertEquals(expected + "\n", actual);
}
}

0 comments on commit c7fbf1d

Please sign in to comment.