diff --git a/README.jrf b/README.jrf index 2953e626..59bb292c 100644 --- a/README.jrf +++ b/README.jrf @@ -1,5 +1,5 @@ # This is a Jamal reference file containing serialized base64 encoded macros -# Created: 2024-06-03 15:53:36 +0200 +# Created: 2024-06-14 12:42:03 +0200 # id|openStr|closeStr|verbatim|tailParameter|pure|content|parameters # TOC VE9D|eyU=|JX0=|0|0|0|Ci4gPDxJbnN0YWxsYXRpb24+PgouIDw8R1M+PgouIDw8Q29uZmlndXJhdGlvbj4+Ci4gPDxGZWF0dXJlcz4+Ci4gPDxDb250cmlidXRpbmc+PgouIDw8RG9jdW1lbnRhdGlvbj4+Ci4gPDxMaWNlbnNlPj4KLiA8PENoYW5nZWxvZz4+Ci4gPDxSb2FkbWFwPj4KLiA8PFN1cHBvcnQ+PgouIDw8RkFRPj4KLiA8PE1haW50ZW5hbmNlPj4=| diff --git a/documentation/JINJA.adoc.jam b/documentation/JINJA.adoc.jam new file mode 100644 index 00000000..e20f2498 --- /dev/null +++ b/documentation/JINJA.adoc.jam @@ -0,0 +1,165 @@ += Comparing JINJA Templates to Jamal + +JINJA is a templating engine for Python. +It is similar to Jamal that it places directives inside the document. +In most of the cases, you can use Jamal where you use JINJA as the other way around. +These are competing products. + +In the following, I will compare JINJA to Jamal along different features. +I am fairly knowledgeable with Jamal, but my knowledge about JINJA is limited, so I might be wrong in some comparisons. +If you feel that some statements in this document are incorrect, please create a pull request correcting them. +I will review the suggested changes, and I reference the contribution. + + + +== License + +|=== +|Jamal |JINJA + +|Apache 2 License +|BSD-3-Clause License + +|=== + +Both products are open source licensed and are available in source code. + +== Programming Language + +|=== +|Jamal |JINJA + +|Java + +Groovy, Ruby, ScriptBasic, Kotlin, basic +|Python + +|language independence, meta-markup +|strongly integrated, template language + +|=== + +JINJA is developed in Python. +Jamal is developed in Java. + +Usually, the language of the development is not important for the users of the software. +This is partially true for JINJA. + +Although you can use JINJA in any project without caring that it is running in Python, this product is mainly targeted for Python applications. +The primary use of JINJA is to be a Python templating framework. +A good example is the looping construct that iterates over Python lists or dictionaries. + +Jamal is written in Java, but its design is not tied to Java. +You can use Jamal in any project. +For example, the looping construct in Jamal is not tied to Java collections, but it can iterate over any list of strings you present in the document. +Looping over Java structures is also possible, but it is not the primary use case. + +Jamal also has features, which are tight to Java, but these are implemented in optional modules. + +Jamal also supports JVM languages, like Groovy, Scala, Kotlin, etc. +It also supports ScriptBasic and has a simple built-in imperative BASIC like language. + +== Extensibility + +|=== +|Jamal |JINJA + +|JVM language macros +|Python code + +| IO modules +| + +| Debugger +| + +|=== + +JINJA is tightly integrated with implementation language and language features and can be extended with Python code. + +Jamal is designed to be modular and can be extended with Java code. +Even the core macros are provided in a separate library and could be replaced with custom implementations. +Macros for Jamal can be written in Java, Groovy, Ruby, Kotlin, or any other JVM language. + +Jamal can also be extended with debuggers and IO modules. +The standard installation provides a web-based graphical debugger and IO channels that can open files on the disk (default), in JAR files, Java resources and from the network. + +== Integration, Embedding + +|=== +|Jamal |JINJA + +| Command line, Jbang +| Flask + +| Maven, Gradle +| Django + +| Asciidoctor IntelliJ, AsciidocFX +| Babel + +| JavaDoc +| Pylons + +|=== + +Both applications can be embedded into different applications. +JINJA can be embedded into Python applications, Jamal can be embedded into Java applications. +JINJA is integrated with the + +* `Flask`, +* `Django`, +* `Babel`, and +* `Pylons` + +content management systems. + +Jamal is integrated into more Java technology related applications, like Maven, Gradle, JavaDoc, but also into the Asciidoctor plugin in IntelliJ IDEA and AsciidocFX. +This makes it possible to edit Jamal meta markup files interactively in these environments for Asciidoc, Markdown, XML, and other format documents. + +== Editor Support + +|=== +|Jamal |JINJA + +| AsciidocFX, IntelliJ IDEA WYSIWYG editing +| PyCharm template support + +|=== + +Jamal can be interactively edited in AsciidoctorFX and IntelliJ IDEA. +If you choose IntelliJ IDEA, you have to install the Asciidoctor plugin in IntelliJ IDEA and configure the editor to treat all your `*.jam` files as AsciiDoc files. +You need also copy the Jamal files from the maven central into the project library. +After that, you can edit Jamal meta-markup enhanced Markdown and Asciidoctor files in IntelliJ. + +If you use AsciidoctorFX, you do not need a plugin. +The rest of the steps are the same. + +Copying the filed from maven central is automatic, you can run Jamal with a special command line option to do it for you. + +You can also download and install IntelliJ IDEA templates that support a bit of syntax autocompletion for Jamal. + +JINJA is supported in PyCharm. +It is part of the editor bundled, you only configure the file extensions that you want to use it for. +The editor support is syntactical, highlighting, and code completion and not WYSIWYG editing. + +Generally, the editor support is fairly mature for both products. +The lack of WYSIWYG editing in the case of JINJA is not a significant drawback because of the main focus area supporting templates which then are used from program code. + +== Debug Support + +|=== +|Jamal |JINJA + +| Interactive Web-based React.js debugger, trace +| trace + +|=== + +Jamal supports a debugger interface, and there is a web-based debugger implemented in React.js. +Using that, you can interactively debug and execute the Jamal evaluation step by step. + +It is also possible to generate an XML trace file that will contain all macro evaluations hierarchically. +The format XML was selected because it has a tree structure supporting the hierarchical nature of the macro evaluation, and it also has superb editor support in different editors. + +JINJA cannot be debugged interactively, but has extensive tracing facility. \ No newline at end of file diff --git a/documentation/MODULES.adoc b/documentation/MODULES.adoc index c5790a99..d09ba1f9 100644 --- a/documentation/MODULES.adoc +++ b/documentation/MODULES.adoc @@ -39,6 +39,11 @@ Input handlers are loaded via the service loader and can load resource files, ma +=== packaging + +Directory containing different packaging tools to be executed after the release of the Java code version. +These create different packages, like docker image, Debian package, etc. + == Modules === 1. link:https://github.com/verhas/jamal/blob/master/jamal-all/README.adoc[ALL] ^_core_^ @@ -272,5 +277,11 @@ The code in the directory `jamal-debug-ui` contains the REACT.js based ui for th This is not a module. The code in the directory `jamal-docker` contains a Dockerfile to build a Docker image with Jamal. +=== 40. link:https://github.com/verhas/jamal/blob/master/jamal-packaging/README.adoc[PACKAGING] ^_packaging_^ + + + +Contains the different packaging code in the subdirectories. + diff --git a/documentation/MODULES.adoc.jam b/documentation/MODULES.adoc.jam index 72a5d069..045f1e6b 100644 --- a/documentation/MODULES.adoc.jam +++ b/documentation/MODULES.adoc.jam @@ -45,6 +45,11 @@ Input handlers are loaded via the service loader and can load resource files, ma {#define ! _{#replace(detectNoChange=false)/$x/-/_/}=.}{@comment makes chapter doubly define an error} } +=== {category packaging} + +Directory containing different packaging tools to be executed after the release of the Java code version. +These create different packages, like docker image, Debian package, etc. + == Modules {chapter :all:core} This module contains no code. @@ -198,6 +203,10 @@ The code in the directory `jamal-debug-ui` contains the REACT.js based ui for th This is not a module. The code in the directory `jamal-docker` contains a Dockerfile to build a Docker image with Jamal. +{chapter :packaging:packaging} + +Contains the different packaging code in the subdirectories. + {#define module_directory_count={@listDir (maxDepth=1 pattern=jamal-[\w\-]*$ countOnly) ..}} {!#if [not equals={module_directory_count}]/{chapter_counter last}/ diff --git a/documentation/PAROPS.adoc b/documentation/PAROPS.adoc index 58a322d8..eb82497f 100644 --- a/documentation/PAROPS.adoc +++ b/documentation/PAROPS.adoc @@ -119,7 +119,7 @@ As we have seen, the `include` macro with the parops uses the `[` and `]` characters. The built-in core macros use these separator characters. -Currently 13 built-in core macros have parops. +Currently 14 built-in core macros have parops. The class `Scanner` provides the tools to ease parop parsing. diff --git a/documentation/macros/env.adoc b/documentation/macros/env.adoc index 25b6d087..36dd3306 100644 --- a/documentation/macros/env.adoc +++ b/documentation/macros/env.adoc @@ -28,6 +28,27 @@ on the machine where the original `env.adoc.jam` file was converted to ASCIIDOC. If there is a `?` after the name of the variable name, then the macro will result either the `true` or `false`. This can be used to test that an environment variable exists or not. +In principle + +.Jamal source +[source] +---- +{#if|{@env JAVA_HOME}|Java installed|Java not installed} +---- + +also results + +.output +[source] +---- +Java installed +---- + + +as anything that is not empty string or the literal `false` is considered `true` in Jamal. +It may happen that the value of the environment variable is `false` and in that case the code would think it is not defined, instead of defined to be `false`. +Also the value of the environment variable may contain the macro separating character + Testing the value of the environment variable in an `{@if ... }` macro may be misleading when the value is literal `false` or an empty string. Starting with Jamal 1.9.0, it is possible to use `!` after the name of the variable. diff --git a/documentation/macros/env.adoc.jam b/documentation/macros/env.adoc.jam index 3b6691cd..0097a8b1 100644 --- a/documentation/macros/env.adoc.jam +++ b/documentation/macros/env.adoc.jam @@ -18,6 +18,20 @@ on the machine where the original `env.adoc.jam` file was converted to ASCIIDOC. If there is a `?` after the name of the variable name, then the macro will result either the `true` or `false`. This can be used to test that an environment variable exists or not. +In principle + +{%sample/ +{#if|{@env JAVA_HOME}|Java installed|Java not installed} +%} + +also results + +{%output%} + +as anything that is not empty string or the literal `false` is considered `true` in Jamal. +It may happen that the value of the environment variable is `false` and in that case the code would think it is not defined, instead of defined to be `false`. +Also the value of the environment variable may contain the macro separating character + Testing the value of the environment variable in an `{@if ... }` macro may be misleading when the value is literal `false` or an empty string. Starting with Jamal {%RELEASE:OBERGLATT%}, it is possible to use `!` after the name of the variable. diff --git a/documentation/macros/if.adoc b/documentation/macros/if.adoc index 1a12c5d9..cf635063 100644 --- a/documentation/macros/if.adoc +++ b/documentation/macros/if.adoc @@ -34,7 +34,7 @@ When no options are specified the `test` is true, if The literal `false` is false using any combination of upper and lower case letters with or without surrounding spaces. The evaluation of the test string can be modified using options. -There are 11 options. +There are 12 options. The first three options are "boolean" options. @@ -130,6 +130,9 @@ The options test that the string given as the first parameter is a defined user defined macro, a locally defined macro, or a globally defined macro. A macro is locally defined if it was defined in the same scope where the `if` is used. +The option + +* `eval` aliased as `evaluate` will evaluate the test string before assessing it as a boolean value. The following examples show a few cases, as demonstrations: @@ -165,6 +168,7 @@ The following examples show a few cases, as demonstrations: {@if [greaterThan=13 not]/13/true/false}=true 13 is not greater than 13, it is the same logic as the previous {@if [lessThan=13 equals=14]/13/true/false}=false 13 is not less than 13 and does not equal 14 {@if [lessThan=13 and largerThan=2]/12/true/false}=true 12 is in the range (2,13) +{@if [eval]/{a}/true/false}=true {a} is in the range (2,13) as _a_ is defined to be 12 ---- diff --git a/documentation/macros/if.adoc.jam b/documentation/macros/if.adoc.jam index 71a1535e..36bae722 100644 --- a/documentation/macros/if.adoc.jam +++ b/documentation/macros/if.adoc.jam @@ -2,7 +2,8 @@ {%MACRO name="if" since={%RELEASE:ZURICH%}%} -{%@snip:check file=../../jamal-core/src/main/java/javax0/jamal/builtins/If.java hash=9b9c6bfa%} +{%@snip:check file=../../jamal-core/src/main/java/javax0/jamal/builtins/If.java hash=5dfc882d +%} {%@snip:collect from=../../jamal-core/src/main/java%}\ The `if` macro makes it possible to evaluate the content conditionally. @@ -30,7 +31,7 @@ When no options are specified the `test` is true, if The literal `false` is false using any combination of upper and lower case letters with or without surrounding spaces. The evaluation of the test string can be modified using options. -There are {%@define nrIfOptions=11%} {%nrIfOptions%} options. +There are {%@def nrIfOptions=12%} options. {%#snip:check id=if_options lines={%nrIfOptions%} %} The first three options are "boolean" options. @@ -112,6 +113,9 @@ The options test that the string given as the first parameter is a defined user defined macro, a locally defined macro, or a globally defined macro. A macro is locally defined if it was defined in the same scope where the `if` is used. +The option + +* `eval` aliased as `evaluate` will evaluate the test string before assessing it as a boolean value. The following examples show a few cases, as demonstrations: @@ -147,6 +151,7 @@ they are expected, that way this sample is also a simple integration test of the {s/{`@if [greaterThan=13 not]/13/true/false}/true} 13 is not greater than 13, it is the same logic as the previous {s/{`@if [lessThan=13 equals=14]/13/true/false}/false} 13 is not less than 13 and does not equal 14 {s/{`@if [lessThan=13 and largerThan=2]/12/true/false}/true} 12 is in the range (2,13) +{s/{@define a=12}{`@if [eval]/{a}/true/false}/true} {}a} is in the range (2,13) as _a_ is defined to be 12 %}%} {%output%} diff --git a/jamal-api/src/main/java/javax0/jamal/api/ServiceLoaded.java b/jamal-api/src/main/java/javax0/jamal/api/ServiceLoaded.java index ef0cec0b..4ba49edb 100644 --- a/jamal-api/src/main/java/javax0/jamal/api/ServiceLoaded.java +++ b/jamal-api/src/main/java/javax0/jamal/api/ServiceLoaded.java @@ -35,7 +35,7 @@ public interface ServiceLoaded { */ static List getInstances(Class klass) { final var services = getInstances(klass, Thread.currentThread().getContextClassLoader()); - if( services.size() > 0 ){ + if(!services.isEmpty()){ return services; } return getInstances(klass, ServiceLoaded.class.getClassLoader()); @@ -46,7 +46,7 @@ static List getInstances(Class klass, final ClassLoader cl) { try { final ServiceLoader services = ServiceLoader.load(klass, cl); services.iterator().forEachRemaining(list::add); - if (list.size() == 0) { + if (list.isEmpty()) { loadViaMetaInf(klass, list, cl); } return list; diff --git a/jamal-asciidoc/pom.xml b/jamal-asciidoc/pom.xml index 82fbb26b..92af2bab 100644 --- a/jamal-asciidoc/pom.xml +++ b/jamal-asciidoc/pom.xml @@ -162,7 +162,7 @@ org.mockito mockito-core - 5.11.0 + 5.12.0 io.github.markdown-asciidoc diff --git a/jamal-cmd/README.adoc b/jamal-cmd/README.adoc index 97c2254f..eb9b0c2c 100644 --- a/jamal-cmd/README.adoc +++ b/jamal-cmd/README.adoc @@ -8,7 +8,7 @@ With the command line version of Jamal you can convert files processing the Jamal meta markup. You can process a single file or multiple files traversing in a directory structure. -To convert a single file you simply have to specify the input and the output file: +To convert a single file, you simply have to specify the input and the output file: .Jamal source [source] diff --git a/jamal-cmd/README.adoc.jam b/jamal-cmd/README.adoc.jam index 42eaeb14..70b218d9 100644 --- a/jamal-cmd/README.adoc.jam +++ b/jamal-cmd/README.adoc.jam @@ -15,7 +15,7 @@ on the help screen and also no option is documented that does not exist With the command line version of Jamal you can convert files processing the Jamal meta markup. You can process a single file or multiple files traversing in a directory structure. -To convert a single file you simply have to specify the input and the output file: +To convert a single file, you simply have to specify the input and the output file: {%sample/ jamal input output %} diff --git a/jamal-cmd/pom.jam b/jamal-cmd/pom.jam index 14658c20..238b233a 100644 --- a/jamal-cmd/pom.jam +++ b/jamal-cmd/pom.jam @@ -1,6 +1,8 @@ {@import https://raw.githubusercontent.com/central7/pom/1/pom.jim} {@import ../version.jim} {@import ../modules.jim} +{@define descriptors(x)=x} +{@define descriptor(x)=x} {project jamal command line} {packaging jar} {GAV ::jamal-cmd:{VERSION}} @@ -9,26 +11,13 @@ {build| {plugins| {plugin| - {GAV :org.apache.maven.plugins:maven-assembly-plugin:{MAVEN_ASSEMBLY_PLUGIN_VERSION}} - {configuration> - - - javax0.jamal.cmd.JamalMain - - - - jar-with-dependencies - - } - - - make-assembly - package - - single - - - + {GAV :org.apache.maven.plugins:maven-assembly-plugin:{MAVEN_ASSEMBLY_PLUGIN_VERSION}} + {executions|{execution| + {id dist} + {phase package} + {goals|{goal single}} + {configuration|{descriptors {descriptor src/main/assembly/bin.xml}}} + }} } {plugin|{GAV :org.apache.maven.plugins:maven-surefire-plugin:} {configuration| @@ -37,24 +26,6 @@ }} {plugin|{GAV :org.apache.maven.plugins:maven-source-plugin:}} {plugin|{GAV :org.apache.maven.plugins:maven-javadoc-plugin:}} - {plugin|{GAV :org.codehaus.mojo:appassembler-maven-plugin:{APPASSEMBLER_PLUGIN_VERSION}} - - - package - - assemble - - - - - - - javax0.jamal.cmd.JamalMain - jamal - - - - } {plugin|{GAV :org.apache.maven.plugins:maven-dependency-plugin:} {configuration| ./target/dependencies.txt diff --git a/jamal-cmd/pom.xml b/jamal-cmd/pom.xml index 25a7ca30..745ae3bd 100644 --- a/jamal-cmd/pom.xml +++ b/jamal-cmd/pom.xml @@ -17,25 +17,18 @@ org.apache.maven.plugins maven-assembly-plugin 3.7.1 - - - - javax0.jamal.cmd.JamalMain - - - - jar-with-dependencies - - - make-assembly - + dist package - single + + + src/main/assembly/bin.xml + + @@ -56,27 +49,6 @@ org.apache.maven.plugins maven-javadoc-plugin - - org.codehaus.mojo - appassembler-maven-plugin - 2.1.0 - - - package - - assemble - - - - - - - javax0.jamal.cmd.JamalMain - jamal - - - - org.apache.maven.plugins maven-dependency-plugin @@ -144,6 +116,10 @@ com.javax0.jamal jamal-sql + + com.javax0.jamal + jamal-xls + com.javax0.jamal jamal-maven-input diff --git a/jamal-cmd/src/main/assembly/bin.xml b/jamal-cmd/src/main/assembly/bin.xml new file mode 100644 index 00000000..2b017e6f --- /dev/null +++ b/jamal-cmd/src/main/assembly/bin.xml @@ -0,0 +1,25 @@ + + distribution + + zip + + false + + + / + false + + ${artifact} + + + + / + false + + ${artifact} + + + + \ No newline at end of file diff --git a/jamal-core/src/main/java/javax0/jamal/builtins/Env.java b/jamal-core/src/main/java/javax0/jamal/builtins/Env.java index c99da9ef..986ee550 100644 --- a/jamal-core/src/main/java/javax0/jamal/builtins/Env.java +++ b/jamal-core/src/main/java/javax0/jamal/builtins/Env.java @@ -1,10 +1,8 @@ package javax0.jamal.builtins; -import javax0.jamal.api.BadSyntax; -import javax0.jamal.api.EnvironmentVariables; -import javax0.jamal.api.Input; import javax0.jamal.api.Macro; -import javax0.jamal.api.Processor; +import javax0.jamal.api.*; +import javax0.jamal.tools.Scanner; import static javax0.jamal.api.SpecialCharacters.QUERY; import static javax0.jamal.api.SpecialCharacters.REPORT_ERRMES; @@ -34,24 +32,28 @@ * or {@code !} character then you cannot use this macro, and you are in trouble. However, in that case you are a sick * bastard, and you are in trouble anyway. */ -public class Env implements Macro { +public class Env implements Macro, Scanner.Core { + public enum Mode { + normal, query, report + } + @Override public String evaluate(Input in, Processor processor) throws BadSyntax { - final var arg = in.toString().trim(); - BadSyntax.when(arg.length() == 0, "Empty string as environment variable name"); - final var test = arg.charAt(arg.length() - 1) == QUERY; - final var report = arg.charAt(arg.length() - 1) == REPORT_ERRMES; - final String name; - if (test || report) { - name = arg.substring(0, arg.length() - 1).trim(); - } else { - name = arg; - } + final var scanner = newScanner(in, processor); + final var mode = scanner.enumeration(Mode.class).defaultValue(Mode.normal); + scanner.done(); + final var variableName = in.toString().trim(); + BadSyntax.when(variableName.isEmpty(), "Empty string as environment variable name"); + final var lastChar = variableName.charAt(variableName.length() - 1); + final var testChar = lastChar == QUERY; + final var reportChar = lastChar == REPORT_ERRMES; + final var name = (testChar || reportChar) ? variableName.substring(0, variableName.length() - 1).trim() : variableName; + final var value = EnvironmentVariables.getenv(name).orElse(null); - if (test) { + if (testChar || mode.get(Mode.class) == Mode.query) { return "" + (null != value); } else { - BadSyntax.when(report && null == value, "Environment variable '%s' is not defined", name); + BadSyntax.when((mode.get(Mode.class)== Mode.report || reportChar) && null == value, "Environment variable '%s' is not defined", name); return null == value ? "" : value; } } diff --git a/jamal-core/src/main/java/javax0/jamal/builtins/If.java b/jamal-core/src/main/java/javax0/jamal/builtins/If.java index 7840f1ad..085dee93 100644 --- a/jamal-core/src/main/java/javax0/jamal/builtins/If.java +++ b/jamal-core/src/main/java/javax0/jamal/builtins/If.java @@ -41,6 +41,7 @@ private static class Options { final BooleanParameter isDefined; final BooleanParameter isGlobal; final BooleanParameter isLocal; + final BooleanParameter eval; final ListParameter lessThan; final ListParameter greaterThan; final ListParameter equals; @@ -57,6 +58,7 @@ private Options(ScannerObject scnr) { isDefined = scanner.bool("isDefined", "defined"); isGlobal = scanner.bool("isGlobal", "global"); isLocal = scanner.bool("isLocal", "local"); + eval = scanner.bool("eval", "evaluate"); lessThan = scanner.list("lessThan", "less", "smaller", "smallerThan"); greaterThan = scanner.list("greaterThan", "greater", "bigger", "biggerThan", "larger", "largerThan"); equals = scanner.list("equals", "equal", "equalsTo", "equalTo"); @@ -158,8 +160,14 @@ private static boolean eq(final String a, final String b) { } private static boolean isTrue(final Processor processor, - final String test, + final String testO, final Options opt) throws BadSyntax { + final String test; + if( opt.eval.is()){ + test = processor.process(testO); + }else{ + test = testO; + } if (opt.countNumOptionsPresent() > 0) { if (opt.and.is()) { return (!opt.lessThan.isPresent() || compare(opt.lessThan.get(), true, n -> lt(n, test))) diff --git a/jamal-core/src/main/java/javax0/jamal/builtins/Use.java b/jamal-core/src/main/java/javax0/jamal/builtins/Use.java index 962e4e9f..b7d8fb10 100644 --- a/jamal-core/src/main/java/javax0/jamal/builtins/Use.java +++ b/jamal-core/src/main/java/javax0/jamal/builtins/Use.java @@ -1,10 +1,9 @@ package javax0.jamal.builtins; -import javax0.jamal.api.BadSyntax; -import javax0.jamal.api.Input; import javax0.jamal.api.Macro; -import javax0.jamal.api.Processor; +import javax0.jamal.api.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; /** @@ -14,79 +13,182 @@ * class {@code com.my.class} as a macro implementation. The class has to implement the {@link Macro} interface. In case * it is defined as {@code global} then it will get into the global level, otherwise to the local level. *

- * If the class name does not contain any dot character then it is assument that this is not a class name, rather an + * If the class name does not contain any dot character, then it is assumed that this is not a class name, rather an * already loaded macro name. In that case the {@code as alias} is not optional. + *

+ * The syntax is + * + *

{@code
+ *  use [global] com.package.name.MacroClass [as Alias]
+ * }
+ *

+ * or + * + *

{@code
+ *  use [global] macroname as Alias
+ * }
+ *

+ * There can be many such declarations on the macro input separated by commas. */ public class Use implements Macro { - // The syntax is: [global] com.package.name.MacroClass [as Alias] - // $1 will be "global" or "" - // $2 will be the fully qualified name of the class, or the name of the macro if there is no . in the name - // $3 will be the alias or null if no alias + /** + * The syntax is: {@code [global] com.package.name.MacroClass [as Alias]} + *

    + *
  • {@code $1} will be "global" or "" + *
  • {@code $2} will be the fully qualified name of the class, or the name of the macro if there is no . in the name + *
  • {@code $3} will be the alias or null if no alias + *
+ */ private static final Pattern pattern = Pattern.compile("((?:global\\s+)?)([\\w\\d:.]+)(?:\\s+as\\s+([\\w][\\w\\d:]*))?"); @Override public String evaluate(Input input, Processor processor) throws BadSyntax { final var macroImports = input.toString().split(","); for (final var macroImport : macroImports) { - final var stripped = macroImport - .trim() - .replace("\n", " ") - .replace("\r", " ") - .replace("\t", " ") - .replaceAll("\\s+", " "); - if (stripped.length() > 0) { - var matcher = pattern.matcher(stripped); - BadSyntax.when(!matcher.matches(), "use macro has bad syntax '%s'", stripped); - var isGlobal = matcher.group(1).length() > 0; - final var klassName = matcher.group(2); - final var alias = matcher.group(3); - final Macro macro; - final var register = processor.getRegister(); - if (klassName.contains(".")) { - macro = forName(klassName); - } else { - if (klassName.contains(":")) { - isGlobal = true; - } - macro = register.getMacro(klassName) - .orElseThrow(() -> new BadSyntax("There is no built-in macro with the name '" + klassName + "'")); - BadSyntax.when(alias == null || alias.length() == 0, () -> String.format("You cannot define an alias for the macro '%s' without actually providing an alias after the 'as'", - klassName)); - } - if (isGlobal) { - if (alias != null && alias.length() > 0) { - register.global(macro, alias); - } else { - register.global(macro); - } - } else { - if (alias != null && alias.length() > 0) { - register.define(macro, alias); - } else { - register.define(macro); - } - } + final var stripped = getTrimmed(macroImport); + if (!stripped.isEmpty()) { + evaluateSingleUseDeclaration(processor, stripped); } } return ""; } + /** + * Remove all white space characters from the string and replace them with a single space character. + * The final string will not have any other white space character than normal space and there will be no sequence of + * consecutive space characters. + * + * @param useDeclaration the string to be trimmed + * @return the trimmed string + */ + private static String getTrimmed(String useDeclaration) { + return useDeclaration + .replace("\n", " ") + .replace("\r", " ") + .replace("\t", " ") + .replaceAll("\\s+", " ") + .trim(); + } + + /** + * Evaluate a single use declaration. + * + * @param processor the processor + * @param stripped the stripped declaration, no new lines in it and only one declaration + * @throws BadSyntax if the declaration is not correct, or the actual macro does not exist or the class cannot be + * loaded, instantiated or the class is not a macro + */ + private void evaluateSingleUseDeclaration(Processor processor, String stripped) throws BadSyntax { + var matcher = pattern.matcher(stripped); + BadSyntax.when(!matcher.matches(), "use macro has bad syntax '%s'", stripped); + final var isGlobal = new AtomicBoolean(!matcher.group(1).isEmpty()); + final var klassName = matcher.group(2); + final var alias = matcher.group(3); + final var register = processor.getRegister(); + final Macro macro = getMacro(klassName, isGlobal, register, alias); + registerMacro(macro, register, isGlobal.get(), alias); + } + + /** + * Get the macro instance from the register or create a new instance if the macro is given by the name of the class. + *

+ * The method has two side effects. It sets the {@code isGlobal} to {@code true} if the macro is global, and it throws + * an exception if the macro is provided by the name of the macro and the alias is not provided. + * + * @param klassName the name of the class or the name of the macro. + * It is assumed that it is a class name if it contains a dot. + * @param isGlobal if the macro is global. If the name of the macro is provided, and it contains a colon {@code :}, + * then the macro is global and in that case this variable is also set to be {@code true}. + * @param register the register that holds all the macros + * @param alias the alias of the macro. It is only used in error checking to throw an exception if the alias is + * null or empty when the macro is provided by the name of the macro. In that case, an alias is mandatory. + * There is no point to registering a macro with a name it is already registered with. + * @return the macro instance that is either created or found in the register + * @throws BadSyntax if the macro is specified + *

    + *
  • by a class name, but + *
      + *
    • the class does not exist, or + *
    • does not have a public no-argument constructor, + *
    • the class is not an instance of {@link Macro}, or + *
    + *
  • by the name + *
      + *
    • but there is no macro registered by that name, or + *
    • there is no alias specified in the declaration. + *
    + *
+ */ + private Macro getMacro(String klassName, AtomicBoolean isGlobal, MacroRegister register, String alias) throws BadSyntax { + final Macro macro; + if (klassName.contains(".")) { + macro = forName(klassName); + } else { + if (klassName.contains(":")) { + isGlobal.set(true); + } + macro = register.getMacro(klassName) + .orElseThrow(() -> new BadSyntax("There is no built-in macro with the name '" + klassName + "'")); + BadSyntax.when(alias == null || alias.isEmpty(), () -> String.format("You cannot define an alias for the macro '%s' without actually providing an alias after the 'as'", + klassName)); + } + return macro; + } + + /** + * Register the macro in the register. + * + * @param macro the macro to register + * @param register the register that holds all the macros + * @param isGlobal if the macro is global + * @param alias the alias of the macro. Ignored if {@code null} or empty + */ + private static void registerMacro(Macro macro, MacroRegister register, boolean isGlobal, String alias) { + if (isGlobal) { + if (alias != null && !alias.isEmpty()) { + register.global(macro, alias); + } else { + register.global(macro); + } + } else { + if (alias != null && !alias.isEmpty()) { + register.define(macro, alias); + } else { + register.define(macro); + } + } + } + + /** + * Get a new instance of the built-in macro. + * + * @param klassName the name of the class with dots as separator that implements the {@link Macro} interface + * @return a new instance of the macro class + * @throws BadSyntax if the class cannot be found or the instance cannot be created or the class is not a macro + */ private Macro forName(final String klassName) throws BadSyntax { return forName(klassName, klassName); } /** - * Get a new instance of the built-in macro. The implementation loads the class first. If the class is an static - * inner class then it recognizes that and loads the inner class. + * Get a new instance of the built-in macro. + *

+ * The implementation tries to load the class first. + * If the class is a static inner class, this attempt fails. + * In that case, the method changes the last `.` to `$` and tires again so long as long there is a `.` in the + * class name. + *

+ * The class can be a top level class or a static inner class. + * The class has to implement the {@link Macro} interface and should have a public constructor that takes no arguments. * * @param klassName the name of the class with dots as separator. Using {@code $} in case of inner classes is * also possible, but it is not the intended use. In case of an inner class the algorithm tries - * to load the class as a top level class and in case it fails it replaces the last dot with a + * to load the class as a top level class, and in case it fails it replaces the last dot with a * {@code $} sign and tries to load again and again until all dots are replaced. * @param originalName is the original class name before the search process started to replace the {@code .} to * {@code $}. Used only to create the exception and needed for the recursive calls. * @return the instance of the macro class - * @throws BadSyntax if the class cannot be found or the instance cannot be created + * @throws BadSyntax if the class cannot be found or the instance cannot be created or the class is not a macro */ private Macro forName(final String klassName, final String originalName) throws BadSyntax { final Macro macro; diff --git a/jamal-maven-extension/README.adoc b/jamal-maven-extension/README.adoc index aa91bb92..7d30c052 100644 --- a/jamal-maven-extension/README.adoc +++ b/jamal-maven-extension/README.adoc @@ -273,6 +273,11 @@ The library provides macros that can load Java Built-in Macros from Maven artifa For further information read the link:../jamal-sql/README.adoc[documentation] of the module. +=== `xls` + +For further information read the link:../jamal-xls/README.adoc[documentation] of the module. + + diff --git a/jamal-maven-extension/README.adoc.jam b/jamal-maven-extension/README.adoc.jam index 2f1dfdaa..2c29045f 100644 --- a/jamal-maven-extension/README.adoc.jam +++ b/jamal-maven-extension/README.adoc.jam @@ -299,6 +299,11 @@ The library provides macros that can load Java Built-in Macros from Maven artifa {%further sql%} +=== {%#block {%depCounter%}%} `xls` + +{%further xls%} + + {%#assert:equals /{%dependencies%}/{%depCounter last%}/Not all dependencies are documented or some documented dependency is not there any more%} diff --git a/jamal-maven-extension/pom.xml b/jamal-maven-extension/pom.xml index c6312e79..3b5e2503 100644 --- a/jamal-maven-extension/pom.xml +++ b/jamal-maven-extension/pom.xml @@ -113,6 +113,11 @@ jamal-sql 2.6.1-SNAPSHOT + + com.javax0.jamal + jamal-xls + 2.6.1-SNAPSHOT + com.javax0.jamal jamal-maven-input @@ -134,7 +139,7 @@ org.apache.maven maven-core - 4.0.0-alpha-13 + 4.0.0-beta-3 provided diff --git a/jamal-mock/README.adoc b/jamal-mock/README.adoc index 8b7dce9a..85a6b4e4 100644 --- a/jamal-mock/README.adoc +++ b/jamal-mock/README.adoc @@ -24,7 +24,7 @@ To use this module, you have to add the dependency to your Maven project, as: com.javax0.jamal jamal-mock - 2.5.0-SNAPSHOT + 2.6.1-SNAPSHOT ---- @@ -49,11 +49,13 @@ You have to define the name of the macro you want to mock and the output. [title=simple mock] ==== +.Jamal source [source] ---- {@mock (macro=w)ajaja}{@w} ---- outputs +.output [source] ---- ajaja @@ -69,11 +71,13 @@ The mocks are only available on the current scope. [title=mock locality] ==== +.Jamal source [source] ---- {#ident {@mock (macro=comment)comment is overridden}{@comment}}{@comment is not overridden here} ---- outputs +.output [source] ---- comment is overridden @@ -89,11 +93,13 @@ These responses will be used in the other they are defined. [title=repeated mock] ==== +.Jamal source [source] ---- {@mock (macro=ww)1}{@mock (macro=ww)2}{@ww}{@ww} ---- outputs +.output [source] ---- 12 @@ -108,11 +114,13 @@ If you use a mock more times it has defined values it will result syntax error. [title=mock exhausted] ==== +.Jamal source [source] ---- {@try! {@mock (macro=z)1}{@z}{@z}} ---- outputs +.output [source] ---- Mock z has exhausted after 1 uses. @@ -127,6 +135,7 @@ It will simply use the original macro. [title=mock exhausted] ==== +.Jamal source [source] ---- {@mock (macro=ident)exhausted}\ @@ -134,6 +143,7 @@ It will simply use the original macro. {@ident whatever is here, mocked} {@ident second time also mocked} {@ident are not mocked anymore} ---- outputs +.output [source] ---- exhausted macros are not mocked anymore @@ -148,11 +158,13 @@ If you want to use a mock multiple times resulting the same value you can use th [title=mock repeated] ==== +.Jamal source [source] ---- {@mock (macro=w repeat=2)A}{@w}{@w} ---- outputs +.output [source] ---- AA @@ -167,11 +179,13 @@ In addition to the repeat option it is also possible to use the `infinite` optio [title=mock infinite] ==== +.Jamal source [source] ---- {@mock (macro=w infinite)X}{@w}{@w}{@w}{@w}{@w}{@w}{@w}{@w}{@w} ---- outputs +.output [source] ---- XXXXXXXXX @@ -188,6 +202,7 @@ If there are more than one mock that would match the input then the one first de [title=mock infinite] ==== +.Jamal source [source] ---- {@mock (macro=q when=".*bee.*")bee}{@comment defined first, but used second} @@ -196,6 +211,7 @@ If there are more than one mock that would match the input then the one first de {@q there is a bee} ---- outputs +.output [source] ---- apple @@ -256,6 +272,7 @@ After that the macro `comment` is mocked again, as the mocking of `mock` is exha [title=Mocking `mock`] ==== +.Jamal source [source] ---- {@mock (macro=mock)mock the mock once}{@mock (macro=ident)}{@mock (macro=comment)this is a comment} @@ -263,6 +280,7 @@ After that the macro `comment` is mocked again, as the mocking of `mock` is exha {#comment mocked, does not matter what I write here} ---- outputs +.output [source] ---- mock the mock once diff --git a/jamal-packaging/debian/Dockerfile b/jamal-packaging/debian/Dockerfile new file mode 100644 index 00000000..932a4221 --- /dev/null +++ b/jamal-packaging/debian/Dockerfile @@ -0,0 +1,15 @@ +# Dockerfile +FROM debian:latest + +RUN apt-get update && \ + apt-get install -y \ + wget \ + openjdk-17-jdk \ + dpkg-dev \ + dh-make \ + debhelper \ + devscripts + +WORKDIR /build + +ENTRYPOINT ["/build/build_deb.sh"] diff --git a/jamal-packaging/debian/build.sh b/jamal-packaging/debian/build.sh new file mode 100755 index 00000000..a91d8913 --- /dev/null +++ b/jamal-packaging/debian/build.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -e + + +# Variables +PACKAGE_NAME="jamal" +VERSION=$(grep /version ../../pom.xml |head -1| sed "s/ *<[/]*version> *//g") +REPO=build/repo +ARCHITECTURE="all" +MAINTAINER="Peter Verhas " +DESCRIPTION="Jamal - Meta-Markup language" +DEPENDENCIES="openjdk-17-jdk" +MAIN_CLASS=javax0.jamal.cmd.JamalMain + +rm -rf build +mkdir build + +# Create directories +mkdir -p ${REPO}/DEBIAN +mkdir -p ${REPO}/usr/local/bin + +# Create the control file +cat < "${REPO}/DEBIAN/control" +Package: ${PACKAGE_NAME} +Version: ${VERSION} +Architecture: ${ARCHITECTURE} +Maintainer: ${MAINTAINER} +Depends: ${DEPENDENCIES} +Description: ${DESCRIPTION} +EOL + + +# Copy the JAR files +rm -rf ${REPO}/usr/local/bin/jamal.d +mkdir ${REPO}/usr/local/bin/jamal.d +unzip -d ${REPO}/usr/local/bin/jamal.d "../../jamal-cmd/target/jamal-cmd-${VERSION}-distribution.zip" +cd ${REPO} +CLASSPATH=$(find usr/local/bin/jamal.d -type f | sed 's#^#/#' | paste -sd ':' -) +cd .. +cd .. + +cat < "${REPO}/usr/local/bin/jamal" +#!/bin/bash +exec java -cp ${CLASSPATH} ${MAIN_CLASS} "\$@" +EOL + +# Make the script executable +chmod +x ${REPO}/usr/local/bin/jamal + +cat < build/build_deb.sh +#!/bin/bash +# Build the package +dpkg-deb --build repo + +# Run lintian to check for common issues +lintian ${PACKAGE_NAME}-${VERSION}.deb + +# Move the package to the output directory +mv ${PACKAGE_NAME}-${VERSION}.deb /build +EOL + +chmod +x build/build_deb.sh + + +# create the container to build the package +docker build -t jamal-builder:latest . +docker run -d -v "$(pwd)/build":/build jamal-builder:latest + diff --git a/jamal-packaging/docker/Dockerfile b/jamal-packaging/docker/Dockerfile new file mode 100644 index 00000000..f985312c --- /dev/null +++ b/jamal-packaging/docker/Dockerfile @@ -0,0 +1,18 @@ +# Dockerfile +FROM debian:latest + +RUN apt-get update +RUN apt-get install -y openjdk-17-jdk + +RUN mkdir -p /build +RUN mkdir -p /lib + +WORKDIR /workspace + +# Download the JAR file and other necessary files +ARG VERSION +COPY build/lib/* /build/lib/ +COPY jamal /build/jamal +RUN chmod +x /build/jamal + +ENTRYPOINT ["/build/jamal"] diff --git a/jamal-packaging/docker/README.adoc b/jamal-packaging/docker/README.adoc new file mode 100644 index 00000000..c4e89302 --- /dev/null +++ b/jamal-packaging/docker/README.adoc @@ -0,0 +1,48 @@ += Jamal Dockerization +This directory contains the source code to create and publish Jamal to Docker Hub. +To run the build of the container, you need the files + +* `build.sh`, and +* `Dockerfile`. + +The Dockerfile is used to build the container, and the build.sh script is used to run the build. + +== Running the Build + +To run the build, you need the whole project directory, not only the subproject. +The container building will copy and fetch files from different parts of the project. +The project also has to be built with the command: + + mvn clean package + +so that the zip file containing all the classpath dependencies for the command-line version of Jamal are available. +The build script explicitly depends on the root project `pom.xm` file. +It uses the pom.xml to retrieve the version of Jamal to build the container with. + +The build script extracts the JAR files into the `build/lib` directory. +After that, it creates two shell scripts. + +* `jamal` is the command-line version of Jamal for the container +* `jamal_local` is the command-line version of Jamal for local use + +The difference is that the classpath definition in + +* the local version uses `build/lib` as directory name relative to the current directory, while +* in the container version, it uses `build/lib` as the directory name. + +During the execution the file `test.adoc.jam` is converted to `test1.adoc` and compared with the existing `test.adoc` file. +If the two files are different, the build aborts. +If they are the same, the build continues and creates the container. + +The container name is `jamal` and the tag is the version of Jamal. +When the container is ready, the test is executed again, but this time using the container version of Jamal. + +The final step is to display how to push the container to Docker Hub. + +== Using the docker version of Jamal + +To execute Jamal from the container, you should execut ethe command + + docker run --rm -v $(pwd):/workspace verhas/jamal:2.6.1-SNAPSHOT jamal_command_line_arguments + +to work on files that are in the current working directory. \ No newline at end of file diff --git a/jamal-packaging/docker/README.adoc.jam b/jamal-packaging/docker/README.adoc.jam new file mode 100644 index 00000000..9d422c76 --- /dev/null +++ b/jamal-packaging/docker/README.adoc.jam @@ -0,0 +1,52 @@ += Jamal Dockerization +{%@snip:xml pom=../../pom.xml%}\ +{%#define VERSION={%pom /project/version/text()%}%}\ +{%@define fileFormat=`$name`%}\ +{%@define file(x)=`x`%}\ +This directory contains the source code to create and publish Jamal to Docker Hub. +To run the build of the container, you need the files + +* {%@file build.sh%}, and +* {%@file Dockerfile%}. + +The Dockerfile is used to build the container, and the build.sh script is used to run the build. + +== Running the Build + +To run the build, you need the whole project directory, not only the subproject. +The container building will copy and fetch files from different parts of the project. +The project also has to be built with the command: + + mvn clean package + +so that the zip file containing all the classpath dependencies for the command-line version of Jamal are available. +The build script explicitly depends on the root project {%file pom.xm%} file. +It uses the pom.xml to retrieve the version of Jamal to build the container with. + +The build script extracts the JAR files into the {%file build/lib%} directory. +After that, it creates two shell scripts. + +* {%file jamal%} is the command-line version of Jamal for the container +* {%file jamal_local%} is the command-line version of Jamal for local use + +The difference is that the classpath definition in + +* the local version uses {%file build/lib%} as directory name relative to the current directory, while +* in the container version, it uses {%file /build/lib%} as the directory name. + +During the execution the file {%@file test.adoc.jam%} is converted to {%file test1.adoc%} and compared with the existing {%@file test.adoc%} file. +If the two files are different, the build aborts. +If they are the same, the build continues and creates the container. + +The container name is `jamal` and the tag is the version of Jamal. +When the container is ready, the test is executed again, but this time using the container version of Jamal. + +The final step is to display how to push the container to Docker Hub. + +== Using the docker version of Jamal + +To execute Jamal from the container, you should execut ethe command + + docker run --rm -v $(pwd):/workspace verhas/jamal:{%VERSION%} jamal_command_line_arguments + +to work on files that are in the current working directory. \ No newline at end of file diff --git a/jamal-packaging/docker/build.sh b/jamal-packaging/docker/build.sh new file mode 100755 index 00000000..018f1eec --- /dev/null +++ b/jamal-packaging/docker/build.sh @@ -0,0 +1,62 @@ +#! /usr/bin/env bash + +VERSION=$(grep /version ../../pom.xml |head -1| sed "s/ *<[/]*version> *//g") + +rm -rf build +rm -rf build/lib +mkdir build +mkdir build/lib +cd build/lib || exit +unzip "../../../../jamal-cmd/target/jamal-cmd-${VERSION}-distribution.zip" +cd .. +cd .. +CLASSPATH=$(find build/lib -type f | sed 's#^#/#' | paste -sd ':' -) +cat < jamal +#! /usr/bin/env bash +java -cp $CLASSPATH javax0.jamal.cmd.JamalMain \$@ +EOL +chmod u+x jamal + +CLASSPATH=$(find build/lib -type f | paste -sd ':' -) +cat < jamal_local +#! /usr/bin/env bash +java -cp $CLASSPATH javax0.jamal.cmd.JamalMain \$@ +EOL +chmod u+x jamal_local +echo "Running Jamal on test file locally" +./jamal_local test.adoc.jam test1.adoc + +if ! cmp -s "test.adoc" "test1.adoc"; then + echo "Files are different. Exiting." + exit 1 +else + echo "Files are the same running Jamal local. Continuing." +fi +rm test1.adoc + +docker build --build-arg VERSION="$VERSION" -t jamal:"$VERSION" . + +echo "Running Jamal on test file from Docker image" +docker run --rm -v .:/workspace -p 5005:5005 jamal:"$VERSION" test.adoc.jam test1.adoc + +if ! cmp -s "test.adoc" "test1.adoc"; then + echo "Files are different. Exiting." + exit 1 +else + cat < @@ -69,13 +70,13 @@ org.junit.jupiter junit-jupiter-api - 5.11.0-M1 + 5.11.0-M2 test org.junit.jupiter junit-jupiter-engine - 5.11.0-M1 + 5.11.0-M2 test diff --git a/jamal-snippet/README.adoc b/jamal-snippet/README.adoc index fd95a46b..1e4057e7 100644 --- a/jamal-snippet/README.adoc +++ b/jamal-snippet/README.adoc @@ -3816,7 +3816,7 @@ will result in the output .output [source] ---- -2024-06-03 15:53:36 +2024-06-14 12:42:03 ---- diff --git a/jamal-sql/demodb.mv.db b/jamal-sql/demodb.mv.db index f3249b17..1c260b12 100644 Binary files a/jamal-sql/demodb.mv.db and b/jamal-sql/demodb.mv.db differ diff --git a/jamal-test/IT_DOCKER/README.adoc b/jamal-test/IT_DOCKER/README.adoc index 214c4e4f..14d3a1a2 100644 --- a/jamal-test/IT_DOCKER/README.adoc +++ b/jamal-test/IT_DOCKER/README.adoc @@ -72,4 +72,4 @@ The test sets the file permissions to the directory and the configuration file i ### Command Line execution -The test runs the command line tool `jamal` with different parameters. \ No newline at end of file +The test runs the command line tool `jamal` with different parameters. diff --git a/jamal-test/IT_DOCKER/integrationtest b/jamal-test/IT_DOCKER/integrationtest index 71efb4a6..64c7537d 100644 --- a/jamal-test/IT_DOCKER/integrationtest +++ b/jamal-test/IT_DOCKER/integrationtest @@ -181,4 +181,4 @@ if [ "$(diff -b test_scriptbasic.txt test_scriptbasic.test)" ] ;then diff -b test_scriptbasic.txt test_scriptbasic.test exit 1 fi -rm test_scriptbasic.test \ No newline at end of file +rm test_scriptbasic.test diff --git a/jamal-test/IT_DOCKER/test.sh b/jamal-test/IT_DOCKER/test.sh index a493535d..c34353db 100755 --- a/jamal-test/IT_DOCKER/test.sh +++ b/jamal-test/IT_DOCKER/test.sh @@ -4,4 +4,8 @@ chmod 0700 ../../jamal.sh ../../jamal.sh integrationtest.jam integrationtest ../../jamal.sh README.adoc.jam README.adoc docker build -t jamal-test . +if [ $? -ne 0 ]; then + echo "Docker build failed. Exiting." + exit 1 +fi docker run -it jamal-test \ No newline at end of file diff --git a/jamal-test/src/test/java/javax0/jamal/test/core/TestEnv.java b/jamal-test/src/test/java/javax0/jamal/test/core/TestEnv.java index a7ea9f63..bfd4ffda 100644 --- a/jamal-test/src/test/java/javax0/jamal/test/core/TestEnv.java +++ b/jamal-test/src/test/java/javax0/jamal/test/core/TestEnv.java @@ -34,6 +34,28 @@ void testEnv() throws Exception { ); } + @Test + @DisplayName("test that env returns using parops the ... whatever it returns. we assume there is a JAVA_HOME and there is no CICA_HOME") + void testEnvParop() throws Exception { + final var javaHome = EnvironmentVariables.getenv("JAVA_HOME").orElse(null); + final var cicaHome = EnvironmentVariables.getenv("CICA_HOME").orElse(null); + Assertions.assertNull(cicaHome); + + TestThat.theInput( + "{@env JAVA_HOME}\n" + + "{@env [report]JAVA_HOME}\n" + + "JAVA_HOME {#if /{@env [query]JAVA_HOME}/is defined/is not defined}\n" + + "{@env CICA_HOME}\n" + + "CICA_HOME {#if /{@env [query]CICA_HOME}/is defined/is not defined}\n" + ).ignoreLineEnding().results( + javaHome + "\n" + + javaHome + "\n" + + "JAVA_HOME is defined\n" + + "\n" + + "CICA_HOME is not defined\n" + ); + } + @Test @DisplayName("test that env throws up when the variable is not defined and we use a ! after the name CICADA_HOME") void testEnvThrow() throws Exception { diff --git a/jamal-test/src/test/resources/javax0/jamal/test/core/TestIf.jyt b/jamal-test/src/test/resources/javax0/jamal/test/core/TestIf.jyt index 98972ab3..c18d69a3 100644 --- a/jamal-test/src/test/resources/javax0/jamal/test/core/TestIf.jyt +++ b/jamal-test/src/test/resources/javax0/jamal/test/core/TestIf.jyt @@ -2,6 +2,10 @@ Empty string is evaluated false: Input: '{@if || this is not the output| this is the output}' Output: ' this is the output' +Input evaluates if flag eval is used: + Input: '{@define a=true}{@if [eval]|{a}| this is the output| this is not the output}' + Output: ' this is the output' + Content with spaces only is false: Input: '{@if | | this is not the output| this is the output}' Output: ' this is the output' diff --git a/jamal-tools/src/main/java/javax0/jamal/tools/NamedMarker.java b/jamal-tools/src/main/java/javax0/jamal/tools/NamedMarker.java index 0e0d31b1..5744fb1a 100644 --- a/jamal-tools/src/main/java/javax0/jamal/tools/NamedMarker.java +++ b/jamal-tools/src/main/java/javax0/jamal/tools/NamedMarker.java @@ -22,7 +22,7 @@ public class NamedMarker implements Marker { * @param name is the identifier of the marker used to check the equality * @param decorator a decorator used to create the string representation of the marker. This string representation * is used in the error messages in the exceptions that are thrown. - * @param position is used in error messages and should point to the input position where the scope was opened + * @param position is used in error messages and should point to the input position where the marker was created */ public NamedMarker(String name, Function decorator, Position position) { this.name = name; @@ -30,11 +30,21 @@ public NamedMarker(String name, Function decorator, Position pos this.position = position; } + /** + * Convert the marker to string supporting the representation of the marker in the error messages. + * The conversion uses the decorator function passed to the constructor. + */ @Override public String toString() { return decorator.apply(name); } + /** + * This implementation of {@code equals} checks that the name of the markers is the same. + * + * @param o the object to compare to + * @return {@code true} if the object is a {@code NamedMarker} and the name of the two markers is the same. + */ @Override public boolean equals(Object o) { if (this == o) return true; @@ -43,11 +53,21 @@ public boolean equals(Object o) { return Objects.equals(name, that.name); } + /** + * This implementation of {@code hashCode} uses the name of the marker to calculate the hash code. + * + * @return the hash code of the name of the marker + */ @Override public int hashCode() { return Objects.hash(name); } + /** + * Get the position where the marker was created. + * + * @return the position + */ @Override public Position getPosition() { return position; diff --git a/jamal-word/src/test/resources/demoConverted.docx b/jamal-word/src/test/resources/demoConverted.docx index 93bc181f..b8fd2b5b 100644 Binary files a/jamal-word/src/test/resources/demoConverted.docx and b/jamal-word/src/test/resources/demoConverted.docx differ diff --git a/jamal-word/src/test/resources/includetestConverted.docx b/jamal-word/src/test/resources/includetestConverted.docx index b75efef7..4d77eec5 100644 Binary files a/jamal-word/src/test/resources/includetestConverted.docx and b/jamal-word/src/test/resources/includetestConverted.docx differ diff --git a/jamal-word/src/test/resources/pictureConverted.docx b/jamal-word/src/test/resources/pictureConverted.docx index 9d82592f..a33fcbce 100644 Binary files a/jamal-word/src/test/resources/pictureConverted.docx and b/jamal-word/src/test/resources/pictureConverted.docx differ diff --git a/jamal-word/src/test/resources/sampleConverted.docx b/jamal-word/src/test/resources/sampleConverted.docx index 8e0e030a..0fa380b2 100644 Binary files a/jamal-word/src/test/resources/sampleConverted.docx and b/jamal-word/src/test/resources/sampleConverted.docx differ diff --git a/jamal.sh b/jamal.sh index 61184141..284b275c 100755 --- a/jamal.sh +++ b/jamal.sh @@ -110,22 +110,24 @@ download "com.javax0.jamal" "jamal-maven-load" "2.6.1-SNAPSHOT" download "com.javax0.mavenDownload" "mavenDownloader" "1.1.0" download "com.javax0.jamal" "jamal-sql" "2.6.1-SNAPSHOT" download "com.github.jsqlparser" "jsqlparser" "4.9" -download "com.javax0.jamal" "jamal-maven-input" "2.6.1-SNAPSHOT" -download "com.javax0.jamal" "jamal-extensions" "2.6.1-SNAPSHOT" -download "com.javax0.jamal" "jamal-jar-input" "2.6.1-SNAPSHOT" -download "com.javax0.jamal" "jamal-word" "2.6.1-SNAPSHOT" -download "org.apache.poi" "poi-ooxml" "5.2.5" +download "com.javax0.jamal" "jamal-xls" "2.6.1-SNAPSHOT" download "org.apache.poi" "poi" "5.2.5" download "commons-codec" "commons-codec" "1.16.0" +download "org.apache.commons" "commons-collections4" "4.4" download "org.apache.commons" "commons-math3" "3.6.1" +download "commons-io" "commons-io" "2.15.0" download "com.zaxxer" "SparseBitSet" "1.3" +download "org.apache.logging.log4j" "log4j-api" "2.21.1" +download "org.apache.poi" "poi-ooxml" "5.2.5" download "org.apache.poi" "poi-ooxml-lite" "5.2.5" download "org.apache.xmlbeans" "xmlbeans" "5.2.0" -download "org.apache.commons" "commons-compress" "1.25.0" -download "commons-io" "commons-io" "2.15.0" download "com.github.virtuald" "curvesapi" "1.08" -download "org.apache.logging.log4j" "log4j-api" "2.21.1" -download "org.apache.commons" "commons-collections4" "4.4" +download "org.apache.commons" "commons-compress" "1.26.2" +download "org.apache.commons" "commons-lang3" "3.14.0" +download "com.javax0.jamal" "jamal-maven-input" "2.6.1-SNAPSHOT" +download "com.javax0.jamal" "jamal-extensions" "2.6.1-SNAPSHOT" +download "com.javax0.jamal" "jamal-jar-input" "2.6.1-SNAPSHOT" +download "com.javax0.jamal" "jamal-word" "2.6.1-SNAPSHOT" download "com.javax0.jamal" "jamal-testsupport" "2.6.1-SNAPSHOT" diff --git a/pom.xml b/pom.xml index b1a9412c..6c512e2b 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.7.0 false none @@ -173,7 +173,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 true ossrh @@ -369,19 +369,19 @@ org.junit.jupiter junit-jupiter-api - 5.11.0-M1 + 5.11.0-M2 test org.junit.jupiter junit-jupiter-engine - 5.11.0-M1 + 5.11.0-M2 test org.junit.jupiter junit-jupiter-params - 5.11.0-M1 + 5.11.0-M2 test @@ -397,7 +397,7 @@ org.mockito mockito-core - 5.11.0 + 5.12.0 test diff --git a/versions.adoc b/versions.adoc index 65a176a5..3afb53a5 100644 --- a/versions.adoc +++ b/versions.adoc @@ -83,7 +83,7 @@ When the new Asciidoctor version is released and used as an IntelliJ plugin we w * `OK` `MAVEN_DOWNLOADER` `1.1.0` -* `OK` `SOURCE_BUDDY` `2.5.1` +* `*[red]#NO#*` `SOURCE_BUDDY` `2.5.1` `*[red]#upgrade to 2.5.3#*` * `OK` `MAVEN_CORE_VERSION` `4.0.0-beta-3`