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

orpiske pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new b319bb770a7 CAMEL-19868: parse manually when necessary (#11408)
b319bb770a7 is described below

commit b319bb770a7669615d923cb998b73537b523dda0
Author: Otavio Rodolfo Piske <[email protected]>
AuthorDate: Fri Sep 15 12:00:58 2023 +0200

    CAMEL-19868: parse manually when necessary (#11408)
    
    The AbstractCamelCatalog needs to be able to parse some endpoint properties 
manually, so that certain groups of characters and URI items are properly 
evaluated.
---
 .../camel/catalog/impl/AbstractCamelCatalog.java   |   2 +-
 .../apache/camel/catalog/impl/CatalogHelper.java   | 188 +++++++++++++++++++++
 2 files changed, 189 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
index 28df093493e..abe4bd56d5f 100644
--- 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
+++ 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
@@ -674,7 +674,7 @@ public abstract class AbstractCamelCatalog {
         }
 
         // now parse the uri parameters
-        Map<String, Object> parameters = URISupport.parseParameters(u);
+        Map<String, Object> parameters = CatalogHelper.parseParameters(u);
 
         // and covert the values to String so its JMX friendly
         while (!parameters.isEmpty()) {
diff --git 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/CatalogHelper.java
 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/CatalogHelper.java
index 4d39ce96bf7..c07f1120a5c 100644
--- 
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/CatalogHelper.java
+++ 
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/CatalogHelper.java
@@ -21,12 +21,23 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.LineNumberReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
 
 public final class CatalogHelper {
+    private static final Charset CHARSET = StandardCharsets.UTF_8;
 
     private CatalogHelper() {
     }
@@ -113,4 +124,181 @@ public final class CatalogHelper {
             return true;
         }
     }
+
+
+
+    /**
+     * Parses the query parameters of the uri (eg the query part) manually.
+     * <p>
+     * Note: we cannot use {@link URISupport#parseParameters(URI)} because it 
uses the URIScanner and that scanner does
+     * not support "{{" and "}}".
+     *
+     * @param  uri                the uri
+     * @return                    the parameters, or an empty map if no 
parameters (eg never null)
+     * @throws URISyntaxException is thrown if uri has invalid syntax.
+     */
+    static Map<String, Object> parseParameters(URI uri) throws 
URISyntaxException {
+        String query = uri.getQuery();
+        if (query == null) {
+            String schemeSpecificPart = uri.getSchemeSpecificPart();
+            query = StringHelper.after(schemeSpecificPart, "?");
+            if (query == null) {
+                return new LinkedHashMap<>(0);
+            }
+        } else {
+            query = URISupport.stripPrefix(query, "?");
+        }
+        return parseQueryManually(query);
+    }
+
+
+    /**
+     * Parses the query part of the uri (eg the parameters) manually. This 
method is mostly used by the CamelCatalog in
+     * order to be able to handle certain special characters markers (i.e.: 
"{{" and "}}"). It should not be used anywhere
+     * else.
+     * <p/>
+     * The URI parameters will by default be URI encoded. However, you can 
define a parameter values with the syntax:
+     * <tt>key=RAW(value)</tt> which tells Camel to not encode the value, and 
use the value as is (eg key=value) and the
+     * value has <b>not</b> been encoded.
+     *
+     * @param  uri                the uri
+     * @return                    the parameters, or an empty map if no 
parameters (eg never null)
+     * @throws URISyntaxException is thrown if uri has invalid syntax.
+     */
+    static Map<String, Object> parseQueryManually(String uri) throws 
URISyntaxException {
+        if (uri == null || uri.isEmpty()) {
+            // return an empty map
+            return Collections.emptyMap();
+        }
+
+        // must check for trailing & as the uri.split("&") will ignore those
+        if (uri.endsWith("&")) {
+            throw new URISyntaxException(
+                    uri, "Invalid uri syntax: Trailing & marker found. "
+                    + "Check the uri and remove the trailing & marker.");
+        }
+
+        // need to parse the uri query parameters manually as we cannot rely 
on splitting by &,
+        // as & can be used in a parameter value as well.
+
+        // use a linked map so the parameters is in the same order
+        Map<String, Object> rc = new LinkedHashMap<>();
+
+        boolean isKey = true;
+        boolean isValue = false;
+        boolean isRaw = false;
+        StringBuilder key = new StringBuilder();
+        StringBuilder value = new StringBuilder();
+
+        // parse the uri parameters char by char
+        for (int i = 0; i < uri.length(); i++) {
+            // current char
+            char ch = uri.charAt(i);
+
+            isRaw = isRaw(isRaw, value);
+
+            // if its a key and there is a = sign then the key ends and we are 
in value mode
+            if (isKey && ch == '=') {
+                isKey = false;
+                isValue = true;
+                isRaw = false;
+                continue;
+            }
+
+            // the & denote parameter is ended
+            if (ch == '&') {
+                // parameter is ended, as we hit & separator
+                String aKey = key.toString();
+                // the key may be a placeholder of options which we then do 
not know what is
+                addKeyIfPresent(aKey, value, rc, isRaw);
+                key.setLength(0);
+                value.setLength(0);
+                isKey = true;
+                isValue = false;
+                isRaw = false;
+                continue;
+            }
+
+            // regular char so add it to the key or value
+            if (isKey) {
+                key.append(ch);
+            } else if (isValue) {
+                value.append(ch);
+            }
+        }
+
+        // any left over parameters, then add that
+        if (!key.isEmpty()) {
+            String aKey = key.toString();
+            // the key may be a placeholder of options which we then do not 
know what is
+            addKeyIfPresent(aKey, value, rc, isRaw);
+        }
+
+        return rc;
+
+    }
+
+    private static boolean isRaw(boolean isRaw, StringBuilder value) {
+        // are we a raw value
+        for (int j = 0; j < URISupport.RAW_TOKEN_START.length; j++) {
+            String rawTokenStart = URISupport.RAW_TOKEN_PREFIX + 
URISupport.RAW_TOKEN_START[j];
+            isRaw = value.toString().startsWith(rawTokenStart);
+            if (isRaw) {
+                break;
+            }
+        }
+        return isRaw;
+    }
+
+    private static void addKeyIfPresent(String aKey, StringBuilder value, 
Map<String, Object> rc, boolean isRaw) {
+        boolean validKey = !aKey.startsWith("{{") && !aKey.endsWith("}}");
+        if (validKey) {
+            String valueStr = optionallyDecode(value.toString(), isRaw);
+
+            addParameter(aKey, valueStr, rc, isRaw);
+        }
+    }
+
+
+    private static void addParameter(String name, final String value, 
Map<String, Object> map, boolean isRaw) {
+        name = URLDecoder.decode(name, CHARSET);
+
+        // does the key already exist?
+        if (map.containsKey(name)) {
+            // yes it does, so make sure we can support multiple values, but 
using a list
+            // to hold the multiple values
+            map.computeIfPresent(name, (k, v) -> replaceWithList(v, value));
+        } else {
+            map.put(name, value);
+        }
+    }
+
+    private static String optionallyDecode(String value, boolean isRaw) {
+        if (!isRaw) {
+            // need to replace % with %25
+            return URLDecoder.decode(value.replace("%", "%25"), CHARSET);
+        }
+
+        return value;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Object replaceWithList(Object oldValue, String newValue) {
+        List<String> list;
+        if (oldValue instanceof List) {
+            list = (List<String>) oldValue;
+            list.add(newValue);
+
+        } else {
+            // create a new list to hold the multiple values
+            list = new ArrayList<>();
+            String s = oldValue != null ? oldValue.toString() : null;
+            if (s != null) {
+                list.add(s);
+            }
+
+        }
+        return list;
+    }
+
 }

Reply via email to