You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/05/24 13:40:19 UTC

[sling-org-apache-sling-scripting-sightly-compiler] branch issue/SLING-6779 updated (7e38340 -> 0608671)

This is an automated email from the ASF dual-hosted git repository.

radu pushed a change to branch issue/SLING-6779
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-compiler.git.


 discard 7e38340  SLING-6779 - The HTL compiler and Maven Plugin should warn when using potentially invalid options
 discard 9b790d2  SLING-6779 - [htl-maven-plugin] No warning / error on wrong options
     new 0608671  SLING-6779 - The HTL compiler and Maven Plugin should warn when using potentially invalid options

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (7e38340)
            \
             N -- N -- N   refs/heads/issue/SLING-6779 (0608671)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml | 41 -----------------------------------------
 1 file changed, 41 deletions(-)


[sling-org-apache-sling-scripting-sightly-compiler] 01/01: SLING-6779 - The HTL compiler and Maven Plugin should warn when using potentially invalid options

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch issue/SLING-6779
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-compiler.git

commit 06086711cdd55688f3953087ea735b8a308f735b
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu May 23 21:23:40 2019 +0200

    SLING-6779 - The HTL compiler and Maven Plugin should warn when using potentially invalid options
    
    * collected known options from filters
    * added a warn message in the compiler if a certain expression option is not handled by
    any of the filters and the expression context doesn't allow arbitrary options
---
 bnd.bnd                                            |  2 +
 pom.xml                                            | 52 ++++------------------
 .../impl/compiler/frontend/CompilerContext.java    |  5 +--
 .../impl/compiler/frontend/ExpressionWrapper.java  | 25 ++++++++++-
 .../sightly/impl/filter/AbstractFilter.java        | 25 ++++++++++-
 .../scripting/sightly/impl/filter/Filter.java      | 32 +++++++++++++
 .../sightly/impl/filter/FormatFilter.java          | 40 +++++++++++------
 .../scripting/sightly/impl/filter/I18nFilter.java  | 42 +++++++++++------
 .../scripting/sightly/impl/filter/JoinFilter.java  | 33 +++++++++-----
 .../sightly/impl/filter/URIManipulationFilter.java | 43 +++++++++---------
 .../scripting/sightly/impl/filter/XSSFilter.java   | 16 +++----
 .../sightly/impl/html/dom/MarkupHandler.java       |  2 +-
 .../sightly/impl/compiler/SightlyCompilerTest.java | 21 ++++++---
 .../impl/frontend/ExpressionWrapperTest.java       |  9 ++--
 14 files changed, 218 insertions(+), 129 deletions(-)

diff --git a/bnd.bnd b/bnd.bnd
new file mode 100644
index 0000000..b036455
--- /dev/null
+++ b/bnd.bnd
@@ -0,0 +1,2 @@
+Provide-Capability: io.sightly.compiler; version:Version="1.0", io.sightly.compiler; version:Version="1.1", io.sightly.compiler; version:Version="1.2", io.sightly.compiler; version:Version="1.3", io.sightly.compiler; version:Version="1.3.1", io.sightly.compiler; version:Version="1.4",
+Require-Capability: io.sightly.runtime; filter:="(&(version>=1.0)(!(version>=2.0)))"
diff --git a/pom.xml b/pom.xml
index 8797fb0..b039498 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,8 +24,8 @@
     <!-- ======================================================================= -->
     <parent>
         <groupId>org.apache.sling</groupId>
-        <artifactId>sling</artifactId>
-        <version>34</version>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35</version>
         <relativePath />
     </parent>
 
@@ -38,7 +38,6 @@
         releasing to only increase the first part, unless the module provides support for a newer version of the HTL specification.
     -->
     <version>1.1.3-1.4.0-SNAPSHOT</version>
-    <packaging>bundle</packaging>
 
     <name>Apache Sling Scripting HTL Compiler</name>
 
@@ -65,35 +64,6 @@
     <build>
         <plugins>
             <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <executions>
-                    <execution>
-                        <id>scr-metadata</id>
-                        <goals>
-                            <goal>manifest</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <exportScr>true</exportScr>
-                    <instructions>
-                        <Provide-Capability>
-                            io.sightly.compiler; version:Version=1.0,
-                            io.sightly.compiler; version:Version=1.1,
-                            io.sightly.compiler; version:Version=1.2,
-                            io.sightly.compiler; version:Version=1.3,
-                            io.sightly.compiler; version:Version=1.3.1,
-                            io.sightly.compiler; version:Version=1.4
-                        </Provide-Capability>
-                        <Require-Capability>
-                            io.sightly.runtime; filter:="(&amp;(version&gt;=1.0)(!(version&gt;=2.0)))"
-                        </Require-Capability>
-                    </instructions>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.antlr</groupId>
                 <artifactId>antlr4-maven-plugin</artifactId>
                 <version>${antlr.version}</version>
@@ -126,9 +96,9 @@
                 </executions>
             </plugin>
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>findbugs-maven-plugin</artifactId>
-                <version>3.0.5</version>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>3.1.11</version>
                 <configuration>
                     <effort>Max</effort>
                     <xmlOutput>true</xmlOutput>
@@ -257,20 +227,14 @@
         </dependency>
         <dependency>
             <groupId>org.powermock</groupId>
-            <artifactId>powermock-reflect</artifactId>
-            <version>1.6.5</version>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <version>2.0.2</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.powermock</groupId>
             <artifactId>powermock-module-junit4</artifactId>
-            <version>1.6.5</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito</artifactId>
-            <version>1.6.5</version>
+            <version>2.0.2</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
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 60b4247..3e8cf0b 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
@@ -47,10 +47,7 @@ public class CompilerContext {
     }
 
     public Expression adjustToContext(Expression expression, MarkupContext context, ExpressionContext expressionContext) {
-        if (!expression.getOptions().containsKey(Syntax.CONTEXT_OPTION)) {
-            return expressionWrapper.adjustToContext(expression, context, expressionContext);
-        }
-        return expression;
+        return expressionWrapper.adjustToContext(expression, context, expressionContext);
     }
 
     public PushStream getPushStream() {
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/ExpressionWrapper.java b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/ExpressionWrapper.java
index cf26b1c..53d1024 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/ExpressionWrapper.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/frontend/ExpressionWrapper.java
@@ -19,15 +19,20 @@
 package org.apache.sling.scripting.sightly.impl.compiler.frontend;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 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.nodes.BinaryOperation;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.StringConstant;
+import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
 import org.apache.sling.scripting.sightly.impl.compiler.Syntax;
+import org.apache.sling.scripting.sightly.impl.filter.AbstractFilter;
 import org.apache.sling.scripting.sightly.impl.filter.ExpressionContext;
 import org.apache.sling.scripting.sightly.impl.filter.Filter;
 import org.apache.sling.scripting.sightly.compiler.expression.MarkupContext;
@@ -38,9 +43,18 @@ import org.apache.sling.scripting.sightly.compiler.expression.MarkupContext;
 public class ExpressionWrapper {
 
     private final List<Filter> filters;
+    private final Set<String> knownOptions;
+    private final PushStream stream;
 
-    public ExpressionWrapper(List<Filter> filters) {
+    public ExpressionWrapper(PushStream stream, List<Filter> filters) {
+        this.stream = stream;
         this.filters = filters;
+        Set<String> options = new HashSet<>();
+        for (Filter filter : filters) {
+            options.addAll(filter.getOptions());
+        }
+        options.add(Syntax.CONTEXT_OPTION);
+        knownOptions = Collections.unmodifiableSet(options);
     }
 
     public Expression transform(Interpolation interpolation, MarkupContext markupContext, ExpressionContext expressionContext) {
@@ -51,13 +65,20 @@ public class ExpressionWrapper {
                 nodes.add(new StringConstant(fragment.getText()));
             } else {
                 Expression expression = fragment.getExpression();
+                if (AbstractFilter.NON_PARAMETRIZABLE_CONTEXTS.contains(expressionContext)) {
+                    for (String option : expression.getOptions().keySet()) {
+                        if (!knownOptions.contains(option)) {
+                            stream.warn(new PushStream.StreamMessage(String.format("Unknown option '%s'.", option), expression.getRawText()));
+                        }
+                    }
+                }
                 Expression transformed = adjustToContext(expression, markupContext, expressionContext);
                 nodes.add(transformed.getRoot());
                 options.putAll(transformed.getOptions());
             }
         }
         ExpressionNode root = join(nodes);
-        if (interpolation.size() > 1 && options.containsKey(Syntax.CONTEXT_OPTION)) {
+        if (interpolation.size() > 1) {
             //context must not be calculated by merging
             options.remove(Syntax.CONTEXT_OPTION);
         }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/AbstractFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/AbstractFilter.java
index f72ded7..53ee22c 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/AbstractFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/AbstractFilter.java
@@ -18,8 +18,12 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
@@ -27,6 +31,14 @@ import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
 public abstract class AbstractFilter implements Filter {
 
     protected int priority = 100;
+    public static final Set<ExpressionContext> NON_PARAMETRIZABLE_CONTEXTS;
+    static {
+        Set<ExpressionContext> contexts = new HashSet<>(Arrays.asList(ExpressionContext.values()));
+        contexts.remove(ExpressionContext.PLUGIN_DATA_SLY_USE);
+        contexts.remove(ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE);
+        contexts.remove(ExpressionContext.PLUGIN_DATA_SLY_CALL);
+        NON_PARAMETRIZABLE_CONTEXTS = Collections.unmodifiableSet(contexts);
+    }
 
     @Override
     public int priority() {
@@ -43,6 +55,17 @@ public abstract class AbstractFilter implements Filter {
         return 1;
     }
 
+    @Override
+    public Expression apply(Expression expression, ExpressionContext expressionContext) {
+        Set<String> expressionOptions = expression.getOptions().keySet();
+        if (getApplicableContexts().contains(expressionContext) && expressionOptions.containsAll(getRequiredOptions())) {
+            return apply(expression, getFilterOptions(expression, getOptions()));
+        }
+        return expression;
+    }
+
+    protected abstract Expression apply(Expression expression, Map<String, ExpressionNode> options);
+
     /**
      * Collects the options passed in the {@code options} array into a new map while removing them from the original expression.
      *
@@ -50,7 +73,7 @@ public abstract class AbstractFilter implements Filter {
      * @param options    the options of interest for the {@link Filter}
      * @return a map with the retrieved options; the map can be empty if none of the options were found
      */
-    protected Map<String, ExpressionNode> getFilterOptions(Expression expression, String... options) {
+    protected Map<String, ExpressionNode> getFilterOptions(Expression expression, Set<String> options) {
         Map<String, ExpressionNode> collector = new HashMap<>();
         for (String option : options) {
             ExpressionNode optionNode = expression.removeOption(option);
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/Filter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/Filter.java
index be239ef..4607df1 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/Filter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/Filter.java
@@ -18,6 +18,11 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 
 /**
@@ -45,4 +50,31 @@ public interface Filter extends Comparable<Filter> {
      */
     int priority();
 
+    /**
+     * Provides the option names this {@code Filter} will process.
+     *
+     * @return a set of option names
+     */
+    default Set<String> getOptions() {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Provides the option names that will trigger a filter's execution.
+     *
+     * @return the required options from an expression in order to trigger the filter
+     */
+    default Set<String> getRequiredOptions() {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Provides the applicable contexts for this filter.
+     *
+     * @return the applicable contexts for this filter
+     */
+    default Set<ExpressionContext> getApplicableContexts() {
+        return new HashSet<>(Arrays.asList(ExpressionContext.values()));
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/FormatFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/FormatFilter.java
index 1962dc1..349e13e 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/FormatFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/FormatFilter.java
@@ -18,6 +18,12 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 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.nodes.MapLiteral;
@@ -33,6 +39,9 @@ public class FormatFilter extends AbstractFilter {
     public static final String FORMAT_LOCALE_OPTION = "formatLocale";
     public static final String TIMEZONE_OPTION = "timezone";
 
+    private static final Set<String> OPTIONS = new HashSet<>(Arrays.asList(FORMAT_OPTION, TYPE_OPTION, FORMAT_LOCALE_OPTION, TIMEZONE_OPTION));
+    private static final Set<String> REQUIRED_OPTIONS = Collections.singleton(FORMAT_OPTION);
+
     private static final class FormatFilterLoader {
         private static final FormatFilter INSTANCE = new FormatFilter();
     }
@@ -45,21 +54,24 @@ public class FormatFilter extends AbstractFilter {
     }
 
     @Override
-    public Expression apply(Expression expression, ExpressionContext expressionContext) {
-        //todo: if the expression is a string constant, we can produce the transformation at
-        //compile time, with no need of a runtime function
-        if (!expression.containsOption(FORMAT_OPTION) || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_USE || expressionContext
-                == ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_CALL) {
-            return expression;
-        }
+    protected Expression apply(Expression expression, Map<String, ExpressionNode> options) {
         ExpressionNode translation =
-                new RuntimeCall(RuntimeCall.FORMAT, expression.getRoot(),
-                        new MapLiteral(getFilterOptions(expression,
-                                FORMAT_OPTION,
-                                TYPE_OPTION,
-                                I18nFilter.LOCALE_OPTION,
-                                FORMAT_LOCALE_OPTION,
-                                TIMEZONE_OPTION)));
+                new RuntimeCall(RuntimeCall.FORMAT, expression.getRoot(), new MapLiteral(getFilterOptions(expression, getOptions())));
         return expression.withNode(translation);
     }
+
+    @Override
+    public Set<String> getOptions() {
+        return OPTIONS;
+    }
+
+    @Override
+    public Set<String> getRequiredOptions() {
+        return REQUIRED_OPTIONS;
+    }
+
+    @Override
+    public Set<ExpressionContext> getApplicableContexts() {
+        return NON_PARAMETRIZABLE_CONTEXTS;
+    }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/I18nFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/I18nFilter.java
index 5275b13..94353f0 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/I18nFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/I18nFilter.java
@@ -18,7 +18,11 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
@@ -35,14 +39,14 @@ public final class I18nFilter extends AbstractFilter {
     public static final String LOCALE_OPTION = "locale";
     public static final String BASENAME_OPTION = "basename";
 
+    private static final Set<String> OPTIONS = new HashSet<>(Arrays.asList(I18N_OPTION, HINT_OPTION, LOCALE_OPTION, BASENAME_OPTION));
+    private static final Set<String> REQUIRED_OPTIONS = Collections.singleton(I18N_OPTION);
+
     private static final class I18nFilterLoader {
         private static final I18nFilter INSTANCE = new I18nFilter();
     }
 
     private I18nFilter() {
-        if (I18nFilterLoader.INSTANCE != null) {
-            throw new IllegalStateException("INSTANCE was already defined.");
-        }
         priority = 90;
     }
 
@@ -51,16 +55,28 @@ public final class I18nFilter extends AbstractFilter {
     }
 
     @Override
-    public Expression apply(Expression expression, ExpressionContext expressionContext) {
-        if (!expression.containsOption(I18N_OPTION) || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_USE || expressionContext
-                == ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_CALL) {
-            return expression;
+    protected Expression apply(Expression expression, Map<String, ExpressionNode> options) {
+        if (options.containsKey(I18N_OPTION)) {
+            ExpressionNode translation = new RuntimeCall(RuntimeCall.I18N, expression.getRoot(), new MapLiteral(options));
+            expression.removeOption(I18N_OPTION);
+            expression.getOptions().put(FormatFilter.FORMAT_LOCALE_OPTION, options.get(LOCALE_OPTION));
+            return expression.withNode(translation);
         }
-        Map <String, ExpressionNode> options = getFilterOptions(expression, HINT_OPTION, LOCALE_OPTION, BASENAME_OPTION);
-        ExpressionNode translation = new RuntimeCall(RuntimeCall.I18N, expression.getRoot(), new MapLiteral
-                (options));
-        expression.removeOption(I18N_OPTION);
-        expression.getOptions().put(FormatFilter.FORMAT_LOCALE_OPTION, options.get(LOCALE_OPTION));
-        return expression.withNode(translation);
+        return expression;
+    }
+
+    @Override
+    public Set<String> getOptions() {
+        return OPTIONS;
+    }
+
+    @Override
+    public Set<String> getRequiredOptions() {
+        return REQUIRED_OPTIONS;
+    }
+
+    @Override
+    public Set<ExpressionContext> getApplicableContexts() {
+        return NON_PARAMETRIZABLE_CONTEXTS;
     }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/JoinFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/JoinFilter.java
index 113b3f5..73cdc62 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/JoinFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/JoinFilter.java
@@ -18,6 +18,10 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
 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.nodes.RuntimeCall;
@@ -28,29 +32,38 @@ import org.apache.sling.scripting.sightly.compiler.expression.nodes.RuntimeCall;
 public class JoinFilter extends AbstractFilter {
 
     public static final String JOIN_OPTION = "join";
+    private static final Set<String> OPTIONS = Collections.singleton(JOIN_OPTION);
 
     private static final class JoinFilterLoader {
         private static final JoinFilter INSTANCE = new JoinFilter();
     }
 
-    private JoinFilter() {
-        if (JoinFilterLoader.INSTANCE != null) {
-            throw new IllegalStateException("INSTANCE was already defined.");
-        }
-    }
+    private JoinFilter() {}
 
     public static JoinFilter getInstance() {
         return JoinFilterLoader.INSTANCE;
     }
 
     @Override
-    public Expression apply(Expression expression, ExpressionContext expressionContext) {
-        if (!expression.containsOption(JOIN_OPTION) || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_USE || expressionContext
-                == ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_CALL) {
-            return expression;
-        }
+    protected Expression apply(Expression expression, Map<String, ExpressionNode> options) {
         ExpressionNode translation =
                 new RuntimeCall(RuntimeCall.JOIN, expression.getRoot(), expression.removeOption(JOIN_OPTION));
         return expression.withNode(translation);
     }
+
+    @Override
+    public Set<String> getOptions() {
+        return OPTIONS;
+    }
+
+    @Override
+    public Set<String> getRequiredOptions() {
+        return OPTIONS;
+    }
+
+    @Override
+    public Set<ExpressionContext> getApplicableContexts() {
+        return NON_PARAMETRIZABLE_CONTEXTS;
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/URIManipulationFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/URIManipulationFilter.java
index 8ba4b22..40ba54b 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/URIManipulationFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/URIManipulationFilter.java
@@ -18,7 +18,10 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.sling.scripting.sightly.compiler.expression.Expression;
 import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
@@ -48,43 +51,37 @@ public class URIManipulationFilter extends AbstractFilter {
     public static final String ADD_QUERY = "addQuery";
     public static final String REMOVE_QUERY = "removeQuery";
 
+    private static final Set<String> OPTIONS = new HashSet<>(Arrays.asList(SCHEME, DOMAIN, PATH, APPEND_PATH, PREPEND_PATH, SELECTORS,
+            ADD_SELECTORS, REMOVE_SELECTORS, EXTENSION, SUFFIX, PREPEND_SUFFIX, APPEND_SUFFIX, FRAGMENT, QUERY, ADD_QUERY, REMOVE_QUERY));
+
 
     private static final class URIManipulationFilterLoader {
         private static final URIManipulationFilter INSTANCE = new URIManipulationFilter();
     }
 
-    private URIManipulationFilter() {
-        if (URIManipulationFilterLoader.INSTANCE != null) {
-            throw new IllegalStateException("INSTANCE was already defined.");
-        }
-    }
+    private URIManipulationFilter() {}
 
     public static URIManipulationFilter getInstance() {
         return URIManipulationFilterLoader.INSTANCE;
     }
 
     @Override
-    public Expression apply(Expression expression, ExpressionContext expressionContext) {
-        if ((expression.containsOption(SCHEME) || expression.containsOption(DOMAIN) || expression.containsOption(PATH) || expression
-                .containsOption(APPEND_PATH) || expression.containsOption(PREPEND_PATH) || expression.containsOption(SELECTORS) ||
-                expression.containsOption(ADD_SELECTORS) || expression.containsOption(REMOVE_SELECTORS) || expression.containsOption
-                (EXTENSION) || expression.containsOption(SUFFIX) || expression.containsOption(PREPEND_SUFFIX) || expression
-                .containsOption(APPEND_SUFFIX) || expression.containsOption(FRAGMENT) || expression.containsOption(QUERY) || expression
-                .containsOption(ADD_QUERY) || expression.containsOption(REMOVE_QUERY)) && expressionContext != ExpressionContext
-                .PLUGIN_DATA_SLY_USE && expressionContext
-                != ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE && expressionContext != ExpressionContext.PLUGIN_DATA_SLY_CALL &&
-                expressionContext != ExpressionContext.PLUGIN_DATA_SLY_RESOURCE) {
-            Map<String, ExpressionNode> uriOptions = getFilterOptions(expression, SCHEME, DOMAIN, PATH, APPEND_PATH, PREPEND_PATH,
-                    SELECTORS, ADD_SELECTORS, REMOVE_SELECTORS, EXTENSION, SUFFIX, PREPEND_SUFFIX, APPEND_SUFFIX, FRAGMENT, QUERY,
-                    ADD_QUERY, REMOVE_QUERY);
-            if (uriOptions.size() > 0) {
-                ExpressionNode translation =
-                        new RuntimeCall(RuntimeCall.URI_MANIPULATION, expression.getRoot(), new MapLiteral(uriOptions));
-                return expression.withNode(translation);
-            }
+    protected Expression apply(Expression expression, Map<String, ExpressionNode> options) {
+        if (options.size() > 0) {
+            ExpressionNode translation =
+                    new RuntimeCall(RuntimeCall.URI_MANIPULATION, expression.getRoot(), new MapLiteral(options));
+            return expression.withNode(translation);
         }
         return expression;
     }
 
+    @Override
+    public Set<String> getOptions() {
+        return OPTIONS;
+    }
 
+    @Override
+    public Set<ExpressionContext> getApplicableContexts() {
+        return NON_PARAMETRIZABLE_CONTEXTS;
+    }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/XSSFilter.java b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/XSSFilter.java
index f693102..2451d90 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/filter/XSSFilter.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/filter/XSSFilter.java
@@ -18,6 +18,9 @@
  ******************************************************************************/
 package org.apache.sling.scripting.sightly.impl.filter;
 
+import java.util.Map;
+import java.util.Set;
+
 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.nodes.RuntimeCall;
@@ -33,9 +36,6 @@ public class XSSFilter extends AbstractFilter {
     }
 
     private XSSFilter() {
-        if (XSSFilterLoader.INSTANCE != null) {
-            throw new IllegalStateException("INSTANCE was already defined.");
-        }
         priority = 110;
     }
 
@@ -44,11 +44,7 @@ public class XSSFilter extends AbstractFilter {
     }
 
     @Override
-    public Expression apply(Expression expression, ExpressionContext expressionContext) {
-        if (expressionContext == ExpressionContext.PLUGIN_DATA_SLY_USE || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_TEMPLATE
-                || expressionContext == ExpressionContext.PLUGIN_DATA_SLY_CALL) {
-            return expression;
-        }
+    protected Expression apply(Expression expression, Map<String, ExpressionNode> options) {
         ExpressionNode context = expression.removeOption(Syntax.CONTEXT_OPTION);
         if (context != null) {
             return expression.withNode(new RuntimeCall(RuntimeCall.XSS, expression.getRoot(), context));
@@ -56,4 +52,8 @@ public class XSSFilter extends AbstractFilter {
         return expression;
     }
 
+    @Override
+    public Set<ExpressionContext> getApplicableContexts() {
+        return NON_PARAMETRIZABLE_CONTEXTS;
+    }
 }
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java b/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
index c8c4966..314b29a 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
@@ -78,7 +78,7 @@ public class MarkupHandler {
     public MarkupHandler(PushStream stream, Map<String, Plugin> pluginRegistry, List<Filter> filters) {
         this.stream = stream;
         this.pluginRegistry = pluginRegistry;
-        this.expressionWrapper = new ExpressionWrapper(filters);
+        this.expressionWrapper = new ExpressionWrapper(stream, filters);
         this.compilerContext = new CompilerContext(symbolGenerator, expressionWrapper, stream);
     }
 
diff --git a/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java b/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
index 7d95e1d..044561e 100644
--- a/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
+++ b/src/test/java/org/apache/sling/scripting/sightly/impl/compiler/SightlyCompilerTest.java
@@ -51,7 +51,7 @@ public class SightlyCompilerTest {
 
     @Test
     public void testMissingExplicitContext() {
-        for (String s : new String[] {"", "-win", "-mac"}) {
+        for (String s : new String[]{"", "-win", "-mac"}) {
             String script = "/missing-explicit-context" + s + ".html";
             testMissingExplicitContext(script);
         }
@@ -74,7 +74,7 @@ public class SightlyCompilerTest {
         PowerMockito.mockStatic(System.class);
         PowerMockito.when(System.lineSeparator()).thenReturn("\r\n");
 
-        for (String s : new String[] {"", "-win", "-mac"}) {
+        for (String s : new String[]{"", "-win", "-mac"}) {
             String script = "/missing-explicit-context" + s + ".html";
             testMissingExplicitContext(script);
         }
@@ -85,7 +85,7 @@ public class SightlyCompilerTest {
         PowerMockito.mockStatic(System.class);
         PowerMockito.when(System.lineSeparator()).thenReturn("\r");
 
-        for (String s : new String[] {"", "-win", "-mac"}) {
+        for (String s : new String[]{"", "-win", "-mac"}) {
             String script = "/missing-explicit-context" + s + ".html";
             testMissingExplicitContext(script);
         }
@@ -129,8 +129,8 @@ public class SightlyCompilerTest {
         }
 
         // doubles
-        double  doubleTestRange = 20.00;
-        for (double i = -1.00 * doubleTestRange; i < doubleTestRange; i+= 0.1) {
+        double doubleTestRange = 20.00;
+        for (double i = -1.00 * doubleTestRange; i < doubleTestRange; i += 0.1) {
             assertEquals(0, compileSource("${" + i + "}").getErrors().size());
         }
 
@@ -150,6 +150,17 @@ public class SightlyCompilerTest {
         assertEquals(0, compileSource("${1e+2}").getErrors().size());
     }
 
+    @Test
+    public void testUnknownExpressionOptions() {
+        assertEquals(0, compileSource("<sly data-sly-use.a=\"${com.example.Pojo @ param1=1, param2=2}\"></sly>").getWarnings().size());
+        assertEquals(0, compileSource("<sly data-sly-call=\"${myTemplate @ param1=1, param2=2}\"></sly>").getWarnings().size());
+        assertEquals(0, compileSource("<template data-sly-template.myTemplate=\"${@ param1, param2}\"></template>").getWarnings().size());
+        assertEquals(1, compileSource("${currentPage.title @ contex = 'scriptString'}").getWarnings().size());
+        assertEquals(1, compileSource("${a @ unknownOption}").getWarnings().size());
+        assertEquals(2, compileSource("${a @ unknownOption1, unknownOption2}").getWarnings().size());
+        assertEquals(1, compileSource("<div data-sly-text='${\"text\" @ i18nn}'>Replaced</div>").getWarnings().size());
+    }
+
     private CompilationResult compileFile(final String file) {
         InputStream stream = this.getClass().getResourceAsStream(file);
         final Reader reader = new InputStreamReader(stream);
diff --git a/src/test/java/org/apache/sling/scripting/sightly/impl/frontend/ExpressionWrapperTest.java b/src/test/java/org/apache/sling/scripting/sightly/impl/frontend/ExpressionWrapperTest.java
index 4eea958..a713265 100644
--- a/src/test/java/org/apache/sling/scripting/sightly/impl/frontend/ExpressionWrapperTest.java
+++ b/src/test/java/org/apache/sling/scripting/sightly/impl/frontend/ExpressionWrapperTest.java
@@ -33,6 +33,7 @@ import org.apache.sling.scripting.sightly.compiler.expression.nodes.NullLiteral;
 import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
 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.impl.compiler.PushStream;
 import org.apache.sling.scripting.sightly.impl.compiler.frontend.ExpressionWrapper;
 import org.apache.sling.scripting.sightly.impl.compiler.frontend.Interpolation;
 import org.apache.sling.scripting.sightly.impl.filter.ExpressionContext;
@@ -70,7 +71,7 @@ public class ExpressionWrapperTest {
         options.put(I18nFilter.LOCALE_OPTION, new StringConstant("de"));
         options.put(I18nFilter.I18N_OPTION, NullLiteral.INSTANCE);
         interpolation.addExpression(new Expression(new StringConstant("hello"), options));
-        ExpressionWrapper wrapper = new ExpressionWrapper(filters);
+        ExpressionWrapper wrapper = new ExpressionWrapper(new PushStream(), filters);
         Expression result = wrapper.transform(interpolation, MarkupContext.TEXT, ExpressionContext.TEXT);
         List<ExpressionNode> xssArguments = runOptionsAndXSSAssertions(result, 1);
         RuntimeCall i18n = (RuntimeCall) xssArguments.get(0);
@@ -86,7 +87,7 @@ public class ExpressionWrapperTest {
         formatArray.add(new StringConstant("Doe"));
         options.put(FormatFilter.FORMAT_OPTION, new ArrayLiteral(formatArray));
         interpolation.addExpression(new Expression(new StringConstant("Hello {0} {1}"), options));
-        ExpressionWrapper wrapper = new ExpressionWrapper(filters);
+        ExpressionWrapper wrapper = new ExpressionWrapper(new PushStream(), filters);
         Expression result = wrapper.transform(interpolation, MarkupContext.TEXT, ExpressionContext.TEXT);
         List<ExpressionNode> xssArguments = runOptionsAndXSSAssertions(result, 0);
         RuntimeCall format = (RuntimeCall) xssArguments.get(0);
@@ -102,7 +103,7 @@ public class ExpressionWrapperTest {
         array.add(new NumericConstant(0));
         array.add(new NumericConstant(1));
         interpolation.addExpression(new Expression(new ArrayLiteral(array), options));
-        ExpressionWrapper wrapper = new ExpressionWrapper(filters);
+        ExpressionWrapper wrapper = new ExpressionWrapper(new PushStream(), filters);
         Expression result = wrapper.transform(interpolation, MarkupContext.TEXT, ExpressionContext.TEXT);
         List<ExpressionNode> xssArguments = runOptionsAndXSSAssertions(result, 0);
         RuntimeCall join = (RuntimeCall) xssArguments.get(0);
@@ -138,7 +139,7 @@ public class ExpressionWrapperTest {
         interpolation.addExpression(
                 new Expression(new StringConstant("http://www.example.com/resource.selector.extension/suffix#fragment?param=value"),
                         options));
-        ExpressionWrapper wrapper = new ExpressionWrapper(filters);
+        ExpressionWrapper wrapper = new ExpressionWrapper(new PushStream(), filters);
         Expression result = wrapper.transform(interpolation, MarkupContext.TEXT, ExpressionContext.TEXT);
         List<ExpressionNode> xssArguments = runOptionsAndXSSAssertions(result, 0);
         RuntimeCall join = (RuntimeCall) xssArguments.get(0);