From 3dd7e532ef8b79b412348ba6ce4208d25600e490 Mon Sep 17 00:00:00 2001 From: john-mcpeek Date: Tue, 11 Feb 2014 22:09:25 -0500 Subject: [PATCH] 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. --- .../cassandra/core/CqlIdentifier.java | 69 ++++++++++++++++++- .../cassandra/core/cql/CqlStringUtils.java | 57 +-------------- .../keyspace/ColumnChangeSpecification.java | 12 ++-- .../core/keyspace/ColumnSpecification.java | 22 +++--- .../keyspace/CreateIndexSpecification.java | 13 ++-- .../core/keyspace/IndexNameSpecification.java | 12 ++-- .../keyspace/KeyspaceActionSpecification.java | 20 +++--- .../core/keyspace/TableNameSpecification.java | 12 ++-- .../test/unit/core/cql/CqlIdentifierTest.java | 20 ++++++ .../unit/core/cql/CqlStringUtilsTest.java | 13 ---- .../AlterTableCqlGeneratorTests.java | 1 + .../BasicCassandraPersistentEntity.java | 1 - ...andraPersistentEntityIntegrationTests.java | 2 - .../template/CassandraDataOperationsTest.java | 9 --- 14 files changed, 129 insertions(+), 134 deletions(-) create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java index 6f11f5792..3b3b17e97 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java @@ -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. + * + */ 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; + } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java index 61180cc7f..73d0c70e3 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java @@ -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. - * - */ - 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 null, returns null. diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java index 6cea473f8..665a52797 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java @@ -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(); } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java index 9c2d0ba03..aa6e491e0 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java @@ -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 diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java index 1f9f89d46..bd782b1f3 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java @@ -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 /** * The name of the index. */ - private String name; + private CqlIdentifier identifier; /** * Sets the index name. @@ -38,17 +37,16 @@ public abstract class IndexNameSpecification */ @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(); } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java index cddc2115d..83c6db79e 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java @@ -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> { /** - * 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 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(); } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java index f99addd15..f66eb8e91 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java @@ -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 /** * The name of the table. */ - private String name; + private CqlIdentifier identifier; /** * Sets the table name. @@ -38,16 +37,15 @@ public abstract class TableNameSpecification */ @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(); } } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java new file mode 100644 index 000000000..8b404ec5e --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java @@ -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")); + } + +} diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java index e54d7b29b..9da6874a5 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java @@ -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")); - } } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java index e281fdaa7..e1b3fe86e 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java @@ -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 + " ")); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java index a1b52dd25..863e8f838 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java @@ -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; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java index 306351a00..a59e06da6 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java @@ -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; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java index 047545bc8..7b8ca6c36 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java @@ -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;