All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nz.co.gregs.dbvolution.internal.properties.PropertyWrapper Maven / Gradle / Ivy

package nz.co.gregs.dbvolution.internal.properties;

import java.util.List;
import nz.co.gregs.dbvolution.DBRow;
import nz.co.gregs.dbvolution.annotations.AutoFillDuringQueryIfPossible;
import nz.co.gregs.dbvolution.annotations.DBForeignKey;
import nz.co.gregs.dbvolution.databases.definitions.DBDefinition;
import nz.co.gregs.dbvolution.datatypes.DBEnumValue;
import nz.co.gregs.dbvolution.datatypes.DBNumberStatistics;
import nz.co.gregs.dbvolution.datatypes.QueryableDatatype;
import nz.co.gregs.dbvolution.exceptions.DBThrownByEndUserCodeException;
import nz.co.gregs.dbvolution.expressions.BooleanExpression;
import nz.co.gregs.dbvolution.expressions.DBExpression;
import nz.co.gregs.dbvolution.expressions.NumberExpression;
import nz.co.gregs.dbvolution.expressions.StringExpression;
import nz.co.gregs.dbvolution.query.RowDefinition;

/**
 * Abstracts a java field or bean-property on a target object as a
 * DBvolution-centric property, which contains values from a specific column in
 * a database table. Transparently handles all annotations associated with the
 * property, including type adaption.
 *
 * 

* Provides access to the meta-data defined on a single java property of a * class, and provides methods for reading and writing the value of the property * on a single bound object, given a specified database definition. * *

* DB properties can be seen to have the types and values in the table that * follows. This class provides a virtual view over the property whereby the * DBv-centric type and value are easily accessible via the * {@link #getQueryableDatatype()} and * {@link #setQueryableDatatype(QueryableDatatype) } methods. *

    *
  • rawType/rawValue - the type and value actually stored on the declared * java property *
  • dbvType/dbvValue - the type and value used within DBv (a * QueryableDataType) *
  • databaseType/databaseValue - the type and value of the database column * itself (this class doesn't deal with these) *
* *

* Note: instances of this class are cheap to create and do not need to be * cached. * *

* This class is thread-safe. * *

* This class is not serializable. References to it within serializable classes * should be marked as {@code transient}. */ public class PropertyWrapper { private final RowDefinitionInstanceWrapper dbRowInstanceWrapper; private final PropertyWrapperDefinition propertyDefinition; private final RowDefinition target; /** * @param instanceWrapper instanceWrapper * @param classProperty the class-level wrapper * @param target the target object containing the given property */ public PropertyWrapper(RowDefinitionInstanceWrapper instanceWrapper, PropertyWrapperDefinition classProperty, RowDefinition target) { this.dbRowInstanceWrapper = instanceWrapper; this.propertyDefinition = classProperty; this.target = target; } /** * Gets a string representation of the wrapped property, suitable for * debugging and logging. * *

* For example:
* {@code "DBInteger nz.co.mycompany.myproject.Vehicle.fkSpecOptionColour = [15241672]"} * *

Support DBvolution at * Patreon

* * @return a String representing this PropertyWrapper */ @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(type().getSimpleName()); buf.append(" "); buf.append(qualifiedJavaName()); if (!javaName().equalsIgnoreCase(columnName())) { buf.append("<").append(columnName()).append(">"); } if (isReadable()) { buf.append(" = ["); try { buf.append(getQueryableDatatype()); } catch (Exception e) { buf.append(""); } buf.append("]"); } if (isTypeAdapted()) { buf.append(" ("); buf.append(getRawJavaType().getSimpleName()); if (isReadable()) { buf.append(" = ["); try { buf.append(rawJavaValue()); } catch (Exception e) { buf.append(""); } buf.append("]"); } buf.append(")"); } return buf.toString(); } /** * Generates a hash-code of this property wrapper definition, based on the * java property it wraps and the referenced target object. * *

Support DBvolution at * Patreon

* * @return a hash code for this instance */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((propertyDefinition == null) ? 0 : propertyDefinition.hashCode()); result = prime * result + ((target == null) ? 0 : target.hashCode()); return result; } /** * Equality of this property wrapper definition, based on the java property it * wraps in a specific class, plus the underlying object reference containing * the wrapped property. * *

* Two instances are identical if they wrap the same java property (field or * bean-property) in the same object instance (by object reference, rather * than {@code .equals()} equality). * * @param obj obj *

Support DBvolution at * Patreon

* @return TRUE if this PropertyWrapper wraps the same property on the same * RowDefinition as the object supplied, FALSE otherwise */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof PropertyWrapper)) { return false; } PropertyWrapper other = (PropertyWrapper) obj; if (propertyDefinition == null) { if (other.propertyDefinition != null) { return false; } } else if (!propertyDefinition.equals(other.propertyDefinition)) { return false; } return target == other.target; } /** * Gets the name of the java property, without the containing class name. * Mainly used within error messages. eg: {@code "uid"} * *

* Use {@link #columnName()} to determine column name. * *

Support DBvolution at * Patreon

* * @return a String of the declared field name of this property */ public String javaName() { return propertyDefinition.javaName(); } /** * Gets the partially qualified name of the underlying java property, using * the short-name of the containing class. Mainly used within logging and * error messages. eg: {@code "Customer.uid"} * *

* Use {@link #columnName()} to determine column name. * *

Support DBvolution at * Patreon

* * @return a convenient String including the class name of the RowDefinition * and the field name for this property */ public String shortQualifiedJavaName() { return propertyDefinition.shortQualifiedJavaName(); } /** * Gets the fully qualified name of the underlying java property, including * the fully qualified name of the containing class. Mainly used within * logging and error messages. eg: * {@code "nz.co.mycompany.myproject.Customer.uid"} * *

* Use {@link #columnName()} to determine column name. * *

Support DBvolution at * Patreon

* * @return the String of the full class name of the containing RowDefinition. */ public String qualifiedJavaName() { return propertyDefinition.qualifiedJavaName(); } /** * Gets the DBvolution-centric type of the property. If a type adaptor is * present, then this is the type after conversion from the target object's * actual property type. * *

* Use {@link #getRawJavaType()} in the rare case that you need to know the * underlying java property type. * *

Support DBvolution at * Patreon

* * @return the Class of the QDT used internally to handle database values. */ @SuppressWarnings("unchecked") public Class> type() { return propertyDefinition.type(); } /** * Convenience method for testing the type of the QueryableDatatype. * Equivalent to {@code refType.isAssignableFrom(this.type())}. * * @param refType refType *

Support DBvolution at * Patreon

* @return TRUE if this property's internal QueryableDatatype is the similar * to that of the supplied instance. */ public boolean isInstanceOf(Class> refType) { return propertyDefinition.isInstanceOf(refType); } /** * Convenience method for testing the type of the QueryableDatatype. * Equivalent to {@code refType.isAssignableFrom(this.type())}. * *

Support DBvolution at * Patreon

* * @return TRUE if this property's internal QueryableDatatype is the similar * to that of the supplied instance. */ public boolean isInstanceOfLargeObject() { return propertyDefinition.isInstanceOfLargeObject(); } /** * Gets the annotated table name of the table this property belongs to. * Equivalent to calling * {@code getRowDefinitionInstanceWrapper().tableName()}. * *

Support DBvolution at * Patreon

* * @return a String of the table name for this property */ public String tableName() { return propertyDefinition.tableName(); } /** * Gets the annotated column name. Applies defaulting if the {@code DBColumn} * annotation is present but does not explicitly specify the column name. * *

* If the {@code DBColumn} annotation is missing, this method returns * {@code null}. * *

Support DBvolution at * Patreon

* * @return the column name, if specified explicitly or implicitly */ public String columnName() { return propertyDefinition.getColumnName(); } /** * Indicates whether this property is a column. * *

Support DBvolution at * Patreon

* * @return {@code true} if this property is a column */ public boolean isColumn() { return propertyDefinition.isColumn(); } /** * Indicates whether this property is a primary key. * *

Support DBvolution at * Patreon

* * @return {@code true} if this property is a primary key */ public boolean isPrimaryKey() { return propertyDefinition.isPrimaryKey(); } /** * Indicates whether this property is a foreign key. * *

Support DBvolution at * Patreon

* * @return {@code true} if this property is a foreign key */ public boolean isForeignKey() { return propertyDefinition.isForeignKey(); } /** * Gets the class referenced by this property, if this property is a foreign * key. * *

Support DBvolution at * Patreon

* * @return the referenced class if this property is a foreign key; null if not * a foreign key */ public Class referencedClass() { return propertyDefinition.referencedClass(); } /** * Gets the table referenced by this property, if this property is a foreign * key. * *

Support DBvolution at * Patreon

* * @return the referenced table name if this property is a foreign key; null * if not a foreign key */ public String referencedTableName() { return propertyDefinition.referencedTableName(); } /** * Gets the column name in the foreign table referenced by this property. The * referenced column is either explicitly indicated by use of the * {@link DBForeignKey#column()} attribute, or it is implicitly the single * primary key of the referenced table if the {@link DBForeignKey#column()} * attribute is unset. * *

Support DBvolution at * Patreon

* * @return the non-null referenced column name if this property is a foreign * key; null if not a foreign key */ public String referencedColumnName() { return propertyDefinition.referencedColumnName(); } /** * Gets information for the referenced property in the referenced table. The * referenced property is either explicitly indicated by use of the * {@link DBForeignKey#column()} attribute, or it is implicitly the single * primary key of the referenced table. * *

* Note that the property definition returned provides identity of the * property only. It provides access to the property's: java name, column * name, type, and identity information about the table it belongs to (ie: * table name). Attempts to get or set its value or get the type adaptor * instance will result in an internal exception. * *

Support DBvolution at * Patreon

* * @return the referenced property if this property is a foreign key; null if * not a foreign key */ public PropertyWrapperDefinition referencedPropertyDefinitionIdentity() { return propertyDefinition.referencedPropertyDefinitionIdentity(); } /** * Gets the enum type, or null if not appropriate * *

Support DBvolution at * Patreon

* * @return the enum type, which may also implement {@link DBEnumValue} */ public Class> getEnumType() { return propertyDefinition.getEnumType(); } /** * Gets the type of the code supplied by enum values. This is derived from the * {@link DBEnumValue} implementation in the enum. * *

Support DBvolution at * Patreon

* * @return null if not known or not appropriate */ public Class getEnumCodeType() { return propertyDefinition.getEnumCodeType(); } /** * Indicates whether the value of the property can be retrieved. Bean * properties which are missing a 'getter' can not be read, but may be able to * be set. * *

Support DBvolution at * Patreon

* * @return TRUE if this property is readable, FALSE otherwise. */ public boolean isReadable() { return propertyDefinition.isReadable(); } /** * Indicates whether the value of the property can be modified. Bean * properties which are missing a 'setter' can not be written to, but may be * able to be read. * *

Support DBvolution at * Patreon

* * @return TRUE if the property can set, FALSE otherwise. */ public boolean isWritable() { return propertyDefinition.isWritable(); } /** * Indicates whether the property's type is adapted by an explicit or implicit * type adaptor. (Note: at present there is no support for implicit type * adaptors) * *

Support DBvolution at * Patreon

* * @return {@code true} if a type adaptor is being used */ public boolean isTypeAdapted() { return propertyDefinition.isTypeAdapted(); } /** * Gets the DBvolution-centric value of the property. The value returned may * have undergone type conversion from the target object's actual property * type, if a type adaptor is present. * *

* Use {@link #isReadable()} beforehand to check whether the property can be * read. * * @param the QDT type *

Support DBvolution at * Patreon

* @return The queryableDatatype instance representing this property * @throws IllegalStateException if not readable (you should have called * isReadable() first) * @throws DBThrownByEndUserCodeException if any user code throws an exception */ @SuppressWarnings("unchecked") public > A getQueryableDatatype() { return (A) propertyDefinition.getQueryableDatatype(target); } /** * Sets the DBvolution-centric value of the property. The value set may have * undergone type conversion to the target object's actual property type, if a * type adaptor is present. * *

* Use {@link #isWritable()} beforehand to check whether the property can be * modified. * * @param value value * @throws IllegalStateException if not writable (you should have called * isWritable() first) * @throws DBThrownByEndUserCodeException if any user code throws an exception */ public void setQueryableDatatype(QueryableDatatype value) { propertyDefinition.setQueryableDatatype(target, value); } /** * Gets the value of the declared property in the end-user's target object, * prior to type conversion to the DBvolution-centric type. * *

* In most cases you will not need to call this method, as type conversion is * done transparently via the {@link #getQueryableDatatype()} and * {@link #setQueryableDatatype(QueryableDatatype)} methods. * *

* Use {@link #isReadable()} beforehand to check whether the property can be * read. * *

Support DBvolution at * Patreon

* * @return value * @throws IllegalStateException if not readable (you should have called * isReadable() first) * @throws DBThrownByEndUserCodeException if any user code throws an exception */ public Object rawJavaValue() { return propertyDefinition.rawJavaValue(target); } /** * Set the value of the declared property in the end-user's target object, * without type conversion to/from the DBvolution-centric type. * *

* In most cases you will not need to call this method, as type conversion is * done transparently via the {@link #getQueryableDatatype()} and * {@link #setQueryableDatatype(QueryableDatatype)} methods. * *

* Use {@link #isWritable()} beforehand to check whether the property can be * modified. * * @param value new value * @throws IllegalStateException if not writable (you should have called * isWritable() first) * @throws DBThrownByEndUserCodeException if any user code throws an exception */ public void setRawJavaValue(Object value) { propertyDefinition.setRawJavaValue(target, value); } /** * Gets the declared type of the property in the end-user's target object, * prior to type conversion to the DBvolution-centric type. * *

* In most cases you will not need to call this method, as type conversion is * done transparently via the {@link #getQueryableDatatype()} and * {@link #setQueryableDatatype(QueryableDatatype)} methods. Use the * {@link #type()} method to get the DBv-centric property type, after type * conversion. * *

Support DBvolution at * Patreon

* * @return the declared Java class of the property. */ public Class getRawJavaType() { return propertyDefinition.getRawJavaType(); } /** * Gets the definition of the property, independent of any DBRow instance. * *

Support DBvolution at * Patreon

* * @return the propertyDefinition */ public PropertyWrapperDefinition getPropertyWrapperDefinition() { return propertyDefinition; } /** * Gets the wrapper for the DBRow instance containing this property. * *

Support DBvolution at * Patreon

* * @return the RowDefinitionInstanceWrapper for this property. */ public RowDefinitionInstanceWrapper getRowDefinitionInstanceWrapper() { return dbRowInstanceWrapper; } /** * Indicates whether this property has a ColumnExpression. * *

* Column expressions are derived values created with * {@link DBExpression DBexpressions} like * {@link StringExpression}, {@link NumberExpression}, and * {@link BooleanExpression}. The often involve database columns using * {@link DBRow#column(java.lang.Boolean) the DBRow column methods} or literal * values using the, for instance, the StringExpression * {@link StringExpression#value(java.lang.String) value method}. * *

Support DBvolution at * Patreon

* * @return TRUE if this property uses an expression to generate a value, * otherwise FALSE. */ public boolean hasColumnExpression() { return this.getQueryableDatatype().hasColumnExpression(); } /** * Returns the Column Expression, if any, defined on this property. * *

Support DBvolution at * Patreon

* * @return the expression used by this property to generate values, or null. * */ public DBExpression[] getColumnExpression() throws ClassCastException { return this.getQueryableDatatype().getColumnExpression(); } /** * The names and aliases of this property as it will appear in a SELECT and * WHERE clauses. * *

* Multiple names and aliases are supported for * {@link QueryableDatatype QueryableDatatypes} like * {@link DBNumberStatistics} which retrieve several expressions at once.

* * @param db db *

Support DBvolution at * Patreon

* @return A map of all the selectable name and column aliases for this * property. */ public List getColumnAspects(DBDefinition db) { final RowDefinition adapteeRowProvider = this.getRowDefinitionInstanceWrapper().adapteeRowDefinition(); return getPropertyWrapperDefinition().getColumnAspects(db, adapteeRowProvider); } /** * The name of this property as it will appear in a SELECT and WHERE clauses. * * @param db db *

Support DBvolution at * Patreon

* @return A String of the property for use in SELECT and WHERE clauses. */ // private String[] getSelectableName(DBDatabase db) { // final RowDefinition adapteeRowProvider = this.getRowDefinitionInstanceWrapper().adapteeRowDefinition(); // return getPropertyWrapperDefinition().getSelectableName(db, adapteeRowProvider); // } /** * The alias to the column for use in the select clause and during value * retrieval * * @param defn db *

Support DBvolution at * Patreon

* @return the column alias for this property. */ public String[] getColumnAlias(DBDefinition defn) { final RowDefinition actualRow = this.getRowDefinitionInstanceWrapper().adapteeRowDefinition(); return propertyDefinition.getColumnAlias(defn, actualRow); } /** * Returns TRUE if the property wrapped is a foreign key reference to the * table supplied * * @param table table *

Support DBvolution at * Patreon

* @return TRUE if the property wrapped is a foreign key reference to the * table supplied, otherwise FALSE. */ public boolean isForeignKeyTo(DBRow table) { return propertyDefinition.isForeignKeyTo(table); } /** * Returns true if the property wrapped is an auto-incrementing column. * *

* This generally means that the column is a primary key. and definitely means * you do not need to set the value of the column. * *

Support DBvolution at * Patreon

* * @return TRUE if the value of this column is provided by the database's * auto-increment functionality, otherwise FALSE. */ public boolean isAutoIncrement() { return propertyDefinition.isAutoIncrementColumn(); } /** * Returns true if the property wrapped is a Spatial2D column. * *

* This generally means that the column is a polygon, line, point, or other 2D * geometry. * *

* Spatial columns are special in that some databases need indexes to support * them properly. * *

Support DBvolution at * Patreon

* * @return TRUE if the value of this column is a 2D geometry type, otherwise * FALSE. */ public boolean isSpatial2DType() { return propertyDefinition.isSpatial2DType(); } /** * Returns true if the property wrapped is an auto-filling field. * *

Support DBvolution at * Patreon

* * @return TRUE if the value of this column is to be automatically filled * during queries if possible, otherwise FALSE. */ public boolean isAutoFilling() { return propertyDefinition.isAutoFilling(); } /** * Returns the class provided to the {@link AutoFillDuringQueryIfPossible} * annotation. * *

Support DBvolution at * Patreon

* * @return the class that should be auto-filled if it is present in the query. */ public Class getAutoFillingClass() { return propertyDefinition.getAutoFillingClass(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy