diff --git a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java index c8c6181c..4f7e964c 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixMethod.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixMethod.java @@ -244,7 +244,7 @@ private Map fileMap(final String location, final String separato private static final Pattern NAMED_GROUP_PATTERN = Pattern.compile("\\(\\?<(.+?)>"); private static final String EMPTY = ""; - private static final String DOT_APPEND = "." + Value.Hash.APPEND_FIELD; + private static final String DOT_APPEND = "." + Value.APPEND_FIELD; abstract void apply(Record record, List params, Map options); diff --git a/metafix/src/main/java/org/metafacture/metafix/FixPredicate.java b/metafix/src/main/java/org/metafacture/metafix/FixPredicate.java index 866eafc2..6d280c58 100644 --- a/metafix/src/main/java/org/metafacture/metafix/FixPredicate.java +++ b/metafix/src/main/java/org/metafacture/metafix/FixPredicate.java @@ -31,7 +31,7 @@ public Predicate of(final String string) { equal { @Override public Predicate of(final String string) { - return v -> v.toString().equals(string); + return v -> v.toString().equals(string); } }, match { diff --git a/metafix/src/main/java/org/metafacture/metafix/Metafix.java b/metafix/src/main/java/org/metafacture/metafix/Metafix.java index 1e03be87..e3daed4d 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Metafix.java +++ b/metafix/src/main/java/org/metafacture/metafix/Metafix.java @@ -149,14 +149,14 @@ private void emit(final String field, final Value value) { Value.asList(value, array -> { final boolean isMulti = array.size() > 1 || value.isArray(); if (isMulti) { - outputStreamReceiver.startEntity(field + "[]"); + outputStreamReceiver.startEntity(field); } for (int i = 0; i < array.size(); ++i) { final Value arrayValue = array.get(i); if (arrayValue.isHash()) { - outputStreamReceiver.startEntity(isMulti ? "" : field); + outputStreamReceiver.startEntity(isMulti ? (i + 1) + "" : field); arrayValue.asHash().forEach(this::emit); outputStreamReceiver.endEntity(); } @@ -182,21 +182,9 @@ public void startEntity(final String name) { entities.size() <= currentEntityIndex ? null : entities.get(currentEntityIndex); entityCountStack.push(Integer.valueOf(entityCount)); flattener.startEntity(name); - entities.add(currentEntity(name, previousEntity != null ? previousEntity : currentRecord)); - } - - private Value.Hash currentEntity(final String name, final Value.Hash previousEntity) { - final Value existingValue = previousEntity != null ? previousEntity.get(name) : null; - final Value.Hash currentEntity; - if (existingValue != null && existingValue.isHash()) { - currentEntity = previousEntity.get(name).asHash(); - } - else { - final Value value = Value.newHash(); - currentEntity = value.asHash(); - (previousEntity != null ? previousEntity : currentRecord).add(name, value); - } - return currentEntity; + final Value value = Value.newHash(); + (previousEntity != null ? previousEntity : currentRecord).add(name, value); + entities.add(value.asHash()); } @Override diff --git a/metafix/src/main/java/org/metafacture/metafix/Value.java b/metafix/src/main/java/org/metafacture/metafix/Value.java index f31f4259..2fddd53d 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Value.java +++ b/metafix/src/main/java/org/metafacture/metafix/Value.java @@ -33,6 +33,10 @@ */ public class Value { + /*package-private*/ static final String APPEND_FIELD = "$append"; + private static final String LAST_FIELD = "$last"; + private static final String ASTERISK = "*"; + private final Array array; private final Hash hash; private final String string; @@ -148,6 +152,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; @@ -199,14 +207,7 @@ public Value asList(final Consumer consumer) { } public Value merge(final Value value) { - if (isHash() && value.isHash()) { - final Hash asHash = asHash(); - value.asHash().forEach(asHash::put); - return this; - } - else { - return asList(a1 -> value.asList(a2 -> a2.forEach(a1::add))); - } + return asList(a1 -> value.asList(a2 -> a2.forEach(a1::add))); } @Override @@ -235,6 +236,10 @@ public String toString() { return result; } + private static String[] tail(final String[] fields) { + return Arrays.copyOfRange(fields, 1, fields.length); + } + enum Type { Array, Hash, @@ -250,6 +255,27 @@ public String toString() { public abstract String asString(); + private enum InsertMode { + + REPLACE { + @Override + void apply(final Hash hash, final String field, final String value) { + hash.put(field, new Value(value)); + } + }, + APPEND { + @Override + void apply(final Hash hash, final String field, final String value) { + final Value oldValue = hash.get(field); + final Value newValue = new Value(value); + hash.put(field, oldValue == null ? newValue : oldValue.merge(newValue)); + } + }; + + abstract void apply(Hash hash, String field, String value); + + } + } /** @@ -292,6 +318,107 @@ public String asString() { return list.toString(); } + public void remove(final int index) { + list.remove(index); + } + + private void removeNested(final String[] fields) { + if (fields.length >= 1 && fields[0].equals(ASTERISK)) { + list.clear(); + } + else if (fields.length >= 1 && isNumber(fields[0])) { + final int index = Integer.parseInt(fields[0]) - 1; // TODO: 0-based Catmandu vs. 1-based Metafacture + if (index >= 0 && index < size()) { + remove(index); + } + } + } + + private Value find(final String[] path) { + final Value result; + if (path.length > 0) { + if (path[0].equals(ASTERISK)) { + result = newArray(a -> forEach(v -> a.add(findInValue(tail(path), v)))); + } + else if (isNumber(path[0])) { + final int index = Integer.parseInt(path[0]) - 1; // TODO: 0-based Catmandu vs. 1-based Metafacture + if (index >= 0 && index < size()) { + result = findInValue(tail(path), get(index)); + } + else { + result = null; + } + } + // TODO: WDCD? copy_field('your.name','author[].name'), where name is an array + else { + result = newArray(a -> forEach(v -> a.add(findInValue(path, v)))); + } + } + else { + result = new Value(this); + } + return result; + } + + private Value findInValue(final String[] path, final Value value) { + final Value result; + // TODO: move impl into enum elements, here call only value.find + if (value != null) { + switch (value.type) { + case Hash: + result = value.asHash().find(path); + break; + case Array: + result = value.asArray().find(path); + break; + case String: + result = value; + break; + default: + result = null; + break; + } + } + else { + result = null; + } + return result; + } + + private void insert(final AbstractValueType.InsertMode mode, final String[] fields, final String newValue) { + switch (fields[0]) { + case ASTERISK: + // TODO: WDCD? descend into the array? + break; + case APPEND_FIELD: + add(newHash(h -> h.insert(mode, tail(fields), newValue))); + break; + case LAST_FIELD: + if (size() > 0) { + final Value last = get(size() - 1); + if (last.isHash()) { + last.asHash().insert(mode, tail(fields), newValue); + } + } + break; + default: + if (isNumber(fields[0])) { + // TODO: WDCD? insert at the given index? also descend into the array? + if (fields.length == 1) { + add(new Value(newValue)); + } + if (fields.length > 1) { + final Value newHash = Value.newHash(); + mode.apply(newHash.asHash(), fields[1], newValue); + add(newHash); + } + } + else { + add(newHash(h -> h.insert(mode, fields, newValue))); + } + break; + } + } } /** @@ -299,11 +426,10 @@ public String asString() { */ public static class Hash extends AbstractValueType { - /*package-private*/ static final String APPEND_FIELD = "$append"; - private static final String LAST_FIELD = "$last"; - private static final String FIELD_PATH_SEPARATOR = "\\."; + private static final String UNEXPECTED = "expected array or hash, got "; + private final Map map = new LinkedHashMap<>(); /** @@ -366,11 +492,11 @@ public void replace(final String field, final Value value) { } public Value replace(final String fieldPath, final String newValue) { - return insert(InsertMode.REPLACE, fieldPath, newValue); + return insert(AbstractValueType.InsertMode.REPLACE, fieldPath, newValue); } public Value append(final String fieldPath, final String newValue) { - return insert(InsertMode.APPEND, fieldPath, newValue); + return insert(AbstractValueType.InsertMode.APPEND, fieldPath, newValue); } /** @@ -389,21 +515,30 @@ public Value find(final String fieldPath) { private 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, Arrays.copyOfRange(fields, 1, fields.length)); + findNested(field, tail(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.isHash()) { - return value.asHash().find(remainingFields); + Value result = null; + if (value != null) { + switch (value.type) { + case Array: + result = value.asArray().find(remainingFields); + break; + case Hash: + result = value.asHash().find(remainingFields); + break; + default: + throw new IllegalStateException(UNEXPECTED + value.type); + } } - - throw new IllegalStateException("expected hash, got " + value.type); + return result; } public Value findList(final String fieldPath, final Consumer consumer) { @@ -431,49 +566,43 @@ public void add(final String field, final Value newValue) { put(field, oldValue == null ? newValue : oldValue.merge(newValue)); } - public Value insert(final InsertMode mode, final String fieldPath, final String newValue) { + public Value insert(final AbstractValueType.InsertMode mode, final String fieldPath, final String newValue) { return insert(mode, split(fieldPath), newValue); } - private Value insert(final InsertMode mode, final String[] fields, final String newValue) { + private Value insert(final AbstractValueType.InsertMode mode, final String[] fields, final String newValue) { final String field = fields[0]; - + if (field.equals(APPEND_FIELD) || field.equals(LAST_FIELD)) { + // TODO: WDCD? $last, $append skipped for hashes here: + return insert(mode, tail(fields), newValue); + } if (fields.length == 1) { - mode.apply(this, field, newValue); + if (fields[0].equals(ASTERISK)) { + //TODO: WDCD? insert into each element? + } + else { + mode.apply(this, field, newValue); + } } else { if (!containsField(field)) { put(field, newHash()); } - final String[] remainingFields = Arrays.copyOfRange(fields, 1, fields.length); - final String[] nestedFields = Arrays.copyOfRange(remainingFields, 1, remainingFields.length); final Value value = get(field); - - if (value.isHash()) { - value.asHash().insert(mode, remainingFields, newValue); - } - else if (value.isArray()) { - final Array array = value.asArray(); - - switch (remainingFields[0]) { - case APPEND_FIELD: - array.add(newHash(h -> h.insert(mode, nestedFields, newValue))); + if (value != null) { + switch (value.type) { + // TODO: move impl into enum elements, here call only value.insert + case Hash: + value.asHash().insert(mode, tail(fields), newValue); break; - case LAST_FIELD: - final Value last = array.get(array.size() - 1); - if (last.isHash()) { - last.asHash().insert(mode, nestedFields, newValue); - } + case Array: + value.asArray().insert(mode, tail(fields), newValue); break; default: - array.add(newHash(h -> h.insert(mode, remainingFields, newValue))); - break; + throw new IllegalStateException(UNEXPECTED + value.type); } } - else { - throw new IllegalStateException("expected array or hash, got " + value.type); - } } return new Value(this); @@ -499,23 +628,55 @@ 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); + // TODO: impl and call just value.remove + if (value != null) { + switch (value.type) { + case String: + throw new IllegalStateException(UNEXPECTED + value.type); + case Array: + value.asArray().removeNested(tail(fields)); + break; + case Hash: + value.asHash().removeNested(tail(fields)); + break; + default: + break; + } + } } } public void copy(final List params) { final String oldName = params.get(0); final String newName = params.get(1); - findList(oldName, a -> a.forEach(v -> append(newName, v.toString()))); + findList(oldName, a -> a.forEach(v -> appendValue(split(newName), v))); + } + + private void appendValue(final String[] newName, final Value v) { + // TODO: impl and call just value.append + if (v != null) { + switch (v.type) { + case String: + append(String.join(".", newName), v.asString()); + break; + case Array: + // TODO: do something here? + break; + case Hash: + appendValue(newName, v.asHash().find(tail(newName))); + break; + default: + break; + } + } } public void transformFields(final List params, final UnaryOperator operator) { final String field = params.get(0); final Value value = find(field); - if (value != null) { removeNested(field); - if (operator != null) { value.asList(a -> a.forEach(v -> append(field, operator.apply(v.toString())))); } @@ -558,27 +719,6 @@ public String asString() { return map.toString(); } - private enum InsertMode { - - REPLACE { - @Override - void apply(final Hash hash, final String field, final String value) { - hash.put(field, new Value(value)); - } - }, - APPEND { - @Override - void apply(final Hash hash, final String field, final String value) { - final Value oldValue = hash.get(field); - final Value newValue = new Value(value); - hash.put(field, oldValue == null ? newValue : oldValue.merge(newValue)); - } - }; - - abstract void apply(Hash hash, String field, String value); - - } - } } diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java index 71d066bf..6d15db98 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixBindTest.java @@ -62,7 +62,7 @@ public void doList() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("author[]"); + o.get().startEntity("author"); o.get().literal("1", "A UNIVERSITY"); o.get().literal("2", "MAX"); o.get().endEntity(); @@ -88,7 +88,7 @@ public void doListPathWithDots() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("author[]"); + o.get().startEntity("author"); o.get().literal("1", "A UNIVERSITY"); o.get().literal("2", "MAX"); o.get().endEntity(); @@ -100,9 +100,9 @@ public void doListPathWithDots() { public void doListWithAppendAndLast() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( "do list('path': 'creator', 'var': 'c')", - " set_array('author')", - " copy_field('c.name', 'author.$append.name')", - " add_field('author.$last.type', 'Default')", + " set_array('author[]')", + " copy_field('c.name', 'author[].$append.name')", + " add_field('author[].$last.type', 'Default')", "end", "remove_field('creator')"), i -> { @@ -117,11 +117,11 @@ public void doListWithAppendAndLast() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("author[]"); - o.get().startEntity(""); + o.get().startEntity("1"); o.get().literal("name", "A University"); - // o.get().literal("type", "Default"); // FIXME: bind scope broken + o.get().literal("type", "Default"); o.get().endEntity(); - o.get().startEntity(""); + o.get().startEntity("2"); o.get().literal("name", "Max"); o.get().literal("type", "Default"); f.apply(2).endEntity(); @@ -149,7 +149,7 @@ public void doListEntitesToLiterals() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("author[]"); + o.get().startEntity("author"); o.get().literal("1", "A UNIVERSITY"); o.get().literal("2", "MAX"); o.get().endEntity(); @@ -160,13 +160,13 @@ public void doListEntitesToLiterals() { @Test public void doListEntitesToEntities() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "do list('path': 'creator.name', 'var': 'c')", - " set_array('author')", - " copy_field('c', 'author.$append.name')", - " if all_contain('c', 'University')", - " add_field('author.$last.type', 'Organization')", + "set_array('author[]')", + "do list('path': 'creator', 'var': 'c')", + " copy_field('c.name', 'author[].$append.name')", + " if all_contain('c.name', 'University')", + " add_field('author[].$last.type', 'Organization')", " else", - " add_field('author.$last.type', 'Person')", //", + " add_field('author[].$last.type', 'Person')", //", " end", "end", "remove_field('creator')"), @@ -182,11 +182,11 @@ public void doListEntitesToEntities() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("author[]"); - o.get().startEntity(""); + o.get().startEntity("1"); o.get().literal("name", "A University"); o.get().literal("type", "Organization"); o.get().endEntity(); - o.get().startEntity(""); + o.get().startEntity("2"); o.get().literal("name", "Max"); o.get().literal("type", "Person"); f.apply(2).endEntity(); @@ -195,17 +195,12 @@ public void doListEntitesToEntities() { } @Test - @Disabled("TODO: how to handle repeated entities: turn to array vs. merge because it's the same?") - public void doListEntitesWithFieldsToEntities() { + public void wildcardForNestedEntities() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "set_array('author[]')", "do list('path': 'creator', 'var': 'c')", - " set_array('author')", - " copy_field('c.name', 'author.$append.name')", - " if all_contain('c.type', 'corporate')", - " add_field('author.$last.type', 'Organization')", - " end", - " if all_contain('c.type', 'personal')", - " add_field('author.$last.type', 'Person')", //", + " if any_match('c.role.*.roleTerm.*.value','aut|cre')", + " copy_field('c.name', 'author[].$append.name')", " end", "end", "remove_field('creator')"), @@ -213,23 +208,35 @@ public void doListEntitesWithFieldsToEntities() { i.startRecord("1"); i.startEntity("creator"); i.literal("name", "A University"); - i.literal("type", "corporate"); + i.startEntity("role"); + i.startEntity("roleTerm"); + i.literal("value", "aut"); + i.endEntity(); + i.startEntity("roleTerm"); + i.literal("value", "tau"); + i.endEntity(); + i.endEntity(); i.endEntity(); i.startEntity("creator"); i.literal("name", "Max"); - i.literal("type", "personal"); + i.startEntity("role"); + i.startEntity("roleTerm"); + i.literal("value", "cre"); + i.endEntity(); + i.startEntity("roleTerm"); + i.literal("value", "rec"); + i.endEntity(); + i.endEntity(); i.endEntity(); i.endRecord(); }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("author[]"); - o.get().startEntity(""); + o.get().startEntity("1"); o.get().literal("name", "A University"); - o.get().literal("type", "Organization"); o.get().endEntity(); - o.get().startEntity(""); + o.get().startEntity("2"); o.get().literal("name", "Max"); - o.get().literal("type", "Person"); f.apply(2).endEntity(); o.get().endRecord(); }); diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixIfTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixIfTest.java index 958c49b1..bfa040f1 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixIfTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixIfTest.java @@ -66,7 +66,7 @@ public void ifAny() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Mary"); o.get().literal("2", "A University"); o.get().endEntity(); @@ -74,7 +74,7 @@ public void ifAny() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Mary"); o.get().literal("2", "Max"); o.get().endEntity(); @@ -106,14 +106,14 @@ public void ifAll() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Mary"); o.get().literal("2", "A University"); o.get().endEntity(); o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Great University"); o.get().literal("2", "A University"); o.get().endEntity(); @@ -151,7 +151,7 @@ public void ifNone() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("author"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Mary"); o.get().literal("2", "A University"); f.apply(2).endEntity(); @@ -159,7 +159,7 @@ public void ifNone() { o.get().startRecord("2"); o.get().startEntity("author"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Max"); o.get().literal("2", "Mary"); f.apply(2).endEntity(); @@ -285,7 +285,7 @@ public void ifAnyMatch() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Some University"); o.get().literal("2", "Filibandrina"); o.get().endEntity(); @@ -312,14 +312,14 @@ public void ifAllMatch() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Max"); o.get().literal("2", "A University"); o.get().endEntity(); o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "Some University"); o.get().literal("2", "University Filibandrina"); o.get().endEntity(); diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java index a9fc99a2..25f9ad5d 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixLookupTest.java @@ -67,7 +67,7 @@ public void inline() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Alohaeha"); o.get().literal("2", "Moin zäme"); o.get().literal("3", "Tach"); @@ -92,7 +92,7 @@ public void inlineMultilineIndent() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Alohaeha"); o.get().literal("2", "Moin zäme"); o.get().endEntity(); @@ -115,7 +115,7 @@ public void inlineDotNotationNested() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("data"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Alohaeha"); o.get().literal("2", "Moin zäme"); o.get().literal("3", "Tach"); @@ -146,7 +146,7 @@ public void csv() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Alohaeha"); o.get().literal("2", "Moin zäme"); o.get().literal("3", "Tach"); @@ -179,7 +179,7 @@ public void tsv() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Alohaeha"); o.get().literal("2", "Moin zäme"); o.get().literal("3", "Tach"); diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java index c27bb225..d2219bfd 100644 --- a/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/MetafixMethodTest.java @@ -70,7 +70,7 @@ public void upcase() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "MARC"); o.get().literal("2", "JSON"); o.get().endEntity(); @@ -95,7 +95,7 @@ public void upcaseDotNotationNested() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("data"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "MARC"); o.get().literal("2", "JSON"); f.apply(2).endEntity(); @@ -123,7 +123,7 @@ public void downcase() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "marc"); o.get().literal("2", "json"); o.get().endEntity(); @@ -154,7 +154,7 @@ public void capitalize() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "Marc"); o.get().literal("2", "Json"); o.get().endEntity(); @@ -185,7 +185,7 @@ public void substring() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "m"); o.get().literal("2", "j"); o.get().endEntity(); @@ -217,7 +217,7 @@ public void substringWithVar() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "ma"); o.get().literal("2", "js"); o.get().endEntity(); @@ -248,7 +248,7 @@ public void trim() { o.get().endRecord(); o.get().startRecord("2"); - o.get().startEntity("title[]"); + o.get().startEntity("title"); o.get().literal("1", "marc"); o.get().literal("2", "json"); o.get().endEntity(); @@ -270,7 +270,7 @@ public void format() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("number[]"); + o.get().startEntity("number"); o.get().literal("1", "41 : 15"); o.get().endEntity(); o.get().endRecord(); @@ -287,7 +287,7 @@ public void parseText() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("date[]"); + o.get().startEntity("date"); o.get().literal("1", "2015"); o.get().literal("2", "03"); o.get().literal("3", "07"); diff --git a/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java b/metafix/src/test/java/org/metafacture/metafix/MetafixRecordTest.java index aef164ed..8cf6c446 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( @@ -129,9 +102,11 @@ public void entitiesPassThroughRepeatNestedEntity() { o.get().startRecord("1"); o.get().startEntity("deep"); o.get().startEntity("nested"); - o.get().startEntity("field[]"); - o.get().literal("1", "value1"); - o.get().literal("2", "value2"); + o.get().startEntity("1"); + o.get().literal("field", "value1"); + o.get().endEntity(); + o.get().startEntity("2"); + o.get().literal("field", "value2"); f.apply(3).endEntity(); o.get().endRecord(); }); @@ -211,7 +186,7 @@ public void add() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("my"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "patrick"); o.get().literal("2", "nicolas"); f.apply(2).endEntity(); @@ -219,7 +194,7 @@ public void add() { o.get().startRecord("2"); o.get().startEntity("my"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "max"); o.get().literal("2", "patrick"); o.get().literal("3", "nicolas"); @@ -228,7 +203,7 @@ public void add() { o.get().startRecord("3"); o.get().startEntity("my"); - o.get().startEntity("name[]"); + o.get().startEntity("name"); o.get().literal("1", "patrick"); o.get().literal("2", "nicolas"); f.apply(2).endEntity(); @@ -306,7 +281,7 @@ public void copy() { public void copyIntoArrayOfStrings() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( // "set_array('author')", <- results in separate objects/entities here - "copy_field('your.name','author.name')", + "copy_field('your.name','author.name[]')", "remove_field('your')"), i -> { i.startRecord("1"); @@ -329,8 +304,8 @@ public void copyIntoArrayOfStrings() { @Test public void copyIntoArrayOfObjects() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "set_array('author')", - "copy_field('your.name','author.name')", + "set_array('author[]')", + "copy_field('your.name','author[].name')", "remove_field('your')"), i -> { i.startRecord("1"); @@ -344,10 +319,10 @@ public void copyIntoArrayOfObjects() { }, (o, f) -> { o.get().startRecord("1"); o.get().startEntity("author[]"); - o.get().startEntity(""); + o.get().startEntity("1"); o.get().literal("name", "max"); o.get().endEntity(); - o.get().startEntity(""); + o.get().startEntity("2"); o.get().literal("name", "mo"); f.apply(2).endEntity(); o.get().endRecord(); @@ -357,8 +332,8 @@ public void copyIntoArrayOfObjects() { @Test public void copyIntoArrayTopLevel() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "set_array('author')", - "copy_field('your.name', 'author')", + "set_array('author[]')", + "copy_field('your.name', 'author[]')", "remove_field('your')"), i -> { i.startRecord("1"); @@ -464,10 +439,42 @@ public void removeEntity() { }); } + @Test + public void removeArray() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "remove_field('name')"), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, o -> { + o.get().startRecord("1"); + o.get().endRecord(); + }); + } + + @Test + public void removeArrayElementsByWildcard() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "remove_field('name.*')"), + i -> { + i.startRecord("1"); + i.literal("name", "max"); + i.literal("name", "mo"); + i.endRecord(); + }, o -> { + o.get().startRecord("1"); + o.get().startEntity("name"); + o.get().endEntity(); + o.get().endRecord(); + }); + } + @Test public void setArray() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "set_array('foo','a','b','c')"), + "set_array('foo[]','a','b','c')"), i -> { i.startRecord("1"); i.endRecord(); @@ -594,7 +601,7 @@ public void arrayFromHash() { i.endRecord(); }, o -> { o.get().startRecord("1"); - o.get().startEntity("foo[]"); + o.get().startEntity("foo"); o.get().literal("1", "a"); o.get().literal("2", "b"); o.get().literal("3", "c"); @@ -625,8 +632,8 @@ public void reject() { @Test public void appendArray() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "set_array('nums', '1')", - "set_array('nums.$append', '2', '3')"), + "set_array('nums[]', '1')", + "set_array('nums[].$append', '2', '3')"), i -> { i.startRecord("1"); i.endRecord(); @@ -644,8 +651,8 @@ public void appendArray() { @Test public void mixedArray() { MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( - "set_array('@context', 'https://w3id.org/kim/lrmi-profile/draft/context.jsonld')", - "set_hash('@context.$append', '@language': 'de')"), + "set_array('@context[]', 'https://w3id.org/kim/lrmi-profile/draft/context.jsonld')", + "set_hash('@context[].$append', '@language': 'de')"), i -> { i.startRecord("1"); i.endRecord(); @@ -653,7 +660,7 @@ public void mixedArray() { o.get().startRecord("1"); o.get().startEntity("@context[]"); o.get().literal("1", "https://w3id.org/kim/lrmi-profile/draft/context.jsonld"); - o.get().startEntity(""); + o.get().startEntity("2"); o.get().literal("@language", "de"); f.apply(2).endEntity(); o.get().endRecord(); @@ -715,4 +722,191 @@ 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 accessArrayImplicit() { + 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: WDCD? explicit * for array fields?") + 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 + 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 + 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: implement implicit iteration?") + public void accessArrayOfObjectsByWildcard() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "upcase('author.*.name')", + "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 + public void accessArrayOfObjectsByDoListBind() { + MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList( + "do list('path':'author','var':'a')", + " upcase('a.name')", + "end", + "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(); + }); + } + }