Project

General

Profile

Download (16.7 KB) Statistics
| Branch: | Tag: | Revision:
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

    
10
package eu.etaxonomy.cdm.model.common;
11

    
12
import java.io.Serializable;
13
import java.util.Calendar;
14
import java.util.Date;
15

    
16
import javax.persistence.Embeddable;
17
import javax.persistence.MappedSuperclass;
18
import javax.persistence.Transient;
19
import javax.xml.bind.annotation.XmlAccessType;
20
import javax.xml.bind.annotation.XmlAccessorType;
21
import javax.xml.bind.annotation.XmlElement;
22
import javax.xml.bind.annotation.XmlRootElement;
23
import javax.xml.bind.annotation.XmlType;
24
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
25

    
26
import org.apache.commons.lang.StringUtils;
27
import org.apache.log4j.Logger;
28
import org.hibernate.annotations.Type;
29
import org.hibernate.search.annotations.Analyze;
30
import org.hibernate.search.annotations.Field;
31
import org.hibernate.search.annotations.FieldBridge;
32
import org.joda.time.DateTime;
33
import org.joda.time.DateTimeFieldType;
34
import org.joda.time.LocalDate;
35
import org.joda.time.Partial;
36
import org.joda.time.ReadableInstant;
37
import org.joda.time.format.DateTimeFormatter;
38

    
39
import com.fasterxml.jackson.annotation.JsonIgnore;
40

    
41
import eu.etaxonomy.cdm.common.CdmUtils;
42
import eu.etaxonomy.cdm.hibernate.search.PartialBridge;
43
import eu.etaxonomy.cdm.jaxb.PartialAdapter;
44
import eu.etaxonomy.cdm.strategy.cache.common.TimePeriodPartialFormatter;
45

    
46
/**
47
 * @author m.doering
48
 * @since 08-Nov-2007 13:07:00
49
 * @updated 05-Dec-2008 23:00:05
50
 * @updated 14-Jul-2013 move parser methods to TimePeriodParser
51
 */
52
@XmlAccessorType(XmlAccessType.FIELD)
53
@XmlType(name = "TimePeriod", propOrder = {
54
    "start",
55
    "end",
56
    "freeText"
57
})
58
@XmlRootElement(name = "TimePeriod")
59
@Embeddable
60
@MappedSuperclass
61
public class TimePeriod implements Cloneable, Serializable {
62
    private static final long serialVersionUID = 3405969418194981401L;
63
    private static final Logger logger = Logger.getLogger(TimePeriod.class);
64
    public static final DateTimeFieldType MONTH_TYPE = DateTimeFieldType.monthOfYear();
65
    public static final DateTimeFieldType YEAR_TYPE = DateTimeFieldType.year();
66
    public static final DateTimeFieldType DAY_TYPE = DateTimeFieldType.dayOfMonth();
67

    
68
    @XmlElement(name = "Start")
69
    @XmlJavaTypeAdapter(value = PartialAdapter.class)
70
    @Type(type="partialUserType")
71
    @Field(analyze = Analyze.NO)
72
    @FieldBridge(impl = PartialBridge.class)
73
    @JsonIgnore // currently used for swagger model scanner
74
    private Partial start;
75

    
76
    @XmlElement(name = "End")
77
    @XmlJavaTypeAdapter(value = PartialAdapter.class)
78
    @Type(type="partialUserType")
79
    @Field(analyze = Analyze.NO)
80
    @FieldBridge(impl = PartialBridge.class)
81
    @JsonIgnore // currently used for swagger model scanner
82
    private Partial end;
83

    
84

    
85
    @XmlElement(name = "FreeText")
86
    private String freeText;
87

    
88
// ********************** FACTORY METHODS **************************/
89

    
90
    /**
91
     * Factory method
92
     * @return
93
     */
94
    public static final TimePeriod NewInstance(){
95
        return new TimePeriod();
96
    }
97

    
98

    
99
    /**
100
     * Factory method
101
     * @return
102
     */
103
    public static final TimePeriod NewInstance(Partial startDate){
104
        return new TimePeriod(startDate);
105
    }
106

    
107

    
108
    /**
109
     * Factory method
110
     * @return
111
     */
112
    public static final TimePeriod NewInstance(Partial startDate, Partial endDate){
113
        return new TimePeriod(startDate, endDate);
114
    }
115

    
116

    
117
    /**
118
     * Factory method
119
     * @return
120
     */
121
    public static final TimePeriod NewInstance(Integer year){
122
        Integer endYear = null;
123
        return NewInstance(year, endYear);
124
    }
125

    
126
    /**
127
     * Factory method
128
     * @return
129
     */
130
    public static final TimePeriod NewInstance(Integer startYear, Integer endYear){
131
        Partial startDate = null;
132
        Partial endDate = null;
133
        if (startYear != null){
134
            startDate = new Partial().with(YEAR_TYPE, startYear);
135
        }
136
        if (endYear != null){
137
            endDate = new Partial().with(YEAR_TYPE, endYear);
138
        }
139
        return new TimePeriod(startDate, endDate);
140
    }
141

    
142

    
143

    
144
    /**
145
     * Factory method to create a TimePeriod from a <code>Calendar</code>. The Calendar is stored as the starting instant.
146
     * @return
147
     */
148
    public static final TimePeriod NewInstance(Calendar startCalendar){
149
        return NewInstance(startCalendar, null);
150
    }
151

    
152
    /**
153
     * Factory method to create a TimePeriod from a <code>ReadableInstant</code>(e.g. <code>DateTime</code>).
154
     * The <code>ReadableInstant</code> is stored as the starting instant.
155
     * @return
156
     */
157
    public static final TimePeriod NewInstance(ReadableInstant readableInstant){
158
        return NewInstance(readableInstant, null);
159
    }
160

    
161
    /**
162
     * Factory method to create a TimePeriod from a starting and an ending <code>Calendar</code>
163
     * @return
164
     */
165
    public static final TimePeriod NewInstance(Calendar startCalendar, Calendar endCalendar){
166
        Partial startDate = null;
167
        Partial endDate = null;
168
        if (startCalendar != null){
169
            startDate = calendarToPartial(startCalendar);
170
        }
171
        if (endCalendar != null){
172
            endDate = calendarToPartial(endCalendar);
173
        }
174
        return new TimePeriod(startDate, endDate);
175
    }
176

    
177
    /**
178
     * Factory method to create a TimePeriod from a starting and an ending <code>Date</code>
179
     * @return TimePeriod
180
     */
181
    public static final TimePeriod NewInstance(Date startDate, Date endDate){
182
        //TODO conversion untested, implemented according to http://www.roseindia.net/java/java-conversion/datetocalender.shtml
183
        Calendar calStart = null;
184
        Calendar calEnd = null;
185
        if (startDate != null){
186
            calStart = Calendar.getInstance();
187
            calStart.setTime(startDate);
188
        }
189
        if (endDate != null){
190
            calEnd = Calendar.getInstance();
191
            calEnd.setTime(endDate);
192
        }
193
        return NewInstance(calStart, calEnd);
194
    }
195

    
196

    
197
    /**
198
     * Factory method to create a TimePeriod from a starting and an ending <code>ReadableInstant</code>(e.g. <code>DateTime</code>)
199
     * @return
200
     */
201
    public static final TimePeriod NewInstance(ReadableInstant startInstant, ReadableInstant endInstant){
202
        Partial startDate = null;
203
        Partial endDate = null;
204
        if (startInstant != null){
205
            startDate = readableInstantToPartial(startInstant);
206
        }
207
        if (endInstant != null){
208
            endDate = readableInstantToPartial(endInstant);
209
        }
210
        return new TimePeriod(startDate, endDate);
211
    }
212

    
213
//****************** CONVERTERS ******************/
214

    
215
    /**
216
     * Transforms a {@link Calendar} into a <code>Partial</code>
217
     * @param calendar
218
     * @return
219
     */
220
    public static Partial calendarToPartial(Calendar calendar){
221
        LocalDate ld = new LocalDate(calendar);
222
        Partial partial = new Partial(ld);
223
        return partial;
224
    }
225

    
226
    /**
227
     * Transforms a {@link ReadableInstant} into a <code>Partial</code>
228
     * @param calendar
229
     * @return
230
     */
231
    public static Partial readableInstantToPartial(ReadableInstant readableInstant){
232
        DateTime dt = readableInstant.toInstant().toDateTime();
233
        LocalDate ld = dt.toLocalDate();
234
        Partial partial = new Partial(ld);
235
        return partial;
236
    }
237

    
238

    
239
    public static Integer getPartialValue(Partial partial, DateTimeFieldType type){
240
        if (partial == null || ! partial.isSupported(type)){
241
            return null;
242
        }else{
243
            return partial.get(type);
244
        }
245
    }
246

    
247

    
248

    
249
//*********************** CONSTRUCTOR *********************************/
250

    
251
    /**
252
     * Constructor
253
     */
254
    protected TimePeriod() {
255
        super();
256
    }
257
    public TimePeriod(Partial startDate) {
258
        start=startDate;
259
    }
260
    public TimePeriod(Partial startDate, Partial endDate) {
261
        start=startDate;
262
        end=endDate;
263
    }
264

    
265
//******************* GETTER / SETTER ************************************/
266

    
267

    
268
    @JsonIgnore // currently used for swagger model scanner
269
    public Partial getStart() {
270
        return start;
271
    }
272

    
273
    public void setStart(Partial start) {
274
        this.start = start;
275
    }
276

    
277

    
278
    @JsonIgnore // currently used for swagger model scanner
279
    public Partial getEnd() {
280
        return end;
281
    }
282

    
283
    public void setEnd(Partial end) {
284
        this.end = end;
285
    }
286

    
287
    /**
288
     * For time periods that need to store more information than the one
289
     * that can be stored in <code>start</code> and <code>end</code>.
290
     * If free text is not <code>null</null> {@link #toString()} will always
291
     * return the free text value.
292
     * <BR>Use {@link #toString()} for public use.
293
     * @return the freeText
294
     */
295
    public String getFreeText() {
296
        return freeText;
297
    }
298

    
299

    
300
    /**
301
     * Use {@link #parseSingleDate(String)} for public use.
302
     * @param freeText the freeText to set
303
     */
304
    public void setFreeText(String freeText) {
305
        this.freeText = freeText;
306
    }
307

    
308

    
309
//******************* Transient METHODS ************************************/
310

    
311
    /**
312
     * True, if this time period represents a period not a single point in time.
313
     * This is by definition, that the time period has a start and an end value,
314
     * and both have a year value that is not null
315
     * @return
316
     */
317
    @Transient
318
    public boolean isPeriod(){
319
        if (getStartYear() != null && getEndYear() != null ){
320
            return true;
321
        }else{
322
            return false;
323
        }
324
    }
325

    
326
    /**
327
     * True, if there is no start date and no end date and no freetext representation exists.
328
     * @return
329
     */
330
    @Transient
331
    public boolean isEmpty(){
332
        if (StringUtils.isBlank(this.getFreeText()) && start == null  && end == null ){
333
            return true;
334
        }else{
335
            return false;
336
        }
337
    }
338

    
339

    
340

    
341
    @Transient
342
    public String getYear(){
343
        String result = "";
344
        if (getStartYear() != null){
345
            result += String.valueOf(getStartYear());
346
            if (getEndYear() != null){
347
                result += "-" + String.valueOf(getEndYear());
348
            }
349
        }else{
350
            if (getEndYear() != null){
351
                result += String.valueOf(getEndYear());
352
            }
353
        }
354
        return result;
355
    }
356

    
357
    @Transient
358
    public Integer getStartYear(){
359
        return getPartialValue(start, YEAR_TYPE);
360
    }
361

    
362
    @Transient
363
    public Integer getStartMonth(){
364
        return getPartialValue(start, MONTH_TYPE);
365
    }
366

    
367
    @Transient
368
    public Integer getStartDay(){
369
        return getPartialValue(start, DAY_TYPE);
370
    }
371

    
372
    @Transient
373
    public Integer getEndYear(){
374
        return getPartialValue(end, YEAR_TYPE);
375
    }
376

    
377
    @Transient
378
    public Integer getEndMonth(){
379
        return getPartialValue(end, MONTH_TYPE);
380
    }
381

    
382
    @Transient
383
    public Integer getEndDay(){
384
        return getPartialValue(end, DAY_TYPE);
385
    }
386

    
387
    public TimePeriod setStartYear(Integer year){
388
        return setStartField(year, YEAR_TYPE);
389
    }
390

    
391
    public TimePeriod setStartMonth(Integer month) throws IndexOutOfBoundsException{
392
        return setStartField(month, MONTH_TYPE);
393
    }
394

    
395
    public TimePeriod setStartDay(Integer day) throws IndexOutOfBoundsException{
396
        return setStartField(day, DAY_TYPE);
397
    }
398

    
399
    public TimePeriod setEndYear(Integer year){
400
        return setEndField(year, YEAR_TYPE);
401
    }
402

    
403
    public TimePeriod setEndMonth(Integer month) throws IndexOutOfBoundsException{
404
        return setEndField(month, MONTH_TYPE);
405
    }
406

    
407
    public TimePeriod setEndDay(Integer day) throws IndexOutOfBoundsException{
408
        return setEndField(day, DAY_TYPE);
409
    }
410

    
411
    public static Partial setPartialField(Partial partial, Integer value, DateTimeFieldType type)
412
            throws IndexOutOfBoundsException{
413
        if (partial == null){
414
            partial = new Partial();
415
        }
416
        if (value == null){
417
            return partial.without(type);
418
        }else{
419
            checkFieldValues(value, type, partial);
420
            return partial.with(type, value);
421
        }
422
    }
423

    
424
    @Transient
425
    private TimePeriod setStartField(Integer value, DateTimeFieldType type)
426
            throws IndexOutOfBoundsException{
427
        start = setPartialField(start, value, type);
428
        return this;
429
    }
430

    
431
    @Transient
432
    private TimePeriod setEndField(Integer value, DateTimeFieldType type)
433
            throws IndexOutOfBoundsException{
434
        end = setPartialField(end, value, type);
435
        return this;
436
    }
437

    
438
// ******************************** internal methods *******************************/
439

    
440
    /**
441
     * Throws an IndexOutOfBoundsException if the value does not have a valid value
442
     * (e.g. month > 12, month < 1, day > 31, etc.)
443
     * @param value
444
     * @param type
445
     * @throws IndexOutOfBoundsException
446
     */
447
    private static void checkFieldValues(Integer value, DateTimeFieldType type, Partial partial)
448
            throws IndexOutOfBoundsException{
449
        int max = 9999999;
450
        if (type.equals(MONTH_TYPE)){
451
            max = 12;
452
        }
453
        if (type.equals(DAY_TYPE)){
454
            max = 31;
455
            Integer month = null;
456
            if (partial.isSupported(MONTH_TYPE)){
457
                month = partial.get(MONTH_TYPE);
458
            }
459
            if (month != null){
460
                if (month == 2){
461
                    max = 29;
462
                }else if (month == 4 ||month == 6 ||month == 9 ||month == 11){
463
                    max = 30;
464
                }
465
            }
466
        }
467
        if ( (value < 1 || value > max) ){
468
            throw new IndexOutOfBoundsException("Value must be between 1 and " +  max);
469
        }
470
    }
471

    
472

    
473
//**************************** to String ****************************************
474

    
475
    /**
476
     * Returns the {@link #getFreeText()} value if free text is not <code>null</code>.
477
     * Otherwise the concatenation of <code>start</code> and <code>end</code> is returned.
478
     *
479
     * @see java.lang.Object#toString()
480
     */
481
    @Override
482
    public String toString(){
483
        String result = null;
484
//        DateTimeFormatter formatter = TimePeriodPartialFormatter.NewInstance();
485
        if ( StringUtils.isNotBlank(this.getFreeText())){
486
            result = this.getFreeText();
487
        }else{
488
            result = getTimePeriod();
489
        }
490
        return result;
491
    }
492

    
493
    /**
494
     * Returns the concatenation of <code>start</code> and <code>end</code>
495
     *
496
     */
497
    public String getTimePeriod(){
498
        String result = null;
499
        DateTimeFormatter formatter = TimePeriodPartialFormatter.NewInstance();
500
        String strStart = start != null ? start.toString(formatter): null;
501
        String strEnd = end != null ? end.toString(formatter): null;
502
        result = CdmUtils.concat("-", strStart, strEnd);
503

    
504
        return result;
505
    }
506

    
507
//*********** EQUALS **********************************/
508

    
509
    @Override
510
    public boolean equals(Object obj) {
511
        if (obj == null){
512
            return false;
513
        }
514
        if (! (obj instanceof TimePeriod)){
515
            return false;
516
        }
517
        TimePeriod that = (TimePeriod)obj;
518

    
519
        if (! CdmUtils.nullSafeEqual(this.start, that.start)){
520
            return false;
521
        }
522
        if (! CdmUtils.nullSafeEqual(this.end, that.end)){
523
            return false;
524
        }
525
        if (! CdmUtils.nullSafeEqual(this.freeText, that.freeText)){
526
            return false;
527
        }
528
        //see comment in verbatimTimePeriod#equals
529
        String thisVerbatimDate = (this instanceof VerbatimTimePeriod)?
530
                ((VerbatimTimePeriod)this).getVerbatimDate():null;
531
        String thatVerbatimDate = (obj instanceof VerbatimTimePeriod)?
532
                ((VerbatimTimePeriod)obj).getVerbatimDate():null;
533
        if (! CdmUtils.nullSafeEqual(thisVerbatimDate, thatVerbatimDate)){
534
            return false;
535
        }
536
        return true;
537
    }
538

    
539
    @Override
540
    public int hashCode() {
541
        int hashCode = 7;
542
        hashCode = 29*hashCode +
543
                    (start== null? 33: start.hashCode()) +
544
                    (end== null? 39: end.hashCode()) +
545
                    (freeText== null? 41: freeText.hashCode());
546
        return hashCode;
547
    }
548

    
549

    
550
//*********** CLONE **********************************/
551

    
552
    @Override
553
    public Object clone()  {
554
        try {
555
            TimePeriod result = (TimePeriod)super.clone();
556
            copyCloned(this, result);
557
            return result;
558
        } catch (CloneNotSupportedException e) {
559
            logger.warn("Clone not supported exception. Should never occurr !!");
560
            return null;
561
        }
562
    }
563

    
564

    
565
    /**
566
     * @param result
567
     */
568
    protected static void copyCloned(TimePeriod origin, TimePeriod target) {
569
        target.setStart(origin.start);   //DateTime is immutable
570
        target.setEnd(origin.end);
571
        target.setFreeText(origin.freeText);
572
    }
573

    
574

    
575
}
(71-71/80)