fix #8978 implement BigDecimal for StatisticalMeasurementValue
authorAndreas Müller <a.mueller@bgbm.org>
Tue, 28 Apr 2020 17:02:20 +0000 (19:02 +0200)
committerAndreas Müller <a.mueller@bgbm.org>
Wed, 27 May 2020 09:22:06 +0000 (11:22 +0200)
31 files changed:
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/BigDecimalUtil.java [new file with mode: 0644]
cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/function/TestBigDecimal.java [new file with mode: 0644]
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/sdd/in/SDDImport.java
cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/sdd/out/SDDDocumentBuilder.java
cdmlib-io/src/test/java/eu/etaxonomy/cdm/io/dwca/out/DwcaExportTest.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/format/description/QuantitativeDataFormatter.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/hibernate/BigDecimalUserType.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/package-info.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/QuantitativeData.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/description/StatisticalMeasurementValue.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/location/Point.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/generate/PolytomousKeyGenerator.java
cdmlib-model/src/test/java/eu/etaxonomy/cdm/format/QuantitativeDataFormatterTest.java
cdmlib-model/src/test/java/eu/etaxonomy/cdm/model/description/QuantitativeDataTest.java
cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/generate/PolytomousKeyGeneratorTest.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/data/FullCoverageDataGenerator.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/ColumnAdder.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/ColumnNameChanger.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/ColumnTypeChanger.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/Datatype.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/Float2BigDecimalTypeChanger.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/v512_515/SchemaUpdater_5120_5150.java
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/hibernate/BigDecimalUserTypeTest.java [new file with mode: 0644]
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/AbstractQuantitativeDescriptionBuilder.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/DefaultQuantitativeDescriptionBuilder.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/DescriptiveDataSetService.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/MicroFormatQuantitativeDescriptionBuilder.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregation.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/dto/RowWrapperDTO.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/NaturalLanguageGeneratorTest.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregationTest.java

diff --git a/cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/BigDecimalUtil.java b/cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/BigDecimalUtil.java
new file mode 100644 (file)
index 0000000..df48ba8
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+* Copyright (C) 2020 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.common;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Set;
+
+/**
+ * @author a.mueller
+ * @since 25.04.2020
+ */
+public final class BigDecimalUtil {
+
+    public static final BigDecimal MAX_BIGDECIMAL = BigDecimal.valueOf(Double.MAX_VALUE);
+
+    public static final BigDecimal MIN_BIGDECIMAL = BigDecimal.valueOf(Double.MIN_VALUE);
+
+    public static BigDecimal average(Set<BigDecimal> bigDecimals) {
+        BigDecimal sum = BigDecimal.ZERO;
+        int count=0;
+        for(BigDecimal bigDecimal : bigDecimals) {
+            if(null != bigDecimal) {
+                sum = sum.add(bigDecimal);
+                count++;
+            }
+        }
+        return sum.divide(new BigDecimal(count), RoundingMode.HALF_EVEN);
+    }
+}
diff --git a/cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/function/TestBigDecimal.java b/cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/function/TestBigDecimal.java
new file mode 100644 (file)
index 0000000..2ea7a94
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+* Copyright (C) 2020 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.common.function;
+
+import java.math.BigDecimal;
+
+/**
+ * This is class is for testing how BigDecimal actually works
+ * and how float can be transformed to BigDecimal.
+ * Can be deleted.
+ *
+ * @author a.mueller
+ * @since 27.04.2020
+ */
+public class TestBigDecimal {
+
+    public static void main(String[] args) {
+        BigDecimal a = new BigDecimal(new Float(2.600f).toString());
+        System.out.println(a.toString());
+        System.out.println(a.toPlainString());
+        System.out.println(a.toEngineeringString());
+        String str = a.toString();
+        String strZero = "000";
+        String strNine = "999";
+        if (str.contains(strZero)){
+            int pos = str.indexOf(strZero);
+            String str2 = str.substring(0, pos);
+            BigDecimal b = new BigDecimal(str2);
+            System.out.println(b.toString());
+        }else if (str.contains(strNine)){
+            int pos = str.indexOf(strNine);
+            String str2 = str.substring(0, pos);
+            BigDecimal b = new BigDecimal(str2);
+            b = b.add(new BigDecimal(1).movePointLeft(b.scale()));
+            Float f = Float.parseFloat(b.toString());
+            System.out.println(b.toString());
+            System.out.println(f.toString());
+        }
+    }
+}
index 0b20734d0828cfaa0fdb88c8e2d4a10340abbb3f..29128f4fc7c2fba0eb9a49caec3b755ddd819156 100644 (file)
@@ -11,6 +11,7 @@ package eu.etaxonomy.cdm.io.sdd.in;
 \r
 import java.io.File;\r
 import java.io.IOException;\r
+import java.math.BigDecimal;\r
 import java.net.MalformedURLException;\r
 import java.net.URI;\r
 import java.net.URL;\r
@@ -1362,7 +1363,7 @@ public class SDDImport extends XmlImportBase<SDDImportConfigurator, SDDImportSta
                                if (value.contains(",")) {\r
                                        value = value.replace(',', '.');\r
                                }\r
-                               Float v = Float.parseFloat(value);\r
+                               BigDecimal v = new BigDecimal(value);\r
                                //Float v = new Float(0);\r
                                StatisticalMeasure t = null;\r
                                if (type.equals("Min")) {\r
index be4c42a7d76a5f85bbff9039d1f4c48931fecc85..5459961b7f7b8811488bd639355e5e9d858dc7b9 100644 (file)
@@ -15,6 +15,7 @@ import java.io.IOException;
 import java.io.OutputStream;\r
 import java.io.OutputStreamWriter;\r
 import java.io.Writer;\r
+import java.math.BigDecimal;\r
 import java.util.HashMap;\r
 import java.util.HashSet;\r
 import java.util.Iterator;\r
@@ -1015,7 +1016,7 @@ public class SDDDocumentBuilder {
                } else {\r
                        measure.setAttribute("type", label);\r
                }\r
-               float value = statisticalValue.getValue();\r
+               BigDecimal value = statisticalValue.getValue();\r
                measure.setAttribute("value", String.valueOf(value));\r
                element.appendChild(measure);\r
        }\r
index 271c3b8c8aaad384398980c31530988f33eb6e66..d48296483e8b15a163d6063af9d5e38be888b6ca 100644 (file)
@@ -10,6 +10,7 @@ package eu.etaxonomy.cdm.io.dwca.out;
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.math.BigDecimal;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -448,8 +449,8 @@ public class DwcaExportTest  extends CdmTransactionalIntegrationTest{
         description.addElement(catData);
 
         //QuantitativeData
-        QuantitativeData quantData = QuantitativeData.NewMinMaxInstance(Feature.CHROMOSOME_NUMBER(), 0.1f, 1.3f);
-        quantData.setSampleSize(2f, null);
+        QuantitativeData quantData = QuantitativeData.NewMinMaxInstance(Feature.CHROMOSOME_NUMBER(), new BigDecimal("0.1"), new BigDecimal("1.3"));
+        quantData.setSampleSize(new BigDecimal("2"), null);
         setUuid(quantData,"011264eb-d3d4-44be-86f3-e6f69a21f36b");
         description.addElement(quantData);
 
index 9fdbd485e501b364eb314ab5d99334d3541edc9e..7b41b09a59847f2e921c2be25f7d7bdf326e9bb7 100644 (file)
@@ -8,6 +8,8 @@
 */
 package eu.etaxonomy.cdm.format.description;
 
+import java.math.BigDecimal;
+
 import eu.etaxonomy.cdm.common.CdmUtils;
 import eu.etaxonomy.cdm.model.description.MeasurementUnit;
 import eu.etaxonomy.cdm.model.description.QuantitativeData;
@@ -33,8 +35,8 @@ public class QuantitativeDataFormatter
         String result = "";
 
         //values
-        Float min = quantData.getMin();
-        Float max = quantData.getMax();
+        BigDecimal min = quantData.getMin();
+        BigDecimal max = quantData.getMax();
         String minMax = "";
         if (min != null){
             minMax = String.valueOf(min);
@@ -45,7 +47,7 @@ public class QuantitativeDataFormatter
             minMax = "<" + String.valueOf(max);
         }
         String exactValueStr = "";
-        for(Float exactValue : quantData.getExactValues()){
+        for(BigDecimal exactValue : quantData.getExactValues()){
             if (exactValue != null){
                 exactValueStr += CdmUtils.concat(";", exactValueStr, String.valueOf(exactValue));
             }
@@ -66,7 +68,7 @@ public class QuantitativeDataFormatter
         result = CdmUtils.concat(" ", result, unitStr);
 
         //bracket
-        Float n = quantData.getSampleSize();
+        BigDecimal n = quantData.getSampleSize();
         String size = (n == null) ? "" : "n="+String.valueOf(n);
         String strBracket = isNotBlank(size) ? "[" + size + "]" : "";
 
index 364cf4f3e1de0d99266a90ccc25eb2122ccc8049..a7e8b738f458931bff0f80cad5661462c2965a6a 100644 (file)
@@ -10,19 +10,34 @@ import java.sql.Types;
 \r
 import org.hibernate.HibernateException;\r
 import org.hibernate.engine.spi.SessionImplementor;\r
+import org.hibernate.type.BigDecimalType;\r
 import org.hibernate.type.StandardBasicTypes;\r
 import org.hibernate.usertype.UserType;\r
 \r
 /**\r
- * This software is public domain and carries NO WARRANTY.\r
+ * Hibernate {@link UserType} for BigDecimal with correct handling for scale.\r
+ * Correct means that a BigDecimal with scale 2 will be stored and reloaded\r
+ * with scale 2 even if the defined scale in the database has a higher scale defined.\r
  *\r
- * Patches, bug reports and feature requests welcome:\r
+ * E.g. "1.32" is persisted as exactly this BigDecimal even if scale of the column is\r
+ * defined with scale = 4. The default {@link BigDecimalType} stores and reloads it\r
+ * as 1.3200 which is a difference when handling e.g. measurement values as it looses the\r
+ * information about the exactness of the data.<BR><BR>\r
  *\r
- * https://bitbucket.org/ratkins/bigdecimalusertype/\r
+ * Usage example with annotations (with type already declared in according package-info.java):<BR>\r
+ *\r
+ * <BR>@Columns(columns={@Column(name="xxx", precision = 18, scale = 9), @Column(name="xxx_scale")})\r
+ * <BR>@Type(type="bigDecimalUserType")\r
+ * <BR><BR>\r
+ *\r
+ * This class has been originally copied and adapted from\r
+ * https://bitbucket.org/ratkins/bigdecimalusertype/src/default/<BR><BR>\r
  */\r
 public class BigDecimalUserType implements UserType {\r
 \r
-       private static final int[] SQL_TYPES = new int[] {Types.DECIMAL, Types.INTEGER};\r
+       private static final int[] SQL_TYPES = new int[] {\r
+               Types.NUMERIC, //for some reason Types.Decimal does not exist at least in MySQL Dialect\r
+               Types.INTEGER};\r
 \r
        @Override\r
        public Object assemble(Serializable arg0, Object arg1) throws HibernateException {\r
index 04f0a2d916a7245484a66d4e886599eed90185e7..5be942739e132c00ae8b06254496d217fa0ad4bc 100644 (file)
        @org.hibernate.annotations.TypeDef(name="doiUserType", typeClass=eu.etaxonomy.cdm.hibernate.DOIUserType.class),
        @org.hibernate.annotations.TypeDef(name="orcidUserType", typeClass=eu.etaxonomy.cdm.hibernate.OrcidUserType.class),
     @org.hibernate.annotations.TypeDef(name="shiftUserType", typeClass=eu.etaxonomy.cdm.hibernate.ShiftUserType.class),
+    @org.hibernate.annotations.TypeDef(name="bigDecimalUserType", typeClass=eu.etaxonomy.cdm.hibernate.BigDecimalUserType.class),
 })
 @org.hibernate.annotations.AnyMetaDef(name = "CdmBase" ,
                                              metaType="string",
index 3d8618bd94d441ad063c90c954c15661f2d6ea31..90bc9a3e4c797b42cad5c044a33a79de1af88df4 100644 (file)
@@ -9,6 +9,7 @@
 
 package eu.etaxonomy.cdm.model.description;
 
+import java.math.BigDecimal;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -35,6 +36,7 @@ import org.hibernate.envers.Audited;
 import org.hibernate.search.annotations.Indexed;
 import org.hibernate.validator.constraints.NotEmpty;
 
+import eu.etaxonomy.cdm.common.BigDecimalUtil;
 import eu.etaxonomy.cdm.model.term.DefinedTerm;
 import eu.etaxonomy.cdm.validation.Level2;
 
@@ -72,9 +74,10 @@ import eu.etaxonomy.cdm.validation.Level2;
 @Entity
 @Indexed(index = "eu.etaxonomy.cdm.model.description.DescriptionElementBase")
 @Audited
-public class QuantitativeData extends DescriptionElementBase implements Cloneable {
-       private static final long serialVersionUID = -2755806455420051488L;
+public class QuantitativeData
+        extends DescriptionElementBase {
 
+       private static final long serialVersionUID = -2755806455420051488L;
        private static final Logger logger = Logger.getLogger(QuantitativeData.class);
 
        @XmlElement(name = "MeasurementUnit")
@@ -133,7 +136,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
     /**
      * Creates a new quantitative data instance of type feature with defined min and may value.
      */
-    public static QuantitativeData NewMinMaxInstance(Feature feature, float min, float max){
+    public static QuantitativeData NewMinMaxInstance(Feature feature, BigDecimal min, BigDecimal max){
         QuantitativeData result = new QuantitativeData(feature);
         StatisticalMeasurementValue minValue = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(),min);
         result.addStatisticalValue(minValue);
@@ -145,9 +148,9 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
     /**
      * Creates a new quantitative data instance of type feature with defined exact value.
      */
-    public static QuantitativeData NewExactValueInstance(Feature feature, float... exactValues){
+    public static QuantitativeData NewExactValueInstance(Feature feature, BigDecimal... exactValues){
         QuantitativeData result = new QuantitativeData(feature);
-        for (float exactVal : exactValues){
+        for (BigDecimal exactVal : exactValues){
             StatisticalMeasurementValue exactValue = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.EXACT_VALUE(), exactVal);
             result.addStatisticalValue(exactValue);
         }
@@ -232,25 +235,25 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
 // ******************************** TRANSIENT METHODS *******************************/
 
     @Transient
-    public Float getOverallMin(){
-        float result = Float.MAX_VALUE;
+    public BigDecimal getOverallMin(){
+        BigDecimal result = BigDecimalUtil.MAX_BIGDECIMAL;
         for (StatisticalMeasurementValue value : statisticalValues){
             if (withRangeValue(value)){
-                result = Math.min(result, value.getValue());;
+                result = result.min(value.getValue());
             }
         }
-        return (result == Float.MAX_VALUE)? null: result;
+        return (result == BigDecimalUtil.MAX_BIGDECIMAL)? null: result;
     }
 
     @Transient
-    public Float getOverallMax(){
-        float result = Float.MIN_VALUE;
+    public BigDecimal getOverallMax(){
+        BigDecimal result = BigDecimalUtil.MIN_BIGDECIMAL;
         for (StatisticalMeasurementValue value : statisticalValues){
             if (withRangeValue(value)){
-                result = Math.max(result, value.getValue());;
+                result = result.max(value.getValue());
             }
         }
-        return (result == Float.MIN_VALUE)? null: result;
+        return (result == BigDecimalUtil.MIN_BIGDECIMAL)? null: result;
     }
 
     private boolean withRangeValue(StatisticalMeasurementValue value) {
@@ -270,7 +273,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * statistical measurement value instance exists.
         */
        @Transient
-       public Float getMin(){
+       public BigDecimal getMin(){
                return getSpecificStatisticalValue(StatisticalMeasure.MIN());
        }
 
@@ -281,7 +284,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * statistical measurement value instance exists.
         */
        @Transient
-       public Float getMax(){
+       public BigDecimal getMax(){
                return getSpecificStatisticalValue(StatisticalMeasure.MAX());
        }
 
@@ -292,12 +295,12 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * Returns <code>null</code> if no such statistical measurement value instance exists.
         */
        @Transient
-       public Float getTypicalLowerBoundary(){
+       public BigDecimal getTypicalLowerBoundary(){
                return getSpecificStatisticalValue(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY());
        }
 
        @Transient
-    public Set<Float> getExactValues(){
+    public Set<BigDecimal> getExactValues(){
         return getSpecificStatisticalValues(StatisticalMeasure.EXACT_VALUE());
     }
 
@@ -308,7 +311,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * Returns <code>null</code> if no such statistical measurement value instance exists.
         */
        @Transient
-       public Float getAverage(){
+       public BigDecimal getAverage(){
                return getSpecificStatisticalValue(StatisticalMeasure.AVERAGE());
        }
 
@@ -319,7 +322,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * Returns <code>null</code> if no such statistical measurement value instance exists.
         */
        @Transient
-       public Float getStandardDeviation(){
+       public BigDecimal getStandardDeviation(){
                return getSpecificStatisticalValue(StatisticalMeasure.STANDARD_DEVIATION());
        }
 
@@ -330,7 +333,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * Returns <code>null</code> if no such statistical measurement value instance exists.
         */
        @Transient
-       public Float getSampleSize(){
+       public BigDecimal getSampleSize(){
                return getSpecificStatisticalValue(StatisticalMeasure.SAMPLE_SIZE());
        }
 
@@ -341,7 +344,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * Returns <code>null</code> if no such statistical measurement value instance exists.
         */
        @Transient
-       public Float getTypicalUpperBoundary(){
+       public BigDecimal getTypicalUpperBoundary(){
                return getSpecificStatisticalValue(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY());
        }
 
@@ -352,8 +355,8 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @param type
         * @return the value
         */
-       public Float getSpecificStatisticalValue(StatisticalMeasure type){
-               Float result = null;
+       public BigDecimal getSpecificStatisticalValue(StatisticalMeasure type){
+           BigDecimal result = null;
                for (StatisticalMeasurementValue value : statisticalValues){
                        if (type.equals(value.getType())){
                                result = value.getValue();
@@ -363,8 +366,8 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
                return result;
        }
 
-    public Set<Float> getSpecificStatisticalValues(StatisticalMeasure type){
-        Set<Float> result = new HashSet<>();
+    public Set<BigDecimal> getSpecificStatisticalValues(StatisticalMeasure type){
+        Set<BigDecimal> result = new HashSet<>();
         for (StatisticalMeasurementValue value : statisticalValues){
             if (type.equals(value.getType())){
                 result.add(value.getValue());
@@ -383,7 +386,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setMinimum(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setMinimum(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.MIN());
        }
 
@@ -397,7 +400,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setMaximum(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setMaximum(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.MAX());
        }
 
@@ -411,11 +414,10 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setAverage(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setAverage(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.AVERAGE());
        }
 
-
        /**
         * Sets the statistical value for the standard deviation.
         * If such value exists the old value is replaced by the new value.
@@ -425,7 +427,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setStandardDeviation(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setStandardDeviation(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.STANDARD_DEVIATION());
        }
 
@@ -438,7 +440,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setSampleSize(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setSampleSize(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.SAMPLE_SIZE());
        }
 
@@ -452,7 +454,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setTypicalLowerBoundary(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setTypicalLowerBoundary(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.TYPICAL_LOWER_BOUNDARY());
        }
 
@@ -466,7 +468,7 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @return the newValue
         */
        @Transient
-       public StatisticalMeasurementValue setTypicalUpperBoundary(Float value, Set<DefinedTerm> modifiers){
+       public StatisticalMeasurementValue setTypicalUpperBoundary(BigDecimal value, Set<DefinedTerm> modifiers){
                return setSpecificStatisticalValue(value, modifiers, StatisticalMeasure.TYPICAL_UPPER_BOUNDARY());
        }
 
@@ -478,7 +480,8 @@ public class QuantitativeData extends DescriptionElementBase implements Cloneabl
         * @param value
         * @return the newValue
         */
-       public StatisticalMeasurementValue setSpecificStatisticalValue(Float value, Set<DefinedTerm> modifiers, StatisticalMeasure type){
+       public StatisticalMeasurementValue setSpecificStatisticalValue(BigDecimal value,
+               Set<DefinedTerm> modifiers, StatisticalMeasure type){
 
            StatisticalMeasurementValue result = null;
                StatisticalMeasurementValue existingSmValue = null;
index 186c97371293377310e4f4633c52fe4785447dee..7e79d320921104de2deb8281a77192aca4915221 100644 (file)
 package eu.etaxonomy.cdm.model.description;\r
 \r
 \r
+import java.math.BigDecimal;\r
 import java.util.HashSet;\r
 import java.util.Set;\r
 \r
+import javax.persistence.Column;\r
 import javax.persistence.Entity;\r
 import javax.persistence.FetchType;\r
 import javax.persistence.ManyToMany;\r
 import javax.persistence.ManyToOne;\r
+import javax.validation.constraints.NotNull;\r
 import javax.xml.bind.annotation.XmlAccessType;\r
 import javax.xml.bind.annotation.XmlAccessorType;\r
 import javax.xml.bind.annotation.XmlElement;\r
@@ -27,6 +30,8 @@ import javax.xml.bind.annotation.XmlSchemaType;
 import javax.xml.bind.annotation.XmlType;\r
 \r
 import org.apache.log4j.Logger;\r
+import org.hibernate.annotations.Columns;\r
+import org.hibernate.annotations.Type;\r
 import org.hibernate.envers.Audited;\r
 import org.hibernate.search.annotations.Indexed;\r
 import org.hibernate.search.annotations.IndexedEmbedded;\r
@@ -61,8 +66,11 @@ public class StatisticalMeasurementValue
        private static final long serialVersionUID = -3576311887760351982L;\r
        private static final Logger logger = Logger.getLogger(StatisticalMeasurementValue.class);\r
 \r
-       @XmlElement(name = "Value")\r
-       private float value;\r
+    @XmlElement(name = "Value")\r
+    @Columns(columns={@Column(name="value", precision = 18, scale = 9), @Column(name="value_scale")})\r
+    @Type(type="bigDecimalUserType")\r
+    @NotNull  //#8978 the old float value also could not be null and a value without value does not make sense, but do we want to have a default?\r
+    private BigDecimal value;\r
 \r
        @XmlElementWrapper(name = "Modifiers")\r
        @XmlElement(name = "Modifier")\r
@@ -85,29 +93,26 @@ public class StatisticalMeasurementValue
     @IndexedEmbedded(depth=1)\r
        private QuantitativeData quantitativeData;\r
 \r
+// ***************** FACTORY ****************************/\r
 \r
+    public static StatisticalMeasurementValue NewInstance(){\r
+        return new StatisticalMeasurementValue();\r
+    }\r
+\r
+    public static StatisticalMeasurementValue NewInstance(StatisticalMeasure type, BigDecimal value){\r
+        StatisticalMeasurementValue result = new StatisticalMeasurementValue();\r
+        result.setValue(value);\r
+        result.setType(type);\r
+        return result;\r
+    }\r
+\r
+// ******************* CONSTRUCTOR *************************/\r
 \r
        protected StatisticalMeasurementValue(){\r
                super();\r
        }\r
 \r
-       /**\r
-        * Creates a new empty statistical measurement value instance.\r
-        */\r
-       public static StatisticalMeasurementValue NewInstance(){\r
-               return new StatisticalMeasurementValue();\r
-       }\r
-\r
-\r
-       /**\r
-        * Creates a new empty statistical measurement value instance.\r
-        */\r
-       public static StatisticalMeasurementValue NewInstance(StatisticalMeasure type, float value){\r
-               StatisticalMeasurementValue result = new StatisticalMeasurementValue();\r
-               result.setValue(value);\r
-               result.setType(type);\r
-               return result;\r
-       }\r
+// ***************** GETTER / SETTER **************************/\r
 \r
 \r
        /**\r
@@ -130,13 +135,13 @@ public class StatisticalMeasurementValue
         * corresponding to the {@link QuantitativeData quantitative data} <i>this</i>\r
         * statistical measurement value belongs to.\r
         */\r
-       public float getValue(){\r
+       public BigDecimal getValue(){\r
                return this.value;\r
        }\r
        /**\r
         * @see #getValue()\r
         */\r
-       public void setValue(float value){\r
+       public void setValue(BigDecimal value){\r
                this.value = value;\r
        }\r
 \r
index f8a0f12e4246f98c7ea0b97fbcbefbd039ca2762..31245a36012e7210a941e8da98d262f8ca166a0b 100644 (file)
@@ -62,7 +62,7 @@ public class Point implements Cloneable, Serializable {
     private static final long serialVersionUID = 531030660792800636L;
     private static final Logger logger = Logger.getLogger(Point.class);
 
-    //TODO was Float but H2 threw errors
+    //TODO was Float but H2 threw errors, maybe we should also use BigDecimal for exactness, see #8978
     @XmlElement(name = "Longitude")
     @Longitude(of="point")
     @NotNull(groups = Level2.class)
index a1eba1417e2a400d3cbff797a33ec904a7560fbc..a7f80a420ff8ab63dcf3482c2aee0f1a7ee012e7 100644 (file)
@@ -1,5 +1,6 @@
 package eu.etaxonomy.cdm.strategy.generate;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -15,6 +16,7 @@ import java.util.UUID;
 
 import org.apache.log4j.Logger;
 
+import eu.etaxonomy.cdm.common.BigDecimalUtil;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
@@ -260,7 +262,7 @@ public class PolytomousKeyGenerator {
                logger.warn("Only 1 or no taxon covered. This should currently only be possible on top level and is not yet handled. ");
                }else {
                        // this map stores the thresholds giving the best dichotomy of taxa for the corresponding feature supporting quantitative data
-                       Map<Feature,Float> quantitativeFeaturesThresholds = new HashMap<>();
+                       Map<Feature,BigDecimal> quantitativeFeaturesThresholds = new HashMap<>();
                        // the scores of the different features are calculated, the thresholds in the same time
                        if (config.isDebug()){
                            System.out.println("Feature left: " + featuresLeft + ", taxa: " + taxaCovered);
@@ -319,18 +321,18 @@ public class PolytomousKeyGenerator {
             }else if (feature.isSupportsQuantitativeData()){
                 Iterator<KeyTaxon> it = candidates.iterator();
                 while (it.hasNext()){
-                    float min = Float.MAX_VALUE;
-                    Float max = Float.MIN_VALUE;
+                    BigDecimal min = BigDecimalUtil.MAX_BIGDECIMAL;
+                    BigDecimal max = BigDecimalUtil.MIN_BIGDECIMAL;
                     Set<QuantitativeData> qds = it.next().quantitativeData.get(feature);
                     qds = qds == null? new HashSet<>(): qds;
                     for (QuantitativeData qd : qds){
-                        Float qdMin = qd.getOverallMin();
+                        BigDecimal qdMin = qd.getOverallMin();
                         if(qdMin != null){
-                            min = Math.min(min, qdMin);
+                            min = min.min(qdMin);
                         }
-                        Float qdMax = qd.getOverallMax();
+                        BigDecimal qdMax = qd.getOverallMax();
                         if(qdMax != null){
-                            max = Math.max(max, qdMax);
+                            max = max.max(qdMax);
                         }
                     }
                     boolean staysCandidate = true;
@@ -338,8 +340,8 @@ public class PolytomousKeyGenerator {
                         Set<QuantitativeData> tqds = taxon.quantitativeData.get(feature);
                         tqds = tqds == null? new HashSet<>(): tqds;
                         for (QuantitativeData qd : tqds){
-                            staysCandidate &= qd.getOverallMin() == null || qd.getOverallMin() >= min;
-                            staysCandidate &= qd.getOverallMax() == null || qd.getOverallMax() <= max;
+                            staysCandidate &= qd.getOverallMin() == null || qd.getOverallMin().compareTo(min) >= 0;
+                            staysCandidate &= qd.getOverallMax() == null || qd.getOverallMax().compareTo(max) <= 0;
                         }
                         if (!staysCandidate){
                             break;
@@ -674,11 +676,11 @@ public class PolytomousKeyGenerator {
     }
 
     private void handleQuantitativeData(PolytomousKeyNode parent, List<Feature> featuresLeft,
-            Set<KeyTaxon> taxaCovered, Map<Feature, Float> quantitativeFeaturesThresholds,
+            Set<KeyTaxon> taxaCovered, Map<Feature, BigDecimal> quantitativeFeaturesThresholds,
             Feature winnerFeature, Map<Feature, Set<State>> featureStatesFilter) {
 
         // first, get the threshold
-        float threshold = quantitativeFeaturesThresholds.get(winnerFeature);
+        BigDecimal threshold = quantitativeFeaturesThresholds.get(winnerFeature);
         //TODO unit not seems to be used yet
         StringBuilder unit = new StringBuilder();
         // then determine which taxa are before and which are after this threshold (dichotomy)
@@ -699,7 +701,7 @@ public class PolytomousKeyGenerator {
      * TODO if the quantitative feature has dependent features they are not yet handled
      */
     private void handleQuantitativeBranch(PolytomousKeyNode parent, List<Feature> featuresLeft,
-            int parentTaxaCoveredSize, Feature winnerFeature, float threshold, StringBuilder unit,
+            int parentTaxaCoveredSize, Feature winnerFeature, BigDecimal threshold, StringBuilder unit,
             List<Set<KeyTaxon>> quantitativeStates, Map<Feature, Set<State>> featureStatesFilter,
             int brunchNum) {
 
@@ -732,7 +734,7 @@ public class PolytomousKeyGenerator {
     }
 
     private Feature computeScores(List<Feature> featuresLeft, Set<KeyTaxon> taxaCovered,
-            Map<Feature, Float> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter) {
+            Map<Feature,BigDecimal> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter) {
 
         Map<Feature,Float> scoreMap = featureScores(featuresLeft, taxaCovered, quantitativeFeaturesThresholds, featureStatesFilter);
         dependenciesScores(scoreMap, featuresLeft, taxaCovered, quantitativeFeaturesThresholds, featureStatesFilter);
@@ -753,7 +755,7 @@ public class PolytomousKeyGenerator {
         * "onlyApplicableIf" or "InapplicableIf", the feature it depends can be chosen in order to build a better key.
         */
        private void dependenciesScores(Map<Feature,Float> scoreMap, List<Feature> featuresLeft,
-               Set<KeyTaxon> coveredTaxa, Map<Feature,Float> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter){
+               Set<KeyTaxon> coveredTaxa, Map<Feature,BigDecimal> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter){
 
            //TODO maybe we need to do this recursive?
 
@@ -1022,7 +1024,7 @@ public class PolytomousKeyGenerator {
         * This function fills the map of features (keys) with their respecting scores (values)
         */
        private Map<Feature,Float> featureScores(List<Feature> featuresLeft, Set<KeyTaxon> coveredTaxa,
-               Map<Feature,Float> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter){
+               Map<Feature,BigDecimal> quantitativeFeaturesThresholds, Map<Feature, Set<State>> featureStatesFilter){
 
                Map<Feature,Float> scoreMap = new HashMap<>();
                for (Feature feature : featuresLeft){
@@ -1042,7 +1044,7 @@ public class PolytomousKeyGenerator {
         * It returns two Sets of {@link KeyTaxon}, one with the taxa under this threshold (taxaBefore) and another one
         * with the taxa over (taxaAfter).
         */
-       private List<Set<KeyTaxon>> determineQuantitativeStates (Float threshold, Feature feature,
+       private List<Set<KeyTaxon>> determineQuantitativeStates (BigDecimal threshold, Feature feature,
                Set<KeyTaxon> taxa, StringBuilder unit){
 
            List<Set<KeyTaxon>> list = new ArrayList<>();
@@ -1060,12 +1062,12 @@ public class PolytomousKeyGenerator {
                     StatisticalMeasure type = smv.getType();
                     //TODO DONT FORGET sample size, MEAN etc
                     if (type.isMax() || type.isTypicalUpperBoundary() || type.isAverage() || type.isExactValue()) {
-                        if (smv.getValue()>threshold){
+                        if (smv.getValue().compareTo(threshold) > 0){
                             taxaAfter.add(td);
                         }
                     }
                     if (type.isMin() || type.isTypicalLowerBoundary() || type.isAverage() || type.isExactValue()) {
-                        if (smv.getValue()<=threshold){
+                        if (smv.getValue().compareTo(threshold) <= 0){
                             taxaBefore.add(td);
                         }
                     }
@@ -1078,30 +1080,31 @@ public class PolytomousKeyGenerator {
        /**
         * This function returns the score of a quantitative feature.
         */
-       private float quantitativeFeatureScore(Feature feature, Set<KeyTaxon> coveredTaxa, Map<Feature,Float> quantitativeFeaturesThresholds){
+       private float quantitativeFeatureScore(Feature feature, Set<KeyTaxon> coveredTaxa,
+               Map<Feature,BigDecimal> quantitativeFeaturesThresholds){
 
-           List<Float> allValues = new ArrayList<>();
+           List<BigDecimal> allValues = new ArrayList<>();
                for (KeyTaxon td : coveredTaxa){
                    for (QuantitativeData qd : td.getQuantitativeData(feature)){
                        computeAllValues(allValues, qd);
                    }
                }
                int i,j;
-               float threshold=0;
-               float bestThreshold=0;
-               int difference=allValues.size();
+               BigDecimal threshold = BigDecimal.ZERO;
+               BigDecimal bestThreshold = BigDecimal.ZERO;
+               int difference = allValues.size();
                int differenceMin = difference;
-               int bestTaxaBefore=0;
-               int bestTaxaAfter=0;
-               for (i=0;i<allValues.size()/2;i++) {
+               int bestTaxaBefore = 0;
+               int bestTaxaAfter = 0;
+               for (i=0; i<allValues.size()/2; i++) {
                        threshold = allValues.get(i*2+1);
                        int taxaBefore=0;
                        int taxaAfter=0;
                        for (j=0;j<allValues.size()/2;j++) {
-                               if (allValues.get(j*2+1)<=threshold){
+                               if (allValues.get(j*2+1).compareTo(threshold) <= 0){
                                        taxaBefore++;
                                }
-                               if (allValues.get(j*2)>threshold){
+                               if (allValues.get(j*2).compareTo(threshold) > 0){
                                        taxaAfter++;
                                }
                        }
@@ -1121,26 +1124,26 @@ public class PolytomousKeyGenerator {
                return defaultQuantitativeScore;
        }
 
-    private void computeAllValues(List<Float> allValues, QuantitativeData qd) {
+    private void computeAllValues(List<BigDecimal> allValues, QuantitativeData qd) {
         Set<StatisticalMeasurementValue> values = qd.getStatisticalValues();
-        Float lowerboundary=null;
-        Float upperboundary=null;
+        BigDecimal lowerboundary = null;
+        BigDecimal upperboundary = null;
         for (StatisticalMeasurementValue smv : values){
                StatisticalMeasure type = smv.getType();
-               float value = smv.getValue();
+               BigDecimal value = smv.getValue();
                // DONT FORGET sample size, MEAN etc
                if(type.isMin() || type.isTypicalLowerBoundary() || type.isAverage() || type.isExactValue()){
                    if (lowerboundary == null){
                        lowerboundary = value;
                    }else{
-                       lowerboundary = Math.min(lowerboundary, value);
+                       lowerboundary = lowerboundary.min(value);
                    }
                }
                if(type.isMax() || type.isTypicalUpperBoundary() || type.isAverage() || type.isExactValue()){
                 if (upperboundary == null){
                     upperboundary = value;
                 }else{
-                    upperboundary = Math.max(upperboundary, value);
+                    upperboundary = upperboundary.max(value);
                 }
             }
 
index be8615cba5d75d78c853176d05ede2c34f0d0aaf..006f8e68a89a32a9c3788bc36db0cdb8a9ca1e3a 100644 (file)
@@ -8,6 +8,8 @@
 */
 package eu.etaxonomy.cdm.format;
 
+import java.math.BigDecimal;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -37,9 +39,9 @@ public class QuantitativeDataFormatterTest {
 
     @Before
     public void setUp() throws Exception {
-        min1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), 0.1f);
-        max1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), 1.3f);
-        n1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.SAMPLE_SIZE(), 2);
+        min1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), new BigDecimal("0.1"));
+        max1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), new BigDecimal("1.3"));
+        n1 = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.SAMPLE_SIZE(), new BigDecimal("2"));
     }
 
     @BeforeClass
@@ -65,8 +67,6 @@ public class QuantitativeDataFormatterTest {
         quantData.setUnit(unit);
 
         text = formatter.format(quantData, formatKey);
-        Assert.assertEquals("0.1-1.3 m [n=2.0]", text);
-
+        Assert.assertEquals("0.1-1.3 m [n=2]", text);
     }
-
 }
index 5be616278ce804a858b3efdb524ecf921bd57cfe..a990b3046ac3f5ce453b84428a5f7dce8240b3d4 100644 (file)
@@ -11,6 +11,8 @@ package eu.etaxonomy.cdm.model.description;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
 
+import java.math.BigDecimal;
+
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -41,15 +43,15 @@ public class QuantitativeDataTest {
 
         StatisticalMeasurementValue statisticalValue = StatisticalMeasurementValue.NewInstance();
         statisticalValue.setType(StatisticalMeasure.AVERAGE() );
-        statisticalValue.setValue((float) 23.8);
+        statisticalValue.setValue(new BigDecimal("23.8"));
         quantData.addStatisticalValue(statisticalValue);
     }
 
     @Test
     public void testClone(){
         QuantitativeData clone = (QuantitativeData) quantData.clone();
-        float cloneValue = clone.getStatisticalValues().iterator().next().getValue();
-        float origValue = quantData.getStatisticalValues().iterator().next().getValue();
+        BigDecimal cloneValue = clone.getStatisticalValues().iterator().next().getValue();
+        BigDecimal origValue = quantData.getStatisticalValues().iterator().next().getValue();
         assertTrue(origValue == cloneValue);
         assertNotSame(clone.getStatisticalValues().iterator().next(), quantData.getStatisticalValues().iterator().next());
     }
index 3476834b9cc509e7d415530a7387cbe777c9bf7b..fc13ac4fa66bafe5e3e99f05ed2e74c7be87c158 100644 (file)
@@ -2,6 +2,7 @@ package eu.etaxonomy.cdm.strategy.generate;
 
 import static org.junit.Assert.assertNotNull;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -54,9 +55,9 @@ public class PolytomousKeyGeneratorTest {
     private static final boolean debug = true;
 
     private static final String GT_3 = " > 3.0";
-    private static final String GT_3_5 = " > 3.5";
+//    private static final String GT_3_5 = " > 3.5";
     private static final String LESS_3 = " < 3.0";
-    private static final String LESS_3_5 = " < 3.5";
+//    private static final String LESS_3_5 = " < 3.5";
 
        private static final boolean QUANTITATIVE = true;
        private static final boolean CATEGORICAL = false;
@@ -195,15 +196,15 @@ public class PolytomousKeyGeneratorTest {
 
                //*************************/
 
-               qtd31 = QuantitativeData.NewExactValueInstance(featureLength, 0.0f, 3.0f);
+               qtd31 = QuantitativeData.NewExactValueInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
 //        qtd31 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
-               qtd32 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
-               qtd33 = QuantitativeData.NewMinMaxInstance(featureLength, 6, 9);
-               qtd34 = QuantitativeData.NewMinMaxInstance(featureLength, 6, 9);
-               qtd35 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
-               qtd36 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
-               qtd37 = QuantitativeData.NewMinMaxInstance(featureLength, 6, 9);
-               qtd38 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
+               qtd32 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
+               qtd33 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
+               qtd34 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
+               qtd35 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
+               qtd36 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
+               qtd37 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
+               qtd38 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
 
                //*************************/
 
index 1a5eff27845ee275c1800b771d560b721892d08a..41cb84dceb68e75979b04325daac885ac7f8f9de 100644 (file)
@@ -3,6 +3,7 @@
  */
 package eu.etaxonomy.cdm.database.data;
 
+import java.math.BigDecimal;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
@@ -330,7 +331,9 @@ public class FullCoverageDataGenerator {
                MeasurementUnit measurementUnit = MeasurementUnit.NewInstance("Measurement Unit", "munit", null);
                cdmBases.add(measurementUnit);
                quantitativeData.setUnit(measurementUnit);
-               StatisticalMeasurementValue statisticalMeasurementValue = quantitativeData.setAverage((float)22.9 , null);
+               quantitativeData.setUuid(UUID.fromString("920fce5e-4913-4a3f-89bf-1611f5081869"));
+               StatisticalMeasurementValue statisticalMeasurementValue = quantitativeData.setAverage(
+                       new BigDecimal("22.9215"), null);
                handleAnnotatableEntity(quantitativeData);
                handleIdentifiableEntity(measurementUnit);
                DefinedTerm valueModifier = DefinedTerm.NewModifierInstance("about", "about", null);
index 005ea994129608e9c63b1b0f97831f8cb44faffd..aa5d3d18f350ee623f430f35760d94b925463995 100644 (file)
@@ -26,7 +26,8 @@ public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
 \r
        private final String newColumnName;\r
        private final Datatype columnType;\r
-       private Integer size;\r
+       private final Integer size;\r
+       private final Integer scale;\r
        private final Object defaultValue;\r
        private boolean isNotNull;\r
        private final String referencedTable;\r
@@ -38,59 +39,64 @@ public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
         * @return\r
         */\r
        public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull, String referencedTable){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, includeAudTable, null, notNull, referencedTable);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, null, notNull, referencedTable);\r
        }\r
 \r
        public static final ColumnAdder NewIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Integer defaultValue, boolean notNull){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, includeAudTable, defaultValue, notNull, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.INTEGER, null, null, includeAudTable, defaultValue, notNull, null);\r
        }\r
 \r
        public static final ColumnAdder NewTinyIntegerInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.TINYINTEGER, null, includeAudTable, null, notNull, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.TINYINTEGER, null, null, includeAudTable, null, notNull, null);\r
        }\r
 \r
        public static final ColumnAdder NewDoubleInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DOUBLE, null, includeAudTable, null, notNull, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DOUBLE, null, null, includeAudTable, null, notNull, null);\r
        }\r
 \r
+    public static final ColumnAdder NewDecimalInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int precision,  int scale, boolean includeAudTable, Integer defaultValue, boolean notNull){\r
+        return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIGDECIMAL, precision, scale, includeAudTable, defaultValue, notNull, null);\r
+    }\r
+\r
        public static final ColumnAdder NewBooleanInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, Boolean defaultValue){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIT, null, includeAudTable, defaultValue, false, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.BIT, null, null, includeAudTable, defaultValue, false, null);\r
        }\r
 \r
        /**\r
         * Adds a string column with length 255 and default value <code>null</code>\r
         */\r
        public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, 255, includeAudTable, null, false, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, 255, null, includeAudTable, null, false, null);\r
        }\r
 \r
     public static final ColumnAdder NewDTYPEInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String defaultValue, boolean includeAudTable){\r
-        return new ColumnAdder(stepList, stepName, tableName, "DTYPE", Datatype.VARCHAR, 31, includeAudTable, defaultValue, true, null);\r
+        return new ColumnAdder(stepList, stepName, tableName, "DTYPE", Datatype.VARCHAR, 31, null, includeAudTable, defaultValue, true, null);\r
     }\r
 \r
     /**\r
      * Adds a string column with the given length and default value <code>null</code>\r
      */\r
        public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, boolean includeAudTable){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, includeAudTable, null, false, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, null, false, null);\r
        }\r
 \r
     /**\r
      * Adds a string column with the given length and the given default value\r
      */\r
     public static final ColumnAdder NewStringInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, int length, String defaultValue, boolean includeAudTable){\r
-        return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, includeAudTable, defaultValue, false, null);\r
+        return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.VARCHAR, length, null, includeAudTable, defaultValue, false, null);\r
     }\r
 \r
        public static final ColumnAdder NewClobInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.CLOB, null, includeAudTable, null, false, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.CLOB, null, null, includeAudTable, null, false, null);\r
        }\r
 \r
        public static final ColumnAdder NewDateTimeInstance(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, boolean includeAudTable, boolean notNull){\r
-               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DATETIME, null, includeAudTable, null, notNull, null);\r
+               return new ColumnAdder(stepList, stepName, tableName, newColumnName, Datatype.DATETIME, null, null, includeAudTable, null, notNull, null);\r
        }\r
 \r
-       protected ColumnAdder(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName, Datatype columnType, Integer size, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {\r
+       protected ColumnAdder(List<? extends ISchemaUpdaterStep> stepList, String stepName, String tableName, String newColumnName,\r
+               Datatype columnType, Integer size, Integer scale, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {\r
                super(stepList, stepName, tableName, includeAudTable);\r
                this.newColumnName = newColumnName;\r
                this.columnType = columnType;\r
@@ -98,6 +104,7 @@ public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
                this.defaultValue = defaultValue;\r
                this.isNotNull = notNull;\r
                this.referencedTable = referencedTable;\r
+               this.scale = scale;\r
        }\r
 \r
        public ColumnAdder setNotNull(boolean isNotNull) {\r
@@ -156,7 +163,7 @@ public class ColumnAdder extends AuditedSchemaUpdaterStepBase {
        public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {\r
                String updateQuery;\r
                DatabaseTypeEnum type = datasource.getDatabaseType();\r
-               String databaseColumnType = this.columnType.format(datasource, size);\r
+               String databaseColumnType = this.columnType.format(datasource, size, scale);\r
 \r
                if (type.equals(DatabaseTypeEnum.SqlServer2005)){\r
                        //MySQL allows both syntaxes\r
index d62d7890e47ea7c526c7ad0e2466c096d7f7c8a2..2aa86c03b1d39057991397319aea2f7b4d15fec6 100644 (file)
@@ -35,6 +35,10 @@ public class ColumnNameChanger
                return new ColumnNameChanger(stepList, stepName, tableName, oldColumnName, newColumnName, includeAudTable, null, Datatype.INTEGER, null);\r
        }\r
 \r
+    public static ColumnNameChanger NewFloatInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String oldColumnName, String newColumnName, boolean includeAudTable){\r
+        return new ColumnNameChanger(stepList, stepName, tableName, oldColumnName, newColumnName, includeAudTable, null, Datatype.FLOAT, null);\r
+    }\r
+\r
        public static ColumnNameChanger NewClobInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String oldColumnName,\r
                String newColumnName, boolean includeAudTable){\r
                return new ColumnNameChanger(stepList, stepName, tableName, oldColumnName, newColumnName, includeAudTable, null, Datatype.CLOB, null);\r
index 8fb891794d724d4c3a938040838e361e8b12fb84..c7a304aceb0992dcd6edb3b9eb79637e2bef5340 100644 (file)
@@ -31,44 +31,55 @@ public class ColumnTypeChanger
        private final String columnName;\r
        private final Datatype newColumnType;\r
        private final Integer newSize;\r
+       private final Integer newScale;\r
        private final Object defaultValue;\r
        private final boolean isNotNull;\r
        private final String referencedTable;\r
+       private final boolean isBigDecimal;\r
+       private String secondColumnName;\r
 \r
 \r
        public static final ColumnTypeChanger NewStringSizeInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, int newSize, boolean includeAudTable){\r
-               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, newSize, includeAudTable, null, false, null);\r
+               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, newSize, null, includeAudTable, null, false, null, false, null);\r
        }\r
 \r
        public static final ColumnTypeChanger NewClobInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, boolean includeAudTable){\r
-               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.CLOB, null, includeAudTable, null, false, null);\r
+               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.CLOB, null, null, includeAudTable, null, false, null, false, null);\r
        }\r
 \r
        public static final ColumnTypeChanger NewInt2DoubleInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, boolean includeAudTable){\r
-               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.DOUBLE, null, includeAudTable, null, false, null);\r
+               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.DOUBLE, null, null, includeAudTable, null, false, null, false, null);\r
        }\r
 \r
        public static final ColumnTypeChanger NewInt2StringInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, int size, boolean includeAudTable, Integer defaultValue, boolean notNull){\r
-               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, size, includeAudTable, defaultValue, notNull, null);\r
+               return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, size, null, includeAudTable, defaultValue, notNull, null, false, null);\r
        }\r
 \r
        public static final ColumnTypeChanger NewChangeAllowNullOnIntChanger(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, boolean includeAudTable){\r
-           return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.INTEGER, null, includeAudTable, null, false, null);\r
+           return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.INTEGER, null, null, includeAudTable, null, false, null, false, null);\r
        }\r
 \r
     public static final ColumnTypeChanger NewChangeAllowNullOnStringChanger(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, Integer size, boolean includeAudTable){\r
-        return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, size, includeAudTable, null, false, null);\r
+        return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.VARCHAR, size, null, includeAudTable, null, false, null, false, null);\r
     }\r
 \r
+    public static final ColumnTypeChanger NewFloat2BigDecimalInstance(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, String newSecondColumnName, int newPrecision, int newScale, boolean includeAudTable){\r
+        return new ColumnTypeChanger(stepList, stepName, tableName, columnName, Datatype.BIGDECIMAL, newPrecision, newScale, includeAudTable, null, false, null, true, newSecondColumnName);\r
+    }\r
+\r
+       protected ColumnTypeChanger(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, Datatype newColumnType, Integer size, Integer scale,\r
+               boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable, boolean isBigDecimal, String secondColumnName) {\r
 \r
-       protected ColumnTypeChanger(List<ISchemaUpdaterStep> stepList, String stepName, String tableName, String columnName, Datatype newColumnType, Integer size, boolean includeAudTable, Object defaultValue, boolean notNull, String referencedTable) {\r
-               super(stepList, stepName, tableName, includeAudTable);\r
+           super(stepList, stepName, tableName, includeAudTable);\r
                this.columnName = columnName;\r
                this.newColumnType = newColumnType;\r
                this.newSize = size;\r
                this.defaultValue = defaultValue;\r
                this.isNotNull = notNull;\r
                this.referencedTable = referencedTable;\r
+               this.newScale = scale;\r
+               this.isBigDecimal = isBigDecimal;\r
+               this.secondColumnName = secondColumnName;\r
        }\r
 \r
     @Override\r
@@ -139,7 +150,7 @@ public class ColumnTypeChanger
        public String getUpdateQueryString(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {\r
                String updateQuery;\r
                DatabaseTypeEnum type = datasource.getDatabaseType();\r
-               String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType, newSize);\r
+               String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType, newSize, newScale);\r
 \r
                if (type.equals(DatabaseTypeEnum.SqlServer2005)){\r
                        //MySQL allows both syntaxes\r
@@ -191,7 +202,7 @@ public class ColumnTypeChanger
         //\r
         boolean includeAuditing = false;\r
         String colNameChanged = this.columnName + _OLDXXX;\r
-        String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType, newSize);\r
+        String databaseColumnType = getDatabaseColumnType(datasource, this.newColumnType, newSize, newScale);\r
 \r
         //change old column name\r
         //note data type is not relevant for ColumnNameChanger with Postgres\r
@@ -202,7 +213,7 @@ public class ColumnTypeChanger
         //create new column\r
 //        step = ColumnAdder.NewStringInstance(this.stepName + " - Add new column", tableName, this.columnName, includeAuditing);\r
         Object defaultValue = null;\r
-        step = new ColumnAdder(null, this.stepName + " - Add new column", tableName, this.columnName, newColumnType, newSize, includeAuditing, defaultValue, false, null);\r
+        step = new ColumnAdder(null, this.stepName + " - Add new column", tableName, this.columnName, newColumnType, newSize, null, includeAuditing, defaultValue, false, null);\r
         step.invoke(datasource, monitor, caseType, result);\r
 \r
         //move data\r
@@ -224,8 +235,8 @@ public class ColumnTypeChanger
         step.invoke(datasource, monitor, caseType, result);\r
     }\r
 \r
-       private String getDatabaseColumnType(ICdmDataSource datasource, Datatype columnType, Integer size) {\r
-               return columnType.format(datasource, size);\r
+       private String getDatabaseColumnType(ICdmDataSource datasource, Datatype columnType, Integer size, Integer scale) {\r
+               return columnType.format(datasource, size, scale);\r
        }\r
 \r
        public String getReferencedTable() {\r
index 578155aef479c49d6ae7fd32b5ffcc31eaad7f7c..d50252224d9ef9b083ea1b472af3367b6c92ca0a 100644 (file)
@@ -23,8 +23,10 @@ public enum Datatype {
     VARCHAR("varchar"),
     DATETIME("datetime"),
     DOUBLE("double"),
+    FLOAT("float"),
     TINYINTEGER("tinyint"),
-    BIT("bit")
+    BIT("bit"),
+    BIGDECIMAL("decimal"),
     ;
 
     private String defaultStr;
@@ -34,14 +36,19 @@ public enum Datatype {
     }
 
     public String format(ICdmDataSource datasource, Integer size) {
+        return format(datasource, size, null);
+    }
+
+    public String format(ICdmDataSource datasource, Integer size, Integer scale) {
         String result = defaultStr;
         DatabaseTypeEnum dbType = datasource.getDatabaseType();
         //nvarchar
         if (dbType.equals(DatabaseTypeEnum.PostgreSQL)){  //TODO use PostgeSQL82 Dialect infos
             result = result.replace("nvarchar", "varchar");
-            result = result.replace("double", "float8");
-            result = result.replace("bit", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.BIT));
-            result = result.replace("datetime", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.TIMESTAMP));
+            result = result.replace("float", dbType.getHibernateDialect().getTypeName(Types.FLOAT));
+            result = result.replace("double", dbType.getHibernateDialect().getTypeName(Types.DOUBLE));
+            result = result.replace("bit", dbType.getHibernateDialect().getTypeName(Types.BIT));
+            result = result.replace("datetime", dbType.getHibernateDialect().getTypeName(Types.TIMESTAMP));
             result = result.replace("tinyint", DatabaseTypeEnum.PostgreSQL.getHibernateDialect().getTypeName(Types.TINYINT));
         }
         //CLOB
@@ -58,6 +65,8 @@ public enum Datatype {
             }
         }else if (this == VARCHAR){
             result = result + "(" + size + ")";
+        }else if (this == BIGDECIMAL){
+            result = result + "(" + size + "," + scale + ")";
         }
         return result;
     }
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/Float2BigDecimalTypeChanger.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/Float2BigDecimalTypeChanger.java
new file mode 100644 (file)
index 0000000..0d4a2a8
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+* Copyright (C) 2020 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.database.update;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+import eu.etaxonomy.cdm.database.ICdmDataSource;
+import eu.etaxonomy.cdm.hibernate.BigDecimalUserType;
+
+/**
+ * Changes a float column into a BigDecimal (2 columns) for usage in {@link BigDecimalUserType}.
+ * First renames the old column and creates the 2 new columns via {@link #getInnerSteps() inner steps}
+ * and then fills the new columns by using the mapping algorithm defined in
+ *  {@link #invokeOnTable(String, ICdmDataSource, IProgressMonitor, CaseType, SchemaUpdateResult)}.
+ *
+ * Does not yet delete the old column. This should be done in a follow up model update.
+ *
+ * @author a.mueller
+ * @since 27.04.2020
+ */
+public class Float2BigDecimalTypeChanger extends AuditedSchemaUpdaterStepBase {
+
+    private static final String OLD_POSTFIX = "_old";
+
+    private final String valueColumn;
+    private final String scaleColumn;
+    private final Integer newScale;
+    private final Integer newPrecision;
+    private final boolean isNotNull = false;
+    private boolean isRounder = false;
+
+    public static Float2BigDecimalTypeChanger NewInstance (List<? extends ISchemaUpdaterStep> stepList,
+            String stepName, String tableName, String valueColumn, String scaleColumn, Integer newPrecision, Integer newScale,
+            boolean includedAudTable){
+        return new Float2BigDecimalTypeChanger(stepList, stepName, tableName, valueColumn,
+                scaleColumn, newPrecision, newScale, includedAudTable);
+    }
+
+    protected Float2BigDecimalTypeChanger(List<? extends ISchemaUpdaterStep> stepList,
+            String stepName, String tableName, String valueColumn, String scaleColumn,
+            Integer newPrecision, Integer newScale, boolean includedAudTable) {
+        super(stepList, stepName, tableName, includedAudTable);
+        this.valueColumn = valueColumn;
+        this.scaleColumn = scaleColumn;
+        this.newPrecision = newPrecision;
+        this.newScale = newScale;
+    }
+
+    @Override
+    protected void invokeOnTable(String tableName, ICdmDataSource datasource, IProgressMonitor monitor,
+            CaseType caseType, SchemaUpdateResult result) {
+        if(!isRounder){
+            return;
+        }
+        String sql = "SELECT id, %s as value_float FROM %s as t ";
+        sql = String.format(sql, valueColumn+OLD_POSTFIX, tableName);
+        try {
+            ResultSet rs = datasource.executeQuery(sql);
+            while(rs.next()){
+                Float floatValue = rs.getFloat("value_float");
+                BigDecimal bd = new BigDecimal(floatValue.toString());
+                int id = rs.getInt("id");
+                sql = "UPDATE %s SET %s = %s, %s = %s WHERE id = %s ";
+                sql = String.format(sql, tableName, valueColumn, bd.toString(), scaleColumn, bd.scale(), String.valueOf(id));
+                datasource.executeUpdate(sql);
+            }
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    @Override
+    public List<ISchemaUpdaterStep> getInnerSteps() {
+        if(!isRounder){
+            List<ISchemaUpdaterStep> result = new ArrayList<>();
+            ColumnNameChanger.NewFloatInstance(result, stepName +  " - rename old column", tableName, valueColumn, valueColumn + OLD_POSTFIX, includeAudTable);
+            ColumnAdder.NewDecimalInstance(result, stepName + " - add value column", tableName, valueColumn, newPrecision, newScale, includeAudTable, 0, isNotNull);
+            ColumnAdder.NewIntegerInstance(result, stepName + " - add scale column", tableName, scaleColumn, includeAudTable, newScale, isNotNull);
+            Float2BigDecimalTypeChanger rounder = Float2BigDecimalTypeChanger.NewInstance(result, stepName + " - round", tableName, valueColumn, scaleColumn, newPrecision, newScale, this.includeAudTable);
+            rounder.isRounder = true;
+            return result;
+        }else{
+            return super.getInnerSteps();
+        }
+    }
+
+}
index 235e837e84c42d1df628ab40046a7cfdaa26eff4..575e30876801da5f7f73f3e1609f1e7ce8cc8ae7 100644 (file)
@@ -15,6 +15,7 @@ import java.util.UUID;
 
 import org.apache.log4j.Logger;
 
+import eu.etaxonomy.cdm.database.update.Float2BigDecimalTypeChanger;
 import eu.etaxonomy.cdm.database.update.ISchemaUpdater;
 import eu.etaxonomy.cdm.database.update.ISchemaUpdaterStep;
 import eu.etaxonomy.cdm.database.update.SchemaUpdaterBase;
@@ -70,6 +71,16 @@ public class SchemaUpdater_5120_5150 extends SchemaUpdaterBase {
         TermRepresentationUpdater.NewInstanceWithTitleCache(stepList, stepName,
                 uuidTerm, label, label, null, uuidLanguage);
 
+        //#8978
+        stepName = "make statistical measurment value BigDecimal";
+        tableName = "StatisticalMeasurementValue";
+        columnName = "value";
+        String scaleColumnName = "value_scale";
+        int newPrecision = 18;
+        int newScale = 9;
+//        ColumnTypeChanger.NewFloat2BigDecimalInstance(stepList, stepName, tableName, columnName, scaleColumnName, newPrecision, newScale, INCLUDE_AUDIT);
+        Float2BigDecimalTypeChanger.NewInstance(stepList, stepName, tableName, columnName, scaleColumnName, newPrecision, newScale, INCLUDE_AUDIT);
+
         return stepList;
     }
 
diff --git a/cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/hibernate/BigDecimalUserTypeTest.java b/cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/hibernate/BigDecimalUserTypeTest.java
new file mode 100644 (file)
index 0000000..4e39d0a
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+* Copyright (C) 2020 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.hibernate;
+
+import java.io.FileNotFoundException;
+import java.math.BigDecimal;
+import java.sql.Types;
+
+import org.hibernate.usertype.UserType;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
+
+/**
+ * Test for {@link BigDecimalUserType}
+ *
+ * The test class has originally been copied and adapted from
+ * https://bitbucket.org/ratkins/bigdecimalusertype/src/default/BigDecimalUserTypeTest.java
+ *
+ * @author a.mueller
+ * @since 27.04.2020
+ */
+public class BigDecimalUserTypeTest extends CdmTransactionalIntegrationTest {
+
+//    @Mock ResultSet rs;
+//    @Mock PreparedStatement st;
+
+    private UserType userType;
+
+    @Before
+    public void setUp() throws Exception {
+        userType = new BigDecimalUserType();
+    }
+
+    @Test
+    public void testNotMutable() throws Exception {
+        Assert.assertFalse(userType.isMutable());
+    }
+
+    @Test
+    public void testDeepCopyReturnsSelf() throws Exception {
+        BigDecimal foo = new BigDecimal("1.0");
+        Assert.assertSame(userType.deepCopy(foo), foo);
+    }
+
+    @Test
+    public void testReturnedClassIsBigDecimal() throws Exception {
+        Assert.assertEquals(userType.returnedClass(), BigDecimal.class);
+        Assert.assertTrue(userType.returnedClass() == BigDecimal.class);
+    }
+
+    @Test
+    public void testEqualsDelegatesToBigDecimalEquals() throws Exception {
+        Assert.assertTrue(userType.equals(new BigDecimal("1.0"), new BigDecimal("1.0")));
+        Assert.assertFalse(userType.equals(new BigDecimal("1.0"), new BigDecimal("2.0")));
+        Assert.assertFalse(userType.equals(new BigDecimal("1.0"), new BigDecimal("1.00")));
+    }
+
+    @Test
+    public void testHashCodeDelegatesToBigDecimalHashCode() throws Exception {
+        Assert.assertEquals(userType.hashCode(new BigDecimal("1.0")), new BigDecimal("1.0").hashCode());
+        Assert.assertNotEquals(userType.hashCode(new BigDecimal("1.00")), new BigDecimal("1.0").hashCode());
+        Assert.assertNotEquals(userType.hashCode(new BigDecimal("1.0")), new BigDecimal("2.0").hashCode());
+    }
+
+    @Test
+    public void testAssembleReturnsSelf() throws Exception {
+        BigDecimal foo = new BigDecimal("1.0");
+        Assert.assertSame(userType.assemble(foo, null), foo);
+    }
+
+    @Test
+    public void testDisassembleReturnsSelf() throws Exception {
+        BigDecimal foo = new BigDecimal("1.0");
+        Assert.assertSame(userType.disassemble(foo), foo);
+        Assert.assertEquals(userType.disassemble(null), (BigDecimal) null);
+    }
+
+    @Test
+    public void testReplaceReturnsSelf() throws Exception {
+        BigDecimal foo = new BigDecimal("1.0");
+        Assert.assertSame(userType.replace(foo, null, null), foo);
+        Assert.assertEquals(userType.disassemble(null), (BigDecimal) null);
+    }
+
+    @Test
+    public void testSqlTypesAreNumberAndInteger() throws Exception {
+        Assert.assertEquals(userType.sqlTypes().length, 2);
+        Assert.assertEquals(userType.sqlTypes()[0], Types.NUMERIC);   //Types.DECIMAL does not work (tested for MySQL)
+        Assert.assertEquals(userType.sqlTypes()[1], Types.INTEGER);
+    }
+
+    //TODO update tests from original file
+//    @Test
+//    public void testNullSafeGet() throws Exception {
+//        when(rs.getBigDecimal("a")).thenReturn(new BigDecimal("1.000000"));
+//        when(rs.getInt("b")).thenReturn(1);
+//
+//        assertThat((BigDecimal) userType.nullSafeGet(rs, new String[] {"a", "b"}, null), equalTo(new BigDecimal("1.0")));
+//    }
+//
+//    @Test
+//    public void testNullSafeGetWithNull() throws Exception {
+//        when(rs.getBigDecimal("a")).thenReturn(null);
+//        when(rs.getInt("b")).thenReturn(0);
+//
+//        assertThat((BigDecimal) userType.nullSafeGet(rs, new String[] {"a", "b"}, null), equalTo((BigDecimal) null));
+//    }
+//
+//    @Test
+//    public void testNullSafeSet() throws Exception {
+//        userType.nullSafeSet(st, new BigDecimal("1.000"), 17);
+//
+//        verify(st).setBigDecimal(17, new BigDecimal("1.000"));
+//        verify(st).setInt(18, 3);
+//    }
+//
+//    @Test
+//    public void testNullSafeSetWithNull() throws Exception {
+//        userType.nullSafeSet(st, null, 17);
+//
+//        verify(st).setNull(17, Types.NUMERIC);
+//        verify(st).setNull(18, Types.INTEGER);
+//    }
+
+
+    @Override
+    public void createTestDataSet() throws FileNotFoundException {}
+}
index 8406beef4f30e1f59019815ae3ac5b75d51f6020..ebc1c32a3fe62a81735d26ea0d44bb50b08a8567 100644 (file)
@@ -1,5 +1,6 @@
 package eu.etaxonomy.cdm.api.service;
 
+import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -12,15 +13,17 @@ import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
 import eu.etaxonomy.cdm.model.description.TextData;
 
 public abstract class AbstractQuantitativeDescriptionBuilder extends DescriptionBuilder<QuantitativeData>{
-       
-       public TextData build(QuantitativeData data, List<Language> languages) {
-                  Map<StatisticalMeasure,Float> measures = new HashMap<StatisticalMeasure,Float>();
+
+       @Override
+    public TextData build(QuantitativeData data, List<Language> languages) {
+                  Map<StatisticalMeasure,BigDecimal> measures = new HashMap<>();
                   for (StatisticalMeasurementValue smv : data.getStatisticalValues()){
                     measures.put(smv.getType(),smv.getValue());
                   }
                   return doBuild(measures,data.getUnit(), languages);
                 }
-                
-       protected abstract TextData doBuild(Map<StatisticalMeasure,Float> measures, MeasurementUnit unit, List<Language> languages);
+
+       protected abstract TextData doBuild(Map<StatisticalMeasure,BigDecimal> measures,
+               MeasurementUnit unit, List<Language> languages);
 
 }
index ecdafb0360ed15e3840ca8a96a469415c76f9cd4..42ffe988786310c281adebd9b4e5c698cde42e55 100644 (file)
@@ -1,5 +1,6 @@
 package eu.etaxonomy.cdm.api.service;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -18,7 +19,7 @@ public class DefaultQuantitativeDescriptionBuilder extends AbstractQuantitativeD
        private String space = " ";
 
        @Override
-       protected TextData doBuild(Map<StatisticalMeasure,Float> measures, MeasurementUnit mUnit, List<Language> languages){
+       protected TextData doBuild(Map<StatisticalMeasure,BigDecimal> measures, MeasurementUnit mUnit, List<Language> languages){
                StringBuilder QuantitativeDescription = new StringBuilder(); // this StringBuilder is used to concatenate the different words of the description before saving it in the TextData
                TextData textData = TextData.NewInstance(); // TextData that will contain the description and the language corresponding
                // booleans indicating whether a kind of value is present or not and the float that will eventually hold the value
@@ -45,22 +46,46 @@ public class DefaultQuantitativeDescriptionBuilder extends AbstractQuantitativeD
                // the booleans and floats are updated according to the presence or absence of values
 
                Boolean max, min, upperb, lowerb, average, sd;
-               
-               String averagevalue = getValue(measures,StatisticalMeasure.AVERAGE());
-               if (averagevalue!=null) average=true; else average=false;
-               String sdvalue = getValue(measures,StatisticalMeasure.STANDARD_DEVIATION());
-               if (sdvalue!=null) sd=true; else sd=false;
-               String minvalue = getValue(measures,StatisticalMeasure.MIN());
-               if (minvalue!=null) min=true; else min=false;
-               String maxvalue = getValue(measures,StatisticalMeasure.MAX());
-               if (maxvalue!=null) max=true; else max=false;
-               String lowerbvalue = getValue(measures,StatisticalMeasure.TYPICAL_LOWER_BOUNDARY());
-               if (lowerbvalue!=null) lowerb=true; else lowerb=false;
-               String upperbvalue = getValue(measures,StatisticalMeasure.TYPICAL_UPPER_BOUNDARY());
-               if (upperbvalue!=null) upperb=true; else upperb=false;
-               
-               
-               // depending on the different associations of values, a sentence is built       
+
+               String averagevalue = getValue(measures, StatisticalMeasure.AVERAGE());
+               if (averagevalue!=null) {
+            average=true;
+        } else {
+            average=false;
+        }
+               String sdvalue = getValue(measures, StatisticalMeasure.STANDARD_DEVIATION());
+               if (sdvalue!=null) {
+            sd=true;
+        } else {
+            sd=false;
+        }
+               String minvalue = getValue(measures, StatisticalMeasure.MIN());
+               if (minvalue!=null) {
+            min=true;
+        } else {
+            min=false;
+        }
+               String maxvalue = getValue(measures, StatisticalMeasure.MAX());
+               if (maxvalue!=null) {
+            max=true;
+        } else {
+            max=false;
+        }
+               String lowerbvalue = getValue(measures, StatisticalMeasure.TYPICAL_LOWER_BOUNDARY());
+               if (lowerbvalue!=null) {
+            lowerb=true;
+        } else {
+            lowerb=false;
+        }
+               String upperbvalue = getValue(measures, StatisticalMeasure.TYPICAL_UPPER_BOUNDARY());
+               if (upperbvalue!=null) {
+            upperb=true;
+        } else {
+            upperb=false;
+        }
+
+
+               // depending on the different associations of values, a sentence is built
                if (max && min) {
                        QuantitativeDescription.append(space + from + space + minvalue + space + to + space + maxvalue + space + unit);
                }
@@ -111,8 +136,8 @@ public class DefaultQuantitativeDescriptionBuilder extends AbstractQuantitativeD
         * @param key the desired measure
         * @return
         */
-       private String getValue(Map<StatisticalMeasure,Float> measures, Object key) {
-               Float floatValue;
+       private String getValue(Map<StatisticalMeasure,BigDecimal> measures, Object key) {
+               BigDecimal floatValue;
                Integer intValue;
                if(measures.containsKey(key)) {
                        floatValue = measures.get(key);
index 01cc054cf1f18cf9f855730faa26f5af4b7a3e50..b164b6408788660c9b05886c9bf33e137c3745f1 100644 (file)
@@ -1,5 +1,6 @@
 package eu.etaxonomy.cdm.api.service;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -660,15 +661,15 @@ public class DescriptiveDataSetService
 
         private DescriptionElementBase element;
         private Set<UUID> stateUuids = new HashSet<>();
-        private Set<Float> avgs = new HashSet<>();
-        private Set<Float> exacts = new HashSet<>();
-        private Set<Float> maxs = new HashSet<>();
-        private Set<Float> mins = new HashSet<>();
-        private Set<Float> sampleSizes = new HashSet<>();
-        private Set<Float> standardDevs = new HashSet<>();
-        private Set<Float> lowerBounds = new HashSet<>();
-        private Set<Float> upperBounds = new HashSet<>();
-        private Set<Float> variances = new HashSet<>();
+        private Set<BigDecimal> avgs = new HashSet<>();
+        private Set<BigDecimal> exacts = new HashSet<>();
+        private Set<BigDecimal> maxs = new HashSet<>();
+        private Set<BigDecimal> mins = new HashSet<>();
+        private Set<BigDecimal> sampleSizes = new HashSet<>();
+        private Set<BigDecimal> standardDevs = new HashSet<>();
+        private Set<BigDecimal> lowerBounds = new HashSet<>();
+        private Set<BigDecimal> upperBounds = new HashSet<>();
+        private Set<BigDecimal> variances = new HashSet<>();
 
         public DescriptionElementCompareWrapper(DescriptionElementBase element) {
             this.element = element;
index f5f4ae125699f6e13f96fc81b0aaaeaa7eda40dc..4db9358d6995312e4a6c1fa5a11fef3943dd4502 100644 (file)
@@ -1,5 +1,6 @@
 package eu.etaxonomy.cdm.api.service;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -18,7 +19,8 @@ public class MicroFormatQuantitativeDescriptionBuilder extends AbstractQuantitat
        private String spanEnd = "</span>";
 
        @Override
-       protected TextData doBuild(Map<StatisticalMeasure,Float> measures, MeasurementUnit mUnit, List<Language> languages){
+       protected TextData doBuild(Map<StatisticalMeasure,BigDecimal> measures, MeasurementUnit mUnit,
+               List<Language> languages){
                StringBuilder QuantitativeDescription = new StringBuilder(); // this StringBuilder is used to concatenate the different words of the description before saving it in the TextData
                TextData textData = TextData.NewInstance(); // TextData that will contain the description and the language corresponding
 
index a5ad3613c202f8a1ba5f68bc7c10cf01c8fc2632..412c073cc639e10e1203f02fbceb096d38a47099 100644 (file)
@@ -8,7 +8,9 @@
 */
 package eu.etaxonomy.cdm.api.service.description;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -17,6 +19,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import eu.etaxonomy.cdm.common.BigDecimalUtil;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
@@ -340,20 +343,21 @@ public class StructuredDescriptionAggregation
 
     private QuantitativeData aggregateSingleQuantitativeData(QuantitativeData sourceQd){
         QuantitativeData aggQD = QuantitativeData.NewInstance(sourceQd.getFeature());
-        Set<Float> exactValues = sourceQd.getExactValues();
+        Set<BigDecimal> exactValues = sourceQd.getExactValues();
         if(!exactValues.isEmpty()){
+            Comparator<BigDecimal> comp = Comparator.naturalOrder();
             // qd is not already aggregated
-            float exactValueSampleSize = exactValues.size();
-            float exactValueMin = new Float(exactValues.stream().mapToDouble(value->(double)value).min().getAsDouble());
-            float exactValueMax = new Float(exactValues.stream().mapToDouble(value->(double)value).max().getAsDouble());
-            float exactValueAvg = new Float(exactValues.stream().mapToDouble(value->(double)value).average().getAsDouble());
+            int exactValueSampleSize = exactValues.size();
+            BigDecimal exactValueMin = exactValues.stream().min(comp).get();
+            BigDecimal exactValueMax = exactValues.stream().max(comp).get();
+            BigDecimal exactValueAvg = BigDecimalUtil.average(exactValues);
             //TODO also check for typical boundary data
             if(sourceQd.getMin() == null && sourceQd.getMax() == null){
-                aggQD.setSampleSize(exactValueSampleSize, null);
+                aggQD.setSampleSize(new BigDecimal(exactValueSampleSize), null);
                 aggQD.setAverage(exactValueAvg, null);
             }
-            aggQD.setMinimum(sourceQd.getMin() == null ? exactValueMin: Math.min(sourceQd.getMin(), exactValueMin), null);
-            aggQD.setMaximum(sourceQd.getMax() == null ? exactValueMax: Math.min(sourceQd.getMax(), exactValueMax), null);
+            aggQD.setMinimum(sourceQd.getMin() == null ? exactValueMin: sourceQd.getMin().min(exactValueMin), null);
+            aggQD.setMaximum(sourceQd.getMax() == null ? exactValueMax: sourceQd.getMax().max(exactValueMax), null);
         }
         else{
             // qd has only min, max, ... but no exact values
@@ -366,9 +370,9 @@ public class StructuredDescriptionAggregation
     private QuantitativeData handleMissingValues(QuantitativeData qd) {
         qd = handleMissingMinOrMax(qd);
         if (qd != null && qd.getAverage() == null){
-            Float n = qd.getSampleSize();
+            BigDecimal n = qd.getSampleSize();
             if(n != null && !n.equals(0f)){
-                qd.setAverage((qd.getMax()+qd.getMin())/n, null);
+                qd.setAverage((qd.getMax().add(qd.getMin())).divide(n), null);
             }
         }
         return qd;
@@ -383,7 +387,7 @@ public class StructuredDescriptionAggregation
             MissingMaximumMode missingMaxMode) {
         if(aggQD.getMin() == null && aggQD.getMax() != null){
             if (missingMinMode == MissingMinimumMode.MinToZero) {
-                aggQD.setMinimum(0f, null);
+                aggQD.setMinimum(BigDecimal.valueOf(0f), null);
             }else if (missingMinMode == MissingMinimumMode.MinToMax){
                 aggQD.setMinimum(aggQD.getMax(), null);
             }else if (missingMinMode == MissingMinimumMode.SkipRecord){
@@ -404,22 +408,22 @@ public class StructuredDescriptionAggregation
 
         newQd = aggregateSingleQuantitativeData(newQd); //alternatively we could check, if newQd is already basically aggregated, but for this we need a cleear definition what the minimum requirements are and how ExactValues and MinMax if existing in parallel should be handled.
 
-        Float min = null;
-        Float max = null;
-        Float average = null;
-        Float sampleSize = null;
+        BigDecimal min = null;
+        BigDecimal max = null;
+        BigDecimal average = null;
+        BigDecimal sampleSize = null;
         newQd = handleMissingValues(newQd);
         if (newQd == null){
             return aggQd;
         }
-        min = Math.min(aggQd.getMin(), newQd.getMin());
-        max = Math.max(aggQd.getMax(), newQd.getMax());
-        if (newQd.getSampleSize()!= null && aggQd.getSampleSize() != null){
-            sampleSize = newQd.getSampleSize()+aggQd.getSampleSize();
+        min = aggQd.getMin().min(newQd.getMin());
+        max = aggQd.getMax().max(newQd.getMax());
+        if (newQd.getSampleSize() != null && aggQd.getSampleSize() != null){
+            sampleSize = newQd.getSampleSize().add(aggQd.getSampleSize());
         }
         if (sampleSize != null && !sampleSize.equals(0f) && aggQd.getAverage() != null && newQd.getAverage() != null){
-            float totalSum = aggQd.getAverage()*aggQd.getSampleSize() + newQd.getAverage() * newQd.getSampleSize();
-            average = new Float(totalSum/sampleSize);
+            BigDecimal totalSum = aggQd.getAverage().multiply(aggQd.getSampleSize()).add(newQd.getAverage().multiply(newQd.getSampleSize()));
+            average = totalSum.divide(sampleSize);
         }
         aggQd.setMinimum(min, null);
         aggQd.setMaximum(max, null);
@@ -428,8 +432,8 @@ public class StructuredDescriptionAggregation
         return aggQd;
     }
 
-    private static List<Float> getExactValues(QuantitativeData qd) {
-        List<Float> exactValues = qd.getStatisticalValues().stream()
+    private static List<BigDecimal> getExactValues(QuantitativeData qd) {
+        List<BigDecimal> exactValues = qd.getStatisticalValues().stream()
                 .filter(value->value.getType().equals(StatisticalMeasure.EXACT_VALUE()))
                 .map(exact->exact.getValue())
                 .collect(Collectors.toList());
index b67c5890bbf9aa8b0dd3ae612b9d0d46f9b84403..04deda70cd599ccfc08fe106e456ca038e8aeb9c 100644 (file)
@@ -10,6 +10,7 @@
 package eu.etaxonomy.cdm.api.service.dto;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -117,15 +118,15 @@ public abstract class RowWrapperDTO <T extends DescriptionBase> implements Seria
     private String generateQuantitativeDataString(QuantitativeData quantitativeData) {
         String displayData;
         displayData = "";
-        Float min = quantitativeData.getMin();
-        Float max = quantitativeData.getMax();
+        BigDecimal min = quantitativeData.getMin();
+        BigDecimal max = quantitativeData.getMax();
         if(min!=null||max!=null){
             displayData += "["+(min!=null?min.toString():"?")+"-"+(max!=null?max.toString():"?")+"] ";
         }
-        displayData += quantitativeData.getStatisticalValues().stream().
-        filter(value->value.getType().equals(StatisticalMeasure.EXACT_VALUE()))
-        .map(exact->Float.toString(exact.getValue()))
-        .collect(Collectors.joining(", "));
+        displayData += quantitativeData.getStatisticalValues().stream()
+                .filter(value->value.getType().equals(StatisticalMeasure.EXACT_VALUE()))
+                .map(exact->exact.getValue().toString())
+                .collect(Collectors.joining(", "));
         if (quantitativeData.getUnit() != null){
             displayData += " "+ quantitativeData.getUnit().getIdInVocabulary();
         }
@@ -174,7 +175,7 @@ public abstract class RowWrapperDTO <T extends DescriptionBase> implements Seria
                 texts.forEach(text->{
                     String string = text;
                     try {
-                        float exactValue = Float.parseFloat(string);
+                        BigDecimal exactValue = new BigDecimal(string);
                         quantitativeData.addStatisticalValue(StatisticalMeasurementValue.NewInstance(measure, exactValue));
                     } catch (NumberFormatException e) {
                     }
index cd5d64fa80f896232f52e2d29706bc70909e66fd..de7a512dab93d1a1258576c33146bcc7bcaac6fb 100644 (file)
@@ -4,6 +4,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.FileNotFoundException;
+import java.math.BigDecimal;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -73,7 +74,7 @@ public class NaturalLanguageGeneratorTest extends CdmIntegrationTest {
                MeasurementUnit munit = MeasurementUnit.NewInstance(null, "mm", null);
                StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance();
                smv.setType(StatisticalMeasure.AVERAGE());
-               smv.setValue(12);
+               smv.setValue(new BigDecimal(12));
                qd.addStatisticalValue(smv);
                qd.setUnit(munit);
                qd.setFeature(qFeature);
index d5a42c2a03237da474872b1dcfa1630af6a17b22..2575546c1aa3074b420b52eac84b086d1e525251 100644 (file)
@@ -9,6 +9,7 @@
 package eu.etaxonomy.cdm.api.service.description;
 
 import java.io.FileNotFoundException;
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -180,10 +181,10 @@ public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegr
         datasetService.save(dataSet);
 
         SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1");
-        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.MIN(), 5.0f);
+        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.MIN(), new BigDecimal("5.0"));
 
         SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2");
-        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.MAX(), 7.0f);
+        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.MAX(), new BigDecimal("7.0"));
 
         TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
         Assert.assertNotNull(tnLapsana);
@@ -307,17 +308,17 @@ public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegr
 
         SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1");
         addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
-        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), 5.0f);
+        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("5.0"));
         addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
 
         SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2");
         addCategoricalData(specDescAlpina2, uuidFeatureLeafPA, State.uuidPresent);
-        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), 7.0f);
+        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("7.0"));
         addCategoricalData(specDescAlpina2, uuidFeatureLeafColor, uuidLeafColorBlue);
 
         SpecimenDescription specDescAdenophora = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ADENOPHORA_UUID, "adenophora specimen");
         addCategoricalData(specDescAdenophora, uuidFeatureLeafPA, State.uuidPresent);
-        addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), 12.0f);
+        addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("12.0"));
         addCategoricalData(specDescAdenophora, uuidFeatureLeafColor, uuidLeafColorYellow);
 
         TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
@@ -383,7 +384,7 @@ public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegr
         Assert.assertEquals(stateUuid, stateData.getState().getUuid());
     }
 
-    private void addQuantitativeData(SpecimenDescription specDesc, UUID uuidFeature, StatisticalMeasure type, float value) {
+    private void addQuantitativeData(SpecimenDescription specDesc, UUID uuidFeature, StatisticalMeasure type, BigDecimal value) {
         Feature feature = (Feature)termService.find(uuidFeature);
         QuantitativeData qd = QuantitativeData.NewInstance(feature);
         StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(type, value);