From 9b1bac1e6fb5a2f9455da7cee417d3e425a99450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Thu, 20 Oct 2022 15:12:44 +0200 Subject: [PATCH] add methods to initialize MarcField and MarcRecord from Java Maps --- gradle.properties | 2 +- src/main/java/org/xbib/marc/MarcField.java | 51 ++++++++++++++ src/main/java/org/xbib/marc/MarcRecord.java | 70 ++++++++++++++++--- .../java/org/xbib/marc/MarcFieldTest.java | 18 +++++ .../java/org/xbib/marc/MarcRecordTest.java | 10 +++ 5 files changed, 142 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index 1a80cde..b333f3f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = marc -version = 2.7.1 +version = 2.8.0 org.gradle.warning.mode = ALL diff --git a/src/main/java/org/xbib/marc/MarcField.java b/src/main/java/org/xbib/marc/MarcField.java index 5719346..2470b27 100644 --- a/src/main/java/org/xbib/marc/MarcField.java +++ b/src/main/java/org/xbib/marc/MarcField.java @@ -531,6 +531,57 @@ public class MarcField implements Comparable { return this; } + /** + * A key is a compact representation of tag/indicator/value + * @param key + * @return + */ + public Builder key(String key, String separator, String value) { + String[] s = key.split(separator); + switch (s.length) { + case 3: { + tag(s[0]); + String indicator = s[1].replace('_', ' '); + if (indicator.isEmpty()) { + indicator(" "); + } else { + indicator(indicator); + } + String subfieldId = s[2].replace('_', ' '); + if (subfieldId.isEmpty()) { + subfield(" ", value); + } else { + subfield(subfieldId, value); + } + break; + } + case 2: { + tag(s[0]); + String indicator = s[1].replace('_', ' '); + if (indicator.isEmpty()) { + indicator(" "); + } else { + indicator(indicator); + } + value(value); + break; + } + case 1: { + tag(s[0]); + value(value); + break; + } + case 0: { + tag(key); + value(value); + break; + } + default: + throw new IllegalArgumentException("key specification is invalid: " + key + " length = " + s.length); + } + return this; + } + public Builder setValidator(MarcFieldValidator validator) { this.validator = validator; return this; diff --git a/src/main/java/org/xbib/marc/MarcRecord.java b/src/main/java/org/xbib/marc/MarcRecord.java index 66ab692..04cb983 100644 --- a/src/main/java/org/xbib/marc/MarcRecord.java +++ b/src/main/java/org/xbib/marc/MarcRecord.java @@ -21,11 +21,13 @@ import static org.xbib.marc.json.MarcJsonWriter.TYPE_TAG; import org.xbib.marc.label.RecordLabel; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiConsumer; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -37,14 +39,19 @@ public class MarcRecord extends LinkedHashMap { private static final MarcRecord EMPTY_RECORD = Marc.builder().buildRecord(); - private final String format; + private String format; - private final String type; + private String type; - private final transient RecordLabel recordLabel; + private transient RecordLabel recordLabel; private final transient List marcFields; + private MarcRecord(Map map) { + super(map); + this.marcFields = new LinkedList<>(); + } + /** * Create a MARC record. Use {@link Marc.Builder} to create a MARC record. * @@ -54,8 +61,11 @@ public class MarcRecord extends LinkedHashMap { * @param marcFields the MARC field * @param lightweight true if MARC record fields should not be entered into the underlying hash map. */ - public MarcRecord(String format, String type, RecordLabel recordLabel, - List marcFields, boolean lightweight) { + public MarcRecord(String format, + String type, + RecordLabel recordLabel, + List marcFields, + boolean lightweight) { super(); this.format = format; this.type = type; @@ -64,9 +74,6 @@ public class MarcRecord extends LinkedHashMap { throw new NullPointerException("record label must not be null"); } this.marcFields = marcFields; - if (marcFields == null) { - throw new NullPointerException("fields must not be null"); - } if (!lightweight) { createMap(); } @@ -80,6 +87,33 @@ public class MarcRecord extends LinkedHashMap { return EMPTY_RECORD; } + public static MarcRecord from(Map map) { + return from(map, FORMAT_TAG, TYPE_TAG, LEADER_TAG, RecordLabel.EMPTY); + } + + public static MarcRecord from(Map map, + String formatTag, + String typeTag, + String leaderTag, + RecordLabel recordLabel) { + MarcRecord marcRecord = new MarcRecord(map); + marcRecord.parseMap(map, ".", "", (key, value) -> { + marcRecord.marcFields.add(MarcField.builder().key(key, "\\.", value.toString()).build()); + }); + if (map.containsKey(formatTag)) { + marcRecord.format = map.get(formatTag).toString(); + } + if ( map.containsKey(typeTag)) { + marcRecord.type = map.get(typeTag).toString(); + } + if (map.containsKey(leaderTag)) { + marcRecord.recordLabel = RecordLabel.builder().from(map.get(leaderTag).toString()).build(); + } else { + marcRecord.recordLabel = recordLabel; + } + return marcRecord; + } + /** * Return the MARC record format. * @@ -213,4 +247,24 @@ public class MarcRecord extends LinkedHashMap { } } } + + @SuppressWarnings("unchecked") + private void parseMap(Map source, String separator, String prefix, BiConsumer consumer) { + source.forEach((k, v) -> { + if (v instanceof Map) { + parseMap((Map) v, separator, prefix + k + separator, consumer); + } else if (v instanceof Collection) { + Collection collection = (Collection) v; + for (Object object : collection) { + if (object instanceof Map) { + parseMap((Map) object, separator, prefix + k + separator, consumer); + } else { + consumer.accept(prefix + k, object); + } + } + } else { + consumer.accept(prefix + k, v); + } + }); + } } diff --git a/src/test/java/org/xbib/marc/MarcFieldTest.java b/src/test/java/org/xbib/marc/MarcFieldTest.java index ac14364..3e13477 100644 --- a/src/test/java/org/xbib/marc/MarcFieldTest.java +++ b/src/test/java/org/xbib/marc/MarcFieldTest.java @@ -249,4 +249,22 @@ public class MarcFieldTest { assertNull(marcField.getFirstSubfield().getValue()); assertNull(marcField.getLastSubfield().getValue()); } + + @Test + public void testMarcFieldFromKey() { + MarcField marcField = MarcField.builder().key("100.__.a", "\\.", "Hello World").build(); + assertFalse(marcField.isControl()); + assertEquals("100", marcField.getTag()); + assertEquals(" ", marcField.getIndicator()); + assertEquals("Hello World", marcField.getFirstSubfieldValue("a")); + marcField = MarcField.builder().key("001._._", "\\.", "123").build(); + assertTrue(marcField.isControl()); + assertEquals("001", marcField.getTag()); + assertEquals(" ", marcField.getIndicator()); + assertEquals("123", marcField.getFirstSubfieldValue(" ")); + marcField = MarcField.builder().key("001", "\\.", "123").build(); + assertTrue(marcField.isControl()); + assertEquals("001", marcField.getTag()); + assertEquals("123", marcField.getValue()); + } } diff --git a/src/test/java/org/xbib/marc/MarcRecordTest.java b/src/test/java/org/xbib/marc/MarcRecordTest.java index da337eb..9ff914a 100644 --- a/src/test/java/org/xbib/marc/MarcRecordTest.java +++ b/src/test/java/org/xbib/marc/MarcRecordTest.java @@ -29,6 +29,7 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.text.Normalizer; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; @@ -210,4 +211,13 @@ public class MarcRecordTest { } }); } + + @Test + public void testMarcRecordFromMap() { + Map map = Map.of("001", "123", + "100", Map.of("_", Map.of("a", "Hello World"))); + MarcRecord marcRecord = MarcRecord.from(map); + assertEquals("123", marcRecord.getFields().stream().filter(m -> m.getTag().equals("001")).findFirst().get().getValue()); + assertEquals("Hello World", marcRecord.getFields().stream().filter(m -> m.getTag().equals("100")).findFirst().get().getFirstSubfieldValue("a")); + } }