Project

General

Profile

Download (16.1 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.Transient;
18
import javax.xml.bind.annotation.XmlAccessType;
19
import javax.xml.bind.annotation.XmlAccessorType;
20
import javax.xml.bind.annotation.XmlElement;
21
import javax.xml.bind.annotation.XmlRootElement;
22
import javax.xml.bind.annotation.XmlType;
23
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
24

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

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

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

    
45
/**
46
 * @author m.doering
47
 * @created 08-Nov-2007 13:07:00
48
 * @updated 05-Dec-2008 23:00:05
49
 * @updated 14-Jul-2013 move parser methods to TimePeriodParser
50
 */
51
@XmlAccessorType(XmlAccessType.FIELD)
52
@XmlType(name = "TimePeriod", propOrder = {
53
    "start",
54
    "end",
55
    "freeText"
56
})
57
@XmlRootElement(name = "TimePeriod")
58
@Embeddable
59
public class TimePeriod implements Cloneable, Serializable {
60
    private static final long serialVersionUID = 3405969418194981401L;
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(analyze = Analyze.NO)
70
    @FieldBridge(impl = PartialBridge.class)
71
    @JsonIgnore // currently used for swagger model scanner
72
    private Partial start;
73

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

    
82

    
83
    @XmlElement(name = "FreeText")
84
    private String freeText;
85

    
86
// ********************** FACTORY METHODS **************************/
87

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

    
96

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

    
105

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

    
114

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

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

    
140

    
141

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

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

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

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

    
194

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

    
211
//****************** CONVERTERS ******************/
212

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

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

    
236

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

    
245

    
246

    
247
//*********************** CONSTRUCTOR *********************************/
248

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

    
263
//******************* GETTER / SETTER ************************************/
264

    
265

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

    
271
    public void setStart(Partial start) {
272
        this.start = start;
273
    }
274

    
275

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

    
281
    public void setEnd(Partial end) {
282
        this.end = end;
283
    }
284

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

    
297

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

    
306

    
307
//******************* Transient METHODS ************************************/
308

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

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

    
337

    
338

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
436
// ******************************** internal methods *******************************/
437

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

    
470
    private void initStart(){
471
        if (start == null){
472
            start = new Partial();
473
        }
474
    }
475

    
476
    private void initEnd(){
477
        if (end == null){
478
            end = new Partial();
479
        }
480
    }
481

    
482

    
483
//**************************** to String ****************************************
484

    
485
    /**
486
     * Returns the {@link #getFreeText()} value if free text is not <code>null</code>.
487
     * Otherwise the concatenation of <code>start</code> and <code>end</code> is returned.
488
     *
489
     * @see java.lang.Object#toString()
490
     */
491
    @Override
492
    public String toString(){
493
        String result = null;
494
        DateTimeFormatter formatter = TimePeriodPartialFormatter.NewInstance();
495
        if ( StringUtils.isNotBlank(this.getFreeText())){
496
            result = this.getFreeText();
497
        }else{
498
            String strStart = start != null ? start.toString(formatter): null;
499
            String strEnd = end != null ? end.toString(formatter): null;
500
            result = CdmUtils.concat("-", strStart, strEnd);
501
        }
502
        return result;
503
    }
504

    
505
//*********** EQUALS **********************************/
506

    
507

    
508
    /* (non-Javadoc)
509
     * @see java.lang.Object#equals(java.lang.Object)
510
     */
511
    @Override
512
    public boolean equals(Object obj) {
513
        if (obj == null){
514
            return false;
515
        }
516
        if (! (obj instanceof TimePeriod)){
517
            return false;
518
        }
519
        TimePeriod that = (TimePeriod)obj;
520

    
521
        if (! CdmUtils.nullSafeEqual(this.start, that.start)){
522
            return false;
523
        }
524
        if (! CdmUtils.nullSafeEqual(this.end, that.end)){
525
            return false;
526
        }
527
        if (! CdmUtils.nullSafeEqual(this.freeText, that.freeText)){
528
            return false;
529
        }
530
        return true;
531
    }
532

    
533
    /* (non-Javadoc)
534
     * @see java.lang.Object#hashCode()
535
     */
536
    @Override
537
    public int hashCode() {
538
        int hashCode = 7;
539
        hashCode = 29*hashCode +
540
                    (start== null? 33: start.hashCode()) +
541
                    (end== null? 39: end.hashCode()) +
542
                    (freeText== null? 41: freeText.hashCode());
543
        return super.hashCode();
544
    }
545

    
546

    
547
//*********** CLONE **********************************/
548

    
549

    
550
    /* (non-Javadoc)
551
     * @see java.lang.Object#clone()
552
     */
553
    @Override
554
    public Object clone()  {
555
        try {
556
            TimePeriod result = (TimePeriod)super.clone();
557
            result.setStart(this.start);   //DateTime is immutable
558
            result.setEnd(this.end);
559
            result.setFreeText(this.freeText);
560
            return result;
561
        } catch (CloneNotSupportedException e) {
562
            logger.warn("Clone not supported exception. Should never occurr !!");
563
            return null;
564
        }
565
    }
566

    
567

    
568
}
(66-66/72)