Skip to content

Commit e376dde

Browse files
committed
Add lookup() option to print unknown values. (#212)
1 parent 99976e7 commit e376dde

File tree

4 files changed

+166
-26
lines changed

4 files changed

+166
-26
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,17 @@ Options:
562562

563563
- `__default`: Default value to use for unknown values. (Default: Old value)
564564
- `delete`: Whether to delete unknown values. (Default: `false`)
565+
- `print_unknown`: Whether to print unknown values. (Default: `false`)
566+
567+
Additional options when printing unknown values:
568+
569+
- `compression` (file output only): Compression mode. (Default: `auto`)
570+
- `destination`: Destination to write unknown values to; may include [format directives](https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax) for counter and record ID (in that order). (Default: `stdout`)
571+
- `encoding` (file output only): Encoding used by the underlying writer. (Default: `UTF-8`)
572+
- `footer`: Footer which is written at the end of the output. (Default: `\n`)
573+
- `header`: Header which is written at the beginning of the output. (Default: Empty string)
574+
- `id`: Field name which contains the record ID; if found, will be available for inclusion in `destination`. (Default: `_id`)
575+
- `separator`: Separator which is written after the unknown value. (Default: `\n`)
565576

566577
```perl
567578
lookup("<sourceField>"[, <mapName>][, <options>...])
@@ -586,6 +597,9 @@ lookup("path.to.field", "file-map")
586597

587598
# with default value
588599
lookup("path.to.field", "map-name", __default: "NA")
600+
601+
# with printing unknown values to a file
602+
lookup("path.to.field", "map-name", print_unknown: "true", destination: "unknown.txt")
589603
```
590604

591605
##### `prepend`

metafix/src/main/java/org/metafacture/metafix/FixMethod.java

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Map;
3535
import java.util.Random;
3636
import java.util.concurrent.atomic.LongAdder;
37+
import java.util.function.Consumer;
3738
import java.util.function.Function;
3839
import java.util.function.Predicate;
3940
import java.util.function.UnaryOperator;
@@ -241,7 +242,6 @@ private boolean literalString(final String s) {
241242

242243
@Override
243244
public void apply(final Metafix metafix, final Record record, final List<String> params, final Map<String, String> options) {
244-
final String destination = options.getOrDefault("destination", ObjectWriter.STDOUT);
245245
final Value idValue = record.get(options.getOrDefault("id", StandardEventNames.ID));
246246

247247
final boolean internal = getBoolean(options, "internal");
@@ -250,33 +250,29 @@ public void apply(final Metafix metafix, final Record record, final List<String>
250250
final LongAdder counter = scopedCounter.computeIfAbsent(metafix, k -> new LongAdder());
251251
counter.increment();
252252

253-
final String id = Value.isNull(idValue) ? "" : idValue.toString();
254-
final String prefix = params.isEmpty() ? "" : String.format(params.get(0), counter.sum(), id);
255-
final ObjectWriter<String> writer = new ObjectWriter<>(String.format(destination, counter.sum(), id));
253+
final UnaryOperator<String> formatter = s -> String.format(s,
254+
counter.sum(), Value.isNull(idValue) ? "" : idValue.toString());
256255

257-
withOption(options, "compression", writer::setCompression);
258-
withOption(options, "encoding", writer::setEncoding);
259-
withOption(options, "footer", writer::setFooter);
260-
withOption(options, "header", writer::setHeader);
256+
final String prefix = params.isEmpty() ? "" : formatter.apply(params.get(0));
261257

262-
if (internal) {
263-
if (pretty) {
264-
record.forEach((f, v) -> writer.process(prefix + f + "=" + v));
258+
withWriter(options, formatter, w -> {
259+
if (internal) {
260+
if (pretty) {
261+
record.forEach((f, v) -> w.process(prefix + f + "=" + v));
262+
}
263+
else {
264+
w.process(prefix + record);
265+
}
265266
}
266267
else {
267-
writer.process(prefix + record);
268-
}
269-
}
270-
else {
271-
try {
272-
writer.process(prefix + record.toJson(pretty));
273-
}
274-
catch (final IOException e) {
275-
// Log a warning? Print string representation instead?
268+
try {
269+
w.process(prefix + record.toJson(pretty));
270+
}
271+
catch (final IOException e) {
272+
// Log a warning? Print string representation instead?
273+
}
276274
}
277-
}
278-
279-
writer.closeStream();
275+
});
280276
}
281277
},
282278
random {
@@ -501,10 +497,29 @@ public void apply(final Metafix metafix, final Record record, final List<String>
501497
}
502498

503499
final String defaultValue = map.get(Maps.DEFAULT_MAP_KEY); // TODO: Catmandu uses 'default'
504-
record.transform(params.get(0), oldValue -> {
505-
final String newValue = map.getOrDefault(oldValue, defaultValue);
506-
return newValue != null ? newValue : getBoolean(options, "delete") ? null : oldValue;
500+
final boolean delete = getBoolean(options, "delete");
501+
final boolean printUnknown = getBoolean(options, "print_unknown");
502+
503+
final Consumer<ObjectWriter<String>> consumer = w -> record.transform(params.get(0), oldValue -> {
504+
final String newValue = map.get(oldValue);
505+
if (newValue != null) {
506+
return newValue;
507+
}
508+
else {
509+
if (w != null) {
510+
w.process(oldValue);
511+
}
512+
513+
return defaultValue != null ? defaultValue : delete ? null : oldValue;
514+
}
507515
});
516+
517+
if (printUnknown) {
518+
withWriter(options, null, consumer);
519+
}
520+
else {
521+
consumer.accept(null);
522+
}
508523
}
509524
},
510525
prepend {

metafix/src/main/java/org/metafacture/metafix/api/FixFunction.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.metafacture.metafix.api;
1818

19+
import org.metafacture.io.ObjectWriter;
1920
import org.metafacture.metafix.Metafix;
2021
import org.metafacture.metafix.Record;
2122
import org.metafacture.metafix.Value;
@@ -26,6 +27,7 @@
2627
import java.util.Set;
2728
import java.util.function.BiFunction;
2829
import java.util.function.Consumer;
30+
import java.util.function.UnaryOperator;
2931
import java.util.stream.Stream;
3032

3133
@FunctionalInterface
@@ -43,6 +45,24 @@ default <T> void withOption(final Map<String, String> options, final String key,
4345
}
4446
}
4547

48+
default void withWriter(final Map<String, String> options, final UnaryOperator<String> operator, final Consumer<ObjectWriter<String>> consumer) {
49+
final String destination = options.getOrDefault("destination", ObjectWriter.STDOUT);
50+
final ObjectWriter<String> writer = new ObjectWriter<>(operator != null ? operator.apply(destination) : destination);
51+
52+
withOption(options, "compression", writer::setCompression);
53+
withOption(options, "encoding", writer::setEncoding);
54+
withOption(options, "footer", writer::setFooter);
55+
withOption(options, "header", writer::setHeader);
56+
withOption(options, "separator", writer::setSeparator);
57+
58+
try {
59+
consumer.accept(writer);
60+
}
61+
finally {
62+
writer.closeStream();
63+
}
64+
}
65+
4666
default boolean getBoolean(final Map<String, String> options, final String key) {
4767
return Boolean.parseBoolean(options.get(key));
4868
}

metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.mockito.Mock;
2525
import org.mockito.junit.jupiter.MockitoExtension;
2626

27+
import java.io.IOException;
2728
import java.util.Arrays;
2829

2930
/**
@@ -846,6 +847,96 @@ public void shouldFailLookupInUnknownExternalMap() {
846847
);
847848
}
848849

850+
private void shouldPrintUnknown(final String args, final String defaultValue, final String expected) {
851+
MetafixTestHelpers.assertStdout(expected, () ->
852+
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
853+
LOOKUP + " Aloha: Alohaeha, 'Moin': 'Moin zäme'" + args + ", print_unknown: 'true')"
854+
),
855+
i -> {
856+
i.startRecord("rec1");
857+
i.literal("title", "Aloha");
858+
i.literal("title", "Moin");
859+
i.literal("title", "Hey");
860+
i.literal("title", "you");
861+
i.literal("title", "there");
862+
i.endRecord();
863+
864+
i.startRecord("rec2");
865+
i.literal("title", "Aloha");
866+
i.literal("title", "you");
867+
i.literal("title", "too");
868+
i.endRecord();
869+
},
870+
(o, f) -> {
871+
final boolean delete = "__delete".equals(defaultValue);
872+
873+
o.get().startRecord("rec1");
874+
o.get().literal("title", "Alohaeha");
875+
o.get().literal("title", "Moin zäme");
876+
877+
if (defaultValue == null) {
878+
o.get().literal("title", "Hey");
879+
o.get().literal("title", "you");
880+
o.get().literal("title", "there");
881+
}
882+
else if (!delete) {
883+
f.apply(3).literal("title", defaultValue);
884+
}
885+
886+
o.get().endRecord();
887+
888+
o.get().startRecord("rec2");
889+
o.get().literal("title", "Alohaeha");
890+
891+
if (defaultValue == null) {
892+
o.get().literal("title", "you");
893+
o.get().literal("title", "too");
894+
}
895+
else if (!delete) {
896+
f.apply(2).literal("title", defaultValue);
897+
}
898+
899+
o.get().endRecord();
900+
}
901+
)
902+
);
903+
}
904+
905+
@Test
906+
public void shouldPrintUnknown() {
907+
shouldPrintUnknown("", null, "Hey\nyou\nthere\nyou\ntoo\n");
908+
}
909+
910+
@Test
911+
public void shouldPrintUnknownWithDefault() {
912+
shouldPrintUnknown(", __default: Tach", "Tach", "Hey\nyou\nthere\nyou\ntoo\n");
913+
}
914+
915+
@Test
916+
public void shouldPrintUnknownWithDelete() {
917+
shouldPrintUnknown(", delete: 'true'", "__delete", "Hey\nyou\nthere\nyou\ntoo\n");
918+
}
919+
920+
@Test
921+
public void shouldPrintUnknownWithHeader() {
922+
shouldPrintUnknown(", header: '<%d:%s>'", null, "<%d:%s>Hey\nyou\nthere\n<%d:%s>you\ntoo\n");
923+
}
924+
925+
@Test
926+
public void shouldPrintUnknownWithFooter() {
927+
shouldPrintUnknown(", footer: '<%d:%s>'", null, "Hey\nyou\nthere<%d:%s>you\ntoo<%d:%s>");
928+
}
929+
930+
@Test
931+
public void shouldPrintUnknownWithSeparator() {
932+
shouldPrintUnknown(", separator: '<%d:%s>'", null, "Hey<%d:%s>you<%d:%s>there\nyou<%d:%s>too\n");
933+
}
934+
935+
@Test
936+
public void shouldPrintUnknownToFile() throws IOException {
937+
MetafixTestHelpers.assertTempFile("you\ntoo\n", p -> shouldPrintUnknown(", destination: '" + p + "'", null, ""));
938+
}
939+
849940
private void assertMap(final String... fixDef) {
850941
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(fixDef),
851942
i -> {

0 commit comments

Comments
 (0)