/*
* Copyright (c) 2010, SQL Power Group Inc.
*
* This file is part of SQL Power Library.
*
* SQL Power Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* SQL Power Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.sqlobject;
import java.beans.PropertyChangeEvent;
import java.sql.DatabaseMetaData;
import javax.annotation.Nonnull;
import org.apache.log4j.Logger;
import ca.sqlpower.object.AbstractPoolingSPListener;
import ca.sqlpower.object.SPChildEvent;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.sqlobject.SQLRelationship.ColumnMapping;
import ca.sqlpower.sqlobject.SQLTypePhysicalProperties.SQLTypeConstraint;
import ca.sqlpower.sqlobject.SQLTypePhysicalPropertiesProvider.BasicSQLType;
import ca.sqlpower.sqlobject.SQLTypePhysicalPropertiesProvider.PropertyType;
import ca.sqlpower.util.SQLPowerUtils;
/**
* This listener will update the fk columns of the fk table based on the
* mappings in a relationship when there are property changes to the columns in
* the pk table. This listener only needs to be attached to the pk table.
*
* This class is made package private because only the {@link SQLRelationship}
* should use it. It has been refactored out because it is quite lengthy in
* code.
*/
class ForeignKeyColumnUpdaterPoolingSPListener extends
AbstractPoolingSPListener {
private static final Logger logger =
Logger.getLogger(ForeignKeyColumnUpdaterPoolingSPListener.class);
private final SQLRelationship relationship;
/**
* Creates a new {@link ForeignKeyColumnUpdaterPoolingSPListener}.
*
* @param relationship
* The {@link SQLRelationship} that this listener is tied to.
* This value cannot be null.
*/
ForeignKeyColumnUpdaterPoolingSPListener(@Nonnull SQLRelationship relationship) {
super(false);
this.relationship = relationship;
}
@Override
protected void propertyChangeImpl(PropertyChangeEvent e) {
if (!((SQLObject) e.getSource()).isMagicEnabled()){
logger.debug("Magic disabled; ignoring sqlobject changed event "+e);
return;
}
String prop = e.getPropertyName();
if (logger.isDebugEnabled()) {
logger.debug("Property changed!" +
"\n source=" + e.getSource() +
"\n property=" + prop +
"\n old=" + e.getOldValue() +
"\n new=" + e.getNewValue());
}
if (e.getSource() instanceof SQLColumn) {
SQLColumn col = (SQLColumn) e.getSource();
if (col.getParent() != null && col.getParent().equals(relationship.getParent())) {
ColumnMapping m = relationship.getMappingByPkCol(col);
if (m == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring change for column "+col+" parent "+col.getParent());
}
return;
}
if (m.getPkColumn() == null) throw new NullPointerException("Missing pk column in mapping");
//The fk column can be null in a mapping but the table and column name must
//exist. This is done to improve the speed of populating 1 table.
if (m.getFkColumn() == null) {
if (m.getFkColName() == null) throw new NullPointerException("Missing pk column name in mapping");
if (m.getFkTable() == null) throw new NullPointerException("Missing pk table in mapping");
return;
}
if (prop == null
|| prop.equals("parent")
|| prop.equals("remarks")
|| prop.equals("autoIncrement")) {
// don't care
} else if (prop.equals("sourceColumn")) {
m.getFkColumn().setSourceColumn(m.getPkColumn().getSourceColumn());
} else if (prop.equals("name")) {
// only update the fkcol name if its name was the same as the old pkcol name
if (m.getFkColumn().getName().equalsIgnoreCase((String) e.getOldValue())) {
m.getFkColumn().setName(m.getPkColumn().getName());
}
} else {
logger.warn("Warning: unknown column property "+prop
+" changed while monitoring pkTable");
}
}
} else if (e.getSource() instanceof UserDefinedSQLType) {
UserDefinedSQLType sourceType = (UserDefinedSQLType) e.getSource();
SPObject parent = sourceType.getParent();
if (!(parent instanceof SQLColumn)) {
throw new IllegalStateException("UserDefinedSQLType " +
sourceType.getPhysicalName() + " must have a SQLColumn parent.");
}
SQLColumn sourceColumn = (SQLColumn) parent;
ColumnMapping m = relationship.getMappingByPkCol(sourceColumn);
if (m == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring change for UserDefinedSQLType "+sourceType.getName());
}
return;
}
final SQLColumn fkCol = m.getFkColumn();
if (fkCol == null) throw new NullPointerException("Missing fk column in mapping");
final UserDefinedSQLType fkType = fkCol.getUserDefinedSQLType();
if (prop.equals("type")) {
fkType.setType((Integer) e.getNewValue());
} else if (prop.equals("name")) {
fkType.setName((String) e.getNewValue());
} else if (prop.equals("myNullability")) {
// if ForeignKey Column is nullable then set it's nullability to DatabaseMetaData.columnNullable
if (fkCol.getUserDefinedSQLType().getNullability() == DatabaseMetaData.columnNullable) {
logger.debug("setting nullability for foreign column:"+ fkCol + " to "+DatabaseMetaData.columnNullable);
fkType.setMyNullability(DatabaseMetaData.columnNullable);
} else {
logger.debug("setting nullability for foreign column:"+ fkCol + " new val :"+(Integer) e.getNewValue());
fkType.setMyNullability((Integer) e.getNewValue());
}
} else if (prop.equals("upstreamType")) {
fkType.setUpstreamType((UserDefinedSQLType) e.getNewValue());
} else if (prop.equals("basicType")) {
fkType.setBasicType((BasicSQLType) e.getNewValue());
} else {
logger.warn("Warning: unknown UserDefinedSQLType property "+prop
+" changed while monitoring pkTable");
}
} else if (e.getSource() instanceof SQLTypePhysicalProperties) {
// Find all the column mappings where the primary key column's
// type is the type that fired this property change event.
SQLTypePhysicalProperties sourceProperties = (SQLTypePhysicalProperties) e.getSource();
SPObject parent = sourceProperties.getParent();
if (!(parent instanceof UserDefinedSQLType)) {
throw new IllegalStateException("SQLTypePhysicalProperties " +
sourceProperties.getPhysicalName() +
" must have a UserDefinedSQLType parent.");
}
UserDefinedSQLType sourceType = (UserDefinedSQLType) parent;
parent = sourceType.getParent();
if (!(parent instanceof SQLColumn)) {
throw new IllegalStateException("UserDefinedSQLType " +
sourceType.getPhysicalName() + " must have a SQLColumn parent.");
}
SQLColumn sourceColumn = (SQLColumn) parent;
ColumnMapping m = relationship.getMappingByPkCol(sourceColumn);
if (m == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring change for SQLTypePhysicalProperties "+sourceProperties.getName());
}
return;
}
final SQLColumn fkCol = m.getFkColumn();
if (fkCol == null) throw new NullPointerException("Missing fk column in mapping");
final UserDefinedSQLType fkType = fkCol.getUserDefinedSQLType();
final String fkPlatform = fkCol.getPlatform();
if (prop.equals("scale") &&
(e.getNewValue() == null || fkType.getScale(fkPlatform) > (Integer) e.getNewValue())) {
// Foreign key's scale must conform to primary key's scale.
// FK scale <= PK scale
fkType.setScale(fkPlatform, (Integer) e.getNewValue());
} else if (prop.equals("scaleType")) {
fkType.setScaleType(fkPlatform, (PropertyType) e.getNewValue());
} else if (prop.equals("precision") &&
(e.getNewValue() == null || fkType.getPrecision(fkPlatform) > (Integer) e.getNewValue())) {
// Foreign key's precision must conform to primary key's precision.
// FK precision <= PK precision
fkType.setPrecision(fkPlatform, (Integer) e.getNewValue());
} else if (prop.equals("precisionType")) {
fkType.setPrecisionType(fkPlatform, (PropertyType) e.getNewValue());
} else if (prop.equals("constraintType")) {
fkType.setConstraintType(fkPlatform, (SQLTypeConstraint) e.getNewValue());
} else if (prop.equals("defaultValue")) {
fkType.setDefaultValue(fkPlatform, (String) e.getNewValue());
} else {
logger.warn("Warning: unknown SQLTypePhysicalProperties property "+prop
+" changed while monitoring pkTable");
}
} else if (e.getSource() instanceof SQLCheckConstraint) {
SQLCheckConstraint sourceConstraint = (SQLCheckConstraint) e.getSource();
SPObject parent = sourceConstraint.getParent();
if (!(parent instanceof SQLTypePhysicalProperties)) {
throw new IllegalStateException("SQLCheckConstraint " +
sourceConstraint.getPhysicalName() +
" must have a SQLTypePhysicalProperties parent.");
}
SQLTypePhysicalProperties sourceProperties = (SQLTypePhysicalProperties) parent;
parent = sourceProperties.getParent();
if (!(parent instanceof UserDefinedSQLType)) {
throw new IllegalStateException("SQLTypePhysicalProperties " +
sourceProperties.getPhysicalName() +
" must have a UserDefinedSQLType parent.");
}
UserDefinedSQLType sourceType = (UserDefinedSQLType) parent;
parent = sourceType.getParent();
if (!(parent instanceof SQLColumn)) {
throw new IllegalStateException("UserDefinedSQLType " +
sourceType.getPhysicalName() + " must have a SQLColumn parent.");
}
SQLColumn sourceColumn = (SQLColumn) parent;
ColumnMapping m = relationship.getMappingByPkCol(sourceColumn);
if (m == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring change for SQLCheckConstraint "+sourceConstraint.getName());
}
return;
}
final SQLColumn fkCol = m.getFkColumn();
if (fkCol == null) throw new NullPointerException("Missing fk column in mapping");
final String fkPlatform = fkCol.getPlatform();
final SQLTypePhysicalProperties fkProperties =
fkCol.getUserDefinedSQLType().getPhysicalProperties(fkPlatform);
for (SQLCheckConstraint constraint : fkProperties.getChildren(SQLCheckConstraint.class)) {
if (prop.equals("name")
&& constraint.getName().equals((String) e.getOldValue())
&& constraint.getConstraint().equals(sourceConstraint.getConstraint())) {
constraint.setName((String) e.getNewValue());
} else if (prop.equals("constraint")
&& constraint.getName().equals(sourceConstraint)
&& constraint.getConstraint().equals((String) e.getOldValue())) {
constraint.setConstraint((String) e.getNewValue());
}
}
} else if (e.getSource() instanceof SQLEnumeration) {
SQLEnumeration sourceEnumeration = (SQLEnumeration) e.getSource();
SPObject parent = sourceEnumeration.getParent();
if (!(parent instanceof SQLTypePhysicalProperties)) {
throw new IllegalStateException("SQLEnumeration " +
sourceEnumeration.getPhysicalName() +
" must have a SQLTypePhysicalProperties parent.");
}
SQLTypePhysicalProperties sourceProperties = (SQLTypePhysicalProperties) parent;
parent = sourceProperties.getParent();
if (!(parent instanceof UserDefinedSQLType)) {
throw new IllegalStateException("SQLTypePhysicalProperties " +
sourceProperties.getPhysicalName() +
" must have a UserDefinedSQLType parent.");
}
UserDefinedSQLType sourceType = (UserDefinedSQLType) parent;
parent = sourceType.getParent();
if (!(parent instanceof SQLColumn)) {
throw new IllegalStateException("UserDefinedSQLType " +
sourceType.getPhysicalName() + " must have a SQLColumn parent.");
}
SQLColumn sourceColumn = (SQLColumn) parent;
ColumnMapping m = relationship.getMappingByPkCol(sourceColumn);
if (m == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring change for SQLEnumeration "+sourceEnumeration.getName());
}
return;
}
final SQLColumn fkCol = m.getFkColumn();
if (fkCol == null) throw new NullPointerException("Missing fk column in mapping");
final String fkPlatform = fkCol.getPlatform();
final SQLTypePhysicalProperties fkProperties =
fkCol.getUserDefinedSQLType().getPhysicalProperties(fkPlatform);
for (SQLEnumeration constraint : fkProperties.getChildren(SQLEnumeration.class)) {
if (prop.equals("name")
&& constraint.getName().equals((String) e.getOldValue())) {
constraint.setName((String) e.getNewValue());
}
}
}
}
@Override
protected void childAddedImpl(SPChildEvent e) {
SQLPowerUtils.listenToHierarchy(e.getChild(), this);
}
@Override
protected void childRemovedImpl(SPChildEvent e) {
SQLPowerUtils.unlistenToHierarchy(e.getChild(), this);
}
}