1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.mets.iiif;
20
21 import java.net.URI;
22 import java.net.URISyntaxException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.Logger;
35 import org.jdom2.Document;
36 import org.mycore.access.MCRAccessException;
37 import org.mycore.common.MCRConstants;
38 import org.mycore.common.MCRException;
39 import org.mycore.iiif.image.MCRIIIFImageUtil;
40 import org.mycore.iiif.image.impl.MCRIIIFImageImpl;
41 import org.mycore.iiif.image.impl.MCRIIIFImageNotFoundException;
42 import org.mycore.iiif.image.impl.MCRIIIFImageProvidingException;
43 import org.mycore.iiif.image.model.MCRIIIFImageInformation;
44 import org.mycore.iiif.image.model.MCRIIIFImageProfile;
45 import org.mycore.iiif.presentation.model.additional.MCRIIIFAnnotation;
46 import org.mycore.iiif.presentation.model.attributes.MCRDCMIType;
47 import org.mycore.iiif.presentation.model.attributes.MCRIIIFMetadata;
48 import org.mycore.iiif.presentation.model.attributes.MCRIIIFResource;
49 import org.mycore.iiif.presentation.model.attributes.MCRIIIFService;
50 import org.mycore.iiif.presentation.model.attributes.MCRIIIFViewingHint;
51 import org.mycore.iiif.presentation.model.basic.MCRIIIFCanvas;
52 import org.mycore.iiif.presentation.model.basic.MCRIIIFManifest;
53 import org.mycore.iiif.presentation.model.basic.MCRIIIFRange;
54 import org.mycore.iiif.presentation.model.basic.MCRIIIFSequence;
55 import org.mycore.mets.model.Mets;
56 import org.mycore.mets.model.files.File;
57 import org.mycore.mets.model.files.FileGrp;
58 import org.mycore.mets.model.sections.DmdSec;
59 import org.mycore.mets.model.struct.LogicalDiv;
60 import org.mycore.mets.model.struct.LogicalStructMap;
61 import org.mycore.mets.model.struct.MDTYPE;
62 import org.mycore.mets.model.struct.MdWrap;
63 import org.mycore.mets.model.struct.PhysicalDiv;
64 import org.mycore.mets.model.struct.PhysicalStructMap;
65 import org.mycore.mets.model.struct.PhysicalSubDiv;
66
67 public class MCRMetsMods2IIIFConverter {
68
69 private static final Logger LOGGER = LogManager.getLogger();
70
71 protected final Document metsDocument;
72
73 protected final Mets mets;
74
75 protected final String identifier;
76
77 protected final FileGrp imageGrp;
78
79 protected final Map<String, List<String>> logicalIdIdentifiersMap = new HashMap<>();
80
81 protected final Map<String, PhysicalSubDiv> identifierPhysicalMap = new ConcurrentHashMap<>();
82
83 protected final Map<PhysicalSubDiv, String> physicalIdentifierMap = new ConcurrentHashMap<>();
84
85 protected final Map<String, PhysicalSubDiv> idPhysicalMetsMap = new ConcurrentHashMap<>();
86
87 public MCRMetsMods2IIIFConverter(Document metsDocument, String identifier) {
88 this.metsDocument = metsDocument;
89 this.mets = new Mets(metsDocument);
90 this.identifier = identifier;
91
92 FileGrp imageGrp = mets.getFileSec().getFileGroup("MASTER");
93 if (imageGrp == null) {
94 imageGrp = mets.getFileSec().getFileGroup("IVIEW");
95 }
96 if (imageGrp == null) {
97 imageGrp = mets.getFileSec().getFileGroup("MAX");
98 }
99 if (imageGrp == null) {
100 imageGrp = mets.getFileSec().getFileGroup("DEFAULT");
101 }
102 if (imageGrp == null) {
103 throw new MCRException("Could not find a image file group in mets!");
104 }
105 this.imageGrp = imageGrp;
106
107 this.mets.getPhysicalStructMap()
108 .getDivContainer()
109 .getChildren()
110 .parallelStream()
111 .forEach(physicalSubDiv -> {
112 String id = getIIIFIdentifier(physicalSubDiv);
113 identifierPhysicalMap.put(id, physicalSubDiv);
114 physicalIdentifierMap.put(physicalSubDiv, id);
115 idPhysicalMetsMap.put(physicalSubDiv.getId(), physicalSubDiv);
116 });
117
118 mets.getStructLink().getSmLinks().forEach(smLink -> {
119 String logicalId = smLink.getFrom();
120
121 List<String> identifiers;
122 if (this.logicalIdIdentifiersMap.containsKey(logicalId)) {
123 identifiers = this.logicalIdIdentifiersMap.get(logicalId);
124 } else {
125 identifiers = new ArrayList<>();
126 this.logicalIdIdentifiersMap.put(logicalId, identifiers);
127 }
128
129 identifiers.add(physicalIdentifierMap.get(idPhysicalMetsMap.get(smLink.getTo())));
130 });
131
132 }
133
134 public MCRIIIFManifest convert() {
135 MCRIIIFManifest manifest = new MCRIIIFManifest();
136
137
138 LogicalStructMap logicalStructMap = (LogicalStructMap) mets.getStructMap("LOGICAL");
139 LogicalDiv divContainer = logicalStructMap.getDivContainer();
140 List<MCRIIIFMetadata> metadata = extractMedataFromLogicalDiv(mets, divContainer);
141 manifest.metadata = metadata;
142 manifest.setId(this.identifier);
143
144 PhysicalStructMap physicalStructMap = (PhysicalStructMap) mets.getStructMap("PHYSICAL");
145 PhysicalDiv physicalDivContainer = physicalStructMap.getDivContainer();
146 String id = physicalDivContainer.getId();
147
148 MCRIIIFSequence sequence = new MCRIIIFSequence(id);
149
150 List<PhysicalSubDiv> children = physicalDivContainer.getChildren();
151 MCRIIIFImageImpl imageImpl = MCRIIIFImageImpl.getInstance(getImageImplName());
152 MCRIIIFImageProfile profile = imageImpl.getProfile();
153 profile.setId(MCRIIIFImageUtil.getProfileLink(imageImpl));
154 sequence.canvases = children.stream().map(physicalSubDiv -> {
155 String order = physicalSubDiv.asElement().getAttributeValue("ORDER");
156 String orderLabel = physicalSubDiv.getOrderLabel();
157 String contentIds = physicalSubDiv.getContentIds();
158 String label = Stream.of(order, orderLabel, contentIds)
159 .filter(o -> o != null && !o.isEmpty())
160 .collect(Collectors.joining(" - "));
161 label = ("".equals(label)) ? physicalSubDiv.getId() : label;
162
163 String identifier = this.physicalIdentifierMap.get(physicalSubDiv);
164 try {
165 MCRIIIFImageInformation information = imageImpl.getInformation(new URI(identifier).getPath());
166 MCRIIIFCanvas canvas = new MCRIIIFCanvas(identifier, label, information.width, information.height);
167
168 MCRIIIFAnnotation annotation = new MCRIIIFAnnotation(identifier, canvas);
169 canvas.images.add(annotation);
170
171 MCRIIIFResource resource = new MCRIIIFResource(information.getId(), MCRDCMIType.Image);
172 resource.setWidth(information.width);
173 resource.setHeight(information.height);
174
175 MCRIIIFService service = new MCRIIIFService(information.getId(), profile.getContext());
176 service.profile = MCRIIIFImageProfile.IIIF_PROFILE_2_0;
177 resource.setService(service);
178
179 annotation.setResource(resource);
180
181 return canvas;
182 } catch (MCRIIIFImageNotFoundException | MCRIIIFImageProvidingException | URISyntaxException e) {
183 throw new MCRException("Error while providing ImageInfo for " + identifier, e);
184 } catch (MCRAccessException e) {
185 LOGGER.warn("User has no access to {}", identifier);
186 return null;
187 }
188 }).filter(Objects::nonNull)
189 .collect(Collectors.toList());
190
191 manifest.sequences.add(sequence);
192
193 List<MCRIIIFRange> complete = new ArrayList<>();
194 processDivContainer(complete, divContainer);
195 manifest.structures.addAll(complete);
196
197 manifest.setLabel(
198 metadata.stream().filter(m -> m.getLabel().equals("title")).findFirst().get().getStringValue().get());
199
200 return manifest;
201 }
202
203 protected void processDivContainer(List<MCRIIIFRange> complete, LogicalDiv divContainer) {
204 MCRIIIFRange range = new MCRIIIFRange(divContainer.getId());
205 if (divContainer.getParent() == null) {
206 range.setViewingHint(MCRIIIFViewingHint.top);
207 }
208 complete.add(range);
209 range.setLabel((divContainer.getLabel() != null) ? divContainer.getLabel() : divContainer.getType());
210
211 range.canvases.addAll(this.logicalIdIdentifiersMap.getOrDefault(divContainer.getId(), Collections.emptyList()));
212
213 divContainer.getChildren()
214 .forEach(div -> {
215 processDivContainer(complete, div);
216 range.ranges.add(div.getId());
217 });
218
219 range.metadata = extractMedataFromLogicalDiv(mets, divContainer);
220
221 }
222
223 protected String getImageImplName() {
224 return "Iview";
225 }
226
227 protected String getIIIFIdentifier(PhysicalSubDiv subDiv) {
228 File file = subDiv.getChildren()
229 .stream()
230 .map(fptr -> imageGrp.getFileById(fptr.getFileId()))
231 .filter(Objects::nonNull)
232 .findAny().get();
233
234 String cleanHref = file.getFLocat().getHref();
235 cleanHref = cleanHref.substring(cleanHref.indexOf(this.identifier));
236
237 return cleanHref;
238 }
239
240 protected List<MCRIIIFMetadata> extractMedataFromLogicalDiv(Mets mets, LogicalDiv divContainer) {
241 String dmdId = divContainer.getDmdId();
242 if (dmdId != null && !dmdId.isEmpty()) {
243 DmdSec dmdSec = mets.getDmdSecById(dmdId);
244 if (dmdSec != null) {
245 MdWrap mdWrap = dmdSec.getMdWrap();
246 MDTYPE mdtype = mdWrap.getMdtype();
247 MCRMetsIIIFModsMetadataExtractor extractor;
248 switch (mdtype) {
249 case MODS:
250 extractor = new MCRMetsIIIFModsMetadataExtractor();
251 break;
252 default:
253 LOGGER.info("No extractor found for mdType: {}", mdtype);
254 return Collections.emptyList();
255 }
256
257 return extractor
258 .extractModsMetadata(mdWrap.asElement().getChild("xmlData", MCRConstants.METS_NAMESPACE));
259 }
260 }
261
262 return Collections.emptyList();
263 }
264
265 }