killbill-aplcache

improved property validation

2/28/2012 8:48:11 PM

Details

diff --git a/util/src/main/java/com/ning/billing/util/validation/ColumnInfo.java b/util/src/main/java/com/ning/billing/util/validation/ColumnInfo.java
index 31d8bf0..545425b 100644
--- a/util/src/main/java/com/ning/billing/util/validation/ColumnInfo.java
+++ b/util/src/main/java/com/ning/billing/util/validation/ColumnInfo.java
@@ -22,13 +22,18 @@ public class ColumnInfo {
     private final int scale;
     private final int precision;
     private final boolean isNullable;
+    private final int maximumLength;
+    private final String dataType;
 
-    public ColumnInfo(String tableName, String columnName, int scale, int precision, boolean nullable) {
+    public ColumnInfo(String tableName, String columnName, int scale, int precision,
+                      boolean nullable, int maximumLength, String dataType) {
         this.tableName = tableName;
         this.columnName = columnName;
         this.scale = scale;
         this.precision = precision;
         isNullable = nullable;
+        this.maximumLength = maximumLength;
+        this.dataType = dataType;
     }
 
     public String getTableName() {
@@ -50,4 +55,12 @@ public class ColumnInfo {
     public boolean getIsNullable() {
         return isNullable;
     }
+
+    public int getMaximumLength() {
+        return maximumLength;
+    }
+
+    public String getDataType() {
+        return dataType;
+    }
 }
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/validation/dao/DatabaseSchemaSqlDao.java b/util/src/main/java/com/ning/billing/util/validation/dao/DatabaseSchemaSqlDao.java
index 250ecce..53fccf7 100644
--- a/util/src/main/java/com/ning/billing/util/validation/dao/DatabaseSchemaSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/validation/dao/DatabaseSchemaSqlDao.java
@@ -42,8 +42,10 @@ public interface DatabaseSchemaSqlDao {
             final Integer scale = r.getInt("numeric_scale");
             final Integer precision = r.getInt("numeric_precision");
             final boolean isNullable = r.getBoolean("is_nullable");
+            final Integer maximumLength = r.getInt("character_maximum_length");
+            final String dataType = r.getString("data_type");
 
-            return new ColumnInfo(tableName, columnName, scale, precision, isNullable);
+            return new ColumnInfo(tableName, columnName, scale, precision, isNullable, maximumLength, dataType);
         }
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/validation/ValidationManager.java b/util/src/main/java/com/ning/billing/util/validation/ValidationManager.java
index 4bd8e8e..cac7736 100644
--- a/util/src/main/java/com/ning/billing/util/validation/ValidationManager.java
+++ b/util/src/main/java/com/ning/billing/util/validation/ValidationManager.java
@@ -20,6 +20,7 @@ import com.google.inject.Inject;
 import com.ning.billing.util.validation.dao.DatabaseSchemaDao;
 
 import java.lang.reflect.Field;
+import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -84,9 +85,11 @@ public class ValidationManager {
                     return true;
                 }
 
-                if (!columnInfo.getIsNullable()) {
-                    if (value == null) {return false;}
-                }
+                if (!hasValidNullability(columnInfo, value)) {return false;}
+                if (!isValidLengthString(columnInfo, value)) {return false;}
+                if (!isValidLengthChar(columnInfo, value)) {return false;}
+                if (!hasValidPrecision(columnInfo, value)) {return false;}
+                if (!hasValidScale(columnInfo, value)) {return false;}
             } catch (NoSuchFieldException e) {
                 // if the field doesn't exist, assume the configuration is faulty and skip this property
             } catch (IllegalAccessException e) {
@@ -98,6 +101,66 @@ public class ValidationManager {
         return true;
     }
 
+    private boolean hasValidNullability(final ColumnInfo columnInfo, final Object value) {
+        if (!columnInfo.getIsNullable()) {
+            if (value == null) {return false;}
+        }
+
+        return true;
+    }
+
+    private boolean isValidLengthString(final ColumnInfo columnInfo, final Object value) {
+        if (columnInfo.getMaximumLength() != 0) {
+            if (value != null) {
+                if (value.toString().length() > columnInfo.getMaximumLength()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean isValidLengthChar(final ColumnInfo columnInfo, final Object value) {
+        if (columnInfo.getDataType().equals("char")) {
+            if (value== null) {
+                return false;
+            } else {
+                if (value.toString().length() != columnInfo.getMaximumLength()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean hasValidPrecision(final ColumnInfo columnInfo, final Object value) {
+        if (columnInfo.getPrecision() != 0) {
+            if (value != null) {
+                BigDecimal bigDecimalValue = new BigDecimal(value.toString());
+                if (bigDecimalValue.precision() > columnInfo.getPrecision()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean hasValidScale(final ColumnInfo columnInfo, final Object value) {
+        if (columnInfo.getScale() != 0) {
+            if (value != null) {
+                BigDecimal bigDecimalValue = new BigDecimal(value.toString());
+                if (bigDecimalValue.scale() > columnInfo.getScale()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
     public boolean hasConfiguration(Class clazz) {
         return configurations.containsKey(clazz);
     }
diff --git a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
index f664da3..b7e5bc6 100644
--- a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
+++ b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
@@ -56,7 +56,7 @@ public class TestValidationManager {
         helper.startMysql();
         StringBuilder ddl = new StringBuilder();
         ddl.append(String.format("DROP TABLE IF EXISTS %s;", TABLE_NAME));
-        ddl.append(String.format("CREATE TABLE %s (column1 varchar(1), column2 char(2) NOT NULL, column3 numeric(10,4), column4 datetime) ENGINE = innodb;", TABLE_NAME));
+        ddl.append(String.format("CREATE TABLE %s (column1 varchar(25), column2 char(2) NOT NULL, column3 numeric(10,4), column4 datetime) ENGINE = innodb;", TABLE_NAME));
         helper.initDb(ddl.toString());
     }
 
@@ -87,7 +87,7 @@ public class TestValidationManager {
         String STRING_FIELD_2 = "column2";
         String STRING_FIELD_2_PROPERTY = "stringField2";
 
-        SimpleTestClass testObject = new SimpleTestClass("a", null, 7.9, new DateTime());
+        SimpleTestClass testObject = new SimpleTestClass(null, null, 7.9, new DateTime());
 
         vm.setConfiguration(testObject.getClass(), STRING_FIELD_2_PROPERTY, vm.getColumnInfo(TABLE_NAME, STRING_FIELD_2));
 
@@ -98,10 +98,57 @@ public class TestValidationManager {
         assertNotNull(configuration);
         assertTrue(configuration.hasMapping(STRING_FIELD_2_PROPERTY));
 
+        // set char field to value that is too short
         assertFalse(vm.validate(testObject));
+        testObject.setStringField2("a");
+        assertFalse(vm.validate(testObject));
+
+        // set char to excessively long string
+        testObject.setStringField2("abc");
+        assertFalse(vm.validate(testObject));
+
+        // set char to proper length
         testObject.setStringField2("ab");
         assertTrue(vm.validate(testObject));
 
+        // add the first string field and add a string that exceeds the length
+        final String STRING_FIELD_1 = "column1";
+        final String STRING_FIELD_1_PROPERTY = "stringField1";
+        vm.setConfiguration(testObject.getClass(), STRING_FIELD_1_PROPERTY, vm.getColumnInfo(TABLE_NAME, STRING_FIELD_1));
+
+        assertTrue(vm.validate(testObject));
+        testObject.setStringField1("This is a long string that exceeds the length limit for column 1.");
+        assertFalse(vm.validate(testObject));
+        testObject.setStringField1("This is a short string.");
+        assertTrue(vm.validate(testObject));
+
+        // verify numeric values
+        final String NUMERIC_FIELD = "column3";
+        final String NUMERIC_FIELD_PROPERTY = "numericField1";
+        vm.setConfiguration(testObject.getClass(), NUMERIC_FIELD_PROPERTY, vm.getColumnInfo(TABLE_NAME, NUMERIC_FIELD));
+        assertTrue(vm.validate(testObject));
+
+        // set the value to have more than 4 decimal places
+        testObject.setNumericField1(0.123456);
+        assertFalse(vm.validate(testObject));
+
+        // set the value to have more than 10 digits
+        testObject.setNumericField1(12345678901234D);
+        assertFalse(vm.validate(testObject));
+
+        // set to a valid value
+        testObject.setNumericField1(1234567890);
+        assertTrue(vm.validate(testObject));
+
+        // check another valid number
+        testObject.setNumericField1(123456.7891);
+        assertTrue(vm.validate(testObject));
+
+        // check another valid number
+        testObject.setNumericField1(12345678.91);
+        assertTrue(vm.validate(testObject));
+
+
     }
 
     private class SimpleTestClass {