|
18 | 18 |
|
19 | 19 | package org.mycore.pi.doi; |
20 | 20 |
|
21 | | -import java.util.Comparator; |
| 21 | +import static org.mycore.pi.util.MCRPIGeneratorUtils.formatCount; |
| 22 | +import static org.mycore.pi.util.MCRPIGeneratorUtils.getCountPattern; |
| 23 | +import static org.mycore.pi.util.MCRPIGeneratorUtils.getCreateDate; |
| 24 | +import static org.mycore.pi.util.MCRPIGeneratorUtils.readCountFromDatabase; |
| 25 | + |
22 | 26 | import java.util.Date; |
23 | 27 | import java.util.HashMap; |
24 | | -import java.util.List; |
25 | 28 | import java.util.Locale; |
26 | 29 | import java.util.Map; |
27 | 30 | import java.util.Objects; |
28 | | -import java.util.Optional; |
29 | 31 | import java.util.concurrent.atomic.AtomicInteger; |
30 | | -import java.util.function.Predicate; |
31 | | -import java.util.regex.Matcher; |
| 32 | +import java.util.function.Supplier; |
32 | 33 | import java.util.regex.Pattern; |
33 | 34 |
|
34 | | -import org.mycore.common.MCRException; |
35 | | -import org.mycore.common.MCRPersistenceException; |
36 | | -import org.mycore.common.config.MCRConfiguration2; |
| 35 | +import org.mycore.common.config.annotation.MCRConfigurationProxy; |
| 36 | +import org.mycore.common.config.annotation.MCRProperty; |
37 | 37 | import org.mycore.datamodel.common.MCRISO8601Date; |
38 | 38 | import org.mycore.datamodel.metadata.MCRBase; |
39 | | -import org.mycore.datamodel.metadata.MCRObjectService; |
40 | 39 | import org.mycore.pi.MCRPIGenerator; |
41 | | -import org.mycore.pi.MCRPIManager; |
42 | | -import org.mycore.pi.MCRPIRegistrationInfo; |
| 40 | +import org.mycore.pi.exceptions.MCRPersistentIdentifierException; |
| 41 | + |
| 42 | +/** |
| 43 | + * {@link MCRCreateDateDOIGenerator} is a {@link MCRPIGenerator} for {@link MCRDigitalObjectIdentifier} identifiers |
| 44 | + * that generates identifiers using a given prefix and the current date and a per-date counter for the suffix. |
| 45 | + * <p> |
| 46 | + * The following configuration options are available: |
| 47 | + * <ul> |
| 48 | + * <li> The property suffix {@link MCRCreateDateDOIGenerator#DATE_FORMAT_KEY} can be used to |
| 49 | + * specify the date format to be used (optional, defaults to {@link MCRCreateDateDOIGenerator#DEFAULT_DATE_FORMAT}). |
| 50 | + * <li> The property suffix {@link MCRCreateDateDOIGenerator#PREFIX_KEY} can be used to |
| 51 | + * specify the prefix. |
| 52 | + * <li> The property suffix {@link MCRCreateDateDOIGenerator#COUNT_PRECISION_KEY} can be used to |
| 53 | + * specify number of digits to be used for the count (optional, defaults to <code>-1</code>, |
| 54 | + * which uses the natural number of digits). |
| 55 | + * </ul> |
| 56 | + * Example: |
| 57 | + * <pre><code> |
| 58 | + * [...].Class=org.mycore.pi.doi.MCRCreateDateDOIGenerator |
| 59 | + * [...].DateFormat=yyyy-MM-dd |
| 60 | + * [...].Prefix=10.1234 |
| 61 | + * [...].CountPrecision=6 |
| 62 | + * </code></pre> |
| 63 | + */ |
| 64 | +@MCRConfigurationProxy(proxyClass = MCRCreateDateDOIGenerator.Factory.class) |
| 65 | +public class MCRCreateDateDOIGenerator extends MCRDOIGeneratorBase { |
| 66 | + |
| 67 | + public static final String DEFAULT_DATE_FORMAT = "yyyyMMdd-HHmmss"; |
43 | 68 |
|
44 | | -public class MCRCreateDateDOIGenerator extends MCRPIGenerator<MCRDigitalObjectIdentifier> { |
| 69 | + public static final Locale DEFAULT_DATE_LOCALE = Locale.ENGLISH; |
45 | 70 |
|
46 | | - private static final String DATE_PATTERN = "yyyyMMdd-HHmmss"; |
| 71 | + public static final String DATE_FORMAT_KEY = "DateFormat"; |
47 | 72 |
|
48 | | - private static final String CREATE_DATE_PLACEHOLDER = "$createDate$"; |
| 73 | + public static final String PREFIX_KEY = "Prefix"; |
49 | 74 |
|
50 | | - private static final String DATE_REGEXP = CREATE_DATE_PLACEHOLDER + "-([0-9]+)"; |
| 75 | + public static final String COUNT_PRECISION_KEY = "CountPrecision"; |
51 | 76 |
|
52 | 77 | private static final Map<String, AtomicInteger> PATTERN_COUNT_MAP = new HashMap<>(); |
53 | 78 |
|
54 | | - private final MCRDOIParser mcrdoiParser; |
| 79 | + private final String dateFormat; |
55 | 80 |
|
56 | | - private final String prefix = MCRConfiguration2.getStringOrThrow("MCR.DOI.Prefix"); |
| 81 | + private final String prefix; |
57 | 82 |
|
58 | | - public MCRCreateDateDOIGenerator() { |
59 | | - super(); |
60 | | - mcrdoiParser = new MCRDOIParser(); |
| 83 | + private final int countPrecision; |
| 84 | + |
| 85 | + private final String countPattern; |
| 86 | + |
| 87 | + public MCRCreateDateDOIGenerator(MCRDOIParser parser, String dateFormat, String prefix, |
| 88 | + int countPrecision) { |
| 89 | + super(parser); |
| 90 | + this.dateFormat = Objects.requireNonNull(dateFormat, "Date format must not be null"); |
| 91 | + this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null"); |
| 92 | + this.countPrecision = countPrecision; |
| 93 | + this.countPattern = getCountPattern(countPrecision); |
61 | 94 | } |
62 | 95 |
|
63 | 96 | @Override |
64 | | - public MCRDigitalObjectIdentifier generate(MCRBase mcrObj, String additional) { |
65 | | - Date createdate = mcrObj.getService().getDate(MCRObjectService.DATE_TYPE_CREATEDATE); |
66 | | - if (createdate != null) { |
67 | | - MCRISO8601Date mcrdate = new MCRISO8601Date(); |
68 | | - mcrdate.setDate(createdate); |
69 | | - String createDate = mcrdate.format(DATE_PATTERN, Locale.ENGLISH); |
70 | | - final int count = getCountForCreateDate(createDate); |
71 | | - Optional<MCRDigitalObjectIdentifier> parse = mcrdoiParser.parse(prefix + "/" + createDate + "-" + count); |
72 | | - return parse.orElseThrow(() -> new MCRException("Error while parsing default doi!")); |
73 | | - } else { |
74 | | - throw new MCRPersistenceException("The object " + mcrObj.getId() + " doesn't have a createdate!"); |
75 | | - } |
| 97 | + protected String buildDOI(MCRBase base, String additional) throws MCRPersistentIdentifierException { |
| 98 | + |
| 99 | + String prefixWithDate = prefix + "/" + formatDate(getCreateDate(base)) + "-"; |
| 100 | + int count = getCount(Pattern.quote(prefixWithDate) + countPattern); |
| 101 | + |
| 102 | + return prefixWithDate + formatCount(count, countPrecision); |
| 103 | + |
76 | 104 | } |
77 | 105 |
|
78 | | - private int getCountForCreateDate(String createDate) { |
79 | | - return getCount(prefix + "/" + DATE_REGEXP.replace(CREATE_DATE_PLACEHOLDER, createDate)); |
| 106 | + private String formatDate(Date date) { |
| 107 | + |
| 108 | + MCRISO8601Date isoDate = new MCRISO8601Date(); |
| 109 | + isoDate.setDate(date); |
| 110 | + |
| 111 | + return isoDate.format(dateFormat, DEFAULT_DATE_LOCALE); |
| 112 | + |
80 | 113 | } |
81 | 114 |
|
82 | 115 | private synchronized int getCount(final String pattern) { |
83 | | - AtomicInteger count = PATTERN_COUNT_MAP.computeIfAbsent(pattern, p -> { |
84 | | - Pattern regExpPattern = Pattern.compile(p); |
85 | | - Predicate<String> matching = regExpPattern.asPredicate(); |
86 | | - |
87 | | - List<MCRPIRegistrationInfo> list = MCRPIManager.getInstance() |
88 | | - .getList(MCRDigitalObjectIdentifier.TYPE, -1, -1); |
89 | | - |
90 | | - Comparator<Integer> integerComparator = Integer::compareTo; |
91 | | - // extract the number of the PI |
92 | | - Optional<Integer> highestNumber = list.stream() |
93 | | - .map(MCRPIRegistrationInfo::getIdentifier) |
94 | | - .filter(matching) |
95 | | - .map(pi -> { |
96 | | - // extract the number of the PI |
97 | | - Matcher matcher = regExpPattern.matcher(pi); |
98 | | - if (matcher.find() && matcher.groupCount() == 1) { |
99 | | - String group = matcher.group(1); |
100 | | - return Integer.parseInt(group, 10); |
101 | | - } else { |
102 | | - return null; |
103 | | - } |
104 | | - }).filter(Objects::nonNull) |
105 | | - .max(integerComparator) |
106 | | - .map(n -> n + 1); |
107 | | - return new AtomicInteger(highestNumber.orElse(0)); |
108 | | - }); |
109 | | - |
110 | | - return count.getAndIncrement(); |
| 116 | + return PATTERN_COUNT_MAP |
| 117 | + .computeIfAbsent(pattern, p -> readCountFromDatabase(MCRDigitalObjectIdentifier.TYPE, p)) |
| 118 | + .getAndIncrement(); |
| 119 | + } |
| 120 | + |
| 121 | + public static class Factory implements Supplier<MCRCreateDateDOIGenerator> { |
| 122 | + |
| 123 | + @MCRProperty(name = DATE_FORMAT_KEY, required = false) |
| 124 | + public String dateFormat; |
| 125 | + |
| 126 | + @MCRProperty(name = PREFIX_KEY, defaultName = "MCR.DOI.Prefix") |
| 127 | + public String prefix; |
| 128 | + |
| 129 | + @MCRProperty(name = COUNT_PRECISION_KEY, required = false) |
| 130 | + public String countPrecision = "-1"; |
| 131 | + |
| 132 | + @Override |
| 133 | + public MCRCreateDateDOIGenerator get() { |
| 134 | + return new MCRCreateDateDOIGenerator(new MCRDOIParser(), getDateFormat(), prefix, |
| 135 | + Integer.parseInt(countPrecision)); |
| 136 | + } |
| 137 | + |
| 138 | + private String getDateFormat() { |
| 139 | + return dateFormat != null ? dateFormat : DEFAULT_DATE_FORMAT; |
| 140 | + } |
| 141 | + |
111 | 142 | } |
112 | 143 |
|
113 | 144 | } |
0 commit comments