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