aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/iciql/ModelUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/iciql/ModelUtils.java')
-rw-r--r--src/main/java/com/iciql/ModelUtils.java927
1 files changed, 459 insertions, 468 deletions
diff --git a/src/main/java/com/iciql/ModelUtils.java b/src/main/java/com/iciql/ModelUtils.java
index 7fa1de6..14dc615 100644
--- a/src/main/java/com/iciql/ModelUtils.java
+++ b/src/main/java/com/iciql/ModelUtils.java
@@ -17,7 +17,8 @@
package com.iciql;
-import static com.iciql.util.StringUtils.isNullOrEmpty;
+import com.iciql.TableDefinition.FieldDefinition;
+import com.iciql.util.StringUtils;
import java.lang.reflect.Method;
import java.math.BigDecimal;
@@ -32,8 +33,7 @@ import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
-import com.iciql.TableDefinition.FieldDefinition;
-import com.iciql.util.StringUtils;
+import static com.iciql.util.StringUtils.isNullOrEmpty;
/**
* Utility methods for models related to type mapping, default value validation,
@@ -41,469 +41,460 @@ import com.iciql.util.StringUtils;
*/
class ModelUtils {
- /**
- * The list of supported data types. It is used by the runtime mapping for
- * CREATE statements.
- */
- private static final Map<Class<?>, String> SUPPORTED_TYPES = new HashMap<Class<?>, String>();
-
- static {
- Map<Class<?>, String> m = SUPPORTED_TYPES;
- m.put(String.class, "VARCHAR");
- m.put(Boolean.class, "BOOLEAN");
- m.put(Byte.class, "TINYINT");
- m.put(Short.class, "SMALLINT");
- m.put(Integer.class, "INT");
- m.put(Long.class, "BIGINT");
- m.put(Float.class, "REAL");
- m.put(Double.class, "DOUBLE");
- m.put(BigDecimal.class, "DECIMAL");
- m.put(java.sql.Timestamp.class, "TIMESTAMP");
- m.put(java.util.Date.class, "TIMESTAMP");
- m.put(java.sql.Date.class, "DATE");
- m.put(java.sql.Time.class, "TIME");
- m.put(byte[].class, "BLOB");
- m.put(UUID.class, "UUID");
-
- // map primitives
- m.put(boolean.class, m.get(Boolean.class));
- m.put(byte.class, m.get(Byte.class));
- m.put(short.class, m.get(Short.class));
- m.put(int.class, m.get(Integer.class));
- m.put(long.class, m.get(Long.class));
- m.put(float.class, m.get(Float.class));
- m.put(double.class, m.get(Double.class));
- }
-
- /**
- * Convert SQL type aliases to the list of supported types. This map is used
- * by generation and validation.
- */
- private static final Map<String, String> SQL_TYPES = new HashMap<String, String>();
-
- static {
- Map<String, String> m = SQL_TYPES;
- m.put("CHAR", "VARCHAR");
- m.put("CHARACTER", "VARCHAR");
- m.put("NCHAR", "VARCHAR");
- m.put("VARCHAR_CASESENSITIVE", "VARCHAR");
- m.put("VARCHAR_IGNORECASE", "VARCHAR");
- m.put("LONGVARCHAR", "VARCHAR");
- m.put("VARCHAR2", "VARCHAR");
- m.put("NVARCHAR", "VARCHAR");
- m.put("NVARCHAR2", "VARCHAR");
- m.put("TEXT", "VARCHAR");
- m.put("NTEXT", "VARCHAR");
- m.put("TINYTEXT", "VARCHAR");
- m.put("MEDIUMTEXT", "VARCHAR");
- m.put("LONGTEXT", "VARCHAR");
- m.put("CLOB", "VARCHAR");
- m.put("NCLOB", "VARCHAR");
-
- // logic
- m.put("BIT", "BOOLEAN");
- m.put("BOOL", "BOOLEAN");
-
- // numeric
- m.put("BYTE", "TINYINT");
- m.put("INT2", "SMALLINT");
- m.put("YEAR", "SMALLINT");
- m.put("INTEGER", "INT");
- m.put("MEDIUMINT", "INT");
- m.put("INT4", "INT");
- m.put("SIGNED", "INT");
- m.put("INT8", "BIGINT");
- m.put("IDENTITY", "BIGINT");
- m.put("SERIAL", "INT");
- m.put("BIGSERIAL", "BIGINT");
-
- // decimal
- m.put("NUMBER", "DECIMAL");
- m.put("DEC", "DECIMAL");
- m.put("NUMERIC", "DECIMAL");
- m.put("FLOAT", "DOUBLE");
- m.put("FLOAT4", "DOUBLE");
- m.put("FLOAT8", "DOUBLE");
- m.put("DOUBLE PRECISION", "DOUBLE");
-
- // date
- m.put("DATETIME", "TIMESTAMP");
- m.put("SMALLDATETIME", "TIMESTAMP");
-
- // binary types
- m.put("TINYBLOB", "BLOB");
- m.put("MEDIUMBLOB", "BLOB");
- m.put("LONGBLOB", "BLOB");
- m.put("IMAGE", "BLOB");
- m.put("OID", "BLOB");
- }
-
- private static final List<String> KEYWORDS = Arrays.asList("abstract", "assert", "boolean", "break",
- "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else",
- "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import",
- "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected",
- "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this",
- "throw", "throws", "transient", "try", "void", "volatile", "while", "false", "null", "true");
-
- /**
- * Returns a SQL type mapping for a Java class.
- *
- * @param fieldDef
- * the field to map
- * @return
- */
- static String getDataType(FieldDefinition fieldDef) {
- Class<?> fieldClass = fieldDef.field.getType();
- if (fieldClass.isEnum()) {
- switch (fieldDef.enumType) {
- case ORDINAL:
- return "INT";
- case ENUMID:
- String sqlType = SUPPORTED_TYPES.get(fieldDef.enumTypeClass);
- if (sqlType == null) {
- throw new IciqlException("Unsupported enum mapping type {0} for {1}",
- fieldDef.enumTypeClass, fieldDef.columnName);
- }
- return sqlType;
- case NAME:
- default:
- return "VARCHAR";
- }
- }
- if (SUPPORTED_TYPES.containsKey(fieldClass)) {
- return SUPPORTED_TYPES.get(fieldClass);
- }
- throw new IciqlException("Unsupported type " + fieldClass.getName());
- }
-
- /**
- * Returns the Java class for a given SQL type.
- *
- * @param sqlType
- * @param dateTimeClass
- * the preferred date class (java.util.Date or
- * java.sql.Timestamp)
- * @return
- */
- static Class<?> getClassForSqlType(String sqlType, Class<? extends java.util.Date> dateTimeClass) {
- sqlType = sqlType.toUpperCase();
- // XXX dropping "UNSIGNED" or parts like that could be trouble
- sqlType = sqlType.split(" ")[0].trim();
- if (sqlType.indexOf('(') > -1) {
- // strip out length or precision
- sqlType = sqlType.substring(0, sqlType.indexOf('('));
- }
-
- if (SQL_TYPES.containsKey(sqlType)) {
- // convert the sqlType to a standard type
- sqlType = SQL_TYPES.get(sqlType);
- }
- Class<?> mappedClass = null;
- for (Class<?> clazz : SUPPORTED_TYPES.keySet()) {
- if (clazz.isPrimitive()) {
- // do not map from SQL TYPE to primitive type
- continue;
- }
- if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) {
- mappedClass = clazz;
-
- break;
- }
- }
- if (mappedClass != null) {
- if (mappedClass.equals(java.util.Date.class) || mappedClass.equals(java.sql.Timestamp.class)) {
- return dateTimeClass;
- }
- return mappedClass;
- }
- return null;
- }
-
- /**
- * Tries to create a convert a SQL table name to a camel case class name.
- *
- * @param tableName
- * the SQL table name
- * @return the class name
- */
- static String convertTableToClassName(String tableName) {
- String[] chunks = StringUtils.arraySplit(tableName, '_', false);
- StringBuilder className = new StringBuilder();
- for (String chunk : chunks) {
- if (chunk.length() == 0) {
- // leading or trailing _
- continue;
- }
- String[] subchunks = StringUtils.arraySplit(chunk, ' ', false);
- for (String subchunk : subchunks) {
- if (subchunk.length() == 0) {
- // leading or trailing space
- continue;
- }
- className.append(Character.toUpperCase(subchunk.charAt(0)));
- className.append(subchunk.substring(1).toLowerCase());
- }
- }
- return className.toString();
- }
-
- /**
- * Ensures that SQL column names don't collide with Java keywords.
- *
- * @param columnName
- * the column name
- * @return the Java field name
- */
- static String convertColumnToFieldName(String columnName) {
- String lower = columnName.toLowerCase();
- if (KEYWORDS.contains(lower)) {
- lower += "Value";
- }
- return lower;
- }
-
- /**
- * Converts a DEFAULT clause value into an object.
- *
- * @param field
- * definition
- * @return object
- */
- static Object getDefaultValue(FieldDefinition def, Class<? extends Date> dateTimeClass) {
- Class<?> valueType = getClassForSqlType(def.dataType, dateTimeClass);
- if (String.class.isAssignableFrom(valueType)) {
- if (StringUtils.isNullOrEmpty(def.defaultValue)) {
- // literal default must be specified within single quotes
- return null;
- }
- if (def.defaultValue.charAt(0) == '\''
- && def.defaultValue.charAt(def.defaultValue.length() - 1) == '\'') {
- // strip leading and trailing single quotes
- return def.defaultValue.substring(1, def.defaultValue.length() - 1).trim();
- }
- return def.defaultValue;
- }
-
- if (StringUtils.isNullOrEmpty(def.defaultValue)) {
- // can not create object from empty string
- return null;
- }
-
- // strip leading and trailing single quotes
- String content = def.defaultValue;
- if (content.charAt(0) == '\'') {
- content = content.substring(1);
- }
- if (content.charAt(content.length() - 1) == '\'') {
- content = content.substring(0, content.length() - 2);
- }
-
- if (StringUtils.isNullOrEmpty(content)) {
- // can not create object from empty string
- return null;
- }
-
- if (Boolean.class.isAssignableFrom(valueType) || boolean.class.isAssignableFrom(valueType)) {
- return Boolean.parseBoolean(content);
- }
-
- if (Number.class.isAssignableFrom(valueType)) {
- try {
- // delegate to static valueOf() method to parse string
- Method m = valueType.getMethod("valueOf", String.class);
- return m.invoke(null, content);
- } catch (NumberFormatException e) {
- throw new IciqlException(e, "Failed to parse {0} as a number!", def.defaultValue);
- } catch (Throwable t) {
- }
- }
-
- String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}";
- String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}";
-
- if (java.sql.Date.class.isAssignableFrom(valueType)) {
- // this may be a little loose....
- // 00-00-00
- // 00/00/00
- // 00.00.00
- Pattern pattern = Pattern.compile(dateRegex);
- if (pattern.matcher(content).matches()) {
- DateFormat df = DateFormat.getDateInstance();
- try {
- return df.parse(content);
- } catch (Exception e) {
- throw new IciqlException(e, "Failed to parse {0} as a date!", def.defaultValue);
- }
- }
- }
-
- if (java.sql.Time.class.isAssignableFrom(valueType)) {
- // 00:00:00
- Pattern pattern = Pattern.compile(timeRegex);
- if (pattern.matcher(content).matches()) {
- DateFormat df = DateFormat.getTimeInstance();
- try {
- return df.parse(content);
- } catch (Exception e) {
- throw new IciqlException(e, "Failed to parse {0} as a time!", def.defaultValue);
- }
- }
- }
-
- if (java.util.Date.class.isAssignableFrom(valueType)) {
- // this may be a little loose....
- // 00-00-00 00:00:00
- // 00/00/00T00:00:00
- // 00.00.00T00:00:00
- Pattern pattern = Pattern.compile(dateRegex + "." + timeRegex);
- if (pattern.matcher(content).matches()) {
- DateFormat df = DateFormat.getDateTimeInstance();
- try {
- return df.parse(content);
- } catch (Exception e) {
- throw new IciqlException(e, "Failed to parse {0} as a datetimestamp!", def.defaultValue);
- }
- }
- }
- return content;
- }
-
- /**
- * Converts the object into a DEFAULT clause value.
- *
- * @param o
- * the default object
- * @return the value formatted for a DEFAULT clause
- */
- static String formatDefaultValue(Object o) {
- Class<?> objectClass = o.getClass();
- String value = null;
- if (Number.class.isAssignableFrom(objectClass)) {
- // NUMBER
- return ((Number) o).toString();
- } else if (Boolean.class.isAssignableFrom(objectClass)) {
- // BOOLEAN
- return o.toString();
- } else if (java.sql.Date.class.isAssignableFrom(objectClass)) {
- // DATE
- value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o);
- } else if (java.sql.Time.class.isAssignableFrom(objectClass)) {
- // TIME
- value = new SimpleDateFormat("HH:mm:ss").format((Date) o);
- } else if (Date.class.isAssignableFrom(objectClass)) {
- // DATETIME
- value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) o);
- } else if (String.class.isAssignableFrom(objectClass)) {
- // STRING
- value = o.toString();
- }
- if (value == null) {
- return "''";
- }
- return MessageFormat.format("''{0}''", value);
- }
-
- /**
- * Checks the formatting of IQColumn.defaultValue().
- *
- * @param defaultValue
- * the default value
- * @return true if it is
- */
- static boolean isProperlyFormattedDefaultValue(String defaultValue) {
- if (isNullOrEmpty(defaultValue)) {
- return true;
- }
- Pattern literalDefault = Pattern.compile("'.*'");
- Pattern functionDefault = Pattern.compile("[^'].*[^']");
- return literalDefault.matcher(defaultValue).matches()
- || functionDefault.matcher(defaultValue).matches();
- }
-
- /**
- * Checks to see if the default value matches the class.
- *
- * @param modelClass
- * the class
- * @param defaultValue
- * the value
- * @return true if it does
- */
- static boolean isValidDefaultValue(Class<?> modelClass, String defaultValue) {
-
- if (defaultValue == null) {
- // NULL
- return true;
- }
- if (defaultValue.trim().length() == 0) {
- // NULL (effectively)
- return true;
- }
-
- // function / variable
- Pattern functionDefault = Pattern.compile("[^'].*[^']");
- if (functionDefault.matcher(defaultValue).matches()) {
- // hard to validate this since its in the database
- // assume it is good
- return true;
- }
-
- // STRING
- if (modelClass == String.class) {
- Pattern stringDefault = Pattern.compile("'(.|\\n)*'");
- return stringDefault.matcher(defaultValue).matches();
- }
-
- String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}";
- String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}";
-
- // TIMESTAMP
- if (modelClass == java.util.Date.class || modelClass == java.sql.Timestamp.class) {
- // this may be a little loose....
- // 00-00-00 00:00:00
- // 00/00/00T00:00:00
- // 00.00.00T00:00:00
- Pattern pattern = Pattern.compile("'" + dateRegex + "." + timeRegex + "'");
- return pattern.matcher(defaultValue).matches();
- }
-
- // DATE
- if (modelClass == java.sql.Date.class) {
- // this may be a little loose....
- // 00-00-00
- // 00/00/00
- // 00.00.00
- Pattern pattern = Pattern.compile("'" + dateRegex + "'");
- return pattern.matcher(defaultValue).matches();
- }
-
- // TIME
- if (modelClass == java.sql.Time.class) {
- // 00:00:00
- Pattern pattern = Pattern.compile("'" + timeRegex + "'");
- return pattern.matcher(defaultValue).matches();
- }
-
- // NUMBER
- if (Number.class.isAssignableFrom(modelClass)) {
- // strip single quotes
- String unquoted = defaultValue;
- if (unquoted.charAt(0) == '\'') {
- unquoted = unquoted.substring(1);
- }
- if (unquoted.charAt(unquoted.length() - 1) == '\'') {
- unquoted = unquoted.substring(0, unquoted.length() - 1);
- }
-
- try {
- // delegate to static valueOf() method to parse string
- Method m = modelClass.getMethod("valueOf", String.class);
- m.invoke(null, unquoted);
- } catch (NumberFormatException ex) {
- return false;
- } catch (Throwable t) {
- }
- }
- return true;
- }
+ /**
+ * The list of supported data types. It is used by the runtime mapping for
+ * CREATE statements.
+ */
+ private static final Map<Class<?>, String> SUPPORTED_TYPES = new HashMap<Class<?>, String>();
+
+ static {
+ Map<Class<?>, String> m = SUPPORTED_TYPES;
+ m.put(String.class, "VARCHAR");
+ m.put(Boolean.class, "BOOLEAN");
+ m.put(Byte.class, "TINYINT");
+ m.put(Short.class, "SMALLINT");
+ m.put(Integer.class, "INT");
+ m.put(Long.class, "BIGINT");
+ m.put(Float.class, "REAL");
+ m.put(Double.class, "DOUBLE");
+ m.put(BigDecimal.class, "DECIMAL");
+ m.put(java.sql.Timestamp.class, "TIMESTAMP");
+ m.put(java.util.Date.class, "TIMESTAMP");
+ m.put(java.sql.Date.class, "DATE");
+ m.put(java.sql.Time.class, "TIME");
+ m.put(byte[].class, "BLOB");
+ m.put(UUID.class, "UUID");
+
+ // map primitives
+ m.put(boolean.class, m.get(Boolean.class));
+ m.put(byte.class, m.get(Byte.class));
+ m.put(short.class, m.get(Short.class));
+ m.put(int.class, m.get(Integer.class));
+ m.put(long.class, m.get(Long.class));
+ m.put(float.class, m.get(Float.class));
+ m.put(double.class, m.get(Double.class));
+ }
+
+ /**
+ * Convert SQL type aliases to the list of supported types. This map is used
+ * by generation and validation.
+ */
+ private static final Map<String, String> SQL_TYPES = new HashMap<String, String>();
+
+ static {
+ Map<String, String> m = SQL_TYPES;
+ m.put("CHAR", "VARCHAR");
+ m.put("CHARACTER", "VARCHAR");
+ m.put("NCHAR", "VARCHAR");
+ m.put("VARCHAR_CASESENSITIVE", "VARCHAR");
+ m.put("VARCHAR_IGNORECASE", "VARCHAR");
+ m.put("LONGVARCHAR", "VARCHAR");
+ m.put("VARCHAR2", "VARCHAR");
+ m.put("NVARCHAR", "VARCHAR");
+ m.put("NVARCHAR2", "VARCHAR");
+ m.put("TEXT", "VARCHAR");
+ m.put("NTEXT", "VARCHAR");
+ m.put("TINYTEXT", "VARCHAR");
+ m.put("MEDIUMTEXT", "VARCHAR");
+ m.put("LONGTEXT", "VARCHAR");
+ m.put("CLOB", "VARCHAR");
+ m.put("NCLOB", "VARCHAR");
+
+ // logic
+ m.put("BIT", "BOOLEAN");
+ m.put("BOOL", "BOOLEAN");
+
+ // numeric
+ m.put("BYTE", "TINYINT");
+ m.put("INT2", "SMALLINT");
+ m.put("YEAR", "SMALLINT");
+ m.put("INTEGER", "INT");
+ m.put("MEDIUMINT", "INT");
+ m.put("INT4", "INT");
+ m.put("SIGNED", "INT");
+ m.put("INT8", "BIGINT");
+ m.put("IDENTITY", "BIGINT");
+ m.put("SERIAL", "INT");
+ m.put("BIGSERIAL", "BIGINT");
+
+ // decimal
+ m.put("NUMBER", "DECIMAL");
+ m.put("DEC", "DECIMAL");
+ m.put("NUMERIC", "DECIMAL");
+ m.put("FLOAT", "DOUBLE");
+ m.put("FLOAT4", "DOUBLE");
+ m.put("FLOAT8", "DOUBLE");
+ m.put("DOUBLE PRECISION", "DOUBLE");
+
+ // date
+ m.put("DATETIME", "TIMESTAMP");
+ m.put("SMALLDATETIME", "TIMESTAMP");
+
+ // binary types
+ m.put("TINYBLOB", "BLOB");
+ m.put("MEDIUMBLOB", "BLOB");
+ m.put("LONGBLOB", "BLOB");
+ m.put("IMAGE", "BLOB");
+ m.put("OID", "BLOB");
+ }
+
+ private static final List<String> KEYWORDS = Arrays.asList("abstract", "assert", "boolean", "break",
+ "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else",
+ "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import",
+ "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected",
+ "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this",
+ "throw", "throws", "transient", "try", "void", "volatile", "while", "false", "null", "true");
+
+ /**
+ * Returns a SQL type mapping for a Java class.
+ *
+ * @param fieldDef the field to map
+ * @return
+ */
+ static String getDataType(FieldDefinition fieldDef) {
+ Class<?> fieldClass = fieldDef.field.getType();
+ if (fieldClass.isEnum()) {
+ switch (fieldDef.enumType) {
+ case ORDINAL:
+ return "INT";
+ case ENUMID:
+ String sqlType = SUPPORTED_TYPES.get(fieldDef.enumTypeClass);
+ if (sqlType == null) {
+ throw new IciqlException("Unsupported enum mapping type {0} for {1}",
+ fieldDef.enumTypeClass, fieldDef.columnName);
+ }
+ return sqlType;
+ case NAME:
+ default:
+ return "VARCHAR";
+ }
+ }
+ if (SUPPORTED_TYPES.containsKey(fieldClass)) {
+ return SUPPORTED_TYPES.get(fieldClass);
+ }
+ throw new IciqlException("Unsupported type " + fieldClass.getName());
+ }
+
+ /**
+ * Returns the Java class for a given SQL type.
+ *
+ * @param sqlType
+ * @param dateTimeClass the preferred date class (java.util.Date or
+ * java.sql.Timestamp)
+ * @return
+ */
+ static Class<?> getClassForSqlType(String sqlType, Class<? extends java.util.Date> dateTimeClass) {
+ sqlType = sqlType.toUpperCase();
+ // XXX dropping "UNSIGNED" or parts like that could be trouble
+ sqlType = sqlType.split(" ")[0].trim();
+ if (sqlType.indexOf('(') > -1) {
+ // strip out length or precision
+ sqlType = sqlType.substring(0, sqlType.indexOf('('));
+ }
+
+ if (SQL_TYPES.containsKey(sqlType)) {
+ // convert the sqlType to a standard type
+ sqlType = SQL_TYPES.get(sqlType);
+ }
+ Class<?> mappedClass = null;
+ for (Class<?> clazz : SUPPORTED_TYPES.keySet()) {
+ if (clazz.isPrimitive()) {
+ // do not map from SQL TYPE to primitive type
+ continue;
+ }
+ if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) {
+ mappedClass = clazz;
+
+ break;
+ }
+ }
+ if (mappedClass != null) {
+ if (mappedClass.equals(java.util.Date.class) || mappedClass.equals(java.sql.Timestamp.class)) {
+ return dateTimeClass;
+ }
+ return mappedClass;
+ }
+ return null;
+ }
+
+ /**
+ * Tries to create a convert a SQL table name to a camel case class name.
+ *
+ * @param tableName the SQL table name
+ * @return the class name
+ */
+ static String convertTableToClassName(String tableName) {
+ String[] chunks = StringUtils.arraySplit(tableName, '_', false);
+ StringBuilder className = new StringBuilder();
+ for (String chunk : chunks) {
+ if (chunk.length() == 0) {
+ // leading or trailing _
+ continue;
+ }
+ String[] subchunks = StringUtils.arraySplit(chunk, ' ', false);
+ for (String subchunk : subchunks) {
+ if (subchunk.length() == 0) {
+ // leading or trailing space
+ continue;
+ }
+ className.append(Character.toUpperCase(subchunk.charAt(0)));
+ className.append(subchunk.substring(1).toLowerCase());
+ }
+ }
+ return className.toString();
+ }
+
+ /**
+ * Ensures that SQL column names don't collide with Java keywords.
+ *
+ * @param columnName the column name
+ * @return the Java field name
+ */
+ static String convertColumnToFieldName(String columnName) {
+ String lower = columnName.toLowerCase();
+ if (KEYWORDS.contains(lower)) {
+ lower += "Value";
+ }
+ return lower;
+ }
+
+ /**
+ * Converts a DEFAULT clause value into an object.
+ *
+ * @param field definition
+ * @return object
+ */
+ static Object getDefaultValue(FieldDefinition def, Class<? extends Date> dateTimeClass) {
+ Class<?> valueType = getClassForSqlType(def.dataType, dateTimeClass);
+ if (String.class.isAssignableFrom(valueType)) {
+ if (StringUtils.isNullOrEmpty(def.defaultValue)) {
+ // literal default must be specified within single quotes
+ return null;
+ }
+ if (def.defaultValue.charAt(0) == '\''
+ && def.defaultValue.charAt(def.defaultValue.length() - 1) == '\'') {
+ // strip leading and trailing single quotes
+ return def.defaultValue.substring(1, def.defaultValue.length() - 1).trim();
+ }
+ return def.defaultValue;
+ }
+
+ if (StringUtils.isNullOrEmpty(def.defaultValue)) {
+ // can not create object from empty string
+ return null;
+ }
+
+ // strip leading and trailing single quotes
+ String content = def.defaultValue;
+ if (content.charAt(0) == '\'') {
+ content = content.substring(1);
+ }
+ if (content.charAt(content.length() - 1) == '\'') {
+ content = content.substring(0, content.length() - 2);
+ }
+
+ if (StringUtils.isNullOrEmpty(content)) {
+ // can not create object from empty string
+ return null;
+ }
+
+ if (Boolean.class.isAssignableFrom(valueType) || boolean.class.isAssignableFrom(valueType)) {
+ return Boolean.parseBoolean(content);
+ }
+
+ if (Number.class.isAssignableFrom(valueType)) {
+ try {
+ // delegate to static valueOf() method to parse string
+ Method m = valueType.getMethod("valueOf", String.class);
+ return m.invoke(null, content);
+ } catch (NumberFormatException e) {
+ throw new IciqlException(e, "Failed to parse {0} as a number!", def.defaultValue);
+ } catch (Throwable t) {
+ }
+ }
+
+ String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}";
+ String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}";
+
+ if (java.sql.Date.class.isAssignableFrom(valueType)) {
+ // this may be a little loose....
+ // 00-00-00
+ // 00/00/00
+ // 00.00.00
+ Pattern pattern = Pattern.compile(dateRegex);
+ if (pattern.matcher(content).matches()) {
+ DateFormat df = DateFormat.getDateInstance();
+ try {
+ return df.parse(content);
+ } catch (Exception e) {
+ throw new IciqlException(e, "Failed to parse {0} as a date!", def.defaultValue);
+ }
+ }
+ }
+
+ if (java.sql.Time.class.isAssignableFrom(valueType)) {
+ // 00:00:00
+ Pattern pattern = Pattern.compile(timeRegex);
+ if (pattern.matcher(content).matches()) {
+ DateFormat df = DateFormat.getTimeInstance();
+ try {
+ return df.parse(content);
+ } catch (Exception e) {
+ throw new IciqlException(e, "Failed to parse {0} as a time!", def.defaultValue);
+ }
+ }
+ }
+
+ if (java.util.Date.class.isAssignableFrom(valueType)) {
+ // this may be a little loose....
+ // 00-00-00 00:00:00
+ // 00/00/00T00:00:00
+ // 00.00.00T00:00:00
+ Pattern pattern = Pattern.compile(dateRegex + "." + timeRegex);
+ if (pattern.matcher(content).matches()) {
+ DateFormat df = DateFormat.getDateTimeInstance();
+ try {
+ return df.parse(content);
+ } catch (Exception e) {
+ throw new IciqlException(e, "Failed to parse {0} as a datetimestamp!", def.defaultValue);
+ }
+ }
+ }
+ return content;
+ }
+
+ /**
+ * Converts the object into a DEFAULT clause value.
+ *
+ * @param o the default object
+ * @return the value formatted for a DEFAULT clause
+ */
+ static String formatDefaultValue(Object o) {
+ Class<?> objectClass = o.getClass();
+ String value = null;
+ if (Number.class.isAssignableFrom(objectClass)) {
+ // NUMBER
+ return ((Number) o).toString();
+ } else if (Boolean.class.isAssignableFrom(objectClass)) {
+ // BOOLEAN
+ return o.toString();
+ } else if (java.sql.Date.class.isAssignableFrom(objectClass)) {
+ // DATE
+ value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o);
+ } else if (java.sql.Time.class.isAssignableFrom(objectClass)) {
+ // TIME
+ value = new SimpleDateFormat("HH:mm:ss").format((Date) o);
+ } else if (Date.class.isAssignableFrom(objectClass)) {
+ // DATETIME
+ value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) o);
+ } else if (String.class.isAssignableFrom(objectClass)) {
+ // STRING
+ value = o.toString();
+ }
+ if (value == null) {
+ return "''";
+ }
+ return MessageFormat.format("''{0}''", value);
+ }
+
+ /**
+ * Checks the formatting of IQColumn.defaultValue().
+ *
+ * @param defaultValue the default value
+ * @return true if it is
+ */
+ static boolean isProperlyFormattedDefaultValue(String defaultValue) {
+ if (isNullOrEmpty(defaultValue)) {
+ return true;
+ }
+ Pattern literalDefault = Pattern.compile("'.*'");
+ Pattern functionDefault = Pattern.compile("[^'].*[^']");
+ return literalDefault.matcher(defaultValue).matches()
+ || functionDefault.matcher(defaultValue).matches();
+ }
+
+ /**
+ * Checks to see if the default value matches the class.
+ *
+ * @param modelClass the class
+ * @param defaultValue the value
+ * @return true if it does
+ */
+ static boolean isValidDefaultValue(Class<?> modelClass, String defaultValue) {
+
+ if (defaultValue == null) {
+ // NULL
+ return true;
+ }
+ if (defaultValue.trim().length() == 0) {
+ // NULL (effectively)
+ return true;
+ }
+
+ // function / variable
+ Pattern functionDefault = Pattern.compile("[^'].*[^']");
+ if (functionDefault.matcher(defaultValue).matches()) {
+ // hard to validate this since its in the database
+ // assume it is good
+ return true;
+ }
+
+ // STRING
+ if (modelClass == String.class) {
+ Pattern stringDefault = Pattern.compile("'(.|\\n)*'");
+ return stringDefault.matcher(defaultValue).matches();
+ }
+
+ String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}";
+ String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}";
+
+ // TIMESTAMP
+ if (modelClass == java.util.Date.class || modelClass == java.sql.Timestamp.class) {
+ // this may be a little loose....
+ // 00-00-00 00:00:00
+ // 00/00/00T00:00:00
+ // 00.00.00T00:00:00
+ Pattern pattern = Pattern.compile("'" + dateRegex + "." + timeRegex + "'");
+ return pattern.matcher(defaultValue).matches();
+ }
+
+ // DATE
+ if (modelClass == java.sql.Date.class) {
+ // this may be a little loose....
+ // 00-00-00
+ // 00/00/00
+ // 00.00.00
+ Pattern pattern = Pattern.compile("'" + dateRegex + "'");
+ return pattern.matcher(defaultValue).matches();
+ }
+
+ // TIME
+ if (modelClass == java.sql.Time.class) {
+ // 00:00:00
+ Pattern pattern = Pattern.compile("'" + timeRegex + "'");
+ return pattern.matcher(defaultValue).matches();
+ }
+
+ // NUMBER
+ if (Number.class.isAssignableFrom(modelClass)) {
+ // strip single quotes
+ String unquoted = defaultValue;
+ if (unquoted.charAt(0) == '\'') {
+ unquoted = unquoted.substring(1);
+ }
+ if (unquoted.charAt(unquoted.length() - 1) == '\'') {
+ unquoted = unquoted.substring(0, unquoted.length() - 1);
+ }
+
+ try {
+ // delegate to static valueOf() method to parse string
+ Method m = modelClass.getMethod("valueOf", String.class);
+ m.invoke(null, unquoted);
+ } catch (NumberFormatException ex) {
+ return false;
+ } catch (Throwable t) {
+ }
+ }
+ return true;
+ }
}