From 8eb590005b2968bd894f050c0939574b4b7f55a4 Mon Sep 17 00:00:00 2001 From: Jens Wille Date: Fri, 12 Nov 2021 13:35:41 +0100 Subject: [PATCH] Introduce Mapping abstraction. (#64) Intended as a shared type for records and map values. --- .../java/org/metafacture/metafix/Mapping.java | 146 ++++++++++++ .../java/org/metafacture/metafix/Record.java | 117 +--------- .../org/metafacture/metafix/MappingTest.java | 219 ++++++++++++++++++ .../org/metafacture/metafix/RecordTest.java | 185 --------------- 4 files changed, 368 insertions(+), 299 deletions(-) create mode 100644 metafix/src/main/java/org/metafacture/metafix/Mapping.java create mode 100644 metafix/src/test/java/org/metafacture/metafix/MappingTest.java diff --git a/metafix/src/main/java/org/metafacture/metafix/Mapping.java b/metafix/src/main/java/org/metafacture/metafix/Mapping.java new file mode 100644 index 00000000..ba846c7a --- /dev/null +++ b/metafix/src/main/java/org/metafacture/metafix/Mapping.java @@ -0,0 +1,146 @@ +/* + * Copyright 2021 hbz NRW + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.metafacture.metafix; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * Represents a mapping of metadata fields and values. + */ +public class Mapping { + + private static final String EMPTY = ""; + + private final Map map = new LinkedHashMap<>(); + + /** + * Creates an empty instance of {@link Mapping}. + */ + public Mapping() { + } + + /** + * Checks whether this mapping contains the metadata field. + * + * @param field the field name + * @return true if this mapping contains the metadata field, false otherwise + */ + public boolean containsField(final String field) { + return map.containsKey(field); + } + + /** + * Checks whether this mapping is empty. + * + * @return true if this mapping is empty, false otherwise + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Gets the number of field/value pairs in this mapping. + * + * @return the number of field/value pairs in this mapping + */ + public int size() { + return map.size(); + } + + /** + * Adds a field/value pair to this mapping, provided it's not {@code null}. + * + * @param field the field name + * @param value the metadata value + */ + public void put(final String field, final Object value) { + if (value != null) { + map.put(field, value); + } + } + + /** + * {@link #put(String, Object) Replaces} a field/value pair in this mapping, + * provided the field name is already {@link #containsField(String) present}. + * + * @param field the field name + * @param value the metadata value + */ + public void replace(final String field, final Object value) { + if (containsField(field)) { + put(field, value); + } + } + + /** + * Retrieves the field value from this mapping. + * + * @param field the field name + * @return the metadata value + */ + public Object get(final String field) { + return map.get(field); + } + + /** + * Removes the given field/value pair from this mapping. + * + * @param field the field name + */ + public void remove(final String field) { + map.remove(field); + } + + /** + * Retains only the given field/value pairs in this mapping. + * + * @param fields the field names + */ + public void retainFields(final Collection fields) { + map.keySet().retainAll(fields); + } + + /** + * Removes all field/value pairs from this mapping whose value is empty. + */ + public void removeEmptyValues() { + map.values().removeIf(EMPTY::equals); + } + + /** + * Iterates over all field/value pairs in this mapping. + * + * @param consumer the action to be performed for each field/value pair + */ + public void forEach(final BiConsumer consumer) { + map.forEach(consumer); + } + + @Override + public String toString() { + return map.toString(); + } + + // TODO: Replace map accesses with record operations! + public Map temporarilyGetMap() { + return map; + } + +} diff --git a/metafix/src/main/java/org/metafacture/metafix/Record.java b/metafix/src/main/java/org/metafacture/metafix/Record.java index a6423d50..55747f69 100644 --- a/metafix/src/main/java/org/metafacture/metafix/Record.java +++ b/metafix/src/main/java/org/metafacture/metafix/Record.java @@ -16,19 +16,10 @@ package org.metafacture.metafix; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.BiConsumer; - /** - * Represents a metadata record, i.e., a mapping of fields and values. + * Represents a metadata record, i.e., a {@link Mapping} of fields and values. */ -public class Record { - - private static final String EMPTY = ""; - - private final Map map = new LinkedHashMap<>(); +public class Record extends Mapping { private boolean reject; @@ -70,112 +61,10 @@ public boolean getReject() { return reject; } - /** - * Checks whether this record contains the metadata field. - * - * @param field the field name - * @return true if this record contains the metadata field, false otherwise - */ - public boolean containsField(final String field) { - return map.containsKey(field); - } - - /** - * Checks whether this record is empty. - * - * @return true if this record is empty, false otherwise - */ - public boolean isEmpty() { - return map.isEmpty(); - } - - /** - * Gets the number of field/value pairs in this record. - * - * @return the number of field/value pairs in this record - */ - public int size() { - return map.size(); - } - - /** - * Adds a field/value pair to this record, provided it's not {@code null}. - * - * @param field the field name - * @param value the metadata value - */ - public void put(final String field, final Object value) { - if (value != null) { - map.put(field, value); - } - } - - /** - * {@link #put(String, Object) Replaces} a field/value pair in this record, - * provided the field name is already {@link #containsField(String) present}. - * - * @param field the field name - * @param value the metadata value - */ - public void replace(final String field, final Object value) { - if (containsField(field)) { - put(field, value); - } - } - - /** - * Retrieves the field value from this record. - * - * @param field the field name - * @return the metadata value - */ - public Object get(final String field) { - return map.get(field); - } - - /** - * Removes the given field/value pair from this record. - * - * @param field the field name - */ - public void remove(final String field) { - map.remove(field); - } - - /** - * Retains only the given field/value pairs in this record. - * - * @param fields the field names - */ - public void retainFields(final Collection fields) { - map.keySet().retainAll(fields); - } - - /** - * Removes all field/value pairs from this record whose value is empty. - */ - public void removeEmptyValues() { - map.values().removeIf(EMPTY::equals); - } - - /** - * Iterates over all field/value pairs in this record. - * - * @param consumer the action to be performed for each field/value pair - */ - public void forEach(final BiConsumer consumer) { - map.forEach(consumer); - } - @Override public String toString() { // TODO: Improve string representation? Include reject status, etc.? - return map.toString(); - } - - // TODO: Replace map accesses with record operations! - public Map temporarilyGetMap() { - return map; + return super.toString(); } } diff --git a/metafix/src/test/java/org/metafacture/metafix/MappingTest.java b/metafix/src/test/java/org/metafacture/metafix/MappingTest.java new file mode 100644 index 00000000..1f06e660 --- /dev/null +++ b/metafix/src/test/java/org/metafacture/metafix/MappingTest.java @@ -0,0 +1,219 @@ +/* + * Copyright 2021 hbz NRW + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.metafacture.metafix; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MappingTest { + + private static final String FIELD = "field"; + private static final String OTHER_FIELD = "other field"; + private static final String VALUE = "value"; + private static final String OTHER_VALUE = "other value"; + + public MappingTest() { + } + + @Test + public void shouldNotContainMissingField() { + final Mapping mapping = new Mapping(); + Assertions.assertFalse(mapping.containsField(FIELD)); + } + + @Test + public void shouldContainExistingField() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + + Assertions.assertTrue(mapping.containsField(FIELD)); + } + + @Test + public void shouldNotContainNullValue() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, null); + + Assertions.assertFalse(mapping.containsField(FIELD)); + } + + @Test + public void shouldBeEmptyByDefault() { + final Mapping mapping = new Mapping(); + Assertions.assertTrue(mapping.isEmpty()); + } + + @Test + public void shouldNotBeEmptyAfterAddingValue() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + + Assertions.assertFalse(mapping.isEmpty()); + } + + @Test + public void shouldNotAddNullValue() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, null); + + Assertions.assertTrue(mapping.isEmpty()); + } + + @Test + public void shouldGetSizeOfDefaultMapping() { + final Mapping mapping = new Mapping(); + Assertions.assertEquals(0, mapping.size()); + } + + @Test + public void shouldGetSizeAfterAddingValues() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.put(OTHER_FIELD, OTHER_VALUE); + + Assertions.assertEquals(2, mapping.size()); + } + + @Test + public void shouldNotGetMissingField() { + final Mapping mapping = new Mapping(); + Assertions.assertNull(mapping.get(FIELD)); + } + + @Test + public void shouldGetExistingField() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + + Assertions.assertEquals(VALUE, mapping.get(FIELD)); + } + + @Test + public void shouldNotReplaceMissingField() { + final Mapping mapping = new Mapping(); + mapping.replace(FIELD, VALUE); + + Assertions.assertNull(mapping.get(FIELD)); + Assertions.assertFalse(mapping.containsField(FIELD)); + } + + @Test + public void shouldReplaceExistingField() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.replace(FIELD, OTHER_VALUE); + + Assertions.assertEquals(OTHER_VALUE, mapping.get(FIELD)); + } + + @Test + public void shouldNotReplaceExistingFieldWithNullValue() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.replace(FIELD, null); + + Assertions.assertEquals(VALUE, mapping.get(FIELD)); + } + + @Test + public void shouldRemoveMissingField() { + final Mapping mapping = new Mapping(); + mapping.remove(FIELD); + + Assertions.assertNull(mapping.get(FIELD)); + Assertions.assertFalse(mapping.containsField(FIELD)); + } + + @Test + public void shouldRemoveExistingField() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.remove(FIELD); + + Assertions.assertNull(mapping.get(FIELD)); + Assertions.assertFalse(mapping.containsField(FIELD)); + } + + @Test + public void shouldRetainFields() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.put(OTHER_FIELD, OTHER_VALUE); + + mapping.retainFields(Arrays.asList(FIELD)); + + Assertions.assertTrue(mapping.containsField(FIELD)); + Assertions.assertFalse(mapping.containsField(OTHER_FIELD)); + } + + @Test + public void shouldRetainNoFields() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + + mapping.retainFields(Arrays.asList()); + + Assertions.assertTrue(mapping.isEmpty()); + } + + @Test + public void shouldNotRetainMissingFields() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + + mapping.retainFields(Arrays.asList(FIELD, OTHER_FIELD)); + + Assertions.assertTrue(mapping.containsField(FIELD)); + Assertions.assertFalse(mapping.containsField(OTHER_FIELD)); + } + + @Test + public void shouldRemoveEmptyValues() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.put(OTHER_FIELD, ""); + + mapping.removeEmptyValues(); + + Assertions.assertTrue(mapping.containsField(FIELD)); + Assertions.assertFalse(mapping.containsField(OTHER_FIELD)); + } + + @Test + public void shouldIterateOverFieldValuePairs() { + final Mapping mapping = new Mapping(); + mapping.put(FIELD, VALUE); + mapping.put(OTHER_FIELD, OTHER_VALUE); + mapping.put("empty field", ""); + mapping.put("_special field", 1); + + final List fields = new ArrayList<>(); + final List values = new ArrayList<>(); + mapping.forEach((k, v) -> { + fields.add(k); + values.add(v); + }); + + Assertions.assertEquals(Arrays.asList(FIELD, OTHER_FIELD, "empty field", "_special field"), fields); + Assertions.assertEquals(Arrays.asList(VALUE, OTHER_VALUE, "", 1), values); + } + +} diff --git a/metafix/src/test/java/org/metafacture/metafix/RecordTest.java b/metafix/src/test/java/org/metafacture/metafix/RecordTest.java index db2f6d19..34990044 100644 --- a/metafix/src/test/java/org/metafacture/metafix/RecordTest.java +++ b/metafix/src/test/java/org/metafacture/metafix/RecordTest.java @@ -20,202 +20,17 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class RecordTest { private static final String FIELD = "field"; - private static final String OTHER_FIELD = "other field"; private static final String VALUE = "value"; private static final String OTHER_VALUE = "other value"; public RecordTest() { } - @Test - public void shouldNotContainMissingField() { - final Record record = new Record(); - Assertions.assertFalse(record.containsField(FIELD)); - } - - @Test - public void shouldContainExistingField() { - final Record record = new Record(); - record.put(FIELD, VALUE); - - Assertions.assertTrue(record.containsField(FIELD)); - } - - @Test - public void shouldNotContainNullValue() { - final Record record = new Record(); - record.put(FIELD, null); - - Assertions.assertFalse(record.containsField(FIELD)); - } - - @Test - public void shouldBeEmptyByDefault() { - final Record record = new Record(); - Assertions.assertTrue(record.isEmpty()); - } - - @Test - public void shouldNotBeEmptyAfterAddingValue() { - final Record record = new Record(); - record.put(FIELD, VALUE); - - Assertions.assertFalse(record.isEmpty()); - } - - @Test - public void shouldNotAddNullValue() { - final Record record = new Record(); - record.put(FIELD, null); - - Assertions.assertTrue(record.isEmpty()); - } - - @Test - public void shouldGetSizeOfDefaultRecord() { - final Record record = new Record(); - Assertions.assertEquals(0, record.size()); - } - - @Test - public void shouldGetSizeAfterAddingValues() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.put(OTHER_FIELD, OTHER_VALUE); - - Assertions.assertEquals(2, record.size()); - } - - @Test - public void shouldNotGetMissingField() { - final Record record = new Record(); - Assertions.assertNull(record.get(FIELD)); - } - - @Test - public void shouldGetExistingField() { - final Record record = new Record(); - record.put(FIELD, VALUE); - - Assertions.assertEquals(VALUE, record.get(FIELD)); - } - - @Test - public void shouldNotReplaceMissingField() { - final Record record = new Record(); - record.replace(FIELD, VALUE); - - Assertions.assertNull(record.get(FIELD)); - Assertions.assertFalse(record.containsField(FIELD)); - } - - @Test - public void shouldReplaceExistingField() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.replace(FIELD, OTHER_VALUE); - - Assertions.assertEquals(OTHER_VALUE, record.get(FIELD)); - } - - @Test - public void shouldNotReplaceExistingFieldWithNullValue() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.replace(FIELD, null); - - Assertions.assertEquals(VALUE, record.get(FIELD)); - } - - @Test - public void shouldRemoveMissingField() { - final Record record = new Record(); - record.remove(FIELD); - - Assertions.assertNull(record.get(FIELD)); - Assertions.assertFalse(record.containsField(FIELD)); - } - - @Test - public void shouldRemoveExistingField() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.remove(FIELD); - - Assertions.assertNull(record.get(FIELD)); - Assertions.assertFalse(record.containsField(FIELD)); - } - - @Test - public void shouldRetainFields() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.put(OTHER_FIELD, OTHER_VALUE); - - record.retainFields(Arrays.asList(FIELD)); - - Assertions.assertTrue(record.containsField(FIELD)); - Assertions.assertFalse(record.containsField(OTHER_FIELD)); - } - - @Test - public void shouldRetainNoFields() { - final Record record = new Record(); - record.put(FIELD, VALUE); - - record.retainFields(Arrays.asList()); - - Assertions.assertTrue(record.isEmpty()); - } - - @Test - public void shouldNotRetainMissingFields() { - final Record record = new Record(); - record.put(FIELD, VALUE); - - record.retainFields(Arrays.asList(FIELD, OTHER_FIELD)); - - Assertions.assertTrue(record.containsField(FIELD)); - Assertions.assertFalse(record.containsField(OTHER_FIELD)); - } - - @Test - public void shouldRemoveEmptyValues() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.put(OTHER_FIELD, ""); - - record.removeEmptyValues(); - - Assertions.assertTrue(record.containsField(FIELD)); - Assertions.assertFalse(record.containsField(OTHER_FIELD)); - } - - @Test - public void shouldIterateOverFieldValuePairs() { - final Record record = new Record(); - record.put(FIELD, VALUE); - record.put(OTHER_FIELD, OTHER_VALUE); - record.put("empty field", ""); - record.put("_special field", 1); - - final List fields = new ArrayList<>(); - final List values = new ArrayList<>(); - record.forEach((k, v) -> { - fields.add(k); - values.add(v); - }); - - Assertions.assertEquals(Arrays.asList(FIELD, OTHER_FIELD, "empty field", "_special field"), fields); - Assertions.assertEquals(Arrays.asList(VALUE, OTHER_VALUE, "", 1), values); - } - @Test public void shouldCreateShallowCloneFromEmptyRecord() { final Record record = new Record();