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.codehaus.jackson.annotate.JsonIgnore;
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 eu.etaxonomy.cdm.common.CdmUtils;
40
import eu.etaxonomy.cdm.hibernate.search.PartialBridge;
41
import eu.etaxonomy.cdm.jaxb.PartialAdapter;
42
import eu.etaxonomy.cdm.strategy.cache.common.TimePeriodPartialFormatter;
43

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

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

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

    
81

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

    
85
// ********************** FACTORY METHODS **************************/
86

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

    
95

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

    
104

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

    
113

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

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

    
139

    
140

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

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

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

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

    
193

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

    
210
//****************** CONVERTERS ******************/
211

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

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

    
235

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

    
244

    
245

    
246
//*********************** CONSTRUCTOR *********************************/
247

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

    
262
//******************* GETTER / SETTER ************************************/
263

    
264

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

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

    
274

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

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

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

    
296

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

    
305

    
306
//******************* Transient METHODS ************************************/
307

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

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

    
336

    
337

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
435
// ******************************** internal methods *******************************/
436

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

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

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

    
481

    
482
//**************************** to String ****************************************
483

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

    
504
//*********** EQUALS **********************************/
505

    
506

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

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

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

    
545

    
546
//*********** CLONE **********************************/
547

    
548

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

    
566

    
567
}
(64-64/71)