SLING-9829 - data-sly-element should correctly handle void elements
* defined the set of void elements in ElementPlugin; every time the
data-sly-element plugin is used, the dynamically passed tag name will
be checked against the set; if the tag is a void element, no closing tag
will be added
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/StringConstant.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/StringConstant.java
index 2862490..4a6501f 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/StringConstant.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/StringConstant.java
@@ -18,6 +18,8 @@
******************************************************************************/
package org.apache.sling.scripting.sightly.compiler.expression.nodes;
+import java.util.Objects;
+
import org.apache.sling.scripting.sightly.compiler.expression.NodeVisitor;
/**
@@ -63,4 +65,20 @@
'}';
}
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(text);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof StringConstant) {
+ StringConstant other = (StringConstant) obj;
+ return Objects.equals(text, other.text);
+ }
+ return false;
+ }
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
index e2c21a3..efda58f 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/package-info.java
@@ -19,7 +19,7 @@
* The {@code org.apache.sling.scripting.sightly.compiler.expression.nodes} package exposes the various
* {@link org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode} types.
*/
-@Version("1.2.0")
+@Version("1.2.1")
package org.apache.sling.scripting.sightly.compiler.expression.nodes;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/CompilerContext.java b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/CompilerContext.java
index 6096c20..9df6c3b 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/CompilerContext.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/CompilerContext.java
@@ -45,6 +45,10 @@
return symbolGenerator.next(hint);
}
+ public String generateGlobalVariable(String hint) {
+ return symbolGenerator.global(hint);
+ }
+
public Expression adjustToContext(Expression expression, MarkupContext context, ExpressionContext expressionContext) {
return expressionWrapper.adjustToContext(expression, context, expressionContext);
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/util/SymbolGenerator.java b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/util/SymbolGenerator.java
index 6277b1f..39b11bf 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/util/SymbolGenerator.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/util/SymbolGenerator.java
@@ -18,12 +18,16 @@
******************************************************************************/
package org.apache.sling.scripting.sightly.impl.compiler.util;
+import java.util.HashMap;
+import java.util.Map;
+
public class SymbolGenerator {
public static final String DEFAULT_VAR_PREFIX = "var_";
private int counter = 0;
private final String prefix;
+ private final Map<String, String> globals = new HashMap<>();
public SymbolGenerator() {
this(DEFAULT_VAR_PREFIX);
@@ -34,10 +38,15 @@
}
public String next(String hint) {
- String middle = (hint != null) ? hint.replaceAll("\\-", "_") : "";
+ String middle = (hint != null) ? hint.replace("-", "_") : "";
return prefix + middle + counter++;
}
+ public String global(String hint) {
+ String name = prefix + ((hint != null) ? hint.replace("-", "_") : "");
+ return globals.computeIfAbsent(name, key -> name + counter++);
+ }
+
public String next() {
return next(null);
}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ElementPlugin.java b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ElementPlugin.java
index 29439b1..9a58ef2 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ElementPlugin.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/plugin/ElementPlugin.java
@@ -18,6 +18,12 @@
******************************************************************************/
package org.apache.sling.scripting.sightly.impl.plugin;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
import org.apache.sling.scripting.sightly.compiler.commands.OutText;
import org.apache.sling.scripting.sightly.compiler.commands.OutputVariable;
@@ -25,7 +31,14 @@
import org.apache.sling.scripting.sightly.compiler.expression.Expression;
import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
import org.apache.sling.scripting.sightly.compiler.expression.MarkupContext;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.ArrayLiteral;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.RuntimeCall;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.StringConstant;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperator;
import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
import org.apache.sling.scripting.sightly.impl.compiler.frontend.CompilerContext;
import org.apache.sling.scripting.sightly.impl.filter.ExpressionContext;
@@ -36,27 +49,40 @@
name = "element";
}
+ public static final Set<ExpressionNode> VOID_ELEMENTS =
+ Collections.unmodifiableSet(new HashSet<>(
+ Arrays.asList(new StringConstant("area"), new StringConstant("base"), new StringConstant("br"),
+ new StringConstant("col"), new StringConstant("embed"), new StringConstant("hr"), new StringConstant("img"),
+ new StringConstant("input"), new StringConstant("link"), new StringConstant("meta"),
+ new StringConstant("param"), new StringConstant("source"), new StringConstant("track"),
+ new StringConstant("wbr"))));
+
@Override
public PluginInvoke invoke(final Expression expression, final PluginCallInfo callInfo, final CompilerContext compilerContext) {
return new DefaultPluginInvoke() {
- private final ExpressionNode node = adjustContext(compilerContext, expression, MarkupContext.ELEMENT_NAME, ExpressionContext
- .ELEMENT).getRoot();
- private String tagVar = compilerContext.generateVariable("tagVar");
+ private final ExpressionNode node = adjustContext(compilerContext, expression).getRoot();
+ private final String tagVar = compilerContext.generateVariable("tagVar");
+ private final String tagAllowed = compilerContext.generateVariable("tagAllowed");
+ private final String voidElements = compilerContext.generateGlobalVariable("elementPluginVoidElements");
+ private final String selfClosingTag = compilerContext.generateVariable("selfClosingTag");
@Override
public void beforeElement(PushStream stream, String tagName) {
+ stream.write(new VariableBinding.Global(voidElements, new ArrayLiteral(new ArrayList<>(VOID_ELEMENTS))));
stream.write(new VariableBinding.Start(tagVar, node));
+ stream.write(new VariableBinding.Start(tagAllowed, new UnaryOperation(UnaryOperator.NOT,
+ new UnaryOperation(UnaryOperator.NOT, new Identifier(tagVar)))));
}
@Override
public void beforeTagOpen(PushStream stream) {
- stream.write(new Conditional.Start(tagVar, true));
+ stream.write(new Conditional.Start(tagAllowed, true));
stream.write(new OutText("<"));
stream.write(new OutputVariable(tagVar));
stream.write(Conditional.END);
- stream.write(new Conditional.Start(tagVar, false));
+ stream.write(new Conditional.Start(tagAllowed, false));
}
@Override
@@ -65,15 +91,34 @@
}
@Override
+ public void afterAttributes(PushStream stream) {
+ stream.write(new Conditional.Start(tagAllowed, true));
+ stream.write(new OutText(">"));
+ stream.write(Conditional.END);
+ stream.write(new Conditional.Start(tagAllowed, false));
+ }
+
+ @Override
+ public void afterTagOpen(PushStream stream) {
+ stream.write(Conditional.END);
+ }
+
+ @Override
public void beforeTagClose(PushStream stream, boolean isSelfClosing) {
- if (!isSelfClosing) {
- stream.write(new Conditional.Start(tagVar, true));
- stream.write(new OutText("</"));
- stream.write(new OutputVariable(tagVar));
- stream.write(new OutText(">"));
- stream.write(Conditional.END);
- }
- stream.write(new Conditional.Start(tagVar, false));
+ stream.write(new Conditional.Start(tagAllowed, true));
+ stream.write(
+ new VariableBinding.Start(selfClosingTag,
+ new BinaryOperation(BinaryOperator.IN, new Identifier(tagVar), new Identifier(voidElements))
+ )
+ );
+ stream.write(new Conditional.Start(selfClosingTag, false));
+ stream.write(new OutText("</"));
+ stream.write(new OutputVariable(tagVar));
+ stream.write(new OutText(">"));
+ stream.write(Conditional.END);
+ stream.write(VariableBinding.END);
+ stream.write(Conditional.END);
+ stream.write(new Conditional.Start(tagAllowed, false));
}
@Override
@@ -84,13 +129,13 @@
@Override
public void afterElement(PushStream stream) {
stream.write(VariableBinding.END);
+ stream.write(VariableBinding.END);
}
};
}
- private Expression adjustContext(CompilerContext compilerContext, Expression expression, MarkupContext markupContext,
- ExpressionContext expressionContext) {
+ private Expression adjustContext(CompilerContext compilerContext, Expression expression) {
ExpressionNode root = expression.getRoot();
if (root instanceof RuntimeCall) {
RuntimeCall runtimeCall = (RuntimeCall) root;
@@ -98,6 +143,6 @@
return expression;
}
}
- return compilerContext.adjustToContext(expression, markupContext, expressionContext);
+ return compilerContext.adjustToContext(expression, MarkupContext.ELEMENT_NAME, ExpressionContext.ELEMENT);
}
}