package net.sourceforge.mayfly.datastore.constraint;
import net.sourceforge.mayfly.MayflyException;
import net.sourceforge.mayfly.MayflyInternalException;
import net.sourceforge.mayfly.datastore.Column;
import net.sourceforge.mayfly.datastore.DataStore;
import net.sourceforge.mayfly.datastore.Row;
import net.sourceforge.mayfly.datastore.Rows;
import net.sourceforge.mayfly.datastore.TableReference;
import net.sourceforge.mayfly.evaluation.select.Evaluator;
import net.sourceforge.mayfly.parser.Location;
import net.sourceforge.mayfly.util.ImmutableList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Constraints implements Iterable<Constraint> {
private final ImmutableList<Constraint> constraints;
public Constraints() {
this(new ImmutableList());
}
public Constraints(ImmutableList constraints) {
this.constraints = constraints;
checkDuplicates(constraints);
checkOnlyOnePrimaryKey(constraints);
}
private static void checkDuplicates(ImmutableList constraints) {
for (int i = 0; i < constraints.size(); ++i) {
Constraint constraint = (Constraint) constraints.get(i);
constraint.checkDuplicates(constraints.subList(0, i));
}
}
private static void checkOnlyOnePrimaryKey(
ImmutableList constraints) {
int keys = 0;
for (int i = 0; i < constraints.size(); ++i) {
Constraint constraint = (Constraint) constraints.get(i);
if (constraint instanceof PrimaryKey) {
++keys;
}
}
if (keys > 1) {
/* We don't have table name and location, to provide
a suitable message for the real case. So this check
is just as a backup. */
throw new MayflyInternalException(
"attempt to define " + keys + " primary keys");
}
}
/**
* @internal
* Check some constraints.
*
* Not-null is checked in
* {@link Column#coerce(net.sourceforge.mayfly.datastore.Cell, Location)}
*/
public void check(Rows rows, Row newRow, TableReference table, Location location) {
for (Constraint constraint : constraints) {
constraint.check(rows, newRow, table, location);
}
}
public void checkInsert(
DataStore store, String schema, String table, Row proposedRow, Location location) {
for (Constraint constraint : constraints) {
constraint.checkInsert(store, schema, table, proposedRow, location);
}
}
public DataStore checkDelete(
DataStore store, String schema, String table,
Row rowToDelete, Row replacementRow) {
for (Constraint constraint : constraints) {
store = constraint.checkDelete(store, schema, table,
rowToDelete, replacementRow);
}
return store;
}
public void checkDropTable(DataStore store, String schema, String table) {
for (Constraint constraint : constraints) {
constraint.checkDropTable(store, schema, table);
}
}
public Constraints dropColumn(TableReference table, String column) {
return new Constraints(
filterConstraintsForDropColumn(table, column)
);
}
private ImmutableList filterConstraintsForDropColumn(
TableReference table, String column) {
List newConstraints = new ArrayList();
for (Constraint constraint : constraints) {
if (constraint.checkDropColumn(table, column)) {
newConstraints.add(constraint);
}
}
return new ImmutableList(newConstraints);
}
public void checkDropColumn(TableReference table, String column) {
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
constraint.checkDropTargetColumn(table, column);
}
}
public Constraints renameColumn(String oldName, String newName) {
List newConstraints = new ArrayList();
for (Constraint constraint : constraints) {
if (constraint.refersTo(oldName)) {
throw new MayflyException("cannot rename column " + oldName +
" because a constraint refers to it");
}
Constraint newConstraint =
constraint.renameColumn(oldName, newName);
newConstraints.add(newConstraint);
}
return new Constraints(new ImmutableList(newConstraints));
}
public Constraints renameTable(String oldName, String newName) {
boolean somethingChanged = false;
List newConstraints = new ArrayList();
for (Constraint constraint : constraints) {
Constraint newConstraint =
constraint.renameTable(oldName, newName);
if (newConstraint != constraint) {
somethingChanged = true;
}
newConstraints.add(newConstraint);
}
if (somethingChanged) {
return new Constraints(new ImmutableList(newConstraints));
}
else {
return this;
}
}
public Constraints dropForeignKey(String constraintName) {
return new Constraints(
constraintsWithout(constraintName, ForeignKey.class));
}
public Constraints dropConstraint(String constraintName) {
return new Constraints(constraintsWithout(constraintName));
}
private ImmutableList constraintsWithout(String constraintName) {
List keys = new ArrayList();
findConstraint(constraintName, keys);
return new ImmutableList(keys);
}
private ImmutableList constraintsWithout(String constraintName, Class constraintType) {
List keys = new ArrayList();
Constraint found = findConstraint(constraintName, keys);
if (found.getClass() != constraintType) {
throw new MayflyException(
"constraint " + constraintName +
" is not a " + describe(constraintType));
}
return new ImmutableList(keys);
}
private Constraint findConstraint(String constraintName, List keys) {
Constraint found = null;
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint key = (Constraint) iter.next();
if (!key.nameMatches(constraintName)) {
keys.add(key);
}
else {
found = key;
}
}
if (found == null) {
throw new MayflyException("no constraint " + constraintName);
}
return found;
}
private String describe(Class constraintType) {
if (constraintType == ForeignKey.class) {
return "foreign key";
}
else {
return constraintType.getName();
}
}
public Constraints addConstraint(Constraint constraint) {
constraint.checkDuplicates(constraints);
return new Constraints(constraints.with(constraint));
}
public boolean canBeTargetOfForeignKey(String targetColumn) {
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
if (constraint.canBeTargetOfForeignKey(targetColumn)) {
return true;
}
}
return false;
}
public int constraintCount() {
return constraints.size();
}
public Constraint constraint(int index) {
return constraints.get(index);
}
public boolean hasPrimaryKey() {
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
if (constraint instanceof PrimaryKey) {
return true;
}
}
return false;
}
public boolean refersTo(String table, Evaluator evaluator) {
evaluator.store().table(table);
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
if (constraint.refersTo(table, evaluator)) {
return true;
}
}
return false;
}
public List referencedTables(Evaluator evaluator) {
List result = new ArrayList();
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
result.addAll(constraint.referencedTables());
}
return result;
}
public boolean mustInsertBefore(Row first, Row second) {
for (Iterator iter = constraints.iterator(); iter.hasNext();) {
Constraint constraint = (Constraint) iter.next();
boolean requiredOrder = constraint.mustInsertBefore(first, second);
if (requiredOrder) {
return requiredOrder;
}
}
return false;
}
public Iterator<Constraint> iterator() {
return constraints.iterator();
}
}