mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 16:47:18 +00:00
#908 - Remove support for Immutable Compound Values
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* API from creating and getting property values from an Immutable Compound
|
||||
* Value Object.
|
||||
* <p>
|
||||
* A Compound Value object should contain multiple properties that are stored
|
||||
* separately. If you only have a single scalar value you should instead look to
|
||||
* use {@link ScalarTypeConverter}.
|
||||
* </p>
|
||||
* <p>
|
||||
* For each property in the compound type you need to implement the
|
||||
* {@link CompoundTypeProperty} interface. These must be returned from
|
||||
* {@link #getProperties()} in the same order that the properties appear in the
|
||||
* constructor.
|
||||
* </p>
|
||||
* <p>
|
||||
* If your compound type is mutable then you should look to use the JPA Embedded
|
||||
* annotation instead of implementing this interface.
|
||||
* </p>
|
||||
* <p>
|
||||
* When using classpath search Ebean will detect and automatically register any
|
||||
* implementations of this interface (along with detecting the entity classes
|
||||
* etc).
|
||||
* </p>
|
||||
*
|
||||
* @param <V> The type of the Value Object
|
||||
* @author rbygrave
|
||||
* @see ScalarTypeConverter
|
||||
*/
|
||||
public interface CompoundType<V> {
|
||||
|
||||
/**
|
||||
* Create an instance of the compound type given its property values.
|
||||
*/
|
||||
V create(Object[] propertyValues);
|
||||
|
||||
/**
|
||||
* Return the properties in the order they appear in the constructor.
|
||||
*/
|
||||
CompoundTypeProperty<V, ?>[] getProperties();
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Represents a Property of a Compound Value Object.
|
||||
* <p>
|
||||
* For each property in a {@link CompoundType} you need an implementation of
|
||||
* this CompoundTypeProperty interface.
|
||||
* </p>
|
||||
*
|
||||
* @param <V> The type of the Compound value object
|
||||
* @param <P> The type of the property
|
||||
* @author rbygrave
|
||||
* @see CompoundType
|
||||
* @see ScalarTypeConverter
|
||||
*/
|
||||
public interface CompoundTypeProperty<V, P> {
|
||||
|
||||
/**
|
||||
* The name of this property.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the property value from the containing compound value object.
|
||||
*
|
||||
* @param valueObject the compound value object
|
||||
* @return the property value.
|
||||
*/
|
||||
P getValue(V valueObject);
|
||||
|
||||
/**
|
||||
* This should <b>ONLY</b> be used when the persistence type is different from
|
||||
* the logical type returned. It most cases just return 0 and Ebean will
|
||||
* persist the logical type.
|
||||
* <p>
|
||||
* Typically this should be used when the logical type is long but the
|
||||
* persistence type is java.sql.Timestamp. In this case return
|
||||
* java.sql.Types.TIMESTAMP (rather than 0).
|
||||
* </p>
|
||||
*
|
||||
* @return Return the java.sql.Type that you want to use to persist this
|
||||
* property or 0 and Ebean will use the logical type.
|
||||
*/
|
||||
int getDbType();
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.IndexDefinition;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
@@ -134,16 +133,6 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) {
|
||||
visitScalar(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebean.dbmigration.model.visitor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
|
||||
/**
|
||||
* Used to help mark PropertyVisitor methods that need to be implemented
|
||||
@@ -44,16 +43,4 @@ public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor {
|
||||
*/
|
||||
public abstract void visitScalar(BeanProperty p);
|
||||
|
||||
/**
|
||||
* Not required in that the scalar properties map to the columns.
|
||||
*/
|
||||
public void visitCompound(BeanPropertyCompound p) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method for scalar property inside a Immutable Compound Value object.
|
||||
*/
|
||||
public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebean.dbmigration.model.visitor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
|
||||
/**
|
||||
* Used to visit a BeanProperty given the type of bean property it is.
|
||||
@@ -45,14 +44,4 @@ public interface BeanPropertyVisitor {
|
||||
*/
|
||||
void visitScalar(BeanProperty p);
|
||||
|
||||
/**
|
||||
* Visit a compound value object.
|
||||
*/
|
||||
void visitCompound(BeanPropertyCompound p);
|
||||
|
||||
/**
|
||||
* Visit the scalar value inside a compound value object.
|
||||
*/
|
||||
void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor;
|
||||
|
||||
@@ -103,16 +102,6 @@ public class VisitAllUsing {
|
||||
pv.visitOneImported(assocOne);
|
||||
}
|
||||
|
||||
} else if (p instanceof BeanPropertyCompound) {
|
||||
// compound type
|
||||
BeanPropertyCompound compound = (BeanPropertyCompound) p;
|
||||
pv.visitCompound(compound);
|
||||
|
||||
BeanProperty[] properties = compound.getScalarProperties();
|
||||
for (BeanProperty property : properties) {
|
||||
pv.visitCompoundScalar(compound, property);
|
||||
}
|
||||
|
||||
} else {
|
||||
// simple scalar type
|
||||
pv.visitScalar(p);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.core.bootup;
|
||||
|
||||
import com.avaje.ebean.annotation.DocStore;
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.IdGenerator;
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
@@ -50,9 +49,6 @@ public class BootupClasses implements ClassFilter {
|
||||
|
||||
private final List<Class<? extends AttributeConverter<?, ?>>> attributeConverterList = new ArrayList<>();
|
||||
|
||||
private final List<Class<? extends CompoundType<?>>> compoundTypeList = new ArrayList<>();
|
||||
|
||||
|
||||
// The following objects are instantiated on first request
|
||||
// there is always a candidate list, that holds the class and an
|
||||
// instance list, that holds the instance. Once a class is instantiated
|
||||
@@ -347,13 +343,6 @@ public class BootupClasses implements ClassFilter {
|
||||
return attributeConverterList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of ScalarConverters found.
|
||||
*/
|
||||
public List<Class<? extends CompoundType<?>>> getCompoundTypes() {
|
||||
return compoundTypeList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Class<?> cls) {
|
||||
|
||||
@@ -402,11 +391,6 @@ public class BootupClasses implements ClassFilter {
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (CompoundType.class.isAssignableFrom(cls)) {
|
||||
compoundTypeList.add((Class<? extends CompoundType<?>>) cls);
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (IdGenerator.class.isAssignableFrom(cls)) {
|
||||
idGeneratorCandidates.add((Class<? extends IdGenerator>) cls);
|
||||
interesting = true;
|
||||
|
||||
@@ -343,7 +343,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
* List of the scalar properties excluding id and secondary table properties.
|
||||
*/
|
||||
private final BeanProperty[] propertiesBaseScalar;
|
||||
private final BeanPropertyCompound[] propertiesBaseCompound;
|
||||
|
||||
private final BeanProperty[] propertiesTransient;
|
||||
|
||||
@@ -474,7 +473,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.propertiesTransient = listHelper.getTransients();
|
||||
this.propertiesNonTransient = listHelper.getNonTransients();
|
||||
this.propertiesBaseScalar = listHelper.getBaseScalar();
|
||||
this.propertiesBaseCompound = listHelper.getBaseCompound();
|
||||
this.propertiesEmbedded = listHelper.getEmbedded();
|
||||
this.propertiesLocal = listHelper.getLocal();
|
||||
this.propertiesMutable = listHelper.getMutable();
|
||||
@@ -2956,16 +2954,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return propertiesBaseScalar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return properties that are immutable compound value objects.
|
||||
* <p>
|
||||
* These are compound types but are not enhanced (Embedded are enhanced).
|
||||
* </p>
|
||||
*/
|
||||
public BeanPropertyCompound[] propertiesBaseCompound() {
|
||||
return propertiesBaseCompound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties local to this type for inheritance.
|
||||
*/
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.json.EJson;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
import com.avaje.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import com.avaje.ebeaninternal.server.text.json.ReadJson;
|
||||
import com.avaje.ebeaninternal.server.text.json.WriteJson;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Property mapped to an Immutable Compound Value Object.
|
||||
* <p>
|
||||
* An Immutable Compound Value Object is similar to an Embedded bean but it
|
||||
* doesn't require enhancement and MUST be treated as an Immutable type.
|
||||
* </p>
|
||||
*/
|
||||
public class BeanPropertyCompound extends BeanProperty {
|
||||
|
||||
private final CtCompoundType<?> compoundType;
|
||||
|
||||
private final BeanProperty[] scalarProperties;
|
||||
|
||||
private final LinkedHashMap<String, BeanProperty> propertyMap = new LinkedHashMap<>();
|
||||
|
||||
private final LinkedHashMap<String, CtCompoundPropertyElAdapter> nonScalarMap = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Create the property.
|
||||
*/
|
||||
public BeanPropertyCompound(BeanDescriptor<?> descriptor, DeployBeanPropertyCompound deploy) {
|
||||
|
||||
super(descriptor, deploy);
|
||||
this.compoundType = deploy.getCompoundType();
|
||||
|
||||
BeanPropertyCompoundRoot root = deploy.getFlatProperties();
|
||||
this.scalarProperties = root.getScalarProperties();
|
||||
|
||||
for (BeanProperty scalarProperty : scalarProperties) {
|
||||
propertyMap.put(scalarProperty.getName(), scalarProperty);
|
||||
}
|
||||
|
||||
List<CtCompoundProperty> nonScalarPropsList = root.getNonScalarProperties();
|
||||
|
||||
for (CtCompoundProperty ctProp : nonScalarPropsList) {
|
||||
CtCompoundPropertyElAdapter adapter = new CtCompoundPropertyElAdapter(ctProp);
|
||||
nonScalarMap.put(ctProp.getRelativeName(), adapter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
// do nothing for normal BeanProperty
|
||||
if (!isTransient && compoundType == null) {
|
||||
String msg = "No cvoInternalType assigned to " + descriptor.getFullName() + "." + getName();
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeployOrder(int deployOrder) {
|
||||
this.deployOrder = deployOrder;
|
||||
for (CtCompoundPropertyElAdapter adapter : nonScalarMap.values()) {
|
||||
adapter.setDeployOrder(deployOrder);
|
||||
}
|
||||
}
|
||||
|
||||
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
|
||||
|
||||
if (chain == null) {
|
||||
chain = new ElPropertyChainBuilder(true, propName);
|
||||
}
|
||||
|
||||
// first add this property
|
||||
chain.add(this);
|
||||
|
||||
// handle all the rest of the chain handled by the
|
||||
// BeanProperty (all depth for nested compound type)
|
||||
BeanProperty p = propertyMap.get(remainder);
|
||||
if (p != null) {
|
||||
return chain.add(p).build();
|
||||
}
|
||||
CtCompoundPropertyElAdapter elAdapter = nonScalarMap.get(remainder);
|
||||
if (elAdapter == null) {
|
||||
throw new RuntimeException("property [" + remainder + "] not found in " + getFullBeanName());
|
||||
}
|
||||
return chain.add(elAdapter).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
if (!isTransient) {
|
||||
for (BeanProperty scalarProperty : scalarProperties) {
|
||||
scalarProperty.appendSelect(ctx, subQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BeanProperty[] getScalarProperties() {
|
||||
return scalarProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
|
||||
|
||||
Object v = compoundType.read(ctx.getDataReader());
|
||||
setValue(bean, v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the data from the resultSet effectively ignoring it and returning
|
||||
* null.
|
||||
*/
|
||||
@Override
|
||||
public Object read(DbReadContext ctx) throws SQLException {
|
||||
return compoundType.read(ctx.getDataReader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadIgnore(DbReadContext ctx) {
|
||||
compoundType.loadIgnore(ctx.getDataReader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(SqlBeanLoad sqlBeanLoad) {
|
||||
sqlBeanLoad.load(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pathGetNested(Object bean) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException {
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
Object value = getValueIntercept(bean);
|
||||
if (value == null) {
|
||||
ctx.writeNullField(name);
|
||||
} else {
|
||||
ctx.writeFieldName(name);
|
||||
compoundType.jsonWrite(ctx, value, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonRead(ReadJson readJson, EntityBean bean) throws IOException {
|
||||
|
||||
if (!jsonDeserialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> map = EJson.parseObject(readJson.getParser());
|
||||
if (map == null) {
|
||||
setValue(bean, null);
|
||||
} else {
|
||||
Object objValue = compoundType.jsonConvert(map);
|
||||
setValue(bean, objValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the root BeanProperty for properties of a compound type.
|
||||
* <p>
|
||||
* Holds all the scalar and non-scalar properties of the compound type. The
|
||||
* scalar properties match to DB columns and the non-scalar ones are here solely
|
||||
* to support EL expression language for nested compound types.
|
||||
* </p>
|
||||
*/
|
||||
public class BeanPropertyCompoundRoot {
|
||||
|
||||
private final BeanPropertySetter setter;
|
||||
|
||||
private final String name;
|
||||
private final String fullBeanName;
|
||||
|
||||
private final ArrayList<BeanPropertyCompoundScalar> propList;
|
||||
|
||||
private List<CtCompoundProperty> nonScalarProperties;
|
||||
|
||||
public BeanPropertyCompoundRoot(DeployBeanProperty deploy) {
|
||||
this.fullBeanName = deploy.getFullBeanName();
|
||||
this.name = deploy.getName();
|
||||
this.setter = deploy.getSetter();
|
||||
this.propList = new ArrayList<>();
|
||||
}
|
||||
|
||||
public BeanProperty[] getScalarProperties() {
|
||||
|
||||
return propList.toArray(new BeanProperty[propList.size()]);
|
||||
}
|
||||
|
||||
public void register(BeanPropertyCompoundScalar prop) {
|
||||
propList.add(prop);
|
||||
}
|
||||
|
||||
public List<CtCompoundProperty> getNonScalarProperties() {
|
||||
return nonScalarProperties;
|
||||
}
|
||||
|
||||
public void setNonScalarProperties(List<CtCompoundProperty> nonScalarProperties) {
|
||||
this.nonScalarProperties = nonScalarProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the property without interception or
|
||||
* PropertyChangeSupport.
|
||||
*/
|
||||
public void setRootValue(EntityBean bean, Object value) {
|
||||
try {
|
||||
setter.set(bean, value);
|
||||
} catch (Exception ex) {
|
||||
String beanType = bean == null ? "null" : bean.getClass().getName();
|
||||
String msg = "set " + name + " with arg[" + value + "] on [" + fullBeanName + "] with type[" + beanType + "] threw error";
|
||||
throw new RuntimeException(msg, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the property.
|
||||
*/
|
||||
public void setRootValueIntercept(EntityBean bean, Object value) {
|
||||
try {
|
||||
setter.setIntercept(bean, value);
|
||||
} catch (Exception ex) {
|
||||
String beanType = bean == null ? "null" : bean.getClass().getName();
|
||||
String msg = "setIntercept " + name + " arg[" + value + "] on [" + fullBeanName + "] with type[" + beanType + "] threw error";
|
||||
throw new RuntimeException(msg, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
|
||||
|
||||
/**
|
||||
* A BeanProperty owned by a Compound value object that maps to
|
||||
* a real scalar type.
|
||||
*/
|
||||
public class BeanPropertyCompoundScalar extends BeanProperty {
|
||||
|
||||
private final BeanPropertyCompoundRoot rootProperty;
|
||||
|
||||
private final CtCompoundProperty ctProperty;
|
||||
|
||||
public BeanPropertyCompoundScalar(BeanPropertyCompoundRoot rootProperty, DeployBeanProperty scalarDeploy, CtCompoundProperty ctProperty) {
|
||||
super(scalarDeploy);
|
||||
this.rootProperty = rootProperty;
|
||||
this.ctProperty = ctProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return one of the scalar values from a compound type.
|
||||
*/
|
||||
public Object getValueObject(Object compoundValue) {
|
||||
return ctProperty.getValue(compoundValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(EntityBean valueObject) {
|
||||
return ctProperty.getValue(valueObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(EntityBean bean, Object value) {
|
||||
setValueInCompound(bean, value, false);
|
||||
}
|
||||
|
||||
public void setValueInCompound(EntityBean bean, Object value, boolean intercept) {
|
||||
|
||||
Object compoundValue = ctProperty.setValue(bean, value);
|
||||
|
||||
if (compoundValue != null) {
|
||||
// we are at the top level and we have a compound value
|
||||
// that we can set using the root property
|
||||
if (intercept) {
|
||||
rootProperty.setRootValueIntercept(bean, compoundValue);
|
||||
} else {
|
||||
rootProperty.setRootValue(bean, compoundValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No interception on embedded scalar values inside a CVO.
|
||||
*/
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
setValueInCompound(bean, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* No interception on embedded scalar values inside a CVO.
|
||||
*/
|
||||
@Override
|
||||
public Object getValueIntercept(EntityBean bean) {
|
||||
return getValue(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pathGetNested(Object bean) {
|
||||
return pathGet(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pathGet(Object bean) {
|
||||
return ctProperty.getValue(bean);
|
||||
}
|
||||
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundRoot;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundScalar;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundProperty;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Property mapped to a joined bean.
|
||||
*/
|
||||
public class DeployBeanPropertyCompound extends DeployBeanProperty {
|
||||
|
||||
private final CtCompoundType<?> compoundType;
|
||||
|
||||
private DeployBeanEmbedded deployEmbedded;
|
||||
|
||||
/**
|
||||
* Create the property.
|
||||
*/
|
||||
public DeployBeanPropertyCompound(DeployBeanDescriptor<?> desc, Class<?> targetType, CtCompoundType<?> compoundType) {
|
||||
|
||||
super(desc, targetType, null, null);
|
||||
this.compoundType = compoundType;
|
||||
}
|
||||
|
||||
public BeanPropertyCompoundRoot getFlatProperties() {
|
||||
|
||||
// get a 'flat' list of all the scalar types, their relative property names
|
||||
// and also set their matching dbColumn
|
||||
|
||||
// represents the root property
|
||||
BeanPropertyCompoundRoot rootProperty = new BeanPropertyCompoundRoot(this);
|
||||
|
||||
// Walk the tree of a compound type collecting the
|
||||
// scalar types and non-scalar properties
|
||||
CtCompoundTypeScalarList ctMeta = new CtCompoundTypeScalarList();
|
||||
|
||||
compoundType.accumulateScalarTypes(null, ctMeta);
|
||||
|
||||
|
||||
// for each of the scalar types inside a compound value object
|
||||
// build a BeanPropertyCompoundScalar with appropriate deployment
|
||||
// information.
|
||||
|
||||
for (Entry<String, ScalarType<?>> entry : ctMeta.entries()) {
|
||||
|
||||
String relativePropertyName = entry.getKey();
|
||||
ScalarType<?> scalarType = entry.getValue();
|
||||
|
||||
CtCompoundProperty ctProp = ctMeta.getCompoundType(relativePropertyName);
|
||||
|
||||
|
||||
String dbColumn = (getName() + "." + relativePropertyName).replace(".", "_");
|
||||
dbColumn = getDbColumn(relativePropertyName, dbColumn);
|
||||
|
||||
DeployBeanProperty deploy = new DeployBeanProperty(null, scalarType.getType(), scalarType, null);
|
||||
deploy.setScalarType(scalarType);
|
||||
deploy.setDbColumn(dbColumn);
|
||||
deploy.setName(relativePropertyName);
|
||||
deploy.setDbInsertable(true);
|
||||
deploy.setDbUpdateable(true);
|
||||
deploy.setDbRead(true);
|
||||
|
||||
rootProperty.register(new BeanPropertyCompoundScalar(rootProperty, deploy, ctProp));
|
||||
}
|
||||
|
||||
rootProperty.setNonScalarProperties(ctMeta.getNonScalarProperties());
|
||||
return rootProperty;
|
||||
}
|
||||
|
||||
private String getDbColumn(String propName, String defaultDbColumn) {
|
||||
if (deployEmbedded == null) {
|
||||
return defaultDbColumn;
|
||||
}
|
||||
String dbColumn = deployEmbedded.getPropertyColumnMap().get(propName);
|
||||
return dbColumn == null ? defaultDbColumn : dbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the deploy information specifically for the deployment
|
||||
* of Embedded beans.
|
||||
*/
|
||||
public DeployBeanEmbedded getDeployEmbedded() {
|
||||
// deployment should be single threaded
|
||||
if (deployEmbedded == null) {
|
||||
deployEmbedded = new DeployBeanEmbedded();
|
||||
}
|
||||
return deployEmbedded;
|
||||
}
|
||||
|
||||
public CtCompoundType<?> getCompoundType() {
|
||||
return compoundType;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-17
@@ -6,7 +6,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -57,8 +56,6 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
private final List<BeanProperty> baseScalar = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyCompound> baseCompound = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> transients = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> nonTransients = new ArrayList<>();
|
||||
@@ -219,12 +216,8 @@ public class DeployBeanPropertyLists {
|
||||
} else if (prop.isTenantId()) {
|
||||
tenant = prop;
|
||||
}
|
||||
if (prop instanceof BeanPropertyCompound) {
|
||||
baseCompound.add((BeanPropertyCompound) prop);
|
||||
} else {
|
||||
if (!prop.isAggregation()) {
|
||||
baseScalar.add(prop);
|
||||
}
|
||||
if (!prop.isAggregation()) {
|
||||
baseScalar.add(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,10 +239,6 @@ public class DeployBeanPropertyLists {
|
||||
return baseScalar.toArray(new BeanProperty[baseScalar.size()]);
|
||||
}
|
||||
|
||||
public BeanPropertyCompound[] getBaseCompound() {
|
||||
return baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]);
|
||||
}
|
||||
|
||||
public BeanProperty getId() {
|
||||
if (ids.size() > 1) {
|
||||
String msg = "Issue with bean "+desc+". Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
|
||||
@@ -440,10 +429,6 @@ public class DeployBeanPropertyLists {
|
||||
return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp);
|
||||
}
|
||||
|
||||
if (deployProp instanceof DeployBeanPropertyCompound) {
|
||||
return new BeanPropertyCompound(desc, (DeployBeanPropertyCompound) deployProp);
|
||||
}
|
||||
|
||||
return new BeanProperty(desc, deployProp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,9 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -260,33 +257,11 @@ public class DeployCreateProperties {
|
||||
return new DeployBeanProperty(desc, propertyType, scalarType, null);
|
||||
}
|
||||
|
||||
CtCompoundType<?> compoundType = typeManager.getCompoundType(propertyType);
|
||||
if (compoundType != null) {
|
||||
return new DeployBeanPropertyCompound(desc, propertyType, compoundType);
|
||||
}
|
||||
|
||||
if (isTransientField(field)) {
|
||||
// return with no ScalarType (still support JSON features)
|
||||
return new DeployBeanProperty(desc, propertyType, null, null);
|
||||
}
|
||||
try {
|
||||
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(propertyType);
|
||||
if (checkImmutable.isImmutable()) {
|
||||
if (checkImmutable.isCompoundType()) {
|
||||
// use reflection to support compound immutable value objects
|
||||
typeManager.recursiveCreateScalarDataReader(propertyType);
|
||||
compoundType = typeManager.getCompoundType(propertyType);
|
||||
if (compoundType != null) {
|
||||
return new DeployBeanPropertyCompound(desc, propertyType, compoundType);
|
||||
}
|
||||
|
||||
} else {
|
||||
// use reflection to support simple immutable value objects
|
||||
scalarType = typeManager.recursiveCreateScalarTypes(propertyType);
|
||||
return new DeployBeanProperty(desc, propertyType, scalarType, null);
|
||||
}
|
||||
}
|
||||
|
||||
return new DeployBeanPropertyAssocOne(desc, propertyType);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.config.dbplatform.DbPlatformType;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeArrayList;
|
||||
@@ -176,10 +175,6 @@ public class DeployUtil {
|
||||
// this will be an Enum type...
|
||||
return;
|
||||
}
|
||||
if (property instanceof DeployBeanPropertyCompound) {
|
||||
// compound properties have a CvoInternalType instead
|
||||
return;
|
||||
}
|
||||
|
||||
ScalarType<?> scalarType = getScalarType(property);
|
||||
if (scalarType != null) {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.persist.dmlbind;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bindable for a Immutable Compound value object.
|
||||
*/
|
||||
public class BindableCompound implements Bindable {
|
||||
|
||||
private final BindableProperty[] items;
|
||||
|
||||
private final BeanPropertyCompound compound;
|
||||
|
||||
public BindableCompound(BeanPropertyCompound embProp, List<BindableProperty> list) {
|
||||
this.compound = embProp;
|
||||
this.items = list.toArray(new BindableProperty[list.size()]);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BindableCompound " + compound + " items:" + Arrays.toString(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDraftOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void dmlAppend(GenerateDmlRequest request) {
|
||||
|
||||
for (BindableProperty item : items) {
|
||||
item.dmlAppend(request);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
|
||||
if (request.isAddToUpdate(compound)) {
|
||||
list.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException {
|
||||
|
||||
// get the compound type value
|
||||
Object valueObject = compound.getValue(bean);
|
||||
|
||||
// bind each of the underlying scalar values for this compound type
|
||||
for (BindableProperty item : items) {
|
||||
item.dmlBindObject(bindRequest, valueObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-31
@@ -2,10 +2,8 @@ package com.avaje.ebeaninternal.server.persist.dmlbind;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
|
||||
import com.avaje.ebeaninternal.server.persist.dml.DmlMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -28,24 +26,7 @@ public class FactoryBaseProperties {
|
||||
*/
|
||||
public void create(List<Bindable> list, BeanDescriptor<?> desc, DmlMode mode, boolean withLobs) {
|
||||
|
||||
add(desc.propertiesBaseScalar(), list, mode, withLobs);
|
||||
|
||||
BeanPropertyCompound[] compoundProps = desc.propertiesBaseCompound();
|
||||
for (BeanPropertyCompound compoundProp : compoundProps) {
|
||||
BeanProperty[] props = compoundProp.getScalarProperties();
|
||||
|
||||
List<BindableProperty> newList = new ArrayList<>(props.length);
|
||||
addCompound(props, newList, mode, withLobs);
|
||||
|
||||
BindableCompound compoundBindable = new BindableCompound(compoundProp, newList);
|
||||
|
||||
list.add(compoundBindable);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(BeanProperty[] props, List<Bindable> list, DmlMode mode, boolean withLobs) {
|
||||
|
||||
for (BeanProperty prop : props) {
|
||||
for (BeanProperty prop : desc.propertiesBaseScalar()) {
|
||||
Bindable item = factoryProperty.create(prop, mode, withLobs);
|
||||
if (item != null) {
|
||||
list.add(item);
|
||||
@@ -53,15 +34,4 @@ public class FactoryBaseProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private void addCompound(BeanProperty[] props, List<BindableProperty> list, DmlMode mode, boolean withLobs) {
|
||||
|
||||
for (BeanProperty prop : props) {
|
||||
BindableProperty item = (BindableProperty) factoryProperty.create(prop, mode, withLobs);
|
||||
if (item != null) {
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -457,7 +457,6 @@ public class SqlTreeBuilder {
|
||||
|
||||
// normal simple properties of the bean
|
||||
selectProps.add(desc.propertiesBaseScalar());
|
||||
selectProps.add(desc.propertiesBaseCompound());
|
||||
selectProps.add(desc.propertiesEmbedded());
|
||||
|
||||
BeanPropertyAssocOne<?>[] propertiesOne = desc.propertiesOne();
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
|
||||
import java.time.Period;
|
||||
|
||||
/**
|
||||
* Compound type for Period value type.
|
||||
* <p>
|
||||
* Persists the Period into 3 separate integer columns for years, months and days.
|
||||
* </p>
|
||||
*/
|
||||
public class CompoundTypePeriod implements CompoundType<Period> {
|
||||
|
||||
final CompoundTypeProperty[] properties = new CompoundTypeProperty[3];
|
||||
|
||||
public CompoundTypePeriod() {
|
||||
properties[0] = new CTPeriodYear();
|
||||
properties[1] = new CTPeriodMonth();
|
||||
properties[2] = new CTPeriodDay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period create(Object[] propertyValues) {
|
||||
return Period.of((Integer) propertyValues[0], (Integer) propertyValues[1], (Integer) propertyValues[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public CompoundTypeProperty<Period, ?>[] getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
static class CTPeriodYear implements CompoundTypeProperty<Period, Integer> {
|
||||
|
||||
public String getName() {
|
||||
return "years";
|
||||
}
|
||||
|
||||
public Integer getValue(Period valueObject) {
|
||||
return valueObject.getYears();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CTPeriodMonth implements CompoundTypeProperty<Period, Integer> {
|
||||
|
||||
public String getName() {
|
||||
return "months";
|
||||
}
|
||||
|
||||
public Integer getValue(Period valueObject) {
|
||||
return valueObject.getMonths();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CTPeriodDay implements CompoundTypeProperty<Period, Integer> {
|
||||
|
||||
public String getName() {
|
||||
return "days";
|
||||
}
|
||||
|
||||
public Integer getValue(Period valueObject) {
|
||||
return valueObject.getDays();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
|
||||
/**
|
||||
* Wraps a CompoundTypeProperty with it's type and parent for nested compound
|
||||
* types.
|
||||
*/
|
||||
public class CtCompoundProperty {
|
||||
|
||||
private final String relativeName;
|
||||
|
||||
private final CtCompoundProperty parent;
|
||||
|
||||
private final CtCompoundType<?> compoundType;
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
private final CompoundTypeProperty property;
|
||||
|
||||
public CtCompoundProperty(String relativeName, CtCompoundProperty parent, CtCompoundType<?> ctType,
|
||||
CompoundTypeProperty<?, ?> property) {
|
||||
|
||||
this.relativeName = relativeName;
|
||||
this.parent = parent;
|
||||
this.compoundType = ctType;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* The property name relative to the root of the compound type.
|
||||
*/
|
||||
public String getRelativeName() {
|
||||
return relativeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The property name local to its type.
|
||||
*/
|
||||
public String getPropertyName() {
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return relativeName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getValue(Object valueObject) {
|
||||
if (valueObject == null) {
|
||||
return null;
|
||||
}
|
||||
if (parent != null) {
|
||||
valueObject = parent.getValue(valueObject);
|
||||
}
|
||||
return property.getValue(valueObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a scalar value that is used to build the immutable compound value
|
||||
* object.
|
||||
* <p>
|
||||
* When all the scalar values have been collected then the compound value
|
||||
* object is built and this can be recursive for nested compound types.
|
||||
* </p>
|
||||
*/
|
||||
public Object setValue(Object bean, Object value) {
|
||||
|
||||
// compoundType and propertyName should be correct depth
|
||||
Object compoundValue = ImmutableCompoundTypeBuilder.set(compoundType, property.getName(), value);
|
||||
|
||||
if (compoundValue != null && parent != null) {
|
||||
// Continue up the tree
|
||||
return parent.setValue(bean, compoundValue);
|
||||
|
||||
} else {
|
||||
return compoundValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.text.StringParser;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
|
||||
|
||||
/**
|
||||
* Adapter for CtCompoundProperty to ElPropertyValue.
|
||||
* <p>
|
||||
* This is used for non-scalar properties of a Compound Value Object. These only
|
||||
* occur in nested compound types.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class CtCompoundPropertyElAdapter implements ElPropertyValue {
|
||||
|
||||
private final CtCompoundProperty prop;
|
||||
|
||||
private int deployOrder;
|
||||
|
||||
public CtCompoundPropertyElAdapter(CtCompoundProperty prop) {
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
public void setDeployOrder(int deployOrder) {
|
||||
this.deployOrder = deployOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAggregation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pathGetNested(Object bean) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pathGet(Object bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pathSet(Object bean, Object value) {
|
||||
prop.setValue(bean, value);
|
||||
}
|
||||
|
||||
public String getAssocIdExpression(String prefix, String operator) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
public Object[] getAssocIdValues(EntityBean bean) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
public String getAssocIdInExpr(String prefix) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
public String getAssocIdInValueExpr(int size) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
public BeanProperty getBeanProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StringParser getStringParser() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isDbEncrypted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLocalEncrypted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAssocProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getJdbcType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Object parseDateTime(long systemTimeMillis) {
|
||||
throw new RuntimeException("Not Supported or Expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsMany() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean containsManySince(String sinceProperty) {
|
||||
return containsMany();
|
||||
}
|
||||
|
||||
public String getDbColumn() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getElPlaceholder(boolean encrypted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getElPrefix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return prop.getPropertyName();
|
||||
}
|
||||
|
||||
public String getElName() {
|
||||
return prop.getPropertyName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.ebeaninternal.server.text.json.WriteJson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The internal representation of a Compound Type (Immutable Compound Value Object).
|
||||
*
|
||||
* @param <V> The Type of the "Immutable Compound Value Object".
|
||||
*/
|
||||
public final class CtCompoundType<V> implements ScalarDataReader<V> {
|
||||
|
||||
private final Class<V> cvoClass;
|
||||
|
||||
private final CompoundType<V> cvoType;
|
||||
|
||||
private final ScalarDataReader<Object>[] propReaders;
|
||||
|
||||
private final CompoundTypeProperty<V, ?>[] properties;
|
||||
|
||||
public CtCompoundType(Class<V> cvoClass, CompoundType<V> cvoType, ScalarDataReader<Object>[] propReaders) {
|
||||
|
||||
this.cvoClass = cvoClass;
|
||||
this.cvoType = cvoType;
|
||||
this.properties = cvoType.getProperties();
|
||||
this.propReaders = propReaders;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return cvoClass.toString();
|
||||
}
|
||||
|
||||
public Class<V> getCompoundTypeClass() {
|
||||
return cvoClass;
|
||||
}
|
||||
|
||||
public V create(Object[] propertyValues) {
|
||||
return cvoType.create(propertyValues);
|
||||
}
|
||||
|
||||
|
||||
public V create(Map<String, Object> valueMap) {
|
||||
|
||||
if (valueMap.size() != properties.length) {
|
||||
// not enough elements in the map
|
||||
return null;
|
||||
}
|
||||
|
||||
// we expect the map to contain a value for
|
||||
// each property and that the values are the
|
||||
// correct type
|
||||
Object[] propertyValues = new Object[properties.length];
|
||||
for (int i = 0; i < properties.length; i++) {
|
||||
propertyValues[i] = valueMap.get(properties[i].getName());
|
||||
if (propertyValues[i] == null) {
|
||||
String m = "Null value for " + properties[i].getName() + " in map " + valueMap;
|
||||
throw new RuntimeException(m);
|
||||
}
|
||||
}
|
||||
|
||||
return create(propertyValues);
|
||||
}
|
||||
|
||||
public V read(DataReader source) throws SQLException {
|
||||
|
||||
boolean nullValue = false;
|
||||
Object[] values = new Object[propReaders.length];
|
||||
|
||||
for (int i = 0; i < propReaders.length; i++) {
|
||||
Object o = propReaders[i].read(source);
|
||||
values[i] = o;
|
||||
if (o == null) {
|
||||
nullValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nullValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return create(values);
|
||||
}
|
||||
|
||||
public void loadIgnore(DataReader dataReader) {
|
||||
for (ScalarDataReader<Object> propReader : propReaders) {
|
||||
propReader.loadIgnore(dataReader);
|
||||
}
|
||||
}
|
||||
|
||||
public void bind(DataBind b, V value) throws SQLException {
|
||||
|
||||
CompoundTypeProperty<V, ?>[] props = cvoType.getProperties();
|
||||
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
Object o = props[i].getValue(value);
|
||||
propReaders[i].bind(b, o);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively accumulate all the scalar types (in depth first order).
|
||||
* <p>
|
||||
* This creates a flat list of scalars even when compound types are embedded
|
||||
* inside compound types.
|
||||
* </p>
|
||||
*/
|
||||
public void accumulateScalarTypes(String parent, CtCompoundTypeScalarList list) {
|
||||
|
||||
CompoundTypeProperty<V, ?>[] props = cvoType.getProperties();
|
||||
|
||||
for (int i = 0; i < propReaders.length; i++) {
|
||||
String propName = getFullPropName(parent, props[i].getName());
|
||||
|
||||
list.addCompoundProperty(propName, this, props[i]);
|
||||
|
||||
propReaders[i].accumulateScalarTypes(propName, list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full property name (for compound types embedded in other
|
||||
* compound types).
|
||||
*
|
||||
* @param parent the parent property name
|
||||
* @param propName the local property name
|
||||
*/
|
||||
private String getFullPropName(String parent, String propName) {
|
||||
if (parent == null) {
|
||||
return propName;
|
||||
} else {
|
||||
return parent + "." + propName;
|
||||
}
|
||||
}
|
||||
|
||||
public Object jsonConvert(Map<String, Object> map) {
|
||||
return readJsonElementObject(map);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object readJsonElementObject(Map<String, Object> jsonObject) {
|
||||
|
||||
boolean nullValue = false;
|
||||
Object[] values = new Object[propReaders.length];
|
||||
|
||||
for (int i = 0; i < propReaders.length; i++) {
|
||||
String propName = properties[i].getName();
|
||||
Object jsonElement = jsonObject.get(propName);
|
||||
|
||||
if (propReaders[i] instanceof CtCompoundType<?>) {
|
||||
values[i] = ((CtCompoundType<?>) propReaders[i]).readJsonElementObject((Map<String, Object>) jsonElement);
|
||||
} else {
|
||||
values[i] = ((ScalarType<?>) propReaders[i]).parse(jsonElement.toString());
|
||||
}
|
||||
if (values[i] == null) {
|
||||
nullValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nullValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return create(values);
|
||||
}
|
||||
|
||||
|
||||
public void jsonWrite(WriteJson ctx, Object valueObject, String propertyName) throws IOException {
|
||||
|
||||
ctx.beginAssocOne(propertyName, valueObject);
|
||||
jsonWriteProps(ctx, valueObject);
|
||||
ctx.endAssocOne();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void jsonWriteProps(WriteJson ctx, Object valueObject) throws IOException {
|
||||
|
||||
ctx.gen().writeStartObject();
|
||||
|
||||
for (int i = 0; i < properties.length; i++) {
|
||||
String propName = properties[i].getName();
|
||||
Object value = properties[i].getValue((V) valueObject);
|
||||
if (propReaders[i] instanceof CtCompoundType<?>) {
|
||||
ctx.writeFieldName(propName);
|
||||
((CtCompoundType) propReaders[i]).jsonWrite(ctx, value, propName);
|
||||
|
||||
} else {
|
||||
ctx.writeFieldName(propName);
|
||||
((ScalarType) propReaders[i]).jsonWrite(ctx.gen(), value);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.gen().writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to build a flat list of all the scalar types nested in a compound type.
|
||||
*/
|
||||
public final class CtCompoundTypeScalarList {
|
||||
|
||||
private final LinkedHashMap<String, ScalarType<?>> scalarProps = new LinkedHashMap<>();
|
||||
|
||||
private final LinkedHashMap<String, CtCompoundProperty> compoundProperties = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Return the list of non-scalar properties. These occur when compound types are nested.
|
||||
*/
|
||||
public List<CtCompoundProperty> getNonScalarProperties() {
|
||||
|
||||
List<CtCompoundProperty> nonScalarProps = new ArrayList<>();
|
||||
|
||||
for (String propKey : compoundProperties.keySet()) {
|
||||
if (!scalarProps.containsKey(propKey)) {
|
||||
nonScalarProps.add(compoundProperties.get(propKey));
|
||||
}
|
||||
}
|
||||
|
||||
return nonScalarProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a property with it's associated compound type and relative name.
|
||||
*/
|
||||
public void addCompoundProperty(String propName, CtCompoundType<?> t, CompoundTypeProperty<?, ?> prop) {
|
||||
|
||||
CtCompoundProperty parent = null;
|
||||
String[] split = SplitName.split(propName);
|
||||
if (split[0] != null) {
|
||||
parent = compoundProperties.get(split[0]);
|
||||
}
|
||||
|
||||
CtCompoundProperty p = new CtCompoundProperty(propName, parent, t, prop);
|
||||
compoundProperties.put(propName, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a scalarType used in the compound type with its given property name.
|
||||
*/
|
||||
public void addScalarType(String propName, ScalarType<?> scalar) {
|
||||
scalarProps.put(propName, scalar);
|
||||
}
|
||||
|
||||
public CtCompoundProperty getCompoundType(String propName) {
|
||||
return compoundProperties.get(propName);
|
||||
}
|
||||
|
||||
public Set<Entry<String, ScalarType<?>>> entries() {
|
||||
return scalarProps.entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import com.avaje.ebean.annotation.DbArray;
|
||||
import com.avaje.ebean.annotation.DbEnumType;
|
||||
import com.avaje.ebean.annotation.DbEnumValue;
|
||||
import com.avaje.ebean.annotation.EnumValue;
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.ebean.config.JsonConfig;
|
||||
import com.avaje.ebean.config.Platform;
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
@@ -15,14 +13,6 @@ import com.avaje.ebean.config.dbplatform.DbPlatformType;
|
||||
import com.avaje.ebean.dbmigration.DbOffline;
|
||||
import com.avaje.ebean.plugin.ExtraTypeFactory;
|
||||
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMeta;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMetaFactory;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.KnownImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedCompoundTypeProperty;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ReflectionBasedTypeBuilder;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -57,27 +47,12 @@ import java.time.Month;
|
||||
import java.time.MonthDay;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.Period;
|
||||
import java.time.Year;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -86,12 +61,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* Manages the list of ScalarType that is available.
|
||||
* </p>
|
||||
*/
|
||||
public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultTypeManager.class);
|
||||
|
||||
private final ConcurrentHashMap<Class<?>, CtCompoundType<?>> compoundTypeMap;
|
||||
|
||||
private final ConcurrentHashMap<Class<?>, ScalarType<?>> typeMap;
|
||||
|
||||
private final ConcurrentHashMap<Integer, ScalarType<?>> nativeMap;
|
||||
@@ -150,12 +123,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
private final List<ScalarType<?>> customScalarTypes = new ArrayList<>();
|
||||
|
||||
private final CheckImmutable checkImmutable;
|
||||
|
||||
private final ImmutableMetaFactory immutableMetaFactory = new ImmutableMetaFactory();
|
||||
|
||||
private final ReflectionBasedTypeBuilder reflectScalarBuilder;
|
||||
|
||||
private final JsonConfig.DateTime jsonDateTime;
|
||||
|
||||
private final Object objectMapper;
|
||||
@@ -198,10 +165,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
this.java7Present = config.getClassLoadConfig().isJava7Present();
|
||||
this.jsonDateTime = config.getJsonDateTime();
|
||||
this.checkImmutable = new CheckImmutable(this);
|
||||
this.reflectScalarBuilder = new ReflectionBasedTypeBuilder(this);
|
||||
|
||||
this.compoundTypeMap = new ConcurrentHashMap<>();
|
||||
this.typeMap = new ConcurrentHashMap<>();
|
||||
this.nativeMap = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -225,7 +188,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
initialiseCustomScalarTypes(jsonDateTime, bootupClasses);
|
||||
initialiseScalarConverters(bootupClasses);
|
||||
initialiseAttributeConverters(bootupClasses);
|
||||
initialiseCompoundTypes(bootupClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,53 +241,12 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return scalarDataReader != null;
|
||||
}
|
||||
|
||||
public CheckImmutableResponse checkImmutable(Class<?> cls) {
|
||||
return checkImmutable.checkImmutable(cls);
|
||||
}
|
||||
|
||||
private ScalarType<?> register(ScalarType<?> st) {
|
||||
add(st);
|
||||
logger.debug("Registering ScalarType for " + st.getType() + " implemented using reflection");
|
||||
return st;
|
||||
}
|
||||
|
||||
public ScalarDataReader<?> recursiveCreateScalarDataReader(Class<?> cls) {
|
||||
|
||||
ScalarDataReader<?> scalarReader = getScalarDataReader(cls);
|
||||
if (scalarReader != null) {
|
||||
return scalarReader;
|
||||
}
|
||||
|
||||
ImmutableMeta meta = immutableMetaFactory.createImmutableMeta(cls);
|
||||
|
||||
if (!meta.isCompoundType()) {
|
||||
return register(reflectScalarBuilder.buildScalarType(meta));
|
||||
}
|
||||
|
||||
ReflectionBasedCompoundType compoundType = reflectScalarBuilder.buildCompound(meta);
|
||||
Class<?> compoundTypeClass = compoundType.getCompoundType();
|
||||
|
||||
return createCompoundScalarDataReader(compoundTypeClass, compoundType, " using reflection");
|
||||
|
||||
}
|
||||
|
||||
public ScalarType<?> recursiveCreateScalarTypes(Class<?> cls) {
|
||||
|
||||
ScalarType<?> scalarType = getScalarType(cls);
|
||||
if (scalarType != null) {
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
ImmutableMeta meta = immutableMetaFactory.createImmutableMeta(cls);
|
||||
|
||||
if (!meta.isCompoundType()) {
|
||||
return register(reflectScalarBuilder.buildScalarType(meta));
|
||||
}
|
||||
|
||||
throw new RuntimeException("Not allowed compound types here");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom ScalarType.
|
||||
*/
|
||||
@@ -360,10 +281,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
public CtCompoundType<?> getCompoundType(Class<?> type) {
|
||||
return compoundTypeMap.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ScalarType for the given jdbc type as per java.sql.Types.
|
||||
*/
|
||||
@@ -401,29 +318,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScalarDataReader<?> getScalarDataReader(Class<?> propertyType, int sqlType) {
|
||||
|
||||
if (sqlType == 0) {
|
||||
return recursiveCreateScalarDataReader(propertyType);
|
||||
}
|
||||
|
||||
for (ScalarType<?> customScalarType : customScalarTypes) {
|
||||
if (sqlType == customScalarType.getJdbcType() && (propertyType.equals(customScalarType.getType()))) {
|
||||
|
||||
return customScalarType;
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Unable to find a custom ScalarType with type [" + propertyType + "] and java.sql.Type [" + sqlType + "]";
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
private ScalarDataReader<?> getScalarDataReader(Class<?> type) {
|
||||
ScalarDataReader<?> reader = typeMap.get(type);
|
||||
if (reader == null) {
|
||||
reader = compoundTypeMap.get(type);
|
||||
}
|
||||
return reader;
|
||||
return typeMap.get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -857,83 +753,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseCompoundTypes(BootupClasses bootupClasses) {
|
||||
|
||||
List<Class<? extends CompoundType<?>>> compoundTypes = bootupClasses.getCompoundTypes();
|
||||
for (Class<? extends CompoundType<?>> compoundType1 : compoundTypes) {
|
||||
|
||||
Class<?> type = compoundType1;
|
||||
try {
|
||||
|
||||
Class<?>[] paramTypes = TypeReflectHelper.getParams(type, CompoundType.class);
|
||||
if (paramTypes.length != 1) {
|
||||
throw new RuntimeException("Expecting 1 generic paramter type but got " + Arrays.toString(paramTypes) + " for " + type);
|
||||
}
|
||||
|
||||
Class<?> compoundTypeClass = paramTypes[0];
|
||||
|
||||
CompoundType<?> compoundType = (CompoundType<?>) type.newInstance();
|
||||
createCompoundScalarDataReader(compoundTypeClass, compoundType, "");
|
||||
|
||||
} catch (Exception e) {
|
||||
String msg = "Error initialising component " + type;
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private CtCompoundType createCompoundScalarDataReader(Class<?> compoundTypeClass, CompoundType<?> compoundType, String info) {
|
||||
|
||||
CtCompoundType<?> ctCompoundType = compoundTypeMap.get(compoundTypeClass);
|
||||
if (ctCompoundType != null) {
|
||||
logger.info("Already registered compound type " + compoundTypeClass);
|
||||
return ctCompoundType;
|
||||
}
|
||||
|
||||
CompoundTypeProperty<?, ?>[] cprops = compoundType.getProperties();
|
||||
|
||||
ScalarDataReader[] dataReaders = new ScalarDataReader[cprops.length];
|
||||
|
||||
for (int i = 0; i < cprops.length; i++) {
|
||||
|
||||
Class<?> propertyType = getCompoundPropertyType(cprops[i]);
|
||||
|
||||
ScalarDataReader<?> scalarDataReader = getScalarDataReader(propertyType, cprops[i].getDbType());
|
||||
if (scalarDataReader == null) {
|
||||
throw new RuntimeException("Could not find ScalarDataReader for " + propertyType);
|
||||
}
|
||||
|
||||
dataReaders[i] = scalarDataReader;
|
||||
}
|
||||
|
||||
CtCompoundType ctType = new CtCompoundType(compoundTypeClass, compoundType, dataReaders);
|
||||
|
||||
logger.debug("Registering CompoundType " + compoundTypeClass + " " + info);
|
||||
compoundTypeMap.put(compoundTypeClass, ctType);
|
||||
|
||||
return ctType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property type for a given property of a compound type.
|
||||
*/
|
||||
private Class<?> getCompoundPropertyType(CompoundTypeProperty<?, ?> prop) {
|
||||
|
||||
if (prop instanceof ReflectionBasedCompoundTypeProperty) {
|
||||
return ((ReflectionBasedCompoundTypeProperty) prop).getPropertyType();
|
||||
}
|
||||
|
||||
// determine the types from generic parameter types using reflection
|
||||
Class<?>[] propParamTypes = TypeReflectHelper.getParams(prop.getClass(), CompoundTypeProperty.class);
|
||||
if (propParamTypes.length != 2) {
|
||||
throw new RuntimeException("Expecting 2 generic paramter types but got " + Arrays.toString(propParamTypes) + " for " + prop.getClass());
|
||||
}
|
||||
|
||||
return propParamTypes[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add support for Jackson's JsonNode mapping to Clob, Blob, Varchar, JSON and JSONB.
|
||||
*/
|
||||
@@ -983,8 +802,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
typeMap.put(ZoneId.class, new ScalarTypeZoneId());
|
||||
typeMap.put(ZoneOffset.class, new ScalarTypeZoneOffset());
|
||||
|
||||
createCompoundScalarDataReader(Period.class, new CompoundTypePeriod(), "");
|
||||
|
||||
boolean localTimeNanos = config.isLocalTimeWithNanos();
|
||||
typeMap.put(java.time.LocalTime.class, (localTimeNanos) ? new ScalarTypeLocalTimeWithNanos() : new ScalarTypeLocalTime());
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to build Immutable Compound Value objects.
|
||||
* <p>
|
||||
* The individual values are collected for a given type and when they have all
|
||||
* been collected then the immutable compound value object is created and
|
||||
* returned.
|
||||
* </p>
|
||||
*/
|
||||
public final class ImmutableCompoundTypeBuilder {
|
||||
|
||||
private static final ThreadLocal<ImmutableCompoundTypeBuilder> local = new ThreadLocal<ImmutableCompoundTypeBuilder>() {
|
||||
protected synchronized ImmutableCompoundTypeBuilder initialValue() {
|
||||
return new ImmutableCompoundTypeBuilder();
|
||||
}
|
||||
};
|
||||
|
||||
private final Map<Class<?>, Entry> entryMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Clear the cache of partial compound objects.
|
||||
*/
|
||||
public static void clear() {
|
||||
local.get().entryMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the property of a compound type.
|
||||
* <p>
|
||||
* If this is the last value required for the compound type then the
|
||||
* compound type is created and returned, otherwise null is returned (and we
|
||||
* need more values set).
|
||||
* </p>
|
||||
*/
|
||||
public static Object set(CtCompoundType<?> ct, String propName, Object value) {
|
||||
return local.get().setValue(ct, propName, value);
|
||||
}
|
||||
|
||||
private Object setValue(CtCompoundType<?> ct, String propName, Object value) {
|
||||
|
||||
Entry e = getEntry(ct);
|
||||
Object compoundValue = e.set(propName, value);
|
||||
if (compoundValue != null) {
|
||||
removeEntry(ct);
|
||||
}
|
||||
return compoundValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once we have built a compound value we remove the entry.
|
||||
*/
|
||||
private void removeEntry(CtCompoundType<?> ct) {
|
||||
entryMap.remove(ct.getCompoundTypeClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Entry which contains the values collected so far for this type.
|
||||
*/
|
||||
private Entry getEntry(CtCompoundType<?> ct) {
|
||||
Entry e = entryMap.get(ct.getCompoundTypeClass());
|
||||
if (e == null) {
|
||||
e = new Entry(ct);
|
||||
entryMap.put(ct.getCompoundTypeClass(), e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the values collected so far for a given compound type.
|
||||
*/
|
||||
private static class Entry {
|
||||
|
||||
private final CtCompoundType<?> ct;
|
||||
|
||||
private final Map<String, Object> valueMap;
|
||||
|
||||
private Entry(CtCompoundType<?> ct) {
|
||||
this.ct = ct;
|
||||
this.valueMap = new HashMap<>();
|
||||
}
|
||||
|
||||
private Object set(String propName, Object value) {
|
||||
// collect the values...
|
||||
valueMap.put(propName, value);
|
||||
|
||||
// when got all the values this returns the
|
||||
// compound value, otherwise null
|
||||
return ct.create(valueMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,4 @@ public interface ScalarDataReader<T> {
|
||||
*/
|
||||
void bind(DataBind b, T value) throws SQLException;
|
||||
|
||||
/**
|
||||
* Accumulate all the scalar types used by an immutable compound value type.
|
||||
*/
|
||||
void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list);
|
||||
|
||||
}
|
||||
|
||||
@@ -73,8 +73,4 @@ public abstract class ScalarTypeBase<T> implements ScalarType<T> {
|
||||
reader.incrementPos(1);
|
||||
}
|
||||
|
||||
public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) {
|
||||
list.addScalarType(propName, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,10 +121,6 @@ public class ScalarTypeBytesEncrypted implements ScalarType<byte[]> {
|
||||
return baseType.toJdbcType(value);
|
||||
}
|
||||
|
||||
public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) {
|
||||
baseType.accumulateScalarTypes(propName, list);
|
||||
}
|
||||
|
||||
public byte[] readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
|
||||
@@ -137,11 +137,6 @@ public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
return wrapped.toJdbcType(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) {
|
||||
wrapped.accumulateScalarTypes(propName, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
return wrapped.jsonRead(parser);
|
||||
|
||||
@@ -183,11 +183,6 @@ public class ScalarTypeWrapper<B, S> implements ScalarType<B> {
|
||||
return scalarType.toJdbcType(sv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) {
|
||||
list.addScalarType(propName, this);
|
||||
}
|
||||
|
||||
public ScalarType<?> getScalarType() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.annotation.DbArray;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@@ -10,21 +9,6 @@ import java.lang.reflect.Type;
|
||||
*/
|
||||
public interface TypeManager {
|
||||
|
||||
/**
|
||||
* Check if the type is immutable using reflection.
|
||||
*/
|
||||
CheckImmutableResponse checkImmutable(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Create ScalarDataReader's for the Immutable compound type.
|
||||
*/
|
||||
ScalarDataReader<?> recursiveCreateScalarDataReader(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Create ScalarTypes for this Immutable Value Object type.
|
||||
*/
|
||||
ScalarType<?> recursiveCreateScalarTypes(Class<?> cls);
|
||||
|
||||
/**
|
||||
* Register a ScalarType with the system.
|
||||
*/
|
||||
@@ -35,11 +19,6 @@ public interface TypeManager {
|
||||
*/
|
||||
void addEnumType(ScalarType<?> type, Class<? extends Enum> myEnumClass);
|
||||
|
||||
/**
|
||||
* Return the Internal CompoundType handler for a given compound type.
|
||||
*/
|
||||
CtCompoundType<?> getCompoundType(Class<?> type);
|
||||
|
||||
/**
|
||||
* Return the ScalarType for a given jdbc type.
|
||||
*
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
|
||||
public class CheckImmutable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CheckImmutable.class);
|
||||
|
||||
private final KnownImmutable knownImmutable;
|
||||
|
||||
public CheckImmutable(KnownImmutable knownImmutable) {
|
||||
this.knownImmutable = knownImmutable;
|
||||
}
|
||||
|
||||
public CheckImmutableResponse checkImmutable(Class<?> cls) {
|
||||
|
||||
CheckImmutableResponse res = new CheckImmutableResponse();
|
||||
|
||||
isImmutable(cls, res);
|
||||
|
||||
if (res.isImmutable()) {
|
||||
res.setCompoundType(isCompoundType(cls));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean isCompoundType(Class<?> cls) {
|
||||
|
||||
int maxLength = 0;
|
||||
Constructor<?> chosen = null;
|
||||
|
||||
// find the constructor with the most number of parameters
|
||||
Constructor<?>[] constructors = cls.getConstructors();
|
||||
for (Constructor<?> constructor : constructors) {
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
if (parameterTypes.length > maxLength) {
|
||||
maxLength = parameterTypes.length;
|
||||
chosen = constructor;
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("checkImmutable " + cls + " constructor " + chosen);
|
||||
|
||||
return maxLength > 1;
|
||||
}
|
||||
|
||||
private boolean isImmutable(Class<?> cls, CheckImmutableResponse res) {
|
||||
|
||||
|
||||
if (knownImmutable.isKnownImmutable(cls)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cls.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (hasDefaultConstructor(cls)) {
|
||||
// must not have a default constructor to be considered immutable
|
||||
res.setReasonNotImmutable(cls + " has a default constructor");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check super class
|
||||
Class<?> superClass = cls.getSuperclass();
|
||||
|
||||
if (!isImmutable(superClass, res)) {
|
||||
res.setReasonNotImmutable("Super not Immutable " + superClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasAllFinalFields(cls, res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lets hope we didn't forget something
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasAllFinalFields(Class<?> cls, CheckImmutableResponse res) {
|
||||
|
||||
// Check all fields defined in the class for type and if they are final
|
||||
Field[] objFields = cls.getDeclaredFields();
|
||||
for (Field objField : objFields) {
|
||||
if (!Modifier.isStatic(objField.getModifiers())) {
|
||||
if (!Modifier.isFinal(objField.getModifiers())) {
|
||||
res.setReasonNotImmutable("Non final field " + cls + "." + objField.getName());
|
||||
return false;
|
||||
}
|
||||
if (!isImmutable(objField.getType(), res)) {
|
||||
res.setReasonNotImmutable("Non Immutable field type " + objField.getType());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean hasDefaultConstructor(Class<?> cls) {
|
||||
|
||||
Class<?>[] noParams = new Class<?>[0];
|
||||
try {
|
||||
cls.getDeclaredConstructor(noParams);
|
||||
return true;
|
||||
|
||||
} catch (SecurityException e) {
|
||||
// this is ok
|
||||
return false;
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
// this is expected for our IVO's
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
|
||||
public class CheckImmutableResponse {
|
||||
|
||||
private boolean immutable = true;
|
||||
|
||||
private String reasonNotImmutable;
|
||||
|
||||
private boolean compoundType;
|
||||
|
||||
public String toString() {
|
||||
if (immutable) {
|
||||
return "immutable";
|
||||
} else {
|
||||
return "not immutable due to:" + reasonNotImmutable;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCompoundType() {
|
||||
return compoundType;
|
||||
}
|
||||
|
||||
public void setCompoundType(boolean compoundType) {
|
||||
this.compoundType = compoundType;
|
||||
}
|
||||
|
||||
public void setReasonNotImmutable(String error) {
|
||||
this.immutable = false;
|
||||
this.reasonNotImmutable = error;
|
||||
}
|
||||
|
||||
public boolean isImmutable() {
|
||||
return immutable;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ImmutableMeta {
|
||||
|
||||
private final Constructor<?> constructor;
|
||||
|
||||
private final Method[] readers;
|
||||
|
||||
public ImmutableMeta(Constructor<?> constructor, Method[] readers) {
|
||||
this.constructor = constructor;
|
||||
this.readers = readers;
|
||||
}
|
||||
|
||||
|
||||
public Constructor<?> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
public Method[] getReaders() {
|
||||
return readers;
|
||||
}
|
||||
|
||||
public boolean isCompoundType() {
|
||||
return readers.length > 1;
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class ImmutableMetaFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImmutableMetaFactory.class);
|
||||
|
||||
public ImmutableMeta createImmutableMeta(Class<?> cls) {
|
||||
|
||||
ScoreConstructor[] scoreConstructors = scoreConstructors(cls);
|
||||
|
||||
ArrayList<RuntimeException> errors = new ArrayList<>();
|
||||
|
||||
// search the constructors in score order ...
|
||||
// ... we need to find a set of readers for each
|
||||
// ... type in the constructor
|
||||
for (ScoreConstructor scoreConstructor : scoreConstructors) {
|
||||
Constructor<?> constructor = scoreConstructor.constructor;
|
||||
|
||||
try {
|
||||
Method[] getters = findGetters(cls, constructor);
|
||||
|
||||
return new ImmutableMeta(constructor, getters);
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
String msg = "Error finding getter method on " + cls + " with constructor " + constructor;
|
||||
errors.add(new RuntimeException(msg, e));
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Was unable to use reflection to find a constructor and appropriate getters for" +
|
||||
"immutable type " + cls + ". The errors while looking for the getter methods follow:";
|
||||
logger.error(msg);
|
||||
|
||||
for (RuntimeException runtimeException : errors) {
|
||||
logger.error("Error with " + cls, runtimeException);
|
||||
}
|
||||
|
||||
msg = "Unable to use reflection to build ImmutableMeta for " + cls
|
||||
+ ". Associated Errors trying to find a constructor and getter methods have been logged";
|
||||
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
private ScoreConstructor getScore(Constructor<?> c) {
|
||||
|
||||
Class<?>[] parameterTypes = c.getParameterTypes();
|
||||
int score = -1000 * parameterTypes.length;
|
||||
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
if (parameterType.equals(String.class)) {
|
||||
// string is very generic and we would prefer
|
||||
// a more specific type if that was available
|
||||
score = score + 1;
|
||||
|
||||
} else if (parameterType.equals(BigDecimal.class)) {
|
||||
score = score - 10;
|
||||
} else if (parameterType.equals(Timestamp.class)) {
|
||||
score = score - 10;
|
||||
} else if (parameterType.equals(double.class)) {
|
||||
score = score - 9;
|
||||
} else if (parameterType.equals(Double.class)) {
|
||||
score = score - 8;
|
||||
} else if (parameterType.equals(float.class)) {
|
||||
score = score - 7;
|
||||
} else if (parameterType.equals(Float.class)) {
|
||||
score = score - 6;
|
||||
} else if (parameterType.equals(long.class)) {
|
||||
score = score - 5;
|
||||
} else if (parameterType.equals(Long.class)) {
|
||||
score = score - 4;
|
||||
} else if (parameterType.equals(int.class)) {
|
||||
score = score - 3;
|
||||
} else if (parameterType.equals(Integer.class)) {
|
||||
score = score - 2;
|
||||
}
|
||||
}
|
||||
|
||||
return new ScoreConstructor(score, c);
|
||||
}
|
||||
|
||||
private ScoreConstructor[] scoreConstructors(Class<?> cls) {
|
||||
|
||||
// find the constructor with the most number of parameters
|
||||
int maxParamCount = 0;
|
||||
|
||||
Constructor<?>[] constructors = cls.getConstructors();
|
||||
|
||||
ScoreConstructor[] score = new ScoreConstructor[constructors.length];
|
||||
|
||||
for (int i = 0; i < constructors.length; i++) {
|
||||
score[i] = getScore(constructors[i]);
|
||||
if (score[i].hasDuplicateParamTypes()) {
|
||||
String msg = "Duplicate parameter types in " + score[i].constructor;
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
if (score[i].getParamCount() > maxParamCount) {
|
||||
maxParamCount = score[i].getParamCount();
|
||||
}
|
||||
}
|
||||
|
||||
// filter out any constructors with less parameters than the max
|
||||
ArrayList<ScoreConstructor> list = new ArrayList<>();
|
||||
for (ScoreConstructor aScore : score) {
|
||||
if (aScore.getParamCount() == maxParamCount) {
|
||||
list.add(aScore);
|
||||
}
|
||||
}
|
||||
|
||||
score = list.toArray(new ScoreConstructor[list.size()]);
|
||||
|
||||
// sort into score ascending order
|
||||
Arrays.sort(score);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
private Method[] findGetters(Class<?> cls, Constructor<?> c) throws NoSuchMethodException {
|
||||
|
||||
Method[] methods = cls.getMethods();
|
||||
|
||||
Class<?>[] paramTypes = c.getParameterTypes();
|
||||
|
||||
Method[] readers = new Method[paramTypes.length];
|
||||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
Method getter = findGetter(paramTypes[i], methods);
|
||||
if (getter == null && paramTypes.length == 1 && paramTypes[i].equals(String.class)) {
|
||||
getter = findToString(cls);
|
||||
}
|
||||
if (getter == null) {
|
||||
throw new NoSuchMethodException("Get Method not found for " + paramTypes[i] + " in " + cls);
|
||||
}
|
||||
readers[i] = getter;
|
||||
}
|
||||
|
||||
return readers;
|
||||
}
|
||||
|
||||
private Method findToString(Class<?> cls) throws NoSuchMethodException {
|
||||
|
||||
try {
|
||||
return cls.getDeclaredMethod("toString", new Class<?>[0]);
|
||||
} catch (SecurityException e) {
|
||||
throw new NoSuchMethodException("SecurityException " + e + " trying to find toString method on " + cls);
|
||||
}
|
||||
}
|
||||
|
||||
private Method findGetter(Class<?> paramType, Method[] methods) {
|
||||
|
||||
for (Method method : methods) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
// could be a getter
|
||||
String methName = method.getName();
|
||||
if (!methName.equals("hashCode") && !methName.equals("toString")) {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (paramType.equals(returnType)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ScoreConstructor implements Comparable<ScoreConstructor> {
|
||||
|
||||
final int score;
|
||||
final Constructor<?> constructor;
|
||||
|
||||
private ScoreConstructor(int score, Constructor<?> constructor) {
|
||||
this.score = score;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// remove FindBugs warning
|
||||
return obj == this;
|
||||
}
|
||||
|
||||
public int compareTo(ScoreConstructor o) {
|
||||
return (score < o.score ? -1 : (score == o.score ? 0 : 1));
|
||||
}
|
||||
|
||||
public int getParamCount() {
|
||||
return constructor.getParameterTypes().length;
|
||||
}
|
||||
|
||||
public boolean hasDuplicateParamTypes() {
|
||||
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
if (parameterTypes.length < 2) {
|
||||
return false;
|
||||
}
|
||||
HashSet<Class<?>> set = new HashSet<>();
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
if (!set.add(parameterType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
public interface KnownImmutable {
|
||||
|
||||
boolean isKnownImmutable(Class<?> cls);
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class ReflectionBasedCompoundType implements CompoundType {
|
||||
|
||||
private final Constructor<?> constructor;
|
||||
|
||||
private final ReflectionBasedCompoundTypeProperty[] props;
|
||||
|
||||
public ReflectionBasedCompoundType(Constructor<?> constructor, ReflectionBasedCompoundTypeProperty[] props) {
|
||||
this.constructor = constructor;
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ReflectionBasedCompoundType " + constructor + " " + Arrays.toString(props);
|
||||
}
|
||||
|
||||
public Object create(Object[] propertyValues) {
|
||||
|
||||
try {
|
||||
return constructor.newInstance(propertyValues);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public CompoundTypeProperty[] getProperties() {
|
||||
return props;
|
||||
}
|
||||
|
||||
public Class<?> getPropertyType(int i) {
|
||||
return props[i].getPropertyType();
|
||||
}
|
||||
|
||||
public Class<?> getCompoundType() {
|
||||
return constructor.getDeclaringClass();
|
||||
}
|
||||
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class ReflectionBasedCompoundTypeProperty implements CompoundTypeProperty {
|
||||
|
||||
private static final Object[] NO_ARGS = new Object[0];
|
||||
|
||||
private final Method reader;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Class<?> propertyType;
|
||||
|
||||
public ReflectionBasedCompoundTypeProperty(String name, Method reader, Class<?> propertyType) {
|
||||
this.name = name;
|
||||
this.reader = reader;
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Object getValue(Object valueObject) {
|
||||
|
||||
try {
|
||||
return reader.invoke(valueObject, NO_ARGS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> getPropertyType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class ReflectionBasedScalarTypeConverter implements ScalarTypeConverter {
|
||||
|
||||
private static final Object[] NO_ARGS = new Object[0];
|
||||
|
||||
private final Constructor<?> constructor;
|
||||
|
||||
private final Method reader;
|
||||
|
||||
public ReflectionBasedScalarTypeConverter(Constructor<?> constructor, Method reader) {
|
||||
this.constructor = constructor;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public Object getNullValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object unwrapValue(Object beanType) {
|
||||
if (beanType == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return reader.invoke(beanType, NO_ARGS);
|
||||
} catch (Exception e) {
|
||||
String msg = "Error invoking read method " + reader.getName()
|
||||
+ " on " + beanType.getClass().getName();
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public Object wrapValue(Object scalarType) {
|
||||
try {
|
||||
return constructor.newInstance(scalarType);
|
||||
} catch (Exception e) {
|
||||
String msg = "Error invoking constructor " + constructor + " with " + scalarType;
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper;
|
||||
import com.avaje.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ReflectionBasedTypeBuilder {
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
public ReflectionBasedTypeBuilder(TypeManager typeManager) {
|
||||
this.typeManager = typeManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public ScalarType<?> buildScalarType(ImmutableMeta meta) {
|
||||
|
||||
if (meta.isCompoundType()) {
|
||||
throw new RuntimeException("Must be scalar");
|
||||
}
|
||||
|
||||
Constructor<?> constructor = meta.getConstructor();
|
||||
|
||||
Class<?> logicalType = constructor.getDeclaringClass();
|
||||
|
||||
Method[] readers = meta.getReaders();
|
||||
Class<?> returnType = readers[0].getReturnType();
|
||||
|
||||
ScalarType<?> scalarType = typeManager.recursiveCreateScalarTypes(returnType);
|
||||
|
||||
ReflectionBasedScalarTypeConverter r = new ReflectionBasedScalarTypeConverter(constructor, readers[0]);
|
||||
|
||||
return new ScalarTypeWrapper(logicalType, scalarType, r);
|
||||
}
|
||||
|
||||
public ReflectionBasedCompoundType buildCompound(ImmutableMeta meta) {
|
||||
|
||||
Constructor<?> constructor = meta.getConstructor();
|
||||
|
||||
Method[] readers = meta.getReaders();
|
||||
|
||||
ReflectionBasedCompoundTypeProperty[] props = new ReflectionBasedCompoundTypeProperty[readers.length];
|
||||
|
||||
|
||||
for (int i = 0; i < readers.length; i++) {
|
||||
Class<?> returnType = readers[i].getReturnType();
|
||||
|
||||
// ensure that return type is also a ScalarDataReader
|
||||
typeManager.recursiveCreateScalarDataReader(returnType);
|
||||
|
||||
String name = getPropertyName(readers[i]);
|
||||
|
||||
props[i] = new ReflectionBasedCompoundTypeProperty(name, readers[i], returnType);
|
||||
}
|
||||
|
||||
return new ReflectionBasedCompoundType(constructor, props);
|
||||
}
|
||||
|
||||
private String getPropertyName(Method method) {
|
||||
|
||||
String name = method.getName();
|
||||
if (name.startsWith("is")) {
|
||||
return lowerFirstChar(name.substring(2));
|
||||
} else if (name.startsWith("get")) {
|
||||
return lowerFirstChar(name.substring(3));
|
||||
}
|
||||
String msg = "Expecting method " + name + " to start with is or get "
|
||||
+ " so as to follow bean specification?";
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
private String lowerFirstChar(String name) {
|
||||
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.type.reflect;
|
||||
@@ -4,15 +4,11 @@ import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
import com.avaje.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.type.CtCompoundType;
|
||||
import com.avaje.ebeaninternal.server.type.DefaultTypeManager;
|
||||
import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarDataReader;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
import com.avaje.tests.model.ivo.CMoney;
|
||||
import com.avaje.tests.model.ivo.ExhangeCMoneyRate;
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
import com.avaje.tests.model.ivo.converter.MoneyTypeConverter;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -76,20 +72,7 @@ public class TestTypeManager extends BaseTestCase {
|
||||
|
||||
DefaultTypeManager typeManager = createTypeManager();
|
||||
|
||||
CheckImmutableResponse checkImmutable = typeManager.checkImmutable(Money.class);
|
||||
Assert.assertTrue(checkImmutable.isImmutable());
|
||||
|
||||
checkImmutable = typeManager.checkImmutable(CMoney.class);
|
||||
Assert.assertTrue(checkImmutable.isImmutable());
|
||||
|
||||
ScalarDataReader<?> dataReader = typeManager
|
||||
.recursiveCreateScalarDataReader(ExhangeCMoneyRate.class);
|
||||
Assert.assertTrue(dataReader instanceof CtCompoundType<?>);
|
||||
|
||||
dataReader = typeManager.recursiveCreateScalarDataReader(CMoney.class);
|
||||
Assert.assertTrue(dataReader instanceof CtCompoundType<?>);
|
||||
|
||||
ScalarType<?> scalarType = typeManager.recursiveCreateScalarTypes(Money.class);
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(Money.class);
|
||||
Assert.assertTrue(scalarType.getJdbcType() == Types.DECIMAL);
|
||||
Assert.assertTrue(!scalarType.isJdbcNative());
|
||||
Assert.assertEquals(Money.class, scalarType.getType());
|
||||
@@ -102,6 +85,7 @@ public class TestTypeManager extends BaseTestCase {
|
||||
serverConfig.setDatabasePlatform(new H2Platform());
|
||||
|
||||
BootupClasses bootupClasses = new BootupClasses();
|
||||
bootupClasses.getAttributeConverters().add(MoneyTypeConverter.class);
|
||||
|
||||
return new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.avaje.ebean.plugin.ExpressionPath;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.tests.model.ddd.DPerson;
|
||||
import com.avaje.tests.model.ivo.CMoney;
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -27,7 +26,6 @@ public class TestDPersonEl {
|
||||
p.setFirstName("first");
|
||||
p.setLastName("last");
|
||||
p.setSalary(new Money("12200"));
|
||||
p.setCmoney(new CMoney(new Money("12"), NZD));
|
||||
|
||||
Ebean.save(p);
|
||||
|
||||
@@ -42,26 +40,16 @@ public class TestDPersonEl {
|
||||
DPerson bean = jsonContext.toBean(DPerson.class, json);
|
||||
assertEquals("first", bean.getFirstName());
|
||||
assertEquals(new Money("12200"), bean.getSalary());
|
||||
assertEquals(new Money("12"), bean.getCmoney().getAmount());
|
||||
assertEquals(NZD, bean.getCmoney().getCurrency());
|
||||
|
||||
|
||||
EntityBean entityBean = (EntityBean) p;
|
||||
|
||||
ExpressionPath elCmoney = descriptor.getExpressionPath("cmoney");
|
||||
ExpressionPath elCmoneyAmt = descriptor.getExpressionPath("cmoney.amount");
|
||||
ExpressionPath elCmoneyCur = descriptor.getExpressionPath("cmoney.currency");
|
||||
ExpressionPath elSalary = descriptor.getExpressionPath("salary");
|
||||
|
||||
Object cmoney = elCmoney.pathGet(entityBean);
|
||||
Object amt = elCmoneyAmt.pathGet(entityBean);
|
||||
Object cur = elCmoneyCur.pathGet(entityBean);
|
||||
Object money = elSalary.pathGet(entityBean);
|
||||
|
||||
assertNotNull(cmoney);
|
||||
assertEquals(new Money("12"), amt);
|
||||
assertEquals(NZD, cur);
|
||||
|
||||
p.setCmoney(null);
|
||||
assertNull(p.getCmoney());
|
||||
assertNotNull(money);
|
||||
assertEquals(new Money("12200"), money);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.avaje.tests.model.ddd;
|
||||
|
||||
import com.avaje.tests.model.ivo.ExhangeCMoneyRate;
|
||||
import com.avaje.tests.model.ivo.Oid;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
@@ -14,7 +13,7 @@ public class DExhEntity {
|
||||
@Id
|
||||
Oid<DExhEntity> oid;
|
||||
|
||||
ExhangeCMoneyRate exhange;
|
||||
String exhange;
|
||||
|
||||
@Version
|
||||
Timestamp lastUpdated;
|
||||
@@ -27,11 +26,11 @@ public class DExhEntity {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public ExhangeCMoneyRate getExhange() {
|
||||
public String getExhange() {
|
||||
return exhange;
|
||||
}
|
||||
|
||||
public void setExhange(ExhangeCMoneyRate exhange) {
|
||||
public void setExhange(String exhange) {
|
||||
this.exhange = exhange;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.avaje.tests.model.ddd;
|
||||
|
||||
import com.avaje.tests.model.ivo.CMoney;
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
import com.avaje.tests.model.ivo.Oid;
|
||||
import org.joda.time.Interval;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
@@ -20,10 +18,6 @@ public class DPerson {
|
||||
|
||||
Money salary;
|
||||
|
||||
CMoney cmoney;
|
||||
|
||||
Interval interval;
|
||||
|
||||
public String toString() {
|
||||
return id + " " + firstName + " " + lastName + " " + salary;
|
||||
}
|
||||
@@ -60,20 +54,4 @@ public class DPerson {
|
||||
this.salary = salary;
|
||||
}
|
||||
|
||||
public CMoney getCmoney() {
|
||||
return cmoney;
|
||||
}
|
||||
|
||||
public void setCmoney(CMoney cmoney) {
|
||||
this.cmoney = cmoney;
|
||||
}
|
||||
|
||||
public Interval getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void setInterval(Interval interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
import java.util.Currency;
|
||||
|
||||
/**
|
||||
* Currency and Money.
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public class CMoney {
|
||||
|
||||
private final Money amount;
|
||||
private final Currency currency;
|
||||
|
||||
public CMoney(Money amount, Currency currency) {
|
||||
this.amount = amount;
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
public Money getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public Currency getCurrency() {
|
||||
return currency;
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ public class ESomeConvertType {
|
||||
|
||||
String name;
|
||||
|
||||
Rate rate;
|
||||
Money money;
|
||||
|
||||
public ESomeConvertType(String name, Rate rate) {
|
||||
public ESomeConvertType(String name, Money money) {
|
||||
this.name = name;
|
||||
this.rate = rate;
|
||||
this.money = money;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@@ -34,11 +34,11 @@ public class ESomeConvertType {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Rate getRate() {
|
||||
return rate;
|
||||
public Money getMoney() {
|
||||
return money;
|
||||
}
|
||||
|
||||
public void setRate(Rate rate) {
|
||||
this.rate = rate;
|
||||
public void setMoney(Money money) {
|
||||
this.money = money;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
/**
|
||||
* Test compound type with nested compound type.
|
||||
*/
|
||||
public class ExhangeCMoneyRate {
|
||||
|
||||
private final Rate rate;
|
||||
private final CMoney cmoney;
|
||||
|
||||
public ExhangeCMoneyRate(Rate rate, CMoney cmoney) {
|
||||
this.rate = rate;
|
||||
this.cmoney = cmoney;
|
||||
}
|
||||
|
||||
public Rate getRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
public CMoney getCmoney() {
|
||||
return cmoney;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ExhangeCMoneyRate) {
|
||||
ExhangeCMoneyRate e = (ExhangeCMoneyRate) obj;
|
||||
return e.rate.equals(rate) && e.cmoney.equals(cmoney);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hc = rate.hashCode();
|
||||
hc = hc * 31 + cmoney.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "rate=" + rate + " cmoney=" + cmoney;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
|
||||
/**
|
||||
* A representation of a Rate (like Tax or Discount) that is typically
|
||||
* used to multiple against an amount (like Money).
|
||||
* <p>
|
||||
* A Rate effectively wraps a BigDecimal with associated control over
|
||||
* precision and scale.
|
||||
* <p>
|
||||
* Rates are typically used to multiple or divide against an amount.
|
||||
* For example, you can't ADD a Rate to Money.
|
||||
* </p>
|
||||
*
|
||||
* @author rbygrave
|
||||
*/
|
||||
public final class Rate implements Comparable<Rate>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final Rate ZERO = new Rate(BigDecimal.ZERO);
|
||||
public static final Rate ONE = new Rate(BigDecimal.ONE);
|
||||
|
||||
private final BigDecimal value;
|
||||
|
||||
public Rate(BigDecimal value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("value can not be null");
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Rate(double val) {
|
||||
this(BigDecimal.valueOf(val));
|
||||
}
|
||||
|
||||
public Rate(String val) {
|
||||
this(new BigDecimal(val));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public int compareTo(Rate o) {
|
||||
return value.compareTo(o.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Rate) {
|
||||
// use BigDecimal.compareTo to handle scale differences
|
||||
return value.compareTo(((Rate) obj).getValue()) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public BigDecimal getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Rate subtract(Rate amt) {
|
||||
BigDecimal b = value.subtract(amt.value);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
public Rate subtract(Rate amt, MathContext ctx) {
|
||||
BigDecimal b = value.subtract(amt.value, ctx);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
public Rate add(Rate amt) {
|
||||
BigDecimal b = value.add(amt.value);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
public Rate add(Rate amt, MathContext ctx) {
|
||||
BigDecimal b = value.add(amt.value, ctx);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Rate times Money amount returns Money.
|
||||
*/
|
||||
public Money times(Money amount) {
|
||||
return multiply(amount);
|
||||
}
|
||||
|
||||
public Money multiply(Money amount) {
|
||||
BigDecimal b = value.multiply(amount.getAmount());
|
||||
return new Money(b);
|
||||
}
|
||||
|
||||
public Rate multiply(Rate m) {
|
||||
return multiply(m.value);
|
||||
}
|
||||
|
||||
public Rate multiply(int val) {
|
||||
return multiply(BigDecimal.valueOf(val));
|
||||
}
|
||||
|
||||
public Rate multiply(float val) {
|
||||
return multiply(BigDecimal.valueOf(val));
|
||||
}
|
||||
|
||||
public Rate multiply(double val) {
|
||||
return multiply(BigDecimal.valueOf(val));
|
||||
}
|
||||
|
||||
public Rate multiply(BigDecimal m) {
|
||||
BigDecimal b = value.multiply(m);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
public Rate divide(BigDecimal divisor) {
|
||||
BigDecimal b = value.divide(divisor);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
public Rate divide(BigDecimal divisor, MathContext ctx) {
|
||||
BigDecimal b = value.divide(divisor, ctx);
|
||||
return new Rate(b);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
import com.avaje.ebeaninternal.server.type.reflect.KnownImmutable;
|
||||
|
||||
public class SimpleKnownImmutable implements KnownImmutable {
|
||||
|
||||
public boolean isKnownImmutable(Class<?> cls) {
|
||||
|
||||
// Check for all allowed property types...
|
||||
if (cls.isPrimitive() || String.class.equals(cls) || Object.class.equals(cls)) {
|
||||
return true;
|
||||
}
|
||||
if (java.util.Date.class.equals(cls) || java.sql.Date.class.equals(cls) || java.sql.Timestamp.class.equals(cls)) {
|
||||
// treat as immutable even through they are not strictly so
|
||||
return true;
|
||||
}
|
||||
if (java.math.BigDecimal.class.equals(cls) || java.math.BigInteger.class.equals(cls)) {
|
||||
// treat as immutable (contain non-final fields)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Integer.class.equals(cls) || Long.class.equals(cls) || Double.class.equals(cls) || Float.class.equals(cls)
|
||||
|| Short.class.equals(cls) || Byte.class.equals(cls) || Character.class.equals(cls)
|
||||
|| Boolean.class.equals(cls)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.avaje.tests.model.ivo;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutable;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMeta;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.ImmutableMetaFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
|
||||
public class TestCheckImmutable extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
SimpleKnownImmutable known = new SimpleKnownImmutable();
|
||||
|
||||
CheckImmutable check = new CheckImmutable(known);
|
||||
|
||||
CheckImmutableResponse response = check.checkImmutable(Money.class);
|
||||
|
||||
Assert.assertTrue(response.isImmutable());
|
||||
Assert.assertTrue(!response.isCompoundType());
|
||||
}
|
||||
|
||||
public void testMeta() {
|
||||
|
||||
ImmutableMetaFactory factory = new ImmutableMetaFactory();
|
||||
|
||||
ImmutableMeta meta = factory.createImmutableMeta(Money.class);
|
||||
|
||||
Assert.assertNotNull(meta);
|
||||
Constructor<?> c = meta.getConstructor();
|
||||
Class<?>[] parameterTypes = c.getParameterTypes();
|
||||
|
||||
Method[] readers = meta.getReaders();
|
||||
Assert.assertTrue(parameterTypes.length == 1);
|
||||
Assert.assertTrue(readers.length == 1);
|
||||
|
||||
Assert.assertEquals("getAmount", readers[0].getName());
|
||||
Assert.assertEquals(BigDecimal.class, parameterTypes[0]);
|
||||
|
||||
meta = factory.createImmutableMeta(CMoney.class);
|
||||
|
||||
Assert.assertNotNull(meta);
|
||||
c = meta.getConstructor();
|
||||
parameterTypes = c.getParameterTypes();
|
||||
readers = meta.getReaders();
|
||||
|
||||
Assert.assertTrue(parameterTypes.length == 2);
|
||||
Assert.assertTrue(readers.length == 2);
|
||||
|
||||
Assert.assertEquals(Money.class, parameterTypes[0]);
|
||||
Assert.assertEquals("getAmount", readers[0].getName());
|
||||
Assert.assertEquals(Currency.class, parameterTypes[1]);
|
||||
Assert.assertEquals("getCurrency", readers[1].getName());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.tests.model.ivo.CMoney;
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
|
||||
import java.util.Currency;
|
||||
|
||||
public class CMoneyCompoundType {//implements CompoundType<CMoney> {
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
private CompoundTypeProperty[] props = {new PropertyMoney(), new PropertyCurrency()};
|
||||
|
||||
public CMoney create(Object[] v) {
|
||||
return new CMoney((Money) v[0], (Currency) v[1]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public CompoundTypeProperty<CMoney, ?>[] getProperties() {
|
||||
return props;
|
||||
}
|
||||
|
||||
static class PropertyMoney implements CompoundTypeProperty<CMoney, Money> {
|
||||
|
||||
public String getName() {
|
||||
return "amount";
|
||||
}
|
||||
|
||||
public Money getValue(CMoney valueObject) {
|
||||
return valueObject.getAmount();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class PropertyCurrency implements CompoundTypeProperty<CMoney, Currency> {
|
||||
|
||||
public String getName() {
|
||||
return "currency";
|
||||
}
|
||||
|
||||
public Currency getValue(CMoney valueObject) {
|
||||
return valueObject.getCurrency();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import com.avaje.tests.model.ivo.CMoney;
|
||||
import com.avaje.tests.model.ivo.ExhangeCMoneyRate;
|
||||
import com.avaje.tests.model.ivo.Rate;
|
||||
|
||||
public class ExhangeCompoundType implements CompoundType<ExhangeCMoneyRate> {
|
||||
|
||||
public ExhangeCMoneyRate create(Object[] propertyValues) {
|
||||
return new ExhangeCMoneyRate((Rate) propertyValues[0], (CMoney) propertyValues[1]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public CompoundTypeProperty<ExhangeCMoneyRate, ?>[] getProperties() {
|
||||
return new CompoundTypeProperty[]{new RateProp(), new CMoneyProp()};
|
||||
}
|
||||
|
||||
static class RateProp implements CompoundTypeProperty<ExhangeCMoneyRate, Rate> {
|
||||
|
||||
public String getName() {
|
||||
return "rate";
|
||||
}
|
||||
|
||||
public Rate getValue(ExhangeCMoneyRate valueObject) {
|
||||
return valueObject.getRate();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CMoneyProp implements CompoundTypeProperty<ExhangeCMoneyRate, CMoney> {
|
||||
|
||||
public String getName() {
|
||||
return "cmoney";
|
||||
}
|
||||
|
||||
public CMoney getValue(ExhangeCMoneyRate valueObject) {
|
||||
return valueObject.getCmoney();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.ebean.config.CompoundType;
|
||||
import com.avaje.ebean.config.CompoundTypeProperty;
|
||||
import org.joda.time.Interval;
|
||||
|
||||
public class JodaIntervalCompoundType implements CompoundType<Interval> {
|
||||
|
||||
public Interval create(Object[] propertyValues) {
|
||||
return new Interval((Long) propertyValues[0], (Long) propertyValues[1]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public CompoundTypeProperty<Interval, ?>[] getProperties() {
|
||||
return new CompoundTypeProperty[]{new Start(), new End()};
|
||||
}
|
||||
|
||||
static class Start implements CompoundTypeProperty<Interval, Long> {
|
||||
|
||||
public String getName() {
|
||||
return "startMillis";
|
||||
}
|
||||
|
||||
public Long getValue(Interval valueObject) {
|
||||
return valueObject.getStartMillis();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return java.sql.Types.TIMESTAMP;
|
||||
}
|
||||
}
|
||||
|
||||
static class End implements CompoundTypeProperty<Interval, Long> {
|
||||
|
||||
public String getName() {
|
||||
return "endMillis";
|
||||
}
|
||||
|
||||
public Long getValue(Interval valueObject) {
|
||||
return valueObject.getEndMillis();
|
||||
}
|
||||
|
||||
public int getDbType() {
|
||||
return java.sql.Types.TIMESTAMP;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,18 +2,19 @@ package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Converts between Money and BigDecimal.
|
||||
*/
|
||||
public class MoneyTypeConverter {//implements ScalarTypeConverter<Money,BigDecimal>{
|
||||
public class MoneyTypeConverter implements AttributeConverter<Money,BigDecimal> {
|
||||
|
||||
public BigDecimal unwrapValue(Money beanType) {
|
||||
public BigDecimal convertToDatabaseColumn(Money beanType) {
|
||||
return beanType.getAmount();
|
||||
}
|
||||
|
||||
public Money wrapValue(BigDecimal scalarType) {
|
||||
public Money convertToEntityAttribute(BigDecimal scalarType) {
|
||||
return new Money(scalarType);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.ebean.config.ScalarTypeConverter;
|
||||
import com.avaje.tests.model.ivo.Oid;
|
||||
|
||||
public class OidTypeConverter {//implements ScalarTypeConverter<Oid<?>,Long> {
|
||||
public class OidTypeConverter implements ScalarTypeConverter<Oid<?>,Long> {
|
||||
|
||||
@Override
|
||||
public Oid<?> getNullValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Oid<?> wrapValue(Long scalarType) {
|
||||
if (scalarType == null) {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.avaje.tests.model.ivo.converter;
|
||||
|
||||
import com.avaje.tests.model.ivo.Rate;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Converts between Rate and BigDecimal.
|
||||
*/
|
||||
@Converter
|
||||
public class RateTypeConverter implements AttributeConverter<Rate,BigDecimal> {
|
||||
|
||||
@Override
|
||||
public BigDecimal convertToDatabaseColumn(Rate attribute) {
|
||||
return attribute.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rate convertToEntityAttribute(BigDecimal dbData) {
|
||||
return new Rate(dbData);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.avaje.tests.model.ivo.test;
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.ivo.ESomeConvertType;
|
||||
import com.avaje.tests.model.ivo.Rate;
|
||||
import com.avaje.tests.model.ivo.Money;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestAttributeConverter extends BaseTestCase {
|
||||
@@ -11,12 +11,12 @@ public class TestAttributeConverter extends BaseTestCase {
|
||||
@Test
|
||||
public void insertUpdateDelete() {
|
||||
|
||||
ESomeConvertType bean = new ESomeConvertType("one", new Rate(20.1));
|
||||
ESomeConvertType bean = new ESomeConvertType("one", new Money(20.1));
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
ESomeConvertType found = Ebean.find(ESomeConvertType.class, bean.getId());
|
||||
found.setRate(new Rate("40"));
|
||||
found.setMoney(new Money("40"));
|
||||
Ebean.save(found);
|
||||
|
||||
Ebean.delete(found);
|
||||
|
||||
@@ -4,7 +4,6 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.time.MonthDay;
|
||||
import java.time.Period;
|
||||
|
||||
@Entity
|
||||
public class SomePeriodBean {
|
||||
@@ -15,8 +14,6 @@ public class SomePeriodBean {
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
Period period;
|
||||
|
||||
MonthDay anniversary;
|
||||
|
||||
public Long getId() {
|
||||
@@ -35,14 +32,6 @@ public class SomePeriodBean {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Period getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public void setPeriod(Period period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public MonthDay getAnniversary() {
|
||||
return anniversary;
|
||||
}
|
||||
|
||||
@@ -8,26 +8,21 @@ import org.junit.Test;
|
||||
import java.sql.Date;
|
||||
import java.time.LocalDate;
|
||||
import java.time.MonthDay;
|
||||
import java.time.Period;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TestPeriodType extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testInsert() {
|
||||
|
||||
SomePeriodBean bean = new SomePeriodBean();
|
||||
bean.setPeriod(Period.of(3, 4, 5));
|
||||
bean.setAnniversary(MonthDay.of(4, 29));
|
||||
Ebean.save(bean);
|
||||
|
||||
SomePeriodBean bean1 = Ebean.find(SomePeriodBean.class, bean.getId());
|
||||
assertEquals(bean.getPeriod(), bean1.getPeriod());
|
||||
assertEquals(bean.getAnniversary(), bean1.getAnniversary());
|
||||
|
||||
// insert fetch null value
|
||||
@@ -35,14 +30,11 @@ public class TestPeriodType extends BaseTestCase {
|
||||
Ebean.save(bean2);
|
||||
|
||||
SomePeriodBean bean3 = Ebean.find(SomePeriodBean.class, bean2.getId());
|
||||
assertNull(bean3.getPeriod());
|
||||
assertNull(bean3.getAnniversary());
|
||||
|
||||
List<SomePeriodBean> anniversaryList = Ebean.find(SomePeriodBean.class)
|
||||
.where()
|
||||
.eq("anniversary", MonthDay.of(4, 29))
|
||||
.eq("period_years", 3)
|
||||
.eq("period_months", 4)
|
||||
.findList();
|
||||
|
||||
assertEquals(1, anniversaryList.size());
|
||||
|
||||
Reference in New Issue
Block a user