package com.goodworkalan.addendum;
import static com.goodworkalan.addendum.Addendum.SQL_ADDENDA_COUNT;
import static com.goodworkalan.addendum.Addendum.SQL_ADDENDUM;
import static com.goodworkalan.addendum.Addendum.SQL_CREATE_ADDENDA;
import static com.goodworkalan.addendum.Addendum.SQL_GET_DIALECT;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.goodworkalan.addendum.connector.Connector;
import com.goodworkalan.addendum.dialect.Dialect;
import com.goodworkalan.danger.Danger;
import com.goodworkalan.furnish.Furnish;
/**
* A collection of {@link Addendum} instances with changes to apply to an
* application's data structures.
*
* @author Alan Gutierrez
*/
public class Addenda {
/** The numbers of addenda to skip. */
private final int skip;
/** The tracking schema. */
final Schema schema = new Schema();
/** A list of changes to apply to the database. */
private final List<List<DatabaseUpdate>> scripts = new ArrayList<List<DatabaseUpdate>>();
/** The connector. */
private final Connector connector;
/** A service loader for the Dialect service. */
private Iterable<Dialect> dialects = new Furnish<Dialect>(Dialect.class);
/**
* Create a collection of changes that operates on the database associated
* with the given connector.
*
* @param connector
* The database connector.
* @param skip
* The number of initial addenda to skip.
*/
public Addenda(Connector connector, int skip) {
this.connector = connector;
this.skip = skip;
}
/**
* Create a collection of changes that operates on the database associated
* with the given connector.
*
* @param connector
* The database connector.
*/
public Addenda(Connector connector) {
this(connector, 0);
}
/**
* Apply all of the addenda if they are not already recored in the addenda
* table in the database of the associated connector. A dialect will be
* chosen from the dialects available according to their
* <code>META-INF/com.goodworkalan.addendum.Dialect</code> files.
*
* @exception AddendumException
* For any SQL error.
*/
public void amend() {
Connection connection = connector.open();
try {
Dialect dialect = null;
try {
for (Dialect candidate : dialects) {
dialect = candidate.canTranslate(connection, dialect);
}
} catch (SQLException e) {
throw new Danger(Addendum.class, SQL_GET_DIALECT, e);
}
if (dialect == null) {
throw new Danger(Addendum.class, SQL_GET_DIALECT);
}
try {
dialect.createAddendaTable(connection);
} catch (SQLException e) {
throw new Danger(Addendum.class, SQL_CREATE_ADDENDA, e);
}
int max;
try {
max = dialect.addendaCount(connection);
} catch (SQLException e) {
throw new Danger(Addendum.class, SQL_ADDENDA_COUNT, e);
}
for (int i = max, stop = scripts.size(); i < stop; i++) {
if (i >= skip) {
for (DatabaseUpdate update : scripts.get(i)) {
update.update(connection, dialect);
}
}
try {
dialect.addendum(connection);
} catch (SQLException e) {
throw new Danger(Addendum.class, SQL_ADDENDUM, e);
}
}
} finally {
connector.close(connection);
}
}
/**
* Create a new addendum that will changes to a the database associated with
* the connector of this addenda, or to any other data resources in the
* application.
*
* @return An addendum builder used to specify updates to the database.
*/
public Addendum addendum() {
List<DatabaseUpdate> updates = new ArrayList<DatabaseUpdate>();
scripts.add(updates);
return new Addendum(new Patch(schema, updates));
}
}
/* vim: set et sw=4 ts=4 ai tw=78 nowrap: */