@@ -50,8 +50,14 @@ Alternatively, model classes can be automatically generated by iciql using the m | |||
<tr><td>byte []</td> | |||
<td>BLOB</td></tr> | |||
<tr><td>java.lang.Enum</td> | |||
<td>VARCHAR/TEXT *@IQEnum(STRING)* or INT *@IQEnum(ORDINAL)*</td></tr> | |||
<tr><td>java.lang.Enum.name()</td> | |||
<td>VARCHAR (maxLength > 0) or TEXT (maxLength == 0)<br/>EnumType.STRING</td></tr> | |||
<tr><td>java.lang.Enum.ordinal()</td> | |||
<td>INT<br/>EnumType.ORDINAL</td></tr> | |||
<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td> | |||
<td>INT<br/>EnumType.ENUMID</td></tr> | |||
</table> | |||
@@ -91,9 +91,22 @@ import java.lang.annotation.Target; | |||
* <td>byte []</td> | |||
* <td>BLOB</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum.name()</td> | |||
* <td>VARCHAR (maxLength > 0) or TEXT (maxLength == 0)<br/>EnumType.STRING</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum.ordinal()</td> | |||
* <td>INT<br/>EnumType.ORDINAL</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum implements<br/>com.iciql.Iciql.EnumID.enumId()</td> | |||
* <td>INT<br/>EnumType.ENUMID</td> | |||
* </tr> | |||
* </tr> | |||
* </table> | |||
* <p> | |||
* Unsupported data types: java.lang.Enum, Array Types, and custom types. | |||
* Unsupported data types: primitives, Array Types, and custom types. | |||
* <p> | |||
* Table and field mapping: by default, the mapped table name is the class name | |||
* and the public fields are reflectively mapped, by their name, to columns. As | |||
@@ -373,15 +386,27 @@ public interface Iciql { | |||
} | |||
/** | |||
* Enumeration representing now to map a java.lang.Enum to a column. | |||
* Interface for using the EnumType.ENUMID enumeration mapping strategy. | |||
* <p> | |||
* Enumerations wishing to use EnumType.ENUMID must implement this | |||
* interface. | |||
*/ | |||
public interface EnumId { | |||
int enumId(); | |||
} | |||
/** | |||
* Enumeration representing how to map a java.lang.Enum to a column. | |||
* <p> | |||
* <ul> | |||
* <li>STRING | |||
* <li>ORDINAL | |||
* <li>STRING - name() : string | |||
* <li>ORDINAL - ordinal() : int | |||
* <li>ENUMID - enumId() : int | |||
* </ul> | |||
* @see com.iciql.Iciql.EnumId interface | |||
*/ | |||
public enum EnumType { | |||
STRING, ORDINAL; | |||
STRING, ORDINAL, ENUMID; | |||
} | |||
/** |
@@ -16,6 +16,8 @@ | |||
package com.iciql; | |||
import java.text.MessageFormat; | |||
/** | |||
* Iciql wraps all exceptions with this class. | |||
*/ | |||
@@ -23,8 +25,9 @@ public class IciqlException extends RuntimeException { | |||
private static final long serialVersionUID = 1L; | |||
public IciqlException(String message) { | |||
super(message); | |||
public IciqlException(String message, Object... parameters) { | |||
super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message); | |||
} | |||
public IciqlException(Throwable t) { |
@@ -149,6 +149,7 @@ class ModelUtils { | |||
} | |||
return "VARCHAR"; | |||
case ORDINAL: | |||
case ENUMID: | |||
return "INT"; | |||
} | |||
} |
@@ -27,6 +27,7 @@ import java.util.IdentityHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.iciql.Iciql.EnumId; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.Iciql.IQColumn; | |||
import com.iciql.Iciql.IQEnum; | |||
@@ -97,8 +98,15 @@ class TableDefinition<T> { | |||
if (!field.isAccessible()) { | |||
field.setAccessible(true); | |||
} | |||
o = Utils.convert(o, field.getType()); | |||
Class<?> targetType = field.getType(); | |||
if (targetType.isEnum()) { | |||
o = Utils.convertEnum(o, targetType, enumType); | |||
} else { | |||
o = Utils.convert(o, targetType); | |||
} | |||
field.set(obj, o); | |||
} catch (IciqlException e) { | |||
throw e; | |||
} catch (Exception e) { | |||
throw new IciqlException(e); | |||
} | |||
@@ -344,6 +352,12 @@ class TableDefinition<T> { | |||
return iqenum.name(); | |||
case ORDINAL: | |||
return iqenum.ordinal(); | |||
case ENUMID: | |||
if (!EnumId.class.isAssignableFrom(value.getClass())) { | |||
throw new IciqlException(field.field.getName() + " does not implement EnumId!"); | |||
} | |||
EnumId enumid = (EnumId) value; | |||
return enumid.enumId(); | |||
} | |||
} | |||
if (field.trimString && field.maxLength > 0) { |
@@ -37,6 +37,8 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.atomic.AtomicLong; | |||
import com.iciql.Iciql.EnumId; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.IciqlException; | |||
/** | |||
@@ -209,10 +211,7 @@ public class Utils { | |||
if (targetType.isAssignableFrom(currentType)) { | |||
return o; | |||
} | |||
// convert enum | |||
if (targetType.isEnum()) { | |||
return convertEnum(o, targetType); | |||
} | |||
// convert from CLOB/TEXT/VARCHAR to String | |||
if (targetType == String.class) { | |||
if (Clob.class.isAssignableFrom(currentType)) { | |||
@@ -257,16 +256,18 @@ public class Utils { | |||
} | |||
} | |||
} | |||
throw new IciqlException("Can not convert the value " + o + " from " + currentType + " to " | |||
+ targetType); | |||
throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); | |||
} | |||
private static Object convertEnum(Object o, Class<?> targetType) { | |||
public static Object convertEnum(Object o, Class<?> targetType, EnumType type) { | |||
if (o == null) { | |||
return null; | |||
} | |||
Class<?> currentType = o.getClass(); | |||
if (targetType.isAssignableFrom(currentType)) { | |||
return o; | |||
} | |||
// convert from VARCHAR/TEXT/INT to Enum | |||
Enum<?>[] values = (Enum[]) targetType.getEnumConstants(); | |||
if (Clob.class.isAssignableFrom(currentType)) { | |||
@@ -297,16 +298,28 @@ public class Utils { | |||
} else if (Number.class.isAssignableFrom(currentType)) { | |||
// INT field | |||
int n = ((Number) o).intValue(); | |||
// ORDINAL mapping | |||
for (Enum<?> value : values) { | |||
if (value.ordinal() == n) { | |||
return value; | |||
if (type.equals(EnumType.ORDINAL)) { | |||
// ORDINAL mapping | |||
for (Enum<?> value : values) { | |||
if (value.ordinal() == n) { | |||
return value; | |||
} | |||
} | |||
} else if (type.equals(EnumType.ENUMID)) { | |||
if (!EnumId.class.isAssignableFrom(targetType)) { | |||
throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, | |||
currentType, targetType); | |||
} | |||
// ENUMID mapping | |||
for (Enum<?> value : values) { | |||
EnumId enumid = (EnumId) value; | |||
if (enumid.enumId() == n) { | |||
return value; | |||
} | |||
} | |||
} | |||
} | |||
throw new IciqlException("Can not convert the value " + o + " from " + currentType + " to " | |||
+ targetType); | |||
throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); | |||
} | |||
/** |
@@ -115,7 +115,7 @@ public class ModelsTest { | |||
true); | |||
assertEquals(1, models.size()); | |||
// a poor test, but a start | |||
assertEquals(1838, models.get(0).length()); | |||
assertEquals(1904, models.get(0).length()); | |||
} | |||
@Test |
@@ -21,6 +21,7 @@ import java.math.BigDecimal; | |||
import java.util.List; | |||
import java.util.Random; | |||
import com.iciql.Iciql.EnumId; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.Iciql.IQColumn; | |||
import com.iciql.Iciql.IQEnum; | |||
@@ -51,8 +52,8 @@ public class SupportedTypes { | |||
} | |||
/** | |||
* Test of @IQEnum annotated enumeration. | |||
* This strategy is the default strategy for all fields of the Tree enum. | |||
* Test of @IQEnum annotated enumeration. This strategy is the default | |||
* strategy for all fields of the Tree enum. | |||
* | |||
* Individual Tree field declarations can override this strategy by | |||
* specifying a different @IQEnum annotation. | |||
@@ -60,8 +61,19 @@ public class SupportedTypes { | |||
* Here ORDINAL specifies that this enum will be mapped to an INT column. | |||
*/ | |||
@IQEnum(EnumType.ORDINAL) | |||
public enum Tree { | |||
PINE, OAK, BIRCH, WALNUT, MAPLE; | |||
public enum Tree implements EnumId { | |||
PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); | |||
private int enumid; | |||
Tree(int id) { | |||
this.enumid = id; | |||
} | |||
@Override | |||
public int enumId() { | |||
return enumid; | |||
} | |||
} | |||
@IQColumn(primaryKey = true, autoIncrement = true) | |||
@@ -121,6 +133,11 @@ public class SupportedTypes { | |||
// @IQEnum is set on the enumeration definition and is shared | |||
// by all uses of Tree as an @IQColumn | |||
private Tree myFavoriteTree; | |||
@IQEnum(EnumType.ENUMID) | |||
@IQColumn | |||
// override the default enum strategy and use the custom enumid | |||
private Tree myOtherFavoriteTree; | |||
public static List<SupportedTypes> createList() { | |||
List<SupportedTypes> list = Utils.newArrayList(); | |||
@@ -150,6 +167,7 @@ public class SupportedTypes { | |||
s.myFavoriteFlower = Flower.MUM; | |||
s.myOtherFavoriteFlower = Flower.MARIGOLD; | |||
s.myFavoriteTree = Tree.BIRCH; | |||
s.myOtherFavoriteTree = Tree.WALNUT; | |||
return s; | |||
} | |||
@@ -172,6 +190,7 @@ public class SupportedTypes { | |||
same &= myFavoriteFlower.equals(s.myFavoriteFlower); | |||
same &= myOtherFavoriteFlower.equals(s.myOtherFavoriteFlower); | |||
same &= myFavoriteTree.equals(s.myFavoriteTree); | |||
same &= myOtherFavoriteTree.equals(s.myOtherFavoriteTree); | |||
return same; | |||
} | |||