You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2019/03/22 19:26:11 UTC
[metamodel] 01/03: METAMODEL-1172: Improves searching based on an
expression in MapValueFunction: Add support in cases that the outer most
type is a list/array. Add support for searching in nested lists or
multidimensional arrays.
This is an automated email from the ASF dual-hosted git repository.
kaspersor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/metamodel.git
commit f6329988d8d295c235557a28d988e74ca41858a3
Author: George Katiforis <ge...@hotmail.com>
AuthorDate: Tue Mar 19 01:08:45 2019 +0200
METAMODEL-1172: Improves searching based on an expression in MapValueFunction:
Add support in cases that the outer most type is a list/array.
Add support for searching in nested lists or multidimensional arrays.
---
.../apache/metamodel/query/MapValueFunction.java | 8 +--
.../org/apache/metamodel/util/CollectionUtils.java | 81 ++++++++++++++--------
.../metamodel/query/MapValueFunctionTest.java | 28 ++++++++
.../apache/metamodel/util/CollectionUtilsTest.java | 80 +++++++++++++++++++++
4 files changed, 162 insertions(+), 35 deletions(-)
diff --git a/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java b/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
index e017747..a1ca40e 100644
--- a/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
+++ b/core/src/main/java/org/apache/metamodel/query/MapValueFunction.java
@@ -18,8 +18,6 @@
*/
package org.apache.metamodel.query;
-import java.util.Map;
-
import org.apache.metamodel.data.Row;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.util.CollectionUtils;
@@ -38,11 +36,7 @@ public final class MapValueFunction extends DefaultScalarFunction {
throw new IllegalArgumentException("Expecting path parameter to MAP_VALUE function");
}
final Object value = row.getValue(operandItem);
- if (value instanceof Map) {
- final Map<?, ?> map = (Map<?, ?>) value;
- return CollectionUtils.find(map, (String) parameters[0]);
- }
- return null;
+ return CollectionUtils.find(value, (String) parameters[0]);
}
@Override
diff --git a/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java b/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
index 5246a3a..3dc0d23 100644
--- a/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
+++ b/core/src/main/java/org/apache/metamodel/util/CollectionUtils.java
@@ -39,8 +39,9 @@ public final class CollectionUtils {
}
/**
- * Searches a map for a given key. The key can be a regular map key, or a
- * simple expression of the form:
+ * Searches a map, a list or array for a given key.
+ * Also can be used with nested lists and multidimensional arrays.
+ * The key can be a regular map key, or a simple expression of the form:
*
* <ul>
* <li>foo.bar (will lookup 'foo', and then 'bar' in a potential nested map)
@@ -48,27 +49,39 @@ public final class CollectionUtils {
* <li>foo.bar[0].baz (will lookup 'foo', then 'bar' in a potential nested
* map, then pick the first element in case it is a list/array and then pick
* 'baz' from the potential map at that position).
+ * </li>
+ * <li>[1]foo.bar[0][1].baz (pick the second element in list/array and then
+ * lookup 'foo' then 'bar' in a potential nested map, then pick the second
+ * element in the first row in nested lists or 2 dimensional array and then 'baz' in
+ * a potential nested map).
+ * </li>
* </ul>
*
- * @param map
- * the map to search in
+ * @param collection
+ * the map, list or array to search in
* @param key
* the key to resolve
- * @return the object in the map with the given key/expression. Or null if
+ * @return the object in the map, list or array with the given key/expression. Or null if
* it does not exist.
*/
- public static Object find(Map<?, ?> map, String key) {
- if (map == null || key == null) {
+ public static Object find(Object collection, String key) {
+ if (collection == null || key == null) {
return null;
}
- final Object result = map.get(key);
- if (result == null) {
- return find(map, key, 0);
+ if(collection instanceof Map){
+ Map<?, ?> map = (Map<?, ?>) collection;
+ final Object result = map.get(key);
+ if (result == null) {
+ return find(map, key, 0);
+ }
+ return result;
+ } else if(collection instanceof List || collection.getClass().isArray()){
+ return find( collection, key, 0);
}
- return result;
+ return null;
}
- private static Object find(Map<?, ?> map, String key, int fromIndex) {
+ private static Object find(final Object collection, String key, int fromIndex) {
final int indexOfDot = key.indexOf('.', fromIndex);
final int indexOfBracket = key.indexOf('[', fromIndex);
int indexOfEndBracket = -1;
@@ -79,12 +92,28 @@ public final class CollectionUtils {
if (hasBracket) {
// also check that there is an end-bracket
- indexOfEndBracket = key.indexOf("]", indexOfBracket);
+ indexOfEndBracket = key.indexOf(']', indexOfBracket);
hasBracket = indexOfEndBracket != -1;
if (hasBracket) {
final String indexString = key.substring(indexOfBracket + 1, indexOfEndBracket);
try {
arrayIndex = Integer.parseInt(indexString);
+ if(collection instanceof List || collection.getClass().isArray()){
+ Object obj = null;
+ if(collection instanceof List){
+ obj = ((List) collection).get(arrayIndex);
+ } else if (collection.getClass().isArray()) {
+ obj = Array.get(collection, arrayIndex);
+ }
+ key = key.substring( indexOfEndBracket+1, key.length());
+ if(key.startsWith(".")){
+ key = key.substring(1, key.length());
+ }
+ if(key.isEmpty()){
+ return obj;
+ }
+ return find(obj, key );
+ }
} catch (NumberFormatException e) {
// not a valid array/list index
hasBracket = false;
@@ -102,9 +131,9 @@ public final class CollectionUtils {
if (hasDot) {
final String prefix = key.substring(0, indexOfDot);
- final Object nestedObject = map.get(prefix);
+ final Object nestedObject = ((Map<?,?>)collection).get(prefix);
if (nestedObject == null) {
- return find(map, key, indexOfDot + 1);
+ return find(collection, key, indexOfDot + 1);
}
if (nestedObject instanceof Map) {
final String remainingPart = key.substring(indexOfDot + 1);
@@ -115,10 +144,13 @@ public final class CollectionUtils {
}
if (hasBracket) {
- final String prefix = key.substring(0, indexOfBracket);
- final Object nestedObject = map.get(prefix);
- if (nestedObject == null) {
- return find(map, key, indexOfBracket + 1);
+ Object nestedObject = null;
+ if(collection instanceof Map){
+ final String prefix = key.substring(0, indexOfBracket);
+ nestedObject = ((Map<?,?>)collection).get(prefix);
+ if (nestedObject == null) {
+ return find(collection, key, indexOfBracket + 1);
+ }
}
String remainingPart = key.substring(indexOfEndBracket + 1);
@@ -127,7 +159,7 @@ public final class CollectionUtils {
final Object valueAtIndex;
if (nestedObject instanceof List) {
valueAtIndex = ((List<?>) nestedObject).get(arrayIndex);
- } else if (nestedObject.getClass().isArray()) {
+ } else if (nestedObject!= null && nestedObject.getClass().isArray()) {
valueAtIndex = Array.get(nestedObject, arrayIndex);
} else {
// no way to extract from a non-array and non-list
@@ -143,14 +175,7 @@ public final class CollectionUtils {
return valueAtIndex;
}
- if (valueAtIndex instanceof Map) {
- @SuppressWarnings("unchecked")
- final Map<String, ?> nestedMap = (Map<String, ?>) valueAtIndex;
- return find(nestedMap, remainingPart);
- } else {
- // not traversing any further. Should we want to add
- // support for double-sided arrays, we could do it here.
- }
+ return find(valueAtIndex, remainingPart);
}
} catch (IndexOutOfBoundsException e) {
diff --git a/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java b/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
index 0d87c0c..bbf39c5 100644
--- a/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
+++ b/core/src/test/java/org/apache/metamodel/query/MapValueFunctionTest.java
@@ -20,7 +20,9 @@ package org.apache.metamodel.query;
import static org.junit.Assert.assertEquals;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.metamodel.data.DefaultRow;
@@ -46,6 +48,32 @@ public class MapValueFunctionTest {
}
@Test
+ public void testGetValueFromList() throws Exception {
+ final List<Map<String, Object>> value = new ArrayList<>();
+ final Map<String, Object> innerMap = new HashMap<>();
+ innerMap.put("bar", "baz");
+ value.add(innerMap);
+ final SelectItem operandItem = new SelectItem("foo", "f");
+ final Row row = new DefaultRow(new SimpleDataSetHeader(new SelectItem[] { operandItem }),
+ new Object[] { value });
+ final Object v1 = function.evaluate(row, new Object[] { "[0]bar" }, operandItem);
+ assertEquals("baz", v1.toString());
+ }
+
+ @Test
+ public void testGetValueFromArray() throws Exception {
+
+ final Map<String, Object> innerMap = new HashMap<>();
+ innerMap.put("bar", "baz");
+ final Object[] value = {innerMap};
+ final SelectItem operandItem = new SelectItem("foo", "f");
+ final Row row = new DefaultRow(new SimpleDataSetHeader(new SelectItem[] { operandItem }),
+ new Object[] { value });
+ final Object v1 = function.evaluate(row, new Object[] { "[0]bar" }, operandItem);
+ assertEquals("baz", v1.toString());
+ }
+
+ @Test
public void testNotAMap() throws Exception {
final SelectItem operandItem = new SelectItem("foo", "f");
final Row row = new DefaultRow(new SimpleDataSetHeader(new SelectItem[] { operandItem }),
diff --git a/core/src/test/java/org/apache/metamodel/util/CollectionUtilsTest.java b/core/src/test/java/org/apache/metamodel/util/CollectionUtilsTest.java
index c5a5a6f..3dbcb83 100644
--- a/core/src/test/java/org/apache/metamodel/util/CollectionUtilsTest.java
+++ b/core/src/test/java/org/apache/metamodel/util/CollectionUtilsTest.java
@@ -56,6 +56,86 @@ public class CollectionUtilsTest extends TestCase {
assertEquals(null, CollectionUtils.find(map, "Interests[2]"));
}
+ public void testFindWithNestedTripleListsAndMaps() throws Exception {
+ final Map<String, Object> map3 = new HashMap<>();
+ map3.put("opt.ion1", "W");
+ final List nestedList = Arrays.asList(Arrays.asList("G", "H", map3), "K");
+ final List tripleNestedList = Arrays.asList(Arrays.asList("L", nestedList), "O");
+
+ final Map<String, Object> nestedMap= new HashMap<>();
+ nestedMap.put("option1", "F");
+ nestedMap.put("option2", nestedList);
+ nestedMap.put("option3", tripleNestedList);
+
+ final Map<String, Object> map= new HashMap<>();
+ map.put("option1", "A");
+ map.put("option2", "B");
+ map.put("option3", nestedMap);
+
+ final Map<String, Object> map2 = new HashMap<>();
+ map2.put("option1", "C");
+
+ List list = Arrays.asList(map, map2);
+
+ final Map<String, Object> options4 = new HashMap<>();
+ options4.put("list", list);
+
+ assertEquals(map, CollectionUtils.find(list, "[0]"));
+ assertEquals("B", CollectionUtils.find(list, "[0].option2"));
+ assertEquals("B", CollectionUtils.find(options4, "list[0].option2"));
+ assertEquals("C", CollectionUtils.find(options4, "list[1].option1"));
+ assertEquals("F", CollectionUtils.find(list, "[0].option3.option1"));
+ assertEquals( nestedList, CollectionUtils.find(list, "[0].option3.option2"));
+ assertEquals( "K", CollectionUtils.find(list, "[0].option3.option2[1]"));
+ assertEquals("G", CollectionUtils.find(list, "[0].option3.option2[0][0]"));
+ assertEquals("H", CollectionUtils.find(list, "[0].option3.option2[0][1]"));
+ assertEquals("K", CollectionUtils.find(list, "[0].option3.option3[0][1][1]"));
+ assertEquals(map3, CollectionUtils.find(list, "[0].option3.option3[0][1][0][2]"));
+ assertEquals("W", CollectionUtils.find(list, "[0].option3.option3[0][1][0][2].opt.ion1"));
+ assertEquals(null, CollectionUtils.find(list, "[0].option3.option3[0][1][0][2].opt.ion2"));
+ }
+
+ public void testFindWithNestedArraysAndMaps() throws Exception {
+ final Map<String, Object> map3 = new HashMap<>();
+ map3.put("opt.ion1", "W");
+ final Object nestedList[] = {"G", "H", map3, "K"};
+ final Object twoDimensionalArray[][] = {{"L", nestedList},
+ {"M", nestedList, "L"},};
+
+ final Map<String, Object> nestedMap= new HashMap<>();
+ nestedMap.put("option1", "F");
+ nestedMap.put("option2", nestedList);
+ nestedMap.put("option3", twoDimensionalArray);
+
+ final Map<String, Object> map= new HashMap<>();
+ map.put("option1", "A");
+ map.put("option2", "B");
+ map.put("option3", nestedMap);
+
+ final Map<String, Object> map2 = new HashMap<>();
+ map2.put("option1", "C");
+
+ Object list[] = {map, map2};
+
+ final Map<String, Object> options4 = new HashMap<>();
+ options4.put("list", list);
+
+ assertEquals(map, CollectionUtils.find(list, "[0]"));
+ assertEquals("B", CollectionUtils.find(list, "[0].option2"));
+ assertEquals("B", CollectionUtils.find(options4, "list[0].option2"));
+ assertEquals("C", CollectionUtils.find(options4, "list[1].option1"));
+ assertEquals("F", CollectionUtils.find(list, "[0].option3.option1"));
+ assertEquals( nestedList, CollectionUtils.find(list, "[0].option3.option2"));
+ assertEquals( "H", CollectionUtils.find(list, "[0].option3.option2[1]"));
+ assertEquals("H", CollectionUtils.find(list, "[0].option3.option3[0][1][1]"));
+ assertEquals("L", CollectionUtils.find(list, "[0].option3.option3[1][2]"));
+ assertEquals(map3, CollectionUtils.find(list, "[0].option3.option3[0][1][2]"));
+ assertEquals("W", CollectionUtils.find(list, "[0].option3.option3[0][1][2].opt.ion1"));
+ assertEquals(map3, CollectionUtils.find(list, "[0].option3.option3[0][1][2]"));
+ assertEquals(null, CollectionUtils.find(list, "[0].option3.option3[0][1][2].opt.ion2"));
+ }
+
+
public void testFindWithNestedListsAndMaps() throws Exception {
final Map<String, Object> address1 = new LinkedHashMap<String, Object>();
address1.put("city", "Stockholm");