commit 6cc0be7e64296574192e6e197067bc4f62d77d24
Author: Andreas Kohlbecker <a.kohlbecker@bgbm.org>
Date:   Mon Apr 10 16:08:36 2017 +0200

    ref #6169 JodaTimePartialConverter support for multiple formats

diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/component/PartialDateField.java b/src/main/java/eu/etaxonomy/cdm/vaadin/component/PartialDateField.java
index 70530f02..2f5bcc51 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/component/PartialDateField.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/component/PartialDateField.java
@@ -25,12 +25,14 @@ import eu.etaxonomy.cdm.vaadin.util.converter.JodaTimePartialConverter;
  */
 public class PartialDateField extends TextField {
 
+    JodaTimePartialConverter.DateFormat format = JodaTimePartialConverter.DateFormat.DAY_MONTH_YEAR_DOT;
+
     /**
      *
      */
     public PartialDateField() {
         super();
-        setConverter(new JodaTimePartialConverter());
+        setConverter(new JodaTimePartialConverter(format));
     }
 
     /**
@@ -38,7 +40,7 @@ public class PartialDateField extends TextField {
      */
     public PartialDateField(Property dataSource) {
         super(dataSource);
-        setConverter(new JodaTimePartialConverter());
+        setConverter(new JodaTimePartialConverter(format));
     }
 
     /**
@@ -47,7 +49,7 @@ public class PartialDateField extends TextField {
      */
     public PartialDateField(String caption, Property dataSource) {
         super(caption, dataSource);
-        setConverter(new JodaTimePartialConverter());
+        setConverter(new JodaTimePartialConverter(format));
     }
 
     /**
@@ -56,7 +58,7 @@ public class PartialDateField extends TextField {
      */
     public PartialDateField(String caption, String value) {
         super(caption, value);
-        setConverter(new JodaTimePartialConverter());
+        setConverter(new JodaTimePartialConverter(format));
     }
 
     /**
@@ -64,7 +66,7 @@ public class PartialDateField extends TextField {
      */
     public PartialDateField(String caption) {
         super(caption);
-        setConverter(new JodaTimePartialConverter());
+        setConverter(new JodaTimePartialConverter(format));
     }
 
 
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/component/TimePeriodField.java b/src/main/java/eu/etaxonomy/cdm/vaadin/component/TimePeriodField.java
index b4a8c6c8..f32ee8cd 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/component/TimePeriodField.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/component/TimePeriodField.java
@@ -73,7 +73,9 @@ public class TimePeriodField extends CustomField<TimePeriod> {
         parseField.addTextChangeListener(e -> parseInput(e));
 
         PartialDateField startDate = new PartialDateField("Start");
+        startDate.setInputPrompt("dd.mm.yyy");
         PartialDateField endDate = new PartialDateField("End");
+        endDate.setInputPrompt("dd.mm.yyy");
         freeText = new TextField("FreeText");
         freeText.setWidth(100, Unit.PERCENTAGE);
 
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/util/converter/JodaTimePartialConverter.java b/src/main/java/eu/etaxonomy/cdm/vaadin/util/converter/JodaTimePartialConverter.java
index 71e5450f..500db1fb 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/util/converter/JodaTimePartialConverter.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/util/converter/JodaTimePartialConverter.java
@@ -8,6 +8,8 @@
 */
 package eu.etaxonomy.cdm.vaadin.util.converter;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -20,23 +22,44 @@ import com.vaadin.data.util.converter.Converter;
 
 
 /**
+ *
  * @author a.kohlbecker
  * @since Apr 7, 2017
  *
  */
 public class JodaTimePartialConverter implements Converter<String, Partial> {
 
+    /**
+     * iso8601: YYY-MM-DD
+     * yyyymmddDot: dd.mm.yyyy
+     *
+     * @author a.kohlbecker
+     * @since Apr 10, 2017
+     *
+     */
+    public enum DateFormat {
+        ISO8601,
+        DAY_MONTH_YEAR_DOT
+    }
+
     private static final long serialVersionUID = 976413549472527584L;
 
-    static final String GLUE = "-";
+    DateFormat format;
+
+    Pattern partialPatternIso8601 = Pattern.compile("^(?<year>(?:[1,2][7,8,9,0,1])?[0-9]{2})(?:-(?<month>[0-1]?[0-9])(?:-(?<day>[0-3]?[0-9]))?)?$");
+
+    Pattern partialPatternDayMonthYearDot = Pattern.compile("^(?:(?:(?<day>[0-3]?[0-9])\\.)?(?<month>[0-1]?[0-9])\\.)?(?<year>(?:[1,2][7,8,9,0,1])?[0-9]{2})$");
+
+    List<Pattern> patterns = Arrays.asList(new Pattern[]{partialPatternIso8601, partialPatternDayMonthYearDot});
+
 
-    Pattern partialPattern = Pattern.compile("^(?<year>(?:[1,2][7,8,9,0,1])?[0-9]{2})(?:-(?<month>[0-1]?[0-9])(?:-(?<day>[0-3]?[0-9]))?)?$");
 
     /**
-     *
+     * @param format The format of the string representation
      */
-    public JodaTimePartialConverter() {
+    public JodaTimePartialConverter(DateFormat format) {
         super();
+        this.format = format;
     }
 
     /**
@@ -48,30 +71,47 @@ public class JodaTimePartialConverter implements Converter<String, Partial> {
         Partial partial = null;
         if(value != null){
             partial = new Partial();
-            Matcher m = partialPattern.matcher(value);
-            if(m.matches()){
+            for(Pattern pattern : patterns){
+            Matcher m = pattern.matcher(value);
+                if(m.matches()){
+                    partial = makePartial(m);
+                    break;
+                }
+            }
+        }
+        return partial;
+    }
+
+    /**
+     * @param partial
+     * @param m
+     */
+    private Partial makePartial(Matcher m) {
+
+        Partial partial = new Partial();
+        try {
+            try {
+                String year = m.group("year");
+                partial = partial.with(DateTimeFieldType.year(), Integer.parseInt(year));
+            } catch (IllegalArgumentException e) {
+                // a valid year should be present here
+                throw new ConversionException(e);
+            }
+            try {
+                String month = m.group("month");
+                partial = partial.with(DateTimeFieldType.monthOfYear(), Integer.parseInt(month));
                 try {
-                    try {
-                        partial.with(DateTimeFieldType.year(), Integer.parseInt(m.group("year")));
-                    } catch (IllegalArgumentException e) {
-                        // a valid year should be present here
-                        throw new ConversionException(e);
-                    }
-                    try {
-                        partial.with(DateTimeFieldType.monthOfYear(), Integer.parseInt(m.group("month")));
-                        try {
-                            partial.with(DateTimeFieldType.dayOfMonth(), Integer.parseInt(m.group("day")));
-                        } catch (IllegalArgumentException e) {
-                            /* IGNORE days are not required */
-                        }
-                    } catch (IllegalArgumentException e) {
-                        /* IGNORE months are not required */
-                    }
-                } catch (NumberFormatException ne) {
-                    // all numbers should be parsable, this is guaranteed by the partialPattern
-                    throw new ConversionException(ne);
+                    String day = m.group("day");
+                    partial = partial.with(DateTimeFieldType.dayOfMonth(), Integer.parseInt(day));
+                } catch (IllegalArgumentException e) {
+                    /* IGNORE days are not required */
                 }
+            } catch (IllegalArgumentException e) {
+                /* IGNORE months are not required */
             }
+        } catch (NumberFormatException ne) {
+            // all numbers should be parsable, this is guaranteed by the partialPattern
+            throw new ConversionException(ne);
         }
         return partial;
     }
@@ -82,29 +122,73 @@ public class JodaTimePartialConverter implements Converter<String, Partial> {
     @Override
     public String convertToPresentation(Partial value, Class<? extends String> targetType, Locale locale)
             throws com.vaadin.data.util.converter.Converter.ConversionException {
-        StringBuffer sb = new StringBuffer();
         if(value != null){
+            switch(format) {
+            case ISO8601:
+                return formatIso8601(value);
+            case DAY_MONTH_YEAR_DOT:
+                return formatYyyymmddDot(value);
+            default:
+                return "JodaTimePartialConverter Error: unsupported format";
+           }
+        }
+        return "";
+    }
+
+    /**
+     * @param value
+     * @param sb
+     */
+    private String formatIso8601(Partial value) {
+        StringBuffer sb = new StringBuffer();
+        String glue = "-";
+        try {
+            sb.append(value.get(DateTimeFieldType.year()));
             try {
-                sb.append(value.get(DateTimeFieldType.year()));
+                String month = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.monthOfYear()))), 2, "0");
+                sb.append(glue).append(month);
                 try {
-                    String month = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.monthOfYear()))), 2, "0");
-                    sb.append(GLUE).append(month);
-                    try {
-                        String day = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.dayOfMonth()))), 2, "0");
-                        sb.append(GLUE).append(day);
-                    } catch (IllegalArgumentException e){
-                        /* IGNORE */
-                    }
+                    String day = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.dayOfMonth()))), 2, "0");
+                    sb.append(glue).append(day);
                 } catch (IllegalArgumentException e){
                     /* IGNORE */
                 }
             } catch (IllegalArgumentException e){
                 /* IGNORE */
             }
+        } catch (IllegalArgumentException e){
+            /* IGNORE */
         }
         return sb.toString();
     }
 
+    /**
+     * @param value
+     * @param sb
+     */
+    private String formatYyyymmddDot(Partial value) {
+        StringBuffer sb = new StringBuffer();
+        String glue = ".";
+        try {
+            sb.append(StringUtils.reverse(Integer.toString(value.get(DateTimeFieldType.year()))));
+            try {
+                String month = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.monthOfYear()))), 2, "0");
+                sb.append(glue).append(StringUtils.reverse(month));
+                try {
+                    String day = StringUtils.leftPad(Integer.toString((value.get(DateTimeFieldType.dayOfMonth()))), 2, "0");
+                    sb.append(glue).append(StringUtils.reverse(day));
+                } catch (IllegalArgumentException e){
+                    /* IGNORE */
+                }
+            } catch (IllegalArgumentException e){
+                /* IGNORE */
+            }
+        } catch (IllegalArgumentException e){
+            /* IGNORE */
+        }
+        return StringUtils.reverse(sb.toString());
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/src/test/java/eu/etaxonomy/cdm/vaadin/util/JodaTimePartialConverterTest.java b/src/test/java/eu/etaxonomy/cdm/vaadin/util/JodaTimePartialConverterTest.java
new file mode 100644
index 00000000..77225ca7
--- /dev/null
+++ b/src/test/java/eu/etaxonomy/cdm/vaadin/util/JodaTimePartialConverterTest.java
@@ -0,0 +1,86 @@
+/**
+* Copyright (C) 2017 EDIT
+* 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.
+*/
+package eu.etaxonomy.cdm.vaadin.util;
+
+import org.joda.time.DateTimeFieldType;
+import org.joda.time.Partial;
+import org.junit.Assert;
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.vaadin.util.converter.JodaTimePartialConverter;
+
+/**
+ * @author a.kohlbecker
+ * @since Apr 10, 2017
+ *
+ */
+public class JodaTimePartialConverterTest extends Assert {
+
+    Partial y = new Partial(
+            new DateTimeFieldType[]{
+                    DateTimeFieldType.year()
+                    },
+            new int[]{
+                    2012
+                    }
+            );
+    Partial ym = new Partial(
+            new DateTimeFieldType[]{
+                    DateTimeFieldType.year(),
+                    DateTimeFieldType.monthOfYear()
+                    },
+            new int[]{
+                    2013,
+                    04
+                    }
+            );
+    Partial ymd = new Partial(
+            new DateTimeFieldType[]{
+                    DateTimeFieldType.year(),
+                    DateTimeFieldType.monthOfYear(),
+                    DateTimeFieldType.dayOfMonth()
+                    },
+            new int[]{
+                    2014,
+                    04,
+                    12
+                    }
+            );
+
+
+    @Test
+    public void toISO8601() {
+        JodaTimePartialConverter conv = new JodaTimePartialConverter(JodaTimePartialConverter.DateFormat.ISO8601);
+        assertEquals("2012", conv.convertToPresentation(y, String.class, null));
+        assertEquals("2013-04", conv.convertToPresentation(ym, String.class, null));
+        assertEquals("2014-04-12", conv.convertToPresentation(ymd, String.class, null));
+    }
+
+
+    @Test
+    public void toDAY_MONTH_YEAR_DOT() {
+        JodaTimePartialConverter conv = new JodaTimePartialConverter(JodaTimePartialConverter.DateFormat.DAY_MONTH_YEAR_DOT);
+        assertEquals("2012", conv.convertToPresentation(y, String.class, null));
+        assertEquals("04.2013", conv.convertToPresentation(ym, String.class, null));
+        assertEquals("12.04.2014", conv.convertToPresentation(ymd, String.class, null));
+    }
+
+    @Test
+    public void fromDAY_MONTH_YEAR_DOT() {
+        // using null as format since the conversion should work for all formats
+        JodaTimePartialConverter conv = new JodaTimePartialConverter(null);
+        assertEquals(y, conv.convertToModel("2012", Partial.class, null));
+        assertEquals(ym, conv.convertToModel("2013-04", Partial.class, null));
+        assertEquals(ym, conv.convertToModel("04.2013", Partial.class, null));
+        assertEquals(ymd, conv.convertToModel("2014-04-12", Partial.class, null));
+        assertEquals(ymd, conv.convertToModel("12.04.2014", Partial.class, null));
+        assertEquals(ymd, conv.convertToModel("12.4.2014", Partial.class, null));
+    }
+
+}
diff --git a/src/test/java/eu/etaxonomy/cdm/vaadin/util/TypeDesignationConverterTest.java b/src/test/java/eu/etaxonomy/cdm/vaadin/util/TypeDesignationConverterTest.java
deleted file mode 100644
index d96f336e..00000000
--- a/src/test/java/eu/etaxonomy/cdm/vaadin/util/TypeDesignationConverterTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
-* Copyright (C) 2017 EDIT
-* 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.
-*/
-package eu.etaxonomy.cdm.vaadin.util;
-
-import static org.junit.Assert.assertNotNull;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.junit.Test;
-
-import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
-import eu.etaxonomy.cdm.model.name.Rank;
-import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
-import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
-import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
-import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
-import eu.etaxonomy.cdm.model.reference.Reference;
-import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
-import eu.etaxonomy.cdm.vaadin.CdmVaadinBaseTest;
-
-/**
- * @author a.kohlbecker
- * @since Mar 10, 2017
- *
- */
-public class TypeDesignationConverterTest extends CdmVaadinBaseTest{
-
-
-    @Test
-    public void test1(){
-
-        NameTypeDesignation ntd = NameTypeDesignation.NewInstance();
-        TaxonNameBase typeName = TaxonNameFactory.NewBacterialInstance(Rank.SPECIES());
-        typeName.setTitleCache("Prionus coriatius L.", true);
-        ntd.setTypeName(typeName);
-        Reference citation = ReferenceFactory.newGeneric();
-        citation.setTitleCache("Species Platarum", true);
-        ntd.setCitation(citation);
-
-        SpecimenTypeDesignation std = SpecimenTypeDesignation.NewInstance();
-        DerivedUnit specimen = DerivedUnit.NewInstance(SpecimenOrObservationType.PreservedSpecimen);
-        specimen.setTitleCache("OHA", true);
-        std.setTypeSpecimen(specimen);
-        std.setTypeStatus(SpecimenTypeDesignationStatus.HOLOTYPE());
-
-        List<TypeDesignationBase> tds = new ArrayList<>();
-        tds.add(ntd);
-        tds.add(std);
-
-        String result = new TypeDesignationConverter(tds, null).buildString().print();
-        Logger.getLogger(this.getClass()).debug(result);
-        assertNotNull(result);
-    }
-
-}
diff --git a/src/test/java/eu/etaxonomy/cdm/vaadin/util/converter/TypeDesignationConverterTest.java b/src/test/java/eu/etaxonomy/cdm/vaadin/util/converter/TypeDesignationConverterTest.java
new file mode 100644
index 00000000..50a8f58b
--- /dev/null
+++ b/src/test/java/eu/etaxonomy/cdm/vaadin/util/converter/TypeDesignationConverterTest.java
@@ -0,0 +1,67 @@
+/**
+* Copyright (C) 2017 EDIT
+* 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.
+*/
+package eu.etaxonomy.cdm.vaadin.util.converter;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
+import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
+import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
+import eu.etaxonomy.cdm.model.reference.Reference;
+import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
+import eu.etaxonomy.cdm.vaadin.CdmVaadinBaseTest;
+import eu.etaxonomy.cdm.vaadin.util.TypeDesignationConverter;
+
+/**
+ * @author a.kohlbecker
+ * @since Mar 10, 2017
+ *
+ */
+public class TypeDesignationConverterTest extends CdmVaadinBaseTest{
+
+
+    @Test
+    public void test1(){
+
+        NameTypeDesignation ntd = NameTypeDesignation.NewInstance();
+        TaxonNameBase typeName = TaxonNameFactory.NewBacterialInstance(Rank.SPECIES());
+        typeName.setTitleCache("Prionus coriatius L.", true);
+        ntd.setTypeName(typeName);
+        Reference citation = ReferenceFactory.newGeneric();
+        citation.setTitleCache("Species Platarum", true);
+        ntd.setCitation(citation);
+
+        SpecimenTypeDesignation std = SpecimenTypeDesignation.NewInstance();
+        DerivedUnit specimen = DerivedUnit.NewInstance(SpecimenOrObservationType.PreservedSpecimen);
+        specimen.setTitleCache("OHA", true);
+        std.setTypeSpecimen(specimen);
+        std.setTypeStatus(SpecimenTypeDesignationStatus.HOLOTYPE());
+
+        List<TypeDesignationBase> tds = new ArrayList<>();
+        tds.add(ntd);
+        tds.add(std);
+
+        String result = new TypeDesignationConverter(tds, null).buildString().print();
+        Logger.getLogger(this.getClass()).debug(result);
+        assertNotNull(result);
+    }
+
+}
