diff --git a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java index 96c90847..bfc56f4f 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java @@ -21,9 +21,13 @@ import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -344,6 +348,23 @@ else if (v.isArray()) { }); } }, + sort_field { + public void apply(final Metafix metafix, final Record record, final List params, final Map options) { + record.transformField(params.get(0), v -> { + final boolean numeric = getBoolean(options, "numeric"); + final boolean reverse = getBoolean(options, "reverse"); + final boolean uniq = getBoolean(options, "uniq"); + + final Stream stream = v.asArray().stream(); + final Function function = Value::asString; + final Comparator comparator = numeric ? + Comparator.comparing(function.andThen(Integer::parseInt)) : Comparator.comparing(function); + + return v.isArray() ? new Value((uniq ? unique(stream) : stream) + .sorted(reverse ? comparator.reversed() : comparator).collect(Collectors.toList())) : null; + }); + } + }, substring { public void apply(final Metafix metafix, final Record record, final List params, final Map options) { record.transformFields(params, s -> s.substring(getInteger(params, 1), getInteger(params, 2) - 1)); @@ -382,6 +403,11 @@ private static Value newArray(final Stream stream) { return Value.newArray(a -> stream.forEach(a::add)); } + private static Stream unique(final Stream stream) { + final Set set = new HashSet<>(); + return stream.filter(set::add); + } + abstract void apply(Metafix metafix, Record record, List params, Map options); } diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java index b36fff69..954e9965 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java @@ -701,6 +701,155 @@ public void shouldReverseArray() { ); } + @Test + public void shouldSortField() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers)" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "41"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("numbers"); + o.get().literal("1", "41"); + o.get().literal("2", "42"); + o.get().literal("3", "6"); + o.get().literal("4", "6"); + o.get().endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldSortFieldNumerically() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers, numeric: 'true')" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "41"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("numbers"); + o.get().literal("1", "6"); + o.get().literal("2", "6"); + o.get().literal("3", "41"); + o.get().literal("4", "42"); + o.get().endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldFailToSortNumericallyWithInvalidNumber() { + assertThrows(NumberFormatException.class, "For input string: \"x\"", () -> + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers, numeric: 'true')" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "x"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + } + ) + ); + } + + @Test + public void shouldSortFieldInReverse() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers, reverse: 'true')" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "41"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("numbers"); + o.get().literal("1", "6"); + o.get().literal("2", "6"); + o.get().literal("3", "42"); + o.get().literal("4", "41"); + o.get().endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldSortFieldNumericallyInReverse() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers, numeric: 'true', reverse: 'true')" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "41"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("numbers"); + o.get().literal("1", "42"); + o.get().literal("2", "41"); + o.get().literal("3", "6"); + o.get().literal("4", "6"); + o.get().endEntity(); + o.get().endRecord(); + } + ); + } + + @Test + public void shouldSortFieldAndRemoveDuplicates() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "sort_field(numbers, uniq: 'true')" + ), + i -> { + i.startRecord("1"); + i.literal("numbers", "6"); + i.literal("numbers", "42"); + i.literal("numbers", "41"); + i.literal("numbers", "6"); + i.endRecord(); + }, + o -> { + o.get().startRecord("1"); + o.get().startEntity("numbers"); + o.get().literal("1", "41"); + o.get().literal("2", "42"); + o.get().literal("3", "6"); + o.get().endEntity(); + o.get().endRecord(); + } + ); + } + private void assertThrows(final Class expectedClass, final String expectedMessage, final Executable executable) { final Throwable exception = Assertions.assertThrows(MetafactureException.class, executable).getCause(); Assertions.assertSame(expectedClass, exception.getClass());