diff --git a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java index ed36d0ff..4ab11ab4 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java @@ -131,7 +131,7 @@ public void apply(final Metafix metafix, final Record record, final List final String field = params.get(0); record.getList(field, oldValues -> { - final String newValue = String.format(params.get(1), oldValues.stream().toArray()); + final String newValue = String.format(params.get(1), oldValues.stream().map(v -> v.asString()).toArray()); record.replace(field, new Value(Arrays.asList(new Value(newValue)))); }); } diff --git a/metafix/src/main/java/org/metafacture/metafix/FixPath.java b/metafix/src/main/java/org/metafacture/metafix/FixPath.java index 92b0bc2e..5dd8ff0c 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixPath.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixPath.java @@ -26,6 +26,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.UnaryOperator; +import java.util.stream.Collectors; /** * Our goal here is something like https://metacpan.org/pod/Catmandu::Path::simple @@ -51,12 +52,10 @@ private FixPath(final String[] path) { /*package-private*/ Value findIn(final Hash hash) { final String currentSegment = path[0]; final FixPath remainingPath = new FixPath(tail(path)); - if (currentSegment.equals(ASTERISK)) { // TODO: search in all elements of value.asHash()? return remainingPath.findIn(hash); } - final Value value = hash.get(currentSegment); return value == null || path.length == 1 ? value : value.extractType((m, c) -> m .ifArray(a -> c.accept(remainingPath.findIn(a))) @@ -70,7 +69,15 @@ private FixPath(final String[] path) { if (path.length > 0) { final String currentSegment = path[0]; if (currentSegment.equals(ASTERISK)) { - result = Value.newArray(a -> array.forEach(v -> a.add(findInValue(v, tail(path))))); + result = Value.newArray(resultArray -> array.forEach(v -> { + final Value findInValue = findInValue(v, tail(path)); + if (findInValue != null) { + findInValue.matchType() + // flatten result arrays (use Value#path for structure) + .ifArray(a -> a.forEach(resultArray::add)) + .orElse(c -> resultArray.add(findInValue)); + } + })); } else if (Value.isNumber(currentSegment)) { final int index = Integer.parseInt(currentSegment) - 1; // TODO: 0-based Catmandu vs. 1-based Metafacture @@ -111,37 +118,58 @@ public String toString() { } /*package-private*/ void transformIn(final Hash hash, final UnaryOperator operator) { - // basic idea: reuse findIn logic here? setIn(hash, operator.apply(findIn(hash))) - final String currentSegment = path[0]; - final String[] remainingPath = tail(path); + final Value findIn = findIn(hash); +// System.out.println("Found in hash for '" + Arrays.asList(path) + "': " + findIn); + Value.asList(findIn, results -> { + for (int i = 0; i < results.size(); ++i) { + final Value oldValue = results.get(i); + final FixPath p = pathTo(oldValue, i); + oldValue.matchType() + .ifString(s -> { + final Value newValue = new Value(operator.apply(s)); + p.insertInto(hash, InsertMode.REPLACE, newValue); +// System.out.printf("Inserted at '%s': '%s' into '%s'\n", p, newValue, hash); + }) + .orElseThrow(); + } + }); + } - if (currentSegment.equals(ASTERISK)) { - // TODO: search in all elements of value.asHash()? - new FixPath(remainingPath).transformIn(hash, operator); - return; + /*package-private*/ void transformIn(final Hash hash, final BiConsumer> consumer) { + final Value oldValue = findIn(hash); + + if (oldValue != null) { + final Value newValue = oldValue.extractType(consumer); + + if (newValue != null) { + insertInto(hash, InsertMode.REPLACE, newValue); + } } + } - hash.modifyFields(currentSegment, f -> { - final Value value = hash.getField(f); + private FixPath pathTo(final Value oldValue, final int i) { + FixPath result = this; + // One *: replace with index of current result + if (countAsterisks() == 1) { + result = new FixPath(replaceInPath(ASTERISK, i)); + } + // Multiple * or wildcards: use the old value's path + else if (oldValue.getPath() != null && (countAsterisks() >= 2) || hasWildcard()) { + result = new FixPath(oldValue.getPath()); + } + return result; + } - if (value != null) { - if (remainingPath.length == 0) { - hash.removeField(f); + private String[] replaceInPath(final String find, final int i) { + return Arrays.asList(path).stream().map(s -> s.equals(find) ? String.valueOf(i + 1) : s).collect(Collectors.toList()).toArray(new String[] {}); + } - if (operator != null) { - value.matchType() - .ifString(s -> new FixPath(f).insertInto(hash, InsertMode.APPEND, new Value(operator.apply(s)))) - .orElseThrow(); - } - } - else { - value.matchType() - .ifArray(a -> new FixPath(remainingPath).transformIn(a, operator)) - .ifHash(h -> new FixPath(remainingPath).transformIn(h, operator)) - .orElseThrow(); - } - } - }); + private boolean hasWildcard() { + return Arrays.asList(path).stream().filter(s -> s.contains("?") || s.contains("|") || s.matches(".*?\\[.+?\\].*?")).findAny().isPresent(); + } + + private long countAsterisks() { + return Arrays.asList(path).stream().filter(s -> s.equals(ASTERISK)).count(); } /* package-private */ enum InsertMode { @@ -151,54 +179,28 @@ public String toString() { void apply(final Hash hash, final String field, final Value value) { hash.put(field, value); } + + @Override + void apply(final Array array, final String field, final Value value) { + array.set(Integer.valueOf(field) - 1, value); + } }, APPEND { @Override void apply(final Hash hash, final String field, final Value value) { hash.add(field, value); } - }; - - abstract void apply(Hash hash, String field, Value value); - - } - - /*package-private*/ void transformIn(final Hash hash, final BiConsumer> consumer) { - final Value oldValue = findIn(hash); - if (oldValue != null) { - final Value newValue = oldValue.extractType(consumer); - - if (newValue != null) { - insertInto(hash, InsertMode.REPLACE, newValue); + @Override + void apply(final Array array, final String field, final Value value) { + array.add(value); } - } - } + }; - /*package-private*/ void transformIn(final Array array, final UnaryOperator operator) { - // basic idea: reuse findIn logic here? setIn(findIn(array), newValue) - final String currentSegment = path[0]; - final int size = array.size(); + abstract void apply(Hash hash, String field, Value value); - if (path.length == 0 || currentSegment.equals(ASTERISK)) { - for (int i = 0; i < size; ++i) { - transformValueAt(array, i, tail(path), operator); - } - } - else if (Value.isNumber(currentSegment)) { - final int index = Integer.parseInt(currentSegment) - 1; // TODO: 0-based Catmandu vs. 1-based Metafacture - if (index >= 0 && index < size) { - transformValueAt(array, index, tail(path), operator); - } - } - // TODO: WDCD? copy_field('your.name','author[].name'), where name is an array - else { - for (int i = 0; i < size; ++i) { - transformValueAt(array, i, path, operator); - } - } + abstract void apply(Array array, String field, Value newValue); - array.removeIf(v -> Value.isNull(v)); } /*package-private*/ void removeNestedFrom(final Array array) { @@ -236,10 +238,21 @@ else if (hash.containsField(field)) { final String field = path[0]; if (path.length == 1) { if (field.equals(ASTERISK)) { - //TODO: WDCD? insert into each element? + for (int i = 0; i < array.size(); ++i) { + mode.apply(array, "" + (i + 1), newValue); + } } else { - array.add(newValue); + // TODO unify ref usage from below + if ("$append".equals(field)) { + array.add(newValue); + } + else if (Value.isNumber(field)) { + mode.apply(array, field, newValue); + } + else { + throw new IllegalStateException("Non-index access to array at " + field); + } } } else { @@ -257,7 +270,7 @@ else if (hash.containsField(field)) { final String field = path[0]; if (path.length == 1) { if (field.equals(ASTERISK)) { - //TODO: WDCD? insert into each element? + hash.forEach((k, v) -> mode.apply(hash, k, newValue)); //TODO: WDCD? insert into each element? } else { mode.apply(hash, field, newValue); @@ -288,19 +301,6 @@ private String[] tail(final String[] fields) { return Arrays.copyOfRange(fields, 1, fields.length); } - private void transformValueAt(final Array array, final int index, final String[] p, final UnaryOperator operator) { - final Value value = array.get(index); - if (value != null) { - value.matchType() - .ifString(s -> array.set(index, operator != null ? new Value(operator.apply(s)) : null)) - .orElse(v -> v.matchType() - .ifArray(a -> new FixPath(p).transformIn(a, operator)) - .ifHash(h -> new FixPath(p).transformIn(h, operator)) - .orElseThrow() - ); - } - } - private Value processRef(final Value referencedValue, final InsertMode mode, final Value newValue, final String field, final String[] tail) { if (referencedValue != null) { diff --git a/metafix/src/main/java/org/metafacture/metafix/Metafix.java b/metafix/src/main/java/org/metafacture/metafix/Metafix.java index 76ff7226..757be561 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Metafix.java +++ b/metafix/src/main/java/org/metafacture/metafix/Metafix.java @@ -85,10 +85,14 @@ public class Metafix implements StreamPipe, Maps { // checkstyle public Metafix() { flattener.setReceiver(new DefaultStreamReceiver() { + @Override public void literal(final String name, final String value) { - // TODO: keep flattener as option? - // add(currentRecord, name, value); + final String[] split = Value.split(name); + addValue(split[split.length - 1], new Value(value, name)); + // TODO could this help with https://github.com/metafacture/metafacture-fix/issues/147? + // TODO use full path here to insert only once? + // new FixPath(name).insertInto(currentRecord, InsertMode.APPEND, new Value(value)); } }); } @@ -197,7 +201,7 @@ private void emit(final String field, final Value value) { h.forEach(this::emit); outputStreamReceiver.endEntity(); }) - .orElse(v -> outputStreamReceiver.literal(fieldName, v.toString())); + .orElse(v -> outputStreamReceiver.literal(fieldName, v.asString())); } if (isMulti) { @@ -230,7 +234,6 @@ public void startEntity(final String name) { } final Value value = isArrayName(name) ? Value.newArray() : Value.newHash(); - addValue(name, value); entities.add(value); @@ -247,9 +250,7 @@ public void endEntity() { @Override public void literal(final String name, final String value) { LOG.debug("Putting '{}': '{}'", name, value); - addValue(name, new Value(value)); - // TODO: keep flattener as option? - // flattener.literal(name, value); + flattener.literal(name, value); } @Override diff --git a/metafix/src/main/java/org/metafacture/metafix/Value.java b/metafix/src/main/java/org/metafacture/metafix/Value.java index 7efa9030..502916e5 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Value.java +++ b/metafix/src/main/java/org/metafacture/metafix/Value.java @@ -49,12 +49,15 @@ public class Value { private final Type type; + private final String path; + public Value(final Array array) { type = array != null ? Type.Array : null; this.array = array; this.hash = null; this.string = null; + this.path = null; } public Value(final List array) { @@ -71,6 +74,7 @@ public Value(final Hash hash) { this.array = null; this.hash = hash; this.string = null; + this.path = null; } public Value(final Map hash) { @@ -87,12 +91,22 @@ public Value(final String string) { this.array = null; this.hash = null; this.string = string; + this.path = null; } public Value(final int integer) { this(String.valueOf(integer)); } + public Value(final String string, final String path) { + type = string != null ? Type.String : null; + + this.array = null; + this.hash = null; + this.string = string; + this.path = path; + } + public static Value newArray() { return newArray(null); } @@ -213,7 +227,8 @@ public final boolean equals(final Object object) { return Objects.equals(type, other.type) && Objects.equals(array, other.array) && Objects.equals(hash, other.hash) && - Objects.equals(string, other.string); + Objects.equals(string, other.string) && + Objects.equals(path, other.path); } @Override @@ -221,7 +236,8 @@ public final int hashCode() { return Objects.hashCode(type) + Objects.hashCode(array) + Objects.hashCode(hash) + - Objects.hashCode(string); + Objects.hashCode(string) + + Objects.hashCode(path); } @Override @@ -229,7 +245,7 @@ public String toString() { return isNull() ? null : extractType((m, c) -> m .ifArray(a -> c.accept(a.toString())) .ifHash(h -> c.accept(h.toString())) - .ifString(c) + .ifString(s -> c.accept(path == null ? s : String.format("<%s>@<%s>", s, path))) .orElseThrow() ); } @@ -238,6 +254,10 @@ public String toString() { return fieldPath.split(FIELD_PATH_SEPARATOR); } + public String getPath() { + return path; + } + enum Type { Array, Hash, @@ -538,7 +558,7 @@ public void addAll(final Hash hash) { */ public void add(final String field, final Value newValue) { final Value oldValue = new FixPath(field).findIn(this); - put(field, oldValue == null ? newValue : oldValue.asList(a1 -> newValue.asList(a2 -> a2.forEach(a1::add)))); + put(field, oldValue == null ? newValue : oldValue.asList(oldVals -> newValue.asList(newVals -> newVals.forEach(oldVals::add)))); } /** diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java index b67cb3ef..9316b5f4 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java @@ -748,18 +748,17 @@ public void shouldPerformComplexOperationWithPathWildcard() { public void shouldExecuteCustomJavaContext() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "do org.metafacture.metafix.util.TestContext(test, data: '42')", - " upcase(title)", + " add_field(title,'marc')", "end" ), i -> { i.startRecord("1"); - i.literal("title", "marc"); i.endRecord(); }, o -> { o.get().startRecord("1"); o.get().literal("BEFORE", "test"); - o.get().literal("title", "MARC"); + o.get().literal("title", "marc"); o.get().literal("AFTER", "42"); o.get().endRecord(); } diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java index c50c37f7..0581813a 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java @@ -240,20 +240,22 @@ public void shouldIgnoreOptionsOnLookupInSeparateExternalFileMap() { @Test public void shouldNotLookupInExternalFileMapWithWrongOptions() { - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - LOOKUP + " '" + CSV_MAP + "', sep_char: '\t')" - ), - i -> { - i.startRecord("1"); - i.literal("title", "Aloha"); - i.literal("title", "Moin"); - i.literal("title", "Hey"); - i.endRecord(); - }, - o -> { - o.get().startRecord("1"); - o.get().endRecord(); - } + MetafixTestHelpers.assertThrows(IllegalStateException.class, "Expected String, got null", () -> + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + LOOKUP + " '" + CSV_MAP + "', sep_char: '\t')" + ), + i -> { + i.startRecord("1"); + i.literal("title", "Aloha"); + i.literal("title", "Moin"); + i.literal("title", "Hey"); + i.endRecord(); + }, + (o, f) -> { + o.get().startRecord("1"); + o.get().endRecord(); + } + ) ); } @@ -280,20 +282,22 @@ public void shouldIgnoreOptionsOnSubsequentLookupInExternalFileMap() { @Test public void shouldNotLookupInUnknownInternalMap() { - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - LOOKUP + " 'testMap')" - ), - i -> { - i.startRecord("1"); - i.literal("title", "Aloha"); - i.literal("title", "Moin"); - i.literal("title", "Hey"); - i.endRecord(); - }, - o -> { - o.get().startRecord("1"); - o.get().endRecord(); - } + MetafixTestHelpers.assertThrows(IllegalStateException.class, "Expected String, got null", () -> + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + LOOKUP + " 'testMap')" + ), + i -> { + i.startRecord("1"); + i.literal("title", "Aloha"); + i.literal("title", "Moin"); + i.literal("title", "Hey"); + i.endRecord(); + }, + (o, f) -> { + o.get().startRecord("1"); + o.get().endRecord(); + } + ) ); } diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java index b7af1676..31270999 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java @@ -159,20 +159,23 @@ public void shouldCapitalizeStringsInArray() { } @Test - public void shouldNotCapitalizeArray() { - MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () -> - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "capitalize('title')" - ), - i -> { - i.startRecord("1"); - i.literal("title", "marc"); - i.literal("title", "json"); - i.endRecord(); - }, - o -> { - } - ) + @MetafixToDo("Same name, is replaced. Repeated fields to array?") + public void capitalizeRepeatedField() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "capitalize('title')" + ), + i -> { + i.startRecord("1"); + i.literal("title", "marc"); + i.literal("title", "json"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().literal("title", "Marc"); + o.get().literal("title", "Json"); + o.get().endRecord(); + } ); } @@ -268,7 +271,7 @@ public void shouldGetSubstringWithVar() { } @Test - public void shouldTrimString() { + public void shouldTrimStringSingle() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "trim('title')" ), @@ -286,7 +289,8 @@ public void shouldTrimString() { } @Test - public void shouldTrimStringInHash() { + @MetafixToDo("Same name, is replaced. Repeated fields to array?") + public void shouldTrimStringRepeated() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "trim('data.title')" ), @@ -294,6 +298,7 @@ public void shouldTrimStringInHash() { i.startRecord("1"); i.startEntity("data"); i.literal("title", " marc "); + i.literal("title", " json "); i.endEntity(); i.endRecord(); }, @@ -301,6 +306,7 @@ public void shouldTrimStringInHash() { o.get().startRecord("1"); o.get().startEntity("data"); o.get().literal("title", "marc"); + o.get().literal("title", "json"); o.get().endEntity(); o.get().endRecord(); } @@ -309,9 +315,40 @@ public void shouldTrimStringInHash() { @Test // See https://github.com/metafacture/metafacture-fix/pull/133 - public void shouldTrimStringInArrayOfHashes() { + public void dontTrimStringInImplicitArrayOfHashes() { + MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Non-index access to array at title", () -> + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "trim('data.title')" + ), + i -> { + i.startRecord("1"); + i.startEntity("data"); + i.literal("title", " marc "); + i.endEntity(); + i.startEntity("data"); + i.literal("title", " json "); + i.endEntity(); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("data"); + o.get().literal("title", " marc "); + o.get().endEntity(); + o.get().startEntity("data"); + o.get().literal("title", " json "); + o.get().endEntity(); + o.get().endRecord(); + } + ) + ); + } + + @Test + // See https://github.com/metafacture/metafacture-fix/pull/133 + public void shouldTrimStringInExplicitArrayOfHashes() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "trim('data.title')" + "trim('data.*.title')" ), i -> { i.startRecord("1"); @@ -589,7 +626,6 @@ public void shouldAppendValue() { } @Test - // TODO: Fix order (`animols.name` should stay before `animols.type`) public void shouldAppendValueInHash() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "append(animols.name, ' boss')" @@ -605,8 +641,8 @@ public void shouldAppendValueInHash() { o -> { o.get().startRecord("1"); o.get().startEntity("animols"); - o.get().literal("type", "TEST"); o.get().literal("name", "bird boss"); + o.get().literal("type", "TEST"); o.get().endEntity(); o.get().endRecord(); } @@ -667,24 +703,30 @@ public void shouldAppendValueInEntireArray() { } @Test - // See https://github.com/metafacture/metafacture-fix/issues/100 - public void shouldNotAppendValueToArray() { - MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () -> - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "append('animals[]', ' is cool')" - ), - i -> { - i.startRecord("1"); - i.startEntity("animals[]"); - i.literal("1", "dog"); - i.literal("2", "cat"); - i.literal("3", "zebra"); - i.endEntity(); - i.endRecord(); - }, - o -> { - } - ) + @MetafixToDo("Like this? See also https://github.com/metafacture/metafacture-fix/issues/100") + public void appendValueToArray() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "append('animals[]', 'another one')" + ), + i -> { + i.startRecord("1"); + i.startEntity("animals[]"); + i.literal("1", "dog"); + i.literal("2", "cat"); + i.literal("3", "zebra"); + i.endEntity(); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("animals[]"); + o.get().literal("1", "dog"); + o.get().literal("2", "cat"); + o.get().literal("3", "zebra"); + o.get().literal("4", "another one"); + o.get().endEntity(); + o.get().endRecord(); + } ); } @@ -951,7 +993,6 @@ public void shouldPrependValue() { } @Test - // TODO: Fix order (`animols.name` should stay before `animols.type`) public void shouldPrependValueInHash() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "prepend(animols.name, 'nice ')" @@ -967,8 +1008,8 @@ public void shouldPrependValueInHash() { o -> { o.get().startRecord("1"); o.get().startEntity("animols"); - o.get().literal("type", "TEST"); o.get().literal("name", "nice bird"); + o.get().literal("type", "TEST"); o.get().endEntity(); o.get().endRecord(); } @@ -1076,7 +1117,6 @@ public void shouldPrependValueInNestedArray() { @Test // See https://github.com/metafacture/metafacture-fix/issues/121 - // TODO: Fix order (`coll[].*.a` should stay before `coll[].*.b`) public void shouldPrependValueInArraySubField() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "prepend('coll[].*.a', 'HELLO ')" @@ -1105,12 +1145,12 @@ public void shouldPrependValueInArraySubField() { o.get().startRecord("1"); o.get().startEntity("coll[]"); o.get().startEntity("1"); - o.get().literal("b", "Dog"); o.get().literal("a", "HELLO Dog"); + o.get().literal("b", "Dog"); o.get().endEntity(); o.get().startEntity("2"); - o.get().literal("b", "Ape"); o.get().literal("a", "HELLO Ape"); + o.get().literal("b", "Ape"); o.get().endEntity(); o.get().startEntity("3"); o.get().literal("a", "HELLO Giraffe"); @@ -1124,24 +1164,30 @@ public void shouldPrependValueInArraySubField() { } @Test - // See https://github.com/metafacture/metafacture-fix/issues/100 - public void shouldNotPrependValueToArray() { - MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () -> - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "prepend('animals[]', 'cool ')" - ), - i -> { - i.startRecord("1"); - i.startEntity("animals[]"); - i.literal("1", "dog"); - i.literal("2", "cat"); - i.literal("3", "zebra"); - i.endEntity(); - i.endRecord(); - }, - o -> { - } - ) + @MetafixToDo("Like this? See also https://github.com/metafacture/metafacture-fix/issues/100") + public void shouldPrependValueToArray() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "prepend('animals[]', 'the first one')" + ), + i -> { + i.startRecord("1"); + i.startEntity("animals[]"); + i.literal("1", "dog"); + i.literal("2", "cat"); + i.literal("3", "zebra"); + i.endEntity(); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("animals[]"); + o.get().literal("1", "the first one"); + o.get().literal("2", "dog"); + o.get().literal("3", "cat"); + o.get().literal("4", "zebra"); + o.get().endEntity(); + o.get().endRecord(); + } ); } @@ -1246,7 +1292,6 @@ public void shouldReplaceAllRegexesInArray() { @Test // See https://github.com/metafacture/metafacture-fix/issues/121 - // TODO: Fix order (`coll[].*.a` should stay before `coll[].*.b`) public void shouldReplaceAllRegexesInArraySubField() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "replace_all('coll[].*.a', 'o', '__')" @@ -1275,12 +1320,12 @@ public void shouldReplaceAllRegexesInArraySubField() { o.get().startRecord("1"); o.get().startEntity("coll[]"); o.get().startEntity("1"); - o.get().literal("b", "Dog"); o.get().literal("a", "D__g"); + o.get().literal("b", "Dog"); o.get().endEntity(); o.get().startEntity("2"); - o.get().literal("b", "Ape"); o.get().literal("a", "Ape"); + o.get().literal("b", "Ape"); o.get().endEntity(); o.get().startEntity("3"); o.get().literal("a", "Giraffe"); @@ -1985,7 +2030,7 @@ public void shouldRemoveDuplicateStrings() { } @Test - public void shouldRemoveDuplicateArrays() { + public void shouldNotRemoveDuplicateArraysAtDifferentPath() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "uniq('arrays[]')" ), @@ -2025,6 +2070,10 @@ public void shouldRemoveDuplicateArrays() { o.get().startEntity("3"); o.get().literal("number", "6"); o.get().literal("number", "23"); + o.get().endEntity(); + o.get().startEntity("4"); + o.get().literal("number", "6"); + o.get().literal("number", "23"); f.apply(2).endEntity(); o.get().endRecord(); } @@ -2032,7 +2081,54 @@ public void shouldRemoveDuplicateArrays() { } @Test - public void shouldRemoveDuplicateHashes() { + public void shouldRemoveDuplicateArrays() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "uniq('arrays[]')" + ), + i -> { + i.startRecord("1"); + i.startEntity("arrays[]"); + i.startEntity("1"); + i.literal("number", "41"); + i.literal("number", "23"); + i.endEntity(); + i.startEntity("2"); + i.literal("number", "42"); + i.literal("number", "23"); + i.endEntity(); + i.startEntity("3"); + i.literal("number", "6"); + i.literal("number", "23"); + i.endEntity(); + i.startEntity("3"); + i.literal("number", "6"); + i.literal("number", "23"); + i.endEntity(); + i.endEntity(); + i.endRecord(); + }, + (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("arrays[]"); + o.get().startEntity("1"); + o.get().literal("number", "41"); + o.get().literal("number", "23"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("number", "42"); + o.get().literal("number", "23"); + o.get().endEntity(); + o.get().startEntity("3"); + o.get().literal("number", "6"); + o.get().literal("number", "23"); + f.apply(2).endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldNotRemoveDuplicateHashesAtDifferentPath() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "uniq('hashes[]')" ), @@ -2054,6 +2150,49 @@ public void shouldRemoveDuplicateHashes() { i.endEntity(); i.endRecord(); }, + (o, f) -> { + o.get().startRecord("1"); + o.get().startEntity("hashes[]"); + o.get().startEntity("1"); + o.get().literal("number", "41"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("number", "42"); + o.get().endEntity(); + o.get().startEntity("3"); + o.get().literal("number", "6"); + o.get().endEntity(); + o.get().startEntity("4"); + o.get().literal("number", "6"); + f.apply(2).endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldRemoveDuplicateHashes() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "uniq('hashes[]')" + ), + i -> { + i.startRecord("1"); + i.startEntity("hashes[]"); + i.startEntity("1"); + i.literal("number", "41"); + i.endEntity(); + i.startEntity("2"); + i.literal("number", "42"); + i.endEntity(); + i.startEntity("3"); + i.literal("number", "6"); + i.endEntity(); + i.startEntity("3"); + i.literal("number", "6"); + i.endEntity(); + i.endEntity(); + i.endRecord(); + }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("hashes[]"); diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java index af922f3c..f34e7d8c 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java @@ -742,7 +742,7 @@ public void shouldCopyArrayFieldWithAsterisk() { } @Test - @MetafixToDo("See https://github.com/metafacture/metafacture-fix/issues/121") + // See https://github.com/metafacture/metafacture-fix/issues/121 public void shouldCopyNestedArrayFieldWithAsterisk() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "set_array('TEST_4[]')", @@ -2198,20 +2198,41 @@ public void accessArrayByIndex() { } @Test - public void shouldNotAccessArrayImplicitly() { - MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () -> - MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "upcase('name')" - ), - i -> { - i.startRecord("1"); - i.literal("name", "max"); - i.literal("name", "mo"); - i.endRecord(); - }, - o -> { - } - ) + public void transformSingleField() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('name')" + ), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().literal("name", "MAX"); + o.get().endRecord(); + } + ); + } + + @Test + @MetafixToDo("Same name, is replaced. Repeated fields to array?") + public void transformRepeatedField() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('name')" + ), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().literal("name", "MAX"); + o.get().literal("name", "MO"); + o.get().endRecord(); + } ); } diff --git a/metafix/src/test/resources/org/metafacture/metafix/integration/method/fromJson/toJson/appendSimple/expected.json b/metafix/src/test/resources/org/metafacture/metafix/integration/method/fromJson/toJson/appendSimple/expected.json index 0474554e..3e7fb2e8 100644 --- a/metafix/src/test/resources/org/metafacture/metafix/integration/method/fromJson/toJson/appendSimple/expected.json +++ b/metafix/src/test/resources/org/metafacture/metafix/integration/method/fromJson/toJson/appendSimple/expected.json @@ -1,7 +1,7 @@ { + "animal" : "bunny is cool", "animal_2" : { - "type" : "TEST", - "name" : "bird boss" - }, - "animal" : "bunny is cool" + "name" : "bird boss", + "type" : "TEST" + } }