added option tests => fixed option bugs

This commit is contained in:
Matthew Adams
2013-11-19 09:12:37 -06:00
parent 55846e10e4
commit 08c89c37b0
5 changed files with 160 additions and 23 deletions

View File

@@ -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 + "]";
}
}

View File

@@ -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()

View File

@@ -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;
}
}
/**

View File

@@ -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);
}
}

View File

@@ -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));
}
}