Project

General

Profile

Download (21.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.text.DateFormat;
14
import java.text.ParsePosition;
15
import java.util.Calendar;
16
import java.util.Date;
17
import java.util.regex.Matcher;
18
import java.util.regex.Pattern;
19

    
20
import javax.persistence.Embeddable;
21
import javax.persistence.Transient;
22
import javax.xml.bind.annotation.XmlAccessType;
23
import javax.xml.bind.annotation.XmlAccessorType;
24
import javax.xml.bind.annotation.XmlElement;
25
import javax.xml.bind.annotation.XmlRootElement;
26
import javax.xml.bind.annotation.XmlType;
27
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
28

    
29
import org.apache.commons.lang.time.DateUtils;
30
import org.apache.log4j.Logger;
31
import org.hibernate.annotations.Type;
32
import org.hibernate.search.annotations.Field;
33
import org.hibernate.search.annotations.FieldBridge;
34
import org.joda.time.DateTime;
35
import org.joda.time.DateTimeFieldType;
36
import org.joda.time.LocalDate;
37
import org.joda.time.Partial;
38
import org.joda.time.ReadableInstant;
39
import org.joda.time.ReadablePartial;
40
import org.joda.time.format.DateTimeFormatter;
41

    
42
import eu.etaxonomy.cdm.common.CdmUtils;
43
import eu.etaxonomy.cdm.hibernate.PartialBridge;
44
import 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
60
public 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
}
(56-56/63)