- no change (just updated "last edited" for svn)
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / TimePeriod.java
index 50db153cef6537708260b6d1d858f1422437531b..4b46b648c744dd19a270ab651f0786f7b6970a2c 100644 (file)
@@ -1,8 +1,8 @@
 /**
 * Copyright (C) 2007 EDIT
-* European Distributed Institute of Taxonomy 
+* European Distributed Institute of Taxonomy
 * http://www.e-taxonomy.eu
-* 
+*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * See LICENSE.TXT at the top of this package for the full license terms.
 */
@@ -11,8 +11,7 @@ package eu.etaxonomy.cdm.model.common;
 
 import java.io.Serializable;
 import java.util.Calendar;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.Date;
 
 import javax.persistence.Embeddable;
 import javax.persistence.Transient;
@@ -23,8 +22,10 @@ import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.hibernate.annotations.Type;
+import org.hibernate.search.annotations.Analyze;
 import org.hibernate.search.annotations.Field;
 import org.hibernate.search.annotations.FieldBridge;
 import org.joda.time.DateTime;
@@ -32,18 +33,18 @@ import org.joda.time.DateTimeFieldType;
 import org.joda.time.LocalDate;
 import org.joda.time.Partial;
 import org.joda.time.ReadableInstant;
-import org.joda.time.ReadablePartial;
 import org.joda.time.format.DateTimeFormatter;
 
 import eu.etaxonomy.cdm.common.CdmUtils;
-import eu.etaxonomy.cdm.hibernate.PartialBridge;
+import eu.etaxonomy.cdm.hibernate.search.PartialBridge;
 import eu.etaxonomy.cdm.jaxb.PartialAdapter;
+import eu.etaxonomy.cdm.strategy.cache.common.TimePeriodPartialFormatter;
 
 /**
  * @author m.doering
- * @version 1.0
  * @created 08-Nov-2007 13:07:00
  * @updated 05-Dec-2008 23:00:05
+ * @updated 14-Jul-2013 move parser methods to TimePeriodParser
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "TimePeriod", propOrder = {
@@ -54,30 +55,32 @@ import eu.etaxonomy.cdm.jaxb.PartialAdapter;
 @XmlRootElement(name = "TimePeriod")
 @Embeddable
 public class TimePeriod implements Cloneable, Serializable {
+       private static final long serialVersionUID = 3405969418194981401L;
        private static final Logger logger = Logger.getLogger(TimePeriod.class);
        public static final DateTimeFieldType MONTH_TYPE = DateTimeFieldType.monthOfYear();
        public static final DateTimeFieldType YEAR_TYPE = DateTimeFieldType.year();
        public static final DateTimeFieldType DAY_TYPE = DateTimeFieldType.dayOfMonth();
-       
+
        @XmlElement(name = "Start")
        @XmlJavaTypeAdapter(value = PartialAdapter.class)
        @Type(type="partialUserType")
-       @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
+       @Field(analyze = Analyze.NO)
        @FieldBridge(impl = PartialBridge.class)
        private Partial start;
-       
+
        @XmlElement(name = "End")
        @XmlJavaTypeAdapter(value = PartialAdapter.class)
        @Type(type="partialUserType")
-       @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
+       @Field(analyze = Analyze.NO)
        @FieldBridge(impl = PartialBridge.class)
        private Partial end;
 
-       
+
        @XmlElement(name = "FreeText")
        private String freeText;
-       
-       
+
+// ********************** FACTORY METHODS **************************/
+
        /**
         * Factory method
         * @return
@@ -85,8 +88,8 @@ public class TimePeriod implements Cloneable, Serializable {
        public static TimePeriod NewInstance(){
                return new TimePeriod();
        }
-       
-       
+
+
        /**
         * Factory method
         * @return
@@ -94,8 +97,8 @@ public class TimePeriod implements Cloneable, Serializable {
        public static TimePeriod NewInstance(Partial startDate){
                return new TimePeriod(startDate);
        }
-       
-       
+
+
        /**
         * Factory method
         * @return
@@ -103,8 +106,8 @@ public class TimePeriod implements Cloneable, Serializable {
        public static TimePeriod NewInstance(Partial startDate, Partial endDate){
                return new TimePeriod(startDate, endDate);
        }
-       
-       
+
+
        /**
         * Factory method
         * @return
@@ -113,7 +116,7 @@ public class TimePeriod implements Cloneable, Serializable {
                Integer endYear = null;
                return NewInstance(year, endYear);
        }
-       
+
        /**
         * Factory method
         * @return
@@ -130,10 +133,10 @@ public class TimePeriod implements Cloneable, Serializable {
                return new TimePeriod(startDate, endDate);
        }
 
-       
-       
+
+
        /**
-        * Factory method to create a TimePeriod from a <code>Calendar</code>. The Calendar is stored as the starting instant.   
+        * Factory method to create a TimePeriod from a <code>Calendar</code>. The Calendar is stored as the starting instant.
         * @return
         */
        public static TimePeriod NewInstance(Calendar startCalendar){
@@ -142,15 +145,15 @@ public class TimePeriod implements Cloneable, Serializable {
 
        /**
         * Factory method to create a TimePeriod from a <code>ReadableInstant</code>(e.g. <code>DateTime</code>).
-        * The <code>ReadableInstant</code> is stored as the starting instant.   
+        * The <code>ReadableInstant</code> is stored as the starting instant.
         * @return
         */
        public static TimePeriod NewInstance(ReadableInstant readableInstant){
                return NewInstance(readableInstant, null);
        }
-       
+
        /**
-        * Factory method to create a TimePeriod from a starting and an ending <code>Calendar</code>   
+        * Factory method to create a TimePeriod from a starting and an ending <code>Calendar</code>
         * @return
         */
        public static TimePeriod NewInstance(Calendar startCalendar, Calendar endCalendar){
@@ -165,9 +168,28 @@ public class TimePeriod implements Cloneable, Serializable {
                return new TimePeriod(startDate, endDate);
        }
 
-       
        /**
-        * Factory method to create a TimePeriod from a starting and an ending <code>ReadableInstant</code>(e.g. <code>DateTime</code>)   
+        * Factory method to create a TimePeriod from a starting and an ending <code>Date</code>
+        * @return TimePeriod
+        */
+       public static TimePeriod NewInstance(Date startDate, Date endDate){
+               //TODO conversion untested, implemented according to http://www.roseindia.net/java/java-conversion/datetocalender.shtml
+               Calendar calStart = null;
+               Calendar calEnd = null;
+               if (startDate != null){
+                       calStart = Calendar.getInstance();
+                       calStart.setTime(startDate);
+               }
+               if (endDate != null){
+                       calEnd = Calendar.getInstance();
+                       calEnd.setTime(endDate);
+               }
+               return NewInstance(calStart, calEnd);
+       }
+
+
+       /**
+        * Factory method to create a TimePeriod from a starting and an ending <code>ReadableInstant</code>(e.g. <code>DateTime</code>)
         * @return
         */
        public static TimePeriod NewInstance(ReadableInstant startInstant, ReadableInstant endInstant){
@@ -182,9 +204,10 @@ public class TimePeriod implements Cloneable, Serializable {
                return new TimePeriod(startDate, endDate);
        }
 
-       
+//****************** CONVERTERS ******************/    
+
        /**
-        * Transforms a <code>Calendar</code> into a <code>Partial</code>
+        * Transforms a {@link Calendar} into a <code>Partial</code>
         * @param calendar
         * @return
         */
@@ -193,9 +216,9 @@ public class TimePeriod implements Cloneable, Serializable {
                Partial partial = new Partial(ld);
                return partial;
        }
-       
+
        /**
-        * Transforms a <code>Calendar</code> into a <code>Partial</code>
+        * Transforms a {@link ReadableInstant} into a <code>Partial</code>
         * @param calendar
         * @return
         */
@@ -205,6 +228,19 @@ public class TimePeriod implements Cloneable, Serializable {
                Partial partial = new Partial(ld);
                return partial;
        }
+
+
+       public static Integer getPartialValue(Partial partial, DateTimeFieldType type){
+               if (partial == null || ! partial.isSupported(type)){
+                       return null;
+               }else{
+                       return partial.get(type);
+               }
+       }
+
+
+       
+//*********************** CONSTRUCTOR *********************************/       
        
        /**
         * Constructor
@@ -220,53 +256,77 @@ public class TimePeriod implements Cloneable, Serializable {
                end=endDate;
        }
 
-       /**
-        * True, if this time period represents a period not a single point in time.
-        * This is by definition, that the time period has a start and an end value,
-        * and both have a year value that is not null
-        * @return
-        */
-       @Transient
-       public boolean isPeriod(){
-               if (getStartYear() != null && getEndYear() != null ){
-                       return true;
-               }else{
-                       return false;
-               }
-       }
-       
-       
+//******************* GETTER / SETTER ************************************/
+
        public Partial getStart() {
                return start;
        }
-       
+
        public void setStart(Partial start) {
                this.start = start;
        }
-       
+
        public Partial getEnd() {
                return end;
        }
-       
+
        public void setEnd(Partial end) {
                this.end = end;
        }
-       
+
        /**
+        * For time periods that need to store more information than the one
+        * that can be stored in <code>start</code> and <code>end</code>.
+        * If free text is not <code>null</null> {@link #toString()} will always
+        * return the free text value.
+        * <BR>Use {@link #toString()} for public use.
         * @return the freeText
         */
-       protected String getFreeText() {
+       public String getFreeText() {
                return freeText;
        }
 
 
        /**
+        * Use {@link #parseSingleDate(String)} for public use.
         * @param freeText the freeText to set
         */
-       protected void setFreeText(String freeText) {
+       public void setFreeText(String freeText) {
                this.freeText = freeText;
        }
 
+       
+//******************* Transient METHODS ************************************/
+
+       /**
+        * True, if this time period represents a period not a single point in time.
+        * This is by definition, that the time period has a start and an end value,
+        * and both have a year value that is not null
+        * @return
+        */
+       @Transient
+       public boolean isPeriod(){
+               if (getStartYear() != null && getEndYear() != null ){
+                       return true;
+               }else{
+                       return false;
+               }
+       }
+
+       /**
+        * True, if there is no start date and no end date and no freetext representation exists.
+        * @return
+        */
+       @Transient
+       public boolean isEmpty(){
+               if (StringUtils.isBlank(this.getFreeText()) && start == null  && end == null ){
+                       return true;
+               }else{
+                       return false;
+               }
+       }
+
+
 
        @Transient
        public String getYear(){
@@ -283,12 +343,12 @@ public class TimePeriod implements Cloneable, Serializable {
                }
                return result;
        }
-       
+
        @Transient
        public Integer getStartYear(){
                return getPartialValue(start, YEAR_TYPE);
        }
-       
+
        @Transient
        public Integer getStartMonth(){
                return getPartialValue(start, MONTH_TYPE);
@@ -313,20 +373,11 @@ public class TimePeriod implements Cloneable, Serializable {
        public Integer getEndDay(){
                return getPartialValue(end, DAY_TYPE);
        }
-       
-       public static Integer getPartialValue(Partial partial, DateTimeFieldType type){
-               if (partial == null || ! partial.isSupported(type)){
-                       return null;
-               }else{
-                       return partial.get(type);
-               }
-               
-       }
-       
+
        public TimePeriod setStartYear(Integer year){
                return setStartField(year, YEAR_TYPE);
        }
-       
+
        public TimePeriod setStartMonth(Integer month) throws IndexOutOfBoundsException{
                return setStartField(month, MONTH_TYPE);
        }
@@ -334,7 +385,7 @@ public class TimePeriod implements Cloneable, Serializable {
        public TimePeriod setStartDay(Integer day) throws IndexOutOfBoundsException{
                return setStartField(day, DAY_TYPE);
        }
-       
+
        public TimePeriod setEndYear(Integer year){
                return setEndField(year, YEAR_TYPE);
        }
@@ -346,8 +397,8 @@ public class TimePeriod implements Cloneable, Serializable {
        public TimePeriod setEndDay(Integer day) throws IndexOutOfBoundsException{
                return setEndField(day, DAY_TYPE);
        }
-       
-       public static Partial setPartialField(Partial partial, Integer value, DateTimeFieldType type) 
+
+       public static Partial setPartialField(Partial partial, Integer value, DateTimeFieldType type)
                        throws IndexOutOfBoundsException{
                if (partial == null){
                        partial = new Partial();
@@ -359,18 +410,22 @@ public class TimePeriod implements Cloneable, Serializable {
                        return partial.with(type, value);
                }
        }
-       
-       private TimePeriod setStartField(Integer value, DateTimeFieldType type) 
+
+       @Transient
+       private TimePeriod setStartField(Integer value, DateTimeFieldType type)
                        throws IndexOutOfBoundsException{
                start = setPartialField(start, value, type);
                return this;
        }
 
+       @Transient
        private TimePeriod setEndField(Integer value, DateTimeFieldType type)
                        throws IndexOutOfBoundsException{
                end = setPartialField(end, value, type);
                return this;
        }
+
+// ******************************** internal methods *******************************/  
        
        /**
         * Throws an IndexOutOfBoundsException if the value does not have a valid value
@@ -395,7 +450,7 @@ public class TimePeriod implements Cloneable, Serializable {
                                if (month == 2){
                                        max = 29;
                                }else if (month == 4 ||month == 6 ||month == 9 ||month == 11){
-                                       max = 30; 
+                                       max = 30;
                                }
                        }
                }
@@ -403,177 +458,44 @@ public class TimePeriod implements Cloneable, Serializable {
                        throw new IndexOutOfBoundsException("Value must be between 1 and " +  max);
                }
        }
-       
+
        private void initStart(){
                if (start == null){
                        start = new Partial();
                }
        }
-       
+
        private void initEnd(){
                if (end == null){
                        end = new Partial();
                }
        }
-       
-       
-       //patter for first year in string;
-       private static final Pattern firstYearPattern =  Pattern.compile("\\d{4}");
-       //case "1806"[1807];
-       private static final Pattern uncorrectYearPatter =  Pattern.compile("\"\\d{4}\"\\s*\\[\\d{4}\\]");
-       //case fl. 1806 or c. 1806 or fl. 1806?
-       private static final Pattern prefixedYearPattern =  Pattern.compile("(fl|c)\\.\\s*\\d{4}(\\s*-\\s*\\d{4})?\\??");
-       //standard
-       private static final Pattern standardPattern =  Pattern.compile("\\s*\\d{2,4}(\\s*-\\s*\\d{2,4})?");
-       
-       
-       public static TimePeriod parseString(TimePeriod timePeriod, String periodString){
-               //TODO move to parser class
-               //TODO until now only quick and dirty (and partly wrong)
-               TimePeriod result = timePeriod;
-               
-               if(timePeriod == null){
-                       return timePeriod;
-               }
-               
-               if (periodString == null){
-                       return result;
-               }
-               periodString = periodString.trim();
-               
-               result.setFreeText(periodString);
-               
-               //case "1806"[1807];
-               if (uncorrectYearPatter.matcher(periodString).matches()){
-                       String realYear = periodString.split("\\[")[1];
-                       realYear = realYear.replace("]", "");
-                       result.setStartYear(Integer.valueOf(realYear));
-               //case fl. 1806 or c. 1806 or fl. 1806?
-               }else if(prefixedYearPattern.matcher(periodString).matches()){
-                       Matcher yearMatcher = firstYearPattern.matcher(periodString);
-                       yearMatcher.find();
-                       String startYear = yearMatcher.group();
-                       result.setStartYear(Integer.valueOf(startYear));
-                       if (yearMatcher.find()){
-                               String endYear = yearMatcher.group();
-                               result.setEndYear(Integer.valueOf(endYear));
-                       }
-               }else if (standardPattern.matcher(periodString).matches()){
-                       String[] years = periodString.split("-");
-                       Partial dtStart = null;
-                       Partial dtEnd = null;
-                       
-                       if (years.length > 2 || years.length <= 0){
-                               logger.warn("More than 1 '-' in period String: " + periodString);
-                       }else {
-                               try {
-                                       //start
-                                       if (! CdmUtils.isEmpty(years[0])){
-                                               dtStart = parseSingleDate(years[0].trim());
-                                       }
-                                       
-                                       //end
-                                       if (years.length >= 2 && ! CdmUtils.isEmpty(years[1])){
-                                               years[1] = years[1].trim();
-                                               if (years[1].length()==2 && dtStart != null && dtStart.isSupported(DateTimeFieldType.year())){
-                                                       years[1] = String.valueOf(dtStart.get(DateTimeFieldType.year())/100) + years[1];
-                                               }
-                                               dtEnd = parseSingleDate(years[1]);
-                                       }
-                                       
-                                       result.setStart(dtStart);
-                                       result.setEnd(dtEnd);
-                               } catch (IllegalArgumentException e) {
-                                       //logger.warn(e.getMessage());
-                                       //use freetext instead
-                               }
-                       }
-               }
-               return result;
-       }
-       
-       public static TimePeriod parseString(String strPeriod) {
-               TimePeriod timePeriod = TimePeriod.NewInstance();
-               return parseString(timePeriod, strPeriod);
-       }
-       
-       
-       protected static Partial parseSingleDate(String singleDateString) throws IllegalArgumentException{
-               //FIXME until now only quick and dirty and incomplete
-               Partial partial =  new Partial();
-               singleDateString = singleDateString.trim();
-               if (CdmUtils.isNumeric(singleDateString)){
-                       try {
-                               Integer year = Integer.valueOf(singleDateString.trim());
-                               if (year < 1000 && year > 2100){
-                                       logger.warn("Not a valid year: " + year + ". Year must be between 1000 and 2100");
-                               }else if (year < 1700 && year > 2100){
-                                       logger.warn("Not a valid taxonomic year: " + year + ". Year must be between 1750 and 2100");
-                                       partial = partial.with(YEAR_TYPE, year);
-                               }else{
-                                       partial = partial.with(YEAR_TYPE, year);
-                               }
-                       } catch (NumberFormatException e) {
-                               logger.debug("Not a Integer format in getCalendar()");
-                               throw new IllegalArgumentException(e);
-                       }
-               }else{
-                       throw new IllegalArgumentException("Until now only years can be parsed as single dates. But date is: " + singleDateString);
-               }
-               return partial;
 
-       }
-       
-       
-       
-       private class TimePeriodPartialFormatter extends DateTimeFormatter{
-               private TimePeriodPartialFormatter(){
-                       super(null, null);
-               }
-               public String print(ReadablePartial partial){
-                       //TODO
-                       String result = "";
-                       String year = (partial.isSupported(YEAR_TYPE))? String.valueOf(partial.get(YEAR_TYPE)):null;
-                       String month = (partial.isSupported(MONTH_TYPE))? String.valueOf(partial.get(MONTH_TYPE)):null;;
-                       String day = (partial.isSupported(DAY_TYPE))? String.valueOf(partial.get(DAY_TYPE)):null;;
-                       
-                       if (month !=null){
-                               if (year == null){
-                                       year = "xxxx";
-                               }
-                       }
-                       if (day != null){
-                               if (month == null){
-                                       month = "xx";
-                               }
-                               if (year == null){
-                                       year = "xxxx";
-                               }
-                       }
-                       result = (day != null)? day + "." : "";
-                       result += (month != null)? month + "." : "";
-                       result += (year != null)? year : "";
-                       
-                       return result;
-               }
-               
-       }
-       
-       public String toString(){
+
+//**************************** to String ****************************************
+
+       /**
+        * Returns the {@link #getFreeText()} value if free text is not <code>null</code>.
+        * Otherwise the concatenation of <code>start</code> and <code>end</code> is returned.
+        *
+        * @see java.lang.Object#toString()
+        */
+       @Override
+    public String toString(){
                String result = null;
-               DateTimeFormatter formatter = new TimePeriodPartialFormatter();
-               if (! CdmUtils.isEmpty(this.getFreeText())){
+               DateTimeFormatter formatter = TimePeriodPartialFormatter.NewInstance();
+               if ( StringUtils.isNotBlank(this.getFreeText())){
                        result = this.getFreeText();
                }else{
-                       String strStart = start != null? start.toString(formatter): null;
-                       String strEnd = end != null? end.toString(formatter): null;
+                       String strStart = start != null ? start.toString(formatter): null;
+                       String strEnd = end != null ? end.toString(formatter): null;
                        result = CdmUtils.concat("-", strStart, strEnd);
                }
                return result;
        }
-       
-//*********** EQUALS **********************************/       
-       
+
+//*********** EQUALS **********************************/
+
 
        /* (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
@@ -587,7 +509,7 @@ public class TimePeriod implements Cloneable, Serializable {
                        return false;
                }
                TimePeriod that = (TimePeriod)obj;
-               
+
                if (! CdmUtils.nullSafeEqual(this.start, that.start)){
                        return false;
                }
@@ -599,23 +521,23 @@ public class TimePeriod implements Cloneable, Serializable {
                }
                return true;
        }
-       
+
        /* (non-Javadoc)
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode() {
                int hashCode = 7;
-               hashCode = 29*hashCode +  
-                                       (start== null? 33: start.hashCode()) + 
-                                       (end== null? 39: end.hashCode()) + 
-                                       (freeText== null? 41: freeText.hashCode()); 
+               hashCode = 29*hashCode +
+                                       (start== null? 33: start.hashCode()) +
+                                       (end== null? 39: end.hashCode()) +
+                                       (freeText== null? 41: freeText.hashCode());
                return super.hashCode();
-       }       
-       
-       
-//*********** CLONE **********************************/        
-       
+       }
+
+
+//*********** CLONE **********************************/
+
 
        /* (non-Javadoc)
         * @see java.lang.Object#clone()
@@ -625,7 +547,7 @@ public class TimePeriod implements Cloneable, Serializable {
                try {
                        TimePeriod result = (TimePeriod)super.clone();
                        result.setStart(this.start);   //DateTime is immutable
-                       result.setEnd(this.end);        
+                       result.setEnd(this.end);
                        result.setFreeText(this.freeText);
                        return result;
                } catch (CloneNotSupportedException e) {
@@ -634,5 +556,5 @@ public class TimePeriod implements Cloneable, Serializable {
                }
        }
 
-       
+
 }
\ No newline at end of file