From 0816a6c4bf02828844ee797b61962f741e1aaf5f Mon Sep 17 00:00:00 2001 From: janos erdos Date: Tue, 30 Jul 2024 21:45:26 +0200 Subject: [PATCH 1/7] feat: make install --- Makefile | 5 ++++- build.clj | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c6e33b4f..e13cf8bf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean prepare lint pom jar uberjar javadoc compile clj-test java-test visual-test coverage test all +.PHONY: clean prepare lint pom install jar uberjar javadoc compile clj-test java-test visual-test coverage test all .DEFAULT_GOAL := all clean: @@ -13,6 +13,9 @@ lint: clean pom: clean clojure -T:build pom +install: clean + clojure -T:build install + jar: clean clojure -T:build jar diff --git a/build.clj b/build.clj index 3ba4c646..7e699dc6 100644 --- a/build.clj +++ b/build.clj @@ -76,6 +76,15 @@ (println "Built JAR file") opts) +(defn install [opts] + (jar opts) + (b/install {:basis basis + :lib lib + :version version + :jar-file jar-file-name + :class-dir jar-content}) + opts) + (defn java-test [_] (def basis (b/create-basis {:project "deps.edn" :aliases [:junit]})) From ad00799a29084b16c05bbc6f78c186947b46054e Mon Sep 17 00:00:00 2001 From: Janos Erdos Date: Tue, 30 Jul 2024 21:55:59 +0200 Subject: [PATCH 2/7] feat: functions provider SPI (#166) --- build.clj | 11 ++++++- docs/Functions.md | 11 ++++++- .../stencil/functions/BasicFunctions.java | 8 +++++ .../stencil/functions/DateFunctions.java | 7 +++++ .../stencil/functions/FunctionEvaluator.java | 31 +++++++++++++------ .../stencil/functions/FunctionsProvider.java | 5 +++ .../stencil/functions/LocaleFunctions.java | 8 +++++ .../stencil/functions/NumberFunctions.java | 9 ++++++ .../stencil/functions/StringFunctions.java | 7 +++++ 9 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 java-src/io/github/erdos/stencil/functions/FunctionsProvider.java diff --git a/build.clj b/build.clj index 7e699dc6..fe385089 100644 --- a/build.clj +++ b/build.clj @@ -1,5 +1,6 @@ (ns build - (:require [clojure.tools.build.api :as b] + (:require [clojure.java.io :as io] + [clojure.tools.build.api :as b] [clojure.tools.build.util.file :as file])) (def build-folder "target") @@ -29,6 +30,14 @@ :javac-opts ["-source" "8" "-target" "8"]}) (b/copy-file {:src "java-src/io/github/erdos/stencil/standalone/help.txt" :target "target/classes/io/github/erdos/stencil/standalone/help.txt"}) + + ;; generate service provider config for function providers + (let [services (io/file "target/classes/META-INF/services/io.github.erdos.stencil.functions.FunctionsProvider")] + (io/make-parents services) + (doseq [f (file-seq (clojure.java.io/file jar-content)) + [_ a] (re-seq #"^target/classes/(.*\$Provider).class$" (str f)) + :let [clazz (str (.replace a "/" ".") "\n")]] + (spit services, clazz :append true))) (spit (str jar-content "/stencil-version") version) opts) diff --git a/docs/Functions.md b/docs/Functions.md index 60df5531..38bb7b75 100644 --- a/docs/Functions.md +++ b/docs/Functions.md @@ -233,7 +233,9 @@ Expects one number argument containing a list with numbers. Sums up the numbers ## Custom functions -You can register custom implementations of `io.github.erdos.stencil.functions.Function` or the `stencil.functions/call-fn` multimethod. +You can register custom implementations of `io.github.erdos.stencil.functions.Function` or the `stencil.functions/call-fn` multimethod. +If you implement the `call-fn` multimethod, the namespace containing these implementations should be loaded before rendering a document. +(Keep in mind, that `call-fn` implementations always have priority over `io.github.erdos.stencil.functions.Function` implementations) Clojure example: @@ -271,3 +273,10 @@ public class FirstFuncion implements Function { API.render(preparedTemplate, fragments, data, Arrays.asList(new FirstFunction())); ``` + + +### Automatic registration of custom functions + +Stencil uses the JVM's [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) facility to load function provider implementations. +If you want to register your custom functions automatically, implement the `io.github.erdos.stencil.functions.FunctionsProvider` interface, +and add these implementations to your extension library's `META-INF/services/io.github.erdos.stencil.functions.FunctionsProvider` file. \ No newline at end of file diff --git a/java-src/io/github/erdos/stencil/functions/BasicFunctions.java b/java-src/io/github/erdos/stencil/functions/BasicFunctions.java index cfb8bc3f..c76c9c19 100644 --- a/java-src/io/github/erdos/stencil/functions/BasicFunctions.java +++ b/java-src/io/github/erdos/stencil/functions/BasicFunctions.java @@ -1,5 +1,6 @@ package io.github.erdos.stencil.functions; +import java.util.Arrays; import java.util.Collection; /** @@ -72,4 +73,11 @@ public Object call(Object... arguments) { public String getName() { return name().toLowerCase(); } + + public static class Provider implements FunctionsProvider { + @Override + public Iterable functions() { + return Arrays.asList(values()); + } + } } diff --git a/java-src/io/github/erdos/stencil/functions/DateFunctions.java b/java-src/io/github/erdos/stencil/functions/DateFunctions.java index 2ce892cc..7d748080 100644 --- a/java-src/io/github/erdos/stencil/functions/DateFunctions.java +++ b/java-src/io/github/erdos/stencil/functions/DateFunctions.java @@ -136,4 +136,11 @@ private static Optional maybeLocalDateTime(Object obj) { public String getName() { return name().toLowerCase(); } + + public static class Provider implements FunctionsProvider { + @Override + public Iterable functions() { + return asList(values()); + } + } } diff --git a/java-src/io/github/erdos/stencil/functions/FunctionEvaluator.java b/java-src/io/github/erdos/stencil/functions/FunctionEvaluator.java index b74e49b4..4e8d3648 100644 --- a/java-src/io/github/erdos/stencil/functions/FunctionEvaluator.java +++ b/java-src/io/github/erdos/stencil/functions/FunctionEvaluator.java @@ -3,38 +3,49 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.ServiceLoader; public final class FunctionEvaluator { + private final static Map preloaded; - private final Map functions = new HashMap<>(); + static { + preloaded = new HashMap<>(); + for (FunctionsProvider provider : ServiceLoader.load(FunctionsProvider.class)) { + for (Function fn : provider.functions()) { + registerFunction(preloaded, fn); + } + } + } + + private final Map functions; { - registerFunctions(BasicFunctions.values()); - registerFunctions(StringFunctions.values()); - registerFunctions(NumberFunctions.values()); - registerFunctions(DateFunctions.values()); - registerFunctions(LocaleFunctions.values()); + this.functions = new HashMap<>(preloaded); } - private void registerFunction(Function function) { + private static void registerFunction(Map map, Function function) { if (function == null) throw new IllegalArgumentException("Registered function must not be null."); - functions.put(function.getName().toLowerCase(), function); + Function present = map.put(function.getName().toLowerCase(), function); + if (present != null) + throw new IllegalArgumentException("Function with name has already been registered."); } /** * Registers a function to this evaluator engine. * Registered functions can be invoked from inside template files. * - * @param functions any number of function instances. + * @param functions list of functions to register */ + @SuppressWarnings("WeakerAccess") public void registerFunctions(Function... functions) { for (Function function : functions) { - registerFunction(function); + registerFunction(this.functions, function); } } + /** * Calls a function by name. * diff --git a/java-src/io/github/erdos/stencil/functions/FunctionsProvider.java b/java-src/io/github/erdos/stencil/functions/FunctionsProvider.java new file mode 100644 index 00000000..44d8e585 --- /dev/null +++ b/java-src/io/github/erdos/stencil/functions/FunctionsProvider.java @@ -0,0 +1,5 @@ +package io.github.erdos.stencil.functions; + +public interface FunctionsProvider { + Iterable functions(); +} diff --git a/java-src/io/github/erdos/stencil/functions/LocaleFunctions.java b/java-src/io/github/erdos/stencil/functions/LocaleFunctions.java index fbf14e5a..327160f0 100644 --- a/java-src/io/github/erdos/stencil/functions/LocaleFunctions.java +++ b/java-src/io/github/erdos/stencil/functions/LocaleFunctions.java @@ -1,6 +1,7 @@ package io.github.erdos.stencil.functions; import java.text.NumberFormat; +import java.util.Arrays; import java.util.Locale; import static java.util.Locale.forLanguageTag; @@ -53,4 +54,11 @@ private static String formatting(Function function, java.util.function.Function< public String getName() { return name().toLowerCase(); } + + public static class Provider implements FunctionsProvider { + @Override + public Iterable functions() { + return Arrays.asList(values()); + } + } } diff --git a/java-src/io/github/erdos/stencil/functions/NumberFunctions.java b/java-src/io/github/erdos/stencil/functions/NumberFunctions.java index b44207ba..a1ce7bab 100644 --- a/java-src/io/github/erdos/stencil/functions/NumberFunctions.java +++ b/java-src/io/github/erdos/stencil/functions/NumberFunctions.java @@ -1,5 +1,7 @@ package io.github.erdos.stencil.functions; +import java.util.Arrays; + /** * Common numeric functions. */ @@ -73,4 +75,11 @@ private static Number maybeNumber(Object... arguments) { return (Number) arguments[0]; } } + + public static class Provider implements FunctionsProvider { + @Override + public Iterable functions() { + return Arrays.asList(values()); + } + } } diff --git a/java-src/io/github/erdos/stencil/functions/StringFunctions.java b/java-src/io/github/erdos/stencil/functions/StringFunctions.java index ca2bb6d8..87e9f96e 100644 --- a/java-src/io/github/erdos/stencil/functions/StringFunctions.java +++ b/java-src/io/github/erdos/stencil/functions/StringFunctions.java @@ -112,4 +112,11 @@ public Object call(Object... arguments) throws IllegalArgumentException { public String getName() { return name().toLowerCase(); } + + public static class Provider implements FunctionsProvider { + @Override + public Iterable functions() { + return Arrays.asList(values()); + } + } } From b196f569497b0d16deda55e8376d289590f41bbd Mon Sep 17 00:00:00 2001 From: janos erdos Date: Wed, 31 Jul 2024 06:54:50 +0200 Subject: [PATCH 3/7] fix: reflection errors in service --- service/src/stencil/service.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/service/src/stencil/service.clj b/service/src/stencil/service.clj index 5f4e45da..d847be2a 100644 --- a/service/src/stencil/service.clj +++ b/service/src/stencil/service.clj @@ -9,6 +9,7 @@ [stencil.slf4j :as slf4j] [clojure.data :refer [diff]] [clojure.java.io :as io :refer [file]] + [clojure.string] [ring.middleware.json :refer [wrap-json-body]])) (set! *warn-on-reflection* true) @@ -64,8 +65,8 @@ (prepared template) (throw (ex-info "Template file does not exist!" {:status 404}))))) -(defn- exception->str [e] - (clojure.string/join "\n" (for [e (iterate #(.getCause %) e) :while e] (.getMessage e)))) +(defn- exception->str [^Exception e] + (clojure.string/join "\n" (for [^Exception e (iterate #(.getCause ^Exception %) e) :while e] (.getMessage e)))) (defn wrap-err [handler] (fn [request] From b4b2951c9b3e9860515e5e24761ff4395bada3c5 Mon Sep 17 00:00:00 2001 From: janos erdos Date: Wed, 31 Jul 2024 07:55:27 +0200 Subject: [PATCH 4/7] feat: update graalvm version --- service/Dockerfile.graalvm | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/service/Dockerfile.graalvm b/service/Dockerfile.graalvm index 76bd6f45..baaeaab0 100644 --- a/service/Dockerfile.graalvm +++ b/service/Dockerfile.graalvm @@ -9,10 +9,7 @@ COPY . /app WORKDIR /app RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar - -FROM ghcr.io/graalvm/graalvm-ce:java8-21.1.0 AS BASE - -RUN gu install native-image +FROM container-registry.oracle.com/graalvm/native-image:22 AS BASE COPY --from=CLJ /app/app-standalone.jar /app/app-standalone.jar WORKDIR /app @@ -32,10 +29,11 @@ COPY --from=BASE /lib64/libdl.so.2 /lib64/libdl.so.2 COPY --from=BASE /lib64/libpthread.so.0 /lib64/libpthread.so.0 COPY --from=BASE /lib64/libz.so.1 /lib64/libz.so.1 COPY --from=BASE /lib64/librt.so.1 /lib64/librt.so.1 -COPY --from=BASE /lib64/libcrypt.so.1 /lib64/libcrypt.so.1 COPY --from=BASE /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 # COPY --from=BASE /lib64/libfreebl3.so /lib64/libfreebl3.so COPY --from=BASE /app/app-standalone / CMD ["/app-standalone"] + +EXPOSE 8080 From 48658f7908454a7b9df714a76d0284883f879e9d Mon Sep 17 00:00:00 2001 From: janos erdos Date: Sat, 3 Aug 2024 17:29:27 +0200 Subject: [PATCH 5/7] feat: version v0.6.1 --- README.md | 8 ++++---- deps.edn | 2 +- service/project.clj | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4ab5056e..d5da2bcb 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ The project has a simple [service implementation](https://github.com/erdos/stenc ## Version -**Latest stable** version is `0.6.0` +**Latest stable** version is `0.6.1` -**Latest snapshot** version is `0.6.1-SNAPSHOT` +**Latest snapshot** version is `0.6.2-SNAPSHOT` If you are using Maven, add the followings to your `pom.xml`: @@ -52,7 +52,7 @@ The dependency: io.github.erdos stencil-core - 0.6.0 + 0.6.1 ``` @@ -67,7 +67,7 @@ And the [Clojars](https://clojars.org) repository: Alternatively, if you are using Leiningen, add the following to the `:dependencies` section of your `project.clj` -file: `[io.github.erdos/stencil-core "0.6.0"]` +file: `[io.github.erdos/stencil-core "0.6.1"]` Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page. diff --git a/deps.edn b/deps.edn index de6a39c2..e43a66e5 100644 --- a/deps.edn +++ b/deps.edn @@ -3,7 +3,7 @@ org.slf4j/slf4j-api {:mvn/version "2.0.9"}} :paths ["src" "target/classes"] :aliases - {:stencil/version "0.6.1-SNAPSHOT" + {:stencil/version "0.6.1" :build {:deps {org.clojure/clojure {:mvn/version "1.12.0-beta1"} diff --git a/service/project.clj b/service/project.clj index 3263a95e..79fe1a7a 100644 --- a/service/project.clj +++ b/service/project.clj @@ -1,10 +1,10 @@ -(defproject io.github.erdos/stencil-service "0.6.1-SNAPSHOT" +(defproject io.github.erdos/stencil-service "0.6.1" :description "Web service for the Stencil templating engine" :url "https://github.com/erdos/stencil" :license {:name "Eclipse Public License - v 2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.11.1"] - [io.github.erdos/stencil-core "0.6.1-SNAPSHOT"] + [io.github.erdos/stencil-core "0.6.1"] [org.slf4j/slf4j-api "2.0.9"] [org.mozilla/rhino-engine "1.7.14"] [http-kit "2.7.0"] From c8fca89deb71414486aee73d3d36bc2b04b8fde3 Mon Sep 17 00:00:00 2001 From: janos erdos Date: Fri, 16 Aug 2024 12:26:05 +0200 Subject: [PATCH 6/7] feat: start work on v0.6.2 --- deps.edn | 2 +- service/project.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps.edn b/deps.edn index e43a66e5..272d0762 100644 --- a/deps.edn +++ b/deps.edn @@ -3,7 +3,7 @@ org.slf4j/slf4j-api {:mvn/version "2.0.9"}} :paths ["src" "target/classes"] :aliases - {:stencil/version "0.6.1" + {:stencil/version "0.6.2-SNAPSHOT" :build {:deps {org.clojure/clojure {:mvn/version "1.12.0-beta1"} diff --git a/service/project.clj b/service/project.clj index 79fe1a7a..f599b44b 100644 --- a/service/project.clj +++ b/service/project.clj @@ -1,10 +1,10 @@ -(defproject io.github.erdos/stencil-service "0.6.1" +(defproject io.github.erdos/stencil-service "0.6.2-SNAPSHOT" :description "Web service for the Stencil templating engine" :url "https://github.com/erdos/stencil" :license {:name "Eclipse Public License - v 2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :dependencies [[org.clojure/clojure "1.11.1"] - [io.github.erdos/stencil-core "0.6.1"] + [io.github.erdos/stencil-core "0.6.2-SNAPSHOT"] [org.slf4j/slf4j-api "2.0.9"] [org.mozilla/rhino-engine "1.7.14"] [http-kit "2.7.0"] From 0ab758d8e399d0c080fa2f0b0db798bbc5763247 Mon Sep 17 00:00:00 2001 From: Janos Erdos Date: Sun, 13 Oct 2024 09:52:53 +0200 Subject: [PATCH 7/7] fancy README --- README.md | 62 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d5da2bcb..0bfdcd88 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Stencil Template Engine Stencil is an open source templating engine that transforms Office Open XML documents (mostly Microsoft -Office's Word `.docx` files) from Java programs. It has a simple syntax and no programming is needed to write document templates. +Office's Word `.docx` files) from the JVM. It has a simple syntax and no programming is needed to write document templates.

stencil flow

@@ -20,33 +20,39 @@ them to make your template more readable. ## Features -- Works with `docx` and `pptx` files -- Simple value substitution, conditional and repeating blocks -- Substituting HTML text for dynamic text formatting -- Dynamically replace images and links in the template -- Show/hide rows and columns in tables +- 📄 **Multiple Formats:** Works with `docx` and `pptx` files +- 💻 **Simple syntax:** For value substitution, conditional and repeating blocks +- 🔧 **Extendable:** Dozens of [built-in functions](https://stencil.erdos.dev/Functions.html) callable from the template +- 📰 **Dynamic content:** Substituting HTML text for dynamic text formatting +- 🌄 **Images and links:** Dynamically replace images and links in the template +- 👀 **Tables:** Show/hide rows and columns in tables +- 📐 **Programmable:** Offers API for Java and Clojure. Deployable as a Docker container. -## Getting Started with the Library +## 📖 Getting Started with the Library - See the [Example templates](examples) - Read the [Documentation](https://stencil.erdos.dev) -- Read about the [Java API](docs/GettingStarted.md#java-api) and the [Clojure API](docs/GettingStarted.md#clojure-api). +- Reference the [Java API](docs/GettingStarted.md#java-api) and the [Clojure API](docs/GettingStarted.md#clojure-api). - -## Getting Started with the Service +## 🐳 Getting Started with the Service The project has a simple [service implementation](https://github.com/erdos/stencil/tree/master/service), which is available on GitHub Packages as a [Container image](https://github.com/users/erdos/packages/container/package/stencil). -## Version +## 👉 Version **Latest stable** version is `0.6.1` **Latest snapshot** version is `0.6.2-SNAPSHOT` -If you are using Maven, add the followings to your `pom.xml`: +Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page. + +
+ For Java with Maven -The dependency: + If you are using Maven, add the followings to your `pom.xml`: + + 1. The dependency: ``` xml @@ -56,23 +62,39 @@ The dependency: ``` -And the [Clojars](https://clojars.org) repository: +2. And the [Clojars](https://clojars.org) repository: ``` xml clojars.org https://repo.clojars.org -``` +``` +
-Alternatively, if you are using Leiningen, add the following to -the `:dependencies` section of your `project.clj` -file: `[io.github.erdos/stencil-core "0.6.1"]` +
+ For Java with Gradle + + Add to the `dependencies` section of your `build.gradle` file: `implementation('io.github.erdos/stencil-core:0.6.1')` +
+ +
+ For Clojure with Leiningen + + If you are using Leiningen, add the following to the `:dependencies` section of your `project.clj` file: + + `[io.github.erdos/stencil-core "0.6.1"]` +
+ +
+ For Clojure with deps.edn + + Add `io.github.erdos/stencil-core {:mvn/version "0.6.1"}` +
-Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page. -## License +## 😎 License Copyright (c) Janos Erdos. All rights reserved. The use and distribution terms for this software are covered by the Eclipse Public License 2.0