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.Objects;
23  import java.util.stream.Collectors;
24  
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  import org.jdom2.Element;
28  import org.jdom2.Namespace;
29  import org.mycore.common.MCRCalendar;
30  import org.mycore.common.MCRException;
31  
32  import com.ibm.icu.util.Calendar;
33  import com.ibm.icu.util.GregorianCalendar;
34  
35  /**
36   * This class implements all methods for handling the MCRMetaHistoryDate
37   * part of a metadata object. It uses the GPL licensed ICU library of IBM.
38   * 
39   * @author Juergen Vogler
40   * @author Jens Kupferschmidt
41   * @author Thomas Junge
42   * @version $Revision$ $Date$
43   * @see <a href="http://www.icu-project.org/">http://www.icu-project.org/</a>
44   */
45  public class MCRMetaHistoryDate extends MCRMetaDefault {
46  
47      /** Logger */
48      private static final Logger LOGGER = LogManager.getLogger();
49  
50      /** The maximal length of 'text' */
51      public static final int MCRHISTORYDATE_MAX_TEXT = 512;
52  
53      // Data of this class
54      private ArrayList<MCRMetaHistoryDateText> texts;
55  
56      private Calendar von;
57  
58      private Calendar bis;
59  
60      private int ivon;
61  
62      private int ibis;
63  
64      private String calendar;
65  
66      /**
67       * This is the constructor. <br>
68       * The language element was set to configured default. The text element is
69       * set to an empty string. The calendar is set to 'Gregorian Calendar'. The
70       * von value is set to MIN_JULIAN_DAY_NUMBER, the bis value is set to
71       * MAX_JULIAN_DAY_NUMBER;
72       */
73      public MCRMetaHistoryDate() {
74          super();
75          texts = new ArrayList<>();
76          calendar = MCRCalendar.CALENDARS_LIST.get(0);
77          setDefaultVon();
78          setDefaultBis();
79      }
80  
81      /**
82       * This is the constructor. <br>
83       * The language element was set. If the value of <em>default_lang</em> is
84       * null, empty or false <b>en </b> was set. The subtag element was set to
85       * the value of <em>subtag</em>. If the value of <em>subtag</em>
86       * is null or empty an exception was thrown. The type element was set to
87       * the value of <em>type</em>, if it is null, an empty string was set
88       * to the type element.<br>
89       * The text element is set to an empty string. The calendar is set to 'Gregorian Calendar'. The von value 
90       * is set to MIN_JULIAN_DAY_NUMBER, the bis value is set to MAX_JULIAN_DAY_NUMBER;
91       * @param subtag      the name of the subtag
92       * @param type        the optional type string
93       * @param inherted    a value &gt;= 0
94       *
95       * @exception MCRException if the parameter values are invalid
96       */
97      public MCRMetaHistoryDate(String subtag, String type, int inherted) throws MCRException {
98          super(subtag, null, type, inherted);
99          texts = new ArrayList<>();
100         calendar = MCRCalendar.CALENDARS_LIST.get(0);
101         setDefaultVon();
102         setDefaultBis();
103     }
104 
105     /**
106      * This method set the text field for the default language. If data exists,
107      * it overwrites the value of text.
108      * 
109      * @param text
110      *            the text string for a date or range
111      */
112     @Deprecated
113     public final void setText(String text) {
114         setText(text, lang);
115     }
116 
117     /**
118      * This method set the text field for the given language. If data exists, it
119      * overwrites the value of text.
120      * 
121      * @param text
122      *            the text string for a date or range
123      * @param lang
124      *            the language of the text in the ISO format
125      */
126     public final void setText(String text, String lang) {
127         if (text == null || text.length() == 0) {
128             LOGGER.warn("The text field of MCRMeataHistoryDate is empty.");
129             return;
130         }
131         if (text.length() <= MCRHISTORYDATE_MAX_TEXT) {
132             text = text.trim();
133         } else {
134             text = text.substring(0, MCRHISTORYDATE_MAX_TEXT);
135         }
136         if (lang == null || lang.length() == 0) {
137             addText(text, this.lang);
138         } else {
139             addText(text, lang);
140         }
141     }
142 
143     /**
144      * This method add a MCRMetaHistoryDateTexts instance to the ArrayList of
145      * texts.
146      * 
147      * @param text
148      *            the text- String
149      * @param lang
150      *            the lang- String
151      */
152 
153     public final void addText(String text, String lang) {
154         if (text == null || text.length() == 0) {
155             LOGGER.warn("The text field of MCRMeataHistoryDate is empty.");
156             return;
157         }
158         if (lang == null || lang.length() == 0) {
159             LOGGER.warn("The lang field of MCRMeataHistoryDate is empty.");
160             return;
161         }
162         for (int i = 0; i < texts.size(); i++) {
163             if (texts.get(i).getLang().equals(lang)) {
164                 texts.remove(i);
165                 break;
166             }
167         }
168         texts.add(new MCRMetaHistoryDateText(text, lang));
169     }
170 
171     /**
172      * This method return the MCRMetaHistoryDateTexts instance with the
173      * corresponding language.
174      * 
175      * @param lang
176      *            the language String in ISO format
177      * @return an instance of MCRMetaHistoryDateTexts or null
178      */
179     public final MCRMetaHistoryDateText getText(String lang) {
180         if (lang == null) {
181             return null;
182         }
183         return texts.stream()
184             .filter(text -> text.getLang().equals(lang))
185             .findFirst()
186             .orElse(null);
187     }
188 
189     /**
190      * This method return the MCRMetaHistoryDateTexts instance of the indexed
191      * element of the ArrayList.
192      * 
193      * @param index
194      *            the index of ArryList texts
195      * @return an instance of MCRMetaHistoryDateTexts or null
196      */
197     public final MCRMetaHistoryDateText getText(int index) {
198         if (index >= 0 && index < texts.size()) {
199             return texts.get(index);
200         }
201         return null;
202     }
203 
204     /**
205      * This method read the ArryList texts
206      * 
207      * @return an ArrayList of MCRMetaHistoryDateTexts instances
208      */
209     public final ArrayList<MCRMetaHistoryDateText> getTexts() {
210         return texts;
211     }
212 
213     /**
214      * This method read the size of texts
215      * 
216      * @return the size of the ArrayList of language dependence texts
217      */
218     public final int textSize() {
219         return texts.size();
220     }
221 
222     /**
223      * The method set the calendar String value.
224      * 
225      * @param calstr
226      *            the calendar as String, one of CALENDARS.
227      */
228     public final void setCalendar(String calstr) {
229         if (calstr == null || calstr.trim().length() == 0 || (!MCRCalendar.CALENDARS_LIST.contains(calstr))) {
230             calendar = MCRCalendar.TAG_GREGORIAN;
231             LOGGER.warn("The calendar field of MCRMeataHistoryDate is set to default " + MCRCalendar.TAG_GREGORIAN
232                 + ".");
233             return;
234         }
235         calendar = calstr;
236     }
237 
238     /**
239      * The method set the calendar String value.
240      * 
241      * @param calendar
242      *            the date of the calendar.
243      */
244     public final void setCalendar(Calendar calendar) {
245         this.calendar = MCRCalendar.getCalendarTypeString(calendar);
246     }
247 
248     /**
249      * The method set the von values to the default.
250      */
251     public final void setDefaultVon() {
252         von = new GregorianCalendar();
253         von.set(Calendar.JULIAN_DAY, MCRCalendar.MIN_JULIAN_DAY_NUMBER);
254         ivon = MCRCalendar.MIN_JULIAN_DAY_NUMBER;
255     }
256 
257     /**
258      * The method set the bis values to the default.
259      */
260     public final void setDefaultBis() {
261         bis = new GregorianCalendar();
262         bis.set(Calendar.JULIAN_DAY, MCRCalendar.MAX_JULIAN_DAY_NUMBER);
263         ibis = MCRCalendar.MAX_JULIAN_DAY_NUMBER;
264     }
265 
266     /**
267      * This method set the von to the given date of a supported calendar.
268      * 
269      * @param calendar
270      *            the date of a ICU supported calendar.
271      */
272     public final void setVonDate(Calendar calendar) {
273         if (calendar == null) {
274             setDefaultVon();
275             LOGGER.warn("The calendar to set 'von' is null, default is set.");
276         } else {
277             von = calendar;
278             ivon = von.get(Calendar.JULIAN_DAY);
279         }
280     }
281 
282     /**
283      * This method set the von to the given date.
284      * 
285      * @param date
286      *            a date string
287      * @param calendar
288      *            the calendar as String, one of CALENDARS.
289      */
290     public final void setVonDate(String date, String calendar) {
291         try {
292             von = MCRCalendar.getHistoryDateAsCalendar(date, false, calendar);
293             ivon = von.get(Calendar.JULIAN_DAY);
294         } catch (Exception e) {
295             e.printStackTrace();
296             LOGGER.warn("The von date {} for calendar {} is false. Set to default!", date, calendar);
297             setDefaultVon();
298         }
299     }
300 
301     /**
302      * This method set the bis to the given date of a supported calendar.
303      * 
304      * @param calendar
305      *            the date of a ICU supported calendar
306      */
307     public final void setBisDate(Calendar calendar) {
308         if (calendar == null) {
309             setDefaultBis();
310             LOGGER.warn("The calendar to set 'bis' is null, default is set.");
311         } else {
312             bis = calendar;
313             ibis = bis.get(Calendar.JULIAN_DAY);
314         }
315     }
316 
317     /**
318      * This method set the bis to the given date.
319      * 
320      * @param date
321      *            a date string
322      * @param calendar
323      *            the calendar as String, one of CALENDARS.
324      */
325     public final void setBisDate(String date, String calendar) {
326         Calendar c = bis;
327         try {
328             c = MCRCalendar.getHistoryDateAsCalendar(date, true, calendar);
329         } catch (Exception e) {
330             LOGGER.warn("The bis date {} for calendar {} is false.", date, calendar);
331             c = null;
332         }
333         setBisDate(c);
334     }
335 
336     /**
337      * This method get the 'calendar' text element.
338      * 
339      * @return the calendar string
340      */
341     public final String getCalendar() {
342         return calendar;
343     }
344 
345     /**
346      * This method get the von element as ICU-Calendar.
347      * 
348      * @return the date
349      */
350     public final Calendar getVon() {
351         return von;
352     }
353 
354     /**
355      * This method return the von as string.
356      * 
357      * @return the date
358      */
359     public final String getVonToString() {
360         return MCRCalendar.getCalendarDateToFormattedString(von);
361     }
362 
363     /**
364      * This method get the ivon element as Julian Day integer.
365      * 
366      * @return the date
367      */
368     public final int getIvon() {
369         return ivon;
370     }
371 
372     /**
373      * This method get the bis element as ICU-Calendar.
374      * 
375      * @return the date
376      */
377     public final Calendar getBis() {
378         return bis;
379     }
380 
381     /**
382      * This method return the bis as string.
383      * 
384      * @return the date
385      */
386     public final String getBisToString() {
387         return MCRCalendar.getCalendarDateToFormattedString(bis);
388     }
389 
390     /**
391      * This method get the ibis element as Julian Day integer.
392      * 
393      * @return the date
394      */
395     public final int getIbis() {
396         return ibis;
397     }
398 
399     /**
400      * This method reads the XML input stream part from a DOM part for the
401      * metadata of the document.
402      * 
403      * @param element
404      *            a relevant JDOM element for the metadata
405      */
406     @Override
407     public void setFromDOM(Element element) {
408         super.setFromDOM(element);
409         texts.clear(); // clear
410 
411         for (Element textElement : element.getChildren("text")) {
412             String text = textElement.getText();
413             String lang = textElement.getAttributeValue("lang", Namespace.XML_NAMESPACE);
414             if (lang != null) {
415                 setText(text, lang);
416             } else {
417                 setText(text);
418             }
419         }
420         setCalendar(element.getChildTextTrim("calendar"));
421         setVonDate(element.getChildTextTrim("von"), calendar);
422         setBisDate(element.getChildTextTrim("bis"), calendar);
423         /** 
424          * If dates higher than 1582 and calendar is Julian the calendar must switch to 
425          * Gregorian cause the date transforming is implicit in the calendar methods. In
426          * other cases before 1582 both calendar are equal.
427          * */
428         if (calendar.equals(MCRCalendar.TAG_JULIAN)) {
429             calendar = MCRCalendar.TAG_GREGORIAN;
430         }
431     }
432 
433     /**
434      * This method creates a XML stream for all data in this class, defined by
435      * the MyCoRe XML MCRMetaHistoryDate definition for the given subtag.
436      * 
437      * @exception MCRException
438      *                if the content of this class is not valid
439      * @return a JDOM Element with the XML MCRMetaHistoryDate part
440      */
441     @Override
442     public Element createXML() throws MCRException {
443         Element elm = super.createXML();
444         for (MCRMetaHistoryDateText text : texts) {
445             Element elmt = new Element("text");
446             elmt.addContent(text.getText());
447             elmt.setAttribute("lang", text.getLang(), Namespace.XML_NAMESPACE);
448             elm.addContent(elmt);
449         }
450         elm.addContent(new Element("calendar").addContent(calendar));
451 
452         elm.addContent(new Element("ivon").addContent(Integer.toString(ivon)));
453         elm.addContent(new Element("von").addContent(getVonToString()));
454 
455         elm.addContent(new Element("ibis").addContent(Integer.toString(ibis)));
456         elm.addContent(new Element("bis").addContent(getBisToString()));
457         return elm;
458     }
459 
460     /**
461      * Validates this MCRMetaHistoryDate. This method throws an exception if:
462      * <ul>
463      * <li>the subtag is not null or empty</li>
464      * <li>the lang value was supported</li>
465      * <li>the inherited value is lower than zero</li>
466      * <li>the number of texts is 0 (empty texts are delete)</li>
467      * <li>von is null or bis is null or calendar is null</li>
468      * </ul>
469      * 
470      * @throws MCRException the MCRMetaHistoryDate is invalid
471      */
472     @Override
473     public void validate() throws MCRException {
474         super.validate();
475         texts.removeIf(textItem -> !textItem.isValid());
476         if (texts.size() == 0) {
477             throw new MCRException(getSubTag() + ": no texts defined");
478         }
479         if (von == null || bis == null || calendar == null) {
480             throw new MCRException(getSubTag() + ": von,bis or calendar are null");
481         }
482         if (ibis < ivon) {
483             Calendar swp = (Calendar) von.clone();
484             setVonDate((Calendar) bis.clone());
485             setBisDate(swp);
486         }
487     }
488 
489     /**
490      * clone of this instance
491      * 
492      * you will get a (deep) clone of this element
493      * 
494      * @see java.lang.Object#clone()
495      */
496     @Override
497     public MCRMetaHistoryDate clone() {
498         MCRMetaHistoryDate clone = (MCRMetaHistoryDate) super.clone();
499 
500         clone.texts = this.texts.stream().map(MCRMetaHistoryDateText::clone)
501             .collect(Collectors.toCollection(ArrayList::new));
502         clone.bis = (Calendar) this.bis.clone();
503         clone.von = (Calendar) this.von.clone();
504         clone.ibis = this.ibis;
505         clone.ivon = this.ivon;
506         clone.calendar = this.calendar;
507 
508         return clone;
509     }
510 
511     /**
512      * This method put debug data to the logger (for the debug mode).
513      */
514     @Override
515     public void debug() {
516         if (LOGGER.isDebugEnabled()) {
517             super.debugDefault();
518             for (MCRMetaHistoryDateText text : texts) {
519                 LOGGER.debug("Text / lang         = {} / {}", text.getText(), text.getLang());
520             }
521             LOGGER.debug("Calendar           = {}", calendar);
522             LOGGER.debug("Von (String)       = {}", getVonToString());
523             LOGGER.debug("Von (JulianDay)    = {}", ivon);
524             LOGGER.debug("Bis (String)       = {}", getBisToString());
525             LOGGER.debug("Bis (JulianDay)    = {}", ibis);
526             LOGGER.debug("");
527         }
528     }
529 
530     /**
531      * This method compares this instance with a MCRMetaHistoryDate object
532      */
533     @Override
534     public boolean equals(Object obj) {
535         if (!super.equals(obj)) {
536             return false;
537         }
538         final MCRMetaHistoryDate other = (MCRMetaHistoryDate) obj;
539         boolean fieldTest = Objects.equals(this.calendar, other.calendar) && Objects.equals(this.ivon, other.ivon);
540         boolean textTest = equalText(other.getTexts());
541         return fieldTest && textTest;
542     }
543 
544     private boolean equalText(ArrayList<MCRMetaHistoryDateText> objtexts) {
545         boolean testflag = true;
546         int size = texts.size() < objtexts.size() ? texts.size() : objtexts.size();
547         for (int i = 0; i < size; i++) {
548             testflag &= texts.get(i).equals(objtexts.get(i));
549         }
550         return testflag;
551     }
552 
553     /**
554      * This class describes the structure of pair of language an text. The
555      * language notation is in the ISO format.
556      * 
557      */
558     public static class MCRMetaHistoryDateText implements Cloneable {
559         private String text;
560 
561         private String lang;
562 
563         public MCRMetaHistoryDateText() {
564             text = "";
565             lang = DEFAULT_LANGUAGE;
566         }
567 
568         public MCRMetaHistoryDateText(String text, String lang) {
569             setText(text, lang);
570         }
571 
572         /**
573          * This method get the text element as String.
574          * 
575          * @return the text
576          */
577 
578         public String getText() {
579             return text;
580         }
581 
582         /**
583          * This method get the language element as String of ISO code.
584          * 
585          * @return the language
586          */
587         public String getLang() {
588             return lang;
589         }
590 
591         /**
592          * This method set the text element with the corresponding language.
593          * 
594          * @param text
595          *            the text String
596          * @param lang
597          *            the language String
598          */
599         public void setText(String text, String lang) {
600             if (lang == null || lang.length() == 0) {
601                 this.lang = DEFAULT_LANGUAGE;
602             } else {
603                 this.lang = lang;
604             }
605             if (text == null || text.length() == 0) {
606                 this.text = "";
607             } else {
608                 this.text = text;
609             }
610         }
611 
612         /**
613          * This method set the text element.
614          * 
615          * @param text
616          *            the text String of a date value
617          */
618         @Deprecated
619         public void setText(String text) {
620             if (text == null || text.length() == 0) {
621                 this.text = "";
622             } else {
623                 this.text = text;
624             }
625         }
626 
627         /**
628          * This method set the lang element.
629          * 
630          * @param lang
631          *            the language String of a date value
632          */
633         @Deprecated
634         public void setLang(String lang) {
635             if (lang == null || lang.length() == 0) {
636                 this.lang = DEFAULT_LANGUAGE;
637             } else {
638                 this.lang = lang;
639             }
640         }
641 
642         /**
643          * This method validate the content. If lang and text are not empty, it return true otherwise it return false.
644          * 
645          * @return true if the content is valid.
646          */
647         public boolean isValid() {
648             return !(lang.length() == 0 || text.length() == 0);
649         }
650 
651         /**
652          * This method check the equivalence of lang and text between this object
653          * and a given MCRMetaHistoryDateText object.
654          * 
655          * @param obj a MCRMetaHistoryDateText instance
656          * @return true if both parts are equal
657          */
658         public boolean equals(MCRMetaHistoryDateText obj) {
659             return lang.equals(obj.getLang()) && text.equals(obj.getText());
660         }
661 
662         @Override
663         protected MCRMetaHistoryDateText clone() {
664             MCRMetaHistoryDateText clone = null;
665             try {
666                 clone = (MCRMetaHistoryDateText) super.clone();
667             } catch (Exception e) {
668                 // this can not happen!
669             }
670 
671             clone.text = this.text;
672             clone.lang = this.lang;
673 
674             return clone;
675         }
676     }
677 }