From 5cbe20a9dd27a5470545f53f098c62710a12ce09 Mon Sep 17 00:00:00 2001 From: Fabian Steeg Date: Tue, 1 Feb 2022 17:38:07 +0100 Subject: [PATCH] Extract find* and transform* methods from Value.Hash (#102) --- .../java/org/metafacture/metafix/FixBind.java | 3 +- .../org/metafacture/metafix/FixMethod.java | 42 +++--- .../java/org/metafacture/metafix/Path.java | 77 ++++++++++- .../java/org/metafacture/metafix/Value.java | 124 +++--------------- .../metafacture/metafix/api/FixPredicate.java | 3 +- 5 files changed, 119 insertions(+), 130 deletions(-) diff --git a/metafix/src/main/java/org/metafacture/metafix/FixBind.java b/metafix/src/main/java/org/metafacture/metafix/FixBind.java index 9324c80d..39439df0 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixBind.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixBind.java @@ -29,8 +29,7 @@ public enum FixBind implements FixContext { public void execute(final Metafix metafix, final Record record, final List params, final Map options, final List expressions) { final RecordTransformer recordTransformer = metafix.getRecordTransformer(); final String scopeVariable = options.get("var"); - - record.findList(options.get("path"), a -> { + Value.asList(new Path(options.get("path")).findInHash(record), a -> { for (int i = 0; i < a.size(); ++i) { final Value value = a.get(i); diff --git a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java index bda2312e..3323e438 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java @@ -217,8 +217,8 @@ public void apply(final Metafix metafix, final Record record, final List public void apply(final Metafix metafix, final Record record, final List params, final Map options) { final String joinChar = options.get("join_char"); record.replace(params.get(0), params.subList(1, params.size()).stream() - .filter(f -> literalString(f) || record.find(f) != null) - .map(f -> literalString(f) ? new Value(f.substring(1)) : record.findList(f, null).asArray().get(0)) + .filter(f -> literalString(f) || new Path(f).findInHash(record) != null) + .map(f -> literalString(f) ? new Value(f.substring(1)) : Value.asList(new Path(f).findInHash(record), null).asArray().get(0)) .map(Value::asString).collect(Collectors.joining(joinChar != null ? joinChar : " "))); } @@ -255,7 +255,7 @@ public void apply(final Metafix metafix, final Record record, final List final UnaryOperator operator = s -> s.replaceAll(search, replace); - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(renameArray(a, operator))) .ifHash(h -> c.accept(renameHash(h, operator))) .orElseThrow() @@ -334,19 +334,19 @@ public void apply(final Metafix metafix, final Record record, final List @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { final String value = params.get(1); - record.transformField(params.get(0), s -> s + value); + new Path(params.get(0)).transformInHash(record, s -> s + value); } }, capitalize { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), s -> s.substring(0, 1).toUpperCase() + s.substring(1)); + new Path(params.get(0)).transformInHash(record, s -> s.substring(0, 1).toUpperCase() + s.substring(1)); } }, count { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(new Value(a.size()))) .ifHash(h -> c.accept(new Value(h.size()))) ); @@ -355,7 +355,7 @@ public void apply(final Metafix metafix, final Record record, final List downcase { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), s -> s.toLowerCase()); + new Path(params.get(0)).transformInHash(record, s -> s.toLowerCase()); } }, filter { @@ -366,7 +366,7 @@ public void apply(final Metafix metafix, final Record record, final List final Predicate predicate = s -> search.matcher(s.asString()).find(); - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(newArray(a.stream().filter(invert ? predicate.negate() : predicate)))) ); } @@ -375,14 +375,14 @@ public void apply(final Metafix metafix, final Record record, final List @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { final String search = params.get(1); - record.transformField(params.get(0), s -> String.valueOf(s.indexOf(search))); // TODO: multiple + new Path(params.get(0)).transformInHash(record, s -> String.valueOf(s.indexOf(search))); // TODO: multiple } }, join_field { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { final String joinChar = params.size() > 1 ? params.get(1) : ""; - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(new Value(a.stream().map(Value::asString).collect(Collectors.joining(joinChar))))) ); } @@ -406,14 +406,14 @@ public void apply(final Metafix metafix, final Record record, final List } final String defaultValue = map.get(Maps.DEFAULT_MAP_KEY); // TODO: Catmandu uses 'default' - record.transformField(params.get(0), k -> map.getOrDefault(k, defaultValue)); + new Path(params.get(0)).transformInHash(record, k -> map.getOrDefault(k, defaultValue)); } }, prepend { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { final String value = params.get(1); - record.transformField(params.get(0), s -> value + s); + new Path(params.get(0)).transformInHash(record, s -> value + s); } }, replace_all { @@ -422,13 +422,13 @@ public void apply(final Metafix metafix, final Record record, final List final String search = params.get(1); final String replace = params.get(2); - record.transformField(params.get(0), s -> s.replaceAll(search, replace)); + new Path(params.get(0)).transformInHash(record, s -> s.replaceAll(search, replace)); } }, reverse { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> { final List list = a.stream().collect(Collectors.toList()); Collections.reverse(list); @@ -449,7 +449,7 @@ public void apply(final Metafix metafix, final Record record, final List final Comparator comparator = numeric ? Comparator.comparing(function.andThen(Integer::parseInt)) : Comparator.comparing(function); - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(new Value((uniq ? unique(a.stream()) : a.stream()) .sorted(reverse ? comparator.reversed() : comparator).collect(Collectors.toList())))) ); @@ -464,7 +464,7 @@ public void apply(final Metafix metafix, final Record record, final List final Function splitFunction = s -> newArray(Arrays.stream(splitPattern.split(s)).map(Value::new)); - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(newArray(a.stream().map(Value::asString).map(splitFunction)))) .ifHash(h -> c.accept(Value.newHash(n -> h.forEach((f, w) -> n.put(f, splitFunction.apply(w.asString())))))) .ifString(s -> c.accept(splitFunction.apply(s))) @@ -474,13 +474,13 @@ public void apply(final Metafix metafix, final Record record, final List substring { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), s -> s.substring(getInteger(params, 1), getInteger(params, 2) - 1)); + new Path(params.get(0)).transformInHash(record, s -> s.substring(getInteger(params, 1), getInteger(params, 2) - 1)); } }, sum { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(new Value(a.stream().map(Value::asString).mapToInt(Integer::parseInt).sum()))) ); } @@ -488,13 +488,13 @@ public void apply(final Metafix metafix, final Record record, final List trim { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), String::trim); + new Path(params.get(0)).transformInHash(record, String::trim); } }, uniq { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), (m, c) -> m + new Path(params.get(0)).transformInHash(record, (m, c) -> m .ifArray(a -> c.accept(newArray(unique(a.stream())))) ); } @@ -502,7 +502,7 @@ public void apply(final Metafix metafix, final Record record, final List upcase { @Override public void apply(final Metafix metafix, final Record record, final List params, final Map options) { - record.transformField(params.get(0), s -> s.toUpperCase()); + new Path(params.get(0)).transformInHash(record, s -> s.toUpperCase()); } }; diff --git a/metafix/src/main/java/org/metafacture/metafix/Path.java b/metafix/src/main/java/org/metafacture/metafix/Path.java index 2fbe7574..c729fb5b 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Path.java +++ b/metafix/src/main/java/org/metafacture/metafix/Path.java @@ -16,9 +16,14 @@ package org.metafacture.metafix; +import org.metafacture.metafix.Value.AbstractValueType.InsertMode; import org.metafacture.metafix.Value.Array; +import org.metafacture.metafix.Value.Hash; +import org.metafacture.metafix.Value.TypeMatcher; import java.util.Arrays; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.UnaryOperator; /** @@ -35,6 +40,10 @@ public Path(final String[] path) { this.path = path; } + public Path(final String path) { + this(Value.split(path)); + } + public Value findInArray(final Value.Array array) { final Value result; if (path.length > 0) { @@ -66,7 +75,7 @@ private Value findInValue(final Value value, final String[] p) { // TODO: move impl into enum elements, here call only value.find return value == null ? null : value.extractType((m, c) -> m .ifArray(a -> c.accept(new Path(p).findInArray(a))) - .ifHash(h -> c.accept(h.find(p))) + .ifHash(h -> c.accept(new Path(p).findInHash(h))) .orElse(c) ); } @@ -107,8 +116,72 @@ private void transformValueAt(final Array array, final int index, final String[] .ifString(s -> array.set(index, operator != null ? new Value(operator.apply(s)) : null)) .orElse(v -> new Value.TypeMatcher(v) .ifArray(a -> new Path(p).transformInArray(a, operator)) - .ifHash(h -> h.transformPath(p, operator)) + .ifHash(h -> new Path(p).transformInHash(h, operator)) .orElseThrow()); } } + + public Value findInHash(final Hash hash) { + final String field = path[0]; + if (field.equals(ASTERISK)) { + // TODO: search in all elements of value.asHash()? + return new Path(tail(path)).findInHash(hash); + } + return path.length == 1 || !hash.containsField(field) ? hash.get(field) : + findNested(hash, field, tail(path)); + } + + private Value findNested(final Hash hash, final String field, final String[] remainingFields) { + final Value value = hash.get(field); + return value == null ? null : value.extractType((m, c) -> m + .ifArray(a -> c.accept(new Path(remainingFields).findInArray(a))) + .ifHash(h -> c.accept(new Path(remainingFields).findInHash(h))) + .orElseThrow() + ); + } + + public void transformInHash(final Hash hash, final BiConsumer> consumer) { + final Value oldValue = findInHash(hash); + + if (oldValue != null) { + final Value newValue = oldValue.extractType(consumer); + + if (newValue != null) { + hash.insert(InsertMode.REPLACE, path, newValue); + } + } + } + + public void transformInHash(final Hash hash, final UnaryOperator operator) { + final String currentSegment = path[0]; + final String[] remainingPath = tail(path); + + if (currentSegment.equals(ASTERISK)) { + // TODO: search in all elements of value.asHash()? + new Path(remainingPath).transformInHash(hash, operator); + return; + } + + hash.modifyFields(currentSegment, f -> { + final Value value = hash.getMap().get(f); + + if (value != null) { + if (remainingPath.length == 0) { + hash.getMap().remove(f); + + if (operator != null) { + value.matchType() + .ifString(s -> hash.append(f, operator.apply(s))) + .orElseThrow(); + } + } + else { + new TypeMatcher(value) + .ifArray(a -> new Path(remainingPath).transformInArray(a, operator)) + .ifHash(h -> new Path(remainingPath).transformInHash(h, operator)) + .orElseThrow(); + } + } + }); + } } diff --git a/metafix/src/main/java/org/metafacture/metafix/Value.java b/metafix/src/main/java/org/metafacture/metafix/Value.java index ff79df46..ab2fc3c5 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Value.java +++ b/metafix/src/main/java/org/metafacture/metafix/Value.java @@ -34,7 +34,6 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -61,6 +60,8 @@ static ReservedField fromString(final String string) { private static final String ASTERISK = "*"; + private static final String FIELD_PATH_SEPARATOR = "\\."; + private final Array array; private final Hash hash; private final String string; @@ -256,6 +257,10 @@ private static String[] tail(final String[] fields) { return Arrays.copyOfRange(fields, 1, fields.length); } + static String[] split(final String fieldPath) { + return fieldPath.split(FIELD_PATH_SEPARATOR); + } + enum Type { Array, Hash, @@ -311,7 +316,7 @@ private TypeMatcher match(final Type type, final Consumer consumer, final } - private abstract static class AbstractValueType { + public abstract static class AbstractValueType { protected static final Predicate REMOVE_EMPTY_VALUES = v -> v.extractType((m, c) -> m .ifArray(a -> { @@ -520,9 +525,8 @@ else if (fields.length > 1) { */ public static class Hash extends AbstractValueType { - private static final String FIELD_PATH_SEPARATOR = "\\."; - private final Map map = new LinkedHashMap<>(); + private final SimpleRegexTrie trie = new SimpleRegexTrie<>(); /** @@ -531,6 +535,10 @@ public static class Hash extends AbstractValueType { protected Hash() { } + public Map getMap() { + return map; + } + /** * Checks whether this hash contains the metadata field. * @@ -552,7 +560,7 @@ public boolean containsPath(final String fieldPath) { final Value value; try { - value = find(path); + value = new Path(path).findInHash(this); } catch (final MetafactureException e) { if (e.getCause() instanceof IllegalStateException) { @@ -631,49 +639,14 @@ public Value append(final String fieldPath, final String newValue) { */ public Value get(final String field) { // TODO: special treatment (only) for exact matches? - final List list = findFields(field).map(map::get).collect(Collectors.toList()); + final List list = matchFields(field, Stream::filter).map(map::get).collect(Collectors.toList()); return list.isEmpty() ? null : list.size() == 1 ? list.get(0) : new Value(list); } - // TODO path - public Value find(final String fieldPath) { - return find(split(fieldPath)); - } - - // TODO path - Value find(final String[] fields) { - final String field = fields[0]; - if (field.equals(ASTERISK)) { - // TODO: search in all elements of value.asHash()? - return find(tail(fields)); - } - return fields.length == 1 || !containsField(field) ? get(field) : - findNested(field, tail(fields)); - } - - // TODO path - private Value findNested(final String field, final String[] remainingFields) { - final Value value = get(field); - return value == null ? null : value.extractType((m, c) -> m - .ifArray(a -> c.accept(new Path(remainingFields).findInArray(a))) - .ifHash(h -> c.accept(h.find(remainingFields))) - .orElseThrow() - ); - } - - // TODO path - public Value findList(final String fieldPath, final Consumer consumer) { - return asList(find(fieldPath), consumer); - } - public Value getList(final String field, final Consumer consumer) { return asList(get(field), consumer); } - private String[] split(final String fieldPath) { - return fieldPath.split(FIELD_PATH_SEPARATOR); - } - public void addAll(final String field, final List values) { values.forEach(value -> add(field, new Value(value))); } @@ -698,7 +671,7 @@ public Value insert(final InsertMode mode, final String fieldPath, final String return insert(mode, split(fieldPath), new Value(newValue)); } - private Value insert(final InsertMode mode, final String[] fields, final Value newValue) { + Value insert(final InsertMode mode, final String[] fields, final Value newValue) { final String field = fields[0]; if (fields.length == 1) { if (field.equals(ASTERISK)) { @@ -796,7 +769,7 @@ else if (containsField(field)) { public void copy(final List params) { final String oldName = params.get(0); final String newName = params.get(1); - findList(oldName, a -> a.forEach(v -> appendValue(split(newName), v))); + asList(new Path(oldName).findInHash(this), a -> a.forEach(v -> appendValue(split(newName), v))); } private void appendValue(final String[] newName, final Value v) { @@ -814,7 +787,7 @@ private void appendValue(final String[] newName, final Value v) { add(newName[0], v); } else { - appendValue(newName, v.asHash().find(tail(newName))); + appendValue(newName, new Path(tail(newName)).findInHash(v.asHash())); } break; default: @@ -823,65 +796,13 @@ private void appendValue(final String[] newName, final Value v) { } } - // TODO path - public void transformField(final String path, final BiConsumer> consumer) { - final Value oldValue = find(path); - - if (oldValue != null) { - final Value newValue = oldValue.extractType(consumer); - - if (newValue != null) { - insert(InsertMode.REPLACE, split(path), newValue); - } - } - } - - // TODO path - public void transformField(final String path, final UnaryOperator operator) { - transformPath(split(path), operator); - } - - // TODO path - void transformPath(final String[] path, final UnaryOperator operator) { - final String currentSegment = path[0]; - final String[] remainingPath = tail(path); - - if (currentSegment.equals(ASTERISK)) { - // TODO: search in all elements of value.asHash()? - transformPath(remainingPath, operator); - return; - } - - modifyFields(currentSegment, f -> { - final Value value = map.get(f); - - if (value != null) { - if (remainingPath.length == 0) { - map.remove(f); - - if (operator != null) { - value.matchType() - .ifString(s -> append(f, operator.apply(s))) - .orElseThrow(); - } - } - else { - new TypeMatcher(value) - .ifArray(a -> new Path(remainingPath).transformInArray(a, operator)) - .ifHash(h -> h.transformPath(remainingPath, operator)) - .orElseThrow(); - } - } - }); - } - /** * Retains only the given field/value pairs in this hash. * * @param fields the field names */ public void retainFields(final Collection fields) { - map.keySet().retainAll(fields.stream().flatMap(this::findFields).collect(Collectors.toSet())); + map.keySet().retainAll(fields.stream().flatMap(f -> matchFields(f, Stream::filter)).collect(Collectors.toSet())); } /** @@ -924,13 +845,8 @@ public String toString() { return map.toString(); } - private void modifyFields(final String pattern, final Consumer consumer) { - findFields(pattern).collect(Collectors.toSet()).forEach(consumer); - } - - // TODO path - private Stream findFields(final String pattern) { - return matchFields(pattern, Stream::filter); + void modifyFields(final String pattern, final Consumer consumer) { + matchFields(pattern, Stream::filter).collect(Collectors.toSet()).forEach(consumer); } private T matchFields(final String pattern, final BiFunction, Predicate, T> function) { diff --git a/metafix/src/main/java/org/metafacture/metafix/api/FixPredicate.java b/metafix/src/main/java/org/metafacture/metafix/api/FixPredicate.java index a654af60..9da2a4a9 100644 --- a/metafix/src/main/java/org/metafacture/metafix/api/FixPredicate.java +++ b/metafix/src/main/java/org/metafacture/metafix/api/FixPredicate.java @@ -17,6 +17,7 @@ package org.metafacture.metafix.api; import org.metafacture.metafix.Metafix; +import org.metafacture.metafix.Path; import org.metafacture.metafix.Record; import org.metafacture.metafix.Value; @@ -42,7 +43,7 @@ default boolean testConditional(final Record record, final List params, final String field = params.get(0); final String string = params.get(1); - final Value value = record.find(field); + final Value value = new Path(field).findInHash(record); return value != null && qualifier.test(value.asList(null).asArray().stream(), v -> v.extractType((m, c) -> m .ifString(s -> c.accept(conditional.test(s, string))) .orElse(w -> c.accept(false))