DBStringEnum.java
/*
* Copyright 2014 Gregory Graham.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nz.co.gregs.dbvolution.datatypes;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import nz.co.gregs.dbvolution.DBRow;
import nz.co.gregs.dbvolution.columns.StringColumn;
import nz.co.gregs.dbvolution.databases.definitions.DBDefinition;
import nz.co.gregs.dbvolution.exceptions.IncorrectRowProviderInstanceSuppliedException;
import nz.co.gregs.dbvolution.expressions.StringExpression;
import nz.co.gregs.dbvolution.operators.*;
import nz.co.gregs.dbvolution.query.RowDefinition;
import nz.co.gregs.dbvolution.utility.comparators.ComparableComparator;
/**
* Like {@link DBString} except that the database value can be easily
* interpreted as an enumeration with integer codes.
*
* DBStringEnum maps string values automatically from the database value to the
* enumeration value via the {@link DBEnumValue} interface.
*
* <p>
* Internally stores only the database-centric literal value in its type.
* Conversion to the enumeration type is done lazily so that it's possible to
* handle the case where a database has an invalid value or a new value that
* isn't in the enumeration.
*
* <p>
* Normally declared as something like:</p>
* <pre>
* {@literal @}DBColumn
* DBStringEnum state = new DBStringEnum<MyEnumValue>();
*
*
* public static enum MyEnumValue implements DBEnumValue<String> {
*
* STATE_ONE(1, "One"),
* STATE_TWO(2, "Two"),
* STATE_THREE(3, "Three");
* private final String literalValue;
* private final String displayName;
*
* private GenericEnumType(String code, String displayName) {
* this.literalValue = code;
* this.displayName = displayName;
* }
*
* public String getCode() {
* return literalValue;
* }
*
* public String getDisplayName() {
* return displayName;
* }
* }
* </pre>
*
*
*
* @param <E> an enumeration class that implements the {@link DBEnumValue}
* interface for String values.
*/
public class DBStringEnum<E extends Enum<E> & DBEnumValue<String>> extends DBEnum<E, String> {
private static final long serialVersionUID = 1L;
/**
* The default constructor for DBStringEnum.
*
* <p>
* Creates an unset undefined DBStringEnum object.
*
*/
public DBStringEnum() {
}
/**
* Creates a DBStringEnum with the value provided.
*
* @param value value
*/
public DBStringEnum(String value) {
super(value);
}
/**
* Creates a DBStringEnum column expression based on the
* {@link StringExpression} provided.
*
* <p>
* This is used to generate a DBStringEnum result from data in the database.at
* runtime. Use a StringExpression to create the data.
*
* <p>
* DBvolution can not ensure that the results of your expression will conform
* to the particular DBStringEnum values, so construct your expression
* carefully.
*
* <p>
* This can also be used to transform an existing column into a DBStringEnum,
* the above warning still applies.
*
* @param stringExpression stringExpression
*/
public DBStringEnum(StringExpression stringExpression) {
super(stringExpression);
}
/**
* Creates a DBStringEnum with the value set to the value of the supplied Enum
* object..
*
* @param value value
*/
public DBStringEnum(E value) {
super(value);
}
@Override
protected void validateLiteralValue(E enumValue) {
Object localValue = enumValue.getCode();
if (localValue != null) {
if (!(localValue instanceof String)) {
String enumMethodRef = enumValue.getClass().getName() + "." + enumValue.name() + ".getLiteralValue()";
String literalValueTypeRef = localValue.getClass().getName();
throw new IncompatibleClassChangeError("Enum literal type is not valid: "
+ enumMethodRef + " returned a " + literalValueTypeRef + ", which is not valid for a " + this.getClass().getSimpleName());
}
}
}
@Override
public String getSQLDatatype() {
return new DBString().getSQLDatatype();
}
@Override
public DBString getQueryableDatatypeForExpressionValue() {
return new DBString();
}
@Override
public boolean isAggregator() {
return false;
}
@Override
public Set<DBRow> getTablesInvolved() {
return new HashSet<>();
}
/**
* Create an array of Strings containing the literal values of the provided
* Enums.
*
* <p>
* Provided as a convenience function
*
*
* <p style="color: #F90;">Support DBvolution at
* <a href="http://patreon.com/dbvolution" target=new>Patreon</a></p>
*
* @return a String[] of the enums values.
*/
@SafeVarargs
private final String[] convertToLiteralString(E... enumValues) {
String[] result = new String[enumValues.length];
for (int i = 0; i < enumValues.length; i++) {
E enumValue = enumValues[i];
result[i] = convertToLiteralString(enumValue);
}
return result;
}
/**
* Convert the enum collection to an array of String literal values.
*
*/
private String[] convertToLiteralString(Collection<E> enumValues) {
ArrayList<String> result = new ArrayList<>();
for (E e : enumValues) {
result.add(convertToLiteralString(e));
}
return result.toArray(new String[]{});
}
/**
* Convert the enum to its String literal value.
*
*
* <p style="color: #F90;">Support DBvolution at
* <a href="http://patreon.com/dbvolution" target=new>Patreon</a></p>
*
* @return the literal value of the enum.
*/
private String convertToLiteralString(E enumValue) {
if (enumValue == null || enumValue.getCode() == null) {
return null;
} else {
validateLiteralValue(enumValue);
String newLiteralValue = enumValue.getCode();
return newLiteralValue;
}
}
// /**
// * Reduces the rows returned from a query to only those matching the provided
// * objects.
// *
// * @param permitted permitted
// */
// public void permittedValues(String... permitted) {
// this.setOperator(new DBPermittedValuesOperator<String>(permitted));
// }
/**
* Reduces the rows returned from a query to only those matching the provided
* objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param permitted permitted
*/
public void permittedValuesIgnoreCase(String... permitted) {
this.setOperator(new DBPermittedValuesIgnoreCaseOperator(permitted));
}
/**
* Reduces the rows returned from a query to only those matching the provided
* expression.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param permitted permitted
*/
public void permittedValuesIgnoreCase(StringExpression... permitted) {
this.setOperator(new DBPermittedValuesIgnoreCaseOperator(permitted));
}
/**
* Reduces the rows returned from a query to only those matching the provided
* objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param permitted permitted
*/
public void permittedValuesIgnoreCase(Collection<E> permitted) {
this.setOperator(new DBPermittedValuesIgnoreCaseOperator(convertToLiteralString(permitted)));
}
/**
* Reduces the rows returned from a query by excluding those matching the
* provided objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param excluded excluded
*/
public void excludedValuesIgnoreCase(String... excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(excluded));
negateOperator();
}
/**
* Reduces the rows returned from a query by excluding those matching the
* provided objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param excluded excluded
*/
public void excludedValuesIgnoreCase(StringExpression... excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(excluded));
negateOperator();
}
/**
* Reduces the rows returned from a query by excluding those matching the
* provided objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param excluded excluded
*/
public void excludedValuesIgnoreCase(List<String> excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(excluded));
negateOperator();
}
/**
* Reduces the rows returned from a query by excluding those matching the
* provided objects.
*
* <p>
* The case, upper or lower, will be ignored.
*
* <p>
* Defining case for Unicode characters is complicated and may not work as
* expected.
*
* @param excluded excluded
*/
public void excludedValuesIgnoreCase(Set<String> excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(excluded));
negateOperator();
}
// /**
// * Reduces the rows returned from a query by excluding those matching the
// * provided objects.
// *
// * <p>
// * The case, upper or lower, will be ignored.
// *
// * <p>
// * Defining case for Unicode characters is complicated and may not work as
// * expected.
// *
// * @param excluded excluded
// */
// public void excludedValues(String... excluded) {
// this.setOperator(new DBPermittedValuesOperator<String>(excluded));
// negateOperator();
// }
/**
* Performs searches based on a range.
*
* if both ends of the range are specified the lower-bound will be included in
* the search and the upper-bound excluded. I.e permittedRange(1,3) will
* return 1 and 2.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRange(1,null) will return 1,2,3,4,5, etc.
*
* <p>
* if the upper-bound is null the range will be open ended and exclusive.
* <br>
* I.e permittedRange(null, 5) will return 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRange(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeOperator<String>(lowerBound, upperBound));
}
/**
* Performs searches based on a range.
*
* if both ends of the range are specified both the lower- and upper-bound
* will be included in the search. I.e permittedRangeInclusive(1,3) will
* return 1, 2, and 3.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRangeInclusive(1,null) will return 1,2,3,4,5, etc.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRangeInclusive(null, 5) will return 5,4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRangeInclusive(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeInclusiveOperator(lowerBound, upperBound));
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified both the lower- and upper-bound
* will be excluded in the search. I.e permittedRangeExclusive(1,3) will
* return 2.
*
* <p>
* if the upper-bound is null the range will be open ended up and exclusive.
* <br>
* I.e permittedRangeExclusive(1,null) will return 2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e permittedRangeExclusive(null, 5) will return 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRangeExclusive(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeExclusiveOperator(lowerBound, upperBound));
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified the lower-bound will included and
* the upper-bound will be excluded in the range. I.e excludedRange(1,3) will
* exclude 1,2.
*
* <p>
* if the upper-bound is null the range will be open ended up and inclusive.
* <br>
* I.e excludedRange(1,null) will exclude 1,2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e excludedRange(null, 5) will exclude 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRange(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeOperator<String>(lowerBound, upperBound));
negateOperator();
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified both the lower-bound and the
* upper-bound will be included in the range. I.e excludedRangeInclusive(1,3)
* will exclude 1,2,3.
*
* <p>
* if the upper-bound is null the range will be open ended up and inclusive.
* <br>
* I.e excludedRangeInclusive(1,null) will exclude 1,2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and inclusive.
* <br>
* I.e excludedRangeInclusive(null, 5) will exclude 5,4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRangeInclusive(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeInclusiveOperator(lowerBound, upperBound));
negateOperator();
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified both the lower-bound and the
* upper-bound will be excluded in the range. I.e excludedRangeExclusive(1,3)
* will exclude 2.
*
* <p>
* if the upper-bound is null the range will be open ended up and exclusive.
* <br>
* I.e excludedRangeExclusive(1,null) will exclude 2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e excludedRangeExclusive(null, 5) will exclude 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRangeExclusive(String lowerBound, String upperBound) {
setOperator(new DBPermittedRangeExclusiveOperator(lowerBound, upperBound));
negateOperator();
}
/**
* Perform searches based on using database compatible pattern matching
*
* <p>
* This facilitates the LIKE operator.
*
* <p>
* Please use the pattern system appropriate to your database.
*
* <p>
* Java-style regular expressions are not yet supported.
*
* @param pattern pattern
*/
public void permittedPattern(String pattern) {
this.setOperator(new DBPermittedPatternOperator(pattern));
}
/**
* Perform searches based on using database compatible pattern matching
*
* <p>
* This facilitates the LIKE operator.
*
* <p>
* Please use the pattern system appropriate to your database.
*
* <p>
* Java-style regular expressions are not yet supported.
*
* @param pattern pattern
*/
public void excludedPattern(String pattern) {
this.setOperator(new DBPermittedPatternOperator(pattern));
this.negateOperator();
}
/**
* Perform searches based on using database compatible pattern matching
*
* <p>
* This facilitates the LIKE operator.
*
* <p>
* Please use the pattern system appropriate to your database.
*
* <p>
* Java-style regular expressions are not yet supported.
*
* @param pattern pattern
*/
public void permittedPattern(StringExpression pattern) {
this.setOperator(new DBPermittedPatternOperator(pattern));
}
/**
* Perform searches based on using database compatible pattern matching
*
* <p>
* This facilitates the LIKE operator.
*
* <p>
* Please use the pattern system appropriate to your database.
*
* <p>
* Java-style regular expressions are not yet supported.
*
* @param pattern pattern
*/
public void excludedPattern(StringExpression pattern) {
this.setOperator(new DBPermittedPatternOperator(pattern));
this.negateOperator();
}
// /**
// *
// * reduces the rows to only the object, Set, List, Array, or vararg of objects
// *
// * @param permitted permitted
// */
// @SafeVarargs
// public final void permittedValues(E... permitted) {
// this.setOperator(new DBPermittedValuesOperator<String>(convertToLiteral(permitted)));
// }
@Override
@SafeVarargs
protected final String[] convertToLiteral(E... enumValues) {
return convertToLiteralString(enumValues);
}
/**
*
* reduces the rows to only the object, Set, List, Array, or vararg of Strings
* ignoring letter case.
*
* @param permitted permitted
*/
@SafeVarargs
public final void permittedValuesIgnoreCase(E... permitted) {
this.setOperator(new DBPermittedValuesIgnoreCaseOperator(convertToLiteral(permitted)));
}
/**
* Reduces the rows to excluding the object, Set, List, Array, or vararg of
* Strings ignoring letter case.
*
* @param excluded excluded
*/
@SafeVarargs
public final void excludedValuesIgnoreCase(E... excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(convertToLiteralString(excluded)));
negateOperator();
}
/**
* Reduces the rows to excluding the object, Set, List, Array, or vararg of
* Strings ignoring letter case.
*
* @param excluded excluded
*/
public void excludedValuesIgnoreCase(Collection<E> excluded) {
setOperator(new DBPermittedValuesIgnoreCaseOperator(convertToLiteralString(excluded)));
negateOperator();
}
// /**
// *
// * excludes the object, Set, List, Array, or vararg of objects
// *
// *
// * @param excluded excluded
// */
// @SafeVarargs
// public final void excludedValues(E... excluded) {
// this.setOperator(new DBPermittedValuesOperator<String>(convertToLiteralString(excluded)));
// negateOperator();
// }
/**
* Performs searches based on a range.
*
* if both ends of the range are specified the lower-bound will be included in
* the search and the upper-bound excluded. I.e permittedRange(1,3) will
* return 1 and 2.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRange(1,null) will return 1,2,3,4,5, etc.
*
* <p>
* if the upper-bound is null the range will be open ended and exclusive.
* <br>
* I.e permittedRange(null, 5) will return 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRange(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeOperator<String>(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
}
/**
* Performs searches based on a range.
*
* if both ends of the range are specified both the lower- and upper-bound
* will be included in the search. I.e permittedRangeInclusive(1,3) will
* return 1, 2, and 3.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRangeInclusive(1,null) will return 1,2,3,4,5, etc.
*
* <p>
* if the upper-bound is null the range will be open ended and inclusive.
* <br>
* I.e permittedRangeInclusive(null, 5) will return 5,4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRangeInclusive(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeInclusiveOperator(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
}
/**
* Performs searches based on a range.
*
* if both ends of the range are specified both the lower- and upper-bound
* will be excluded in the search. I.e permittedRangeExclusive(1,3) will
* return 2.
*
* <p>
* if the upper-bound is null the range will be open ended and exclusive.
* <br>
* I.e permittedRangeExclusive(1,null) will return 2,3,4,5, etc.
*
* <p>
* if the upper-bound is null the range will be open ended and exclusive.
* <br>
* I.e permittedRangeExclusive(null, 5) will return 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void permittedRangeExclusive(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeExclusiveOperator(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified the lower-bound will included and
* the upper-bound will be excluded in the range. I.e excludedRange(1,3) will
* exclude 1,2.
*
* <p>
* if the upper-bound is null the range will be open ended up and inclusive.
* <br>
* I.e excludedRange(1,null) will exclude 1,2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e excludedRange(null, 5) will exclude 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRange(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeOperator<String>(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
negateOperator();
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified both the lower-bound and the
* upper-bound will be excluded in the range. I.e excludedRangeExclusive(1,3)
* will exclude 2.
*
* <p>
* if the upper-bound is null the range will be open ended up and exclusive.
* <br>
* I.e excludedRangeExclusive(1,null) will exclude 2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e excludedRangeExclusive(null, 5) will exclude 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRangeInclusive(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeInclusiveOperator(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
negateOperator();
}
/**
* Performs searches based on a range.
*
* <p>
* if both ends of the range are specified both the lower-bound and the
* upper-bound will be excluded in the range. I.e excludedRangeExclusive(1,3)
* will exclude 2.
*
* <p>
* if the upper-bound is null the range will be open ended up and exclusive.
* <br>
* I.e excludedRangeExclusive(1,null) will exclude 2,3,4,5, etc.
*
* <p>
* if the lower-bound is null the range will be open ended down and exclusive.
* <br>
* I.e excludedRangeExclusive(null, 5) will exclude 4,3,2,1, etc.
*
* @param lowerBound lowerBound
* @param upperBound upperBound
*/
public void excludedRangeExclusive(E lowerBound, E upperBound) {
setOperator(new DBPermittedRangeExclusiveOperator(convertToLiteralString(lowerBound), convertToLiteralString(upperBound)));
negateOperator();
}
@Override
public String getValue() {
final Object value = super.getLiteralValue();
if (value == null) {
return null;
} else if (value instanceof String) {
return (String) value;
} else {
return value.toString();
}
}
@Override
protected String getFromResultSet(DBDefinition database, ResultSet resultSet, String fullColumnName) throws SQLException {
return resultSet.getString(fullColumnName);
}
@Override
protected void setValueFromStandardStringEncoding(String encodedValue) {
setValue(encodedValue);
}
@Override
public StringColumn getColumn(RowDefinition row) throws IncorrectRowProviderInstanceSuppliedException {
return new StringColumn(row, this);
}
@Override
public Comparator<String> getComparator() {
return ComparableComparator.forClass(String.class);
}
}