added option tests => fixed option bugs
This commit is contained in:
@@ -5,14 +5,13 @@ import static org.springframework.data.cassandra.cql.CqlStringUtils.singleQuote;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link Option} to which {@link Enum} types can delegate, since they can't extend
|
||||
* anything.
|
||||
* A default implementation of {@link Option}.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
@@ -23,27 +22,33 @@ public class DefaultOption implements Option {
|
||||
private boolean requiresValue;
|
||||
private boolean escapesValue;
|
||||
private boolean quotesValue;
|
||||
private HashSet<Object> enumConstants; // HACK for enums only
|
||||
|
||||
public DefaultOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue, boolean quotesValue) {
|
||||
setName(name);
|
||||
setType(type);
|
||||
this.requiresValue = requiresValue;
|
||||
this.escapesValue = escapesValue;
|
||||
this.quotesValue = quotesValue;
|
||||
|
||||
}
|
||||
|
||||
protected void setName(String name) {
|
||||
Assert.hasLength(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
protected void setType(Class<?> type) {
|
||||
if (type != null) {
|
||||
if (type.isInterface() && !(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) {
|
||||
throw new IllegalArgumentException("given type [" + type.getName() + "] must be a class, Map or Collection");
|
||||
}
|
||||
// HACK for enums only
|
||||
if (type.isEnum()) {
|
||||
enumConstants = new HashSet<Object>(Arrays.asList(type.getEnumConstants()));
|
||||
}
|
||||
}
|
||||
this.type = type;
|
||||
this.requiresValue = requiresValue;
|
||||
this.escapesValue = escapesValue;
|
||||
this.quotesValue = quotesValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public boolean isCoerceable(Object value) {
|
||||
if (value == null) {
|
||||
if (value == null || type == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,8 +62,15 @@ public class DefaultOption implements Option {
|
||||
}
|
||||
// check enum
|
||||
if (type.isEnum()) {
|
||||
// HACK -- prefer to use Enum.valueOf(type, stringValue), but can't
|
||||
return enumConstants.contains(value.toString());
|
||||
try {
|
||||
String name = value instanceof Enum ? name = ((Enum) value).name() : value.toString();
|
||||
Enum.valueOf((Class<? extends Enum>) type, name);
|
||||
return true;
|
||||
} catch (NullPointerException x) {
|
||||
return false;
|
||||
} catch (IllegalArgumentException x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check class via String constructor
|
||||
@@ -129,6 +141,8 @@ public class DefaultOption implements Option {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
checkValue(value);
|
||||
|
||||
String string = value.toString();
|
||||
string = escapesValue ? escapeSingle(string) : string;
|
||||
string = quotesValue ? singleQuote(string) : string;
|
||||
@@ -137,6 +151,7 @@ public class DefaultOption implements Option {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
return "[name=" + name + ", type=" + type.getName() + ", requiresValue=" + requiresValue + ", escapesValue="
|
||||
+ escapesValue + ", quotesValue=" + quotesValue + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,11 @@ public interface Option {
|
||||
boolean isCoerceable(Object value);
|
||||
|
||||
/**
|
||||
* Renders the given value to a string according to this option's settings. Given <code>null</code>, returns
|
||||
* <code>null</code>.
|
||||
* First ensures that the given value is coerceable into the type expected by this option, then returns the result of
|
||||
* {@link Object#toString()} called on the given value. If this option is escaping quotes ({@link #escapesValue()} is
|
||||
* <code>true</code>), then single quotes will be escaped, and if this option is quoting values (
|
||||
* {@link #quotesValue()} is <code>true</code>), then the value will be surrounded by single quotes. Given
|
||||
* <code>null</code>, returns <code>null</code>.
|
||||
*
|
||||
* @see #escapesValue()
|
||||
* @see #quotesValue()
|
||||
|
||||
@@ -6,6 +6,8 @@ import java.util.Map;
|
||||
* Enumeration that represents all known table options. If a table option is not listed here, but is supported by
|
||||
* Cassandra, use the method {@link CreateTableBuilder#with(String, Object, boolean, boolean)} to write the raw value.
|
||||
*
|
||||
* Implements {@link Option} via delegation, since {@link Enum}s can't extend anything.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @see CompactionOption
|
||||
* @see CompressionOption
|
||||
@@ -111,7 +113,17 @@ public enum TableOption implements Option {
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public enum CachingOption {
|
||||
ALL, KEYS_ONLY, ROWS_ONLY, NONE;
|
||||
ALL("all"), KEYS_ONLY("keys_only"), ROWS_ONLY("rows_only"), NONE("none");
|
||||
|
||||
private String value;
|
||||
|
||||
private CachingOption(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package org.springframework.data.cassandra.cql;
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.springframework.data.cassandra.cql.builder.CqlBuilder.createTable;
|
||||
import static org.springframework.data.cassandra.cql.builder.MapBuilder.map;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.BLOOM_FILTER_FP_CHANCE;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.CACHING;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.COMMENT;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.COMPACTION;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.CompactionOption.TOMBSTONE_THRESHOLD;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.cassandra.cql.builder.CreateTableBuilder;
|
||||
import org.springframework.data.cassandra.cql.builder.TableOption.CachingOption;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
@@ -27,13 +29,16 @@ public class CreateTableBuilderTest {
|
||||
String column2 = "column2";
|
||||
Object comment = "this is a comment";
|
||||
Object bloom = "0.00075";
|
||||
Object caching = CachingOption.KEYS_ONLY;
|
||||
|
||||
CreateTableBuilder builder = createTable().ifNotExists().name(name).partitionKeyColumn(partKey0, type0)
|
||||
.partitionKeyColumn(partition1, type0).primaryKeyColumn(primary0, type0).column(column1, type1)
|
||||
.column(column2, type2).with(COMMENT, comment).with(BLOOM_FILTER_FP_CHANCE, bloom)
|
||||
.with(COMPACTION, map().entry(TOMBSTONE_THRESHOLD, "0.15"));
|
||||
.with(COMPACTION, map().entry(TOMBSTONE_THRESHOLD, "0.15")).with(CACHING, caching);
|
||||
|
||||
String cql = builder.toCql();
|
||||
System.out.println(cql);
|
||||
assertEquals(
|
||||
"CREATE TABLE IF NOT EXISTS mytable (partitionKey0 text, partitionKey1 text, primary0 text, column1 text, column2 bigint, PRIMARY KEY ((partitionKey0, partitionKey1), primary0) WITH CLUSTERING ORDER BY (primary0 ASC) AND comment = 'this is a comment' AND bloom_filter_fp_chance = 0.00075 AND compaction = { 'tombstone_threshold' : 0.15 } AND caching = keys_only;",
|
||||
cql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class OptionTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testOptionWithNullName() {
|
||||
new DefaultOption(null, Object.class, true, true, true);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testOptionWithEmptyName() {
|
||||
new DefaultOption("", Object.class, true, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionWithNullType() {
|
||||
new DefaultOption("opt", null, true, true, true);
|
||||
new DefaultOption("opt", null, false, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionWithNullTypeIsCoerceable() {
|
||||
Option op = new DefaultOption("opt", null, true, true, true);
|
||||
assertTrue(op.isCoerceable(""));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionValueCoercion() {
|
||||
String name = "my_option";
|
||||
Class<?> type = String.class;
|
||||
boolean requires = true;
|
||||
boolean escapes = true;
|
||||
boolean quotes = true;
|
||||
|
||||
Option op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
assertTrue(op.isCoerceable("opt"));
|
||||
assertEquals("'opt'", op.toString("opt"));
|
||||
assertEquals("'opt''n'", op.toString("opt'n"));
|
||||
|
||||
type = Long.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
String expected = "1";
|
||||
for (Object value : new Object[] { 1, "1" }) {
|
||||
assertTrue(op.isCoerceable(value));
|
||||
assertEquals(expected, op.toString(value));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = Long.class;
|
||||
escapes = false;
|
||||
quotes = true;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
expected = "'1'";
|
||||
for (Object value : new Object[] { 1, "1" }) {
|
||||
assertTrue(op.isCoerceable(value));
|
||||
assertEquals(expected, op.toString(value));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = Double.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
String[] expecteds = new String[] { "1", "1.0", "1.0", "1", "1.0", null };
|
||||
Object[] values = new Object[] { 1, 1.0F, 1.0D, "1", "1.0", null };
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
assertTrue(op.isCoerceable(values[i]));
|
||||
assertEquals(expecteds[i], op.toString(values[i]));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = RetentionPolicy.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
assertTrue(op.isCoerceable(null));
|
||||
assertTrue(op.isCoerceable(RetentionPolicy.CLASS));
|
||||
assertTrue(op.isCoerceable("CLASS"));
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertEquals("CLASS", op.toString("CLASS"));
|
||||
assertEquals("CLASS", op.toString(RetentionPolicy.CLASS));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user