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.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.stream.Stream;
25  
26  import org.apache.logging.log4j.LogManager;
27  import org.apache.logging.log4j.Logger;
28  import org.jdom2.Element;
29  import org.mycore.common.MCRClassTools;
30  import org.mycore.common.MCRConstants;
31  import org.mycore.common.MCRException;
32  import org.mycore.common.MCRUtils;
33  import org.mycore.common.config.MCRConfiguration2;
34  
35  import com.google.gson.JsonArray;
36  import com.google.gson.JsonObject;
37  
38  /**
39   * This class is designed to to have a basic class for all metadata. The class
40   * has inside a ArrayList that holds all metaddata elements for one XML tag.
41   * Furthermore, this class supports the linking of a document owing this
42   * metadata element to another document, the id of which is given in the
43   * xlink:href attribute of the MCRMetaLink representing the link. The class name
44   * of such a metadata element must be MCRMetaLink, and the metadata element is
45   * considered to be a folder of links.
46   * 
47   * @author Jens Kupferschmidt
48   * @author Mathias Hegner
49   * @version $Revision$ $Date$
50   */
51  public class MCRMetaElement implements Iterable<MCRMetaInterface>, Cloneable {
52      // common data
53      public static final String DEFAULT_LANGUAGE = MCRConfiguration2.getString("MCR.Metadata.DefaultLang")
54          .orElse(MCRConstants.DEFAULT_LANG);
55  
56      public static final boolean DEFAULT_HERITABLE = MCRConfiguration2.getBoolean("MCR.MetaElement.defaults.heritable")
57          .orElse(false);
58  
59      public static final boolean DEFAULT_NOT_INHERIT = MCRConfiguration2
60          .getBoolean("MCR.MetaElement.defaults.notinherit").orElse(true);
61  
62      private static final String META_PACKAGE_NAME = "org.mycore.datamodel.metadata.";
63  
64      // logger
65      static Logger LOGGER = LogManager.getLogger();
66  
67      private Class<? extends MCRMetaInterface> clazz = null;
68  
69      private String tag = null;
70  
71      private boolean heritable;
72  
73      private boolean notinherit;
74  
75      private ArrayList<MCRMetaInterface> list = null;
76  
77      /**
78       * This is the constructor of the MCRMetaElement class. The default language
79       * for the element was set to <b>MCR.Metadata.DefaultLang</b>.
80       */
81      public MCRMetaElement() {
82          list = new ArrayList<>();
83          heritable = DEFAULT_HERITABLE;
84          notinherit = DEFAULT_NOT_INHERIT;
85      }
86  
87      /**
88       * This is the constructor of the MCRMetaElement class.
89       * @param tag
90       *            the name of this tag
91       * @param heritable
92       *            set this flag to true if all child objects of this element can
93       *            inherit this data
94       * @param notinherit
95       *            set this flag to true if this element should not inherit from
96       *            his parent object
97       * @param list
98       *            a list of MCRMeta... data lines to add in this element (can be null)
99       */
100     public MCRMetaElement(Class<? extends MCRMetaInterface> clazz, String tag, boolean heritable, boolean notinherit,
101         List<? extends MCRMetaInterface> list) {
102         this();
103         this.clazz = clazz;
104         setTag(tag);
105         this.heritable = heritable;
106         this.notinherit = notinherit;
107 
108         if (list != null) {
109             this.list.addAll(list);
110         }
111     }
112 
113     /**
114      * This methode return the name of this metadata class as string.
115      * 
116      * @return the name of this metadata class as string
117      */
118     public final String getClassName() {
119         return getClazz().getSimpleName();
120     }
121 
122     /**
123      * This method returns the instance of an element from the list with index
124      * i.
125      * 
126      * @return the instance of an element, if index is out of range return null
127      */
128     public final MCRMetaInterface getElement(int index) {
129         if ((index < 0) || (index > list.size())) {
130             return null;
131         }
132         return list.get(index);
133     }
134 
135     /**
136      * This method returns the instance of an element from the list with the given 
137      * name
138      * 
139      * @return the instance of the element with the given name or null if there is no such element
140      * */
141     public final MCRMetaInterface getElementByName(String name) {
142         for (MCRMetaInterface sub : this) {
143             if (sub.getSubTag().equals(name)) {
144                 return sub;
145             }
146         }
147         return null;
148     }
149 
150     /**
151      * This methode return the heritable flag of this metadata as boolean value.
152      * 
153      * @return the heritable flag of this metadata class
154      */
155     public final boolean isHeritable() {
156         return heritable;
157     }
158 
159     /**
160      * This methode return the nonherit flag of this metadata as boolean value.
161      * 
162      * @return the notherit flag of this metadata class
163      */
164     public final boolean inheritsNot() {
165         return notinherit;
166     }
167 
168     /**
169      * This methode return the tag of this metadata class as string.
170      * 
171      * @return the tag of this metadata class as string
172      */
173     public final String getTag() {
174         return tag;
175     }
176 
177     /**
178      * This methode set the heritable flag for the metadata class.
179      * 
180      * @param heritable
181      *            the heritable flag as boolean value
182      */
183     public void setHeritable(boolean heritable) {
184         this.heritable = heritable;
185     }
186 
187     /**
188      * This methode set the notinherit flag for the metadata class.
189      * 
190      * @param notinherit
191      *            the notinherit flag as boolean value
192      */
193     public void setNotInherit(boolean notinherit) {
194         this.notinherit = notinherit;
195     }
196 
197     /**
198      * This methode set the tag for the metadata class.
199      * 
200      * @param tag
201      *            the tag for the metadata class
202      */
203     public void setTag(String tag) {
204         MCRUtils.filterTrimmedNotEmpty(tag)
205             .ifPresent(s -> this.tag = s);
206     }
207 
208     /**
209      * This methode set the element class for the metadata elements.
210      * 
211      * @param clazz
212      *            the class for the metadata elements
213      */
214     public final void setClass(Class<? extends MCRMetaInterface> clazz) {
215         this.clazz = clazz;
216     }
217 
218     public Class<? extends MCRMetaInterface> getClazz() {
219         return clazz;
220     }
221 
222     /**
223      * <em>size</em> returns the number of elements in this instance.
224      * 
225      * @return int the size of "list"
226      */
227     public final int size() {
228         return list.size();
229     }
230 
231     /**
232      * The method add a metadata object, that implements the MCRMetaInterface to
233      * this element.
234      * 
235      * @param obj
236      *            a metadata object
237      * @exception MCRException
238      *                if the class name of the object is not the same like the
239      *                name of all store metadata in this element.
240      */
241     public final void addMetaObject(MCRMetaInterface obj) {
242         list.add(obj);
243     }
244 
245     /**
246      * This method remove the instance of an element from the list with index
247      * i.
248      * 
249      * @return true if the instance is removed, otherwise return else
250      */
251     public final boolean removeElement(int index) {
252         if ((index < 0) || (index > list.size())) {
253             return false;
254         }
255         list.remove(index);
256         return true;
257     }
258 
259     /**
260      * The method remove a metadata object, that implements the MCRMetaInterface to
261      * this element.
262      * 
263      * @param obj
264      *            a metadata object
265      * @exception MCRException
266      *                if the class name of the object is not the same like the
267      *                name of all store metadata in this element.
268      * @return true if this <code>MCRMetaElement</code> contained the specified
269      *              <code>MCRMetaInterface</code>
270      */
271     public final boolean removeMetaObject(MCRMetaInterface obj) {
272         return list.remove(obj);
273     }
274 
275     /**
276      * The method removes all inherited metadata objects of this MCRMetaElement.
277      */
278     public final void removeInheritedMetadata() {
279         list.removeIf(mi -> mi.getInherited() > 0);
280     }
281 
282     /**
283      * This methode read the XML input stream part from a DOM part for the
284      * metadata of the document.
285      * 
286      * @param element
287      *            a relevant JDOM element for the metadata
288      * @exception MCRException
289      *                if the class can't loaded
290      */
291     @SuppressWarnings("unchecked")
292     public final void setFromDOM(Element element) throws MCRException {
293 
294         String fullname;
295         Class<? extends MCRMetaInterface> forName;
296         try {
297             String classname = element.getAttributeValue("class");
298             if (classname == null) {
299                 throw new MCRException("Missing required class attribute in element " + element.getName());
300             }
301             fullname = META_PACKAGE_NAME + classname;
302             forName = MCRClassTools.forName(fullname);
303             setClass(forName);
304         } catch (ClassNotFoundException e) {
305             throw new MCRException(e);
306         }
307         tag = element.getName();
308         String heritable = element.getAttributeValue("heritable");
309         if (heritable != null) {
310             setHeritable(Boolean.valueOf(heritable));
311         }
312 
313         String notInherit = element.getAttributeValue("notinherit");
314         if (notInherit != null) {
315             setNotInherit(Boolean.valueOf(notInherit));
316         }
317 
318         List<Element> elementList = element.getChildren();
319         for (Element subtag : elementList) {
320             MCRMetaInterface obj;
321             try {
322                 obj = forName.getDeclaredConstructor().newInstance();
323                 obj.setFromDOM(subtag);
324             } catch (ReflectiveOperationException e) {
325                 throw new MCRException(fullname + " ReflectiveOperationException", e);
326             }
327 
328             list.add(obj);
329         }
330     }
331 
332     /**
333      * This methode create a XML stream for all data in this class, defined by
334      * the MyCoRe XML MCRLangText definition for the given subtag.
335      * 
336      * @param flag
337      *            true if all inherited data should be include, else false
338      * @exception MCRException
339      *                if the content of this class is not valid
340      * @return a JDOM Element with the XML Element part
341      */
342     public final Element createXML(boolean flag) throws MCRException {
343         try {
344             validate();
345         } catch (MCRException exc) {
346             debug();
347             throw new MCRException("MCRMetaElement : The content is not valid: Tag=" + this.tag, exc);
348         }
349         Element elm = new Element(tag);
350         elm.setAttribute("class", getClassName());
351         elm.setAttribute("heritable", String.valueOf(heritable));
352         elm.setAttribute("notinherit", String.valueOf(notinherit));
353         list
354             .stream()
355             .filter(metaInterface -> (flag || metaInterface.getInherited() == 0))
356             .map(MCRMetaInterface::createXML)
357             .forEachOrdered(elm::addContent);
358         return elm;
359     }
360 
361     /**
362      * Creates the JSON representation of this metadata element.
363      * 
364      * <pre>
365      *   {
366      *      class: 'MCRMetaLangText',
367      *      heritable: true,
368      *      notinherit: false,
369      *      data: [
370      *        {@link MCRMetaInterface#createJSON()},
371      *        ...
372      *      ]
373      *   }
374      * </pre>
375      * 
376      * @return a json gson representation of this metadata element
377      */
378     public JsonObject createJSON(boolean flag) {
379         JsonObject meta = new JsonObject();
380         meta.addProperty("class", getClassName());
381         meta.addProperty("heritable", isHeritable());
382         meta.addProperty("notinherit", notinherit);
383         JsonArray data = new JsonArray();
384         list
385             .stream()
386             .filter(metaInterface -> (flag || metaInterface.getInherited() == 0))
387             .map(MCRMetaInterface::createJSON)
388             .forEachOrdered(data::add);
389         meta.add("data", data);
390         return meta;
391     }
392 
393     /**
394      * This methode check the validation of the content of this class. The
395      * methode returns <em>true</em> if
396      * <ul>
397      * <li>the classname is not null or empty
398      * <li>the tag is not null or empty
399      * <li>if the list is empty
400      * <li>the lang value was supported
401      * </ul>
402      * otherwise the methode return <em>false</em>
403      * 
404      * @return a boolean value
405      */
406     public final boolean isValid() {
407         try {
408             validate();
409             return true;
410         } catch (MCRException exc) {
411             LOGGER.warn("The '{}' is invalid.", getTag(), exc);
412         }
413         return false;
414     }
415 
416     /**
417      * Validates this MCRMetaElement. This method throws an exception if:
418      * <ul>
419      * <li>the classname is not null or empty</li>
420      * <li>the tag is not null or empty</li>
421      * <li>if the list is empty</li>
422      * <li>the lang value was supported</li>
423      * </ul>
424      * 
425      * @throws MCRException the MCRMetaElement is invalid
426      */
427     public void validate() throws MCRException {
428         tag = MCRUtils.filterTrimmedNotEmpty(tag).orElse(null);
429         if (tag == null) {
430             throw new MCRException("No tag name defined!");
431         }
432         if (clazz == null) {
433             throw new MCRException(getTag() + ": @class is not defined");
434         }
435         if (!clazz.getPackage().getName().equals(META_PACKAGE_NAME.substring(0, META_PACKAGE_NAME.length() - 1))) {
436             throw new MCRException(
437                 getTag() + ": package " + clazz.getPackage().getName() + " does not equal " + META_PACKAGE_NAME);
438         }
439         if (list.size() == 0) {
440             throw new MCRException(getTag() + ": does not contain any sub elements");
441         }
442     }
443 
444     /**
445      * This method make a clone of this class.
446      */
447     @Override
448     public final MCRMetaElement clone() {
449         MCRMetaElement out = new MCRMetaElement();
450         out.setClass(getClazz());
451         out.setTag(tag);
452         out.setHeritable(heritable);
453         out.setNotInherit(notinherit);
454 
455         for (int i = 0; i < size(); i++) {
456             MCRMetaInterface mif = (list.get(i)).clone();
457             out.addMetaObject(mif);
458         }
459 
460         return out;
461     }
462 
463     /**
464      * This method put debug data to the logger (for the debug mode).
465      */
466     public final void debug() {
467         if (LOGGER.isDebugEnabled()) {
468             LOGGER.debug("ClassName          = {}", getClassName());
469             LOGGER.debug("Tag                = {}", tag);
470             LOGGER.debug("Heritable          = {}", String.valueOf(heritable));
471             LOGGER.debug("NotInherit         = {}", String.valueOf(notinherit));
472             LOGGER.debug("Elements           = {}", String.valueOf(list.size()));
473             LOGGER.debug(" ");
474             for (MCRMetaInterface aList : list) {
475                 aList.debug();
476             }
477         }
478     }
479 
480     /**
481      * Streams the {@link MCRMetaInterface} of this element.
482      *
483      * @return stream of MCRMetaInterface's
484      */
485     public Stream<MCRMetaInterface> stream() {
486         return list.stream();
487     }
488 
489     @Override
490     public Iterator<MCRMetaInterface> iterator() {
491         return list.iterator();
492     }
493 
494 }