Skip to content

Instantly share code, notes, and snippets.

@izmajlowiczl
Created October 7, 2017 07:59
Show Gist options
  • Save izmajlowiczl/1e17614d802eaa9174e0c419ba530d6b to your computer and use it in GitHub Desktop.
Save izmajlowiczl/1e17614d802eaa9174e0c419ba530d6b to your computer and use it in GitHub Desktop.
// Source + Unit Tests
package pl.expensive.storage;
import java.util.ArrayList;
import java.util.List;
public class SQLiteTableBuilder {
private String table;
private List<String> commands;
private List<String> fields, pks, fks;
private SQLiteTableBuilder(String table) {
this.table = table;
commands = new ArrayList<>();
}
public static SQLiteTableBuilder newTable(String table) {
checkNotEmpty(table);
return new SQLiteTableBuilder(table);
}
public SQLiteTableBuilder withOptionalText(String column) {
checkNotEmpty(column);
initLazyFieldsCollection();
fields.add(column);
commands.add(column + " TEXT");
return this;
}
public SQLiteTableBuilder withOptionalDecimal(String column) {
checkNotEmpty(column);
initLazyFieldsCollection();
fields.add(column);
commands.add(column + " INTEGER");
return this;
}
public SQLiteTableBuilder withMandatoryText(String column) {
checkNotEmpty(column);
initLazyFieldsCollection();
fields.add(column);
commands.add(column + " TEXT NOT NULL");
return this;
}
public SQLiteTableBuilder withMandatoryDecimal(String column) {
checkNotEmpty(column);
initLazyFieldsCollection();
fields.add(column);
commands.add(column + " INTEGER NOT NULL");
return this;
}
public SQLiteTableBuilder withPrimaryKey(String column) {
checkNotEmpty(column);
initLazyPrimaryKeysCollection();
pks.add(column);
commands.add("PRIMARY KEY (" + column + ")");
return this;
}
/**
* It does not check if referenced column exists.
* It does not check if referenced table exists, is PRIMARY KEY or UNIQUE!
*/
public SQLiteTableBuilder withForeignKey(String column, String referenceTable, String referenceColumn) {
checkNotEmpty(column);
checkNotEmpty(referenceTable);
checkNotEmpty(referenceColumn);
initLazyForeignKeysCollection();
fks.add(column);
commands.add("FOREIGN KEY (" + column + ") REFERENCES " + referenceTable + "(" + referenceColumn + ")");
return this;
}
public String build() {
if (commands.isEmpty()) {
throw new IllegalStateException("Cannot create table without any field");
}
// Check if all Primary Keys have backing fields
if (pks != null) {
for (String pk : pks) {
if (!fields.contains(pk)) {
throw new IllegalStateException("Cannot find field for primary key [" + pk + "]");
}
}
}
// Check if all Foreign Keys have backing fields
if (fks != null) {
for (String pk : fks) {
if (!fields.contains(pk)) {
throw new IllegalStateException("Cannot find field for foreign key [" + pk + "]");
}
}
}
return "CREATE TABLE " + table + " (" + join(commands) + ");";
}
private static void checkNotEmpty(String param) {
if (param == null || param.isEmpty()) {
throw new IllegalArgumentException("Table name is mandatory.");
}
}
private void initLazyPrimaryKeysCollection() {
if (pks == null) {
pks = new ArrayList<>();
}
}
private void initLazyForeignKeysCollection() {
if (fks == null) {
fks = new ArrayList<>();
}
}
private void initLazyFieldsCollection() {
if (fields == null) {
fields = new ArrayList<>();
}
}
/**
*
* Copy of Apache Commons StringUtils.join() with small modification.
*
* <p>Joins the elements of the provided array into a single String
* containing the provided list of elements.</p>
* <p>
* <p>No delimiter is added before or after the list.
* Null objects or empty strings within the array are represented by
* empty strings.</p>
* <p>
* <pre>
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = ""
* StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join([null, "", "a"], ';') = ";;a"
* </pre>
*
* @param array the array of values to join together, may be null
* @return the joined String, <code>null</code> if null array input
* @since 2.0
*/
private static String join(List<String> array) {
if (array == null) {
return null;
}
int startIndex = 0;
int endIndex = array.size();
char separator = ',';
int bufSize = (endIndex - startIndex);
if (bufSize <= 0) {
return "";
}
bufSize *= ((array.get(startIndex) == null ? 16 : array.get(startIndex).length()) + 1);
StringBuilder buf = new StringBuilder(bufSize);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array.get(i) != null) {
buf.append(array.get(i));
}
}
return buf.toString();
}
}
// Tests
package pl.expensive.storage;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
public class SQLiteTableBuilderTest {
@Test
public void CreateOptionalText() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withOptionalText("col_text")
.build();
String expected = "CREATE TABLE tbl (col_text TEXT);";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateOptionalDecimal() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withOptionalDecimal("col_number")
.build();
String expected = "CREATE TABLE tbl (col_number INTEGER);";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateMandatoryText() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("col_text")
.build();
String expected = "CREATE TABLE tbl (col_text TEXT NOT NULL);";
assertThat(sql, equalTo(expected));
}
@Test
public void mandatoryDecimal() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryDecimal("col_number")
.build();
String expected = "CREATE TABLE tbl (col_number INTEGER NOT NULL);";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateMultipleFields() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withOptionalText("col_text")
.withMandatoryDecimal("col_number")
.build();
String expected = "CREATE TABLE tbl (" +
"col_text TEXT," +
"col_number INTEGER NOT NULL);";
assertThat(sql, equalTo(expected));
}
@Test
public void CreatePrimaryKey() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("pk_col")
.withPrimaryKey("pk_col")
.build();
String expected = "CREATE TABLE tbl (" +
"pk_col TEXT NOT NULL," +
"PRIMARY KEY (pk_col));";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateMultiplePrimaryKeys() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("pk_col")
.withMandatoryText("pk_col1")
.withPrimaryKey("pk_col")
.withPrimaryKey("pk_col1")
.build();
String expected = "CREATE TABLE tbl (" +
"pk_col TEXT NOT NULL," +
"pk_col1 TEXT NOT NULL," +
"PRIMARY KEY (pk_col)," +
"PRIMARY KEY (pk_col1)" +
");";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateForeignKey() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("fk_col")
.withForeignKey("fk_col", "tbl_other", "col_other")
.build();
String expected = "CREATE TABLE tbl (" +
"fk_col TEXT NOT NULL," +
"FOREIGN KEY (fk_col) REFERENCES tbl_other(col_other));";
assertThat(sql, equalTo(expected));
}
@Test
public void CreateMultipleForeignKey() {
String sql = SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("fk_col")
.withMandatoryDecimal("fk_col1")
.withForeignKey("fk_col", "tbl_other", "col_other")
.withForeignKey("fk_col1", "tbl_other1", "col_other1")
.build();
String expected = "CREATE TABLE tbl (" +
"fk_col TEXT NOT NULL," +
"fk_col1 INTEGER NOT NULL," +
"FOREIGN KEY (fk_col) REFERENCES tbl_other(col_other)," +
"FOREIGN KEY (fk_col1) REFERENCES tbl_other1(col_other1)" +
");";
assertThat(sql, equalTo(expected));
}
@Test(expected = IllegalStateException.class)
public void CreateTable_WithoutFields_ThrowsException() {
SQLiteTableBuilder.newTable("tbl").build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateTable_WithoutName_ThrowsException() {
SQLiteTableBuilder.newTable("")
.withOptionalText("col")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateOptionalText_WithoutFieldName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withOptionalText("")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateOptionalDecimal_WithoutFieldName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withOptionalDecimal("")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateMandatoryText_WithoutFieldName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withMandatoryText("")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateMandatoryDecimal_WithoutFieldName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withMandatoryDecimal("")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreatePrimaryKey_WithoutFieldName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withOptionalText("col_text")
.withPrimaryKey("")
.build();
}
@Test(expected = IllegalStateException.class)
public void CreatePrimaryKey_WithoutBakingField_ThrowsException() {
SQLiteTableBuilder.newTable("tbl")
.withOptionalDecimal("col_number")
.withPrimaryKey("pk_col")
.build();
}
@Test(expected = IllegalStateException.class)
public void CreateMultiplePrimaryKeys_WithMissingBackingField_ThrowsException() {
SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("pk_col")
.withPrimaryKey("pk_col")
.withPrimaryKey("pk_col1")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateForeignKey_WithoutColumnName_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withForeignKey("", "other_tbl", "other_field")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateForeignKey_WithoutReferenceTable_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withForeignKey("col", "", "other_field")
.build();
}
@Test(expected = IllegalArgumentException.class)
public void CreateForeignKey_WithoutReferenceColumn_ThrowsException() {
SQLiteTableBuilder.newTable("table")
.withForeignKey("col", "other", "")
.build();
}
@Test(expected = IllegalStateException.class)
public void CreateForeignKey_WithoutBakingField_ThrowsException() {
SQLiteTableBuilder.newTable("tbl")
.withOptionalDecimal("not-a-foreign-key")
.withForeignKey("fk_col", "tbl_other", "col_other")
.build();
}
@Test(expected = IllegalStateException.class)
public void CreateMultipleForeignKeys_WithMissingBackingField_ThrowsException() {
SQLiteTableBuilder.newTable("tbl")
.withMandatoryText("fk_col")
.withForeignKey("fk_col", "tbl_other", "col_other")
.withForeignKey("fk_col1", "tbl_other1", "col_other1")
.build();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment