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-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/pom.xml b/cms-content/pom.xml index bb28508d1..c18e9e518 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/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/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/pipeline/ContentPipeline.java b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java index 852b03346..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) { @@ -90,12 +89,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-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..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 @@ -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,20 +30,27 @@ 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 { 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 static List parseShortcodes(String text) { + public ShortCodeParser() { + } + + 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() : ""; @@ -56,8 +64,7 @@ public static 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); + value = value.substring(1, value.length() - 1); // Entfernt die Anführungszeichen oder Klammern bei Arrays match.getParameters().put(key, value); } @@ -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..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; @@ -37,19 +38,25 @@ @RequiredArgsConstructor public class ShortCodes { - private final ShortCodeParser.Codes codes; + private final TagMap tagMap; + private final TagParser parser; - public ShortCodes (Map> codes) { - 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 ShortCodeParser.replace(content, codes); + 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) { - if (codes.get(name) == null) { + if (!tagMap.has(name)) { return ""; } try { @@ -59,7 +66,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..af357e1e6 --- /dev/null +++ b/cms-content/src/main/java/com/condation/cms/content/shortcodes/TagParser.java @@ -0,0 +1,192 @@ +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 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; + } + + // 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()); + } + + // 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/ContentBaseTest.java b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java new file mode 100644 index 000000000..a86307a41 --- /dev/null +++ b/cms-content/src/test/java/com/condation/cms/content/ContentBaseTest.java @@ -0,0 +1,57 @@ +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 com.condation.cms.content.shortcodes.TagParser; +import org.apache.commons.jexl3.JexlBuilder; + +/** + * + * @author t.marx + */ +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( + ); + } + + return shortCodeParser; + } +} 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/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..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 @@ -24,23 +24,24 @@ 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", @@ -51,15 +52,20 @@ public static 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); + tags.put( + "exp", + params -> "%s".formatted(params.get("expression")) + ); + + shortCodes = new ShortCodes(tags, getTagParser()); } @@ -105,18 +111,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

"); } @@ -161,7 +167,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

@@ -170,7 +176,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

@@ -178,4 +184,22 @@ void multiple_hello () { result = shortCodes.replace(input); Assertions.assertThat(result).isEqualTo(expected); } + + @Test + 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"); + } } 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..4ebb118c1 --- /dev/null +++ b/cms-content/src/test/java/com/condation/cms/content/shortcodes/TagParserTest.java @@ -0,0 +1,146 @@ +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 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"); + }); + + tagMap.put("ns1:print", params -> { + return "message: " + params.get("message"); + }); + + 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_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); + 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!"); + } + + @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/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..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 @@ -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") - )); + ), getTagParser()); } - + @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..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 @@ -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") - )); + ), 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 34b5e4ea0..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 @@ -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") - )); + ), getTagParser()); } @Test 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-sandbox/pom.xml b/cms-sandbox/pom.xml index 9ee1fb765..c65a99fa9 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..ac3f80325 --- /dev/null +++ b/cms-sandbox/tests/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + com.condation.cms + cms-sandbox + 6.4.0 + + tests + jar + + com.condation.cms.tests.Tests + + + + + org.apache.commons + commons-jexl3 + + + 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..612ac81bf --- /dev/null +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/content/shortcodes/ShortCodes.java @@ -0,0 +1,169 @@ +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 ShortCodeParserBaseListener { + 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 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().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) { + String name = ctx.selfClosingTag().TAG_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.TAG_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 + var testValue = rawValue.replace("\"", ""); + if (testValue.startsWith("${") && testValue.endsWith("}")) { + String expression = testValue.substring(2, testValue.length() - 1); + return evaluateExpression(expression); + } else { + // Normaler Text + return testValue;// 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/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/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..7743c392c --- /dev/null +++ b/cms-sandbox/tests/src/main/java/com/condation/cms/tests/Tests.java @@ -0,0 +1,57 @@ +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 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-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. + * #L% + */ + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +/** + * + * @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+4}\"/]]", 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"); + } + + @Test + public void shortCode_in_text() { + String result = shortCodes.parseShortcodes("Hello [[content]]CondationCMS[[/content]]!", codes); + Assertions.assertThat(result).isEqualTo("Hello CondationCMS!"); + } +} 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/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 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 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/src/main/java/com/condation/cms/request/RequestContextFactory.java b/cms-server/src/main/java/com/condation/cms/request/RequestContextFactory.java index 3fe24525c..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 @@ -54,6 +54,7 @@ import com.condation.cms.api.utils.RequestUtil; import com.condation.cms.content.RenderContext; 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; @@ -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(TagParser.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(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 add4c630f..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,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.TagParser; import com.condation.cms.extensions.ExtensionManager; import com.condation.cms.filesystem.FileDB; import com.condation.cms.filesystem.MetaData; @@ -80,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; /** @@ -109,6 +111,24 @@ public ContentNodeMapper contentNodeMapper (DB db, ContentParser contentParser) return new ContentNodeMapper(db, contentParser); } + @Provides + @Singleton + public TagParser tagParser (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 TagParser(engine.create()); + } + @Provides @Singleton public ConfigurationManagement configurationManagement(DB db, Configuration configuration, SiteCronJobScheduler scheduler, EventBus eventBus) throws IOException { 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) { 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..274ed3af2 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -43,7 +43,9 @@ 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.content.shortcodes.TagParser; import com.condation.cms.extensions.hooks.DBHooks; import com.condation.cms.extensions.hooks.TemplateHooks; import com.condation.cms.extensions.request.RequestExtensions; @@ -51,6 +53,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; /** @@ -71,10 +74,11 @@ public static RequestContext requestContext(String uri) { var markdownRenderer = TestHelper.getRenderer(); RequestContext context = new RequestContext(); + var shortCodeParser = new TagParser(new JexlBuilder().create()); 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/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 diff --git a/pom.xml b/pom.xml index 79a9c7ecd..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 @@ -111,6 +110,12 @@ ${project.version}
+ + org.apache.commons + commons-jexl3 + 3.4.0 + + com.github.slugify slugify