aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java6
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java83
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java83
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java18
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java24
5 files changed, 173 insertions, 41 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
index 9be67d2..abebec0 100644
--- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
@@ -384,12 +384,12 @@ public class ColumnBuilder {
* @usage _advanced_method_
*/
public void validate(JetFormat format) {
- if(getType() == null) {
- throw new IllegalArgumentException(withErrorContext("must have type"));
- }
DatabaseImpl.validateIdentifierName(
getName(), format.MAX_COLUMN_NAME_LENGTH, "column");
+ if(getType() == null) {
+ throw new IllegalArgumentException(withErrorContext("must have type"));
+ }
if(getType().isUnsupported()) {
throw new IllegalArgumentException(withErrorContext(
"Cannot create column with unsupported type " + getType()));
diff --git a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
index 8f7ae44..795bbd9 100644
--- a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
+++ b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java
@@ -17,45 +17,91 @@ limitations under the License.
package com.healthmarketscience.jackcess;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import com.healthmarketscience.jackcess.impl.DatabaseImpl;
+import com.healthmarketscience.jackcess.impl.RelationshipCreator;
import com.healthmarketscience.jackcess.impl.RelationshipImpl;
/**
+ * Builder style class for constructing a {@link Relationship}, and,
+ * optionally, the associated backing foreign key (if referential integrity
+ * enforcement is enabled).
*
* @author James Ahlborn
*/
public class RelationshipBuilder
{
+ private static final int JOIN_FLAGS =
+ RelationshipImpl.LEFT_OUTER_JOIN_FLAG |
+ RelationshipImpl.RIGHT_OUTER_JOIN_FLAG;
+
/** relationship flags (default to "don't enforce") */
private int _flags = RelationshipImpl.NO_REFERENTIAL_INTEGRITY_FLAG;
+ private String _toTable;
+ private String _fromTable;
+ private List<String> _toCols = new ArrayList<String>();
+ private List<String> _fromCols = new ArrayList<String>();
- // - primary table must have unique index
- // - primary table index name ".rC", ".rD"...
- // - secondary index name "<PTable><STable>"
- // - add <name>1, <name>2 after names to make unique (index names and
- // relationship names)
-
- public RelationshipBuilder()
+ public RelationshipBuilder(String toTable, String fromTable)
{
+ _toTable = toTable;
+ _fromTable = fromTable;
+ }
+
+ /**
+ * Adds a pair of columns to the relationship.
+ */
+ public RelationshipBuilder addColumns(String toCol, String fromCol) {
+ _toCols.add(toCol);
+ _fromCols.add(fromCol);
+ return this;
}
+ /**
+ * Enables referential integrity enforcement for this relationship.
+ *
+ * Note, this requires the "to" table to have an existing unique index on
+ * the relevant columns.
+ */
public RelationshipBuilder setReferentialIntegrity() {
return clearFlag(RelationshipImpl.NO_REFERENTIAL_INTEGRITY_FLAG);
}
+ /**
+ * Enables deletes to be cascaded from the "to" table to the "from" table.
+ *
+ * Note, this requires referential integrity to be enforced.
+ */
public RelationshipBuilder setCascadeDeletes() {
return setFlag(RelationshipImpl.CASCADE_DELETES_FLAG);
}
+ /**
+ * Enables updates to be cascaded from the "to" table to the "from" table.
+ *
+ * Note, this requires referential integrity to be enforced.
+ */
public RelationshipBuilder setCascadeUpdates() {
return setFlag(RelationshipImpl.CASCADE_UPDATES_FLAG);
}
+ /**
+ * Enables deletes in the "to" table to be cascaded as "null" to the "from"
+ * table.
+ *
+ * Note, this requires referential integrity to be enforced.
+ */
public RelationshipBuilder setCascadeNullOnDelete() {
return setFlag(RelationshipImpl.CASCADE_NULL_FLAG);
}
-
+
+ /**
+ * Sets the preferred join type for this relationship.
+ */
public RelationshipBuilder setJoinType(Relationship.JoinType joinType) {
+ clearFlag(JOIN_FLAGS);
switch(joinType) {
case INNER:
// nothing to do
@@ -80,6 +126,22 @@ public class RelationshipBuilder
return _flags;
}
+ public String getToTable() {
+ return _toTable;
+ }
+
+ public String getFromTable() {
+ return _fromTable;
+ }
+
+ public List<String> getToColumns() {
+ return _toCols;
+ }
+
+ public List<String> getFromColumns() {
+ return _fromCols;
+ }
+
/**
* Creates a new Relationship in the given Database with the currently
* configured attributes.
@@ -87,10 +149,7 @@ public class RelationshipBuilder
public Relationship toRelationship(Database db)
throws IOException
{
-
-
- // FIXME writeme
- return null;
+ return new RelationshipCreator((DatabaseImpl)db).createRelationship(this);
}
private RelationshipBuilder setFlag(int flagMask) {
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java
index 273a8ae..452c176 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java
@@ -17,6 +17,7 @@ limitations under the License.
package com.healthmarketscience.jackcess.impl;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -29,12 +30,23 @@ import com.healthmarketscience.jackcess.RelationshipBuilder;
*/
public class RelationshipCreator extends DBMutator
{
+ private final static int CASCADE_FLAGS =
+ RelationshipImpl.CASCADE_DELETES_FLAG |
+ RelationshipImpl.CASCADE_UPDATES_FLAG |
+ RelationshipImpl.CASCADE_NULL_FLAG;
+
+
private TableImpl _primaryTable;
private TableImpl _secondaryTable;
private RelationshipBuilder _relationship;
private List<ColumnImpl> _primaryCols;
private List<ColumnImpl> _secondaryCols;
- private int _flags;
+
+ // - primary table must have unique index
+ // - primary table index name ".rC", ".rD"...
+ // - secondary index name "<PTable><STable>"
+ // - add <name>1, <name>2 after names to make unique (index names and
+ // relationship names)
public RelationshipCreator(DatabaseImpl database)
{
@@ -66,7 +78,7 @@ public class RelationshipCreator extends DBMutator
throws IOException
{
_relationship = relationship;
-
+
validate();
getPageChannel().startExclusiveWrite();
@@ -85,22 +97,26 @@ public class RelationshipCreator extends DBMutator
private void validate() throws IOException {
+ _primaryTable = getDatabase().getTable(_relationship.getToTable());
+ _secondaryTable = getDatabase().getTable(_relationship.getToTable());
+
if((_primaryTable == null) || (_secondaryTable == null)) {
- throw new IllegalArgumentException(
- "Two tables are required in relationship");
- }
- if(_primaryTable.getDatabase() != _secondaryTable.getDatabase()) {
- throw new IllegalArgumentException("Tables are not from same database");
+ throw new IllegalArgumentException(withErrorContext(
+ "Two valid tables are required in relationship"));
}
+ _primaryCols = getColumns(_primaryTable, _relationship.getToColumns());
+ _secondaryCols = getColumns(_secondaryTable, _relationship.getFromColumns());
+
if((_primaryCols == null) || (_primaryCols.isEmpty()) ||
(_secondaryCols == null) || (_secondaryCols.isEmpty())) {
- throw new IllegalArgumentException("Missing columns in relationship");
+ throw new IllegalArgumentException(withErrorContext(
+ "Missing columns in relationship"));
}
if(_primaryCols.size() != _secondaryCols.size()) {
- throw new IllegalArgumentException(
- "Must have same number of columns on each side of relationship");
+ throw new IllegalArgumentException(withErrorContext(
+ "Must have same number of columns on each side of relationship"));
}
for(int i = 0; i < _primaryCols.size(); ++i) {
@@ -108,12 +124,18 @@ public class RelationshipCreator extends DBMutator
ColumnImpl scol = _primaryCols.get(i);
if(pcol.getType() != scol.getType()) {
- throw new IllegalArgumentException(
- "Matched columns must have the same data type");
+ throw new IllegalArgumentException(withErrorContext(
+ "Matched columns must have the same data type"));
}
}
if(!_relationship.hasReferentialIntegrity()) {
+
+ if((_relationship.getFlags() & CASCADE_FLAGS) != 0) {
+ throw new IllegalArgumentException(withErrorContext(
+ "Cascade flags cannot be enabled if referential integrity is not enforced"));
+ }
+
return;
}
@@ -123,7 +145,8 @@ public class RelationshipCreator extends DBMutator
// - cols come from right tables, tables from right db
// - (cols can be duped in index)
// - cols have same data types
- // - if enforce, require unique index on primary (auto-create?), index on secondary
+ // - if enforce, require unique index on primary,
+ // - auto-create index on secondary
// - advanced, check for enforce cycles?
// - index must be ascending
@@ -186,4 +209,38 @@ public class RelationshipCreator extends DBMutator
}
}
}
+
+ private static List<ColumnImpl> getColumns(TableImpl table, List<String> colNames) {
+ List<ColumnImpl> cols = new ArrayList<ColumnImpl>();
+ for(String colName : colNames) {
+ cols.add(table.getColumn(colName));
+ }
+ return cols;
+ }
+
+ private static String getTableErrorContext(
+ TableImpl table, List<ColumnImpl> cols,
+ String tableName, List<String> colNames) {
+ if(table != null) {
+ tableName = table.getName();
+ }
+ if(cols != null) {
+ colNames = new ArrayList<String>();
+ for(ColumnImpl col : cols) {
+ colNames.add(col.getName());
+ }
+ }
+
+ return CustomToStringStyle.valueBuilder(tableName)
+ .append(null, cols)
+ .toString();
+ }
+
+ private String withErrorContext(String msg) {
+ return msg + "(Rel=" +
+ getTableErrorContext(_primaryTable, _primaryCols, _relationship.getToTable(),
+ _relationship.getToColumns()) + " <- " +
+ getTableErrorContext(_secondaryTable, _secondaryCols, _relationship.getFromTable(),
+ _relationship.getFromColumns()) + ")";
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
index 5fb3337..36c866d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
@@ -104,7 +104,8 @@ public class TableCreator extends TableMutator
}
}
}
- throw new IllegalStateException("could not find state for index");
+ throw new IllegalStateException(withErrorContext(
+ "could not find state for index"));
}
public List<IndexDataState> getIndexDataStates() {
@@ -251,13 +252,13 @@ public class TableCreator extends TableMutator
getDatabase().validateNewTableName(_name);
if((_columns == null) || _columns.isEmpty()) {
- throw new IllegalArgumentException(
- "Cannot create table with no columns");
+ throw new IllegalArgumentException(withErrorContext(
+ "Cannot create table with no columns"));
}
if(_columns.size() > getFormat().MAX_COLUMNS_PER_TABLE) {
- throw new IllegalArgumentException(
+ throw new IllegalArgumentException(withErrorContext(
"Cannot create table with more than " +
- getFormat().MAX_COLUMNS_PER_TABLE + " columns");
+ getFormat().MAX_COLUMNS_PER_TABLE + " columns"));
}
Set<String> colNames = new HashSet<String>();
@@ -278,9 +279,9 @@ public class TableCreator extends TableMutator
if(hasIndexes()) {
if(_indexes.size() > getFormat().MAX_INDEXES_PER_TABLE) {
- throw new IllegalArgumentException(
+ throw new IllegalArgumentException(withErrorContext(
"Cannot create table with more than " +
- getFormat().MAX_INDEXES_PER_TABLE + " indexes");
+ getFormat().MAX_INDEXES_PER_TABLE + " indexes"));
}
// now, validate the indexes
@@ -332,4 +333,7 @@ public class TableCreator extends TableMutator
(col1.getFlags() == col2.getFlags()));
}
+ private String withErrorContext(String msg) {
+ return msg + "(Table=" + getName() + ")";
+ }
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java
index ecaa10e..54bfd68 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java
@@ -173,12 +173,13 @@ public class TableUpdater extends TableMutator
private void validateAddColumn() {
if(_column == null) {
- throw new IllegalArgumentException("Cannot add column with no column");
+ throw new IllegalArgumentException(withErrorContext(
+ "Cannot add column with no column"));
}
if((_table.getColumnCount() + 1) > getFormat().MAX_COLUMNS_PER_TABLE) {
- throw new IllegalArgumentException(
+ throw new IllegalArgumentException(withErrorContext(
"Cannot add column to table with " +
- getFormat().MAX_COLUMNS_PER_TABLE + " columns");
+ getFormat().MAX_COLUMNS_PER_TABLE + " columns"));
}
Set<String> colNames = getColumnNames();
@@ -199,12 +200,13 @@ public class TableUpdater extends TableMutator
private void validateAddIndex() {
if(_index == null) {
- throw new IllegalArgumentException("Cannot add index with no index");
+ throw new IllegalArgumentException(withErrorContext(
+ "Cannot add index with no index"));
}
if((_table.getLogicalIndexCount() + 1) > getFormat().MAX_INDEXES_PER_TABLE) {
- throw new IllegalArgumentException(
+ throw new IllegalArgumentException(withErrorContext(
"Cannot add index to table with " +
- getFormat().MAX_INDEXES_PER_TABLE + " indexes");
+ getFormat().MAX_INDEXES_PER_TABLE + " indexes"));
}
boolean foundPk[] = new boolean[1];
@@ -291,4 +293,14 @@ public class TableUpdater extends TableMutator
((col1.getFlags() | ignoreColFlags) ==
(col2.getFlags() | ignoreColFlags)));
}
+
+ private String withErrorContext(String msg) {
+ String objStr = "";
+ if(_column != null) {
+ objStr = ";Column=" + _column.getName();
+ } else if(_index != null) {
+ objStr = ";Index=" + _index.getName();
+ }
+ return msg + "(Table=" + _table.getName() + objStr + ")";
+ }
}