diff --git a/README.jrf b/README.jrf index 2e4db4ae..ad7d3d50 100644 --- a/README.jrf +++ b/README.jrf @@ -1,5 +1,5 @@ # This is a Jamal reference file containing serialized base64 encoded macros -# Created: 2024-10-07 21:54:28 +0200 +# Created: 2024-10-08 20:08:24 +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/jamal-cmd/README.adoc b/jamal-cmd/README.adoc index 8655c6cd..c31807e3 100644 --- a/jamal-cmd/README.adoc +++ b/jamal-cmd/README.adoc @@ -104,4 +104,8 @@ See also the option `-from`. * `-jamalize` will install Jamal in the `.asciidoctor/lib` directory. It version of the installed Jamal is the same as the one installing it. -It can also install a different version if the option `-version=xxx` is specifying a specific versions. \ No newline at end of file +It can also install a different version if the option `-version=xxx` is specifying a specific versions. + + + + diff --git a/jamal-cmd/src/main/java/javax0/jamal/cmd/Jamalizer.java b/jamal-cmd/src/main/java/javax0/jamal/cmd/Jamalizer.java index 241a05cf..4d82f1f3 100644 --- a/jamal-cmd/src/main/java/javax0/jamal/cmd/Jamalizer.java +++ b/jamal-cmd/src/main/java/javax0/jamal/cmd/Jamalizer.java @@ -20,20 +20,50 @@ public class Jamalizer { private static final String version = System.getProperty("jamal.version") == null ? Processor.jamalVersionString() : System.getProperty("jamal.version"); + private static final String DOWNLOAD_URL_TEMPLATE = "https://repo.maven.apache.org/maven2/com/javax0/jamal/jamal-asciidoc/%s/jamal-asciidoc-%s-jamal-asciidoc-distribution.zip"; static void jamalize(String version) throws IOException, BadSyntax { - if( version == null ){ + if (version == null) { version = Jamalizer.version; } + warnAboutSnapshot(version); createAsccidoctorLibDir(); - extratZip(FileTools.getFileBinaryContent( - String.format("https://repo.maven.apache.org/maven2/com/javax0/jamal/jamal-asciidoc/%s/jamal-asciidoc-%s-jamal-asciidoc-distribution.zip", version, version), + extractZip(FileTools.getFileBinaryContent( + String.format(DOWNLOAD_URL_TEMPLATE, version, version), false, new javax0.jamal.engine.Processor()) ); } - private static void extratZip(final byte[] zipContent) throws IOException { + private static void warnAboutSnapshot(String version) { + if (version.contains("SNAPSHOT")) { + System.err.println("The version of the Jamalizer is a SNAPSHOT version.\n" + + "This is not recommended for production use.\n" + + "Since the command 'jamalize' downloads Jamal from Maven central using the URL\n\n" + + String.format(DOWNLOAD_URL_TEMPLATE, version, version) + + "\n\nit is possible that the version is not available in the repository yet.\n" + + "Use 'jamal -jamalize version=...' to specify a version that is available in the repository." + ); + } + } + + /** + * Extracts JAR files from a ZIP archive provided as a byte array. + * + *

This method processes the given byte array, which represents the content + * of a ZIP archive. It scans the archive, looking for entries that are JAR files + * (files with a ".jar" extension). For each JAR file found, it writes the content + * to a specified directory on the filesystem.

+ * + *

The output JAR files are extracted to the path specified by the + * concatenation of {@code ASCIIDOCTOR_DIR} and {@code LIB_DIR}, using + * the JAR filename from the ZIP entry.

+ * + * @param zipContent the byte array containing the ZIP archive content + * @throws IOException if an I/O error occurs while reading the ZIP archive + * or writing the extracted JAR files to the filesystem + */ + private static void extractZip(final byte[] zipContent) throws IOException { try (final var is = new JarInputStream(new ByteArrayInputStream(zipContent))) { ZipEntry entry; while ((entry = is.getNextEntry()) != null) { diff --git a/jamal-snippet/README.adoc b/jamal-snippet/README.adoc index faabdce8..ad71d3be 100644 --- a/jamal-snippet/README.adoc +++ b/jamal-snippet/README.adoc @@ -3816,7 +3816,7 @@ will result in the output .output [source] ---- -2024-10-04 10:17:51 +2024-10-08 09:13:51 ---- diff --git a/jamal-sql/demodb.mv.db b/jamal-sql/demodb.mv.db index 2c88665d..93740a58 100644 Binary files a/jamal-sql/demodb.mv.db and b/jamal-sql/demodb.mv.db differ diff --git a/jamal-word/src/test/resources/demoConverted.docx b/jamal-word/src/test/resources/demoConverted.docx index 61cb542f..4389a1fd 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 22f289b8..c2fdd906 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 60ccbbab..1a047b22 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 9baa898d..db11d766 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-xls/README.adoc b/jamal-xls/README.adoc index 859f7853..98c000ef 100644 --- a/jamal-xls/README.adoc +++ b/jamal-xls/README.adoc @@ -68,7 +68,7 @@ Parameter options (parops) can be defined without parentheses. The `(` and `)` are optional.) * `file` (aliases are `in`, `input`, `from`) is the name of the file that contains the XLS workbook. -Technically `file` is also an alias, thus a macro havign the name of `file` will not be considered. +Technically, `file` is also an alias; thus a macro having the name of `file` will not be considered. This parop must be defined. * `out` (aliases are `output`, `to`) is the name of the file where the XLS workbook is written. This parop is optional. diff --git a/jamal-yaml/README.adoc b/jamal-yaml/README.adoc index 5d9b9c80..4d36943f 100644 --- a/jamal-yaml/README.adoc +++ b/jamal-yaml/README.adoc @@ -17,7 +17,6 @@ To use this module, you have to add the dependency to your Maven project, as: Following that, you can use the - . <> . <> . <> @@ -30,6 +29,8 @@ Following that, you can use the . <> . <> . <> +. <> + macros. @@ -113,7 +114,7 @@ b: this is c.b The advantage of using this macro over just writing the Yaml directly to the output is that: -* you can split up the Yaml file into smaller pieces using the <>, and <> macros, +* you can split up the Yaml file into smaller pieces using the <>, and <> macros, * you can use user-defined macro parameters mixing the yaml content with Jamal macros. @@ -260,7 +261,7 @@ The format of the macro is ---- -User-defined Yaml macros created using the <> or <> macros may reference other user-defined Yaml macros. +User-defined Yaml macros created using the <> or <> macros may reference other user-defined Yaml macros. When you invoke the macro `yaml:resolve`, it will replace the references in the Yaml macro content with the content of the Yaml macro it references. The resolving process is recursive. If there are any references in the referenced Yaml macro, it will also be resolved. @@ -621,7 +622,7 @@ This macro does not test the structure. It simply tells that the structure went through the resolve process or not. Usually there is narrow use of this macro. -There is no penalty invoking <> on a structure that was already resolved. +There is no penalty invoking <> on a structure that was already resolved. The macro resolve does not re-run the resolution process for a structure that was already resolved. Other macros that need resolved structures automatically invoke resolving. @@ -650,7 +651,7 @@ For more information about the OGNL language visit the web site https://commons. When getting a value out of a Yaml user defined macro the macro will automatically be resolved. The resolution can be cloning or in-place. -To control the resolution process the same options can be used as for the <> macro. +To control the resolution process the same options can be used as for the <> macro. ===== Examples @@ -682,9 +683,10 @@ deep h === vii. `yaml:set` -The macro `yaml:set` can define a yaml user defined macro from an already existing yaml macro. -It is similar to `yaml:define` but this macro does not parse a text and interpret it as yaml formatted text. -Instead, it uses an already defined yaml user defined macro and uses some part of it, and it assignes that to a new user defined macro name. +The macro `yaml:set` can define a user-defined YAML macro from an already existing YAML macro. +It is similar to `yaml:define` but this macro does not parse a text and does not interpret it as YAML formatted text. +Instead, it uses an already defined YAML user-defined macro and uses some part of it. +It assigns the specific part to a new user-defined macro. The syntax of the macro is @@ -698,11 +700,11 @@ The syntax of the macro is ** `yamlResolveClone` (alias `clone`) to clone ** `yamlResolveCopy` (alias `copy`) to copy resolve - ** `yamlDataSource` (alias `from`) the name of the user defined macro which is the source of the data + ** `yamlDataSource` (alias `from`) the name of the user-defined macro which is the source of the data -If the `from` value is missing then the macro interprets the `OGNL` expression star as the name of the macro. -It should start as `/xxxx.`. -The identifier `xxxx` between the starting `/` and the `.` is used as the name of the macro, from which the data is to be fetched. +If the `from` value is missing then the macro interprets the `OGNL` expression start as the name of the macro. +In this case the first character of the expression has to be `/`. +It essentially will start with `/xxxx.` where `xxxx` is the name of the macro. The `macroName` is the name of the macro to assign the new object value to. @@ -737,6 +739,43 @@ deep h ---- +When calling set the new macro will refer to the same object as the original one. +It does not create a copy of the object pointed by the OGNL expression. +This is demonstrated with the followinf example: + +.Jamal source +[source] +---- +{@yaml:define a= +a: alma +b: + c: 3 + d: + - 1 + - 2 + - q: + h: shallow dust} +{@yaml:set s=/a.b.d[2].q} +{@yaml:add to=a.b.d[2].q key=z +deep water +} +{s} +---- + +We set the reference to the object `q`. +Then we add a new key-value pair to the object `q` using the macro `a`. +In the output the result is modified in the macro `s` as well. + +.output +[source] +---- +h: shallow dust +z: deep water +---- + + +It is the behavior even of the `clone` or `copy` options are used. +These options are controlling the resolution process and not the object creation. [[add]] === viii. `yaml:add` @@ -781,7 +820,7 @@ When this option is specified it is an error to specify any `key` since in this ==== Examples -===== Adding a value to the top level Map +===== Adding a value to the top-level Map This example adds a new value to the root of the Yaml structure. @@ -1848,13 +1887,146 @@ The format of the macro is: {@yaml:output yamlMacro} ---- -Here the `yamlMacro` is the name of a Yaml macro to be rendered as the final output of the Jamal processing. +Here the `yamlMacro` is the name of a YAML macro to be rendered as the final output of the Jamal processing. It has to be defined at the end of the processing. It also means that this macro has to be on the top level in the macro hierarchy. In other words, it has to be a global macro. When this macro is used, the output of the Jamal processing will be the Yaml formatted structure of the data held in the macro `yamlMacro`. If this macro contained references and was not yet resolved, then it will be resolved. -Since this is the last step processing the whole Jamal structure following the entire process, usually there is no need for cloning. +Since this is the last step to processing the whole Jamal structure following the entire process, usually there is no need for cloning. If for any reason there is need for cloning then the `clone` option may be used on the command. The command also supports the `copy` option. + +[[Looping]] +==== xiii. Looping over values + +There is no special macro to support looping over values in a YAML structure. +It can be done using the core `for` macro. + +The core `for` macro has two forms. +The conventional is when the macro loops over strings provided in the macro invocation. +In this case the loop the `in` keyword separates the variables from the value list. + +The other case is when the looping is over some Java object. +In this case the `from` keyword separates the variable from the macro holding the object. +These macros must be so-called "object holder" macros, and the user-defined YAML macros are such. + +In the following example we have a YAML structure defined in the macro `games`. +This structure has two lists, `PCgames` and `ConsoleGames`. +We want to loop over the `PCgames` list. + +.Jamal source +[source] +---- +{@yaml:define games= +PCgames: + - "Doom" + - "Quake" + - "Unreal" +ConsoleGames: + - "Mario" + - "Zelda" + - "Metroid" + } +{@yaml:set cGames=/games.PCgames} +{@for game from cGames= +- game} +---- + +The output is the list of the games under the `PCgames` key. + +.output +[source] +---- +- Doom +- Quake +- Unreal +---- + + +Note that we had to use the macro `yaml:set` to set the `cGames` macro to the `PCgames` list. +That is because the `for` macro can only loop over an object provided by a macro. +Writing + +.Jamal source +[source] +---- +{@yaml:define games= +PCgames: +- "Doom" +- "Quake" +- "Unreal" +ConsoleGames: +- "Mario" +- "Zelda" +- "Metroid" +} +{@yaml:set cGames=/games.PCgames} +{@try! {#for game from {@yaml:get /games.PCgames} +- game}} +---- + +will result in the error message: + +.output +[source] +---- +The user defined macro '[Doom,' does not exist or cannot be used as data source for a 'for' loop. +---- + + +because the `{@yaml:get /games.PCgames}` macro evaluated is a string and not a macro. +As you can see from the error message, the result of the macro is the list of the game names. + +You can iterate through lists and maps in the same way. +When you iterate through maps you will get the keys: + +.Jamal source +[source] +---- +{@yaml:define cGames= + first: "Doom" + second: "Quake" + third: "Unreal" +} +{@for game from cGames= +- game} +---- + +The output is the list of the games under the `PCgames` key. + +.output +[source] +---- +- first +- second +- third +---- + + +If you want to go deeper, you can use the loop variable as a key in the map. +For example, you can loop through the list of games by key: + +.Jamal source +[source] +---- +cGames is still defined from the prior example +{!@for game from cGames= +{@yaml:get (from=cGames) game}} +---- + +The output is the list of the games under `cGames` + +.output +[source] +---- +cGames is still defined from the prior example + +Doom +Quake +Unreal +---- + + +For a full-fledged example how to document an OpenAPI stack using Jamal and the YAML macros, visit the documentation at https://github.com/serverless-u/AxsessGard/blob/main/README.adoc.jam. \ No newline at end of file diff --git a/jamal-yaml/README.adoc.jam b/jamal-yaml/README.adoc.jam index 33faa4f5..9ba88cd2 100644 --- a/jamal-yaml/README.adoc.jam +++ b/jamal-yaml/README.adoc.jam @@ -18,7 +18,10 @@ To use this module, you have to add the dependency to your Maven project, as: ---- Following that, you can use the -{%@define chap($x)=. <<$x,`yaml:$x`>>%}{%@define ImportingChapter=Importing yaml from a file%} +{%@define chap($x)=. <<$x,`yaml:$x`>>%}\ +{%@define chapNm($x)=. <<$x,`$x`>>%}\ +{%@define ImportingChapter=Importing yaml from a file%}\ +{%@define Looping=Looping over values%}\ {%chap define%} {%chap ref%} @@ -32,10 +35,14 @@ Following that, you can use the {%chap dump%} {%chap xml%} {%chap output%} +. <> {%@define chap($x)=[[$x]] === {%macroChapter%}`yaml:$x` %}{%@define link($x)=<<$x,`yaml:$x`>>%} +{%@define chapNm($x)=[[$x]] +=== {%macroChapter%}`$x` +%}{%@define link($x)=<<$x,`$x`>>%} macros. == Macros implemented in the package @@ -471,9 +478,10 @@ will result {%chap set%} -The macro `yaml:set` can define a yaml user defined macro from an already existing yaml macro. -It is similar to `yaml:define` but this macro does not parse a text and interpret it as yaml formatted text. -Instead, it uses an already defined yaml user defined macro and uses some part of it, and it assignes that to a new user defined macro name. +The macro `yaml:set` can define a user-defined YAML macro from an already existing YAML macro. +It is similar to `yaml:define` but this macro does not parse a text and does not interpret it as YAML formatted text. +Instead, it uses an already defined YAML user-defined macro and uses some part of it. +It assigns the specific part to a new user-defined macro. The syntax of the macro is @@ -485,11 +493,11 @@ The syntax of the macro is ** `yamlResolveClone` (alias `clone`) to clone ** `yamlResolveCopy` (alias `copy`) to copy resolve - ** `yamlDataSource` (alias `from`) the name of the user defined macro which is the source of the data + ** `yamlDataSource` (alias `from`) the name of the user-defined macro which is the source of the data -If the `from` value is missing then the macro interprets the `OGNL` expression star as the name of the macro. -It should start as `/xxxx.`. -The identifier `xxxx` between the starting `/` and the `.` is used as the name of the macro, from which the data is to be fetched. +If the `from` value is missing then the macro interprets the `OGNL` expression start as the name of the macro. +In this case the first character of the expression has to be `/`. +It essentially will start with `/xxxx.` where `xxxx` is the name of the macro. The `macroName` is the name of the macro to assign the new object value to. @@ -515,6 +523,35 @@ will result {%output%} +When calling set the new macro will refer to the same object as the original one. +It does not create a copy of the object pointed by the OGNL expression. +This is demonstrated with the followinf example: + +{%sample/ +{@yaml:define a= +a: alma +b: + c: 3 + d: + - 1 + - 2 + - q: + h: shallow dust} +{@yaml:set s=/a.b.d[2].q} +{@yaml:add to=a.b.d[2].q key=z +deep water +} +{s} +%} + +We set the reference to the object `q`. +Then we add a new key-value pair to the object `q` using the macro `a`. +In the output the result is modified in the macro `s` as well. + +{%output%} + +It is the behavior even of the `clone` or `copy` options are used. +These options are controlling the resolution process and not the object creation. {%chap add%} @@ -555,7 +592,7 @@ When this option is specified it is an error to specify any `key` since in this ==== Examples -===== Adding a value to the top level Map +===== Adding a value to the top-level Map This example adds a new value to the root of the Yaml structure. @@ -1058,13 +1095,110 @@ The format of the macro is: {@yaml:output yamlMacro} %} -Here the `yamlMacro` is the name of a Yaml macro to be rendered as the final output of the Jamal processing. +Here the `yamlMacro` is the name of a YAML macro to be rendered as the final output of the Jamal processing. It has to be defined at the end of the processing. It also means that this macro has to be on the top level in the macro hierarchy. In other words, it has to be a global macro. When this macro is used, the output of the Jamal processing will be the Yaml formatted structure of the data held in the macro `yamlMacro`. If this macro contained references and was not yet resolved, then it will be resolved. -Since this is the last step processing the whole Jamal structure following the entire process, usually there is no need for cloning. +Since this is the last step to processing the whole Jamal structure following the entire process, usually there is no need for cloning. If for any reason there is need for cloning then the `clone` option may be used on the command. The command also supports the `copy` option. + +[[Looping]] +==== {%macroChapter%}{%Looping%} + +There is no special macro to support looping over values in a YAML structure. +It can be done using the core `for` macro. + +The core `for` macro has two forms. +The conventional is when the macro loops over strings provided in the macro invocation. +In this case the loop the `in` keyword separates the variables from the value list. + +The other case is when the looping is over some Java object. +In this case the `from` keyword separates the variable from the macro holding the object. +These macros must be so-called "object holder" macros, and the user-defined YAML macros are such. + +In the following example we have a YAML structure defined in the macro `games`. +This structure has two lists, `PCgames` and `ConsoleGames`. +We want to loop over the `PCgames` list. + +{%sample/ +{@yaml:define games= +PCgames: + - "Doom" + - "Quake" + - "Unreal" +ConsoleGames: + - "Mario" + - "Zelda" + - "Metroid" + } +{@yaml:set cGames=/games.PCgames} +{@for game from cGames= +- game} +%} + +The output is the list of the games under the `PCgames` key. + +{%output%} + +Note that we had to use the macro `yaml:set` to set the `cGames` macro to the `PCgames` list. +That is because the `for` macro can only loop over an object provided by a macro. +Writing + +{%sample/ +{@yaml:define games= +PCgames: +- "Doom" +- "Quake" +- "Unreal" +ConsoleGames: +- "Mario" +- "Zelda" +- "Metroid" +} +{@yaml:set cGames=/games.PCgames} +{@try! {#for game from {@yaml:get /games.PCgames} +- game}} +%} + +will result in the error message: + +{%output%} + +because the `{@yaml:get /games.PCgames}` macro evaluated is a string and not a macro. +As you can see from the error message, the result of the macro is the list of the game names. + +You can iterate through lists and maps in the same way. +When you iterate through maps you will get the keys: + +{%sample/ +{@yaml:define cGames= + first: "Doom" + second: "Quake" + third: "Unreal" +} +{@for game from cGames= +- game} +%} + +The output is the list of the games under the `PCgames` key. + +{%output%} + +If you want to go deeper, you can use the loop variable as a key in the map. +For example, you can loop through the list of games by key: + +{%sample/ +cGames is still defined from the prior example +{!@for game from cGames= +{@yaml:get (from=cGames) game}} +%} + +The output is the list of the games under `cGames` + +{%output%} + +For a full-fledged example how to document an OpenAPI stack using Jamal and the YAML macros, visit the documentation at https://github.com/serverless-u/AxsessGard/blob/main/README.adoc.jam. \ No newline at end of file