From 7b0cc120e4bcee96d99c6f6f24ba5327e14eab12 Mon Sep 17 00:00:00 2001 From: Fabian Steeg Date: Fri, 19 Nov 2021 17:02:01 +0100 Subject: [PATCH] Support accessing strings in arrays via index and wildcard (#65) --- .../java/org/metafacture/metafix/Value.java | 49 +++++- .../metafix/MetafixRecordTest.java | 165 +++++++++++++++--- 2 files changed, 183 insertions(+), 31 deletions(-) diff --git a/metafix/src/main/java/org/metafacture/metafix/Value.java b/metafix/src/main/java/org/metafacture/metafix/Value.java index f31f4259..0f120bd8 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Value.java +++ b/metafix/src/main/java/org/metafacture/metafix/Value.java @@ -148,6 +148,10 @@ public static boolean isNull(final Value value) { return value == null || value.isNull(); } + private static boolean isNumber(final String s) { + return s.matches("\\d+"); + } + public Array asArray() { if (isArray()) { return array; @@ -292,6 +296,21 @@ public String asString() { return list.toString(); } + public void remove(final int index) { + list.remove(index); + } + + private void removeNested(final String[] fields, final Value value) { + if (fields.length > 1 && isNumber(fields[1])) { + final int index = Integer.parseInt(fields[1]) - 1; + if (index >= 0 && index < value.asArray().size()) { + if (value.asArray().get(index).isString()) { + value.asArray().remove(index); + } + } + } + } + } /** @@ -397,7 +416,18 @@ private Value find(final String[] fields) { private Value findNested(final String field, final String[] remainingFields) { final Value value = get(field); - // TODO: array of maps, like in insert nested + if (value.isArray()) { + if (remainingFields.length > 0 && isNumber(remainingFields[0])) { + final int index = Integer.parseInt(remainingFields[0]) - 1; + if (index >= 0 && index < value.asArray().size()) { + final Value nestedValue = value.asArray().get(index); + if (nestedValue.isString()) { + return nestedValue; + } + // TODO: array of maps, like in insert nested + } + } + } if (value.isHash()) { return value.asHash().find(remainingFields); @@ -467,7 +497,12 @@ else if (value.isArray()) { } break; default: - array.add(newHash(h -> h.insert(mode, remainingFields, newValue))); + if (isNumber(remainingFields[0])) { + array.add(new Value(newValue)); + } + else { + array.add(newHash(h -> h.insert(mode, remainingFields, newValue))); + } break; } } @@ -499,7 +534,13 @@ private void removeNested(final String[] fields) { remove(field); } else if (containsField(field)) { - get(field).asHash().removeNested(Arrays.copyOfRange(fields, 1, fields.length)); + final Value value = get(field); + if (value.isArray()) { + value.asArray().removeNested(fields, value); + } + if (value.isHash()) { + value.asHash().removeNested(Arrays.copyOfRange(fields, 1, fields.length)); + } } } @@ -510,7 +551,7 @@ public void copy(final List params) { } public void transformFields(final List params, final UnaryOperator operator) { - final String field = params.get(0); + final String field = params.get(0).replace(".*", ""); final Value value = find(field); if (value != null) { diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java index 45dddecb..1a0ce1f3 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java @@ -83,33 +83,6 @@ public void internalIdUsage() { }); } - @Test - @Disabled("TODO: how to handle repeated entities: turn to array vs. merge because it's the same?") - public void entitiesPassThroughRepeatEntity() { - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "vacuum()"), - i -> { - i.startRecord("1"); - i.startEntity("some"); - i.literal("field", "value1"); - i.endEntity(); - i.startEntity("some"); - i.literal("field", "value2"); - i.endEntity(); - i.endRecord(); - }, (o, f) -> { - o.get().startRecord("1"); - o.get().startEntity("some[]"); - o.get().startEntity(""); - o.get().literal("field", "value1"); - o.get().endEntity(); - o.get().startEntity(""); - o.get().literal("field", "value2"); - f.apply(2).endEntity(); - o.get().endRecord(); - }); - } - @Test public void entitiesPassThroughRepeatNestedEntity() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( @@ -715,4 +688,142 @@ public void nulls() { o.get().endRecord(); }); } + + @Test + public void repeatToArray() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "vacuum()"), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("name"); + o.get().literal("1", "max"); + o.get().literal("2", "mo"); + o.get().endEntity(); + o.get().endRecord(); + }); + } + + @Test + public void accessArrayByIndex() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('name.2')"), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("name"); + o.get().literal("1", "max"); + o.get().literal("2", "MO"); + o.get().endEntity(); + o.get().endRecord(); + }); + } + + @Test + public void accessArrayByWildcard() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('name.*')"), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("name"); + o.get().literal("1", "MAX"); + o.get().literal("2", "MO"); + o.get().endEntity(); + o.get().endRecord(); + }); + } + + @Test + @Disabled("TODO: Repeated entities should become arrays, see #65") + public void repeatToArrayOfObjects() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "vacuum()"), + i -> { + i.startRecord("1"); + i.startEntity("author"); + i.literal("name", "max"); + i.endEntity(); + i.startEntity("author"); + i.literal("name", "mo"); + i.endEntity(); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("author"); + o.get().startEntity("1"); + o.get().literal("name", "max"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("name", "mo"); + f.apply(2).endEntity(); + o.get().endRecord(); + }); + } + + @Test + @Disabled("TODO: Access arrays of objects by index, see #65") + public void accessArrayOfObjectsByIndex() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('author.2.name')"), + i -> { + i.startRecord("1"); + i.startEntity("author"); + i.literal("name", "max"); + i.endEntity(); + i.startEntity("author"); + i.literal("name", "mo"); + i.endEntity(); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("author"); + o.get().startEntity("1"); + o.get().literal("name", "max"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("name", "MO"); + f.apply(2).endEntity(); + o.get().endRecord(); + }); + } + + @Test + @Disabled("TODO: Access arrays of objects by wildcard, see #65") + public void accessArrayOfObjectsByWildcard() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('author.*.name')"), + i -> { + i.startRecord("1"); + i.startEntity("author"); + i.literal("name", "max"); + i.endEntity(); + i.startEntity("author"); + i.literal("name", "mo"); + i.endEntity(); + i.endRecord(); + }, (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("author"); + o.get().startEntity("1"); + o.get().literal("name", "MAX"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("name", "MO"); + f.apply(2).endEntity(); + o.get().endRecord(); + }); + } }