View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
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         // root chapter ^= manifest metadata
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 }