Moved identifier logic to CqlIdentifer. Updated all the classes that

used the checkIdentifier() method to have a CqlIdentifer. Also update
those classes for getName() and such.
This commit is contained in:
john-mcpeek
2014-02-11 22:09:25 -05:00
parent 30787eac5a
commit 3dd7e532ef
14 changed files with 129 additions and 134 deletions

View File

@@ -1,6 +1,15 @@
package org.springframework.cassandra.core;
import java.util.regex.Pattern;
import org.springframework.cassandra.core.cql.CqlStringUtils;
public class CqlIdentifier {
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
private String identifier;
private boolean quoted;
@@ -8,11 +17,29 @@ public class CqlIdentifier {
this(identifier, false);
}
/**
* Renders the given string as a legal Cassandra identifier.
* <ul>
* <li>If the given identifier is a legal quoted identifier or forceQuote is true, it is set encased in double quotes.
* </li>
* <li>If the given identifier is a legal unquoted identifier, it is set unchanged.</li>
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*/
public CqlIdentifier(String identifier, boolean forceQuoting) {
if (isUnquotedIdentifier(identifier) && forceQuoting == false) {
this.identifier = identifier;
} else if (isQuotedIdentifier(identifier)) {
this.identifier = identifier;
quoted = true;
} else {
throw new IllegalArgumentException("[" + identifier + "] is not a valid CQL quoted or unquoted identifier");
}
}
public String toCql() {
return identifier;
String id = quoted ? CqlStringUtils.doubleQuote(identifier) : identifier;
return id;
}
public StringBuilder toCql(StringBuilder sb) {
@@ -41,4 +68,44 @@ public class CqlIdentifier {
CqlIdentifier id = new CqlIdentifier(identifier, true);
return id;
}
public static boolean isIdentifier(CharSequence chars) {
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
}
public static boolean isUnquotedIdentifier(CharSequence chars) {
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static boolean isQuotedIdentifier(CharSequence chars) {
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
result = prime * result + (quoted ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CqlIdentifier other = (CqlIdentifier) obj;
if (identifier == null) {
if (other.identifier != null)
return false;
} else if (!identifier.equals(other.identifier))
return false;
if (quoted != other.quoted)
return false;
return true;
}
}

View File

@@ -23,7 +23,7 @@ public class CqlStringUtils {
protected static final String SINGLE_QUOTE = "\'";
protected static final String DOUBLE_SINGLE_QUOTE = "\'\'";
protected static final String DOUBLE_QUOTE = "\"";
public static final String DOUBLE_QUOTE = "\"";
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
protected static final String EMPTY_STRING = "";
protected static final String TYPE_PARAMETER_PREFIX = "<";
@@ -36,61 +36,6 @@ public class CqlStringUtils {
public static final String UNESCAPED_DOUBLE_QUOTE_REGEX = "TODO";
public static final Pattern UNESCAPED_DOUBLE_QUOTE_PATTERN = Pattern.compile(UNESCAPED_DOUBLE_QUOTE_REGEX);
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
public static boolean isUnquotedIdentifier(CharSequence chars) {
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkUnquotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isUnquotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL identifier");
}
}
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
public static boolean isQuotedIdentifier(CharSequence chars) {
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkQuotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isQuotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted identifier");
}
}
public static boolean isIdentifier(CharSequence chars) {
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
}
public static void checkIdentifier(CharSequence chars) {
if (!CqlStringUtils.isIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted or unquoted identifier");
}
}
/**
* Renders the given string as a legal Cassandra identifier.
* <ul>
* <li>If the given identifier is a legal unquoted identifier, it is returned unchanged.</li>
* <li>If the given identifier is a legal quoted identifier, it is returned encased in double quotes.</li>
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*/
public static String identifize(String candidate) {
checkIdentifier(candidate);
if (isUnquotedIdentifier(candidate)) {
return candidate;
}
// else it must be quoted
return doubleQuote(candidate);
}
/**
* Renders the given string as a legal Cassandra string column or table option value, by escaping single quotes and
* encasing the result in single quotes. Given <code>null</code>, returns <code>null</code>.

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Base class for column change specifications.
@@ -25,22 +24,21 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
*/
public abstract class ColumnChangeSpecification {
private String name;
private CqlIdentifier identifier;
public ColumnChangeSpecification(String name) {
setName(name);
}
private void setName(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
}
public String getName() {
return name;
return identifier.getIdentifier();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -15,15 +15,14 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.PrimaryKeyType.PARTITIONED;
import static org.springframework.cassandra.core.PrimaryKeyType.CLUSTERED;
import static org.springframework.cassandra.core.Ordering.ASCENDING;
import static org.springframework.cassandra.core.PrimaryKeyType.CLUSTERED;
import static org.springframework.cassandra.core.PrimaryKeyType.PARTITIONED;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.cassandra.core.CqlIdentifier;
import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import com.datastax.driver.core.DataType;
@@ -45,7 +44,7 @@ public class ColumnSpecification {
*/
public static final Ordering DEFAULT_ORDERING = ASCENDING;
private String name;
private CqlIdentifier identifier;
private DataType type; // TODO: determining if we should be coupling this to Datastax Java Driver type?
private PrimaryKeyType keyType;
private Ordering ordering;
@@ -56,8 +55,7 @@ public class ColumnSpecification {
* @return this
*/
public ColumnSpecification name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return this;
}
@@ -148,11 +146,11 @@ public class ColumnSpecification {
}
public String getName() {
return name;
return identifier.getIdentifier();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
public DataType getType() {
@@ -172,7 +170,7 @@ public class ColumnSpecification {
}
public StringBuilder toCql(StringBuilder cql) {
return (cql = noNull(cql)).append(name).append(" ").append(type);
return (cql = noNull(cql)).append(identifier).append(" ").append(type);
}
@Override

View File

@@ -15,9 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
import org.springframework.util.StringUtils;
/**
@@ -31,7 +29,7 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
private boolean ifNotExists = false;
private boolean custom = false;
private String tableName;
private CqlIdentifier identifier;
private String columnName;
private String using;
@@ -89,17 +87,16 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
* @return this
*/
public CreateIndexSpecification tableName(String tableName) {
checkIdentifier(tableName);
this.tableName = tableName;
identifier = new CqlIdentifier(tableName);
return this;
}
public String getTableName() {
return tableName;
return identifier.getIdentifier();
}
public String getTableNameAsIdentifier() {
return identifize(tableName);
return identifier.toCql();
}
public CreateIndexSpecification columnName(String columnName) {

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of table specifications.
@@ -29,7 +28,7 @@ public abstract class IndexNameSpecification<T extends IndexNameSpecification<T>
/**
* The name of the index.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the index name.
@@ -38,17 +37,16 @@ public abstract class IndexNameSpecification<T extends IndexNameSpecification<T>
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getIdentifier();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -1,7 +1,6 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of keyspace specifications.
@@ -13,9 +12,9 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecification<T>> {
/**
* The name of the table.
* The name of the keyspace.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the keyspace name.
@@ -24,17 +23,16 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getIdentifier();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
/**
@@ -43,7 +41,7 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Keyspace Action Specification {name: " + name + ", class: " + this.getClass() + "}");
sb.append("Keyspace Action Specification {name: " + identifier + ", class: " + this.getClass() + "}");
return sb.toString();
}
@@ -65,12 +63,12 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
return false;
}
KeyspaceActionSpecification<?> thatSpec = (KeyspaceActionSpecification<?>) that;
return this.name.equals(thatSpec.name) && this.getClass().equals(that.getClass());
return this.identifier.equals(thatSpec.identifier) && this.getClass().equals(that.getClass());
}
@Override
public int hashCode() {
return this.name.hashCode() ^ this.getClass().hashCode();
return this.identifier.hashCode() ^ this.getClass().hashCode();
}
}

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of table specifications.
@@ -29,7 +28,7 @@ public abstract class TableNameSpecification<T extends TableNameSpecification<T>
/**
* The name of the table.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the table name.
@@ -38,16 +37,15 @@ public abstract class TableNameSpecification<T extends TableNameSpecification<T>
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getIdentifier();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -0,0 +1,20 @@
package org.springframework.cassandra.test.unit.core.cql;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.cassandra.core.CqlIdentifier.isQuotedIdentifier;
import static org.springframework.cassandra.core.CqlIdentifier.isUnquotedIdentifier;
import org.junit.Test;
public class CqlIdentifierTest {
@Test
public void testIsQuotedIdentifier() throws Exception {
assertFalse(isQuotedIdentifier("my\"id"));
assertTrue(isQuotedIdentifier("my\"\"id"));
assertFalse(isUnquotedIdentifier("my\"id"));
assertTrue(isUnquotedIdentifier("myid"));
}
}

View File

@@ -1,18 +1,5 @@
package org.springframework.cassandra.test.unit.core.cql;
import static org.junit.Assert.*;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isQuotedIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isUnquotedIdentifier;
import org.junit.Test;
public class CqlStringUtilsTest {
@Test
public void testIsQuotedIdentifier() throws Exception {
assertFalse(isQuotedIdentifier("my\"id"));
assertTrue(isQuotedIdentifier("my\"\"id"));
assertFalse(isUnquotedIdentifier("my\"id"));
assertTrue(isUnquotedIdentifier("myid"));
}
}

View File

@@ -26,6 +26,7 @@ public class AlterTableCqlGeneratorTests {
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
System.out.println("cql: " + cql);
assertTrue(cql.startsWith("ALTER TABLE " + tableName + " "));
}

View File

@@ -27,7 +27,6 @@ import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.cassandra.util.CassandraNamingUtils;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;

View File

@@ -19,8 +19,6 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;

View File

@@ -16,7 +16,6 @@
package org.springframework.data.cassandra.test.integration.template;
import static org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification.createKeyspace;
import static org.springframework.cassandra.core.keyspace.DropTableSpecification.dropTable;
import java.io.IOException;
import java.util.ArrayList;
@@ -24,30 +23,22 @@ import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cassandra.core.ConsistencyLevel;
import org.springframework.cassandra.core.QueryOptions;
import org.springframework.cassandra.core.RetryPolicy;
import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.test.integration.simpletons.Book;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.test.integration.support.TestConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;