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.datamodel.metadata;
20  
21  import java.nio.file.Files;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Objects;
27  
28  import org.apache.logging.log4j.LogManager;
29  import org.apache.logging.log4j.Logger;
30  import org.jdom2.Element;
31  import org.mycore.common.MCRException;
32  import org.mycore.common.MCRPersistenceException;
33  import org.mycore.datamodel.niofs.MCRPath;
34  
35  /**
36   * This class implements all methode for handling one derivate data.
37   * 
38   * @author Jens Kupferschmidt
39   * @version $Revision$ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06. Feb
40   *          2008) $
41   */
42  public class MCRObjectDerivate {
43  
44      private static final Logger LOGGER = LogManager.getLogger();
45  
46      // derivate data
47      private MCRMetaLinkID linkmeta;
48  
49      private final ArrayList<MCRMetaLink> externals;
50  
51      private MCRMetaIFS internals;
52  
53      private final ArrayList<MCRMetaLangText> titles;
54  
55      private final ArrayList<MCRMetaClassification> classifications;
56  
57      private String derivateURN;
58  
59      private List<MCRFileMetadata> files;
60  
61      private MCRObjectID derivateID;
62  
63      /**
64       * This is the constructor of the MCRObjectDerivate class. All data are set
65       * to null.
66       */
67      public MCRObjectDerivate(MCRObjectID derivateID) {
68          linkmeta = null;
69          externals = new ArrayList<>();
70          internals = null;
71          titles = new ArrayList<>();
72          classifications = new ArrayList<>();
73          files = Collections.emptyList();
74          this.derivateID = derivateID;
75      }
76  
77      public MCRObjectDerivate(MCRObjectID derivateID, Element derivate) {
78          this(derivateID);
79          setFromDOM(derivate);
80      }
81  
82      /**
83       * This methode read the XML input stream part from a DOM part for the
84       * structure data of the document.
85       * 
86       * @param derivate
87       *            a list of relevant DOM elements for the derivate
88       */
89      private void setFromDOM(Element derivate) {
90          // Link to Metadata part
91          Element linkmetaElement = derivate.getChild("linkmetas").getChild("linkmeta");
92          MCRMetaLinkID link = new MCRMetaLinkID();
93          link.setFromDOM(linkmetaElement);
94          linkmeta = link;
95  
96          // External part
97          Element externalsElement = derivate.getChild("externals");
98          externals.clear();
99          if (externalsElement != null) {
100             List<Element> externalList = externalsElement.getChildren();
101             for (Element externalElement : externalList) {
102                 MCRMetaLink eLink = new MCRMetaLink();
103                 eLink.setFromDOM(externalElement);
104                 externals.add(eLink);
105             }
106         }
107 
108         // Internal part
109         Element internalsElement = derivate.getChild("internals");
110         if (internalsElement != null) {
111             Element internalElement = internalsElement.getChild("internal");
112             if (internalElement != null) {
113                 internals = new MCRMetaIFS();
114                 internals.setFromDOM(internalElement);
115             }
116         }
117 
118         // Title part
119         Element titlesElement = derivate.getChild("titles");
120         titles.clear();
121         if (titlesElement != null) {
122             List<Element> titleList = titlesElement.getChildren();
123             for (Element titleElement : titleList) {
124                 MCRMetaLangText text = new MCRMetaLangText();
125                 text.setFromDOM(titleElement);
126                 if (text.isValid()) {
127                     titles.add(text);
128                 }
129             }
130         }
131 
132         // Classification part
133         Element classificationElement = derivate.getChild("classifications");
134         classifications.clear();
135         if (classificationElement != null) {
136             final List<Element> classificationList = classificationElement.getChildren();
137             classificationList.stream().map((classElement) -> {
138                 MCRMetaClassification clazzObject = new MCRMetaClassification();
139                 clazzObject.setFromDOM(classElement);
140                 return clazzObject;
141             }).forEach(classifications::add);
142         }
143 
144         // fileset part
145         Element filesetElements = derivate.getChild("fileset");
146         if (filesetElements != null) {
147             String mainURN = filesetElements.getAttributeValue("urn");
148             if (mainURN != null) {
149                 this.derivateURN = mainURN;
150             }
151             List<Element> filesInList = filesetElements.getChildren();
152             if (!filesInList.isEmpty()) {
153                 files = new ArrayList<>(filesInList.size());
154                 for (Element file : filesInList) {
155                     files.add(new MCRFileMetadata(file));
156                 }
157             }
158         }
159     }
160 
161     /**
162      * returns link to the MCRObject.
163      * 
164      * @return a metadata link as MCRMetaLinkID
165      */
166     public MCRMetaLinkID getMetaLink() {
167         return linkmeta;
168     }
169 
170     /**
171      * This method set the metadata link
172      * 
173      * @param link
174      *            the MCRMetaLinkID object
175      */
176     public final void setLinkMeta(MCRMetaLinkID link) {
177         linkmeta = link;
178     }
179 
180     /**
181      * This method return the size of the external array.
182      */
183     public final int getExternalSize() {
184         return externals.size();
185     }
186 
187     /**
188      * This method get a single link from the external list as a MCRMetaLink.
189      * 
190      * @exception IndexOutOfBoundsException
191      *                throw this exception, if the index is false
192      * @return a external link as MCRMetaLink
193      */
194     public final MCRMetaLink getExternal(int index) throws IndexOutOfBoundsException {
195         if ((index < 0) || (index > externals.size())) {
196             throw new IndexOutOfBoundsException("Index error in getExternal(" + index + ").");
197         }
198 
199         return externals.get(index);
200     }
201 
202     /**
203      * This method return the size of the title array.
204      */
205     public final int getTitleSize() {
206         return titles.size();
207     }
208 
209     /**
210      * This method get a single text from the titles list as a MCRMetaLangText.
211      * 
212      * @exception IndexOutOfBoundsException
213      *                throw this exception, if the index is false
214      * @return a title text as MCRMetaLangText
215      */
216     public final MCRMetaLangText getTitle(int index) throws IndexOutOfBoundsException {
217         if ((index < 0) || (index > titles.size())) {
218             throw new IndexOutOfBoundsException("Index error in getTitle(" + index + ").");
219         }
220 
221         return titles.get(index);
222     }
223 
224     /**
225      * This method get a single data from the internal list as a MCRMetaIFS.
226      * 
227      * @return a internal data as MCRMetaIFS
228      */
229     public final MCRMetaIFS getInternals() {
230         return internals;
231     }
232 
233     /**
234      * @param file the file to add
235      * @param urn the urn of the file, if already known, if not provide null
236      * 
237      * @throws NullPointerException if first argument is null
238      */
239     public MCRFileMetadata getOrCreateFileMetadata(MCRPath file, String urn) {
240         return getOrCreateFileMetadata(file, urn, null);
241     }
242 
243     public MCRFileMetadata getOrCreateFileMetadata(MCRPath file, String urn, String handle) {
244         Objects.requireNonNull(file, "File may not be null");
245         String path = "/" + file.subpathComplete();
246         return getOrCreateFileMetadata(path, urn, handle);
247     }
248 
249     /**
250      * @param path
251      * @param urn
252      * @param handle
253      * @return
254      */
255     private MCRFileMetadata getOrCreateFileMetadata(String path, String urn, String handle) {
256         if (path == null) {
257             throw new NullPointerException("path may not be null");
258         }
259         int fileCount = files.size();
260         for (int i = 0; i < fileCount; i++) {
261             MCRFileMetadata fileMetadata = files.get(i);
262             int compare = fileMetadata.getName().compareTo(path);
263             if (compare == 0) {
264                 return fileMetadata;
265             } else if (compare > 0) {
266                 //we need to create entry here
267                 MCRFileMetadata newFileMetadata = createFileMetadata(path, urn, handle);
268                 files.add(i, newFileMetadata);
269                 return newFileMetadata;
270             }
271         }
272         //add path to end of list;
273         if (files.isEmpty()) {
274             files = new ArrayList<>();
275         }
276         MCRFileMetadata newFileMetadata = createFileMetadata(path, urn, handle);
277         files.add(newFileMetadata);
278         return newFileMetadata;
279     }
280 
281     public final MCRFileMetadata getOrCreateFileMetadata(String path) {
282         return getOrCreateFileMetadata(MCRPath.getPath(derivateID.toString(), path), null, null);
283     }
284 
285     public MCRFileMetadata getOrCreateFileMetadata(MCRPath file) {
286         return getOrCreateFileMetadata(file, null, null);
287     }
288 
289     /**
290      * @param path
291      * @param urn
292      * @param handle
293      * @return
294      */
295     private MCRFileMetadata createFileMetadata(String path, String urn, String handle) {
296         MCRPath mcrFile = MCRPath.getPath(derivateID.toString(), path);
297         if (!Files.exists(mcrFile)) {
298             throw new MCRPersistenceException("File does not exist: " + mcrFile);
299         }
300         return new MCRFileMetadata(path, urn, handle, null);
301     }
302 
303     public List<MCRFileMetadata> getFileMetadata() {
304         return Collections.unmodifiableList(files);
305     }
306 
307     /**
308      * Removes file metadata (urn information) from the {@link MCRObjectDerivate}
309      */
310     public void removeFileMetadata() {
311         this.files = Collections.emptyList();
312         this.derivateURN = null;
313     }
314 
315     /**
316      * Deletes file metadata of file idendified by absolute path.
317      * @param path absolute path of this node starting with a '/'
318      * @return true if metadata was deleted and false if file has no metadata.
319      */
320     public boolean deleteFileMetaData(String path) {
321         Iterator<MCRFileMetadata> it = files.iterator();
322         while (it.hasNext()) {
323             MCRFileMetadata metadata = it.next();
324             if (metadata.getName().equals(path)) {
325                 it.remove();
326                 return true;
327             }
328         }
329         return false;
330     }
331 
332     /**
333      * This method set the metadata internals (the IFS data)
334      * 
335      * @param ifs
336      *            the MCRMetaIFS object
337      */
338     public final void setInternals(MCRMetaIFS ifs) {
339         if (ifs == null) {
340             return;
341         }
342 
343         internals = ifs;
344     }
345 
346     /**
347      * This methode create a XML stream for all derivate data.
348      * 
349      * @exception MCRException
350      *                if the content of this class is not valid
351      * @return a JDOM Element with the XML data of the structure data part
352      */
353     public final Element createXML() throws MCRException {
354         try {
355             validate();
356         } catch (MCRException exc) {
357             throw new MCRException("The content is not valid.", exc);
358         }
359         Element elm = new Element("derivate");
360 
361         Element linkmetas = new Element("linkmetas");
362         linkmetas.setAttribute("class", "MCRMetaLinkID");
363         linkmetas.setAttribute("heritable", "false");
364         linkmetas.addContent(linkmeta.createXML());
365         elm.addContent(linkmetas);
366 
367         if (externals.size() != 0) {
368             Element extEl = new Element("externals");
369             extEl.setAttribute("class", "MCRMetaLink");
370             extEl.setAttribute("heritable", "false");
371             for (MCRMetaLink external : externals) {
372                 extEl.addContent(external.createXML());
373             }
374             elm.addContent(extEl);
375         }
376 
377         if (internals != null) {
378             Element intEl = new Element("internals");
379             intEl.setAttribute("class", "MCRMetaIFS");
380             intEl.setAttribute("heritable", "false");
381             intEl.addContent(internals.createXML());
382             elm.addContent(intEl);
383         }
384 
385         if (titles.size() != 0) {
386             Element titEl = new Element("titles");
387             titEl.setAttribute("class", "MCRMetaLangText");
388             titEl.setAttribute("heritable", "false");
389             titles.stream()
390                 .map(MCRMetaLangText::createXML)
391                 .forEach(titEl::addContent);
392             elm.addContent(titEl);
393         }
394 
395         if (classifications.size() > 0) {
396             Element clazzElement = new Element("classifications");
397             clazzElement.setAttribute("class", "MCRMetaClassification");
398             clazzElement.setAttribute("heritable", "false");
399 
400             classifications.stream()
401                 .map(MCRMetaClassification::createXML)
402                 .forEach(clazzElement::addContent);
403             elm.addContent(clazzElement);
404         }
405 
406         if (this.derivateURN != null || !files.isEmpty()) {
407             Element fileset = new Element("fileset");
408 
409             if (this.derivateURN != null) {
410                 fileset.setAttribute("urn", this.derivateURN);
411             }
412             Collections.sort(files);
413             for (MCRFileMetadata file : files) {
414                 fileset.addContent(file.createXML());
415             }
416             elm.addContent(fileset);
417         }
418 
419         return elm;
420     }
421 
422     /**
423      * This method check the validation of the content of this class. The method
424      * returns <em>true</em> if <br>
425      * <ul>
426      * <li>the linkmeta exist and the XLink type of linkmeta is not "arc"</li>
427      * <li>no information in the external AND internal tags</li>
428      * </ul>
429      * 
430      * @return a boolean value
431      */
432     public final boolean isValid() {
433         try {
434             validate();
435             return true;
436         } catch (MCRException exc) {
437             LOGGER.warn("The <derivate> part of the mycorederivate '{}' is invalid.", derivateID, exc);
438         }
439         return false;
440     }
441 
442     /**
443      * Validates this MCRObjectDerivate. This method throws an exception if:
444      *  <ul>
445      *  <li>the linkmeta is null</li>
446      *  <li>the linkmeta xlink:type is not 'locator'</li>
447      *  <li>the internals and the externals are empty</li>
448      *  </ul>
449      * 
450      * @throws MCRException the MCRObjectDerivate is invalid
451      */
452     public void validate() throws MCRException {
453         if (linkmeta == null) {
454             throw new MCRException("linkmeta == null");
455         }
456         if (!linkmeta.getXLinkType().equals("locator")) {
457             throw new MCRException("linkmeta type != locator");
458         }
459         if ((internals == null) && (externals.size() == 0)) {
460             throw new MCRException("(internals == null) && (externals.size() == 0)");
461         }
462     }
463 
464     public void setURN(String urn) {
465         derivateURN = urn;
466     }
467 
468     public String getURN() {
469         return derivateURN;
470     }
471 
472     void setDerivateID(MCRObjectID id) {
473         this.derivateID = id;
474     }
475 
476     public ArrayList<MCRMetaClassification> getClassifications() {
477         return classifications;
478     }
479 
480     public ArrayList<MCRMetaLangText> getTitles() {
481         return titles;
482     }
483 }