You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2021/02/03 09:21:43 UTC

[ignite-3] branch main updated: IGNITE-14102 ConfigurationUtil class with escaping and searching in traversable tree methods. (#40)

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

ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new c9ebaef  IGNITE-14102 ConfigurationUtil class with escaping and searching in traversable tree methods. (#40)
c9ebaef is described below

commit c9ebaef410abdebe4e126ef9822d9d585482d894
Author: ibessonov <be...@gmail.com>
AuthorDate: Wed Feb 3 12:21:22 2021 +0300

    IGNITE-14102 ConfigurationUtil class with escaping and searching in traversable tree methods. (#40)
    
    Signed-off-by: ibessonov <be...@gmail.com>
---
 .../configuration/util/ConfigurationUtilTest.java  | 174 +++++++++++++++++++++
 .../configuration/util/ConfigurationUtil.java      | 113 +++++++++++++
 .../configuration/util/KeyNotFoundException.java   |  31 ++++
 3 files changed, 318 insertions(+)

diff --git a/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java
new file mode 100644
index 0000000..45cc921
--- /dev/null
+++ b/modules/configuration-annotation-processor/src/test/java/org/apache/ignite/configuration/util/ConfigurationUtilTest.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.util;
+
+import java.util.List;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.util.impl.ParentNode;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/** */
+public class ConfigurationUtilTest {
+    /** */
+    @Test
+    public void escape() {
+        assertEquals("foo", ConfigurationUtil.escape("foo"));
+
+        assertEquals("foo\\.bar", ConfigurationUtil.escape("foo.bar"));
+
+        assertEquals("foo\\\\bar", ConfigurationUtil.escape("foo\\bar"));
+
+        assertEquals("\\\\a\\.b\\\\c\\.", ConfigurationUtil.escape("\\a.b\\c."));
+    }
+
+    /** */
+    @Test
+    public void unescape() {
+        assertEquals("foo", ConfigurationUtil.unescape("foo"));
+
+        assertEquals("foo.bar", ConfigurationUtil.unescape("foo\\.bar"));
+
+        assertEquals("foo\\bar", ConfigurationUtil.unescape("foo\\\\bar"));
+
+        assertEquals("\\a.b\\c.", ConfigurationUtil.unescape("\\\\a\\.b\\\\c\\."));
+    }
+
+    /** */
+    @Test
+    public void split() {
+        assertEquals(List.of("a", "b.b", "c\\c", ""), ConfigurationUtil.split("a.b\\.b.c\\\\c."));
+    }
+
+    /** */
+    @Test
+    public void join() {
+        assertEquals("a.b\\.b.c\\\\c", ConfigurationUtil.join(List.of("a", "b.b", "c\\c")));
+    }
+
+    /** */
+    @Config
+    public static class ParentConfigurationSchema {
+        /** */
+        @NamedConfigValue
+        private NamedElementConfigurationSchema elements;
+    }
+
+    /** */
+    @Config
+    public static class NamedElementConfigurationSchema {
+        /** */
+        @ConfigValue
+        private ChildConfigurationSchema child;
+    }
+
+    /** */
+    @Config
+    public static class ChildConfigurationSchema {
+        /** */
+        @Value
+        private String str;
+    }
+
+    /** */
+    @Test
+    public void findSuccessfully() {
+        var parent = new ParentNode();
+
+        parent.changeElements(elements ->
+            elements.put("name", element ->
+                element.changeChild(child ->
+                    child.changeStr("value")
+                )
+            )
+        );
+
+        assertSame(
+            parent,
+            ConfigurationUtil.find(List.of(), parent)
+        );
+
+        assertSame(
+            parent.elements(),
+            ConfigurationUtil.find(List.of("elements"), parent)
+        );
+
+        assertSame(
+            parent.elements().get("name"),
+            ConfigurationUtil.find(List.of("elements", "name"), parent)
+        );
+
+        assertSame(
+            parent.elements().get("name").child(),
+            ConfigurationUtil.find(List.of("elements", "name", "child"), parent)
+        );
+
+        assertSame(
+            parent.elements().get("name").child().str(),
+            ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent)
+        );
+    }
+
+    /** */
+    @Test
+    public void findNulls() {
+        var parent = new ParentNode();
+
+        assertNull(ConfigurationUtil.find(List.of("elements", "name"), parent));
+
+        parent.changeElements(elements -> elements.put("name", element -> {}));
+
+        assertNull(ConfigurationUtil.find(List.of("elements", "name", "child"), parent));
+
+        parent.elements().get("name").changeChild(child -> {});
+
+        assertNull(ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent));
+    }
+
+    /** */
+    @Test
+    public void findUnsuccessfully() {
+        var parent = new ParentNode();
+
+        assertThrows(
+            KeyNotFoundException.class,
+            () -> ConfigurationUtil.find(List.of("elements", "name", "child"), parent)
+        );
+
+        parent.changeElements(elements -> elements.put("name", element -> {}));
+
+        assertThrows(
+            KeyNotFoundException.class,
+            () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str"), parent)
+        );
+
+        parent.elements().get("name").changeChild(child -> child.changeStr("value"));
+
+        assertThrows(
+            KeyNotFoundException.class,
+            () -> ConfigurationUtil.find(List.of("elements", "name", "child", "str", "foo"), parent)
+        );
+    }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java
new file mode 100644
index 0000000..c752fc8
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/util/ConfigurationUtil.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.util;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+import java.util.stream.Collectors;
+import org.apache.ignite.configuration.tree.ConfigurationVisitor;
+import org.apache.ignite.configuration.tree.InnerNode;
+import org.apache.ignite.configuration.tree.NamedListNode;
+import org.apache.ignite.configuration.tree.TraversableTreeNode;
+
+/** */
+public interface ConfigurationUtil {
+    /** */
+    static String escape(String key) {
+        return key.replaceAll("([.\\\\])", "\\\\$1");
+    }
+
+    /** */
+    static String unescape(String key) {
+        return key.replaceAll("\\\\([.\\\\])", "$1");
+    }
+
+    /** */
+    static List<String> split(String keys) {
+        String[] split = keys.split("(?<!\\\\)[.]", -1);
+
+        for (int i = 0; i < split.length; i++)
+            split[i] = unescape(split[i]);
+
+        return Arrays.asList(split);
+    }
+
+    /** */
+    static String join(List<String> keys) {
+        return keys.stream().map(ConfigurationUtil::escape).collect(Collectors.joining("."));
+    }
+
+    /**
+     * Search for the configuration node by the list of keys.
+     *
+     * @param keys Random access list with keys.
+     * @param node Node where method will search for subnode.
+     * @return Either {@link TraversableTreeNode} or {@link Serializable} depending on the keys and schema.
+     * @throws KeyNotFoundException If node is not found.
+     */
+    static Object find(List<String> keys, TraversableTreeNode node) throws KeyNotFoundException {
+        assert keys instanceof RandomAccess : keys.getClass();
+
+        var visitor = new ConfigurationVisitor() {
+            /** */
+            private int i;
+
+            /** */
+            private Object res;
+
+            @Override public void visitLeafNode(String key, Serializable val) {
+                if (i != keys.size())
+                    throw new KeyNotFoundException("Configuration value '" + join(keys.subList(0, i)) + "' is a leaf");
+
+                res = val;
+            }
+
+            @Override public void visitInnerNode(String key, InnerNode node) {
+                if (i == keys.size())
+                    res = node;
+                else if (node == null)
+                    throw new KeyNotFoundException("Configuration node '" + join(keys.subList(0, i)) + "' is null");
+                else {
+                    try {
+                        node.traverseChild(keys.get(i++), this);
+                    }
+                    catch (NoSuchElementException e) {
+                        throw new KeyNotFoundException("Configuration '" + join(keys.subList(0, i)) + "' is not found");
+                    }
+                }
+            }
+
+            @Override public <N extends InnerNode> void visitNamedListNode(String key, NamedListNode<N> node) {
+                if (i == keys.size())
+                    res = node;
+                else {
+                    String name = keys.get(i++);
+
+                    visitInnerNode(name, node.get(name));
+                }
+            }
+        };
+
+        node.accept(null, visitor);
+
+        return visitor.res;
+    }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java
new file mode 100644
index 0000000..90c605f
--- /dev/null
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/util/KeyNotFoundException.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.configuration.util;
+
+/** */
+public class KeyNotFoundException extends RuntimeException {
+    /** Serial version uid. */
+    private static final long serialVersionUID = 0L;
+
+    /**
+     * @param msg Message.
+     */
+    public KeyNotFoundException(String msg) {
+        super(msg);
+    }
+}