NIFI-8666: Allow users to escape parameter names in Expression Language using quotes. (#5133)

diff --git a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
index d431a02a..de6f184 100644
--- a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
+++ b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
@@ -143,8 +143,8 @@
 referenceOrFunction : DOLLAR LBRACE attributeRefOrFunctionCall (COLON functionCall)* RBRACE ->
                       	^(EXPRESSION attributeRefOrFunctionCall functionCall*);
 
-parameterReference : PARAMETER_REFERENCE_START ATTRIBUTE_NAME RBRACE ->
-    ^(PARAMETER_REFERENCE ATTRIBUTE_NAME);
+parameterReference : PARAMETER_REFERENCE_START singleAttrRef RBRACE ->
+    ^(PARAMETER_REFERENCE singleAttrRef);
 
 expression : referenceOrFunction;
 
diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
index 986f8f6..7e1a71e 100644
--- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
+++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
@@ -345,6 +345,20 @@
     }
 
     @Test
+    public void testParameterReferenceWithSpace() {
+        final Map<String, String> attributes = Collections.emptyMap();
+        final Map<String, String> stateValues = Collections.emptyMap();
+        final Map<String, String> parameters = new HashMap<>();
+        parameters.put("test param", "unit");
+
+        final Query query = Query.compile("${'#{test param}'}");
+        verifyEquals("${#{'test param'}}", attributes, stateValues, parameters,"unit");
+        verifyEquals("${#{'test param'}:append(' - '):append(#{'test param'})}", attributes, stateValues, parameters,"unit - unit");
+
+        verifyEquals("${#{\"test param\"}}", attributes, stateValues, parameters,"unit");
+    }
+
+    @Test
     public void testJsonPath() throws IOException {
         Map<String,String> attributes = verifyJsonPathExpressions(
             ADDRESS_BOOK_JSON_PATH_EMPTY,
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
index 763e0fd..1bd149f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
@@ -221,12 +221,32 @@
     public Optional<Parameter> getParameter(final ParameterDescriptor parameterDescriptor) {
         readLock.lock();
         try {
-            return Optional.ofNullable(parameters.get(parameterDescriptor));
+            // When Expression Language is used, the Parameter may require being escaped.
+            // This is the case, for instance, if a Parameter name has a space in it.
+            // Because of this, we may have a case where we attempt to get a Parameter by name
+            // and that Parameter name is enclosed within single tick marks, as a way of escaping
+            // the name via the Expression Language. In this case, we want to strip out those
+            // escaping tick marks and use just the raw name for looking up the Parameter.
+            final ParameterDescriptor unescaped = unescape(parameterDescriptor);
+            return Optional.ofNullable(parameters.get(unescaped));
         } finally {
             readLock.unlock();
         }
     }
 
+    private ParameterDescriptor unescape(final ParameterDescriptor descriptor) {
+        final String parameterName = descriptor.getName().trim();
+        if ((parameterName.startsWith("'") && parameterName.endsWith("'")) || (parameterName.startsWith("\"") && parameterName.endsWith("\""))) {
+            final String stripped = parameterName.substring(1, parameterName.length() - 1);
+            return new ParameterDescriptor.Builder()
+                .from(descriptor)
+                .name(stripped)
+                .build();
+        }
+
+        return descriptor;
+    }
+
     @Override
     public Map<ParameterDescriptor, Parameter> getParameters() {
         readLock.lock();