From a7563799470ad8009908de46694091608d8c9f79 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 9 Oct 2024 15:31:55 +0200 Subject: [PATCH 01/15] add jexl for expressions --- cms-content/pom.xml | 4 ++ .../content/shortcodes/ShortCodeParser.java | 24 +++++--- .../cms/content/shortcodes/ShortCodes.java | 7 ++- .../cms/content/ContentBaseTest.java | 45 ++++++++++++++ .../ShortCodeParserReplaceTest.java | 34 ++++++----- .../shortcodes/ShortCodeParserTest.java | 15 ++--- .../content/shortcodes/ShortCodesTest.java | 10 ++-- ...eeMarkerShortCodeTemplateFunctionTest.java | 15 +++-- .../PebbleShortCodeTemplateFunctionTest.java | 12 ++-- ...hymeleafShortCodeTemplateFunctionTest.java | 13 +++-- cms-sandbox/pom.xml | 2 +- cms-sandbox/tests/pom.xml | 22 +++++++ .../java/com/condation/cms/tests/Tests.java | 58 +++++++++++++++++++ .../cms/request/RequestContextFactory.java | 9 ++- .../cms/server/configs/SiteModule.java | 7 +++ .../java/com/condation/cms/TestHelper.java | 5 +- pom.xml | 6 ++ 17 files changed, 232 insertions(+), 56 deletions(-) create mode 100644 cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java create mode 100644 cms-sandbox/tests/pom.xml create mode 100644 cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java diff --git a/cms-content/pom.xml b/cms-content/pom.xml index 6ab4395dd..4f35b5428 100644 --- a/cms-content/pom.xml +++ b/cms-content/pom.xml @@ -35,6 +35,10 @@ org.jsoup jsoup + + org.apache.commons + commons-jexl3 + org.freemarker freemarker diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java index c960a8dc2..2eddee9bd 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java @@ -29,15 +29,22 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlEngine; @Slf4j public class ShortCodeParser { + + JexlEngine engine; public static final String SHORTCODE_REGEX = "\\[\\[(\\w+)([^\\]]*)\\]\\](.*?)\\[\\[\\/\\1\\]\\]|\\[\\[(\\w+)([^\\]]*)\\s*\\/\\]\\]"; public static final Pattern SHORTCODE_PATTERN = Pattern.compile(SHORTCODE_REGEX, Pattern.DOTALL); public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=(\"[^\"]*\"|'[^']*')"); - public static List parseShortcodes(String text) { + public ShortCodeParser (JexlEngine engine) { + this.engine = engine; + } + + public List parseShortcodes(String text) { List shortcodes = new ArrayList<>(); Matcher matcher = SHORTCODE_PATTERN.matcher(text); @@ -67,17 +74,16 @@ public static List parseShortcodes(String text) { return shortcodes; } - public static String replace(String content, Codes codes) { - String newContent = ""; - + public String replace(String content, Codes codes) { + StringBuilder newContent = new StringBuilder(); int lastPosition = 0; var matches = parseShortcodes(content); - for (var match : matches) { - newContent += content.substring(lastPosition, match.getStart()); + for (var match : matches) { + newContent.append(content, lastPosition, match.getStart()); try { - newContent += codes.get(match.getName()).apply(match.getParameters()); + newContent.append(codes.get(match.getName()).apply(match.getParameters())); } catch (Exception e) { log.error("error executing shortcode", e); } @@ -86,10 +92,10 @@ public static String replace(String content, Codes codes) { } if (content.length() > lastPosition) { - newContent += content.substring(lastPosition); + newContent.append(content.substring(lastPosition)); } - return newContent; + return newContent.toString(); } @RequiredArgsConstructor diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index 0a70b039d..27a8143dc 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -28,6 +28,7 @@ import java.util.function.Function; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlEngine; /** * @@ -38,14 +39,16 @@ public class ShortCodes { private final ShortCodeParser.Codes codes; + private final ShortCodeParser parser; - public ShortCodes (Map> codes) { + public ShortCodes (Map> codes, ShortCodeParser shortCodeParser) { + this.parser = shortCodeParser; this.codes = new ShortCodeParser.Codes(); this.codes.addAll(codes); } public String replace (final String content) { - return ShortCodeParser.replace(content, codes); + return parser.replace(content, codes); } public String execute (String name, Map parameters) { diff --git a/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java new file mode 100644 index 000000000..883780f7f --- /dev/null +++ b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java @@ -0,0 +1,45 @@ +package com.condation.cms.content; + +/*- + * #%L + * cms-content + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.content.shortcodes.ShortCodeParser; +import org.apache.commons.jexl3.JexlBuilder; + +/** + * + * @author t.marx + */ +public abstract class ContentBaseTest { + + private ShortCodeParser shortCodeParser; + + public ShortCodeParser getShortCodeParser () { + if (shortCodeParser == null) { + shortCodeParser = new ShortCodeParser( + new JexlBuilder().cache(512).strict(true).silent(false).create() + ); + } + + return shortCodeParser; + } +} diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserReplaceTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserReplaceTest.java index 133d5f070..743b6955b 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserReplaceTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserReplaceTest.java @@ -23,20 +23,22 @@ */ +import com.condation.cms.content.ContentBaseTest; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * * @author t.marx */ -public class ShortCodeParserReplaceTest { +public class ShortCodeParserReplaceTest extends ContentBaseTest { static ShortCodeParser.Codes tags; - @BeforeAll - public static void init () { + @BeforeEach + public void init () { tags = new ShortCodeParser.Codes(); @@ -61,16 +63,16 @@ public static void init () { @Test void simpleTest () { - var result = ShortCodeParser.replace("[[youtube /]]", tags); + var result = getShortCodeParser().replace("[[youtube /]]", tags); Assertions.assertThat(result).isEqualTo(""); - result = ShortCodeParser.replace("[[youtube/]]", tags); + result = getShortCodeParser().replace("[[youtube/]]", tags); Assertions.assertThat(result).isEqualTo(""); } @Test void simple_with_text_before_and_After () { - var result = ShortCodeParser.replace("before [[youtube /]] after", tags); + var result = getShortCodeParser().replace("before [[youtube /]] after", tags); Assertions.assertThat(result).isEqualTo("before after"); } @@ -85,7 +87,7 @@ void complexTest () { some text after """; - var result = ShortCodeParser.replace(content, tags); + var result = getShortCodeParser().replace(content, tags); var expected = """ some text before @@ -100,32 +102,32 @@ void complexTest () { @Test void unknown_tag () { - var result = ShortCodeParser.replace("before [[vimeo id='TEST' /]] after", tags); + var result = getShortCodeParser().replace("before [[vimeo id='TEST' /]] after", tags); Assertions.assertThat(result).isEqualToIgnoringWhitespace("before after"); } @Test void hello_from () { - var result = ShortCodeParser.replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); + var result = getShortCodeParser().replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); - result = ShortCodeParser.replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); + result = getShortCodeParser().replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); - result = ShortCodeParser.replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); + result = getShortCodeParser().replace("[[hello_from name='Thorsten' from='Bochum' /]]", tags); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); } @Test void test_long () { - var result = ShortCodeParser.replace("[[mark]]Important[[/mark]]", tags); + var result = getShortCodeParser().replace("[[mark]]Important[[/mark]]", tags); Assertions.assertThat(result).isEqualTo("Important"); } @Test void test_long_with_params () { - var result = ShortCodeParser.replace("[[mark2 class='test-class']]Important[[/mark2]]", tags); + var result = getShortCodeParser().replace("[[mark2 class='test-class']]Important[[/mark2]]", tags); Assertions.assertThat(result).isEqualTo("Important"); } @@ -141,7 +143,7 @@ void long_complex () { some text after """; - var result = ShortCodeParser.replace(content,tags); + var result = getShortCodeParser().replace(content,tags); var expected = """ some text before @@ -162,7 +164,7 @@ void multiple_hello () { var expected = """

Thorsten

from Bochum

Thorsten

from Bochum

"""; - var result = ShortCodeParser.replace(input, tags); + var result = getShortCodeParser().replace(input, tags); Assertions.assertThat(result).isEqualTo(expected); input = """ @@ -171,7 +173,7 @@ void multiple_hello () { expected = """

Thorsten

from Bochum

Thorsten

from Bochum

"""; - result = ShortCodeParser.replace(input, tags); + result = getShortCodeParser().replace(input, tags); Assertions.assertThat(result).isEqualTo(expected); } } diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserTest.java index 157f5d8ac..30ea03983 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserTest.java @@ -23,17 +23,18 @@ */ +import com.condation.cms.content.ContentBaseTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import java.util.List; -public class ShortCodeParserTest { +public class ShortCodeParserTest extends ContentBaseTest { @Test public void testParseShortcodes_singleShortcodeWithContent() { String text = "This is a text with a shortcode [[code1 param1=\"value1\" param2=\"value2\"]]This is content[[/code1]]."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(1, shortcodes.size()); @@ -47,7 +48,7 @@ public void testParseShortcodes_singleShortcodeWithContent() { @Test public void testParseShortcodes_multipleShortcodes() { String text = "This is a text with a shortcode [[code1 param1=\"value1\" param2=\"value2\"]]This is content[[/code1]] and another one [[code2 param1=\"value1\" param3=\"value3\" /]]."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(2, shortcodes.size()); @@ -67,7 +68,7 @@ public void testParseShortcodes_multipleShortcodes() { @Test public void testParseShortcodes_multipleShortcodes2() { String text = "This is a text with a shortcode [[code1 param1=\"value1\" param2=\"value2\" ]]This is content[[/code1]] and another one [[code2 param1=\"value1\" param3=\"value3\"/]]."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(2, shortcodes.size()); @@ -87,7 +88,7 @@ public void testParseShortcodes_multipleShortcodes2() { @Test public void testParseShortcodes_noShortcodes() { String text = "This text has no shortcodes."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(0, shortcodes.size()); } @@ -95,7 +96,7 @@ public void testParseShortcodes_noShortcodes() { @Test public void testParseShortcodes_emptyParameters() { String text = "This is a text with a shortcode [[code1]][[/code1]] and another one [[code2 /]]."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(2, shortcodes.size()); @@ -111,7 +112,7 @@ public void testParseShortcodes_emptyParameters() { @Test public void testParseShortcodes_malformedShortcodes() { String text = "This is a text with a malformed shortcode [[code1 param1=\"value1\" param2=\"value2\" ."; - List shortcodes = ShortCodeParser.parseShortcodes(text); + List shortcodes = getShortCodeParser().parseShortcodes(text); assertEquals(0, shortcodes.size()); } diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java index 2be9200af..8fff434a8 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -24,23 +24,25 @@ import com.condation.cms.api.model.Parameter; +import com.condation.cms.content.ContentBaseTest; import java.util.HashMap; import java.util.Map; import java.util.function.Function; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * * @author t.marx */ -public class ShortCodesTest { +public class ShortCodesTest extends ContentBaseTest { static ShortCodes shortCodes; - @BeforeAll - public static void init () { + @BeforeEach + public void init () { Map> tags = new HashMap<>(); tags.put( "youtube", @@ -59,7 +61,7 @@ public static void init () { params -> "%s".formatted(params.get("class"), params.get("content")) ); - shortCodes = new ShortCodes(tags); + shortCodes = new ShortCodes(tags, getShortCodeParser()); } diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java index 18bf04e1e..3d5fef036 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java @@ -23,6 +23,7 @@ */ +import com.condation.cms.content.ContentBaseTest; import com.condation.cms.content.template.functions.shortcode.ShortCodeTemplateFunction; import com.condation.cms.content.shortcodes.ShortCodes; import freemarker.template.Configuration; @@ -34,29 +35,33 @@ import java.util.Map; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * * @author t.marx */ -public class FreeMarkerShortCodeTemplateFunctionTest { +public class FreeMarkerShortCodeTemplateFunctionTest extends ContentBaseTest { static Configuration cfg; - static ShortCodes shortCodes; + ShortCodes shortCodes; @BeforeAll public static void setup() { cfg = new Configuration(Configuration.VERSION_2_3_33); cfg.setDefaultEncoding("UTF-8"); - + } + + @BeforeEach + public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - )); + ), getShortCodeParser()); } - + @Test public void testSomeMethod() throws Exception { String templateString = "${shortCode.call('echo')}"; diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java index d0c291b73..108e09e2d 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java @@ -23,6 +23,7 @@ */ +import com.condation.cms.content.ContentBaseTest; import com.condation.cms.content.template.functions.shortcode.ShortCodeTemplateFunction; import com.condation.cms.content.shortcodes.ShortCodes; import io.pebbletemplates.pebble.PebbleEngine; @@ -34,15 +35,16 @@ import java.util.Map; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * * @author t.marx */ -public class PebbleShortCodeTemplateFunctionTest { +public class PebbleShortCodeTemplateFunctionTest extends ContentBaseTest { - static ShortCodes shortCodes; + ShortCodes shortCodes; static PebbleEngine engine; @@ -51,11 +53,13 @@ public static void setup() { engine = new PebbleEngine.Builder() .loader(new StringLoader()) .build(); - + } + @BeforeEach + public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - )); + ), getShortCodeParser()); } @Test diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java index 34b5e4ea0..1427a0b2b 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java @@ -23,11 +23,13 @@ */ +import com.condation.cms.content.ContentBaseTest; import com.condation.cms.content.template.functions.shortcode.ShortCodeTemplateFunction; import com.condation.cms.content.shortcodes.ShortCodes; import java.util.Map; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; @@ -37,9 +39,9 @@ * * @author t.marx */ -public class ThymeleafShortCodeTemplateFunctionTest { +public class ThymeleafShortCodeTemplateFunctionTest extends ContentBaseTest { - static ShortCodes shortCodes; + ShortCodes shortCodes; static TemplateEngine templateEngine; @@ -51,11 +53,14 @@ public static void setup() { templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); - + } + + @BeforeEach + public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - )); + ), getShortCodeParser()); } @Test diff --git a/cms-sandbox/pom.xml b/cms-sandbox/pom.xml index 5f0bd30e2..50662b7b8 100644 --- a/cms-sandbox/pom.xml +++ b/cms-sandbox/pom.xml @@ -9,6 +9,6 @@ cms-sandbox pom - + tests \ No newline at end of file diff --git a/cms-sandbox/tests/pom.xml b/cms-sandbox/tests/pom.xml new file mode 100644 index 000000000..3a3b2487c --- /dev/null +++ b/cms-sandbox/tests/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + com.condation.cms + cms-sandbox + 6.3.1 + + tests + jar + + com.condation.cms.tests.Tests + + + + + org.apache.commons + commons-jexl3 + 3.4.0 + + + \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java b/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java new file mode 100644 index 000000000..b85c3ab54 --- /dev/null +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java @@ -0,0 +1,58 @@ +package com.condation.cms.tests; + +/*- + * #%L + * tests + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.JexlContext; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.MapContext; + +/** + * + * @author t.marx + */ +public class Tests { + + private static final JexlEngine jexl = new JexlBuilder().cache(512).strict(true).silent(false).create(); + + public static void main(String[] args) throws Exception { + JexlContext context = new MapContext(); + var expr = jexl.createExpression("[1, 2, 3]"); + + int[]value = (int[])expr.evaluate(context); + + var list = Arrays.stream(value) + .boxed().toList(); + + list.forEach(System.out::println); + + + expr = jexl.createExpression("{'key' : 'value'}"); + + var map = expr.evaluate(context); + + System.out.println(map); + } +} diff --git a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java index 3fe24525c..05450767c 100644 --- a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java +++ b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java @@ -53,6 +53,7 @@ import com.condation.cms.api.utils.HTTPUtil; import com.condation.cms.api.utils.RequestUtil; import com.condation.cms.content.RenderContext; +import com.condation.cms.content.shortcodes.ShortCodeParser; import com.condation.cms.content.shortcodes.ShortCodes; import com.condation.cms.extensions.ExtensionManager; import com.condation.cms.extensions.hooks.ContentHooks; @@ -159,8 +160,10 @@ private ShortCodes initShortCodes(RequestContext requestContext) { .forEach(extension -> codes.putAll(extension.shortCodes())); var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes); + + var parser = injector.getInstance(ShortCodeParser.class); - return new ShortCodes(wrapper.getShortCodes()); + return new ShortCodes(wrapper.getShortCodes(), parser); } public RequestContext create() throws IOException { @@ -256,8 +259,8 @@ private ShortCodes createShortCodes(RequestContext requestContext) { .forEach(extension -> codes.putAll(extension.shortCodes())); var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes); - - return new ShortCodes(wrapper.getShortCodes()); + var parser = injector.getInstance(ShortCodeParser.class); + return new ShortCodes(wrapper.getShortCodes(), parser); } } diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java index add4c630f..763a05e80 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java @@ -59,6 +59,7 @@ import com.condation.cms.content.DefaultContentRenderer; import com.condation.cms.content.TaxonomyResolver; import com.condation.cms.content.ViewResolver; +import com.condation.cms.content.shortcodes.ShortCodeParser; import com.condation.cms.extensions.ExtensionManager; import com.condation.cms.filesystem.FileDB; import com.condation.cms.filesystem.MetaData; @@ -109,6 +110,12 @@ public ContentNodeMapper contentNodeMapper (DB db, ContentParser contentParser) return new ContentNodeMapper(db, contentParser); } + @Provides + @Singleton + public ShortCodeParser shortCodeParser (DB db, ContentParser contentParser) { + return new ShortCodeParser(null); + } + @Provides @Singleton public ConfigurationManagement configurationManagement(DB db, Configuration configuration, SiteCronJobScheduler scheduler, EventBus eventBus) throws IOException { diff --git a/cms-server/src/test/java/com/condation/cms/TestHelper.java b/cms-server/src/test/java/com/condation/cms/TestHelper.java index 12f3f72e3..9dffb60ef 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -43,6 +43,7 @@ import com.condation.cms.api.markdown.MarkdownRenderer; import com.condation.cms.api.request.RequestContext; import com.condation.cms.content.RenderContext; +import com.condation.cms.content.shortcodes.ShortCodeParser; import com.condation.cms.content.shortcodes.ShortCodes; import com.condation.cms.extensions.hooks.DBHooks; import com.condation.cms.extensions.hooks.TemplateHooks; @@ -71,10 +72,12 @@ public static RequestContext requestContext(String uri) { var markdownRenderer = TestHelper.getRenderer(); RequestContext context = new RequestContext(); + Jex + var shortCodeParser = new ShortCodeParser(null); context.add(RequestFeature.class, new RequestFeature(uri, Map.of())); context.add(RequestExtensions.class, new RequestExtensions(null)); - context.add(RenderContext.class, new RenderContext(markdownRenderer, new ShortCodes(Map.of()), DefaultTheme.EMPTY)); + context.add(RenderContext.class, new RenderContext(markdownRenderer, new ShortCodes(Map.of(), shortCodeParser), DefaultTheme.EMPTY)); context.add(SiteMediaServiceFeature.class, new SiteMediaServiceFeature(new FileMediaService(null))); context.add(InjectorFeature.class, new InjectorFeature(Mockito.mock(Injector.class))); diff --git a/pom.xml b/pom.xml index 5152ca620..b6d136236 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,12 @@ ${project.version}
+ + org.apache.commons + commons-jexl3 + 3.4.0 + + com.github.slugify slugify From 07b3073bf580d877890ae76ee9e84d99be5b5f56 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Wed, 9 Oct 2024 15:32:16 +0200 Subject: [PATCH 02/15] remove error code --- cms-server/src/test/java/com/condation/cms/TestHelper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cms-server/src/test/java/com/condation/cms/TestHelper.java b/cms-server/src/test/java/com/condation/cms/TestHelper.java index 9dffb60ef..58c68421c 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -72,7 +72,6 @@ public static RequestContext requestContext(String uri) { var markdownRenderer = TestHelper.getRenderer(); RequestContext context = new RequestContext(); - Jex var shortCodeParser = new ShortCodeParser(null); context.add(RequestFeature.class, new RequestFeature(uri, Map.of())); From 103b9e05581075fc7f39c45839ab8743daf1e529 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 12:55:31 +0200 Subject: [PATCH 03/15] current --- cms-sandbox/tests/pom.xml | 40 ++++- .../cms/content/shortcodes/ShortCodes.java | 170 ++++++++++++++++++ .../java/com/condation/cms/tests/Tests.java | 1 - .../cms/content/shortcodes/ShortCode.g4 | 35 ++++ .../cms/content/shortcodes/ShortCodeLexer.g4 | 12 ++ .../content/shortcodes/ShortCodesTest.java | 113 ++++++++++++ 6 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 create mode 100644 cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java diff --git a/cms-sandbox/tests/pom.xml b/cms-sandbox/tests/pom.xml index 3a3b2487c..ac3f80325 100644 --- a/cms-sandbox/tests/pom.xml +++ b/cms-sandbox/tests/pom.xml @@ -1,22 +1,54 @@ - + 4.0.0 com.condation.cms cms-sandbox - 6.3.1 + 6.4.0 tests jar com.condation.cms.tests.Tests - + org.apache.commons commons-jexl3 - 3.4.0 + + + org.antlr + antlr4-runtime + 4.13.2 + + + org.projectlombok + lombok + provided + + + + + org.antlr + antlr4-maven-plugin + 4.13.2 + + src/main/resources + target/generated-sources/antlr4 + + + + + antlr4 + + + + + + \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java new file mode 100644 index 000000000..d7ff40390 --- /dev/null +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -0,0 +1,170 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * tests + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.tree.*; +import org.apache.commons.jexl3.*; +import java.util.*; +import java.util.function.Function; + +public class ShortCodes { + + private static final JexlEngine jexl = new JexlBuilder().create(); // Initialisierung des JEXL-Engines + + public static String parseShortcodes(String text, Codes codes) { + // ANTLR Setup + CharStream input = CharStreams.fromString(text); + ShortCodeLexer lexer = new ShortCodeLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ShortCodeParser parser = new ShortCodeParser(tokens); + ParseTree tree = parser.shortcodes(); // Parse the input + + // Listener Setup + ShortCodeListenerImpl listener = new ShortCodeListenerImpl(codes); + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(listener, tree); // Walk the tree using the listener + + // Return the modified text after processing all shortcodes + return listener.getResult(); + } + + // Listener class to process shortcodes + public static class ShortCodeListenerImpl extends ShortCodeBaseListener { + private final Codes codes; + private final StringBuilder result = new StringBuilder(); // Stores the final output + + public ShortCodeListenerImpl(Codes codes) { + this.codes = codes; + } + + @Override + public void enterText(ShortCodeParser.TextContext ctx) { + result.append(ctx.getText()); + } + + @Override + public void enterOpeningTag(ShortCodeParser.OpeningTagContext ctx) { + System.out.println("enterOpeningTag: " + ctx.NAME()); + } + + @Override + public void enterClosingTag(ShortCodeParser.ClosingTagContext ctx) { + System.out.println("enterClosingTag: " + ctx.NAME()); + } + + @Override + public void enterSelfClosingTag(ShortCodeParser.SelfClosingTagContext ctx) { + System.out.println("enterSelfClosingTag: " + ctx.NAME()); + } + + @Override + public void enterShortcodeWithContent(ShortCodeParser.ShortcodeWithContentContext ctx) { + System.out.println("enterShortcodeWithContent: " + ctx.openingTag().NAME()); + + String name = ctx.openingTag().NAME().getText(); + Map parameters = parseParams(ctx.openingTag().params()); + String content = ctx.content() != null ? ctx.content().getText() : ""; + + // Apply shortcode function if exists + if (codes.hasCode(name)) { + parameters.put("content", content); // Pass content as parameter + result.append(codes.get(name).apply(parameters)); + } else { + result.append(ctx.getText()); // No shortcode function found, append raw text + } + } + + @Override + public void enterSelfClosingShortcode(ShortCodeParser.SelfClosingShortcodeContext ctx) { + String name = ctx.selfClosingTag().NAME().getText(); + Map parameters = parseParams(ctx.selfClosingTag().params()); + + // Apply shortcode function if exists + if (codes.hasCode(name)) { + result.append(codes.get(name).apply(parameters)); + } else { + result.append(ctx.getText()); // No shortcode function found, append raw text + } + } + + // Methode, um Parameter zu extrahieren + private Map parseParams(ShortCodeParser.ParamsContext ctx) { + Map params = new HashMap<>(); + if (ctx != null) { + for (ShortCodeParser.ParamContext paramCtx : ctx.param()) { + String key = paramCtx.NAME().getText(); + String rawValue = paramCtx.value().getText(); + Object value = evaluateIfExpression(rawValue); + params.put(key, value); + } + } + return params; + } + + // Methode, um Ausdrücke innerhalb von ${} zu erkennen und mit JEXL auszuwerten + private Object evaluateIfExpression(String rawValue) { + // Prüfen, ob der Wert im Format ${expression} vorliegt + if (rawValue.startsWith("${") && rawValue.endsWith("}")) { + String expression = rawValue.substring(2, rawValue.length() - 1); + return evaluateExpression(expression); + } else { + // Normaler Text + return rawValue.replace("\"", ""); // Entfernt eventuell vorhandene Anführungszeichen + } + } + + // Methode, um einen JEXL-Ausdruck auszuwerten + private Object evaluateExpression(String expression) { + try { + JexlExpression jexlExpression = jexl.createExpression(expression); + JexlContext context = new MapContext(); // Leerer Kontext für einfache Auswertung + return jexlExpression.evaluate(context); // Ausdruck auswerten + } catch (Exception e) { + e.printStackTrace(); + return null; // Wenn die Auswertung fehlschlägt, gib null zurück + } + } + + public String getResult() { + return result.toString(); // Gibt das finale Ergebnis nach dem Parsen zurück + } + } + + // Codes class to store shortcode functions + public static class Codes { + private final Map, String>> codes = new HashMap<>(); + + public void add(String codeName, Function, String> function) { + codes.put(codeName, function); + } + + public boolean hasCode(String codeName) { + return codes.containsKey(codeName); + } + + public Function, String> get(String codeName) { + return codes.getOrDefault(codeName, (params) -> ""); + } + } +} diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java b/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java index b85c3ab54..7743c392c 100644 --- a/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java @@ -23,7 +23,6 @@ */ import java.util.Arrays; -import java.util.List; import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 new file mode 100644 index 000000000..b62d95359 --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 @@ -0,0 +1,35 @@ +grammar ShortCode; + +shortcodes: (shortcode | text)+ EOF; + +shortcode: + openingTag content? closingTag # shortcodeWithContent + | selfClosingTag # selfClosingShortcode + ; + +openingTag: OPENING_BRACKET NAME params? CLOSING_BRACKET ; +closingTag: OPENING_BRACKET '/' NAME CLOSING_BRACKET ; +selfClosingTag: OPENING_BRACKET NAME params? '/' CLOSING_BRACKET ; + +params: param (WS+ param)* ; +param: NAME '=' value ; + +value: STRING | NUMBER; + +// Der Inhalt muss mindestens ein Zeichen sein, das nicht '[' ist +//content: (text | shortcode)* ; +content: (~'[' | ' ')+ ; +//content: (~OPENING_BRACKET | ' ')+ ; + +//text: (~'[' | ' ')+ ; +text: (~('[' | ']') | ' ' | '-')+ ; +//text: (~OPENING_BRACKET)+ ; + +//TEXT: .; +NAME: [a-zA-Z_][a-zA-Z0-9_]* ; +STRING: '"' (~["])* '"' ; +NUMBER: [0-9]+ ; +WS: [ \t\r\n]+ ; + +OPENING_BRACKET: '[[' ; +CLOSING_BRACKET: ']]' ; \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 new file mode 100644 index 000000000..743e42ec4 --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 @@ -0,0 +1,12 @@ +lexer grammar ShortCodes; + +TAG_OPENING_BRACKET: '[[' -> pushMode(TAG); + +mode TAG; +TAG_CLOSING_BRACKET: ']]'; +TAG_CLOSING_CLOSING_BRACKET: '/]]' -> popMode; +TAG_OPENING_CLOSING_BRACKET: '[[/' -> popMode; +TAG_NAME: [a-zA-Z][a-zA-Z0-9]* ; +TAG_STRING: '"' (~["])* '"' ; +TAG_NUMBER: [0-9]+ ; +TAG_WS: [ \t]+ ; diff --git a/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java new file mode 100644 index 000000000..971dfc152 --- /dev/null +++ b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -0,0 +1,113 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * tests + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; + +/** + * + * @author t.marx + */ +public class ShortCodesTest { + + ShortCodes shortCodes; + ShortCodes.Codes codes = new ShortCodes.Codes(); + + @BeforeEach + void setup() { + shortCodes = new ShortCodes(); + + codes.add("code", params -> { + // Verarbeitung der Parameter hier + return "Ausgabe des Shortcodes"; + }); + codes.add("content", params -> { + return (String)params.get("content"); + }); + + codes.add("exp", params -> { + return "expression: " + params.get("value"); + }); + + codes.add("param", params -> { + return "param: " + params.get("param1"); + }); + } + + @Test + public void no_shortcode() { + String result = shortCodes.parseShortcodes("Dein Shortcode-Text hier", codes); + Assertions.assertThat(result).isEqualTo("Dein Shortcode-Text hier"); + } + + @Test + public void self_closing_tag() { + String result = shortCodes.parseShortcodes("[[code/]]", codes); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void self_closing_tag_with_space() { + String result = shortCodes.parseShortcodes("[[code /]]", codes); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void end_closing_tag() { + String result = shortCodes.parseShortcodes("[[code]][[/code]]", codes); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void tag_with_content() { + String result = shortCodes.parseShortcodes("[[content]]Hello CondationCMS[[/content]]", codes); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS"); + } + + @Test + public void expressions() { + String result = shortCodes.parseShortcodes("[[exp value=\"5\"/]]", codes); + Assertions.assertThat(result).isEqualTo("expression: 9"); + } + + @Test + public void parameters_string() { + String result = shortCodes.parseShortcodes("[[param param1=\"5\"/]]", codes); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_number() { + String result = shortCodes.parseShortcodes("[[param param1=5 /]]", codes); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_with_content() { + String result = shortCodes.parseShortcodes("[[param param1=\"5\"]]Hello[[/param]]", codes); + Assertions.assertThat(result).isEqualTo("param: 5"); + } +} From 37a8cd9904b6c40dd6f7cd016ad07002405c5284 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 15:19:23 +0200 Subject: [PATCH 04/15] first jexl and antlr experiements --- .../content/shortcodes/ShortCodeParser.java | 42 +++++++++++--- .../ShortCodeParserExpressionsTest.java | 55 +++++++++++++++++++ .../content/shortcodes/ShortCodesTest.java | 8 ++- .../cms/content/shortcodes/ShortCodes.java | 2 +- .../{ShortCode.g4 => ShortCode.g4-legacy} | 0 .../cms/content/shortcodes/ShortCodeLexer.g4 | 16 ++++-- .../cms/content/shortcodes/ShortCodeParser.g4 | 25 +++++++++ .../cms/server/configs/SiteModule.java | 17 +++++- .../java/com/condation/cms/TestHelper.java | 3 +- pom.xml | 1 - 10 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java rename cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/{ShortCode.g4 => ShortCode.g4-legacy} (100%) create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java index 2eddee9bd..f5969d409 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java @@ -22,6 +22,7 @@ * #L% */ import com.condation.cms.api.model.Parameter; +import java.lang.reflect.Array; import java.util.*; import java.util.function.Function; import java.util.regex.*; @@ -29,27 +30,30 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlExpression; +import org.apache.commons.jexl3.MapContext; @Slf4j public class ShortCodeParser { - + JexlEngine engine; public static final String SHORTCODE_REGEX = "\\[\\[(\\w+)([^\\]]*)\\]\\](.*?)\\[\\[\\/\\1\\]\\]|\\[\\[(\\w+)([^\\]]*)\\s*\\/\\]\\]"; public static final Pattern SHORTCODE_PATTERN = Pattern.compile(SHORTCODE_REGEX, Pattern.DOTALL); - public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=(\"[^\"]*\"|'[^']*')"); + //public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=(\"[^\"]*\"|'[^']*')"); + public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=((\"[^\"]*\"|'[^']*'|\\[[^\\]]*\\]))"); - public ShortCodeParser (JexlEngine engine) { + public ShortCodeParser(JexlEngine engine) { this.engine = engine; } - + public List parseShortcodes(String text) { List shortcodes = new ArrayList<>(); Matcher matcher = SHORTCODE_PATTERN.matcher(text); while (matcher.find()) { - String name = matcher.group(1) != null ? matcher.group(1) : matcher.group(4); String params = matcher.group(2) != null ? matcher.group(2).trim() : matcher.group(5).trim(); String content = matcher.group(3) != null ? matcher.group(3).trim() : ""; @@ -63,9 +67,11 @@ public List parseShortcodes(String text) { while (paramMatcher.find()) { String key = paramMatcher.group(1); String value = paramMatcher.group(2); - // Remove the surrounding quotes - value = value.substring(1, value.length() - 1); - match.getParameters().put(key, value); + value = value.substring(1, value.length() - 1); // Entfernt die Anführungszeichen oder Klammern bei Arrays + + // Prüfe, ob es ein Array ist und nutze JEXL zur Auswertung + Object evaluatedValue = evaluateExpression(value); + match.getParameters().put(key, evaluatedValue); } shortcodes.add(match); @@ -74,6 +80,26 @@ public List parseShortcodes(String text) { return shortcodes; } + private Object evaluateExpression(String value) { + try { + JexlExpression expression = engine.createExpression(value); + JexlContext context = new MapContext(); + var parsedValue = expression.evaluate(context); + if (parsedValue.getClass().isArray()) { + int length = Array.getLength(parsedValue); + List list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(Array.get(parsedValue, i)); // Holen des Elements am Index i + } + return list; + } else { + return parsedValue; + } + } catch (Exception e) { + return value; + } + } + public String replace(String content, Codes codes) { StringBuilder newContent = new StringBuilder(); int lastPosition = 0; diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java new file mode 100644 index 000000000..8e7855238 --- /dev/null +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java @@ -0,0 +1,55 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * cms-content + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + + +import com.condation.cms.content.ContentBaseTest; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +public class ShortCodeParserExpressionsTest extends ContentBaseTest { + + @Test + public void test_numbers() { + String text = "[[code param1=\"12\" param2=\"12.5\"]][[/code]]."; + List shortcodes = getShortCodeParser().parseShortcodes(text); + + assertEquals(1, shortcodes.size()); + + var shortcode = shortcodes.get(0); + assertEquals(12, shortcode.getParameters().get("param1")); + assertEquals(12.5, shortcode.getParameters().get("param2")); + } + + @Test + public void test_list() { + String text = "[[code param1=\"[12, 13, 14]\" param2=\"['12', '13', '14]\"]][[/code]]."; + List shortcodes = getShortCodeParser().parseShortcodes(text); + + var shortcode = shortcodes.get(0); + assertEquals(List.of(12, 13, 14), shortcode.getParameters().get("param1")); + assertEquals(List.of("12", "13", "14"), shortcode.getParameters().get("param2")); + } +} diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java index 8fff434a8..aea453676 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -29,7 +29,6 @@ import java.util.Map; import java.util.function.Function; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -180,4 +179,11 @@ void multiple_hello () { result = shortCodes.replace(input); Assertions.assertThat(result).isEqualTo(expected); } + + @Test + void test_mismathc() { + var result = shortCodes.replace("[[mark1 class='test-class']]Important[[/mark2]]"); + + Assertions.assertThat(result).isEqualTo("[[mark1 class='test-class']]Important[[/mark2]]"); + } } diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index d7ff40390..19459d1b3 100644 --- a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -50,7 +50,7 @@ public static String parseShortcodes(String text, Codes codes) { } // Listener class to process shortcodes - public static class ShortCodeListenerImpl extends ShortCodeBaseListener { + public static class ShortCodeListenerImpl extends ShortCodeParserBaseListener { private final Codes codes; private final StringBuilder result = new StringBuilder(); // Stores the final output diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4-legacy similarity index 100% rename from cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4 rename to cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCode.g4-legacy diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 index 743e42ec4..960cffefd 100644 --- a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 @@ -1,12 +1,16 @@ -lexer grammar ShortCodes; +lexer grammar ShortCodeLexer; + +TAG_OPENING_BRACKET: '[['; + +EQUALS : '=' ; +SPACE : ' ' ; +SINGLE_OPEN_BRAKET : '[' ; -TAG_OPENING_BRACKET: '[[' -> pushMode(TAG); -mode TAG; TAG_CLOSING_BRACKET: ']]'; -TAG_CLOSING_CLOSING_BRACKET: '/]]' -> popMode; -TAG_OPENING_CLOSING_BRACKET: '[[/' -> popMode; +TAG_CLOSING_CLOSING_BRACKET: '/]]' ; +TAG_OPENING_CLOSING_BRACKET: '[[/'; TAG_NAME: [a-zA-Z][a-zA-Z0-9]* ; TAG_STRING: '"' (~["])* '"' ; TAG_NUMBER: [0-9]+ ; -TAG_WS: [ \t]+ ; +TAG_WS: [ \t]+ ; \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 new file mode 100644 index 000000000..d4afa662a --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 @@ -0,0 +1,25 @@ +parser grammar ShortCodeParser; + +options { + tokenVocab = ShortCodeLexer; +} + +shortcodes: (shortcode)+ EOF; + +shortcode: + openingTag content? closingTag # shortcodeWithContent + | selfClosingTag # selfClosingShortcode + ; + +openingTag: + TAG_OPENING_BRACKET TAG_NAME TAG_CLOSING_BRACKET ; +closingTag: + TAG_OPENING_CLOSING_BRACKET TAG_NAME TAG_CLOSING_BRACKET ; +selfClosingTag: + TAG_OPENING_BRACKET TAG_NAME TAG_WS? TAG_CLOSING_CLOSING_BRACKET ; + +params: param (TAG_WS+ param)* ; +param: TAG_NAME EQUALS value ; + +value: TAG_STRING | TAG_NUMBER; +content: (~SINGLE_OPEN_BRAKET | SPACE)+ ; diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java index 763a05e80..4d27f2e35 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java @@ -81,6 +81,7 @@ import java.nio.file.Path; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.jexl3.JexlBuilder; import org.graalvm.polyglot.Engine; /** @@ -112,8 +113,20 @@ public ContentNodeMapper contentNodeMapper (DB db, ContentParser contentParser) @Provides @Singleton - public ShortCodeParser shortCodeParser (DB db, ContentParser contentParser) { - return new ShortCodeParser(null); + public ShortCodeParser shortCodeParser (Configuration configuration) { + var engine = new JexlBuilder() + .strict(true) + .cache(512); + + boolean IS_DEV = configuration.get(ServerConfiguration.class).serverProperties().dev(); + + if (IS_DEV) { + engine.silent(false); + } else { + engine.silent(true); + } + + return new ShortCodeParser(engine.create()); } @Provides diff --git a/cms-server/src/test/java/com/condation/cms/TestHelper.java b/cms-server/src/test/java/com/condation/cms/TestHelper.java index 58c68421c..11f39eb21 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -52,6 +52,7 @@ import com.condation.cms.core.theme.DefaultTheme; import com.google.inject.Injector; import java.util.Map; +import org.apache.commons.jexl3.JexlBuilder; import org.mockito.Mockito; /** @@ -72,7 +73,7 @@ public static RequestContext requestContext(String uri) { var markdownRenderer = TestHelper.getRenderer(); RequestContext context = new RequestContext(); - var shortCodeParser = new ShortCodeParser(null); + var shortCodeParser = new ShortCodeParser(new JexlBuilder().create()); context.add(RequestFeature.class, new RequestFeature(uri, Map.of())); context.add(RequestExtensions.class, new RequestExtensions(null)); diff --git a/pom.xml b/pom.xml index e5d42cac6..d1d087351 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ cms-filesystem cms-media cms-git - cms-sandbox cms-content cms-extensions cms-auth From 9835e04976fcfaaef897ad7cf6ee064ab0633bbe Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 15:32:24 +0200 Subject: [PATCH 05/15] update antlr grammar --- .../cms/content/shortcodes/ShortCodes.java | 74 ++++++++++--------- .../cms/content/shortcodes/ShortCodeLexer.g4 | 1 + .../cms/content/shortcodes/ShortCodeParser.g4 | 10 ++- .../content/shortcodes/ShortCodesTest.java | 1 - 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index 19459d1b3..8f4136f0c 100644 --- a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -62,42 +62,50 @@ public ShortCodeListenerImpl(Codes codes) { public void enterText(ShortCodeParser.TextContext ctx) { result.append(ctx.getText()); } - - @Override - public void enterOpeningTag(ShortCodeParser.OpeningTagContext ctx) { - System.out.println("enterOpeningTag: " + ctx.NAME()); - } - - @Override - public void enterClosingTag(ShortCodeParser.ClosingTagContext ctx) { - System.out.println("enterClosingTag: " + ctx.NAME()); - } - - @Override - public void enterSelfClosingTag(ShortCodeParser.SelfClosingTagContext ctx) { - System.out.println("enterSelfClosingTag: " + ctx.NAME()); - } +// +// @Override +// public void enterOpeningTag(ShortCodeParser.OpeningTagContext ctx) { +// System.out.println("enterOpeningTag: " + ctx.NAME()); +// } +// +// @Override +// public void enterClosingTag(ShortCodeParser.ClosingTagContext ctx) { +// System.out.println("enterClosingTag: " + ctx.NAME()); +// } + +// @Override +// public void enterSelfClosingTag(ShortCodeParser.SelfClosingTagContext ctx) { +// String name = ctx.TAG_NAME().getText(); +// Map parameters = parseParams(ctx.params()); +// +// // Apply shortcode function if exists +// if (codes.hasCode(name)) { +// result.append(codes.get(name).apply(parameters)); +// } else { +// result.append(ctx.getText()); // No shortcode function found, append raw text +// } +// } - @Override - public void enterShortcodeWithContent(ShortCodeParser.ShortcodeWithContentContext ctx) { - System.out.println("enterShortcodeWithContent: " + ctx.openingTag().NAME()); - - String name = ctx.openingTag().NAME().getText(); - Map parameters = parseParams(ctx.openingTag().params()); - String content = ctx.content() != null ? ctx.content().getText() : ""; - - // Apply shortcode function if exists - if (codes.hasCode(name)) { - parameters.put("content", content); // Pass content as parameter - result.append(codes.get(name).apply(parameters)); - } else { - result.append(ctx.getText()); // No shortcode function found, append raw text - } - } +// @Override +// public void enterShortcodeWithContent(ShortCodeParser.ShortcodeWithContentContext ctx) { +// System.out.println("enterShortcodeWithContent: " + ctx.openingTag().NAME()); +// +// String name = ctx.openingTag().NAME().getText(); +// Map parameters = parseParams(ctx.openingTag().params()); +// String content = ctx.content() != null ? ctx.content().getText() : ""; +// +// // Apply shortcode function if exists +// if (codes.hasCode(name)) { +// parameters.put("content", content); // Pass content as parameter +// result.append(codes.get(name).apply(parameters)); +// } else { +// result.append(ctx.getText()); // No shortcode function found, append raw text +// } +// } @Override public void enterSelfClosingShortcode(ShortCodeParser.SelfClosingShortcodeContext ctx) { - String name = ctx.selfClosingTag().NAME().getText(); + String name = ctx.selfClosingTag().TAG_NAME().getText(); Map parameters = parseParams(ctx.selfClosingTag().params()); // Apply shortcode function if exists @@ -113,7 +121,7 @@ private Map parseParams(ShortCodeParser.ParamsContext ctx) { Map params = new HashMap<>(); if (ctx != null) { for (ShortCodeParser.ParamContext paramCtx : ctx.param()) { - String key = paramCtx.NAME().getText(); + String key = paramCtx.TAG_NAME().getText(); String rawValue = paramCtx.value().getText(); Object value = evaluateIfExpression(rawValue); params.put(key, value); diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 index 960cffefd..b56c01c11 100644 --- a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 @@ -5,6 +5,7 @@ TAG_OPENING_BRACKET: '[['; EQUALS : '=' ; SPACE : ' ' ; SINGLE_OPEN_BRAKET : '[' ; +DASH : '-' ; TAG_CLOSING_BRACKET: ']]'; diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 index d4afa662a..7844367f7 100644 --- a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 @@ -4,7 +4,7 @@ options { tokenVocab = ShortCodeLexer; } -shortcodes: (shortcode)+ EOF; +shortcodes: (shortcode | text)+ EOF; shortcode: openingTag content? closingTag # shortcodeWithContent @@ -12,14 +12,16 @@ shortcode: ; openingTag: - TAG_OPENING_BRACKET TAG_NAME TAG_CLOSING_BRACKET ; + TAG_OPENING_BRACKET TAG_NAME params? TAG_CLOSING_BRACKET ; closingTag: TAG_OPENING_CLOSING_BRACKET TAG_NAME TAG_CLOSING_BRACKET ; selfClosingTag: - TAG_OPENING_BRACKET TAG_NAME TAG_WS? TAG_CLOSING_CLOSING_BRACKET ; + TAG_OPENING_BRACKET TAG_NAME params? TAG_WS? TAG_CLOSING_CLOSING_BRACKET ; params: param (TAG_WS+ param)* ; param: TAG_NAME EQUALS value ; value: TAG_STRING | TAG_NUMBER; -content: (~SINGLE_OPEN_BRAKET | SPACE)+ ; +content: (~SINGLE_OPEN_BRAKET | SPACE | DASH)+ ; + +text: (~SINGLE_OPEN_BRAKET | SPACE)+ ; diff --git a/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java index 971dfc152..55faac51e 100644 --- a/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java +++ b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -25,7 +25,6 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.RepeatedTest; /** * From 42d134628e2d5d2f5567def5bd1258fa441a8a31 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 16:34:06 +0200 Subject: [PATCH 06/15] update antlr grammar --- .../cms/content/shortcodes/ShortCodes.java | 49 +- .../shortcodes/.antlr/ShortCodeLexer.interp | 53 ++ .../shortcodes/.antlr/ShortCodeLexer.java | 164 ++++ .../shortcodes/.antlr/ShortCodeLexer.tokens | 20 + .../shortcodes/.antlr/ShortCodeParser.interp | 45 + .../shortcodes/.antlr/ShortCodeParser.java | 797 ++++++++++++++++++ .../shortcodes/.antlr/ShortCodeParser.tokens | 20 + .../6fbb63088f467e041b3797652b994d4b.atn | 1 + .../cms/content/shortcodes/ShortCodeParser.g4 | 6 +- .../content/shortcodes/ShortCodesTest.java | 8 +- 10 files changed, 1130 insertions(+), 33 deletions(-) create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.interp create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.java create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.tokens create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.interp create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.java create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.tokens create mode 100644 cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/cache/6fbb63088f467e041b3797652b994d4b.atn diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index 8f4136f0c..612ac81bf 100644 --- a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -62,16 +62,6 @@ public ShortCodeListenerImpl(Codes codes) { public void enterText(ShortCodeParser.TextContext ctx) { result.append(ctx.getText()); } -// -// @Override -// public void enterOpeningTag(ShortCodeParser.OpeningTagContext ctx) { -// System.out.println("enterOpeningTag: " + ctx.NAME()); -// } -// -// @Override -// public void enterClosingTag(ShortCodeParser.ClosingTagContext ctx) { -// System.out.println("enterClosingTag: " + ctx.NAME()); -// } // @Override // public void enterSelfClosingTag(ShortCodeParser.SelfClosingTagContext ctx) { @@ -86,22 +76,22 @@ public void enterText(ShortCodeParser.TextContext ctx) { // } // } -// @Override -// public void enterShortcodeWithContent(ShortCodeParser.ShortcodeWithContentContext ctx) { -// System.out.println("enterShortcodeWithContent: " + ctx.openingTag().NAME()); -// -// String name = ctx.openingTag().NAME().getText(); -// Map parameters = parseParams(ctx.openingTag().params()); -// String content = ctx.content() != null ? ctx.content().getText() : ""; -// -// // Apply shortcode function if exists -// if (codes.hasCode(name)) { -// parameters.put("content", content); // Pass content as parameter -// result.append(codes.get(name).apply(parameters)); -// } else { -// result.append(ctx.getText()); // No shortcode function found, append raw text -// } -// } + @Override + public void enterShortcodeWithContent(ShortCodeParser.ShortcodeWithContentContext ctx) { + System.out.println("enterShortcodeWithContent: " + ctx.openingTag().TAG_NAME()); + + String name = ctx.openingTag().TAG_NAME().getText(); + Map parameters = parseParams(ctx.openingTag().params()); + String content = ctx.content() != null ? ctx.content().getText() : ""; + + // Apply shortcode function if exists + if (codes.hasCode(name)) { + parameters.put("content", content); // Pass content as parameter + result.append(codes.get(name).apply(parameters)); + } else { + result.append(ctx.getText()); // No shortcode function found, append raw text + } + } @Override public void enterSelfClosingShortcode(ShortCodeParser.SelfClosingShortcodeContext ctx) { @@ -133,12 +123,13 @@ private Map parseParams(ShortCodeParser.ParamsContext ctx) { // Methode, um Ausdrücke innerhalb von ${} zu erkennen und mit JEXL auszuwerten private Object evaluateIfExpression(String rawValue) { // Prüfen, ob der Wert im Format ${expression} vorliegt - if (rawValue.startsWith("${") && rawValue.endsWith("}")) { - String expression = rawValue.substring(2, rawValue.length() - 1); + var testValue = rawValue.replace("\"", ""); + if (testValue.startsWith("${") && testValue.endsWith("}")) { + String expression = testValue.substring(2, testValue.length() - 1); return evaluateExpression(expression); } else { // Normaler Text - return rawValue.replace("\"", ""); // Entfernt eventuell vorhandene Anführungszeichen + return testValue;// Entfernt eventuell vorhandene Anführungszeichen } } diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.interp b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.interp new file mode 100644 index 000000000..9b48d9b6c --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.interp @@ -0,0 +1,53 @@ +token literal names: +null +'[[' +'=' +' ' +'[' +'-' +']]' +'/]]' +'[[/' +null +null +null +null + +token symbolic names: +null +TAG_OPENING_BRACKET +EQUALS +SPACE +SINGLE_OPEN_BRAKET +DASH +TAG_CLOSING_BRACKET +TAG_CLOSING_CLOSING_BRACKET +TAG_OPENING_CLOSING_BRACKET +TAG_NAME +TAG_STRING +TAG_NUMBER +TAG_WS + +rule names: +TAG_OPENING_BRACKET +EQUALS +SPACE +SINGLE_OPEN_BRAKET +DASH +TAG_CLOSING_BRACKET +TAG_CLOSING_CLOSING_BRACKET +TAG_OPENING_CLOSING_BRACKET +TAG_NAME +TAG_STRING +TAG_NUMBER +TAG_WS + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 12, 73, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 5, 8, 50, 8, 8, 10, 8, 12, 8, 53, 9, 8, 1, 9, 1, 9, 5, 9, 57, 8, 9, 10, 9, 12, 9, 60, 9, 9, 1, 9, 1, 9, 1, 10, 4, 10, 65, 8, 10, 11, 10, 12, 10, 66, 1, 11, 4, 11, 70, 8, 11, 11, 11, 12, 11, 71, 0, 0, 12, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 1, 0, 5, 2, 0, 65, 90, 97, 122, 3, 0, 48, 57, 65, 90, 97, 122, 1, 0, 34, 34, 1, 0, 48, 57, 2, 0, 9, 9, 32, 32, 76, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 1, 25, 1, 0, 0, 0, 3, 28, 1, 0, 0, 0, 5, 30, 1, 0, 0, 0, 7, 32, 1, 0, 0, 0, 9, 34, 1, 0, 0, 0, 11, 36, 1, 0, 0, 0, 13, 39, 1, 0, 0, 0, 15, 43, 1, 0, 0, 0, 17, 47, 1, 0, 0, 0, 19, 54, 1, 0, 0, 0, 21, 64, 1, 0, 0, 0, 23, 69, 1, 0, 0, 0, 25, 26, 5, 91, 0, 0, 26, 27, 5, 91, 0, 0, 27, 2, 1, 0, 0, 0, 28, 29, 5, 61, 0, 0, 29, 4, 1, 0, 0, 0, 30, 31, 5, 32, 0, 0, 31, 6, 1, 0, 0, 0, 32, 33, 5, 91, 0, 0, 33, 8, 1, 0, 0, 0, 34, 35, 5, 45, 0, 0, 35, 10, 1, 0, 0, 0, 36, 37, 5, 93, 0, 0, 37, 38, 5, 93, 0, 0, 38, 12, 1, 0, 0, 0, 39, 40, 5, 47, 0, 0, 40, 41, 5, 93, 0, 0, 41, 42, 5, 93, 0, 0, 42, 14, 1, 0, 0, 0, 43, 44, 5, 91, 0, 0, 44, 45, 5, 91, 0, 0, 45, 46, 5, 47, 0, 0, 46, 16, 1, 0, 0, 0, 47, 51, 7, 0, 0, 0, 48, 50, 7, 1, 0, 0, 49, 48, 1, 0, 0, 0, 50, 53, 1, 0, 0, 0, 51, 49, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 18, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 54, 58, 5, 34, 0, 0, 55, 57, 8, 2, 0, 0, 56, 55, 1, 0, 0, 0, 57, 60, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 61, 1, 0, 0, 0, 60, 58, 1, 0, 0, 0, 61, 62, 5, 34, 0, 0, 62, 20, 1, 0, 0, 0, 63, 65, 7, 3, 0, 0, 64, 63, 1, 0, 0, 0, 65, 66, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 66, 67, 1, 0, 0, 0, 67, 22, 1, 0, 0, 0, 68, 70, 7, 4, 0, 0, 69, 68, 1, 0, 0, 0, 70, 71, 1, 0, 0, 0, 71, 69, 1, 0, 0, 0, 71, 72, 1, 0, 0, 0, 72, 24, 1, 0, 0, 0, 5, 0, 51, 58, 66, 71, 0] \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.java b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.java new file mode 100644 index 000000000..e4eb39621 --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.java @@ -0,0 +1,164 @@ +// Generated from c:/entwicklung/workspaces/tma/cms/cms-server/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeLexer.g4 by ANTLR 4.13.1 +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.*; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"}) +public class ShortCodeLexer extends Lexer { + static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + TAG_OPENING_BRACKET=1, EQUALS=2, SPACE=3, SINGLE_OPEN_BRAKET=4, DASH=5, + TAG_CLOSING_BRACKET=6, TAG_CLOSING_CLOSING_BRACKET=7, TAG_OPENING_CLOSING_BRACKET=8, + TAG_NAME=9, TAG_STRING=10, TAG_NUMBER=11, TAG_WS=12; + public static String[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static String[] modeNames = { + "DEFAULT_MODE" + }; + + private static String[] makeRuleNames() { + return new String[] { + "TAG_OPENING_BRACKET", "EQUALS", "SPACE", "SINGLE_OPEN_BRAKET", "DASH", + "TAG_CLOSING_BRACKET", "TAG_CLOSING_CLOSING_BRACKET", "TAG_OPENING_CLOSING_BRACKET", + "TAG_NAME", "TAG_STRING", "TAG_NUMBER", "TAG_WS" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "'[['", "'='", "' '", "'['", "'-'", "']]'", "'/]]'", "'[[/'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "TAG_OPENING_BRACKET", "EQUALS", "SPACE", "SINGLE_OPEN_BRAKET", + "DASH", "TAG_CLOSING_BRACKET", "TAG_CLOSING_CLOSING_BRACKET", "TAG_OPENING_CLOSING_BRACKET", + "TAG_NAME", "TAG_STRING", "TAG_NUMBER", "TAG_WS" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + + public ShortCodeLexer(CharStream input) { + super(input); + _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @Override + public String getGrammarFileName() { return "ShortCodeLexer.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public String[] getChannelNames() { return channelNames; } + + @Override + public String[] getModeNames() { return modeNames; } + + @Override + public ATN getATN() { return _ATN; } + + public static final String _serializedATN = + "\u0004\u0000\fI\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+ + "\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+ + "\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+ + "\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+ + "\u0007\u000b\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001"+ + "\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004"+ + "\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006"+ + "\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001\u0007\u0001\b\u0001"+ + "\b\u0005\b2\b\b\n\b\f\b5\t\b\u0001\t\u0001\t\u0005\t9\b\t\n\t\f\t<\t\t"+ + "\u0001\t\u0001\t\u0001\n\u0004\nA\b\n\u000b\n\f\nB\u0001\u000b\u0004\u000b"+ + "F\b\u000b\u000b\u000b\f\u000bG\u0000\u0000\f\u0001\u0001\u0003\u0002\u0005"+ + "\u0003\u0007\u0004\t\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n"+ + "\u0015\u000b\u0017\f\u0001\u0000\u0005\u0002\u0000AZaz\u0003\u000009A"+ + "Zaz\u0001\u0000\"\"\u0001\u000009\u0002\u0000\t\t L\u0000\u0001\u0001"+ + "\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001"+ + "\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000"+ + "\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000"+ + "\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000"+ + "\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000"+ + "\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0001\u0019\u0001\u0000\u0000"+ + "\u0000\u0003\u001c\u0001\u0000\u0000\u0000\u0005\u001e\u0001\u0000\u0000"+ + "\u0000\u0007 \u0001\u0000\u0000\u0000\t\"\u0001\u0000\u0000\u0000\u000b"+ + "$\u0001\u0000\u0000\u0000\r\'\u0001\u0000\u0000\u0000\u000f+\u0001\u0000"+ + "\u0000\u0000\u0011/\u0001\u0000\u0000\u0000\u00136\u0001\u0000\u0000\u0000"+ + "\u0015@\u0001\u0000\u0000\u0000\u0017E\u0001\u0000\u0000\u0000\u0019\u001a"+ + "\u0005[\u0000\u0000\u001a\u001b\u0005[\u0000\u0000\u001b\u0002\u0001\u0000"+ + "\u0000\u0000\u001c\u001d\u0005=\u0000\u0000\u001d\u0004\u0001\u0000\u0000"+ + "\u0000\u001e\u001f\u0005 \u0000\u0000\u001f\u0006\u0001\u0000\u0000\u0000"+ + " !\u0005[\u0000\u0000!\b\u0001\u0000\u0000\u0000\"#\u0005-\u0000\u0000"+ + "#\n\u0001\u0000\u0000\u0000$%\u0005]\u0000\u0000%&\u0005]\u0000\u0000"+ + "&\f\u0001\u0000\u0000\u0000\'(\u0005/\u0000\u0000()\u0005]\u0000\u0000"+ + ")*\u0005]\u0000\u0000*\u000e\u0001\u0000\u0000\u0000+,\u0005[\u0000\u0000"+ + ",-\u0005[\u0000\u0000-.\u0005/\u0000\u0000.\u0010\u0001\u0000\u0000\u0000"+ + "/3\u0007\u0000\u0000\u000002\u0007\u0001\u0000\u000010\u0001\u0000\u0000"+ + "\u000025\u0001\u0000\u0000\u000031\u0001\u0000\u0000\u000034\u0001\u0000"+ + "\u0000\u00004\u0012\u0001\u0000\u0000\u000053\u0001\u0000\u0000\u0000"+ + "6:\u0005\"\u0000\u000079\b\u0002\u0000\u000087\u0001\u0000\u0000\u0000"+ + "9<\u0001\u0000\u0000\u0000:8\u0001\u0000\u0000\u0000:;\u0001\u0000\u0000"+ + "\u0000;=\u0001\u0000\u0000\u0000<:\u0001\u0000\u0000\u0000=>\u0005\"\u0000"+ + "\u0000>\u0014\u0001\u0000\u0000\u0000?A\u0007\u0003\u0000\u0000@?\u0001"+ + "\u0000\u0000\u0000AB\u0001\u0000\u0000\u0000B@\u0001\u0000\u0000\u0000"+ + "BC\u0001\u0000\u0000\u0000C\u0016\u0001\u0000\u0000\u0000DF\u0007\u0004"+ + "\u0000\u0000ED\u0001\u0000\u0000\u0000FG\u0001\u0000\u0000\u0000GE\u0001"+ + "\u0000\u0000\u0000GH\u0001\u0000\u0000\u0000H\u0018\u0001\u0000\u0000"+ + "\u0000\u0005\u00003:BG\u0000"; + public static final ATN _ATN = + new ATNDeserializer().deserialize(_serializedATN.toCharArray()); + static { + _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; + for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { + _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); + } + } +} \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.tokens b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.tokens new file mode 100644 index 000000000..e75993c9a --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeLexer.tokens @@ -0,0 +1,20 @@ +TAG_OPENING_BRACKET=1 +EQUALS=2 +SPACE=3 +SINGLE_OPEN_BRAKET=4 +DASH=5 +TAG_CLOSING_BRACKET=6 +TAG_CLOSING_CLOSING_BRACKET=7 +TAG_OPENING_CLOSING_BRACKET=8 +TAG_NAME=9 +TAG_STRING=10 +TAG_NUMBER=11 +TAG_WS=12 +'[['=1 +'='=2 +' '=3 +'['=4 +'-'=5 +']]'=6 +'/]]'=7 +'[[/'=8 diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.interp b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.interp new file mode 100644 index 000000000..57e5c636e --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.interp @@ -0,0 +1,45 @@ +token literal names: +null +'[[' +'=' +' ' +'[' +'-' +']]' +'/]]' +'[[/' +null +null +null +null + +token symbolic names: +null +TAG_OPENING_BRACKET +EQUALS +SPACE +SINGLE_OPEN_BRAKET +DASH +TAG_CLOSING_BRACKET +TAG_CLOSING_CLOSING_BRACKET +TAG_OPENING_CLOSING_BRACKET +TAG_NAME +TAG_STRING +TAG_NUMBER +TAG_WS + +rule names: +shortcodes +shortcode +openingTag +closingTag +selfClosingTag +params +param +value +content +text + + +atn: +[4, 1, 12, 96, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 1, 0, 1, 0, 4, 0, 23, 8, 0, 11, 0, 12, 0, 24, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 31, 8, 1, 1, 1, 1, 1, 1, 1, 3, 1, 36, 8, 1, 1, 2, 1, 2, 1, 2, 3, 2, 41, 8, 2, 1, 2, 3, 2, 44, 8, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 3, 4, 55, 8, 4, 1, 4, 3, 4, 58, 8, 4, 1, 4, 3, 4, 61, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 4, 5, 67, 8, 5, 11, 5, 12, 5, 68, 1, 5, 5, 5, 72, 8, 5, 10, 5, 12, 5, 75, 9, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 4, 8, 86, 8, 8, 11, 8, 12, 8, 87, 1, 9, 1, 9, 4, 9, 92, 8, 9, 11, 9, 12, 9, 93, 1, 9, 0, 0, 10, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 0, 2, 1, 0, 10, 11, 1, 0, 4, 4, 101, 0, 22, 1, 0, 0, 0, 2, 35, 1, 0, 0, 0, 4, 37, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 51, 1, 0, 0, 0, 10, 64, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 80, 1, 0, 0, 0, 16, 85, 1, 0, 0, 0, 18, 91, 1, 0, 0, 0, 20, 23, 3, 2, 1, 0, 21, 23, 3, 18, 9, 0, 22, 20, 1, 0, 0, 0, 22, 21, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 22, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 27, 5, 0, 0, 1, 27, 1, 1, 0, 0, 0, 28, 30, 3, 4, 2, 0, 29, 31, 3, 16, 8, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 33, 3, 6, 3, 0, 33, 36, 1, 0, 0, 0, 34, 36, 3, 8, 4, 0, 35, 28, 1, 0, 0, 0, 35, 34, 1, 0, 0, 0, 36, 3, 1, 0, 0, 0, 37, 38, 5, 1, 0, 0, 38, 40, 5, 9, 0, 0, 39, 41, 5, 12, 0, 0, 40, 39, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 43, 1, 0, 0, 0, 42, 44, 3, 10, 5, 0, 43, 42, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 46, 5, 6, 0, 0, 46, 5, 1, 0, 0, 0, 47, 48, 5, 8, 0, 0, 48, 49, 5, 9, 0, 0, 49, 50, 5, 6, 0, 0, 50, 7, 1, 0, 0, 0, 51, 52, 5, 1, 0, 0, 52, 54, 5, 9, 0, 0, 53, 55, 5, 12, 0, 0, 54, 53, 1, 0, 0, 0, 54, 55, 1, 0, 0, 0, 55, 57, 1, 0, 0, 0, 56, 58, 3, 10, 5, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 60, 1, 0, 0, 0, 59, 61, 5, 12, 0, 0, 60, 59, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 63, 5, 7, 0, 0, 63, 9, 1, 0, 0, 0, 64, 73, 3, 12, 6, 0, 65, 67, 5, 12, 0, 0, 66, 65, 1, 0, 0, 0, 67, 68, 1, 0, 0, 0, 68, 66, 1, 0, 0, 0, 68, 69, 1, 0, 0, 0, 69, 70, 1, 0, 0, 0, 70, 72, 3, 12, 6, 0, 71, 66, 1, 0, 0, 0, 72, 75, 1, 0, 0, 0, 73, 71, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 11, 1, 0, 0, 0, 75, 73, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 78, 5, 2, 0, 0, 78, 79, 3, 14, 7, 0, 79, 13, 1, 0, 0, 0, 80, 81, 7, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 86, 8, 1, 0, 0, 83, 86, 5, 3, 0, 0, 84, 86, 5, 5, 0, 0, 85, 82, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0, 85, 84, 1, 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 85, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 17, 1, 0, 0, 0, 89, 92, 8, 1, 0, 0, 90, 92, 5, 3, 0, 0, 91, 89, 1, 0, 0, 0, 91, 90, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 91, 1, 0, 0, 0, 93, 94, 1, 0, 0, 0, 94, 19, 1, 0, 0, 0, 15, 22, 24, 30, 35, 40, 43, 54, 57, 60, 68, 73, 85, 87, 91, 93] \ No newline at end of file diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.java b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.java new file mode 100644 index 000000000..c1e2303a9 --- /dev/null +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/.antlr/ShortCodeParser.java @@ -0,0 +1,797 @@ +// Generated from c:/entwicklung/workspaces/tma/cms/cms-server/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 by ANTLR 4.13.1 +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) +public class ShortCodeParser extends Parser { + static { RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + public static final int + TAG_OPENING_BRACKET=1, EQUALS=2, SPACE=3, SINGLE_OPEN_BRAKET=4, DASH=5, + TAG_CLOSING_BRACKET=6, TAG_CLOSING_CLOSING_BRACKET=7, TAG_OPENING_CLOSING_BRACKET=8, + TAG_NAME=9, TAG_STRING=10, TAG_NUMBER=11, TAG_WS=12; + public static final int + RULE_shortcodes = 0, RULE_shortcode = 1, RULE_openingTag = 2, RULE_closingTag = 3, + RULE_selfClosingTag = 4, RULE_params = 5, RULE_param = 6, RULE_value = 7, + RULE_content = 8, RULE_text = 9; + private static String[] makeRuleNames() { + return new String[] { + "shortcodes", "shortcode", "openingTag", "closingTag", "selfClosingTag", + "params", "param", "value", "content", "text" + }; + } + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, "'[['", "'='", "' '", "'['", "'-'", "']]'", "'/]]'", "'[[/'" + }; + } + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + private static String[] makeSymbolicNames() { + return new String[] { + null, "TAG_OPENING_BRACKET", "EQUALS", "SPACE", "SINGLE_OPEN_BRAKET", + "DASH", "TAG_CLOSING_BRACKET", "TAG_CLOSING_CLOSING_BRACKET", "TAG_OPENING_CLOSING_BRACKET", + "TAG_NAME", "TAG_STRING", "TAG_NUMBER", "TAG_WS" + }; + } + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); + public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + + /** + * @deprecated Use {@link #VOCABULARY} instead. + */ + @Deprecated + public static final String[] tokenNames; + static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = ""; + } + } + } + + @Override + @Deprecated + public String[] getTokenNames() { + return tokenNames; + } + + @Override + + public Vocabulary getVocabulary() { + return VOCABULARY; + } + + @Override + public String getGrammarFileName() { return "ShortCodeParser.g4"; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + public ShortCodeParser(TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); + } + + @SuppressWarnings("CheckReturnValue") + public static class ShortcodesContext extends ParserRuleContext { + public TerminalNode EOF() { return getToken(ShortCodeParser.EOF, 0); } + public List shortcode() { + return getRuleContexts(ShortcodeContext.class); + } + public ShortcodeContext shortcode(int i) { + return getRuleContext(ShortcodeContext.class,i); + } + public List text() { + return getRuleContexts(TextContext.class); + } + public TextContext text(int i) { + return getRuleContext(TextContext.class,i); + } + public ShortcodesContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_shortcodes; } + } + + public final ShortcodesContext shortcodes() throws RecognitionException { + ShortcodesContext _localctx = new ShortcodesContext(_ctx, getState()); + enterRule(_localctx, 0, RULE_shortcodes); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(22); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + setState(22); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,0,_ctx) ) { + case 1: + { + setState(20); + shortcode(); + } + break; + case 2: + { + setState(21); + text(); + } + break; + } + } + setState(24); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( (((_la) & ~0x3f) == 0 && ((1L << _la) & 8174L) != 0) ); + setState(26); + match(EOF); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ShortcodeContext extends ParserRuleContext { + public ShortcodeContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_shortcode; } + + public ShortcodeContext() { } + public void copyFrom(ShortcodeContext ctx) { + super.copyFrom(ctx); + } + } + @SuppressWarnings("CheckReturnValue") + public static class ShortcodeWithContentContext extends ShortcodeContext { + public OpeningTagContext openingTag() { + return getRuleContext(OpeningTagContext.class,0); + } + public ClosingTagContext closingTag() { + return getRuleContext(ClosingTagContext.class,0); + } + public ContentContext content() { + return getRuleContext(ContentContext.class,0); + } + public ShortcodeWithContentContext(ShortcodeContext ctx) { copyFrom(ctx); } + } + @SuppressWarnings("CheckReturnValue") + public static class SelfClosingShortcodeContext extends ShortcodeContext { + public SelfClosingTagContext selfClosingTag() { + return getRuleContext(SelfClosingTagContext.class,0); + } + public SelfClosingShortcodeContext(ShortcodeContext ctx) { copyFrom(ctx); } + } + + public final ShortcodeContext shortcode() throws RecognitionException { + ShortcodeContext _localctx = new ShortcodeContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_shortcode); + try { + setState(35); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,3,_ctx) ) { + case 1: + _localctx = new ShortcodeWithContentContext(_localctx); + enterOuterAlt(_localctx, 1); + { + setState(28); + openingTag(); + setState(30); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) { + case 1: + { + setState(29); + content(); + } + break; + } + setState(32); + closingTag(); + } + break; + case 2: + _localctx = new SelfClosingShortcodeContext(_localctx); + enterOuterAlt(_localctx, 2); + { + setState(34); + selfClosingTag(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class OpeningTagContext extends ParserRuleContext { + public TerminalNode TAG_OPENING_BRACKET() { return getToken(ShortCodeParser.TAG_OPENING_BRACKET, 0); } + public TerminalNode TAG_NAME() { return getToken(ShortCodeParser.TAG_NAME, 0); } + public TerminalNode TAG_CLOSING_BRACKET() { return getToken(ShortCodeParser.TAG_CLOSING_BRACKET, 0); } + public TerminalNode TAG_WS() { return getToken(ShortCodeParser.TAG_WS, 0); } + public ParamsContext params() { + return getRuleContext(ParamsContext.class,0); + } + public OpeningTagContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_openingTag; } + } + + public final OpeningTagContext openingTag() throws RecognitionException { + OpeningTagContext _localctx = new OpeningTagContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_openingTag); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(37); + match(TAG_OPENING_BRACKET); + setState(38); + match(TAG_NAME); + setState(40); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==TAG_WS) { + { + setState(39); + match(TAG_WS); + } + } + + setState(43); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==TAG_NAME) { + { + setState(42); + params(); + } + } + + setState(45); + match(TAG_CLOSING_BRACKET); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ClosingTagContext extends ParserRuleContext { + public TerminalNode TAG_OPENING_CLOSING_BRACKET() { return getToken(ShortCodeParser.TAG_OPENING_CLOSING_BRACKET, 0); } + public TerminalNode TAG_NAME() { return getToken(ShortCodeParser.TAG_NAME, 0); } + public TerminalNode TAG_CLOSING_BRACKET() { return getToken(ShortCodeParser.TAG_CLOSING_BRACKET, 0); } + public ClosingTagContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_closingTag; } + } + + public final ClosingTagContext closingTag() throws RecognitionException { + ClosingTagContext _localctx = new ClosingTagContext(_ctx, getState()); + enterRule(_localctx, 6, RULE_closingTag); + try { + enterOuterAlt(_localctx, 1); + { + setState(47); + match(TAG_OPENING_CLOSING_BRACKET); + setState(48); + match(TAG_NAME); + setState(49); + match(TAG_CLOSING_BRACKET); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class SelfClosingTagContext extends ParserRuleContext { + public TerminalNode TAG_OPENING_BRACKET() { return getToken(ShortCodeParser.TAG_OPENING_BRACKET, 0); } + public TerminalNode TAG_NAME() { return getToken(ShortCodeParser.TAG_NAME, 0); } + public TerminalNode TAG_CLOSING_CLOSING_BRACKET() { return getToken(ShortCodeParser.TAG_CLOSING_CLOSING_BRACKET, 0); } + public List TAG_WS() { return getTokens(ShortCodeParser.TAG_WS); } + public TerminalNode TAG_WS(int i) { + return getToken(ShortCodeParser.TAG_WS, i); + } + public ParamsContext params() { + return getRuleContext(ParamsContext.class,0); + } + public SelfClosingTagContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_selfClosingTag; } + } + + public final SelfClosingTagContext selfClosingTag() throws RecognitionException { + SelfClosingTagContext _localctx = new SelfClosingTagContext(_ctx, getState()); + enterRule(_localctx, 8, RULE_selfClosingTag); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(51); + match(TAG_OPENING_BRACKET); + setState(52); + match(TAG_NAME); + setState(54); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) { + case 1: + { + setState(53); + match(TAG_WS); + } + break; + } + setState(57); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==TAG_NAME) { + { + setState(56); + params(); + } + } + + setState(60); + _errHandler.sync(this); + _la = _input.LA(1); + if (_la==TAG_WS) { + { + setState(59); + match(TAG_WS); + } + } + + setState(62); + match(TAG_CLOSING_CLOSING_BRACKET); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParamsContext extends ParserRuleContext { + public List param() { + return getRuleContexts(ParamContext.class); + } + public ParamContext param(int i) { + return getRuleContext(ParamContext.class,i); + } + public List TAG_WS() { return getTokens(ShortCodeParser.TAG_WS); } + public TerminalNode TAG_WS(int i) { + return getToken(ShortCodeParser.TAG_WS, i); + } + public ParamsContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_params; } + } + + public final ParamsContext params() throws RecognitionException { + ParamsContext _localctx = new ParamsContext(_ctx, getState()); + enterRule(_localctx, 10, RULE_params); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(64); + param(); + setState(73); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(66); + _errHandler.sync(this); + _la = _input.LA(1); + do { + { + { + setState(65); + match(TAG_WS); + } + } + setState(68); + _errHandler.sync(this); + _la = _input.LA(1); + } while ( _la==TAG_WS ); + setState(70); + param(); + } + } + } + setState(75); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ParamContext extends ParserRuleContext { + public TerminalNode TAG_NAME() { return getToken(ShortCodeParser.TAG_NAME, 0); } + public TerminalNode EQUALS() { return getToken(ShortCodeParser.EQUALS, 0); } + public ValueContext value() { + return getRuleContext(ValueContext.class,0); + } + public ParamContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_param; } + } + + public final ParamContext param() throws RecognitionException { + ParamContext _localctx = new ParamContext(_ctx, getState()); + enterRule(_localctx, 12, RULE_param); + try { + enterOuterAlt(_localctx, 1); + { + setState(76); + match(TAG_NAME); + setState(77); + match(EQUALS); + setState(78); + value(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ValueContext extends ParserRuleContext { + public TerminalNode TAG_STRING() { return getToken(ShortCodeParser.TAG_STRING, 0); } + public TerminalNode TAG_NUMBER() { return getToken(ShortCodeParser.TAG_NUMBER, 0); } + public ValueContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_value; } + } + + public final ValueContext value() throws RecognitionException { + ValueContext _localctx = new ValueContext(_ctx, getState()); + enterRule(_localctx, 14, RULE_value); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(80); + _la = _input.LA(1); + if ( !(_la==TAG_STRING || _la==TAG_NUMBER) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class ContentContext extends ParserRuleContext { + public List SPACE() { return getTokens(ShortCodeParser.SPACE); } + public TerminalNode SPACE(int i) { + return getToken(ShortCodeParser.SPACE, i); + } + public List DASH() { return getTokens(ShortCodeParser.DASH); } + public TerminalNode DASH(int i) { + return getToken(ShortCodeParser.DASH, i); + } + public List SINGLE_OPEN_BRAKET() { return getTokens(ShortCodeParser.SINGLE_OPEN_BRAKET); } + public TerminalNode SINGLE_OPEN_BRAKET(int i) { + return getToken(ShortCodeParser.SINGLE_OPEN_BRAKET, i); + } + public ContentContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_content; } + } + + public final ContentContext content() throws RecognitionException { + ContentContext _localctx = new ContentContext(_ctx, getState()); + enterRule(_localctx, 16, RULE_content); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(85); + _errHandler.sync(this); + _alt = 1; + do { + switch (_alt) { + case 1: + { + setState(85); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) { + case 1: + { + setState(82); + _la = _input.LA(1); + if ( _la <= 0 || (_la==SINGLE_OPEN_BRAKET) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + break; + case 2: + { + setState(83); + match(SPACE); + } + break; + case 3: + { + setState(84); + match(DASH); + } + break; + } + } + break; + default: + throw new NoViableAltException(this); + } + setState(87); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,12,_ctx); + } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + @SuppressWarnings("CheckReturnValue") + public static class TextContext extends ParserRuleContext { + public List SPACE() { return getTokens(ShortCodeParser.SPACE); } + public TerminalNode SPACE(int i) { + return getToken(ShortCodeParser.SPACE, i); + } + public List SINGLE_OPEN_BRAKET() { return getTokens(ShortCodeParser.SINGLE_OPEN_BRAKET); } + public TerminalNode SINGLE_OPEN_BRAKET(int i) { + return getToken(ShortCodeParser.SINGLE_OPEN_BRAKET, i); + } + public TextContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_text; } + } + + public final TextContext text() throws RecognitionException { + TextContext _localctx = new TextContext(_ctx, getState()); + enterRule(_localctx, 18, RULE_text); + int _la; + try { + int _alt; + enterOuterAlt(_localctx, 1); + { + setState(91); + _errHandler.sync(this); + _alt = 1; + do { + switch (_alt) { + case 1: + { + setState(91); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,13,_ctx) ) { + case 1: + { + setState(89); + _la = _input.LA(1); + if ( _la <= 0 || (_la==SINGLE_OPEN_BRAKET) ) { + _errHandler.recoverInline(this); + } + else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); + } + } + break; + case 2: + { + setState(90); + match(SPACE); + } + break; + } + } + break; + default: + throw new NoViableAltException(this); + } + setState(93); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,14,_ctx); + } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static final String _serializedATN = + "\u0004\u0001\f`\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+ + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+ + "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+ + "\b\u0007\b\u0002\t\u0007\t\u0001\u0000\u0001\u0000\u0004\u0000\u0017\b"+ + "\u0000\u000b\u0000\f\u0000\u0018\u0001\u0000\u0001\u0000\u0001\u0001\u0001"+ + "\u0001\u0003\u0001\u001f\b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003"+ + "\u0001$\b\u0001\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u0002)\b\u0002"+ + "\u0001\u0002\u0003\u0002,\b\u0002\u0001\u0002\u0001\u0002\u0001\u0003"+ + "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0004"+ + "\u0003\u00047\b\u0004\u0001\u0004\u0003\u0004:\b\u0004\u0001\u0004\u0003"+ + "\u0004=\b\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0004"+ + "\u0005C\b\u0005\u000b\u0005\f\u0005D\u0001\u0005\u0005\u0005H\b\u0005"+ + "\n\u0005\f\u0005K\t\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+ + "\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001\b\u0004\bV\b\b\u000b\b"+ + "\f\bW\u0001\t\u0001\t\u0004\t\\\b\t\u000b\t\f\t]\u0001\t\u0000\u0000\n"+ + "\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012\u0000\u0002\u0001\u0000"+ + "\n\u000b\u0001\u0000\u0004\u0004e\u0000\u0016\u0001\u0000\u0000\u0000"+ + "\u0002#\u0001\u0000\u0000\u0000\u0004%\u0001\u0000\u0000\u0000\u0006/"+ + "\u0001\u0000\u0000\u0000\b3\u0001\u0000\u0000\u0000\n@\u0001\u0000\u0000"+ + "\u0000\fL\u0001\u0000\u0000\u0000\u000eP\u0001\u0000\u0000\u0000\u0010"+ + "U\u0001\u0000\u0000\u0000\u0012[\u0001\u0000\u0000\u0000\u0014\u0017\u0003"+ + "\u0002\u0001\u0000\u0015\u0017\u0003\u0012\t\u0000\u0016\u0014\u0001\u0000"+ + "\u0000\u0000\u0016\u0015\u0001\u0000\u0000\u0000\u0017\u0018\u0001\u0000"+ + "\u0000\u0000\u0018\u0016\u0001\u0000\u0000\u0000\u0018\u0019\u0001\u0000"+ + "\u0000\u0000\u0019\u001a\u0001\u0000\u0000\u0000\u001a\u001b\u0005\u0000"+ + "\u0000\u0001\u001b\u0001\u0001\u0000\u0000\u0000\u001c\u001e\u0003\u0004"+ + "\u0002\u0000\u001d\u001f\u0003\u0010\b\u0000\u001e\u001d\u0001\u0000\u0000"+ + "\u0000\u001e\u001f\u0001\u0000\u0000\u0000\u001f \u0001\u0000\u0000\u0000"+ + " !\u0003\u0006\u0003\u0000!$\u0001\u0000\u0000\u0000\"$\u0003\b\u0004"+ + "\u0000#\u001c\u0001\u0000\u0000\u0000#\"\u0001\u0000\u0000\u0000$\u0003"+ + "\u0001\u0000\u0000\u0000%&\u0005\u0001\u0000\u0000&(\u0005\t\u0000\u0000"+ + "\')\u0005\f\u0000\u0000(\'\u0001\u0000\u0000\u0000()\u0001\u0000\u0000"+ + "\u0000)+\u0001\u0000\u0000\u0000*,\u0003\n\u0005\u0000+*\u0001\u0000\u0000"+ + "\u0000+,\u0001\u0000\u0000\u0000,-\u0001\u0000\u0000\u0000-.\u0005\u0006"+ + "\u0000\u0000.\u0005\u0001\u0000\u0000\u0000/0\u0005\b\u0000\u000001\u0005"+ + "\t\u0000\u000012\u0005\u0006\u0000\u00002\u0007\u0001\u0000\u0000\u0000"+ + "34\u0005\u0001\u0000\u000046\u0005\t\u0000\u000057\u0005\f\u0000\u0000"+ + "65\u0001\u0000\u0000\u000067\u0001\u0000\u0000\u000079\u0001\u0000\u0000"+ + "\u00008:\u0003\n\u0005\u000098\u0001\u0000\u0000\u00009:\u0001\u0000\u0000"+ + "\u0000:<\u0001\u0000\u0000\u0000;=\u0005\f\u0000\u0000<;\u0001\u0000\u0000"+ + "\u0000<=\u0001\u0000\u0000\u0000=>\u0001\u0000\u0000\u0000>?\u0005\u0007"+ + "\u0000\u0000?\t\u0001\u0000\u0000\u0000@I\u0003\f\u0006\u0000AC\u0005"+ + "\f\u0000\u0000BA\u0001\u0000\u0000\u0000CD\u0001\u0000\u0000\u0000DB\u0001"+ + "\u0000\u0000\u0000DE\u0001\u0000\u0000\u0000EF\u0001\u0000\u0000\u0000"+ + "FH\u0003\f\u0006\u0000GB\u0001\u0000\u0000\u0000HK\u0001\u0000\u0000\u0000"+ + "IG\u0001\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000J\u000b\u0001\u0000"+ + "\u0000\u0000KI\u0001\u0000\u0000\u0000LM\u0005\t\u0000\u0000MN\u0005\u0002"+ + "\u0000\u0000NO\u0003\u000e\u0007\u0000O\r\u0001\u0000\u0000\u0000PQ\u0007"+ + "\u0000\u0000\u0000Q\u000f\u0001\u0000\u0000\u0000RV\b\u0001\u0000\u0000"+ + "SV\u0005\u0003\u0000\u0000TV\u0005\u0005\u0000\u0000UR\u0001\u0000\u0000"+ + "\u0000US\u0001\u0000\u0000\u0000UT\u0001\u0000\u0000\u0000VW\u0001\u0000"+ + "\u0000\u0000WU\u0001\u0000\u0000\u0000WX\u0001\u0000\u0000\u0000X\u0011"+ + "\u0001\u0000\u0000\u0000Y\\\b\u0001\u0000\u0000Z\\\u0005\u0003\u0000\u0000"+ + "[Y\u0001\u0000\u0000\u0000[Z\u0001\u0000\u0000\u0000\\]\u0001\u0000\u0000"+ + "\u0000][\u0001\u0000\u0000\u0000]^\u0001\u0000\u0000\u0000^\u0013\u0001"+ + "\u0000\u0000\u0000\u000f\u0016\u0018\u001e#(+69 Date: Thu, 10 Oct 2024 16:37:24 +0200 Subject: [PATCH 07/15] update antlr grammar --- .../condation/cms/content/shortcodes/ShortCodeParser.g4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 index 771d20471..5461ead93 100644 --- a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 @@ -24,4 +24,9 @@ param: TAG_NAME EQUALS value ; value: TAG_STRING | TAG_NUMBER; content: (~SINGLE_OPEN_BRAKET | SPACE | DASH)+ ; -text: (.)+? { _input.LA(1) != '[' || _input.LA(2) != '[' }? ; +text2: + (.)+? { _input.LA(1) != '[' || _input.LA(2) != '[' }? + ; +text + : (~'[' | ('[' { _input.LA(1) != '[' }?))+ + ; From 010c30c83128af107c461cda57bcd1a23d796b7d Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 16:37:34 +0200 Subject: [PATCH 08/15] update antlr grammar --- .../com/condation/cms/content/shortcodes/ShortCodeParser.g4 | 3 --- 1 file changed, 3 deletions(-) diff --git a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 index 5461ead93..937b237be 100644 --- a/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 +++ b/cms-sandbox/tests/src/main/resources/com/condation/cms/content/shortcodes/ShortCodeParser.g4 @@ -27,6 +27,3 @@ content: (~SINGLE_OPEN_BRAKET | SPACE | DASH)+ ; text2: (.)+? { _input.LA(1) != '[' || _input.LA(2) != '[' }? ; -text - : (~'[' | ('[' { _input.LA(1) != '[' }?))+ - ; From 1a5592ed7a1b7c0664e281998978c29c9cce2edd Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Thu, 10 Oct 2024 21:11:45 +0200 Subject: [PATCH 09/15] new short code tag parser with expression support --- .../content/shortcodes/ShortCodeParser.java | 30 +--- .../cms/content/shortcodes/ShortCodes.java | 19 +-- .../cms/content/shortcodes/TagMap.java | 53 ++++++ .../cms/content/shortcodes/TagParser.java | 157 ++++++++++++++++++ .../cms/content/ContentBaseTest.java | 14 +- .../ShortCodeParserExpressionsTest.java | 55 ------ .../content/shortcodes/ShortCodesTest.java | 18 +- .../cms/content/shortcodes/TagParserTest.java | 123 ++++++++++++++ ...eeMarkerShortCodeTemplateFunctionTest.java | 2 +- .../PebbleShortCodeTemplateFunctionTest.java | 2 +- ...hymeleafShortCodeTemplateFunctionTest.java | 2 +- .../cms/content/shortcodes/TagParser.java | 151 +++++++++++++++++ .../cms/content/shortcodes/TagParserTest.java | 103 ++++++++++++ .../cms/request/RequestContextFactory.java | 6 +- .../cms/server/configs/SiteModule.java | 6 +- .../java/com/condation/cms/TestHelper.java | 3 +- 16 files changed, 631 insertions(+), 113 deletions(-) create mode 100644 cms-content/src/main/java/com/condation/cms/content/shortcodes/TagMap.java create mode 100644 cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java delete mode 100644 cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java create mode 100644 cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java create mode 100644 cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/TagParser.java create mode 100644 cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java index f5969d409..be7c57566 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodeParser.java @@ -38,15 +38,12 @@ @Slf4j public class ShortCodeParser { - JexlEngine engine; - public static final String SHORTCODE_REGEX = "\\[\\[(\\w+)([^\\]]*)\\]\\](.*?)\\[\\[\\/\\1\\]\\]|\\[\\[(\\w+)([^\\]]*)\\s*\\/\\]\\]"; public static final Pattern SHORTCODE_PATTERN = Pattern.compile(SHORTCODE_REGEX, Pattern.DOTALL); //public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=(\"[^\"]*\"|'[^']*')"); public static final Pattern PARAM_PATTERN = Pattern.compile("(\\w+)=((\"[^\"]*\"|'[^']*'|\\[[^\\]]*\\]))"); - public ShortCodeParser(JexlEngine engine) { - this.engine = engine; + public ShortCodeParser() { } public List parseShortcodes(String text) { @@ -68,10 +65,7 @@ public List parseShortcodes(String text) { String key = paramMatcher.group(1); String value = paramMatcher.group(2); value = value.substring(1, value.length() - 1); // Entfernt die Anführungszeichen oder Klammern bei Arrays - - // Prüfe, ob es ein Array ist und nutze JEXL zur Auswertung - Object evaluatedValue = evaluateExpression(value); - match.getParameters().put(key, evaluatedValue); + match.getParameters().put(key, value); } shortcodes.add(match); @@ -80,26 +74,6 @@ public List parseShortcodes(String text) { return shortcodes; } - private Object evaluateExpression(String value) { - try { - JexlExpression expression = engine.createExpression(value); - JexlContext context = new MapContext(); - var parsedValue = expression.evaluate(context); - if (parsedValue.getClass().isArray()) { - int length = Array.getLength(parsedValue); - List list = new ArrayList<>(length); - for (int i = 0; i < length; i++) { - list.add(Array.get(parsedValue, i)); // Holen des Elements am Index i - } - return list; - } else { - return parsedValue; - } - } catch (Exception e) { - return value; - } - } - public String replace(String content, Codes codes) { StringBuilder newContent = new StringBuilder(); int lastPosition = 0; diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index 27a8143dc..9d71e70c7 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -28,7 +28,6 @@ import java.util.function.Function; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.jexl3.JexlEngine; /** * @@ -38,21 +37,21 @@ @RequiredArgsConstructor public class ShortCodes { - private final ShortCodeParser.Codes codes; - private final ShortCodeParser parser; + private final TagMap tagMap; + private final TagParser parser; - public ShortCodes (Map> codes, ShortCodeParser shortCodeParser) { - this.parser = shortCodeParser; - this.codes = new ShortCodeParser.Codes(); - this.codes.addAll(codes); + public ShortCodes (Map> codes, TagParser tagParser) { + this.parser = tagParser; + this.tagMap = new TagMap(); + this.tagMap.putAll(codes); } public String replace (final String content) { - return parser.replace(content, codes); + return parser.parse(content, tagMap); } public String execute (String name, Map parameters) { - if (codes.get(name) == null) { + if (!tagMap.has(name)) { return ""; } try { @@ -62,7 +61,7 @@ public String execute (String name, Map parameters) { } else { params = new Parameter(); } - return codes.get(name).apply(params); + return tagMap.get(name).apply(params); } catch (Exception e) { log.error("",e); } diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagMap.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagMap.java new file mode 100644 index 000000000..e503f067a --- /dev/null +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagMap.java @@ -0,0 +1,53 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * cms-content + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.model.Parameter; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * + * @author t.marx + */ +public class TagMap { + + private final Map> tags = new HashMap<>(); + + public void put(String codeName, Function function) { + tags.put(codeName, function); + } + + public void putAll(Map> tags) { + this.tags.putAll(tags); + } + + public boolean has(String codeName) { + return tags.containsKey(codeName); + } + + public Function get(String codeName) { + return tags.getOrDefault(codeName, (params) -> ""); + } +} diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java new file mode 100644 index 000000000..82dec3038 --- /dev/null +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java @@ -0,0 +1,157 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * cms-content + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.model.Parameter; +import java.util.*; +import java.util.function.Function; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.MapContext; + +public class TagParser { + + private final JexlEngine engine; + + public TagParser(JexlEngine engine) { + this.engine = engine; + } + + public String parse(String text, TagMap tagHandlers) { + StringBuilder result = new StringBuilder(); + int i = 0; + while (i < text.length()) { + if (text.charAt(i) == '[' && i + 1 < text.length() && text.charAt(i + 1) == '[') { + int tagStart = i; + i = parseTag(text, i, result, tagHandlers); + if (i == tagStart) { // Kein gültiger Tag gefunden, füge '[[' hinzu. + result.append("[["); + i += 2; + } + } else { + result.append(text.charAt(i)); + i++; + } + } + return result.toString(); + } + + private int parseTag(String text, int index, StringBuilder result, TagMap tagHandlers) { + int endTagIndex = findTagEnd(text, index); + if (endTagIndex == -1) { + return index; // Kein schließendes ']]' gefunden + } + + String tagContent = text.substring(index + 2, endTagIndex).trim(); + boolean isSelfClosing = tagContent.endsWith("/"); + + if (isSelfClosing) { + tagContent = tagContent.substring(0, tagContent.length() - 1).trim(); + } + + int spaceIndex = tagContent.indexOf(' '); + String tagName = spaceIndex == -1 ? tagContent : tagContent.substring(0, spaceIndex); + Parameter attributes = spaceIndex == -1 + ? new Parameter() + : parseAttributes(tagContent.substring(spaceIndex + 1)); + + int closingTagIndex = -1; + if (!isSelfClosing) { + closingTagIndex = text.indexOf("[[/" + tagName + "]]", endTagIndex + 2); + if (closingTagIndex != -1) { + // Verarbeite den Content für geöffnete und geschlossene Tags + String content = text.substring(endTagIndex + 2, closingTagIndex); + attributes.put("_content", content); + endTagIndex = closingTagIndex + ("[[/" + tagName + "]]").length() - 2; + } + } + + if (tagHandlers.has(tagName)) { + Function handler = tagHandlers.get(tagName); + result.append(handler.apply(attributes)); + // Setze den Index auf das Zeichen direkt nach dem schließenden Tag oder schließenden Tag mit Content + return endTagIndex + 2; + } + + return index; // Tag nicht erkannt + } + + private int findTagEnd(String text, int startIndex) { + for (int i = startIndex; i < text.length() - 1; i++) { + if (text.charAt(i) == ']' && text.charAt(i + 1) == ']') { + return i; + } + } + return -1; // Kein schließendes ']]' gefunden + } + + private Parameter parseAttributes(String attributesString) { + Parameter attributes = new Parameter(); + StringBuilder key = new StringBuilder(); + StringBuilder value = new StringBuilder(); + boolean inQuotes = false; + boolean readingKey = true; + + for (int i = 0; i < attributesString.length(); i++) { + char c = attributesString.charAt(i); + if (c == '"' || c == '\'') { + inQuotes = !inQuotes; + } else if (!inQuotes && (c == '=' || c == ' ')) { + if (readingKey) { + readingKey = false; + } else { + attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + key.setLength(0); + value.setLength(0); + readingKey = true; + } + } else { + if (readingKey) { + key.append(c); + } else { + value.append(c); + } + } + } + + // Letztes Attribut verarbeiten + if (key.length() > 0 && value.length() > 0) { + attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + } + + return attributes; + } + + private Object parseValue(String value) { + if (value.matches("\\d+")) { + return Integer.valueOf(value); + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return Boolean.valueOf(value); + } else if (value.startsWith("${") && value.endsWith("}")) { + String expressionString = value.substring(2, value.length() - 1); + + var expression = engine.createExpression(expressionString); + return expression.evaluate(new MapContext()); + } + return value; + } +} diff --git a/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java index 883780f7f..a86307a41 100644 --- a/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java @@ -23,6 +23,7 @@ */ import com.condation.cms.content.shortcodes.ShortCodeParser; +import com.condation.cms.content.shortcodes.TagParser; import org.apache.commons.jexl3.JexlBuilder; /** @@ -33,10 +34,21 @@ public abstract class ContentBaseTest { private ShortCodeParser shortCodeParser; + private TagParser tagParser; + + public TagParser getTagParser () { + if (tagParser == null) { + tagParser = new TagParser( + new JexlBuilder().cache(512).strict(true).silent(false).create() + ); + } + + return tagParser; + } + public ShortCodeParser getShortCodeParser () { if (shortCodeParser == null) { shortCodeParser = new ShortCodeParser( - new JexlBuilder().cache(512).strict(true).silent(false).create() ); } diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java deleted file mode 100644 index 8e7855238..000000000 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodeParserExpressionsTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.condation.cms.content.shortcodes; - -/*- - * #%L - * cms-content - * %% - * Copyright (C) 2023 - 2024 CondationCMS - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - - -import com.condation.cms.content.ContentBaseTest; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -import java.util.List; - -public class ShortCodeParserExpressionsTest extends ContentBaseTest { - - @Test - public void test_numbers() { - String text = "[[code param1=\"12\" param2=\"12.5\"]][[/code]]."; - List shortcodes = getShortCodeParser().parseShortcodes(text); - - assertEquals(1, shortcodes.size()); - - var shortcode = shortcodes.get(0); - assertEquals(12, shortcode.getParameters().get("param1")); - assertEquals(12.5, shortcode.getParameters().get("param2")); - } - - @Test - public void test_list() { - String text = "[[code param1=\"[12, 13, 14]\" param2=\"['12', '13', '14]\"]][[/code]]."; - List shortcodes = getShortCodeParser().parseShortcodes(text); - - var shortcode = shortcodes.get(0); - assertEquals(List.of(12, 13, 14), shortcode.getParameters().get("param1")); - assertEquals(List.of("12", "13", "14"), shortcode.getParameters().get("param2")); - } -} diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java index aea453676..f48142f27 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -52,15 +52,15 @@ public void init () { tags.put( "mark", - params -> "%s".formatted(params.get("content")) + params -> "%s".formatted(params.get("_content")) ); tags.put( "mark2", - params -> "%s".formatted(params.get("class"), params.get("content")) + params -> "%s".formatted(params.get("class"), params.get("_content")) ); - shortCodes = new ShortCodes(tags, getShortCodeParser()); + shortCodes = new ShortCodes(tags, getTagParser()); } @@ -106,18 +106,18 @@ void complexTest () { @Test void unknown_tag () { var result = shortCodes.replace("before [[vimeo id='TEST' /]] after"); - Assertions.assertThat(result).isEqualToIgnoringWhitespace("before after"); + Assertions.assertThat(result).isEqualToIgnoringWhitespace("before [[vimeo id='TEST' /]] after"); } @Test void hello_from () { - var result = shortCodes.replace("[[hello_from name='Thorsten',from='Bochum' /]]"); + var result = shortCodes.replace("[[hello_from name=\"Thorsten\" from=\"Bochum\" /]]"); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); - result = shortCodes.replace("[[hello_from name='Thorsten',from='Bochum' /]]"); + result = shortCodes.replace("[[hello_from name='Thorsten' from='Bochum' /]]"); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); - result = shortCodes.replace("[[hello_from name='Thorsten', from='Bochum' /]]"); + result = shortCodes.replace("[[hello_from name='Thorsten' from='Bochum' /]]"); Assertions.assertThat(result).isEqualTo("

Thorsten

from Bochum

"); } @@ -162,7 +162,7 @@ void long_complex () { @Test void multiple_hello () { var input = """ - [[hello_from name='Thorsten',from='Bochum']][[/hello_from]][[hello_from name='Thorsten',from='Bochum']][[/hello_from]] + [[hello_from name='Thorsten' from='Bochum']][[/hello_from]][[hello_from name='Thorsten' from='Bochum']][[/hello_from]] """; var expected = """

Thorsten

from Bochum

Thorsten

from Bochum

@@ -171,7 +171,7 @@ void multiple_hello () { Assertions.assertThat(result).isEqualTo(expected); input = """ - [[hello_from name='Thorsten',from='Bochum'/]][[hello_from name='Thorsten',from='Bochum'/]] + [[hello_from name='Thorsten' from='Bochum'/]][[hello_from name='Thorsten' from='Bochum'/]] """; expected = """

Thorsten

from Bochum

Thorsten

from Bochum

diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java new file mode 100644 index 000000000..41142f52b --- /dev/null +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java @@ -0,0 +1,123 @@ +package com.condation.cms.content.shortcodes; + +/*- + * #%L + * cms-content + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.apache.commons.jexl3.JexlBuilder; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; + +/** + * + * @author t.marx + */ +public class TagParserTest { + + TagParser tagParser; + + TagMap tagMap; + + @BeforeEach + void setup() { + tagMap = new TagMap(); + tagMap.put("code", params -> { + // Verarbeitung der Parameter hier + return "Ausgabe des Shortcodes"; + }); + tagMap.put("content", params -> { + return (String)params.get("_content"); + }); + + tagMap.put("exp", params -> { + return "expression: " + params.get("value"); + }); + + tagMap.put("param", params -> { + return "param: " + params.get("param1"); + }); + + this.tagParser = new TagParser(new JexlBuilder().create()); + } + + @Test + public void no_shortcode() { + String result = tagParser.parse("Dein Shortcode-Text hier", tagMap); + Assertions.assertThat(result).isEqualTo("Dein Shortcode-Text hier"); + } + + @Test + public void self_closing_tag() { + String result = tagParser.parse("[[code/]]", tagMap); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void self_closing_tag_with_space() { + String result = tagParser.parse("[[code /]]", tagMap); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void end_closing_tag() { + String result = tagParser.parse("[[code]][[/code]]", tagMap); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void tag_with_content() { + String result = tagParser.parse("[[content]]Hello CondationCMS[[/content]]", tagMap); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS"); + } + + @Test + public void expressions() { + String result = tagParser.parse("[[exp value=\"${5+4}\"/]]", tagMap); + Assertions.assertThat(result).isEqualTo("expression: 9"); + } + + @Test + public void parameters_string() { + String result = tagParser.parse("[[param param1=\"5\"/]]", tagMap); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_number() { + String result = tagParser.parse("[[param param1=5 /]]", tagMap); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_with_content() { + String result = tagParser.parse("[[param param1=\"5\"]]Hello[[/param]]", tagMap); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void shortCode_in_text() { + String result = tagParser.parse("Hello [[content]]CondationCMS[[/content]]!", tagMap); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS!"); + } + +} diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java index 3d5fef036..5a47cf8c6 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/FreeMarkerShortCodeTemplateFunctionTest.java @@ -59,7 +59,7 @@ public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - ), getShortCodeParser()); + ), getTagParser()); } @Test diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java index 108e09e2d..250c856f7 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/PebbleShortCodeTemplateFunctionTest.java @@ -59,7 +59,7 @@ public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - ), getShortCodeParser()); + ), getTagParser()); } @Test diff --git a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java index 1427a0b2b..31f902ae1 100644 --- a/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/template/shortcode/ThymeleafShortCodeTemplateFunctionTest.java @@ -60,7 +60,7 @@ public void setupShortCodes() { shortCodes = new ShortCodes(Map.of( "echo", (params) -> "Hello world", "greet", (params) -> "Hello " + params.get("name") - ), getShortCodeParser()); + ), getTagParser()); } @Test diff --git a/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/TagParser.java b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/TagParser.java new file mode 100644 index 000000000..65d52c578 --- /dev/null +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/TagParser.java @@ -0,0 +1,151 @@ +package com.condation.cms.content.shortcodes; + +import java.util.*; +import java.util.function.Function; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.MapContext; + +public class TagParser { + + private final Codes tagHandlers; + private final JexlEngine engine; + + public TagParser(Codes tagHandlers, JexlEngine engine) { + this.tagHandlers = tagHandlers; + this.engine = engine; + } + + public String parse(String text) { + StringBuilder result = new StringBuilder(); + int i = 0; + while (i < text.length()) { + if (text.charAt(i) == '[' && i + 1 < text.length() && text.charAt(i + 1) == '[') { + int tagStart = i; + i = parseTag(text, i, result); + if (i == tagStart) { // Kein gültiger Tag gefunden, füge '[[' hinzu. + result.append("[["); + i += 2; + } + } else { + result.append(text.charAt(i)); + i++; + } + } + return result.toString(); + } + + private int parseTag(String text, int index, StringBuilder result) { + int endTagIndex = findTagEnd(text, index); + if (endTagIndex == -1) { + return index; // Kein schließendes ']]' gefunden + } + + String tagContent = text.substring(index + 2, endTagIndex).trim(); + boolean isSelfClosing = tagContent.endsWith("/"); + + if (isSelfClosing) { + tagContent = tagContent.substring(0, tagContent.length() - 1).trim(); + } + + int spaceIndex = tagContent.indexOf(' '); + String tagName = spaceIndex == -1 ? tagContent : tagContent.substring(0, spaceIndex); + Map attributes = spaceIndex == -1 ? new HashMap<>() : parseAttributes(tagContent.substring(spaceIndex + 1)); + + int closingTagIndex = -1; + if (!isSelfClosing) { + closingTagIndex = text.indexOf("[[/" + tagName + "]]", endTagIndex + 2); + if (closingTagIndex != -1) { + // Verarbeite den Content für geöffnete und geschlossene Tags + String content = text.substring(endTagIndex + 2, closingTagIndex); + attributes.put("_content", content); + endTagIndex = closingTagIndex + ("[[/" + tagName + "]]").length() - 2; + } + } + + if (tagHandlers.hasCode(tagName)) { + Function, String> handler = tagHandlers.get(tagName); + result.append(handler.apply(attributes)); + // Setze den Index auf das Zeichen direkt nach dem schließenden Tag oder schließenden Tag mit Content + return endTagIndex + 2; + } + + return index; // Tag nicht erkannt + } + + private int findTagEnd(String text, int startIndex) { + for (int i = startIndex; i < text.length() - 1; i++) { + if (text.charAt(i) == ']' && text.charAt(i + 1) == ']') { + return i; + } + } + return -1; // Kein schließendes ']]' gefunden + } + + private Map parseAttributes(String attributesString) { + Map attributes = new HashMap<>(); + StringBuilder key = new StringBuilder(); + StringBuilder value = new StringBuilder(); + boolean inQuotes = false; + boolean readingKey = true; + + for (int i = 0; i < attributesString.length(); i++) { + char c = attributesString.charAt(i); + if (c == '"' || c == '\'') { + inQuotes = !inQuotes; + } else if (!inQuotes && (c == '=' || c == ' ')) { + if (readingKey) { + readingKey = false; + } else { + attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + key.setLength(0); + value.setLength(0); + readingKey = true; + } + } else { + if (readingKey) { + key.append(c); + } else { + value.append(c); + } + } + } + + // Letztes Attribut verarbeiten + if (key.length() > 0 && value.length() > 0) { + attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + } + + return attributes; + } + + private Object parseValue(String value) { + if (value.matches("\\d+")) { + return Integer.valueOf(value); + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return Boolean.valueOf(value); + } else if (value.startsWith("${") && value.endsWith("}")) { + String expressionString = value.substring(2, value.length() - 1); + + var expression = engine.createExpression(expressionString); + return expression.evaluate(new MapContext()); + } + return value; + } + + public static class Codes { + + private final Map, String>> codes = new HashMap<>(); + + public void add(String codeName, Function, String> function) { + codes.put(codeName, function); + } + + public boolean hasCode(String codeName) { + return codes.containsKey(codeName); + } + + public Function, String> get(String codeName) { + return codes.getOrDefault(codeName, (params) -> ""); + } + } +} diff --git a/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java new file mode 100644 index 000000000..ab7830f4c --- /dev/null +++ b/cms-sandbox/tests/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java @@ -0,0 +1,103 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit this template + */ +package com.condation.cms.content.shortcodes; + +import org.apache.commons.jexl3.JexlBuilder; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +/** + * + * @author t.marx + */ +public class TagParserTest { + + + TagParser tagParser; + + @BeforeEach + void setup() { + TagParser.Codes codes = new TagParser.Codes(); + codes.add("code", params -> { + // Verarbeitung der Parameter hier + return "Ausgabe des Shortcodes"; + }); + codes.add("content", params -> { + return (String)params.get("_content"); + }); + + codes.add("exp", params -> { + return "expression: " + params.get("value"); + }); + + codes.add("param", params -> { + return "param: " + params.get("param1"); + }); + + this.tagParser = new TagParser(codes, new JexlBuilder().create()); + } + + @Test + public void no_shortcode() { + String result = tagParser.parse("Dein Shortcode-Text hier"); + Assertions.assertThat(result).isEqualTo("Dein Shortcode-Text hier"); + } + + @Test + public void self_closing_tag() { + String result = tagParser.parse("[[code/]]"); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void self_closing_tag_with_space() { + String result = tagParser.parse("[[code /]]"); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void end_closing_tag() { + String result = tagParser.parse("[[code]][[/code]]"); + Assertions.assertThat(result).isEqualTo("Ausgabe des Shortcodes"); + } + + @Test + public void tag_with_content() { + String result = tagParser.parse("[[content]]Hello CondationCMS[[/content]]"); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS"); + } + + @Test + public void expressions() { + String result = tagParser.parse("[[exp value=\"${5+4}\"/]]"); + Assertions.assertThat(result).isEqualTo("expression: 9"); + } + + @Test + public void parameters_string() { + String result = tagParser.parse("[[param param1=\"5\"/]]"); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_number() { + String result = tagParser.parse("[[param param1=5 /]]"); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void parameters_with_content() { + String result = tagParser.parse("[[param param1=\"5\"]]Hello[[/param]]"); + Assertions.assertThat(result).isEqualTo("param: 5"); + } + + @Test + public void shortCode_in_text() { + String result = tagParser.parse("Hello [[content]]CondationCMS[[/content]]!"); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS!"); + } + +} diff --git a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java index 05450767c..ff53c5e82 100644 --- a/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java +++ b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java @@ -53,8 +53,8 @@ import com.condation.cms.api.utils.HTTPUtil; import com.condation.cms.api.utils.RequestUtil; import com.condation.cms.content.RenderContext; -import com.condation.cms.content.shortcodes.ShortCodeParser; import com.condation.cms.content.shortcodes.ShortCodes; +import com.condation.cms.content.shortcodes.TagParser; import com.condation.cms.extensions.ExtensionManager; import com.condation.cms.extensions.hooks.ContentHooks; import com.condation.cms.extensions.hooks.DBHooks; @@ -161,7 +161,7 @@ private ShortCodes initShortCodes(RequestContext requestContext) { var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes); - var parser = injector.getInstance(ShortCodeParser.class); + var parser = injector.getInstance(TagParser.class); return new ShortCodes(wrapper.getShortCodes(), parser); } @@ -259,7 +259,7 @@ private ShortCodes createShortCodes(RequestContext requestContext) { .forEach(extension -> codes.putAll(extension.shortCodes())); var wrapper = requestContext.get(ContentHooks.class).getShortCodes(codes); - var parser = injector.getInstance(ShortCodeParser.class); + var parser = injector.getInstance(TagParser.class); return new ShortCodes(wrapper.getShortCodes(), parser); } diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java index 4d27f2e35..c5e7cc0b6 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java @@ -59,7 +59,7 @@ import com.condation.cms.content.DefaultContentRenderer; import com.condation.cms.content.TaxonomyResolver; import com.condation.cms.content.ViewResolver; -import com.condation.cms.content.shortcodes.ShortCodeParser; +import com.condation.cms.content.shortcodes.TagParser; import com.condation.cms.extensions.ExtensionManager; import com.condation.cms.filesystem.FileDB; import com.condation.cms.filesystem.MetaData; @@ -113,7 +113,7 @@ public ContentNodeMapper contentNodeMapper (DB db, ContentParser contentParser) @Provides @Singleton - public ShortCodeParser shortCodeParser (Configuration configuration) { + public TagParser tagParser (Configuration configuration) { var engine = new JexlBuilder() .strict(true) .cache(512); @@ -126,7 +126,7 @@ public ShortCodeParser shortCodeParser (Configuration configuration) { engine.silent(true); } - return new ShortCodeParser(engine.create()); + return new TagParser(engine.create()); } @Provides diff --git a/cms-server/src/test/java/com/condation/cms/TestHelper.java b/cms-server/src/test/java/com/condation/cms/TestHelper.java index 11f39eb21..274ed3af2 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -45,6 +45,7 @@ import com.condation.cms.content.RenderContext; import com.condation.cms.content.shortcodes.ShortCodeParser; import com.condation.cms.content.shortcodes.ShortCodes; +import com.condation.cms.content.shortcodes.TagParser; import com.condation.cms.extensions.hooks.DBHooks; import com.condation.cms.extensions.hooks.TemplateHooks; import com.condation.cms.extensions.request.RequestExtensions; @@ -73,7 +74,7 @@ public static RequestContext requestContext(String uri) { var markdownRenderer = TestHelper.getRenderer(); RequestContext context = new RequestContext(); - var shortCodeParser = new ShortCodeParser(new JexlBuilder().create()); + var shortCodeParser = new TagParser(new JexlBuilder().create()); context.add(RequestFeature.class, new RequestFeature(uri, Map.of())); context.add(RequestExtensions.class, new RequestExtensions(null)); From 85e71458b5ebfc772c4ea853f321b0f1ece10bd3 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 08:47:32 +0200 Subject: [PATCH 10/15] fix feature test project --- .../features/extensions/{theme.extension.js => theme.hooks.js} | 0 cms-server/themes/{test => parent}/extensions/parent.extension.js | 0 cms-server/themes/{parent => test}/extensions/theme.extension.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename cms-server/hosts/features/extensions/{theme.extension.js => theme.hooks.js} (100%) rename cms-server/themes/{test => parent}/extensions/parent.extension.js (100%) rename cms-server/themes/{parent => test}/extensions/theme.extension.js (100%) diff --git a/cms-server/hosts/features/extensions/theme.extension.js b/cms-server/hosts/features/extensions/theme.hooks.js similarity index 100% rename from cms-server/hosts/features/extensions/theme.extension.js rename to cms-server/hosts/features/extensions/theme.hooks.js diff --git a/cms-server/themes/test/extensions/parent.extension.js b/cms-server/themes/parent/extensions/parent.extension.js similarity index 100% rename from cms-server/themes/test/extensions/parent.extension.js rename to cms-server/themes/parent/extensions/parent.extension.js diff --git a/cms-server/themes/parent/extensions/theme.extension.js b/cms-server/themes/test/extensions/theme.extension.js similarity index 100% rename from cms-server/themes/parent/extensions/theme.extension.js rename to cms-server/themes/test/extensions/theme.extension.js From 02ec9df09a7551638185144ed9a41d09a01a93fb Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 08:51:45 +0200 Subject: [PATCH 11/15] remove unused code example for an expression --- .../cms/content/DefaultContentRenderer.java | 12 ++---------- .../cms/content/pipeline/ContentPipeline.java | 8 -------- .../hosts/features/content/shortcodes/index.md | 2 +- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java b/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java index e40c7a57d..d0e22bf58 100644 --- a/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java +++ b/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java @@ -120,15 +120,7 @@ public String renderView(final ReadOnlyFile viewFile, final View view, final Con }); } - private Object getFeatureValueOrDefault(RequestContext context, - Class feature, Function valueFunction, Object defaultValue) { - if (context.has(feature)) { - return valueFunction.apply(context.get(feature)); - } - return defaultValue; - } - - private String renderMarkdown(final String rawContent, final RequestContext context, final TemplateEngine.Model model) { + private String renderContent(final String rawContent, final RequestContext context, final TemplateEngine.Model model) { var pipeline = ContentPipelineFactory.create(context, model); return pipeline.process(rawContent); @@ -190,7 +182,7 @@ public String render(final ReadOnlyFile contentFile, final RequestContext contex extendModel(model); model.values.put("content", - renderMarkdown(rawContent, context, model) + renderContent(rawContent, context, model) ); return templates.get().render((String) meta.get("template"), model); diff --git a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java index 852b03346..2d29e7d43 100644 --- a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java +++ b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java @@ -90,12 +90,4 @@ private String processTemplate(FilterContext context) { return context.value(); } } - - private Object getFeatureValueOrDefault(RequestContext context, - Class feature, Function valueFunction, Object defaultValue) { - if (context.has(feature)) { - return valueFunction.apply(context.get(feature)); - } - return defaultValue; - } } diff --git a/cms-server/hosts/features/content/shortcodes/index.md b/cms-server/hosts/features/content/shortcodes/index.md index 545ac4dac..bb55a5234 100644 --- a/cms-server/hosts/features/content/shortcodes/index.md +++ b/cms-server/hosts/features/content/shortcodes/index.md @@ -9,7 +9,7 @@ menu: [[hello name='Thorsten'/]] -[[name_age name='Thorsten' age="46" /]] +[[name_age name='Thorsten' age="${40+6+1}" /]] Or call a shortcode provided by the default theme From 4be934d506dd93ee85a91bc51c37f80e4ac10851 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 09:43:09 +0200 Subject: [PATCH 12/15] add render model to shortcode processing --- .../cms/content/pipeline/ContentPipeline.java | 7 +++--- .../cms/content/shortcodes/ShortCodes.java | 7 +++++- .../cms/content/shortcodes/TagParser.java | 23 +++++++++++-------- .../content/shortcodes/ShortCodesTest.java | 18 ++++++++++++++- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java index 2d29e7d43..edfa3066f 100644 --- a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java +++ b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java @@ -44,15 +44,14 @@ * @author thmar */ @Slf4j -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +@RequiredArgsConstructor public class ContentPipeline { private final HookSystem hookSystem; private final RequestContext requestContext; - private final TemplateEngine.Model model; - void init() { + protected void init() { List pipeline = requestContext.get(ConfigurationFeature.class) .configuration().get(SiteConfiguration.class) @@ -78,7 +77,7 @@ private String processMarkdown(FilterContext context) { } private String processShortCodes(FilterContext context) { - return requestContext.get(RenderContext.class).shortCodes().replace(context.value()); + return requestContext.get(RenderContext.class).shortCodes().replace(context.value(), model.values); } private String processTemplate(FilterContext context) { diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java index 9d71e70c7..33470aed2 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -24,6 +24,7 @@ import com.condation.cms.api.model.Parameter; +import java.util.Collections; import java.util.Map; import java.util.function.Function; import lombok.RequiredArgsConstructor; @@ -47,7 +48,11 @@ public ShortCodes (Map> codes, TagParser tag } public String replace (final String content) { - return parser.parse(content, tagMap); + return replace(content, Collections.emptyMap()); + } + + public String replace (final String content, Map contextModel) { + return parser.parse(content, tagMap, contextModel); } public String execute (String name, Map parameters) { diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java index 82dec3038..3cd099100 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java @@ -23,7 +23,8 @@ */ import com.condation.cms.api.model.Parameter; -import java.util.*; +import java.util.Collections; +import java.util.Map; import java.util.function.Function; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.MapContext; @@ -37,12 +38,16 @@ public TagParser(JexlEngine engine) { } public String parse(String text, TagMap tagHandlers) { + return parse(text, tagHandlers, Collections.emptyMap()); + } + + public String parse(String text, TagMap tagHandlers, Map contextModel) { StringBuilder result = new StringBuilder(); int i = 0; while (i < text.length()) { if (text.charAt(i) == '[' && i + 1 < text.length() && text.charAt(i + 1) == '[') { int tagStart = i; - i = parseTag(text, i, result, tagHandlers); + i = parseTag(text, i, result, tagHandlers, contextModel); if (i == tagStart) { // Kein gültiger Tag gefunden, füge '[[' hinzu. result.append("[["); i += 2; @@ -55,7 +60,7 @@ public String parse(String text, TagMap tagHandlers) { return result.toString(); } - private int parseTag(String text, int index, StringBuilder result, TagMap tagHandlers) { + private int parseTag(String text, int index, StringBuilder result, TagMap tagHandlers, Map contextModel) { int endTagIndex = findTagEnd(text, index); if (endTagIndex == -1) { return index; // Kein schließendes ']]' gefunden @@ -72,7 +77,7 @@ private int parseTag(String text, int index, StringBuilder result, TagMap tagHan String tagName = spaceIndex == -1 ? tagContent : tagContent.substring(0, spaceIndex); Parameter attributes = spaceIndex == -1 ? new Parameter() - : parseAttributes(tagContent.substring(spaceIndex + 1)); + : parseAttributes(tagContent.substring(spaceIndex + 1), contextModel); int closingTagIndex = -1; if (!isSelfClosing) { @@ -104,7 +109,7 @@ private int findTagEnd(String text, int startIndex) { return -1; // Kein schließendes ']]' gefunden } - private Parameter parseAttributes(String attributesString) { + private Parameter parseAttributes(String attributesString, Map contextModel) { Parameter attributes = new Parameter(); StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); @@ -119,7 +124,7 @@ private Parameter parseAttributes(String attributesString) { if (readingKey) { readingKey = false; } else { - attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + attributes.put(key.toString().trim(), parseValue(value.toString().trim(), contextModel)); key.setLength(0); value.setLength(0); readingKey = true; @@ -135,13 +140,13 @@ private Parameter parseAttributes(String attributesString) { // Letztes Attribut verarbeiten if (key.length() > 0 && value.length() > 0) { - attributes.put(key.toString().trim(), parseValue(value.toString().trim())); + attributes.put(key.toString().trim(), parseValue(value.toString().trim(), contextModel)); } return attributes; } - private Object parseValue(String value) { + private Object parseValue(String value, Map contextModel) { if (value.matches("\\d+")) { return Integer.valueOf(value); } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { @@ -150,7 +155,7 @@ private Object parseValue(String value) { String expressionString = value.substring(2, value.length() - 1); var expression = engine.createExpression(expressionString); - return expression.evaluate(new MapContext()); + return expression.evaluate(new MapContext(contextModel)); } return value; } diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java index f48142f27..33de04a80 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/ShortCodesTest.java @@ -60,6 +60,11 @@ public void init () { params -> "%s".formatted(params.get("class"), params.get("_content")) ); + tags.put( + "exp", + params -> "%s".formatted(params.get("expression")) + ); + shortCodes = new ShortCodes(tags, getTagParser()); } @@ -181,9 +186,20 @@ void multiple_hello () { } @Test - void test_mismathc() { + void test_mismach() { var result = shortCodes.replace("[[mark1 class='test-class']]Important[[/mark2]]"); Assertions.assertThat(result).isEqualTo("[[mark1 class='test-class']]Important[[/mark2]]"); } + + @Test + void test_expression() { + var result = shortCodes.replace("[[exp expression='${meta.title}' /]]", + Map.of( + "meta", Map.of("title", "CondationCMS") + ) + ); + + Assertions.assertThat(result).isEqualTo("CondationCMS"); + } } From 264edcfa37022916393f3e7333508af2bf90e74a Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 09:50:50 +0200 Subject: [PATCH 13/15] add more tests --- .../cms/content/shortcodes/TagParserTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java index 41142f52b..e1a05afb1 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java @@ -25,7 +25,6 @@ import org.apache.commons.jexl3.JexlBuilder; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; /** @@ -108,6 +107,18 @@ public void parameters_number() { Assertions.assertThat(result).isEqualTo("param: 5"); } + @Test + public void parameters_boolean_true() { + String result = tagParser.parse("[[param param1=true /]]", tagMap); + Assertions.assertThat(result).isEqualTo("param: true"); + } + + @Test + public void parameters_boolean_false() { + String result = tagParser.parse("[[param param1=false /]]", tagMap); + Assertions.assertThat(result).isEqualTo("param: false"); + } + @Test public void parameters_with_content() { String result = tagParser.parse("[[param param1=\"5\"]]Hello[[/param]]", tagMap); From 98f1c9d8e501c14492328ca566f90af651f02df6 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 12:42:22 +0200 Subject: [PATCH 14/15] update markdown renderer to use new TagParser --- .../condation/cms/auth/AuthShortCodes.java | 3 +- .../rules/block/ShortCodeBlockRule.java | 110 ++++--- .../inline/ShortCodeInlineBlockRule.java | 95 +++--- .../cms/content/shortcodes/TagParser.java | 282 ++++++++++-------- .../rules/block/ShortCodeBlockRuleTest.java | 47 +-- .../inline/ShortCodeInlineBlockRuleTest.java | 29 +- .../cms/content/shortcodes/TagParserTest.java | 12 + .../content/markdown/features.shortcodes.html | 8 +- .../content/markdown/features.shortcodes.md | 6 +- .../hosts/features/content/search/index.md | 2 +- 10 files changed, 336 insertions(+), 258 deletions(-) diff --git a/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java b/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java index 616792e7a..7f14f8e12 100644 --- a/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java +++ b/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java @@ -41,7 +41,8 @@ public class AuthShortCodes extends RegisterShortCodesExtensionPoint { @Override public Map> shortCodes() { return Map.of( - "username", this::getUserName + "username", this::getUserName, + "cms:username", this::getUserName ); } diff --git a/cms-content/src/main/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRule.java b/cms-content/src/main/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRule.java index 2f1502632..eaab38343 100644 --- a/cms-content/src/main/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRule.java +++ b/cms-content/src/main/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRule.java @@ -21,14 +21,12 @@ * . * #L% */ - - import com.condation.cms.content.markdown.Block; import com.condation.cms.content.markdown.BlockElementRule; import com.condation.cms.content.markdown.InlineRenderer; -import com.condation.cms.content.shortcodes.ShortCodeParser; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.condation.cms.content.shortcodes.TagMap; +import com.condation.cms.content.shortcodes.TagParser; +import java.util.List; /** * @@ -36,55 +34,77 @@ */ public class ShortCodeBlockRule implements BlockElementRule { - public static final Pattern TAG_PARAMS_PATTERN_SHORT = Pattern.compile("^(\\[{2})(?[a-z_A-Z0-9]+)( (?.*?))?\\p{Blank}*/\\]{2}", - Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES); - public static final Pattern TAG_PARAMS_PATTERN_LONG = Pattern.compile("^(\\[{2})(?[a-z_A-Z0-9]+)( (?.*?))?\\]{2}(?.*)\\[{2}/\\k\\]{2}", - Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES); - - public static final Pattern SHORTCODE_PATTERN = Pattern.compile("^" + ShortCodeParser.SHORTCODE_REGEX, - Pattern.DOTALL | Pattern.MULTILINE); - + private static final TagParser tagParser = new TagParser(null); + @Override public Block next(final String md) { - /* - Matcher matcher = TAG_PARAMS_PATTERN_SHORT.matcher(md); - if (matcher.find()) { - return new ShortCodeBlock(matcher.start(), matcher.end(), - matcher.group("tag"), matcher.group("params"), "" - ); - } - matcher = TAG_PARAMS_PATTERN_LONG.matcher(md); - if (matcher.find()) { - return new ShortCodeBlock(matcher.start(), matcher.end(), - matcher.group("tag"), matcher.group("params"), matcher.group("content") - ); - } - */ - Matcher matcher = SHORTCODE_PATTERN.matcher(md); - if (matcher.matches()) { - String name = matcher.group(1) != null ? matcher.group(1) : matcher.group(4); - String params = matcher.group(2) != null ? matcher.group(2).trim() : matcher.group(5).trim(); - String content = matcher.group(3) != null ? matcher.group(3).trim() : ""; - ShortCodeParser.Match match = new ShortCodeParser.Match(name, matcher.start(), matcher.end()); - match.setContent(content); - match.getParameters().put("content", content); - - return new ShortCodeBlock(matcher.start(), matcher.end(), name, params, content); + List tags = tagParser.findTags(md, new TagMap() { + @Override + public boolean has(String codeName) { + return true; + } + }).stream() + .filter(tag -> isStandaloneInLine(md, tag)) + .toList(); + if (tags.isEmpty()) { + return null; } - - return null; + var tag = tags.getFirst(); + return new ShortCodeBlock( + tag.startIndex(), + tag.endIndex(), + tag); + } - - public static record ShortCodeBlock (int start, int end, String tag, String params, String content) implements Block { + public static record ShortCodeBlock(int start, int end, TagParser.TagInfo tagInfo) implements Block { @Override public String render(InlineRenderer inlineRenderer) { - return "[[%s %s]]%s[[/%s]]".formatted(tag, params, content, tag); + List params = tagInfo.rawAttributes() + .entrySet().stream() + .filter(entry -> !entry.getKey().equals("_content")) + .sorted((entry1, entry2) -> entry1.getKey().compareTo(entry2.getKey())) + .map(entry -> { + return "%s=%s".formatted(entry.getKey(), parseValue((String) entry.getValue())); + }).toList(); + return "[[%s %s]]%s[[/%s]]" + .formatted( + tagInfo.name(), + String.join(" ", params), + tagInfo.rawAttributes().getOrDefault("_content", ""), + tagInfo.name() + ); + } + } + + private static Object parseValue(String value) { + if (value.matches("\\d+")) { + return value; + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return value; + } + return "\"" + value + "\""; + } + + public boolean isStandaloneInLine(String text, TagParser.TagInfo tag) { + var startIndex = tag.startIndex(); + var endIndex = tag.endIndex(); + // Prüfe, ob die Indizes gültig sind + if (startIndex < 0 || endIndex > text.length() || startIndex >= endIndex) { + throw new IllegalArgumentException("Ungültige Indizes"); } - - + + // Finde die Position des Textausschnitts + String before = text.substring(0, startIndex); + String after = text.substring(endIndex); + + // Prüfe, ob vor und nach dem Ausschnitt ein Zeilenumbruch oder nichts steht + boolean beforeIsLineBreak = before.isEmpty() || before.endsWith("\n") || before.endsWith("\r\n"); + boolean afterIsLineBreak = after.isEmpty() || after.startsWith("\n") || after.startsWith("\r\n"); + + return beforeIsLineBreak && afterIsLineBreak; } - + } diff --git a/cms-content/src/main/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRule.java b/cms-content/src/main/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRule.java index e09f20a96..62080ce1f 100644 --- a/cms-content/src/main/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRule.java +++ b/cms-content/src/main/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRule.java @@ -21,14 +21,11 @@ * . * #L% */ - - import com.condation.cms.content.markdown.InlineBlock; import com.condation.cms.content.markdown.InlineElementRule; -import com.condation.cms.content.shortcodes.ShortCodeParser; -import static com.condation.cms.content.shortcodes.ShortCodeParser.PARAM_PATTERN; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.condation.cms.content.shortcodes.TagMap; +import com.condation.cms.content.shortcodes.TagParser; +import java.util.List; /** * @@ -36,65 +33,55 @@ */ public class ShortCodeInlineBlockRule implements InlineElementRule { - public static final Pattern TAG_PARAMS_PATTERN_SHORT = Pattern.compile("(\\[{2})(?[a-z_A-Z0-9]+)( (?.*?))?\\p{Blank}*/\\]{2}", - Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES); - public static final Pattern TAG_PARAMS_PATTERN_LONG = Pattern.compile("^(\\[{2})(?[a-z_A-Z0-9]+)( (?.*?))?\\]{2}(?.*)\\[{2}/\\k\\]{2}", - Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES); + private static final TagParser tagParser = new TagParser(null); @Override public InlineBlock next(final String md) { - /* - Matcher matcher = TAG_PARAMS_PATTERN_SHORT.matcher(md); - if (matcher.find()) { - return new ShortCodeInlineBlock(matcher.start(), matcher.end(), - matcher.group("tag"), matcher.group("params"), "" - ); - } - matcher = TAG_PARAMS_PATTERN_LONG.matcher(md); - if (matcher.find()) { - return new ShortCodeInlineBlock(matcher.start(), matcher.end(), - matcher.group("tag"), matcher.group("params"), matcher.group("content") - ); - } - */ - - Matcher matcher = ShortCodeParser.SHORTCODE_PATTERN.matcher(md); - if (matcher.find()) { - String name = matcher.group(1) != null ? matcher.group(1) : matcher.group(4); - String params = matcher.group(2) != null ? matcher.group(2).trim() : matcher.group(5).trim(); - String content = matcher.group(3) != null ? matcher.group(3).trim() : ""; - - ShortCodeParser.Match match = new ShortCodeParser.Match(name, matcher.start(), matcher.end()); - match.setContent(content); - match.getParameters().put("content", content); - - /* - Matcher paramMatcher = PARAM_PATTERN.matcher(params); - while (paramMatcher.find()) { - String key = paramMatcher.group(1); - String value = paramMatcher.group(2); - // Remove the surrounding quotes - value = value.substring(1, value.length() - 1); - match.getParameters().put(key, value); + List tags = tagParser.findTags(md, new TagMap() { + @Override + public boolean has(String codeName) { + return true; } - */ - return new ShortCodeInlineBlock(matcher.start(), matcher.end(), name, params, content); + }).stream().toList(); + if (tags.isEmpty()) { + return null; } - - return null; + var tag = tags.getFirst(); + return new ShortCodeInlineBlock( + tag.startIndex(), + tag.endIndex(), + tag); } - - public static record ShortCodeInlineBlock (int start, int end, String tag, String params, String content) implements InlineBlock { + public static record ShortCodeInlineBlock(int start, int end, TagParser.TagInfo tagInfo) implements InlineBlock { @Override public String render() { - return "[[%s %s]]%s[[/%s]]".formatted(tag, params, content, tag); + List params = tagInfo.rawAttributes() + .entrySet().stream() + .filter(entry -> !entry.getKey().equals("_content")) + .sorted((entry1, entry2) -> entry1.getKey().compareTo(entry2.getKey())) + .map(entry -> { + return "%s=%s".formatted(entry.getKey(), parseValue((String) entry.getValue())); + }).toList(); + return "[[%s %s]]%s[[/%s]]" + .formatted( + tagInfo.name(), + String.join(" ", params), + tagInfo.rawAttributes().getOrDefault("_content", ""), + tagInfo.name() + ); } - - - + + } + + private static Object parseValue(String value) { + if (value.matches("\\d+")) { + return value; + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return value; + } + return "\"" + value + "\""; } - } diff --git a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java index 3cd099100..af357e1e6 100644 --- a/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java @@ -23,140 +23,170 @@ */ import com.condation.cms.api.model.Parameter; -import java.util.Collections; -import java.util.Map; -import java.util.function.Function; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.MapContext; +import java.util.*; +import java.util.function.Function; + public class TagParser { - private final JexlEngine engine; - - public TagParser(JexlEngine engine) { - this.engine = engine; - } + private final JexlEngine engine; + + public TagParser(JexlEngine engine) { + this.engine = engine; + } + + // Klasse zur Speicherung der Tag-Informationen + public static record TagInfo (String name, Parameter rawAttributes, int startIndex, int endIndex) {} + + // Erster Schritt: Alle Tags ermitteln und deren Positionen sowie Roh-Attribute speichern + public List findTags(String text, TagMap tagHandlers) { + List tags = new ArrayList<>(); + int i = 0; + + while (i < text.length()) { + if (text.charAt(i) == '[' && i + 1 < text.length() && text.charAt(i + 1) == '[') { + int tagStart = i; + int endTagIndex = findTagEnd(text, i); + if (endTagIndex != -1) { + String tagContent = text.substring(i + 2, endTagIndex).trim(); + boolean isSelfClosing = tagContent.endsWith("/"); + + if (isSelfClosing) { + tagContent = tagContent.substring(0, tagContent.length() - 1).trim(); + } + + int spaceIndex = tagContent.indexOf(' '); + String tagName = spaceIndex == -1 ? tagContent : tagContent.substring(0, spaceIndex); + Parameter rawAttributes = spaceIndex == -1 + ? new Parameter() + : parseRawAttributes(tagContent.substring(spaceIndex + 1)); + + int closingTagIndex = -1; + if (!isSelfClosing) { + closingTagIndex = text.indexOf("[[/" + tagName + "]]", endTagIndex + 2); + if (closingTagIndex != -1) { + String content = text.substring(endTagIndex + 2, closingTagIndex); + rawAttributes.put("_content", content); + endTagIndex = closingTagIndex + ("[[/" + tagName + "]]").length() - 2; + } + } + + if (tagHandlers.has(tagName)) { + tags.add(new TagInfo(tagName, rawAttributes, tagStart, endTagIndex + 2)); + i = endTagIndex + 2; // Zum nächsten Tag springen + } else { + i++; + } + } else { + i++; + } + } else { + i++; + } + } + return tags; + } public String parse(String text, TagMap tagHandlers) { return parse(text, tagHandlers, Collections.emptyMap()); } - public String parse(String text, TagMap tagHandlers, Map contextModel) { - StringBuilder result = new StringBuilder(); - int i = 0; - while (i < text.length()) { - if (text.charAt(i) == '[' && i + 1 < text.length() && text.charAt(i + 1) == '[') { - int tagStart = i; - i = parseTag(text, i, result, tagHandlers, contextModel); - if (i == tagStart) { // Kein gültiger Tag gefunden, füge '[[' hinzu. - result.append("[["); - i += 2; - } - } else { - result.append(text.charAt(i)); - i++; - } - } - return result.toString(); - } - - private int parseTag(String text, int index, StringBuilder result, TagMap tagHandlers, Map contextModel) { - int endTagIndex = findTagEnd(text, index); - if (endTagIndex == -1) { - return index; // Kein schließendes ']]' gefunden - } - - String tagContent = text.substring(index + 2, endTagIndex).trim(); - boolean isSelfClosing = tagContent.endsWith("/"); - - if (isSelfClosing) { - tagContent = tagContent.substring(0, tagContent.length() - 1).trim(); - } - - int spaceIndex = tagContent.indexOf(' '); - String tagName = spaceIndex == -1 ? tagContent : tagContent.substring(0, spaceIndex); - Parameter attributes = spaceIndex == -1 - ? new Parameter() - : parseAttributes(tagContent.substring(spaceIndex + 1), contextModel); - - int closingTagIndex = -1; - if (!isSelfClosing) { - closingTagIndex = text.indexOf("[[/" + tagName + "]]", endTagIndex + 2); - if (closingTagIndex != -1) { - // Verarbeite den Content für geöffnete und geschlossene Tags - String content = text.substring(endTagIndex + 2, closingTagIndex); - attributes.put("_content", content); - endTagIndex = closingTagIndex + ("[[/" + tagName + "]]").length() - 2; - } - } - - if (tagHandlers.has(tagName)) { - Function handler = tagHandlers.get(tagName); - result.append(handler.apply(attributes)); - // Setze den Index auf das Zeichen direkt nach dem schließenden Tag oder schließenden Tag mit Content - return endTagIndex + 2; - } - - return index; // Tag nicht erkannt - } - - private int findTagEnd(String text, int startIndex) { - for (int i = startIndex; i < text.length() - 1; i++) { - if (text.charAt(i) == ']' && text.charAt(i + 1) == ']') { - return i; - } - } - return -1; // Kein schließendes ']]' gefunden - } - - private Parameter parseAttributes(String attributesString, Map contextModel) { - Parameter attributes = new Parameter(); - StringBuilder key = new StringBuilder(); - StringBuilder value = new StringBuilder(); - boolean inQuotes = false; - boolean readingKey = true; - - for (int i = 0; i < attributesString.length(); i++) { - char c = attributesString.charAt(i); - if (c == '"' || c == '\'') { - inQuotes = !inQuotes; - } else if (!inQuotes && (c == '=' || c == ' ')) { - if (readingKey) { - readingKey = false; - } else { - attributes.put(key.toString().trim(), parseValue(value.toString().trim(), contextModel)); - key.setLength(0); - value.setLength(0); - readingKey = true; - } - } else { - if (readingKey) { - key.append(c); - } else { - value.append(c); - } - } - } - - // Letztes Attribut verarbeiten - if (key.length() > 0 && value.length() > 0) { - attributes.put(key.toString().trim(), parseValue(value.toString().trim(), contextModel)); - } - - return attributes; - } - - private Object parseValue(String value, Map contextModel) { - if (value.matches("\\d+")) { - return Integer.valueOf(value); - } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { - return Boolean.valueOf(value); - } else if (value.startsWith("${") && value.endsWith("}")) { - String expressionString = value.substring(2, value.length() - 1); - - var expression = engine.createExpression(expressionString); - return expression.evaluate(new MapContext(contextModel)); - } - return value; - } + // Zweiter Schritt: Tags basierend auf den gespeicherten Positionen ersetzen + public String parse(String text, TagMap tagHandlers, Map contextModel) { + // Erster Schritt: Finde alle Tags + List tags = findTags(text, tagHandlers); + + // Zweiter Schritt: Ersetze alle Tags im Text + StringBuilder result = new StringBuilder(); + int lastIndex = 0; + for (TagInfo tag : tags) { + result.append(text, lastIndex, tag.startIndex); // Unveränderten Teil des Textes hinzufügen + Function handler = tagHandlers.get(tag.name); + + // Im zweiten Schritt: Attribute auswerten + Parameter evaluatedAttributes = evaluateAttributes(tag.rawAttributes, contextModel); + + result.append(handler.apply(evaluatedAttributes)); // Tag-Ersetzung + lastIndex = tag.endIndex; // Aktualisiere den Startpunkt für den nächsten Tag + } + result.append(text.substring(lastIndex)); // Füge den restlichen Text hinzu + + return result.toString(); + } + + // Methode zum Finden des Endes eines Tags + private int findTagEnd(String text, int startIndex) { + for (int i = startIndex; i < text.length() - 1; i++) { + if (text.charAt(i) == ']' && text.charAt(i + 1) == ']') { + return i; + } + } + return -1; // Kein schließendes ']]' gefunden + } + + // Methode zur Attribut-Analyse im ersten Schritt (Rohwerte als Strings speichern) + private Parameter parseRawAttributes(String attributesString) { + Parameter attributes = new Parameter(); + StringBuilder key = new StringBuilder(); + StringBuilder value = new StringBuilder(); + boolean inQuotes = false; + boolean readingKey = true; + + for (int i = 0; i < attributesString.length(); i++) { + char c = attributesString.charAt(i); + if (c == '"' || c == '\'') { + inQuotes = !inQuotes; + } else if (!inQuotes && (c == '=' || c == ' ')) { + if (readingKey) { + readingKey = false; + } else { + attributes.put(key.toString().trim(), value.toString().trim()); // Rohwert speichern + key.setLength(0); + value.setLength(0); + readingKey = true; + } + } else { + if (readingKey) { + key.append(c); + } else { + value.append(c); + } + } + } + + // Letztes Attribut verarbeiten + if (key.length() > 0 && value.length() > 0) { + attributes.put(key.toString().trim(), value.toString().trim()); // Rohwert speichern + } + + return attributes; + } + + // Zweiter Schritt: Attribute auswerten + private Parameter evaluateAttributes(Parameter rawAttributes, Map contextModel) { + Parameter evaluatedAttributes = new Parameter(); + for (Map.Entry entry : rawAttributes.entrySet()) { + String key = entry.getKey(); + String rawValue = (String) entry.getValue(); // Rohwert als String + evaluatedAttributes.put(key, parseValue(rawValue, contextModel)); // Wert erst jetzt parsen + } + return evaluatedAttributes; + } + + // Methode zur Auswertung von Attributwerten im zweiten Schritt + private Object parseValue(String value, Map contextModel) { + if (value.matches("\\d+")) { + return Integer.valueOf(value); + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return Boolean.valueOf(value); + } else if (value.startsWith("${") && value.endsWith("}")) { + String expressionString = value.substring(2, value.length() - 1); + + var expression = engine.createExpression(expressionString); + return expression.evaluate(new MapContext(contextModel)); + } + return value; + } } diff --git a/cms-content/src/test/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRuleTest.java b/cms-content/src/test/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRuleTest.java index 09058224c..facd66f74 100644 --- a/cms-content/src/test/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRuleTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/markdown/rules/block/ShortCodeBlockRuleTest.java @@ -24,6 +24,7 @@ import com.condation.cms.content.markdown.Block; +import java.util.Map; import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; @@ -45,13 +46,16 @@ void long_form() { Assertions.assertThat(next) .isNotNull() - .isInstanceOf(ShortCodeBlockRule.ShortCodeBlock.class) - .asInstanceOf(InstanceOfAssertFactories.type(ShortCodeBlockRule.ShortCodeBlock.class)) - .hasFieldOrPropertyWithValue("tag", "link") - .hasFieldOrPropertyWithValue("params", "url=\"https://google.de/\"") - .hasFieldOrPropertyWithValue("content", "Google") - ; - + .isInstanceOf(ShortCodeBlockRule.ShortCodeBlock.class); + + var tag = (ShortCodeBlockRule.ShortCodeBlock)next; + Assertions.assertThat(tag.tagInfo()) + .hasFieldOrPropertyWithValue("name", "link") + .hasFieldOrPropertyWithValue("rawAttributes", Map.of( + "url", "https://google.de/", + "_content", "Google" + )); + Assertions.assertThat(next.render((content) -> content)).isEqualTo("[[link url=\"https://google.de/\"]]Google[[/link]]"); } @@ -64,12 +68,15 @@ void short_form() { Assertions.assertThat(next) .isNotNull() - .isInstanceOf(ShortCodeBlockRule.ShortCodeBlock.class) - .asInstanceOf(InstanceOfAssertFactories.type(ShortCodeBlockRule.ShortCodeBlock.class)) - .hasFieldOrPropertyWithValue("tag", "link") - .hasFieldOrPropertyWithValue("params", "url=\"https://google.de/\"") - ; - + .isInstanceOf(ShortCodeBlockRule.ShortCodeBlock.class); + + var tag = (ShortCodeBlockRule.ShortCodeBlock)next; + Assertions.assertThat(tag.tagInfo()) + .hasFieldOrPropertyWithValue("name", "link") + .hasFieldOrPropertyWithValue("rawAttributes", Map.of( + "url", "https://google.de/" + )); + Assertions.assertThat(next.render((content) -> content)).isEqualTo("[[link url=\"https://google.de/\"]][[/link]]"); } @@ -82,13 +89,19 @@ void test_issue () { Assertions.assertThat(next) .isNotNull() .isInstanceOf(ShortCodeBlockRule.ShortCodeBlock.class) - .asInstanceOf(InstanceOfAssertFactories.type(ShortCodeBlockRule.ShortCodeBlock.class)) - .hasFieldOrPropertyWithValue("tag", "video") - .hasFieldOrPropertyWithValue("params", "type=\"youtube\" id=\"y0sF5xhGreA\" title=\"Everybody loves little cats\"") ; + var tag = (ShortCodeBlockRule.ShortCodeBlock)next; + Assertions.assertThat(tag.tagInfo()) + .hasFieldOrPropertyWithValue("name", "video") + .hasFieldOrPropertyWithValue("rawAttributes", Map.of( + "type", "youtube", + "id", "y0sF5xhGreA", + "title", "Everybody loves little cats" + )); + Assertions.assertThat(next.render((content) -> content)) - .isEqualTo("[[video type=\"youtube\" id=\"y0sF5xhGreA\" title=\"Everybody loves little cats\"]][[/video]]"); + .isEqualTo("[[video id=\"y0sF5xhGreA\" title=\"Everybody loves little cats\" type=\"youtube\"]][[/video]]"); } } diff --git a/cms-content/src/test/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRuleTest.java b/cms-content/src/test/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRuleTest.java index 942046ac8..24bc99b31 100644 --- a/cms-content/src/test/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRuleTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/markdown/rules/inline/ShortCodeInlineBlockRuleTest.java @@ -25,6 +25,7 @@ import com.condation.cms.content.markdown.InlineBlock; +import java.util.Map; import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; @@ -46,12 +47,15 @@ void long_form() { Assertions.assertThat(next) .isNotNull() - .isInstanceOf(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class) - .asInstanceOf(InstanceOfAssertFactories.type(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class)) - .hasFieldOrPropertyWithValue("tag", "link") - .hasFieldOrPropertyWithValue("params", "url=\"https://google.de/\"") - .hasFieldOrPropertyWithValue("content", "Google") - ; + .isInstanceOf(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class); + + var tag = (ShortCodeInlineBlockRule.ShortCodeInlineBlock)next; + Assertions.assertThat(tag.tagInfo()) + .hasFieldOrPropertyWithValue("name", "link") + .hasFieldOrPropertyWithValue("rawAttributes", Map.of( + "url", "https://google.de/", + "_content", "Google" + )); Assertions.assertThat(next.render()).isEqualTo("[[link url=\"https://google.de/\"]]Google[[/link]]"); } @@ -65,12 +69,15 @@ void short_form() { Assertions.assertThat(next) .isNotNull() - .isInstanceOf(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class) - .asInstanceOf(InstanceOfAssertFactories.type(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class)) - .hasFieldOrPropertyWithValue("tag", "link") - .hasFieldOrPropertyWithValue("params", "url=\"https://google.de/\"") - ; + .isInstanceOf(ShortCodeInlineBlockRule.ShortCodeInlineBlock.class); + var tag = (ShortCodeInlineBlockRule.ShortCodeInlineBlock)next; + Assertions.assertThat(tag.tagInfo()) + .hasFieldOrPropertyWithValue("name", "link") + .hasFieldOrPropertyWithValue("rawAttributes", Map.of( + "url", "https://google.de/" + )); + Assertions.assertThat(next.render()).isEqualTo("[[link url=\"https://google.de/\"]][[/link]]"); } diff --git a/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java index e1a05afb1..4ebb118c1 100644 --- a/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java @@ -56,6 +56,10 @@ void setup() { return "param: " + params.get("param1"); }); + tagMap.put("ns1:print", params -> { + return "message: " + params.get("message"); + }); + this.tagParser = new TagParser(new JexlBuilder().create()); } @@ -131,4 +135,12 @@ public void shortCode_in_text() { Assertions.assertThat(result).isEqualTo("Hello CondationCMS!"); } + @Test + public void namespace() { + String result = tagParser.parse("[[ns1:print message='Hello CondationCMS']][[/ns1:print]]", tagMap); + Assertions.assertThat(result).isEqualTo("message: Hello CondationCMS"); + + result = tagParser.parse("[[ns1:print message='Hello CondationCMS' /]]", tagMap); + Assertions.assertThat(result).isEqualTo("message: Hello CondationCMS"); + } } diff --git a/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.html b/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.html index 275a6b8e4..e780cd780 100644 --- a/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.html +++ b/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.html @@ -22,7 +22,11 @@

task list test

a paragraph with [[link id="google" title="Google"]][[/link]]

-

next

-

[[link id="apache" title="Apache"]][[/link]]

+

+ [[link id="apache" title="Apache"]][[/link]] +

+

with namespace

+

+ [[ns:link id="apache" title="Apache"]][[/ns:link]]
diff --git a/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.md b/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.md index abc97612b..345653e15 100644 --- a/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.md +++ b/cms-content/src/test/resources/com/condation/cms/content/markdown/features.shortcodes.md @@ -4,4 +4,8 @@ a paragraph with [[link id="google" title="Google" /]] # next -[[link id="apache" title="Apache" /]] \ No newline at end of file +[[link id="apache" title="Apache" /]] + +# with namespace + +[[ns:link id="apache" title="Apache" /]] \ No newline at end of file diff --git a/cms-server/hosts/features/content/search/index.md b/cms-server/hosts/features/content/search/index.md index dde8342a2..d57562de0 100644 --- a/cms-server/hosts/features/content/search/index.md +++ b/cms-server/hosts/features/content/search/index.md @@ -8,4 +8,4 @@ menu: ## Search -Hello [[username /]] \ No newline at end of file +Hello [[cms:username /]] \ No newline at end of file From 2bddb2f68177cf096c8327185318c5d032ecd894 Mon Sep 17 00:00:00 2001 From: Thorsten Marx Date: Fri, 11 Oct 2024 13:10:03 +0200 Subject: [PATCH 15/15] fix cachehandler --- .../configuration/ConfigurationManagement.java | 1 - .../cms/server/handler/cache/CacheHandler.java | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cms-api/src/main/java/com/condation/cms/api/configuration/ConfigurationManagement.java b/cms-api/src/main/java/com/condation/cms/api/configuration/ConfigurationManagement.java index 898424d29..edab3e92a 100644 --- a/cms-api/src/main/java/com/condation/cms/api/configuration/ConfigurationManagement.java +++ b/cms-api/src/main/java/com/condation/cms/api/configuration/ConfigurationManagement.java @@ -114,7 +114,6 @@ private List getConfigurations () { public void update(CronJobContext jobContext) { - System.out.println("update"); log.trace("check for modified configurations {}", db.getFileSystem().resolve(".").toString()); getConfigurations().forEach(config -> { try { diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/cache/CacheHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/cache/CacheHandler.java index 4950124e1..caeaf5dd6 100644 --- a/cms-server/src/main/java/com/condation/cms/server/handler/cache/CacheHandler.java +++ b/cms-server/src/main/java/com/condation/cms/server/handler/cache/CacheHandler.java @@ -96,7 +96,7 @@ public boolean handle(Request request, Response response, Callback callback) thr return true; } - final MyResponse cacheResponse = new MyResponse(request, response); + final CacheResponseWrapper cacheResponse = new CacheResponseWrapper(request, response); return super.handle(request, cacheResponse, new Callback.Nested(callback) { @Override public void succeeded() { @@ -138,13 +138,13 @@ private record CachedResponse(String body, Map headers) implemen } - private class MyResponse extends Response.Wrapper { + private class CacheResponseWrapper extends Response.Wrapper { final ByteArrayOutputStream bout = new ByteArrayOutputStream(); - final HttpFields.Mutable httpFields = HttpFields.build(); +// final HttpFields.Mutable httpFields = HttpFields.build(); - public MyResponse(Request request, Response wrapped) { + public CacheResponseWrapper(Request request, Response wrapped) { super(request, wrapped); } @@ -152,10 +152,10 @@ public String getContent() { return bout.toString(StandardCharsets.UTF_8); } - @Override - public HttpFields.Mutable getHeaders() { - return httpFields; - } +// @Override +// public HttpFields.Mutable getHeaders() { +// return httpFields; +// } @Override public void write(boolean last, ByteBuffer byteBuffer, Callback callback) {