package io.ebeaninternal.server.type;
import io.ebean.text.TextException;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* JPA standard based Enum scalar type.
* <p>
* Converts between bean Enum types to database string or integer columns.
* </p>
* <p>
* The limitation of this class is that it converts the Enum to either the ordinal or string value
* of the Enum. If you wish to convert the Enum to some other value then you should look at the
* Ebean specific @EnumMapping.
* </p>
*/
public class ScalarTypeEnumStandard {
@SuppressWarnings({"rawtypes", "unchecked"})
public static class StringEnum extends EnumBase implements ScalarTypeEnum {
private final int length;
/**
* Create a ScalarTypeEnum.
*/
public StringEnum(Class enumType) {
super(enumType, false, Types.VARCHAR);
this.length = maxValueLength(enumType);
}
/**
* Return the IN values for DB constraint construction.
*/
@Override
public Set<String> getDbCheckConstraintValues() {
LinkedHashSet<String> values = new LinkedHashSet<>();
Object[] ea = enumType.getEnumConstants();
for (Object anEa : ea) {
Enum<?> e = (Enum<?>) anEa;
values.add("'" + e.name() + "'");
}
return values;
}
private int maxValueLength(Class<?> enumType) {
int maxLen = 0;
Object[] ea = enumType.getEnumConstants();
for (Object anEa : ea) {
Enum<?> e = (Enum<?>) anEa;
maxLen = Math.max(maxLen, e.name().length());
}
return maxLen;
}
@Override
public int getLength() {
return length;
}
@Override
public void bind(DataBind b, Object value) throws SQLException {
if (value == null) {
b.setNull(Types.VARCHAR);
} else {
b.setString(format(value));
}
}
@Override
public Object read(DataReader dataReader) throws SQLException {
String string = dataReader.getString();
if (string == null) {
return null;
} else {
return parse(string);
}
}
/**
* Convert the Boolean value to the db value.
*/
@Override
public Object toJdbcType(Object beanValue) {
if (beanValue == null) {
return null;
}
return format(beanValue);
}
@Override
public Object toBeanType(Object dbValue) {
if (dbValue == null || dbValue instanceof Enum<?>) {
return dbValue;
}
return Enum.valueOf(enumType, (String) dbValue);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static class OrdinalEnum extends EnumBase implements ScalarTypeEnum {
private final Object[] enumArray;
/**
* Create a ScalarTypeEnum.
*/
public OrdinalEnum(Class enumType) {
super(enumType, false, Types.INTEGER);
this.enumArray = EnumSet.allOf(enumType).toArray();
}
/**
* Return the IN values for DB constraint construction.
*/
@Override
public Set<String> getDbCheckConstraintValues() {
LinkedHashSet<String> values = new LinkedHashSet<>();
for (Object anEnumArray : enumArray) {
Enum<?> e = (Enum<?>) anEnumArray;
values.add(Integer.toString(e.ordinal()));
}
return values;
}
@Override
public void bind(DataBind b, Object value) throws SQLException {
if (value == null) {
b.setNull(Types.INTEGER);
} else {
b.setInt(((Enum<?>) value).ordinal());
}
}
@Override
public Object read(DataReader dataReader) throws SQLException {
Integer ordinal = dataReader.getInt();
if (ordinal == null) {
return null;
} else {
if (ordinal < 0 || ordinal >= enumArray.length) {
String m = "Unexpected ordinal [" + ordinal + "] out of range [" + enumArray.length + "]";
throw new IllegalStateException(m);
}
return enumArray[ordinal];
}
}
/**
* Convert the Enum value to the db value.
*/
@Override
public Object toJdbcType(Object beanValue) {
if (beanValue == null) {
return null;
}
return ((Enum<?>) beanValue).ordinal();
}
/**
* Convert the db value to the Enum value.
*/
@Override
public Object toBeanType(Object dbValue) {
if (dbValue == null || dbValue instanceof Enum<?>) {
return dbValue;
}
int ordinal = (Integer) dbValue;
if (ordinal < 0 || ordinal >= enumArray.length) {
String m = "Unexpected ordinal [" + ordinal + "] out of range [" + enumArray.length + "]";
throw new IllegalStateException(m);
}
return enumArray[ordinal];
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract static class EnumBase extends ScalarTypeBase {
protected final Class enumType;
public EnumBase(Class<?> type, boolean jdbcNative, int jdbcType) {
super(type, jdbcNative, jdbcType);
this.enumType = type;
}
@Override
public String format(Object value) {
return ((Enum<?>) value).name();
}
@Override
public String formatValue(Object value) {
return ((Enum<?>) value).name();
}
@Override
public Object parse(String value) {
return Enum.valueOf(enumType, value);
}
@Override
public Object convertFromMillis(long systemTimeMillis) {
throw new TextException("Not Supported");
}
@Override
public boolean isDateTimeCapable() {
return false;
}
@Override
public Object jsonRead(JsonParser parser) throws IOException {
return parse(parser.getValueAsString());
}
@Override
public void jsonWrite(JsonGenerator writer, Object value) throws IOException {
writer.writeString(formatValue(value));
}
@Override
public DocPropertyType getDocType() {
return DocPropertyType.ENUM;
}
@Override
public Object readData(DataInput dataInput) throws IOException {
if (!dataInput.readBoolean()) {
return null;
} else {
return parse(dataInput.readUTF());
}
}
@Override
public void writeData(DataOutput dataOutput, Object value) throws IOException {
if (value == null) {
dataOutput.writeBoolean(false);
} else {
ScalarHelp.writeUTF(dataOutput, format(value));
}
}
}
}