root/trunk/cdmlib/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/TimePeriod.java

Revision 13258, 21.8 kB (checked in by a.mueller, 7 months ago)

bugfix for Coordinate Parser with whitespaces, Dateparsing, etc.

  • Property svn:keywords set to Id
Line 
1/**
2* Copyright (C) 2007 EDIT
3* European Distributed Institute of Taxonomy
4* http://www.e-taxonomy.eu
5*
6* The contents of this file are subject to the Mozilla Public License Version 1.1
7* See LICENSE.TXT at the top of this package for the full license terms.
8*/
9
10package eu.etaxonomy.cdm.model.common;
11
12import java.io.Serializable;
13import java.text.DateFormat;
14import java.text.ParsePosition;
15import java.util.Calendar;
16import java.util.Date;
17import java.util.regex.Matcher;
18import java.util.regex.Pattern;
19
20import javax.persistence.Embeddable;
21import javax.persistence.Transient;
22import javax.xml.bind.annotation.XmlAccessType;
23import javax.xml.bind.annotation.XmlAccessorType;
24import javax.xml.bind.annotation.XmlElement;
25import javax.xml.bind.annotation.XmlRootElement;
26import javax.xml.bind.annotation.XmlType;
27import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
28
29import org.apache.commons.lang.time.DateUtils;
30import org.apache.log4j.Logger;
31import org.hibernate.annotations.Type;
32import org.hibernate.search.annotations.Field;
33import org.hibernate.search.annotations.FieldBridge;
34import org.joda.time.DateTime;
35import org.joda.time.DateTimeFieldType;
36import org.joda.time.LocalDate;
37import org.joda.time.Partial;
38import org.joda.time.ReadableInstant;
39import org.joda.time.ReadablePartial;
40import org.joda.time.format.DateTimeFormatter;
41
42import eu.etaxonomy.cdm.common.CdmUtils;
43import eu.etaxonomy.cdm.hibernate.PartialBridge;
44import eu.etaxonomy.cdm.jaxb.PartialAdapter;
45
46/**
47 * @author m.doering
48 * @version 1.0
49 * @created 08-Nov-2007 13:07:00
50 * @updated 05-Dec-2008 23:00:05
51 */
52@XmlAccessorType(XmlAccessType.FIELD)
53@XmlType(name = "TimePeriod", propOrder = {
54    "start",
55    "end",
56    "freeText"
57})
58@XmlRootElement(name = "TimePeriod")
59@Embeddable
60public class TimePeriod implements Cloneable, Serializable {
61        private static final Logger logger = Logger.getLogger(TimePeriod.class);
62        public static final DateTimeFieldType MONTH_TYPE = DateTimeFieldType.monthOfYear();
63        public static final DateTimeFieldType YEAR_TYPE = DateTimeFieldType.year();
64        public static final DateTimeFieldType DAY_TYPE = DateTimeFieldType.dayOfMonth();
65       
66        @XmlElement(name = "Start")
67        @XmlJavaTypeAdapter(value = PartialAdapter.class)
68        @Type(type="partialUserType")
69        @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
70        @FieldBridge(impl = PartialBridge.class)
71        private Partial start;
72       
73        @XmlElement(name = "End")
74        @XmlJavaTypeAdapter(value = PartialAdapter.class)
75        @Type(type="partialUserType")
76        @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
77        @FieldBridge(impl = PartialBridge.class)
78        private Partial end;
79
80       
81        @XmlElement(name = "FreeText")
82        private String freeText;
83       
84       
85        /**
86         * Factory method
87         * @return
88         */
89        public static TimePeriod NewInstance(){
90                return new TimePeriod();
91        }
92       
93       
94        /**
95         * Factory method
96         * @return
97         */
98        public static TimePeriod NewInstance(Partial startDate){
99                return new TimePeriod(startDate);
100        }
101       
102       
103        /**
104         * Factory method
105         * @return
106         */
107        public static TimePeriod NewInstance(Partial startDate, Partial endDate){
108                return new TimePeriod(startDate, endDate);
109        }
110       
111       
112        /**
113         * Factory method
114         * @return
115         */
116        public static TimePeriod NewInstance(Integer year){
117                Integer endYear = null;
118                return NewInstance(year, endYear);
119        }
120       
121        /**
122         * Factory method
123         * @return
124         */
125        public static TimePeriod NewInstance(Integer startYear, Integer endYear){
126                Partial startDate = null;
127                Partial endDate = null;
128                if (startYear != null){
129                        startDate = new Partial().with(YEAR_TYPE, startYear);
130                }
131                if (endYear != null){
132                        endDate = new Partial().with(YEAR_TYPE, endYear);
133                }
134                return new TimePeriod(startDate, endDate);
135        }
136
137       
138       
139        /**
140         * Factory method to create a TimePeriod from a <code>Calendar</code>. The Calendar is stored as the starting instant.   
141         * @return
142         */
143        public static TimePeriod NewInstance(Calendar startCalendar){
144                return NewInstance(startCalendar, null);
145        }
146
147        /**
148         * Factory method to create a TimePeriod from a <code>ReadableInstant</code>(e.g. <code>DateTime</code>).
149         * The <code>ReadableInstant</code> is stored as the starting instant.   
150         * @return
151         */
152        public static TimePeriod NewInstance(ReadableInstant readableInstant){
153                return NewInstance(readableInstant, null);
154        }
155       
156        /**
157         * Factory method to create a TimePeriod from a starting and an ending <code>Calendar</code>   
158         * @return
159         */
160        public static TimePeriod NewInstance(Calendar startCalendar, Calendar endCalendar){
161                Partial startDate = null;
162                Partial endDate = null;
163                if (startCalendar != null){
164                        startDate = calendarToPartial(startCalendar);
165                }
166                if (endCalendar != null){
167                        endDate = calendarToPartial(endCalendar);
168                }
169                return new TimePeriod(startDate, endDate);
170        }
171
172       
173        /**
174         * Factory method to create a TimePeriod from a starting and an ending <code>ReadableInstant</code>(e.g. <code>DateTime</code>)   
175         * @return
176         */
177        public static TimePeriod NewInstance(ReadableInstant startInstant, ReadableInstant endInstant){
178                Partial startDate = null;
179                Partial endDate = null;
180                if (startInstant != null){
181                        startDate = readableInstantToPartial(startInstant);
182                }
183                if (endInstant != null){
184                        endDate = readableInstantToPartial(endInstant);
185                }
186                return new TimePeriod(startDate, endDate);
187        }
188
189       
190        /**
191         * Transforms a <code>Calendar</code> into a <code>Partial</code>
192         * @param calendar
193         * @return
194         */
195        public static Partial calendarToPartial(Calendar calendar){
196                LocalDate ld = new LocalDate(calendar);
197                Partial partial = new Partial(ld);
198                return partial;
199        }
200       
201        /**
202         * Transforms a <code>Calendar</code> into a <code>Partial</code>
203         * @param calendar
204         * @return
205         */
206        public static Partial readableInstantToPartial(ReadableInstant readableInstant){
207                DateTime dt = readableInstant.toInstant().toDateTime();
208                LocalDate ld = dt.toLocalDate();
209                Partial partial = new Partial(ld);
210                return partial;
211        }
212       
213        /**
214         * Constructor
215         */
216        protected TimePeriod() {
217                super();
218        }
219        public TimePeriod(Partial startDate) {
220                start=startDate;
221        }
222        public TimePeriod(Partial startDate, Partial endDate) {
223                start=startDate;
224                end=endDate;
225        }
226
227        /**
228         * True, if this time period represents a period not a single point in time.
229         * This is by definition, that the time period has a start and an end value,
230         * and both have a year value that is not null
231         * @return
232         */
233        @Transient
234        public boolean isPeriod(){
235                if (getStartYear() != null && getEndYear() != null ){
236                        return true;
237                }else{
238                        return false;
239                }
240        }
241       
242        /**
243         * True, if there is no start date and no end date and no freetext representation exists.
244         * @return
245         */
246        @Transient
247        public boolean isEmpty(){
248                if (CdmUtils.isEmpty(this.getFreeText()) && start == null  && end == null ){
249                        return true;
250                }else{
251                        return false;
252                }
253        }
254       
255       
256        public Partial getStart() {
257                return start;
258        }
259       
260        public void setStart(Partial start) {
261                this.start = start;
262        }
263       
264        public Partial getEnd() {
265                return end;
266        }
267       
268        public void setEnd(Partial end) {
269                this.end = end;
270        }
271       
272        /**
273         * For time periods that need to store more information than the one
274         * that can be stored in <code>start</code> and <code>end</code>.
275         * If free text is not <code>null</null> {@link #toString()} will always
276         * return the free text value.
277         * <BR>Use {@link #toString()} for public use.
278         * @return the freeText
279         */
280        public String getFreeText() {
281                return freeText;
282        }
283
284
285        /**
286         * Use {@link #parseSingleDate(String)} for public use.
287         * @param freeText the freeText to set
288         */
289        public void setFreeText(String freeText) {
290                this.freeText = freeText;
291        }
292
293
294        @Transient
295        public String getYear(){
296                String result = "";
297                if (getStartYear() != null){
298                        result += String.valueOf(getStartYear());
299                        if (getEndYear() != null){
300                                result += "-" + String.valueOf(getEndYear());
301                        }
302                }else{
303                        if (getEndYear() != null){
304                                result += String.valueOf(getEndYear());
305                        }
306                }
307                return result;
308        }
309       
310        @Transient
311        public Integer getStartYear(){
312                return getPartialValue(start, YEAR_TYPE);
313        }
314       
315        @Transient
316        public Integer getStartMonth(){
317                return getPartialValue(start, MONTH_TYPE);
318        }
319
320        @Transient
321        public Integer getStartDay(){
322                return getPartialValue(start, DAY_TYPE);
323        }
324
325        @Transient
326        public Integer getEndYear(){
327                return getPartialValue(end, YEAR_TYPE);
328        }
329
330        @Transient
331        public Integer getEndMonth(){
332                return getPartialValue(end, MONTH_TYPE);
333        }
334
335        @Transient
336        public Integer getEndDay(){
337                return getPartialValue(end, DAY_TYPE);
338        }
339       
340        public static Integer getPartialValue(Partial partial, DateTimeFieldType type){
341                if (partial == null || ! partial.isSupported(type)){
342                        return null;
343                }else{
344                        return partial.get(type);
345                }
346               
347        }
348       
349        public TimePeriod setStartYear(Integer year){
350                return setStartField(year, YEAR_TYPE);
351        }
352       
353        public TimePeriod setStartMonth(Integer month) throws IndexOutOfBoundsException{
354                return setStartField(month, MONTH_TYPE);
355        }
356
357        public TimePeriod setStartDay(Integer day) throws IndexOutOfBoundsException{
358                return setStartField(day, DAY_TYPE);
359        }
360       
361        public TimePeriod setEndYear(Integer year){
362                return setEndField(year, YEAR_TYPE);
363        }
364
365        public TimePeriod setEndMonth(Integer month) throws IndexOutOfBoundsException{
366                return setEndField(month, MONTH_TYPE);
367        }
368
369        public TimePeriod setEndDay(Integer day) throws IndexOutOfBoundsException{
370                return setEndField(day, DAY_TYPE);
371        }
372       
373        public static Partial setPartialField(Partial partial, Integer value, DateTimeFieldType type) 
374                        throws IndexOutOfBoundsException{
375                if (partial == null){
376                        partial = new Partial();
377                }
378                if (value == null){
379                        return partial.without(type);
380                }else{
381                        checkFieldValues(value, type, partial);
382                        return partial.with(type, value);
383                }
384        }
385       
386        private TimePeriod setStartField(Integer value, DateTimeFieldType type) 
387                        throws IndexOutOfBoundsException{
388                start = setPartialField(start, value, type);
389                return this;
390        }
391
392        private TimePeriod setEndField(Integer value, DateTimeFieldType type)
393                        throws IndexOutOfBoundsException{
394                end = setPartialField(end, value, type);
395                return this;
396        }
397       
398        /**
399         * Throws an IndexOutOfBoundsException if the value does not have a valid value
400         * (e.g. month > 12, month < 1, day > 31, etc.)
401         * @param value
402         * @param type
403         * @throws IndexOutOfBoundsException
404         */
405        private static void checkFieldValues(Integer value, DateTimeFieldType type, Partial partial)
406                        throws IndexOutOfBoundsException{
407                int max = 9999999;
408                if (type.equals(MONTH_TYPE)){
409                        max = 12;
410                }
411                if (type.equals(DAY_TYPE)){
412                        max = 31;
413                        Integer month = null;
414                        if (partial.isSupported(MONTH_TYPE)){
415                                month = partial.get(MONTH_TYPE);
416                        }
417                        if (month != null){
418                                if (month == 2){
419                                        max = 29;
420                                }else if (month == 4 ||month == 6 ||month == 9 ||month == 11){
421                                        max = 30; 
422                                }
423                        }
424                }
425                if ( (value < 1 || value > max) ){
426                        throw new IndexOutOfBoundsException("Value must be between 1 and " +  max);
427                }
428        }
429       
430        private void initStart(){
431                if (start == null){
432                        start = new Partial();
433                }
434        }
435       
436        private void initEnd(){
437                if (end == null){
438                        end = new Partial();
439                }
440        }
441       
442       
443        //patter for first year in string;
444        private static final Pattern firstYearPattern =  Pattern.compile("\\d{4}");
445        //case "1806"[1807];
446        private static final Pattern uncorrectYearPatter =  Pattern.compile("\"\\d{4}\"\\s*\\[\\d{4}\\]");
447        //case fl. 1806 or c. 1806 or fl. 1806?
448        private static final Pattern prefixedYearPattern =  Pattern.compile("(fl|c)\\.\\s*\\d{4}(\\s*-\\s*\\d{4})?\\??");
449        //standard
450        private static final Pattern standardPattern =  Pattern.compile("\\s*\\d{2,4}(\\s*-(\\s*\\d{2,4})?)?");
451        private static final String strDotDate = "[0-3]?\\d\\.[01]?\\d\\.\\d{4,4}";
452        private static final String strDotDatePeriodPattern = String.format("%s(\\s*-\\s*%s?)?", strDotDate, strDotDate);
453        private static final Pattern dotDatePattern =  Pattern.compile(strDotDatePeriodPattern);
454       
455       
456        public static TimePeriod parseString(TimePeriod timePeriod, String periodString){
457                //TODO move to parser class
458                //TODO until now only quick and dirty (and partly wrong)
459                TimePeriod result = timePeriod;
460               
461                if(timePeriod == null){
462                        return timePeriod;
463                }
464               
465                if (periodString == null){
466                        return result;
467                }
468                periodString = periodString.trim();
469               
470                result.setFreeText(null);
471                Date date;
472               
473                //case "1806"[1807];
474                if (uncorrectYearPatter.matcher(periodString).matches()){
475                        result.setFreeText(periodString);
476                        String realYear = periodString.split("\\[")[1];
477                        realYear = realYear.replace("]", "");
478                        result.setStartYear(Integer.valueOf(realYear));
479                        result.setFreeText(periodString);
480                //case fl. 1806 or c. 1806 or fl. 1806?
481                }else if(prefixedYearPattern.matcher(periodString).matches()){
482                        result.setFreeText(periodString);
483                        Matcher yearMatcher = firstYearPattern.matcher(periodString);
484                        yearMatcher.find();
485                        String startYear = yearMatcher.group();
486                        result.setStartYear(Integer.valueOf(startYear));
487                        if (yearMatcher.find()){
488                                String endYear = yearMatcher.group();
489                                result.setEndYear(Integer.valueOf(endYear));
490                        }
491                }else if (dotDatePattern.matcher(periodString).matches()){
492                        parseDotDatePattern(periodString, result);
493                }else if (standardPattern.matcher(periodString).matches()){
494                        parseStandardPattern(periodString, result);
495//TODO first check ambiguity of parser results e.g. for 7/12/11
496//              }else if (isDateString(periodString)){
497//                      String[] startEnd = makeStartEnd(periodString);
498//                      String start = startEnd[0];
499//                      DateTime startDateTime = dateStringParse(start, true);
500//                      result.setStart(startDateTime);
501//                      if (startEnd.length > 1){
502//                              DateTime endDateTime = dateStringParse(startEnd[1], true);
503//                              ;
504//                              result.setEnd(endDateTime.toLocalDate());
505//                      }
506                       
507                }else{
508                        result.setFreeText(periodString);
509                }
510                return result;
511        }
512
513        private static boolean isDateString(String periodString) {
514                String[] startEnd = makeStartEnd(periodString);
515                String start = startEnd[0];
516                DateTime startDateTime = dateStringParse(start, true);
517                if (startDateTime == null){
518                        return false;
519                }
520                if (startEnd.length > 1){
521                        DateTime endDateTime = dateStringParse(startEnd[1], true);
522                        if (endDateTime != null){
523                                return true;
524                        }
525                }
526                return false;
527        }
528
529
530        /**
531         * @param periodString
532         * @return
533         */
534        private static String[] makeStartEnd(String periodString) {
535                String[] startEnd = new String[]{periodString};
536                if (periodString.contains("-") && periodString.matches("^-{2,}-^-{2,}")){
537                        startEnd = periodString.split("-");
538                }
539                return startEnd;
540        }
541
542
543        private static DateTime dateStringParse(String string, boolean strict) {
544                DateFormat dateFormat = DateFormat.getDateInstance();
545                ParsePosition pos = new ParsePosition(0);
546                Date a = dateFormat.parse(string, pos);
547                if (a == null || pos.getIndex() != string.length()){
548                        return null;
549                }
550                Calendar cal = Calendar.getInstance();
551                cal.setTime(a);
552                DateTime result = new DateTime(cal);
553                return result;
554        }
555
556
557        /**
558         * @param periodString
559         * @param result
560         */
561        private static void parseDotDatePattern(String periodString,TimePeriod result) {
562                String[] dates = periodString.split("-");
563                Partial dtStart = null;
564                Partial dtEnd = null;
565               
566                if (dates.length > 2 || dates.length <= 0){
567                        logger.warn("More than 1 '-' in period String: " + periodString);
568                        result.setFreeText(periodString);
569                }else {
570                        try {
571                                //start
572                                if (! CdmUtils.isEmpty(dates[0])){
573                                        dtStart = parseSingleDotDate(dates[0].trim());
574                                }
575                               
576                                //end
577                                if (dates.length >= 2 && ! CdmUtils.isEmpty(dates[1])){
578                                        dtEnd = parseSingleDotDate(dates[1].trim());
579                                }
580                               
581                                result.setStart(dtStart);
582                                result.setEnd(dtEnd);
583                        } catch (IllegalArgumentException e) {
584                                //logger.warn(e.getMessage());
585                                result.setFreeText(periodString);
586                        }
587                }
588        }
589       
590       
591        /**
592         * @param periodString
593         * @param result
594         */
595        private static void parseStandardPattern(String periodString,
596                        TimePeriod result) {
597                String[] years = periodString.split("-");
598                Partial dtStart = null;
599                Partial dtEnd = null;
600               
601                if (years.length > 2 || years.length <= 0){
602                        logger.warn("More than 1 '-' in period String: " + periodString);
603                }else {
604                        try {
605                                //start
606                                if (! CdmUtils.isEmpty(years[0])){
607                                        dtStart = parseSingleDate(years[0].trim());
608                                }
609                               
610                                //end
611                                if (years.length >= 2 && ! CdmUtils.isEmpty(years[1])){
612                                        years[1] = years[1].trim();
613                                        if (years[1].length()==2 && dtStart != null && dtStart.isSupported(DateTimeFieldType.year())){
614                                                years[1] = String.valueOf(dtStart.get(DateTimeFieldType.year())/100) + years[1];
615                                        }
616                                        dtEnd = parseSingleDate(years[1]);
617                                }
618                               
619                                result.setStart(dtStart);
620                                result.setEnd(dtEnd);
621                        } catch (IllegalArgumentException e) {
622                                //logger.warn(e.getMessage());
623                                result.setFreeText(periodString);
624                        }
625                }
626        }
627       
628        public static TimePeriod parseString(String strPeriod) {
629                TimePeriod timePeriod = TimePeriod.NewInstance();
630                return parseString(timePeriod, strPeriod);
631        }
632       
633       
634        protected static Partial parseSingleDate(String singleDateString) throws IllegalArgumentException{
635                //FIXME until now only quick and dirty and incomplete
636                Partial partial =  new Partial();
637                singleDateString = singleDateString.trim();
638                if (CdmUtils.isNumeric(singleDateString)){
639                        try {
640                                Integer year = Integer.valueOf(singleDateString.trim());
641                                if (year < 1000 && year > 2100){
642                                        logger.warn("Not a valid year: " + year + ". Year must be between 1000 and 2100");
643                                }else if (year < 1700 && year > 2100){
644                                        logger.warn("Not a valid taxonomic year: " + year + ". Year must be between 1750 and 2100");
645                                        partial = partial.with(YEAR_TYPE, year);
646                                }else{
647                                        partial = partial.with(YEAR_TYPE, year);
648                                }
649                        } catch (NumberFormatException e) {
650                                logger.debug("Not a Integer format in getCalendar()");
651                                throw new IllegalArgumentException(e);
652                        }
653                }else{
654                        throw new IllegalArgumentException("Until now only years can be parsed as single dates. But date is: " + singleDateString);
655                }
656                return partial;
657
658        }
659       
660        protected static Partial parseSingleDotDate(String singleDateString) throws IllegalArgumentException{
661                Partial partial =  new Partial();
662                singleDateString = singleDateString.trim();
663                String[] split = singleDateString.split("\\.");
664                int length = split.length;
665                if (length > 3){
666                        throw new IllegalArgumentException(String.format("More than 2 dots in date '%s'", singleDateString));
667                }
668                String strYear = split[split.length-1];
669                String strMonth = length >= 2? split[split.length-2]: null;
670                String strDay = length >= 3? split[split.length-3]: null;
671               
672               
673                try {
674                        Integer year = Integer.valueOf(strYear.trim());
675                        Integer month = Integer.valueOf(strMonth.trim());
676                        Integer day = Integer.valueOf(strDay.trim());
677                        if (year < 1000 && year > 2100){
678                                logger.warn("Not a valid year: " + year + ". Year must be between 1000 and 2100");
679                        }else if (year < 1700 && year > 2100){
680                                logger.warn("Not a valid taxonomic year: " + year + ". Year must be between 1750 and 2100");
681                                partial = partial.with(YEAR_TYPE, year);
682                        }else{
683                                partial = partial.with(YEAR_TYPE, year);
684                        }
685                        if (month != null && month != 0){
686                                partial = partial.with(MONTH_TYPE, month);
687                        }
688                        if (day != null && day != 0){
689                                partial = partial.with(DAY_TYPE, day);
690                        }
691                } catch (NumberFormatException e) {
692                        logger.debug("Not a Integer format somewhere in " + singleDateString);
693                        throw new IllegalArgumentException(e);
694                }
695                return partial;
696
697        }
698       
699       
700       
701        private class TimePeriodPartialFormatter extends DateTimeFormatter{
702                private TimePeriodPartialFormatter(){
703                        super(null, null);
704                }
705                public String print(ReadablePartial partial){
706                        //TODO
707                        String result = "";
708                        String year = (partial.isSupported(YEAR_TYPE))? String.valueOf(partial.get(YEAR_TYPE)):null;
709                        String month = (partial.isSupported(MONTH_TYPE))? String.valueOf(partial.get(MONTH_TYPE)):null;;
710                        String day = (partial.isSupported(DAY_TYPE))? String.valueOf(partial.get(DAY_TYPE)):null;;
711                       
712                        if (month !=null){
713                                if (year == null){
714                                        year = "xxxx";
715                                }
716                        }
717                        if (day != null){
718                                if (month == null){
719                                        month = "xx";
720                                }
721                                if (year == null){
722                                        year = "xxxx";
723                                }
724                        }
725                        result = (day != null)? day + "." : "";
726                        result += (month != null)? month + "." : "";
727                        result += (year != null)? year : "";
728                       
729                        return result;
730                }
731               
732        }
733       
734//**************************** to String ****************************************       
735       
736        /**
737         * Returns the {@link #getFreeText()} value if free text is not <code>null</code>.
738         * Otherwise the concatenation of <code>start</code> and <code>end</code> is returned.
739         *
740         * @see java.lang.Object#toString()
741         */
742        public String toString(){
743                String result = null;
744                DateTimeFormatter formatter = new TimePeriodPartialFormatter();
745                if ( CdmUtils.isNotEmpty(this.getFreeText())){
746                        result = this.getFreeText();
747                }else{
748                        String strStart = start != null ? start.toString(formatter): null;
749                        String strEnd = end != null ? end.toString(formatter): null;
750                        result = CdmUtils.concat("-", strStart, strEnd);
751                }
752                return result;
753        }
754       
755//*********** EQUALS **********************************/       
756       
757
758        /* (non-Javadoc)
759         * @see java.lang.Object#equals(java.lang.Object)
760         */
761        @Override
762        public boolean equals(Object obj) {
763                if (obj == null){
764                        return false;
765                }
766                if (! (obj instanceof TimePeriod)){
767                        return false;
768                }
769                TimePeriod that = (TimePeriod)obj;
770               
771                if (! CdmUtils.nullSafeEqual(this.start, that.start)){
772                        return false;
773                }
774                if (! CdmUtils.nullSafeEqual(this.end, that.end)){
775                        return false;
776                }
777                if (! CdmUtils.nullSafeEqual(this.freeText, that.freeText)){
778                        return false;
779                }
780                return true;
781        }
782       
783        /* (non-Javadoc)
784         * @see java.lang.Object#hashCode()
785         */
786        @Override
787        public int hashCode() {
788                int hashCode = 7;
789                hashCode = 29*hashCode + 
790                                        (start== null? 33: start.hashCode()) + 
791                                        (end== null? 39: end.hashCode()) + 
792                                        (freeText== null? 41: freeText.hashCode()); 
793                return super.hashCode();
794        }       
795       
796       
797//*********** CLONE **********************************/
798       
799
800        /* (non-Javadoc)
801         * @see java.lang.Object#clone()
802         */
803        @Override
804        public Object clone()  {
805                try {
806                        TimePeriod result = (TimePeriod)super.clone();
807                        result.setStart(this.start);   //DateTime is immutable
808                        result.setEnd(this.end);       
809                        result.setFreeText(this.freeText);
810                        return result;
811                } catch (CloneNotSupportedException e) {
812                        logger.warn("Clone not supported exception. Should never occurr !!");
813                        return null;
814                }
815        }
816
817       
818}
Note: See TracBrowser for help on using the browser.