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.common;
20  
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.LinkedList;
26  import java.util.Map;
27  import java.util.Optional;
28  import java.util.stream.Collectors;
29  import java.util.stream.Stream;
30  
31  import org.apache.logging.log4j.LogManager;
32  import org.apache.logging.log4j.Logger;
33  import org.mycore.common.MCRUtils;
34  import org.mycore.common.config.MCRConfiguration2;
35  import org.mycore.datamodel.classifications2.MCRCategLinkReference;
36  import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
37  import org.mycore.datamodel.classifications2.MCRCategoryID;
38  import org.mycore.datamodel.metadata.MCRDerivate;
39  import org.mycore.datamodel.metadata.MCRMetaClassification;
40  import org.mycore.datamodel.metadata.MCRMetaDerivateLink;
41  import org.mycore.datamodel.metadata.MCRMetaElement;
42  import org.mycore.datamodel.metadata.MCRMetaLink;
43  import org.mycore.datamodel.metadata.MCRMetaLinkID;
44  import org.mycore.datamodel.metadata.MCRMetadataManager;
45  import org.mycore.datamodel.metadata.MCRObject;
46  import org.mycore.datamodel.metadata.MCRObjectID;
47  import org.mycore.datamodel.metadata.MCRObjectMetadata;
48  import org.mycore.datamodel.metadata.MCRObjectStructure;
49  
50  /**
51   * This class manage all accesses to the link table database. This database
52   * holds all informations about links between MCRObjects/MCRClassifications.
53   * 
54   * @author Jens Kupferschmidt
55   * @version $Revision$ $Date$
56   */
57  public class MCRLinkTableManager {
58      /** The list of entry types */
59      public static final String ENTRY_TYPE_CHILD = "child";
60  
61      public static final String ENTRY_TYPE_DERIVATE = "derivate";
62  
63      public static final String ENTRY_TYPE_DERIVATE_LINK = "derivate_link";
64  
65      public static final String ENTRY_TYPE_PARENT = "parent";
66  
67      public static final String ENTRY_TYPE_REFERENCE = "reference";
68  
69      /** The link table manager singleton */
70      protected static MCRLinkTableManager singleton;
71  
72      // logger
73      static Logger LOGGER = LogManager.getLogger();
74  
75      private MCRLinkTableInterface linkTableInstance = null;
76  
77      /**
78       * Returns the link table manager singleton.
79       * 
80       * @return Returns a MCRLinkTableManager instance.
81       */
82      public static synchronized MCRLinkTableManager instance() {
83          if (singleton == null) {
84              singleton = new MCRLinkTableManager();
85          }
86  
87          return singleton;
88      }
89  
90      /**
91       * The constructor of this class.
92       */
93      protected MCRLinkTableManager() {
94          // Load the persistence class
95          linkTableInstance = MCRConfiguration2
96              .getOrThrow("MCR.Persistence.LinkTable.Store.Class", MCRConfiguration2::instantiateClass);
97      }
98  
99      /**
100      * The method add a reference link pair.
101      * 
102      * @param from
103      *            the source of the reference as MCRObjectID
104      * @param to
105      *            the target of the reference as MCRObjectID
106      * @param type
107      *            the type of the reference as String
108      * @param attr
109      *            the optional attribute of the reference as String
110      */
111     public void addReferenceLink(MCRObjectID from, MCRObjectID to, String type, String attr) {
112         addReferenceLink(from.toString(), to.toString(), type, attr);
113     }
114 
115     /**
116      * The method add a reference link pair.
117      * 
118      * @param from
119      *            the source of the reference as String
120      * @param to
121      *            the target of the reference as String
122      * @param type
123      *            the type of the reference as String
124      * @param attr
125      *            the optional attribute of the reference as String
126      */
127     public void addReferenceLink(String from, String to, String type, String attr) {
128         from = MCRUtils.filterTrimmedNotEmpty(from).orElse(null);
129         if (from == null) {
130             LOGGER.warn("The from value of a reference link is false, the link was not added to the link table");
131             return;
132         }
133 
134         to = MCRUtils.filterTrimmedNotEmpty(to).orElse(null);
135         if (to == null) {
136             LOGGER.warn("The to value of a reference link is false, the link was not added to the link table");
137             return;
138         }
139 
140         type = MCRUtils.filterTrimmedNotEmpty(type).orElse(null);
141         if (type == null) {
142             LOGGER.warn("The type value of a reference link is false, the link was not added to the link table");
143             return;
144         }
145 
146         attr = MCRUtils.filterTrimmedNotEmpty(attr).orElse("");
147 
148         LOGGER.debug("Link in table {} add for {}<-->{} with {} and {}", type, from, to, type, attr);
149 
150         try {
151             linkTableInstance.create(from, to, type, attr);
152         } catch (Exception e) {
153             LOGGER.warn("An error occured while adding a dataset from the reference link table, adding not succesful.",
154                 e);
155         }
156     }
157 
158     /**
159      * The method delete a reference link.
160      * 
161      * @param from
162      *            the source of the reference as MCRObjectID
163      */
164     public void deleteReferenceLink(MCRObjectID from) {
165         deleteReferenceLink(from.toString());
166     }
167 
168     /**
169      * The method delete a reference link.
170      * 
171      * @param from
172      *            the source of the reference as String
173      */
174     public void deleteReferenceLink(String from) {
175         from = MCRUtils.filterTrimmedNotEmpty(from).orElse(null);
176         if (from == null) {
177             LOGGER
178                 .warn("The from value of a reference link is false, the link was " + "not deleted from the link table");
179             return;
180         }
181 
182         try {
183             linkTableInstance.delete(from, null, null);
184         } catch (Exception e) {
185             LOGGER.warn("An error occured while deleting a dataset from the" + from
186                 + " reference link table, deleting could be not succesful.", e);
187         }
188     }
189 
190     /**
191      * The method delete a reference link pair for the given type to the store.
192      * 
193      * @param from
194      *            the source of the reference as String
195      * @param to
196      *            the target of the reference as String
197      * @param type
198      *            the type of the reference as String
199      */
200     public void deleteReferenceLink(String from, String to, String type) {
201         from = MCRUtils.filterTrimmedNotEmpty(from).orElse(null);
202         if (from == null) {
203             LOGGER
204                 .warn("The from value of a reference link is false, the link was " + "not deleted from the link table");
205             return;
206         }
207         try {
208             linkTableInstance.delete(from, to, type);
209         } catch (Exception e) {
210             LOGGER.warn("An error occured while deleting a dataset from the"
211                 + " reference link table, deleting is not succesful.", e);
212         }
213     }
214 
215     /**
216      * The method count the reference links for a given target MCRobjectID.
217      * 
218      * @param to
219      *            the object ID as MCRObjectID, they was referenced
220      * @return the number of references
221      */
222     public int countReferenceLinkTo(MCRObjectID to) {
223         return countReferenceLinkTo(to.toString());
224     }
225 
226     /**
227      * The method count the reference links for a given target object ID.
228      * 
229      * @param to
230      *            the object ID as String, they was referenced
231      * @return the number of references
232      */
233     public int countReferenceLinkTo(String to) {
234         to = MCRUtils.filterTrimmedNotEmpty(to).orElse(null);
235         if (to == null) {
236             LOGGER.warn("The to value of a reference link is false, the link was " + "not added to the link table");
237 
238             return 0;
239         }
240 
241         try {
242             return linkTableInstance.countTo(null, to, null, null);
243         } catch (Exception e) {
244             LOGGER.warn("An error occured while searching for references of " + to + ".", e);
245         }
246 
247         return 0;
248     }
249 
250     /**
251      * counts the reference links for a given to object ID.
252      * 
253      * @param types
254      *            Array of document type slected by the mcrfrom content
255      * @param restriction
256      *            a first part of the to ID as String, it can be null
257      * @return the number of references
258      */
259     public int countReferenceLinkTo(String to, String[] types, String restriction) {
260         Optional<String> myTo = MCRUtils.filterTrimmedNotEmpty(to);
261         if (!myTo.isPresent()) {
262             LOGGER.warn("The to value of a reference link is false, the link was " + "not added to the link table");
263             return 0;
264         }
265 
266         try {
267             if (types != null && types.length > 0) {
268                 return Stream.of(types).mapToInt(type -> linkTableInstance.countTo(null, myTo.get(), type, restriction))
269                     .sum();
270             }
271             return linkTableInstance.countTo(null, myTo.get(), null, restriction);
272         } catch (Exception e) {
273             LOGGER.warn("An error occured while searching for references of " + to + ".", e);
274             return 0;
275         }
276     }
277 
278     /**
279      * The method count the number of references to a category of a
280      * classification without sub ID's and returns it as a Map
281      * 
282      * @param classid
283      *            the classification ID as MCRObjectID
284      * 
285      * @return a Map with key=categID and value=counted number of references
286      */
287     public Map<String, Number> countReferenceCategory(String classid) {
288         return linkTableInstance.getCountedMapOfMCRTO(classid);
289     }
290 
291     /**
292      * The method count the number of references to a category of a
293      * classification.
294      * 
295      * @param classid
296      *            the classification ID as String
297      * @param categid
298      *            the category ID as String
299      * @return the number of references
300      */
301     public int countReferenceCategory(String classid, String categid) {
302         return countReferenceLinkTo(classid + "##" + categid, null, null);
303     }
304 
305     /**
306      * Returns a List of all link sources of <code>to</code>
307      * 
308      * @param to
309      *            The MCRObjectID to referenced.
310      * @return List of Strings (Source-IDs)
311      */
312     public Collection<String> getSourceOf(MCRObjectID to) {
313         return getSourceOf(to.toString());
314     }
315 
316     /**
317      * Returns a List of all link sources of <code>to</code>
318      * 
319      * @param to
320      *            The ID to referenced.
321      * @return List of Strings (Source-IDs)
322      */
323     public Collection<String> getSourceOf(String to) {
324         if (to == null || to.length() == 0) {
325             LOGGER.warn("The to value of a reference link is false, the link was not found in the link table");
326             return Collections.emptyList();
327         }
328 
329         try {
330             return linkTableInstance.getSourcesOf(to, null);
331         } catch (Exception e) {
332             LOGGER.warn("An error occured while searching for references to " + to + ".", e);
333             return Collections.emptyList();
334         }
335     }
336 
337     /**
338      * Returns a List of all link sources of <code>to</code> and a special
339      * <code>type</code>
340      * 
341      * @param to
342      *            Destination-ID
343      * @param type
344      *            link reference type
345      * @return List of Strings (Source-IDs)
346      */
347     public Collection<String> getSourceOf(MCRObjectID to, String type) {
348         return getSourceOf(to.toString(), type);
349     }
350 
351     /**
352      * Returns a List of all link sources of <code>to</code> and a special
353      * <code>type</code>
354      * 
355      * @param to
356      *            Destination-ID
357      * @param type
358      *            link reference type
359      * @return List of Strings (Source-IDs)
360      */
361     public Collection<String> getSourceOf(String to, String type) {
362         if (to == null || to.length() == 0) {
363             LOGGER.warn("The to value of a reference link is false, the link was not found in the link table");
364             return Collections.emptyList();
365         }
366         if (type == null || type.length() == 0) {
367             LOGGER.warn("The type value of a reference link is false, the link was not found in the link table");
368             return Collections.emptyList();
369         }
370 
371         try {
372             return linkTableInstance.getSourcesOf(to, type);
373         } catch (Exception e) {
374             LOGGER.warn("An error occured while searching for references to " + to + " with " + type + ".", e);
375             return Collections.emptyList();
376         }
377     }
378 
379     /**
380      * The method return a list of all source ID's of the refernce target to
381      * with the given type.
382      * 
383      * @param to
384      *            the refernce target to
385      * @param type
386      *            type of the refernce
387      * @return a list of ID's
388      */
389     public Collection<String> getSourceOf(String[] to, String type) {
390         if (to == null || to.length == 0) {
391             LOGGER.warn("The to value of a reference link is false, the link was not found in the link table");
392             return Collections.emptyList();
393         }
394         LinkedList<String> ll = new LinkedList<>();
395         try {
396             for (String singleTo : to) {
397                 ll.addAll(linkTableInstance.getSourcesOf(singleTo, type));
398             }
399             return ll;
400         } catch (Exception e) {
401             LOGGER.warn("An error occured while searching for references to " + Arrays.toString(to) + ".", e);
402             return ll;
403         }
404     }
405 
406     /**
407      * Returns a List of all link destinations of <code>from</code> and a
408      * special <code>type</code>
409      * 
410      * @param from
411      *            Destination-ID
412      * @param type
413      *            link reference type
414      * @return List of Strings (Source-IDs)
415      */
416     public Collection<String> getDestinationOf(MCRObjectID from, String type) {
417         return getDestinationOf(from.toString(), type);
418     }
419 
420     /**
421      * Returns a List of all link destination of <code>from</code> and a
422      * special <code>type</code>
423      * 
424      * @param from
425      *            Source-ID
426      * @param type
427      *            Link reference type, this can be null. Current types are
428      *            classid, child, parent, reference and derivate.
429      * @return List of Strings (Destination-IDs)
430      */
431     public Collection<String> getDestinationOf(String from, String type) {
432         if (from == null || from.length() == 0) {
433             LOGGER.warn("The from value of a reference link is false, the link was not found in the link table");
434             return Collections.emptyList();
435         }
436         if (type == null || type.length() == 0) {
437             LOGGER.warn("The type value of a reference link is false, the link was not found in the link table");
438             return Collections.emptyList();
439         }
440 
441         try {
442             return linkTableInstance.getDestinationsOf(from, type);
443         } catch (Exception e) {
444             LOGGER.warn("An error occured while searching for references from " + from + ".", e);
445             return Collections.emptyList();
446         }
447     }
448 
449     /**
450      * Creates all references for the given object. You should call {@link #delete(MCRObjectID)} before using this
451      * method otherwise doublets could occur.
452      *
453      * @param obj the object to create the references
454      */
455     public void create(MCRObject obj) {
456         MCRObjectID mcrId = obj.getId();
457         // set new entries
458         MCRObjectMetadata meta = obj.getMetadata();
459         //use Set for category collection to remove duplicates if there are any
460         Collection<MCRCategoryID> categories = new HashSet<>();
461         meta.stream().flatMap(MCRMetaElement::stream).forEach(inf -> {
462             if (inf instanceof MCRMetaClassification) {
463                 String classId = ((MCRMetaClassification) inf).getClassId();
464                 String categId = ((MCRMetaClassification) inf).getCategId();
465                 categories.add(new MCRCategoryID(classId, categId));
466             } else if (inf instanceof MCRMetaLinkID) {
467                 addReferenceLink(mcrId.toString(), ((MCRMetaLink) inf).getXLinkHref(),
468                     MCRLinkTableManager.ENTRY_TYPE_REFERENCE, "");
469             } else if (inf instanceof MCRMetaDerivateLink) {
470                 addReferenceLink(mcrId.toString(), ((MCRMetaLink) inf).getXLinkHref(),
471                     MCRLinkTableManager.ENTRY_TYPE_DERIVATE_LINK, "");
472             }
473         });
474         MCRCategoryID state = obj.getService().getState();
475         if (state != null) {
476             categories.add(state);
477         }
478         categories.addAll(obj.getService().getClassifications());
479         if (categories.size() > 0) {
480             MCRCategLinkReference objectReference = new MCRCategLinkReference(mcrId);
481             MCRCategLinkServiceFactory.getInstance().setLinks(objectReference, categories);
482         }
483         // add derivate reference
484         MCRObjectStructure structure = obj.getStructure();
485         for (int i = 0; i < structure.getDerivates().size(); i++) {
486             MCRMetaLinkID lid = structure.getDerivates().get(i);
487             addReferenceLink(obj.getId(), lid.getXLinkHrefID(), MCRLinkTableManager.ENTRY_TYPE_DERIVATE, "");
488         }
489         // add parent reference
490         if (structure.getParentID() != null) {
491             addReferenceLink(mcrId, structure.getParentID(), MCRLinkTableManager.ENTRY_TYPE_PARENT, "");
492         }
493     }
494 
495     /**
496      * Removes all references of this object.
497      *
498      * @param id the object where all references should be removed
499      */
500     public void delete(MCRObjectID id) {
501         deleteReferenceLink(id);
502         MCRCategLinkReference reference = new MCRCategLinkReference(id);
503         MCRCategLinkServiceFactory.getInstance().deleteLink(reference);
504     }
505 
506     /**
507      * Updates all references of this object. Old ones will be removed and new links will be created.
508      *
509      * @param id the mycore object identifer
510      */
511     public void update(MCRObjectID id) {
512         delete(id);
513         if ("derivate".equals(id.getTypeId())) {
514             create(MCRMetadataManager.retrieveMCRDerivate(id));
515         } else {
516             create(MCRMetadataManager.retrieveMCRObject(id));
517         }
518     }
519 
520     public void create(MCRDerivate der) {
521         Collection<MCRCategoryID> categoryList = new HashSet<>();
522         categoryList.addAll(der.getDerivate().getClassifications()
523             .stream()
524             .map(this::metaClassToCategoryID)
525             .collect(Collectors.toList()));
526 
527         MCRCategoryID state = der.getService().getState();
528         if (state != null) {
529             categoryList.add(state);
530         }
531 
532         MCRCategLinkReference objectReference = new MCRCategLinkReference(der.getId());
533         MCRCategLinkServiceFactory.getInstance().setLinks(objectReference, categoryList);
534     }
535 
536     private MCRCategoryID metaClassToCategoryID(MCRMetaClassification metaClazz) {
537         return new MCRCategoryID(metaClazz.getClassId(), metaClazz.getCategId());
538     }
539 }