Project

General

Profile

Download (13.8 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 eu.etaxonomy.cdm.common.CdmUtils;
39
import eu.etaxonomy.cdm.hibernate.search.PartialBridge;
40
import eu.etaxonomy.cdm.jaxb.PartialAdapter;
41
import eu.etaxonomy.cdm.strategy.cache.common.TimePeriodPartialFormatter;
42

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

    
64
	@XmlElement(name = "Start")
65
	@XmlJavaTypeAdapter(value = PartialAdapter.class)
66
	@Type(type="partialUserType")
67
	@Field(analyze = Analyze.NO)
68
	@FieldBridge(impl = PartialBridge.class)
69
	private Partial start;
70

    
71
	@XmlElement(name = "End")
72
	@XmlJavaTypeAdapter(value = PartialAdapter.class)
73
	@Type(type="partialUserType")
74
	@Field(analyze = Analyze.NO)
75
	@FieldBridge(impl = PartialBridge.class)
76
	private Partial end;
77

    
78

    
79
	@XmlElement(name = "FreeText")
80
	private String freeText;
81

    
82
// ********************** FACTORY METHODS **************************/
83

    
84
	/**
85
	 * Factory method
86
	 * @return
87
	 */
88
	public static TimePeriod NewInstance(){
89
		return new TimePeriod();
90
	}
91

    
92

    
93
	/**
94
	 * Factory method
95
	 * @return
96
	 */
97
	public static TimePeriod NewInstance(Partial startDate){
98
		return new TimePeriod(startDate);
99
	}
100

    
101

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

    
110

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

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

    
136

    
137

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

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

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

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

    
190

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

    
207
//****************** CONVERTERS ******************/	
208

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

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

    
232

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

    
241

    
242
	
243
//*********************** CONSTRUCTOR *********************************/	
244
	
245
	/**
246
	 * Constructor
247
	 */
248
	protected TimePeriod() {
249
		super();
250
	}
251
	public TimePeriod(Partial startDate) {
252
		start=startDate;
253
	}
254
	public TimePeriod(Partial startDate, Partial endDate) {
255
		start=startDate;
256
		end=endDate;
257
	}
258

    
259
//******************* GETTER / SETTER ************************************/
260

    
261
	public Partial getStart() {
262
		return start;
263
	}
264

    
265
	public void setStart(Partial start) {
266
		this.start = start;
267
	}
268

    
269
	public Partial getEnd() {
270
		return end;
271
	}
272

    
273
	public void setEnd(Partial end) {
274
		this.end = end;
275
	}
276

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

    
289

    
290
	/**
291
	 * Use {@link #parseSingleDate(String)} for public use.
292
	 * @param freeText the freeText to set
293
	 */
294
	public void setFreeText(String freeText) {
295
		this.freeText = freeText;
296
	}
297

    
298
	
299
//******************* Transient METHODS ************************************/
300

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

    
316
	/**
317
	 * True, if there is no start date and no end date and no freetext representation exists.
318
	 * @return
319
	 */
320
	@Transient
321
	public boolean isEmpty(){
322
		if (StringUtils.isBlank(this.getFreeText()) && start == null  && end == null ){
323
			return true;
324
		}else{
325
			return false;
326
		}
327
	}
328

    
329

    
330

    
331
	@Transient
332
	public String getYear(){
333
		String result = "";
334
		if (getStartYear() != null){
335
			result += String.valueOf(getStartYear());
336
			if (getEndYear() != null){
337
				result += "-" + String.valueOf(getEndYear());
338
			}
339
		}else{
340
			if (getEndYear() != null){
341
				result += String.valueOf(getEndYear());
342
			}
343
		}
344
		return result;
345
	}
346

    
347
	@Transient
348
	public Integer getStartYear(){
349
		return getPartialValue(start, YEAR_TYPE);
350
	}
351

    
352
	@Transient
353
	public Integer getStartMonth(){
354
		return getPartialValue(start, MONTH_TYPE);
355
	}
356

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

    
362
	@Transient
363
	public Integer getEndYear(){
364
		return getPartialValue(end, YEAR_TYPE);
365
	}
366

    
367
	@Transient
368
	public Integer getEndMonth(){
369
		return getPartialValue(end, MONTH_TYPE);
370
	}
371

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

    
377
	public TimePeriod setStartYear(Integer year){
378
		return setStartField(year, YEAR_TYPE);
379
	}
380

    
381
	public TimePeriod setStartMonth(Integer month) throws IndexOutOfBoundsException{
382
		return setStartField(month, MONTH_TYPE);
383
	}
384

    
385
	public TimePeriod setStartDay(Integer day) throws IndexOutOfBoundsException{
386
		return setStartField(day, DAY_TYPE);
387
	}
388

    
389
	public TimePeriod setEndYear(Integer year){
390
		return setEndField(year, YEAR_TYPE);
391
	}
392

    
393
	public TimePeriod setEndMonth(Integer month) throws IndexOutOfBoundsException{
394
		return setEndField(month, MONTH_TYPE);
395
	}
396

    
397
	public TimePeriod setEndDay(Integer day) throws IndexOutOfBoundsException{
398
		return setEndField(day, DAY_TYPE);
399
	}
400

    
401
	public static Partial setPartialField(Partial partial, Integer value, DateTimeFieldType type)
402
			throws IndexOutOfBoundsException{
403
		if (partial == null){
404
			partial = new Partial();
405
		}
406
		if (value == null){
407
			return partial.without(type);
408
		}else{
409
			checkFieldValues(value, type, partial);
410
			return partial.with(type, value);
411
		}
412
	}
413

    
414
	@Transient
415
	private TimePeriod setStartField(Integer value, DateTimeFieldType type)
416
			throws IndexOutOfBoundsException{
417
		start = setPartialField(start, value, type);
418
		return this;
419
	}
420

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

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

    
462
	private void initStart(){
463
		if (start == null){
464
			start = new Partial();
465
		}
466
	}
467

    
468
	private void initEnd(){
469
		if (end == null){
470
			end = new Partial();
471
		}
472
	}
473

    
474

    
475
//**************************** to String ****************************************
476

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

    
497
//*********** EQUALS **********************************/
498

    
499

    
500
	/* (non-Javadoc)
501
	 * @see java.lang.Object#equals(java.lang.Object)
502
	 */
503
	@Override
504
	public boolean equals(Object obj) {
505
		if (obj == null){
506
			return false;
507
		}
508
		if (! (obj instanceof TimePeriod)){
509
			return false;
510
		}
511
		TimePeriod that = (TimePeriod)obj;
512

    
513
		if (! CdmUtils.nullSafeEqual(this.start, that.start)){
514
			return false;
515
		}
516
		if (! CdmUtils.nullSafeEqual(this.end, that.end)){
517
			return false;
518
		}
519
		if (! CdmUtils.nullSafeEqual(this.freeText, that.freeText)){
520
			return false;
521
		}
522
		return true;
523
	}
524

    
525
	/* (non-Javadoc)
526
	 * @see java.lang.Object#hashCode()
527
	 */
528
	@Override
529
	public int hashCode() {
530
		int hashCode = 7;
531
		hashCode = 29*hashCode +
532
					(start== null? 33: start.hashCode()) +
533
					(end== null? 39: end.hashCode()) +
534
					(freeText== null? 41: freeText.hashCode());
535
		return super.hashCode();
536
	}
537

    
538

    
539
//*********** CLONE **********************************/
540

    
541

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

    
559

    
560
}
(63-63/70)