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.model;
20  
21  import java.io.IOException;
22  import java.net.URISyntaxException;
23  import java.nio.file.DirectoryStream;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.nio.file.attribute.BasicFileAttributes;
27  import java.util.Arrays;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.SortedMap;
33  import java.util.TreeMap;
34  
35  import org.apache.logging.log4j.LogManager;
36  import org.apache.logging.log4j.Logger;
37  import org.mycore.common.MCRException;
38  import org.mycore.common.config.MCRConfiguration2;
39  import org.mycore.common.config.MCRConfigurationException;
40  import org.mycore.common.xml.MCRXMLFunctions;
41  import org.mycore.datamodel.metadata.MCRDerivate;
42  import org.mycore.datamodel.metadata.MCRMetadataManager;
43  import org.mycore.datamodel.metadata.MCRObjectID;
44  import org.mycore.datamodel.niofs.MCRContentTypes;
45  import org.mycore.datamodel.niofs.MCRPath;
46  import org.mycore.mets.model.files.FLocat;
47  import org.mycore.mets.model.files.File;
48  import org.mycore.mets.model.files.FileGrp;
49  import org.mycore.mets.model.files.FileSec;
50  import org.mycore.mets.model.sections.AmdSec;
51  import org.mycore.mets.model.sections.DmdSec;
52  import org.mycore.mets.model.struct.Fptr;
53  import org.mycore.mets.model.struct.LOCTYPE;
54  import org.mycore.mets.model.struct.LogicalDiv;
55  import org.mycore.mets.model.struct.LogicalStructMap;
56  import org.mycore.mets.model.struct.PhysicalDiv;
57  import org.mycore.mets.model.struct.PhysicalStructMap;
58  import org.mycore.mets.model.struct.PhysicalSubDiv;
59  import org.mycore.mets.model.struct.SmLink;
60  import org.mycore.mets.model.struct.StructLink;
61  import org.mycore.mets.tools.MCRMetsSave;
62  import org.mycore.services.i18n.MCRTranslation;
63  
64  /**
65   * @author Thomas Scheffler (yagee)
66   * @author Matthias Eichner
67   * @author Sebastian Hofmann
68   * @author Sebastian Röher (basti890)
69   */
70  public class MCRMETSDefaultGenerator extends MCRMETSAbstractGenerator {
71  
72      private static final Logger LOGGER = LogManager.getLogger(MCRMETSGenerator.class);
73  
74      private static final List<String> EXCLUDED_ROOT_FOLDERS = Arrays.asList("alto", "tei");
75  
76      private HashMap<String, String> hrefIdMap = new HashMap<>();
77  
78      @Override
79      public Mets generate() throws MCRException {
80          try {
81              Mets mets = createMets();
82              MCRDerivate owner = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(getOwner()));
83  
84              mets.getLogicalStructMap().getDivContainer().setLabel(
85                  MCRTranslation.exists("MCR.Mets.LogicalStructMap.Default.Label")
86                      ? MCRTranslation.translate("MCR.Mets.LogicalStructMap.Default.Label")
87                      : owner.getId().toString());
88  
89              Map<String, String> urnFileMap = owner.getUrnMap();
90              if (urnFileMap.size() > 0) {
91                  try {
92                      MCRMetsSave.updateURNsInMetsDocument(mets, urnFileMap);
93                  } catch (Exception e) {
94                      LOGGER.error("error while adding urn´s to new Mets file", e);
95                  }
96              }
97              return mets;
98          } catch (Exception ioExc) {
99              throw new MCRException("Unable to create mets.xml of " + getOwner(), ioExc);
100         }
101     }
102 
103     private Mets createMets() throws IOException {
104         Mets mets = new Mets();
105         String owner = getOwner();
106 
107         // add dmdsec
108         DmdSec dmdSec = new DmdSec("dmd_" + owner);
109         // add amdsec
110         AmdSec amdSec = new AmdSec("amd_" + owner);
111         // file sec
112         FileSec fileSec = new FileSec();
113         //for (MCRMetsFileUse fileUse : MCRMetsFileUse.values()) {
114         //    FileGrp fileGrp = new FileGrp(fileUse.toString());
115         //    fileSec.addFileGrp(fileGrp);
116         //}
117 
118         // physical structure
119         PhysicalStructMap physicalStructMap = new PhysicalStructMap();
120         PhysicalDiv physicalDiv = new PhysicalDiv("phys_" + owner, "physSequence");
121         physicalStructMap.setDivContainer(physicalDiv);
122 
123         // logical structure
124         MCRILogicalStructMapTypeProvider typeProvider = getTypeProvider();
125         LogicalStructMap logicalStructMap = new LogicalStructMap();
126 
127         LogicalDiv logicalDiv = new LogicalDiv("log_" + owner, typeProvider.getType(MCRObjectID.getInstance(owner)),
128             owner, amdSec.getId(), dmdSec.getId());
129         logicalDiv.setDmdId(dmdSec.getId());
130         logicalStructMap.setDivContainer(logicalDiv);
131         // struct Link
132         StructLink structLink = new StructLink();
133 
134         // create internal structure
135         structureMets(getDerivatePath(), getIgnorePaths(), fileSec, physicalDiv, logicalDiv, structLink, 0);
136         hrefIdMap.clear();
137 
138         // add to mets
139         mets.addDmdSec(dmdSec);
140         mets.addAmdSec(amdSec);
141         mets.setFileSec(fileSec);
142         mets.addStructMap(physicalStructMap);
143         mets.addStructMap(logicalStructMap);
144         mets.setStructLink(structLink);
145 
146         return mets;
147     }
148 
149     private void structureMets(MCRPath dir, Set<MCRPath> ignoreNodes, FileSec fileSec, PhysicalDiv physicalDiv,
150         LogicalDiv logicalDiv, StructLink structLink, int logOrder) throws IOException {
151         int lOrder = logOrder;
152         SortedMap<MCRPath, BasicFileAttributes> files = new TreeMap<>();
153         SortedMap<MCRPath, BasicFileAttributes> directories = new TreeMap<>();
154 
155         fillFileMap(ignoreNodes, files, directories, dir);
156 
157         for (Map.Entry<MCRPath, BasicFileAttributes> file : files.entrySet()) {
158             createStructure(dir, fileSec, physicalDiv, logicalDiv, structLink, file);
159         }
160         for (Map.Entry<MCRPath, BasicFileAttributes> directory : directories.entrySet()) {
161             String dirName = directory.getKey().getFileName().toString();
162             if (isInExcludedRootFolder(directory.getKey())) {
163                 structureMets(directory.getKey(), ignoreNodes, fileSec, physicalDiv, logicalDiv, structLink, lOrder);
164             } else {
165                 LogicalDiv section = new LogicalDiv("log_" + ++lOrder, "section", dirName);
166                 logicalDiv.add(section);
167                 structureMets(directory.getKey(), ignoreNodes, fileSec, physicalDiv, section, structLink, lOrder);
168             }
169         }
170     }
171 
172     private void createStructure(MCRPath dir, FileSec fileSec, PhysicalDiv physicalDiv, LogicalDiv logicalDiv,
173         StructLink structLink, Map.Entry<MCRPath, BasicFileAttributes> file) throws IOException {
174         String baseID = MCRMetsSave.getFileBase(file.getKey());
175         final String physicalID = "phys_" + baseID;
176         final String href;
177         String path = file.getKey().getOwnerRelativePath().substring(1);
178         try {
179             href = MCRXMLFunctions.encodeURIPath(path, true);
180         } catch (URISyntaxException uriSyntaxException) {
181             LOGGER.error("invalid href {}", path, uriSyntaxException);
182             return;
183         }
184         int beginIndex = href.lastIndexOf("/") == -1 ? 0 : href.lastIndexOf("/") + 1;
185         int endIndex = (href.lastIndexOf(".") == -1 || href.lastIndexOf(".") <= beginIndex) ? href.length()
186             : href.lastIndexOf(".");
187         String fileName = href.substring(beginIndex, endIndex);
188         LOGGER.debug("Created fileName: {}", fileName);
189 
190         if (!(hrefIdMap.containsKey(fileName) || hrefIdMap.containsValue(baseID)
191             && isInExcludedRootFolder(dir))) {
192             hrefIdMap.put(fileName, baseID);
193         }
194 
195         //files
196         String fileUse = MCRMetsModelHelper.getUseForHref(href)
197             .orElseThrow(() -> new MCRConfigurationException("Could not create METS!"));
198         String fileID = fileUse.replace('.', '_') + "_" + baseID;
199         sortFileToGrp(fileSec, file, fileID, href, fileUse);
200 
201         // physical
202         buildPhysDivs(dir, physicalDiv, fileID, physicalID, fileName);
203 
204         // struct link
205         if (!isInExcludedRootFolder(dir)) {
206             SmLink smLink = new SmLink(logicalDiv.getId(), physicalID);
207             structLink.addSmLink(smLink);
208         }
209     }
210 
211     private void fillFileMap(Set<MCRPath> ignoreNodes, SortedMap<MCRPath, BasicFileAttributes> files,
212         SortedMap<MCRPath, BasicFileAttributes> directories, Path dir) throws IOException {
213         try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dir)) {
214             for (Path child : dirStream) {
215                 MCRPath path = MCRPath.toMCRPath(child);
216                 if (ignoreNodes.contains(path)) {
217                     continue;
218                 }
219                 BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
220                 if (attrs.isDirectory()) {
221                     directories.put(path, attrs);
222                 } else {
223                     files.put(path, attrs);
224                 }
225             }
226         }
227     }
228 
229     private void buildPhysDivs(MCRPath dir, PhysicalDiv physicalDiv, String fileID, final String physicalID,
230         String fileName) {
231         if (!fileName.isEmpty() && hrefIdMap.containsKey(fileName) && isInExcludedRootFolder(dir)) {
232             for (PhysicalSubDiv physSubDiv : physicalDiv.getChildren()) {
233                 if (physSubDiv.getId().contains(hrefIdMap.get(fileName))) {
234                     physSubDiv.add(new Fptr(fileID));
235                 }
236             }
237         } else {
238             PhysicalSubDiv pyhsicalPage = new PhysicalSubDiv(physicalID, "page");
239             Fptr fptr = new Fptr(fileID);
240             pyhsicalPage.add(fptr);
241             physicalDiv.add(pyhsicalPage);
242         }
243     }
244 
245     private void sortFileToGrp(FileSec fileSec, Map.Entry<MCRPath, BasicFileAttributes> file, String fileID,
246         final String href, String fileUse) throws IOException {
247         // file
248         File metsFile = new File(fileID, MCRContentTypes.probeContentType(file.getKey()));
249         FLocat fLocat = new FLocat(LOCTYPE.URL, href);
250         metsFile.setFLocat(fLocat);
251 
252         this.createOrGetGroup(fileSec, fileUse).addFile(metsFile);
253     }
254 
255     private FileGrp createOrGetGroup(FileSec fileSec, String fileUse) {
256         FileGrp fileGroup = fileSec.getFileGroup(fileUse);
257         if (fileGroup == null) {
258             fileGroup = new FileGrp(fileUse);
259             fileSec.addFileGrp(fileGroup);
260         }
261         return fileGroup;
262     }
263 
264     /**
265      * Checks if a root directory should be included in mets.xml
266      *
267      * @param directory the directory to check
268      * @return true if the directory should be excluded
269      */
270     private boolean isInExcludedRootFolder(MCRPath directory) {
271         for (String excludedRoot : EXCLUDED_ROOT_FOLDERS) {
272             String path = directory.toString().substring(directory.toString().indexOf(":/") + 2);
273             if (path.startsWith(excludedRoot)) {
274                 return true;
275             }
276         }
277         return false;
278     }
279 
280     private MCRILogicalStructMapTypeProvider getTypeProvider() {
281         try {
282             return MCRConfiguration2.<MCRDefaultLogicalStructMapTypeProvider>getClass(
283                 "MCR.Component.MetsMods.LogicalStructMapTypeProvider")
284                 .orElse(MCRDefaultLogicalStructMapTypeProvider.class).getDeclaredConstructor().newInstance();
285         } catch (Exception e) {
286             LOGGER.warn("Could not load class", e);
287             return new MCRDefaultLogicalStructMapTypeProvider();
288         }
289     }
290 
291 }